summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/algorithm/filter.cpp12
-rw-r--r--src/audio_core/algorithm/filter.h4
-rw-r--r--src/audio_core/algorithm/interpolate.cpp12
-rw-r--r--src/audio_core/algorithm/interpolate.h4
-rw-r--r--src/audio_core/audio_out.cpp3
-rw-r--r--src/audio_core/audio_out.h2
-rw-r--r--src/audio_core/audio_renderer.cpp76
-rw-r--r--src/audio_core/audio_renderer.h51
-rw-r--r--src/audio_core/codec.cpp20
-rw-r--r--src/audio_core/codec.h2
-rw-r--r--src/audio_core/cubeb_sink.cpp129
-rw-r--r--src/audio_core/null_sink.h6
-rw-r--r--src/audio_core/sink_details.cpp2
-rw-r--r--src/audio_core/sink_details.h4
-rw-r--r--src/audio_core/sink_stream.h4
-rw-r--r--src/audio_core/stream.cpp16
-rw-r--r--src/audio_core/stream.h13
-rw-r--r--src/audio_core/time_stretch.cpp69
-rw-r--r--src/audio_core/time_stretch.h35
-rw-r--r--src/common/CMakeLists.txt25
-rw-r--r--src/common/alignment.h4
-rw-r--r--src/common/bit_field.h4
-rw-r--r--src/common/bit_set.h6
-rw-r--r--src/common/cityhash.cpp22
-rw-r--r--src/common/cityhash.h12
-rw-r--r--src/common/file_util.cpp16
-rw-r--r--src/common/file_util.h25
-rw-r--r--src/common/hash.h4
-rw-r--r--src/common/hex_util.cpp4
-rw-r--r--src/common/hex_util.h12
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/backend.h2
-rw-r--r--src/common/logging/filter.cpp5
-rw-r--r--src/common/logging/filter.h4
-rw-r--r--src/common/logging/log.h10
-rw-r--r--src/common/logging/text_formatter.h2
-rw-r--r--src/common/memory_util.cpp12
-rw-r--r--src/common/memory_util.h12
-rw-r--r--src/common/misc.cpp2
-rw-r--r--src/common/ring_buffer.h119
-rw-r--r--src/common/scm_rev.cpp.in4
-rw-r--r--src/common/scm_rev.h2
-rw-r--r--src/common/string_util.cpp42
-rw-r--r--src/common/string_util.h4
-rw-r--r--src/common/thread.h10
-rw-r--r--src/common/x64/xbyak_abi.h21
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/arm_interface.h62
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp79
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h36
-rw-r--r--src/core/arm/exclusive_monitor.h12
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp35
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h14
-rw-r--r--src/core/core.cpp35
-rw-r--r--src/core/core.h15
-rw-r--r--src/core/core_cpu.cpp7
-rw-r--r--src/core/core_cpu.h13
-rw-r--r--src/core/crypto/aes_util.cpp51
-rw-r--r--src/core/crypto/aes_util.h14
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp13
-rw-r--r--src/core/crypto/ctr_encryption_layer.h8
-rw-r--r--src/core/crypto/encryption_layer.cpp6
-rw-r--r--src/core/crypto/encryption_layer.h8
-rw-r--r--src/core/crypto/key_manager.cpp25
-rw-r--r--src/core/crypto/key_manager.h13
-rw-r--r--src/core/crypto/xts_encryption_layer.cpp4
-rw-r--r--src/core/crypto/xts_encryption_layer.h2
-rw-r--r--src/core/file_sys/bis_factory.cpp3
-rw-r--r--src/core/file_sys/bis_factory.h7
-rw-r--r--src/core/file_sys/card_image.cpp54
-rw-r--r--src/core/file_sys/card_image.h19
-rw-r--r--src/core/file_sys/content_archive.cpp187
-rw-r--r--src/core/file_sys/content_archive.h21
-rw-r--r--src/core/file_sys/control_metadata.cpp22
-rw-r--r--src/core/file_sys/control_metadata.h19
-rw-r--r--src/core/file_sys/directory.h2
-rw-r--r--src/core/file_sys/nca_metadata.cpp9
-rw-r--r--src/core/file_sys/nca_metadata.h2
-rw-r--r--src/core/file_sys/nca_patch.cpp210
-rw-r--r--src/core/file_sys/nca_patch.h150
-rw-r--r--src/core/file_sys/partition_filesystem.cpp12
-rw-r--r--src/core/file_sys/partition_filesystem.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp159
-rw-r--r--src/core/file_sys/patch_manager.h64
-rw-r--r--src/core/file_sys/program_metadata.cpp11
-rw-r--r--src/core/file_sys/program_metadata.h7
-rw-r--r--src/core/file_sys/registered_cache.cpp151
-rw-r--r--src/core/file_sys/registered_cache.h58
-rw-r--r--src/core/file_sys/romfs.cpp11
-rw-r--r--src/core/file_sys/romfs.h1
-rw-r--r--src/core/file_sys/romfs_factory.cpp16
-rw-r--r--src/core/file_sys/romfs_factory.h3
-rw-r--r--src/core/file_sys/savedata_factory.cpp14
-rw-r--r--src/core/file_sys/savedata_factory.h2
-rw-r--r--src/core/file_sys/submission_package.cpp245
-rw-r--r--src/core/file_sys/submission_package.h76
-rw-r--r--src/core/file_sys/vfs.cpp22
-rw-r--r--src/core/file_sys/vfs.h32
-rw-r--r--src/core/file_sys/vfs_concat.cpp12
-rw-r--r--src/core/file_sys/vfs_concat.h10
-rw-r--r--src/core/file_sys/vfs_offset.cpp26
-rw-r--r--src/core/file_sys/vfs_offset.h27
-rw-r--r--src/core/file_sys/vfs_real.cpp14
-rw-r--r--src/core/file_sys/vfs_real.h28
-rw-r--r--src/core/file_sys/vfs_vector.cpp2
-rw-r--r--src/core/file_sys/vfs_vector.h1
-rw-r--r--src/core/file_sys/xts_archive.cpp22
-rw-r--r--src/core/file_sys/xts_archive.h5
-rw-r--r--src/core/gdbstub/gdbstub.cpp66
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/ipc_helpers.h19
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp25
-rw-r--r--src/core/hle/kernel/errors.h13
-rw-r--r--src/core/hle/kernel/handle_table.cpp2
-rw-r--r--src/core/hle/kernel/handle_table.h2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp27
-rw-r--r--src/core/hle/kernel/hle_ipc.h20
-rw-r--r--src/core/hle/kernel/kernel.cpp38
-rw-r--r--src/core/hle/kernel/kernel.h27
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp8
-rw-r--r--src/core/hle/kernel/process.h4
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp154
-rw-r--r--src/core/hle/kernel/svc_wrap.h75
-rw-r--r--src/core/hle/kernel/thread.cpp16
-rw-r--r--src/core/hle/kernel/thread.h16
-rw-r--r--src/core/hle/kernel/vm_manager.cpp2
-rw-r--r--src/core/hle/kernel/vm_manager.h4
-rw-r--r--src/core/hle/kernel/wait_object.cpp2
-rw-r--r--src/core/hle/service/acc/acc.cpp19
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp2
-rw-r--r--src/core/hle/service/acc/acc_aa.h1
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_su.h7
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.h1
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.h1
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp28
-rw-r--r--src/core/hle/service/acc/profile_manager.h24
-rw-r--r--src/core/hle/service/am/am.cpp51
-rw-r--r--src/core/hle/service/am/am.h13
-rw-r--r--src/core/hle/service/am/applet_ae.cpp2
-rw-r--r--src/core/hle/service/am/applet_ae.h2
-rw-r--r--src/core/hle/service/am/applet_oe.cpp2
-rw-r--r--src/core/hle/service/am/applet_oe.h2
-rw-r--r--src/core/hle/service/am/idle.cpp2
-rw-r--r--src/core/hle/service/am/idle.h1
-rw-r--r--src/core/hle/service/am/omm.cpp2
-rw-r--r--src/core/hle/service/am/omm.h1
-rw-r--r--src/core/hle/service/am/spsm.cpp2
-rw-r--r--src/core/hle/service/am/spsm.h1
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp2
-rw-r--r--src/core/hle/service/aoc/aoc_u.h2
-rw-r--r--src/core/hle/service/apm/apm.cpp3
-rw-r--r--src/core/hle/service/apm/apm.h4
-rw-r--r--src/core/hle/service/apm/interface.cpp4
-rw-r--r--src/core/hle/service/apm/interface.h3
-rw-r--r--src/core/hle/service/audio/audctl.cpp2
-rw-r--r--src/core/hle/service/audio/audctl.h1
-rw-r--r--src/core/hle/service/audio/auddbg.cpp2
-rw-r--r--src/core/hle/service/audio/auddbg.h1
-rw-r--r--src/core/hle/service/audio/audin_a.cpp2
-rw-r--r--src/core/hle/service/audio/audin_a.h1
-rw-r--r--src/core/hle/service/audio/audin_u.cpp2
-rw-r--r--src/core/hle/service/audio/audin_u.h2
-rw-r--r--src/core/hle/service/audio/audio.cpp1
-rw-r--r--src/core/hle/service/audio/audio.h4
-rw-r--r--src/core/hle/service/audio/audout_a.cpp2
-rw-r--r--src/core/hle/service/audio/audout_a.h1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp21
-rw-r--r--src/core/hle/service/audio/audout_u.h19
-rw-r--r--src/core/hle/service/audio/audrec_a.cpp2
-rw-r--r--src/core/hle/service/audio/audrec_a.h1
-rw-r--r--src/core/hle/service/audio/audrec_u.cpp2
-rw-r--r--src/core/hle/service/audio/audrec_u.h2
-rw-r--r--src/core/hle/service/audio/audren_a.cpp2
-rw-r--r--src/core/hle/service/audio/audren_a.h1
-rw-r--r--src/core/hle/service/audio/audren_u.cpp14
-rw-r--r--src/core/hle/service/audio/audren_u.h3
-rw-r--r--src/core/hle/service/audio/codecctl.cpp2
-rw-r--r--src/core/hle/service/audio/codecctl.h2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp13
-rw-r--r--src/core/hle/service/audio/hwopus.h2
-rw-r--r--src/core/hle/service/bcat/bcat.cpp2
-rw-r--r--src/core/hle/service/bcat/bcat.h1
-rw-r--r--src/core/hle/service/bcat/module.cpp2
-rw-r--r--src/core/hle/service/bcat/module.h1
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/fatal/fatal.h1
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp2
-rw-r--r--src/core/hle/service/fatal/fatal_p.h1
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp2
-rw-r--r--src/core/hle/service/fatal/fatal_u.h1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.h4
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.h1
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.h1
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp32
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h4
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/friend/friend.h1
-rw-r--r--src/core/hle/service/friend/interface.cpp2
-rw-r--r--src/core/hle/service/friend/interface.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp50
-rw-r--r--src/core/hle/service/hid/irs.cpp4
-rw-r--r--src/core/hle/service/hid/irs.h2
-rw-r--r--src/core/hle/service/hid/xcd.cpp2
-rw-r--r--src/core/hle/service/hid/xcd.h1
-rw-r--r--src/core/hle/service/lm/lm.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.h1
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp_user.h1
-rw-r--r--src/core/hle/service/nifm/nifm.cpp19
-rw-r--r--src/core/hle/service/nim/nim.cpp102
-rw-r--r--src/core/hle/service/ns/pl_u.cpp180
-rw-r--r--src/core/hle/service/ns/pl_u.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp29
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h4
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.h2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h8
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp5
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h4
-rw-r--r--src/core/hle/service/pctl/module.cpp2
-rw-r--r--src/core/hle/service/pctl/module.h1
-rw-r--r--src/core/hle/service/pctl/pctl.cpp2
-rw-r--r--src/core/hle/service/pctl/pctl.h1
-rw-r--r--src/core/hle/service/prepo/prepo.cpp63
-rw-r--r--src/core/hle/service/prepo/prepo.h16
-rw-r--r--src/core/hle/service/service.cpp17
-rw-r--r--src/core/hle/service/service.h13
-rw-r--r--src/core/hle/service/set/set.cpp12
-rw-r--r--src/core/hle/service/set/set.h4
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_cal.h2
-rw-r--r--src/core/hle/service/set/set_fd.cpp2
-rw-r--r--src/core/hle/service/set/set_fd.h2
-rw-r--r--src/core/hle/service/sm/controller.cpp2
-rw-r--r--src/core/hle/service/sm/controller.h2
-rw-r--r--src/core/hle/service/sm/sm.cpp14
-rw-r--r--src/core/hle/service/sm/sm.h7
-rw-r--r--src/core/hle/service/sockets/bsd.cpp4
-rw-r--r--src/core/hle/service/sockets/bsd.h3
-rw-r--r--src/core/hle/service/sockets/ethc.cpp4
-rw-r--r--src/core/hle/service/sockets/ethc.h2
-rw-r--r--src/core/hle/service/sockets/nsd.cpp2
-rw-r--r--src/core/hle/service/sockets/nsd.h2
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp2
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h2
-rw-r--r--src/core/hle/service/spl/csrng.cpp2
-rw-r--r--src/core/hle/service/spl/csrng.h1
-rw-r--r--src/core/hle/service/spl/module.cpp4
-rw-r--r--src/core/hle/service/spl/module.h1
-rw-r--r--src/core/hle/service/spl/spl.cpp2
-rw-r--r--src/core/hle/service/spl/spl.h1
-rw-r--r--src/core/hle/service/ssl/ssl.cpp64
-rw-r--r--src/core/hle/service/ssl/ssl.h14
-rw-r--r--src/core/hle/service/time/interface.cpp2
-rw-r--r--src/core/hle/service/time/interface.h1
-rw-r--r--src/core/hle/service/time/time.cpp2
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/vi/vi.cpp51
-rw-r--r--src/core/hle/service/vi/vi.h6
-rw-r--r--src/core/hle/service/vi/vi_m.cpp2
-rw-r--r--src/core/hle/service/vi/vi_m.h1
-rw-r--r--src/core/hle/service/vi/vi_s.cpp2
-rw-r--r--src/core/hle/service/vi/vi_s.h1
-rw-r--r--src/core/hle/service/vi/vi_u.cpp2
-rw-r--r--src/core/hle/service/vi/vi_u.h1
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp44
-rw-r--r--src/core/loader/deconstructed_rom_directory.h8
-rw-r--r--src/core/loader/elf.cpp2
-rw-r--r--src/core/loader/loader.cpp28
-rw-r--r--src/core/loader/loader.h37
-rw-r--r--src/core/loader/nax.cpp26
-rw-r--r--src/core/loader/nax.h4
-rw-r--r--src/core/loader/nca.cpp8
-rw-r--r--src/core/loader/nca.h1
-rw-r--r--src/core/loader/nro.cpp7
-rw-r--r--src/core/loader/nro.h1
-rw-r--r--src/core/loader/nso.cpp3
-rw-r--r--src/core/loader/nsp.cpp125
-rw-r--r--src/core/loader/nsp.h54
-rw-r--r--src/core/loader/xci.cpp25
-rw-r--r--src/core/memory.cpp55
-rw-r--r--src/core/memory.h18
-rw-r--r--src/core/memory_hook.h4
-rw-r--r--src/core/settings.h5
-rw-r--r--src/core/telemetry_session.cpp27
-rw-r--r--src/core/tracer/recorder.cpp2
-rw-r--r--src/input_common/main.cpp12
-rw-r--r--src/input_common/main.h2
-rw-r--r--src/input_common/sdl/sdl.cpp387
-rw-r--r--src/input_common/sdl/sdl.h9
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/common/ring_buffer.cpp130
-rw-r--r--src/tests/core/arm/arm_test_common.cpp9
-rw-r--r--src/tests/core/arm/arm_test_common.h8
-rw-r--r--src/tests/glad.cpp14
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/command_processor.cpp174
-rw-r--r--src/video_core/command_processor.h17
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp45
-rw-r--r--src/video_core/engines/kepler_memory.h90
-rw-r--r--src/video_core/engines/maxwell_3d.cpp24
-rw-r--r--src/video_core/engines/maxwell_3d.h57
-rw-r--r--src/video_core/engines/maxwell_dma.cpp12
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h361
-rw-r--r--src/video_core/engines/shader_header.h103
-rw-r--r--src/video_core/gpu.cpp3
-rw-r--r--src/video_core/gpu.h16
-rw-r--r--src/video_core/macro_interpreter.h2
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_base.cpp1
-rw-r--r--src/video_core/renderer_base.h1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp93
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h57
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp534
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h57
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp502
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h110
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp837
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h70
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_state.h8
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp26
-rw-r--r--src/video_core/textures/decoders.cpp53
-rw-r--r--src/video_core/textures/texture.h12
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/about_dialog.cpp2
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/compatibility_list.cpp18
-rw-r--r--src/yuzu/compatibility_list.h17
-rw-r--r--src/yuzu/configuration/config.cpp21
-rw-r--r--src/yuzu/configuration/configure_audio.cpp3
-rw-r--r--src/yuzu/configuration/configure_audio.ui10
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp103
-rw-r--r--src/yuzu/configuration/configure_gamelist.h7
-rw-r--r--src/yuzu/configuration/configure_general.cpp3
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp16
-rw-r--r--src/yuzu/configuration/configure_graphics.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui21
-rw-r--r--src/yuzu/configuration/configure_input.cpp22
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp3
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp61
-rw-r--r--src/yuzu/game_list.cpp227
-rw-r--r--src/yuzu/game_list.h13
-rw-r--r--src/yuzu/game_list_p.h82
-rw-r--r--src/yuzu/game_list_worker.cpp239
-rw-r--r--src/yuzu/game_list_worker.h72
-rw-r--r--src/yuzu/main.cpp138
-rw-r--r--src/yuzu/main.h18
-rw-r--r--src/yuzu/main.ui19
-rw-r--r--src/yuzu/util/util.cpp3
-rw-r--r--src/yuzu_cmd/config.cpp12
-rw-r--r--src/yuzu_cmd/default_ini.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp18
-rw-r--r--src/yuzu_cmd/yuzu.cpp10
396 files changed, 7589 insertions, 2835 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 82e4850f7..c381dbe1d 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -17,6 +17,8 @@ add_library(audio_core STATIC
17 sink_stream.h 17 sink_stream.h
18 stream.cpp 18 stream.cpp
19 stream.h 19 stream.h
20 time_stretch.cpp
21 time_stretch.h
20 22
21 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> 23 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
22) 24)
@@ -24,6 +26,7 @@ add_library(audio_core STATIC
24create_target_directory_groups(audio_core) 26create_target_directory_groups(audio_core)
25 27
26target_link_libraries(audio_core PUBLIC common core) 28target_link_libraries(audio_core PUBLIC common core)
29target_link_libraries(audio_core PRIVATE SoundTouch)
27 30
28if(ENABLE_CUBEB) 31if(ENABLE_CUBEB)
29 target_link_libraries(audio_core PRIVATE cubeb) 32 target_link_libraries(audio_core PRIVATE cubeb)
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp
index 9fcd0614d..f65bf64f7 100644
--- a/src/audio_core/algorithm/filter.cpp
+++ b/src/audio_core/algorithm/filter.cpp
@@ -35,12 +35,12 @@ Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
35 : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} 35 : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
36 36
37void Filter::Process(std::vector<s16>& signal) { 37void Filter::Process(std::vector<s16>& signal) {
38 const size_t num_frames = signal.size() / 2; 38 const std::size_t num_frames = signal.size() / 2;
39 for (size_t i = 0; i < num_frames; i++) { 39 for (std::size_t i = 0; i < num_frames; i++) {
40 std::rotate(in.begin(), in.end() - 1, in.end()); 40 std::rotate(in.begin(), in.end() - 1, in.end());
41 std::rotate(out.begin(), out.end() - 1, out.end()); 41 std::rotate(out.begin(), out.end() - 1, out.end());
42 42
43 for (size_t ch = 0; ch < channel_count; ch++) { 43 for (std::size_t ch = 0; ch < channel_count; ch++) {
44 in[0][ch] = signal[i * channel_count + ch]; 44 in[0][ch] = signal[i * channel_count + ch];
45 45
46 out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - 46 out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] -
@@ -54,14 +54,14 @@ void Filter::Process(std::vector<s16>& signal) {
54/// Calculates the appropriate Q for each biquad in a cascading filter. 54/// Calculates the appropriate Q for each biquad in a cascading filter.
55/// @param total_count The total number of biquads to be cascaded. 55/// @param total_count The total number of biquads to be cascaded.
56/// @param index 0-index of the biquad to calculate the Q value for. 56/// @param index 0-index of the biquad to calculate the Q value for.
57static double CascadingBiquadQ(size_t total_count, size_t index) { 57static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
58 const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); 58 const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
59 return 1.0 / (2.0 * std::cos(pole)); 59 return 1.0 / (2.0 * std::cos(pole));
60} 60}
61 61
62CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { 62CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) {
63 std::vector<Filter> cascade(cascade_size); 63 std::vector<Filter> cascade(cascade_size);
64 for (size_t i = 0; i < cascade_size; i++) { 64 for (std::size_t i = 0; i < cascade_size; i++) {
65 cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); 65 cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
66 } 66 }
67 return CascadingFilter{std::move(cascade)}; 67 return CascadingFilter{std::move(cascade)};
diff --git a/src/audio_core/algorithm/filter.h b/src/audio_core/algorithm/filter.h
index a41beef98..3546d149b 100644
--- a/src/audio_core/algorithm/filter.h
+++ b/src/audio_core/algorithm/filter.h
@@ -30,7 +30,7 @@ public:
30 void Process(std::vector<s16>& signal); 30 void Process(std::vector<s16>& signal);
31 31
32private: 32private:
33 static constexpr size_t channel_count = 2; 33 static constexpr std::size_t channel_count = 2;
34 34
35 /// Coefficients are in normalized form (a0 = 1.0). 35 /// Coefficients are in normalized form (a0 = 1.0).
36 double a1, a2, b0, b1, b2; 36 double a1, a2, b0, b1, b2;
@@ -46,7 +46,7 @@ public:
46 /// Creates a cascading low-pass filter. 46 /// Creates a cascading low-pass filter.
47 /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. 47 /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
48 /// @param cascade_size Number of biquads in cascade. 48 /// @param cascade_size Number of biquads in cascade.
49 static CascadingFilter LowPass(double cutoff, size_t cascade_size); 49 static CascadingFilter LowPass(double cutoff, std::size_t cascade_size);
50 50
51 /// Passthrough. 51 /// Passthrough.
52 CascadingFilter(); 52 CascadingFilter();
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 11459821f..3aea9b0f2 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -14,7 +14,7 @@
14namespace AudioCore { 14namespace AudioCore {
15 15
16/// The Lanczos kernel 16/// The Lanczos kernel
17static double Lanczos(size_t a, double x) { 17static double Lanczos(std::size_t a, double x) {
18 if (x == 0.0) 18 if (x == 0.0)
19 return 1.0; 19 return 1.0;
20 const double px = M_PI * x; 20 const double px = M_PI * x;
@@ -37,15 +37,15 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
37 } 37 }
38 state.nyquist.Process(input); 38 state.nyquist.Process(input);
39 39
40 constexpr size_t taps = InterpolationState::lanczos_taps; 40 constexpr std::size_t taps = InterpolationState::lanczos_taps;
41 const size_t num_frames = input.size() / 2; 41 const std::size_t num_frames = input.size() / 2;
42 42
43 std::vector<s16> output; 43 std::vector<s16> output;
44 output.reserve(static_cast<size_t>(input.size() / ratio + 4)); 44 output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
45 45
46 double& pos = state.position; 46 double& pos = state.position;
47 auto& h = state.history; 47 auto& h = state.history;
48 for (size_t i = 0; i < num_frames; ++i) { 48 for (std::size_t i = 0; i < num_frames; ++i) {
49 std::rotate(h.begin(), h.end() - 1, h.end()); 49 std::rotate(h.begin(), h.end() - 1, h.end());
50 h[0][0] = input[i * 2 + 0]; 50 h[0][0] = input[i * 2 + 0];
51 h[0][1] = input[i * 2 + 1]; 51 h[0][1] = input[i * 2 + 1];
@@ -53,7 +53,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
53 while (pos <= 1.0) { 53 while (pos <= 1.0) {
54 double l = 0.0; 54 double l = 0.0;
55 double r = 0.0; 55 double r = 0.0;
56 for (size_t j = 0; j < h.size(); j++) { 56 for (std::size_t j = 0; j < h.size(); j++) {
57 l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; 57 l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
58 r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; 58 r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
59 } 59 }
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h
index c79c2eef4..edbd6460f 100644
--- a/src/audio_core/algorithm/interpolate.h
+++ b/src/audio_core/algorithm/interpolate.h
@@ -12,8 +12,8 @@
12namespace AudioCore { 12namespace AudioCore {
13 13
14struct InterpolationState { 14struct InterpolationState {
15 static constexpr size_t lanczos_taps = 4; 15 static constexpr std::size_t lanczos_taps = 4;
16 static constexpr size_t history_size = lanczos_taps * 2 - 1; 16 static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
17 17
18 double current_ratio = 0.0; 18 double current_ratio = 0.0;
19 CascadingFilter nyquist; 19 CascadingFilter nyquist;
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 12632a95c..0c8f5b18e 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -39,7 +39,8 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&&
39 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); 39 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
40} 40}
41 41
42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { 42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
43 std::size_t max_count) {
43 return stream->GetTagsAndReleaseBuffers(max_count); 44 return stream->GetTagsAndReleaseBuffers(max_count);
44} 45}
45 46
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index 39b7e656b..df9607ac7 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -25,7 +25,7 @@ public:
25 Stream::ReleaseCallback&& release_callback); 25 Stream::ReleaseCallback&& release_callback);
26 26
27 /// Returns a vector of recently released buffers specified by tag for the specified stream 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); 28 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
29 29
30 /// Starts an audio stream for playback 30 /// Starts an audio stream for playback
31 void StartStream(StreamPtr stream); 31 void StartStream(StreamPtr stream);
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 397b107f5..83b75e61f 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -3,9 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "audio_core/algorithm/interpolate.h" 5#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/audio_out.h"
6#include "audio_core/audio_renderer.h" 7#include "audio_core/audio_renderer.h"
8#include "audio_core/codec.h"
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/hle/kernel/event.h"
9#include "core/memory.h" 12#include "core/memory.h"
10 13
11namespace AudioCore { 14namespace AudioCore {
@@ -13,20 +16,57 @@ namespace AudioCore {
13constexpr u32 STREAM_SAMPLE_RATE{48000}; 16constexpr u32 STREAM_SAMPLE_RATE{48000};
14constexpr u32 STREAM_NUM_CHANNELS{2}; 17constexpr u32 STREAM_NUM_CHANNELS{2};
15 18
19class AudioRenderer::VoiceState {
20public:
21 bool IsPlaying() const {
22 return is_in_use && info.play_state == PlayState::Started;
23 }
24
25 const VoiceOutStatus& GetOutStatus() const {
26 return out_status;
27 }
28
29 const VoiceInfo& GetInfo() const {
30 return info;
31 }
32
33 VoiceInfo& Info() {
34 return info;
35 }
36
37 void SetWaveIndex(std::size_t index);
38 std::vector<s16> DequeueSamples(std::size_t sample_count);
39 void UpdateState();
40 void RefreshBuffer();
41
42private:
43 bool is_in_use{};
44 bool is_refresh_pending{};
45 std::size_t wave_index{};
46 std::size_t offset{};
47 Codec::ADPCMState adpcm_state{};
48 InterpolationState interp_state{};
49 std::vector<s16> samples;
50 VoiceOutStatus out_status{};
51 VoiceInfo info{};
52};
53
16AudioRenderer::AudioRenderer(AudioRendererParameter params, 54AudioRenderer::AudioRenderer(AudioRendererParameter params,
17 Kernel::SharedPtr<Kernel::Event> buffer_event) 55 Kernel::SharedPtr<Kernel::Event> buffer_event)
18 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { 56 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
19 57
20 audio_core = std::make_unique<AudioCore::AudioOut>(); 58 audio_out = std::make_unique<AudioCore::AudioOut>();
21 stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", 59 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
22 [=]() { buffer_event->Signal(); }); 60 [=]() { buffer_event->Signal(); });
23 audio_core->StartStream(stream); 61 audio_out->StartStream(stream);
24 62
25 QueueMixedBuffer(0); 63 QueueMixedBuffer(0);
26 QueueMixedBuffer(1); 64 QueueMixedBuffer(1);
27 QueueMixedBuffer(2); 65 QueueMixedBuffer(2);
28} 66}
29 67
68AudioRenderer::~AudioRenderer() = default;
69
30u32 AudioRenderer::GetSampleRate() const { 70u32 AudioRenderer::GetSampleRate() const {
31 return worker_params.sample_rate; 71 return worker_params.sample_rate;
32} 72}
@@ -52,8 +92,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
52 memory_pool_count * sizeof(MemoryPoolInfo)); 92 memory_pool_count * sizeof(MemoryPoolInfo));
53 93
54 // Copy VoiceInfo structs 94 // Copy VoiceInfo structs
55 size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + 95 std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
56 config.voice_resource_size}; 96 config.voice_resource_size};
57 for (auto& voice : voices) { 97 for (auto& voice : voices) {
58 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); 98 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
59 offset += sizeof(VoiceInfo); 99 offset += sizeof(VoiceInfo);
@@ -72,7 +112,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
72 112
73 // Update memory pool state 113 // Update memory pool state
74 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); 114 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
75 for (size_t index = 0; index < memory_pool.size(); ++index) { 115 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
76 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { 116 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
77 memory_pool[index].state = MemoryPoolStates::Attached; 117 memory_pool[index].state = MemoryPoolStates::Attached;
78 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { 118 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
@@ -93,7 +133,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
93 response_data.memory_pools_size); 133 response_data.memory_pools_size);
94 134
95 // Copy output voice status 135 // Copy output voice status
96 size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; 136 std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
97 for (const auto& voice : voices) { 137 for (const auto& voice : voices) {
98 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), 138 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
99 sizeof(VoiceOutStatus)); 139 sizeof(VoiceOutStatus));
@@ -103,12 +143,12 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
103 return output_params; 143 return output_params;
104} 144}
105 145
106void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { 146void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
107 wave_index = index & 3; 147 wave_index = index & 3;
108 is_refresh_pending = true; 148 is_refresh_pending = true;
109} 149}
110 150
111std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { 151std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
112 if (!IsPlaying()) { 152 if (!IsPlaying()) {
113 return {}; 153 return {};
114 } 154 }
@@ -117,9 +157,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count)
117 RefreshBuffer(); 157 RefreshBuffer();
118 } 158 }
119 159
120 const size_t max_size{samples.size() - offset}; 160 const std::size_t max_size{samples.size() - offset};
121 const size_t dequeue_offset{offset}; 161 const std::size_t dequeue_offset{offset};
122 size_t size{sample_count * STREAM_NUM_CHANNELS}; 162 std::size_t size{sample_count * STREAM_NUM_CHANNELS};
123 if (size > max_size) { 163 if (size > max_size) {
124 size = max_size; 164 size = max_size;
125 } 165 }
@@ -184,7 +224,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
184 case 1: 224 case 1:
185 // 1 channel is upsampled to 2 channel 225 // 1 channel is upsampled to 2 channel
186 samples.resize(new_samples.size() * 2); 226 samples.resize(new_samples.size() * 2);
187 for (size_t index = 0; index < new_samples.size(); ++index) { 227 for (std::size_t index = 0; index < new_samples.size(); ++index) {
188 samples[index * 2] = new_samples[index]; 228 samples[index * 2] = new_samples[index];
189 samples[index * 2 + 1] = new_samples[index]; 229 samples[index * 2 + 1] = new_samples[index];
190 } 230 }
@@ -210,7 +250,7 @@ static constexpr s16 ClampToS16(s32 value) {
210} 250}
211 251
212void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { 252void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
213 constexpr size_t BUFFER_SIZE{512}; 253 constexpr std::size_t BUFFER_SIZE{512};
214 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); 254 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
215 255
216 for (auto& voice : voices) { 256 for (auto& voice : voices) {
@@ -218,7 +258,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
218 continue; 258 continue;
219 } 259 }
220 260
221 size_t offset{}; 261 std::size_t offset{};
222 s64 samples_remaining{BUFFER_SIZE}; 262 s64 samples_remaining{BUFFER_SIZE};
223 while (samples_remaining > 0) { 263 while (samples_remaining > 0) {
224 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; 264 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
@@ -236,11 +276,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
236 } 276 }
237 } 277 }
238 } 278 }
239 audio_core->QueueBuffer(stream, tag, std::move(buffer)); 279 audio_out->QueueBuffer(stream, tag, std::move(buffer));
240} 280}
241 281
242void AudioRenderer::ReleaseAndQueueBuffers() { 282void AudioRenderer::ReleaseAndQueueBuffers() {
243 const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)}; 283 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
244 for (const auto& tag : released_buffers) { 284 for (const auto& tag : released_buffers) {
245 QueueMixedBuffer(tag); 285 QueueMixedBuffer(tag);
246 } 286 }
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index eba67f28e..2c4f5ab75 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -8,16 +8,20 @@
8#include <memory> 8#include <memory>
9#include <vector> 9#include <vector>
10 10
11#include "audio_core/algorithm/interpolate.h"
12#include "audio_core/audio_out.h"
13#include "audio_core/codec.h"
14#include "audio_core/stream.h" 11#include "audio_core/stream.h"
12#include "common/common_funcs.h"
15#include "common/common_types.h" 13#include "common/common_types.h"
16#include "common/swap.h" 14#include "common/swap.h"
17#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/object.h"
16
17namespace Kernel {
18class Event;
19}
18 20
19namespace AudioCore { 21namespace AudioCore {
20 22
23class AudioOut;
24
21enum class PlayState : u8 { 25enum class PlayState : u8 {
22 Started = 0, 26 Started = 0,
23 Stopped = 1, 27 Stopped = 1,
@@ -158,6 +162,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
158class AudioRenderer { 162class AudioRenderer {
159public: 163public:
160 AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); 164 AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
165 ~AudioRenderer();
166
161 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); 167 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
162 void QueueMixedBuffer(Buffer::Tag tag); 168 void QueueMixedBuffer(Buffer::Tag tag);
163 void ReleaseAndQueueBuffers(); 169 void ReleaseAndQueueBuffers();
@@ -166,45 +172,12 @@ public:
166 u32 GetMixBufferCount() const; 172 u32 GetMixBufferCount() const;
167 173
168private: 174private:
169 class VoiceState { 175 class VoiceState;
170 public:
171 bool IsPlaying() const {
172 return is_in_use && info.play_state == PlayState::Started;
173 }
174
175 const VoiceOutStatus& GetOutStatus() const {
176 return out_status;
177 }
178
179 const VoiceInfo& GetInfo() const {
180 return info;
181 }
182
183 VoiceInfo& Info() {
184 return info;
185 }
186
187 void SetWaveIndex(size_t index);
188 std::vector<s16> DequeueSamples(size_t sample_count);
189 void UpdateState();
190 void RefreshBuffer();
191
192 private:
193 bool is_in_use{};
194 bool is_refresh_pending{};
195 size_t wave_index{};
196 size_t offset{};
197 Codec::ADPCMState adpcm_state{};
198 InterpolationState interp_state{};
199 std::vector<s16> samples;
200 VoiceOutStatus out_status{};
201 VoiceInfo info{};
202 };
203 176
204 AudioRendererParameter worker_params; 177 AudioRendererParameter worker_params;
205 Kernel::SharedPtr<Kernel::Event> buffer_event; 178 Kernel::SharedPtr<Kernel::Event> buffer_event;
206 std::vector<VoiceState> voices; 179 std::vector<VoiceState> voices;
207 std::unique_ptr<AudioCore::AudioOut> audio_core; 180 std::unique_ptr<AudioOut> audio_out;
208 AudioCore::StreamPtr stream; 181 AudioCore::StreamPtr stream;
209}; 182};
210 183
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
index c3021403f..454de798b 100644
--- a/src/audio_core/codec.cpp
+++ b/src/audio_core/codec.cpp
@@ -8,27 +8,27 @@
8 8
9namespace AudioCore::Codec { 9namespace AudioCore::Codec {
10 10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, 11std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
12 ADPCMState& state) { 12 ADPCMState& state) {
13 // GC-ADPCM with scale factor and variable coefficients. 13 // GC-ADPCM with scale factor and variable coefficients.
14 // Frames are 8 bytes long containing 14 samples each. 14 // Frames are 8 bytes long containing 14 samples each.
15 // Samples are 4 bits (one nibble) long. 15 // Samples are 4 bits (one nibble) long.
16 16
17 constexpr size_t FRAME_LEN = 8; 17 constexpr std::size_t FRAME_LEN = 8;
18 constexpr size_t SAMPLES_PER_FRAME = 14; 18 constexpr std::size_t SAMPLES_PER_FRAME = 14;
19 constexpr std::array<int, 16> SIGNED_NIBBLES = { 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}}; 20 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
21 21
22 const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; 22 const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
23 const size_t ret_size = 23 const std::size_t ret_size =
24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. 24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
25 std::vector<s16> ret(ret_size); 25 std::vector<s16> ret(ret_size);
26 26
27 int yn1 = state.yn1, yn2 = state.yn2; 27 int yn1 = state.yn1, yn2 = state.yn2;
28 28
29 const size_t NUM_FRAMES = 29 const std::size_t NUM_FRAMES =
30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. 30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
31 for (size_t framei = 0; framei < NUM_FRAMES; framei++) { 31 for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) {
32 const int frame_header = data[framei * FRAME_LEN]; 32 const int frame_header = data[framei * FRAME_LEN];
33 const int scale = 1 << (frame_header & 0xF); 33 const int scale = 1 << (frame_header & 0xF);
34 const int idx = (frame_header >> 4) & 0x7; 34 const int idx = (frame_header >> 4) & 0x7;
@@ -53,9 +53,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coef
53 return static_cast<s16>(val); 53 return static_cast<s16>(val);
54 }; 54 };
55 55
56 size_t outputi = framei * SAMPLES_PER_FRAME; 56 std::size_t outputi = framei * SAMPLES_PER_FRAME;
57 size_t datai = framei * FRAME_LEN + 1; 57 std::size_t datai = framei * FRAME_LEN + 1;
58 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { 58 for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); 59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
60 ret[outputi] = sample1; 60 ret[outputi] = sample1;
61 outputi++; 61 outputi++;
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
index 3f845c42c..ef2ce01a8 100644
--- a/src/audio_core/codec.h
+++ b/src/audio_core/codec.h
@@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
38 * @param state ADPCM state, this is updated with new state 38 * @param state ADPCM state, this is updated with new state
39 * @return Decoded stereo signed PCM16 data, sample_count in length 39 * @return Decoded stereo signed PCM16 data, sample_count in length
40 */ 40 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, 41std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
42 ADPCMState& state); 42 ADPCMState& state);
43 43
44}; // namespace AudioCore::Codec 44}; // namespace AudioCore::Codec
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 5a1177d0c..392039688 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -3,27 +3,23 @@
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 <atomic>
6#include <cstring> 7#include <cstring>
7#include <mutex>
8
9#include "audio_core/cubeb_sink.h" 8#include "audio_core/cubeb_sink.h"
10#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/ring_buffer.h"
13#include "core/settings.h"
12 14
13namespace AudioCore { 15namespace AudioCore {
14 16
15class SinkStreamImpl final : public SinkStream { 17class CubebSinkStream final : public SinkStream {
16public: 18public:
17 SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 19 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
18 const std::string& name) 20 const std::string& name)
19 : ctx{ctx}, num_channels{num_channels_} { 21 : ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate,
20 22 num_channels} {
21 if (num_channels == 6) {
22 // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
23 // channel for now
24 is_6_channel = true;
25 num_channels = 2;
26 }
27 23
28 cubeb_stream_params params{}; 24 cubeb_stream_params params{};
29 params.rate = sample_rate; 25 params.rate = sample_rate;
@@ -38,7 +34,7 @@ public:
38 34
39 if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device, 35 if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
40 &params, std::max(512u, minimum_latency), 36 &params, std::max(512u, minimum_latency),
41 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, 37 &CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
42 this) != CUBEB_OK) { 38 this) != CUBEB_OK) {
43 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); 39 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
44 return; 40 return;
@@ -50,7 +46,7 @@ public:
50 } 46 }
51 } 47 }
52 48
53 ~SinkStreamImpl() { 49 ~CubebSinkStream() {
54 if (!ctx) { 50 if (!ctx) {
55 return; 51 return;
56 } 52 }
@@ -62,27 +58,32 @@ public:
62 cubeb_stream_destroy(stream_backend); 58 cubeb_stream_destroy(stream_backend);
63 } 59 }
64 60
65 void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override { 61 void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
66 if (!ctx) { 62 if (source_num_channels > num_channels) {
63 // Downsample 6 channels to 2
64 std::vector<s16> buf;
65 buf.reserve(samples.size() * num_channels / source_num_channels);
66 for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
67 for (std::size_t ch = 0; ch < num_channels; ch++) {
68 buf.push_back(samples[i + ch]);
69 }
70 }
71 queue.Push(buf);
67 return; 72 return;
68 } 73 }
69 74
70 std::lock_guard lock{queue_mutex}; 75 queue.Push(samples);
76 }
71 77
72 queue.reserve(queue.size() + samples.size() * GetNumChannels()); 78 std::size_t SamplesInQueue(u32 num_channels) const override {
79 if (!ctx)
80 return 0;
73 81
74 if (is_6_channel) { 82 return queue.Size() / num_channels;
75 // Downsample 6 channels to 2 83 }
76 const size_t sample_count_copy_size = samples.size() * 2; 84
77 queue.reserve(sample_count_copy_size); 85 void Flush() override {
78 for (size_t i = 0; i < samples.size(); i += num_channels) { 86 should_flush = true;
79 queue.push_back(samples[i]);
80 queue.push_back(samples[i + 1]);
81 }
82 } else {
83 // Copy as-is
84 std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
85 }
86 } 87 }
87 88
88 u32 GetNumChannels() const { 89 u32 GetNumChannels() const {
@@ -95,10 +96,11 @@ private:
95 cubeb* ctx{}; 96 cubeb* ctx{};
96 cubeb_stream* stream_backend{}; 97 cubeb_stream* stream_backend{};
97 u32 num_channels{}; 98 u32 num_channels{};
98 bool is_6_channel{};
99 99
100 std::mutex queue_mutex; 100 Common::RingBuffer<s16, 0x10000> queue;
101 std::vector<s16> queue; 101 std::array<s16, 2> last_frame;
102 std::atomic<bool> should_flush{};
103 TimeStretcher time_stretch;
102 104
103 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 105 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
104 void* output_buffer, long num_frames); 106 void* output_buffer, long num_frames);
@@ -117,10 +119,10 @@ CubebSink::CubebSink(std::string target_device_name) {
117 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); 119 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
118 } else { 120 } else {
119 const auto collection_end{collection.device + collection.count}; 121 const auto collection_end{collection.device + collection.count};
120 const auto device{std::find_if(collection.device, collection_end, 122 const auto device{
121 [&](const cubeb_device_info& device) { 123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
122 return target_device_name == device.friendly_name; 124 return target_device_name == info.friendly_name;
123 })}; 125 })};
124 if (device != collection_end) { 126 if (device != collection_end) {
125 output_device = device->devid; 127 output_device = device->devid;
126 } 128 }
@@ -144,44 +146,59 @@ CubebSink::~CubebSink() {
144SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, 146SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
145 const std::string& name) { 147 const std::string& name) {
146 sink_streams.push_back( 148 sink_streams.push_back(
147 std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name)); 149 std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name));
148 return *sink_streams.back(); 150 return *sink_streams.back();
149} 151}
150 152
151long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 153long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
152 void* output_buffer, long num_frames) { 154 void* output_buffer, long num_frames) {
153 SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data); 155 CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
154 u8* buffer = reinterpret_cast<u8*>(output_buffer); 156 u8* buffer = reinterpret_cast<u8*>(output_buffer);
155 157
156 if (!impl) { 158 if (!impl) {
157 return {}; 159 return {};
158 } 160 }
159 161
160 std::lock_guard lock{impl->queue_mutex}; 162 const std::size_t num_channels = impl->GetNumChannels();
161 163 const std::size_t samples_to_write = num_channels * num_frames;
162 const size_t frames_to_write{ 164 std::size_t samples_written;
163 std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))}; 165
166 if (Settings::values.enable_audio_stretching) {
167 const std::vector<s16> in{impl->queue.Pop()};
168 const std::size_t num_in{in.size() / num_channels};
169 s16* const out{reinterpret_cast<s16*>(buffer)};
170 const std::size_t out_frames =
171 impl->time_stretch.Process(in.data(), num_in, out, num_frames);
172 samples_written = out_frames * num_channels;
173
174 if (impl->should_flush) {
175 impl->time_stretch.Flush();
176 impl->should_flush = false;
177 }
178 } else {
179 samples_written = impl->queue.Pop(buffer, samples_to_write);
180 }
164 181
165 memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels()); 182 if (samples_written >= num_channels) {
166 impl->queue.erase(impl->queue.begin(), 183 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
167 impl->queue.begin() + frames_to_write * impl->GetNumChannels()); 184 num_channels * sizeof(s16));
185 }
168 186
169 if (frames_to_write < num_frames) { 187 // Fill the rest of the frames with last_frame
170 // Fill the rest of the frames with silence 188 for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) {
171 memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0, 189 std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16));
172 (num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
173 } 190 }
174 191
175 return num_frames; 192 return num_frames;
176} 193}
177 194
178void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} 195void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
179 196
180std::vector<std::string> ListCubebSinkDevices() { 197std::vector<std::string> ListCubebSinkDevices() {
181 std::vector<std::string> device_list; 198 std::vector<std::string> device_list;
182 cubeb* ctx; 199 cubeb* ctx;
183 200
184 if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) { 201 if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
185 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 202 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
186 return {}; 203 return {};
187 } 204 }
@@ -190,7 +207,7 @@ std::vector<std::string> ListCubebSinkDevices() {
190 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { 207 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
191 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); 208 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
192 } else { 209 } else {
193 for (size_t i = 0; i < collection.count; i++) { 210 for (std::size_t i = 0; i < collection.count; i++) {
194 const cubeb_device_info& device = collection.device[i]; 211 const cubeb_device_info& device = collection.device[i];
195 if (device.friendly_name) { 212 if (device.friendly_name) {
196 device_list.emplace_back(device.friendly_name); 213 device_list.emplace_back(device.friendly_name);
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index f235d93e5..a78d78893 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -21,6 +21,12 @@ public:
21private: 21private:
22 struct NullSinkStreamImpl final : SinkStream { 22 struct NullSinkStreamImpl final : SinkStream {
23 void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} 23 void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
24
25 std::size_t SamplesInQueue(u32 /*num_channels*/) const override {
26 return 0;
27 }
28
29 void Flush() override {}
24 } null_sink_stream; 30 } null_sink_stream;
25}; 31};
26 32
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index 955ba20fb..67cf1f3b2 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = {
24 [] { return std::vector<std::string>{"null"}; }}, 24 [] { return std::vector<std::string>{"null"}; }},
25}; 25};
26 26
27const SinkDetails& GetSinkDetails(std::string sink_id) { 27const SinkDetails& GetSinkDetails(std::string_view sink_id) {
28 auto iter = 28 auto iter =
29 std::find_if(g_sink_details.begin(), g_sink_details.end(), 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; }); 30 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h
index ea666c554..03534b187 100644
--- a/src/audio_core/sink_details.h
+++ b/src/audio_core/sink_details.h
@@ -6,6 +6,8 @@
6 6
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <string>
10#include <string_view>
9#include <utility> 11#include <utility>
10#include <vector> 12#include <vector>
11 13
@@ -30,6 +32,6 @@ struct SinkDetails {
30 32
31extern const std::vector<SinkDetails> g_sink_details; 33extern const std::vector<SinkDetails> g_sink_details;
32 34
33const SinkDetails& GetSinkDetails(std::string sink_id); 35const SinkDetails& GetSinkDetails(std::string_view sink_id);
34 36
35} // namespace AudioCore 37} // namespace AudioCore
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
index 41b6736d8..4309ad094 100644
--- a/src/audio_core/sink_stream.h
+++ b/src/audio_core/sink_stream.h
@@ -25,6 +25,10 @@ public:
25 * @param samples Samples in interleaved stereo PCM16 format. 25 * @param samples Samples in interleaved stereo PCM16 format.
26 */ 26 */
27 virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; 27 virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
28
29 virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
30
31 virtual void Flush() = 0;
28}; 32};
29 33
30using SinkStreamPtr = std::unique_ptr<SinkStream>; 34using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index ad9e2915c..449db2416 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -7,16 +7,18 @@
7 7
8#include "audio_core/sink.h" 8#include "audio_core/sink.h"
9#include "audio_core/sink_details.h" 9#include "audio_core/sink_details.h"
10#include "audio_core/sink_stream.h"
10#include "audio_core/stream.h" 11#include "audio_core/stream.h"
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/microprofile.h"
13#include "core/core_timing.h" 15#include "core/core_timing.h"
14#include "core/core_timing_util.h" 16#include "core/core_timing_util.h"
15#include "core/settings.h" 17#include "core/settings.h"
16 18
17namespace AudioCore { 19namespace AudioCore {
18 20
19constexpr size_t MaxAudioBufferCount{32}; 21constexpr std::size_t MaxAudioBufferCount{32};
20 22
21u32 Stream::GetNumChannels() const { 23u32 Stream::GetNumChannels() const {
22 switch (format) { 24 switch (format) {
@@ -51,7 +53,7 @@ void Stream::Stop() {
51} 53}
52 54
53s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 55s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
54 const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; 56 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
55 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); 57 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
56} 58}
57 59
@@ -72,6 +74,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
72void Stream::PlayNextBuffer() { 74void Stream::PlayNextBuffer() {
73 if (!IsPlaying()) { 75 if (!IsPlaying()) {
74 // Ensure we are in playing state before playing the next buffer 76 // Ensure we are in playing state before playing the next buffer
77 sink_stream.Flush();
75 return; 78 return;
76 } 79 }
77 80
@@ -82,6 +85,7 @@ void Stream::PlayNextBuffer() {
82 85
83 if (queued_buffers.empty()) { 86 if (queued_buffers.empty()) {
84 // No queued buffers - we are effectively paused 87 // No queued buffers - we are effectively paused
88 sink_stream.Flush();
85 return; 89 return;
86 } 90 }
87 91
@@ -89,12 +93,16 @@ void Stream::PlayNextBuffer() {
89 queued_buffers.pop(); 93 queued_buffers.pop();
90 94
91 VolumeAdjustSamples(active_buffer->Samples()); 95 VolumeAdjustSamples(active_buffer->Samples());
96
92 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 97 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
93 98
94 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 99 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
95} 100}
96 101
102MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
103
97void Stream::ReleaseActiveBuffer() { 104void Stream::ReleaseActiveBuffer() {
105 MICROPROFILE_SCOPE(AudioOutput);
98 ASSERT(active_buffer); 106 ASSERT(active_buffer);
99 released_buffers.push(std::move(active_buffer)); 107 released_buffers.push(std::move(active_buffer));
100 release_callback(); 108 release_callback();
@@ -115,9 +123,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
115 return {}; 123 return {};
116} 124}
117 125
118std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { 126std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
119 std::vector<Buffer::Tag> tags; 127 std::vector<Buffer::Tag> tags;
120 for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { 128 for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
121 tags.push_back(released_buffers.front()->GetTag()); 129 tags.push_back(released_buffers.front()->GetTag());
122 released_buffers.pop(); 130 released_buffers.pop();
123 } 131 }
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 049b92ca9..27db1112f 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -11,13 +11,16 @@
11#include <queue> 11#include <queue>
12 12
13#include "audio_core/buffer.h" 13#include "audio_core/buffer.h"
14#include "audio_core/sink_stream.h"
15#include "common/assert.h"
16#include "common/common_types.h" 14#include "common/common_types.h"
17#include "core/core_timing.h" 15
16namespace CoreTiming {
17struct EventType;
18}
18 19
19namespace AudioCore { 20namespace AudioCore {
20 21
22class SinkStream;
23
21/** 24/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut 25 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */ 26 */
@@ -49,7 +52,7 @@ public:
49 bool ContainsBuffer(Buffer::Tag tag) const; 52 bool ContainsBuffer(Buffer::Tag tag) const;
50 53
51 /// Returns a vector of recently released buffers specified by tag 54 /// Returns a vector of recently released buffers specified by tag
52 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); 55 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
53 56
54 /// Returns true if the stream is currently playing 57 /// Returns true if the stream is currently playing
55 bool IsPlaying() const { 58 bool IsPlaying() const {
@@ -57,7 +60,7 @@ public:
57 } 60 }
58 61
59 /// Returns the number of queued buffers 62 /// Returns the number of queued buffers
60 size_t GetQueueSize() const { 63 std::size_t GetQueueSize() const {
61 return queued_buffers.size(); 64 return queued_buffers.size();
62 } 65 }
63 66
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
new file mode 100644
index 000000000..fc14151da
--- /dev/null
+++ b/src/audio_core/time_stretch.cpp
@@ -0,0 +1,69 @@
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#include <cstddef>
8#include "audio_core/time_stretch.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
14 : m_sample_rate(sample_rate), m_channel_count(channel_count) {
15 m_sound_touch.setChannels(channel_count);
16 m_sound_touch.setSampleRate(sample_rate);
17 m_sound_touch.setPitch(1.0);
18 m_sound_touch.setTempo(1.0);
19}
20
21void TimeStretcher::Clear() {
22 m_sound_touch.clear();
23}
24
25void TimeStretcher::Flush() {
26 m_sound_touch.flush();
27}
28
29std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
30 std::size_t num_out) {
31 const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
32
33 // We were given actual_samples number of samples, and num_samples were requested from us.
34 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
35
36 const double max_latency = 1.0; // seconds
37 const double max_backlog = m_sample_rate * max_latency;
38 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
39 if (backlog_fullness > 5.0) {
40 // Too many samples in backlog: Don't push anymore on
41 num_in = 0;
42 }
43
44 // We ideally want the backlog to be about 50% full.
45 // This gives some headroom both ways to prevent underflow and overflow.
46 // We tweak current_ratio to encourage this.
47 constexpr double tweak_time_scale = 0.05; // seconds
48 const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
49 current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
50
51 // This low-pass filter smoothes out variance in the calculated stretch ratio.
52 // The time-scale determines how responsive this filter is.
53 constexpr double lpf_time_scale = 2.0; // seconds
54 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
55 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
56
57 // Place a lower limit of 5% speed. When a game boots up, there will be
58 // many silence samples. These do not need to be timestretched.
59 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
60 m_sound_touch.setTempo(m_stretch_ratio);
61
62 LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
63 backlog_fullness);
64
65 m_sound_touch.putSamples(in, static_cast<u32>(num_in));
66 return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
67}
68
69} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
new file mode 100644
index 000000000..decd760f1
--- /dev/null
+++ b/src/audio_core/time_stretch.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 <cstddef>
8#include <SoundTouch.h>
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class TimeStretcher {
14public:
15 TimeStretcher(u32 sample_rate, u32 channel_count);
16
17 /// @param in Input sample buffer
18 /// @param num_in Number of input frames in `in`
19 /// @param out Output sample buffer
20 /// @param num_out Desired number of output frames in `out`
21 /// @returns Actual number of frames written to `out`
22 std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
23
24 void Clear();
25
26 void Flush();
27
28private:
29 u32 m_sample_rate;
30 u32 m_channel_count;
31 soundtouch::SoundTouch m_sound_touch;
32 double m_stretch_ratio = 1.0;
33};
34
35} // namespace AudioCore
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index d9424ea91..6a3f1fe08 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,13 +1,16 @@
1# Generate cpp with Git revision from template 1# Generate cpp with Git revision from template
2# Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well 2# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
3set(REPO_NAME "") 3set(REPO_NAME "")
4set(BUILD_VERSION "0")
4if ($ENV{CI}) 5if ($ENV{CI})
5 if ($ENV{TRAVIS}) 6 if ($ENV{TRAVIS})
6 set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG}) 7 set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
8 set(BUILD_TAG $ENV{TRAVIS_TAG})
7 elseif($ENV{APPVEYOR}) 9 elseif($ENV{APPVEYOR})
8 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) 10 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
11 set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
9 endif() 12 endif()
10 # regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1 13 # regex capture the string nightly or canary into CMAKE_MATCH_1
11 string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) 14 string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
12 if (${CMAKE_MATCH_COUNT} GREATER 0) 15 if (${CMAKE_MATCH_COUNT} GREATER 0)
13 # capitalize the first letter of each word in the repo name. 16 # capitalize the first letter of each word in the repo name.
@@ -16,10 +19,21 @@ if ($ENV{CI})
16 string(SUBSTRING ${WORD} 0 1 FIRST_LETTER) 19 string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
17 string(SUBSTRING ${WORD} 1 -1 REMAINDER) 20 string(SUBSTRING ${WORD} 1 -1 REMAINDER)
18 string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) 21 string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
19 # this leaves a trailing space on the last word, but we actually want that 22 set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
20 # because of how it's styled in the title bar.
21 set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
22 endforeach() 23 endforeach()
24 if (BUILD_TAG)
25 string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
26 if (${CMAKE_MATCH_COUNT} GREATER 0)
27 set(BUILD_VERSION ${CMAKE_MATCH_1})
28 endif()
29 if (BUILD_VERSION)
30 # This leaves a trailing space on the last word, but we actually want that
31 # because of how it's styled in the title bar.
32 set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ")
33 else()
34 set(BUILD_FULLNAME "")
35 endif()
36 endif()
23 endif() 37 endif()
24endif() 38endif()
25configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY) 39configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
@@ -57,6 +71,7 @@ add_library(common STATIC
57 param_package.cpp 71 param_package.cpp
58 param_package.h 72 param_package.h
59 quaternion.h 73 quaternion.h
74 ring_buffer.h
60 scm_rev.cpp 75 scm_rev.cpp
61 scm_rev.h 76 scm_rev.h
62 scope_exit.h 77 scope_exit.h
diff --git a/src/common/alignment.h b/src/common/alignment.h
index b9dd38746..225770fab 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -8,13 +8,13 @@
8namespace Common { 8namespace Common {
9 9
10template <typename T> 10template <typename T>
11constexpr T AlignUp(T value, size_t size) { 11constexpr T AlignUp(T value, std::size_t size) {
12 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 12 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
13 return static_cast<T>(value + (size - value % size) % size); 13 return static_cast<T>(value + (size - value % size) % size);
14} 14}
15 15
16template <typename T> 16template <typename T>
17constexpr T AlignDown(T value, size_t size) { 17constexpr T AlignDown(T value, std::size_t size) {
18 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 18 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 732201de7..bf803da8d 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -129,8 +129,8 @@ private:
129 129
130public: 130public:
131 /// Constants to allow limited introspection of fields if needed 131 /// Constants to allow limited introspection of fields if needed
132 static constexpr size_t position = Position; 132 static constexpr std::size_t position = Position;
133 static constexpr size_t bits = Bits; 133 static constexpr std::size_t bits = Bits;
134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; 134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
135 135
136 /** 136 /**
diff --git a/src/common/bit_set.h b/src/common/bit_set.h
index 5a197d8c1..5cd1352b2 100644
--- a/src/common/bit_set.h
+++ b/src/common/bit_set.h
@@ -170,14 +170,14 @@ public:
170 m_val |= (IntTy)1 << bit; 170 m_val |= (IntTy)1 << bit;
171 } 171 }
172 172
173 static BitSet AllTrue(size_t count) { 173 static BitSet AllTrue(std::size_t count) {
174 return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); 174 return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
175 } 175 }
176 176
177 Ref operator[](size_t bit) { 177 Ref operator[](std::size_t bit) {
178 return Ref(this, (IntTy)1 << bit); 178 return Ref(this, (IntTy)1 << bit);
179 } 179 }
180 const Ref operator[](size_t bit) const { 180 const Ref operator[](std::size_t bit) const {
181 return (*const_cast<BitSet*>(this))[bit]; 181 return (*const_cast<BitSet*>(this))[bit];
182 } 182 }
183 bool operator==(BitSet other) const { 183 bool operator==(BitSet other) const {
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp
index de31ffbd8..4e1d874b5 100644
--- a/src/common/cityhash.cpp
+++ b/src/common/cityhash.cpp
@@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
114 return b; 114 return b;
115} 115}
116 116
117static uint64 HashLen0to16(const char* s, size_t len) { 117static uint64 HashLen0to16(const char* s, std::size_t len) {
118 if (len >= 8) { 118 if (len >= 8) {
119 uint64 mul = k2 + len * 2; 119 uint64 mul = k2 + len * 2;
120 uint64 a = Fetch64(s) + k2; 120 uint64 a = Fetch64(s) + k2;
@@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, size_t len) {
141 141
142// This probably works well for 16-byte strings as well, but it may be overkill 142// This probably works well for 16-byte strings as well, but it may be overkill
143// in that case. 143// in that case.
144static uint64 HashLen17to32(const char* s, size_t len) { 144static uint64 HashLen17to32(const char* s, std::size_t len) {
145 uint64 mul = k2 + len * 2; 145 uint64 mul = k2 + len * 2;
146 uint64 a = Fetch64(s) * k1; 146 uint64 a = Fetch64(s) * k1;
147 uint64 b = Fetch64(s + 8); 147 uint64 b = Fetch64(s + 8);
@@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint
170} 170}
171 171
172// Return an 8-byte hash for 33 to 64 bytes. 172// Return an 8-byte hash for 33 to 64 bytes.
173static uint64 HashLen33to64(const char* s, size_t len) { 173static uint64 HashLen33to64(const char* s, std::size_t len) {
174 uint64 mul = k2 + len * 2; 174 uint64 mul = k2 + len * 2;
175 uint64 a = Fetch64(s) * k2; 175 uint64 a = Fetch64(s) * k2;
176 uint64 b = Fetch64(s + 8); 176 uint64 b = Fetch64(s + 8);
@@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, size_t len) {
191 return b + x; 191 return b + x;
192} 192}
193 193
194uint64 CityHash64(const char* s, size_t len) { 194uint64 CityHash64(const char* s, std::size_t len) {
195 if (len <= 32) { 195 if (len <= 32) {
196 if (len <= 16) { 196 if (len <= 16) {
197 return HashLen0to16(s, len); 197 return HashLen0to16(s, len);
@@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, size_t len) {
212 x = x * k1 + Fetch64(s); 212 x = x * k1 + Fetch64(s);
213 213
214 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 214 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
215 len = (len - 1) & ~static_cast<size_t>(63); 215 len = (len - 1) & ~static_cast<std::size_t>(63);
216 do { 216 do {
217 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 217 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
218 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 218 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
@@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, size_t len) {
229 HashLen16(v.second, w.second) + x); 229 HashLen16(v.second, w.second) + x);
230} 230}
231 231
232uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { 232uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
233 return CityHash64WithSeeds(s, len, k2, seed); 233 return CityHash64WithSeeds(s, len, k2, seed);
234} 234}
235 235
236uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { 236uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
237 return HashLen16(CityHash64(s, len) - seed0, seed1); 237 return HashLen16(CityHash64(s, len) - seed0, seed1);
238} 238}
239 239
240// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 240// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
241// of any length representable in signed long. Based on City and Murmur. 241// of any length representable in signed long. Based on City and Murmur.
242static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { 242static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
243 uint64 a = Uint128Low64(seed); 243 uint64 a = Uint128Low64(seed);
244 uint64 b = Uint128High64(seed); 244 uint64 b = Uint128High64(seed);
245 uint64 c = 0; 245 uint64 c = 0;
@@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
269 return uint128(a ^ b, HashLen16(b, a)); 269 return uint128(a ^ b, HashLen16(b, a));
270} 270}
271 271
272uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { 272uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
273 if (len < 128) { 273 if (len < 128) {
274 return CityMurmur(s, len, seed); 274 return CityMurmur(s, len, seed);
275 } 275 }
@@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
313 w.first *= 9; 313 w.first *= 9;
314 v.first *= k0; 314 v.first *= k0;
315 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 315 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
316 for (size_t tail_done = 0; tail_done < len;) { 316 for (std::size_t tail_done = 0; tail_done < len;) {
317 tail_done += 32; 317 tail_done += 32;
318 y = Rotate(x + y, 42) * k0 + v.second; 318 y = Rotate(x + y, 42) * k0 + v.second;
319 w.first += Fetch64(s + len - tail_done + 16); 319 w.first += Fetch64(s + len - tail_done + 16);
@@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
331 return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); 331 return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
332} 332}
333 333
334uint128 CityHash128(const char* s, size_t len) { 334uint128 CityHash128(const char* s, std::size_t len) {
335 return len >= 16 335 return len >= 16
336 ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) 336 ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
337 : CityHash128WithSeed(s, len, uint128(k0, k1)); 337 : CityHash128WithSeed(s, len, uint128(k0, k1));
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index bcebdb150..4b94f8e18 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -63,7 +63,7 @@
63 63
64#include <utility> 64#include <utility>
65#include <stdint.h> 65#include <stdint.h>
66#include <stdlib.h> // for size_t. 66#include <stdlib.h> // for std::size_t.
67 67
68namespace Common { 68namespace Common {
69 69
@@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) {
77} 77}
78 78
79// Hash function for a byte array. 79// Hash function for a byte array.
80uint64_t CityHash64(const char* buf, size_t len); 80uint64_t CityHash64(const char* buf, std::size_t len);
81 81
82// Hash function for a byte array. For convenience, a 64-bit seed is also 82// Hash function for a byte array. For convenience, a 64-bit seed is also
83// hashed into the result. 83// hashed into the result.
84uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); 84uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
85 85
86// Hash function for a byte array. For convenience, two seeds are also 86// Hash function for a byte array. For convenience, two seeds are also
87// hashed into the result. 87// hashed into the result.
88uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); 88uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
89 89
90// Hash function for a byte array. 90// Hash function for a byte array.
91uint128 CityHash128(const char* s, size_t len); 91uint128 CityHash128(const char* s, std::size_t len);
92 92
93// Hash function for a byte array. For convenience, a 128-bit seed is also 93// Hash function for a byte array. For convenience, a 128-bit seed is also
94// hashed into the result. 94// hashed into the result.
95uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); 95uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
96 96
97// Hash 128 input bits down to 64 bits of output. 97// Hash 128 input bits down to 64 bits of output.
98// This is intended to be a reasonably good hash function. 98// This is intended to be a reasonably good hash function.
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index baa721481..21a0b9738 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -76,7 +76,7 @@ namespace FileUtil {
76// Modifies argument. 76// Modifies argument.
77static void StripTailDirSlashes(std::string& fname) { 77static void StripTailDirSlashes(std::string& fname) {
78 if (fname.length() > 1) { 78 if (fname.length() > 1) {
79 size_t i = fname.length(); 79 std::size_t i = fname.length();
80 while (i > 0 && fname[i - 1] == DIR_SEP_CHR) 80 while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
81 --i; 81 --i;
82 fname.resize(i); 82 fname.resize(i);
@@ -201,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) {
201 return true; 201 return true;
202 } 202 }
203 203
204 size_t position = 0; 204 std::size_t position = 0;
205 while (true) { 205 while (true) {
206 // Find next sub path 206 // Find next sub path
207 position = fullPath.find(DIR_SEP_CHR, position); 207 position = fullPath.find(DIR_SEP_CHR, position);
@@ -299,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
299 std::array<char, 1024> buffer; 299 std::array<char, 1024> buffer;
300 while (!feof(input.get())) { 300 while (!feof(input.get())) {
301 // read input 301 // read input
302 size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); 302 std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
303 if (rnum != buffer.size()) { 303 if (rnum != buffer.size()) {
304 if (ferror(input.get()) != 0) { 304 if (ferror(input.get()) != 0) {
305 LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", 305 LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
@@ -309,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
309 } 309 }
310 310
311 // write output 311 // write output
312 size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); 312 std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
313 if (wnum != rnum) { 313 if (wnum != rnum) {
314 LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, 314 LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
315 destFilename, GetLastErrorMsg()); 315 destFilename, GetLastErrorMsg());
@@ -756,11 +756,11 @@ std::string GetNANDRegistrationDir(bool system) {
756 return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; 756 return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
757} 757}
758 758
759size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { 759std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
760 return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); 760 return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
761} 761}
762 762
763size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { 763std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
764 IOFile file(filename, text_file ? "r" : "rb"); 764 IOFile file(filename, text_file ? "r" : "rb");
765 765
766 if (!file.IsOpen()) 766 if (!file.IsOpen())
@@ -829,7 +829,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
829std::string_view GetParentPath(std::string_view path) { 829std::string_view GetParentPath(std::string_view path) {
830 const auto name_bck_index = path.rfind('\\'); 830 const auto name_bck_index = path.rfind('\\');
831 const auto name_fwd_index = path.rfind('/'); 831 const auto name_fwd_index = path.rfind('/');
832 size_t name_index; 832 std::size_t name_index;
833 833
834 if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { 834 if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
835 name_index = std::min(name_bck_index, name_fwd_index); 835 name_index = std::min(name_bck_index, name_fwd_index);
@@ -868,7 +868,7 @@ std::string_view GetFilename(std::string_view path) {
868} 868}
869 869
870std::string_view GetExtensionFromFilename(std::string_view name) { 870std::string_view GetExtensionFromFilename(std::string_view name) {
871 const size_t index = name.rfind('.'); 871 const std::size_t index = name.rfind('.');
872 872
873 if (index == std::string_view::npos) { 873 if (index == std::string_view::npos) {
874 return {}; 874 return {};
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 2f13d0b6b..24c1e413c 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -143,8 +143,9 @@ const std::string& GetExeDirectory();
143std::string AppDataRoamingDirectory(); 143std::string AppDataRoamingDirectory();
144#endif 144#endif
145 145
146size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); 146std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
147size_t ReadFileToString(bool text_file, const char* filename, std::string& str); 147
148std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
148 149
149/** 150/**
150 * Splits the filename into 8.3 format 151 * Splits the filename into 8.3 format
@@ -177,10 +178,10 @@ std::string_view RemoveTrailingSlash(std::string_view path);
177 178
178// Creates a new vector containing indices [first, last) from the original. 179// Creates a new vector containing indices [first, last) from the original.
179template <typename T> 180template <typename T>
180std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { 181std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
181 if (first >= last) 182 if (first >= last)
182 return {}; 183 return {};
183 last = std::min<size_t>(last, vector.size()); 184 last = std::min<std::size_t>(last, vector.size());
184 return std::vector<T>(vector.begin() + first, vector.begin() + first + last); 185 return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
185} 186}
186 187
@@ -213,47 +214,47 @@ public:
213 bool Close(); 214 bool Close();
214 215
215 template <typename T> 216 template <typename T>
216 size_t ReadArray(T* data, size_t length) const { 217 std::size_t ReadArray(T* data, std::size_t length) const {
217 static_assert(std::is_trivially_copyable_v<T>, 218 static_assert(std::is_trivially_copyable_v<T>,
218 "Given array does not consist of trivially copyable objects"); 219 "Given array does not consist of trivially copyable objects");
219 220
220 if (!IsOpen()) { 221 if (!IsOpen()) {
221 return std::numeric_limits<size_t>::max(); 222 return std::numeric_limits<std::size_t>::max();
222 } 223 }
223 224
224 return std::fread(data, sizeof(T), length, m_file); 225 return std::fread(data, sizeof(T), length, m_file);
225 } 226 }
226 227
227 template <typename T> 228 template <typename T>
228 size_t WriteArray(const T* data, size_t length) { 229 std::size_t WriteArray(const T* data, std::size_t length) {
229 static_assert(std::is_trivially_copyable_v<T>, 230 static_assert(std::is_trivially_copyable_v<T>,
230 "Given array does not consist of trivially copyable objects"); 231 "Given array does not consist of trivially copyable objects");
231 if (!IsOpen()) { 232 if (!IsOpen()) {
232 return std::numeric_limits<size_t>::max(); 233 return std::numeric_limits<std::size_t>::max();
233 } 234 }
234 235
235 return std::fwrite(data, sizeof(T), length, m_file); 236 return std::fwrite(data, sizeof(T), length, m_file);
236 } 237 }
237 238
238 template <typename T> 239 template <typename T>
239 size_t ReadBytes(T* data, size_t length) const { 240 std::size_t ReadBytes(T* data, std::size_t length) const {
240 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); 241 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
241 return ReadArray(reinterpret_cast<char*>(data), length); 242 return ReadArray(reinterpret_cast<char*>(data), length);
242 } 243 }
243 244
244 template <typename T> 245 template <typename T>
245 size_t WriteBytes(const T* data, size_t length) { 246 std::size_t WriteBytes(const T* data, std::size_t length) {
246 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); 247 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
247 return WriteArray(reinterpret_cast<const char*>(data), length); 248 return WriteArray(reinterpret_cast<const char*>(data), length);
248 } 249 }
249 250
250 template <typename T> 251 template <typename T>
251 size_t WriteObject(const T& object) { 252 std::size_t WriteObject(const T& object) {
252 static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); 253 static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
253 return WriteArray(&object, 1); 254 return WriteArray(&object, 1);
254 } 255 }
255 256
256 size_t WriteString(const std::string& str) { 257 std::size_t WriteString(const std::string& str) {
257 return WriteArray(str.c_str(), str.length()); 258 return WriteArray(str.c_str(), str.length());
258 } 259 }
259 260
diff --git a/src/common/hash.h b/src/common/hash.h
index 2c761e545..40194d1ee 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -17,7 +17,7 @@ namespace Common {
17 * @param len Length of data (in bytes) to compute hash over 17 * @param len Length of data (in bytes) to compute hash over
18 * @returns 64-bit hash value that was computed over the data block 18 * @returns 64-bit hash value that was computed over the data block
19 */ 19 */
20static inline u64 ComputeHash64(const void* data, size_t len) { 20static inline u64 ComputeHash64(const void* data, std::size_t len) {
21 return CityHash64(static_cast<const char*>(data), len); 21 return CityHash64(static_cast<const char*>(data), len);
22} 22}
23 23
@@ -63,7 +63,7 @@ struct HashableStruct {
63 return !(*this == o); 63 return !(*this == o);
64 }; 64 };
65 65
66 size_t Hash() const { 66 std::size_t Hash() const {
67 return Common::ComputeStructHash64(state); 67 return Common::ComputeStructHash64(state);
68 } 68 }
69}; 69};
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index 8e0a9e46f..589ae5cbf 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -18,7 +18,7 @@ u8 ToHexNibble(char c1) {
18 return 0; 18 return 0;
19} 19}
20 20
21std::array<u8, 16> operator""_array16(const char* str, size_t len) { 21std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
22 if (len != 32) { 22 if (len != 32) {
23 LOG_ERROR(Common, 23 LOG_ERROR(Common,
24 "Attempting to parse string to array that is not of correct size (expected=32, " 24 "Attempting to parse string to array that is not of correct size (expected=32, "
@@ -29,7 +29,7 @@ std::array<u8, 16> operator""_array16(const char* str, size_t len) {
29 return HexStringToArray<16>(str); 29 return HexStringToArray<16>(str);
30} 30}
31 31
32std::array<u8, 32> operator""_array32(const char* str, size_t len) { 32std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
33 if (len != 64) { 33 if (len != 64) {
34 LOG_ERROR(Common, 34 LOG_ERROR(Common,
35 "Attempting to parse string to array that is not of correct size (expected=64, " 35 "Attempting to parse string to array that is not of correct size (expected=64, "
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 5fb79bb72..863a5ccd9 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,20 +14,20 @@ namespace Common {
14 14
15u8 ToHexNibble(char c1); 15u8 ToHexNibble(char c1);
16 16
17template <size_t Size, bool le = false> 17template <std::size_t Size, bool le = false>
18std::array<u8, Size> HexStringToArray(std::string_view str) { 18std::array<u8, Size> HexStringToArray(std::string_view str) {
19 std::array<u8, Size> out{}; 19 std::array<u8, Size> out{};
20 if constexpr (le) { 20 if constexpr (le) {
21 for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) 21 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
22 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 22 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
23 } else { 23 } else {
24 for (size_t i = 0; i < 2 * Size; i += 2) 24 for (std::size_t i = 0; i < 2 * Size; i += 2)
25 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 25 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
26 } 26 }
27 return out; 27 return out;
28} 28}
29 29
30template <size_t Size> 30template <std::size_t Size>
31std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { 31std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
32 std::string out; 32 std::string out;
33 for (u8 c : array) 33 for (u8 c : array)
@@ -35,7 +35,7 @@ std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
35 return out; 35 return out;
36} 36}
37 37
38std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); 38std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
39std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); 39std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
40 40
41} // namespace Common 41} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 1323f8d0f..efd776db6 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -135,7 +135,7 @@ FileBackend::FileBackend(const std::string& filename)
135void FileBackend::Write(const Entry& entry) { 135void FileBackend::Write(const Entry& entry) {
136 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 136 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
137 // know) 137 // know)
138 constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; 138 constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
139 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 139 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
140 return; 140 return;
141 } 141 }
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index b3f4b9cef..11edbf1b6 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -100,7 +100,7 @@ public:
100 100
101private: 101private:
102 FileUtil::IOFile file; 102 FileUtil::IOFile file;
103 size_t bytes_written; 103 std::size_t bytes_written;
104}; 104};
105 105
106void AddBackend(std::unique_ptr<Backend> backend); 106void AddBackend(std::unique_ptr<Backend> backend);
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 2dd331152..2eccbcd8d 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) {
71} 71}
72 72
73void Filter::SetClassLevel(Class log_class, Level level) { 73void Filter::SetClassLevel(Class log_class, Level level) {
74 class_levels[static_cast<size_t>(log_class)] = level; 74 class_levels[static_cast<std::size_t>(log_class)] = level;
75} 75}
76 76
77void Filter::ParseFilterString(std::string_view filter_view) { 77void Filter::ParseFilterString(std::string_view filter_view) {
@@ -93,7 +93,8 @@ void Filter::ParseFilterString(std::string_view filter_view) {
93} 93}
94 94
95bool Filter::CheckMessage(Class log_class, Level level) const { 95bool Filter::CheckMessage(Class log_class, Level level) const {
96 return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); 96 return static_cast<u8>(level) >=
97 static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
97} 98}
98 99
99bool Filter::IsDebug() const { 100bool Filter::IsDebug() const {
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index d5ffc5a58..773df6f2c 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -19,7 +19,7 @@ namespace Log {
19class Filter { 19class Filter {
20public: 20public:
21 /// Initializes the filter with all classes having `default_level` as the minimum level. 21 /// Initializes the filter with all classes having `default_level` as the minimum level.
22 Filter(Level default_level = Level::Info); 22 explicit Filter(Level default_level = Level::Info);
23 23
24 /// Resets the filter so that all classes have `level` as the minimum displayed level. 24 /// Resets the filter so that all classes have `level` as the minimum displayed level.
25 void ResetAll(Level level); 25 void ResetAll(Level level);
@@ -49,6 +49,6 @@ public:
49 bool IsDebug() const; 49 bool IsDebug() const;
50 50
51private: 51private:
52 std::array<Level, (size_t)Class::Count> class_levels; 52 std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
53}; 53};
54} // namespace Log 54} // namespace Log
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index e12f47f8f..4d577524f 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -12,14 +12,14 @@ namespace Log {
12/// Specifies the severity or level of detail of the log message. 12/// Specifies the severity or level of detail of the log message.
13enum class Level : u8 { 13enum class Level : u8 {
14 Trace, ///< Extremely detailed and repetitive debugging information that is likely to 14 Trace, ///< Extremely detailed and repetitive debugging information that is likely to
15 /// pollute logs. 15 ///< pollute logs.
16 Debug, ///< Less detailed debugging information. 16 Debug, ///< Less detailed debugging information.
17 Info, ///< Status information from important points during execution. 17 Info, ///< Status information from important points during execution.
18 Warning, ///< Minor or potential problems found during execution of a task. 18 Warning, ///< Minor or potential problems found during execution of a task.
19 Error, ///< Major problems found during execution of a task that prevent it from being 19 Error, ///< Major problems found during execution of a task that prevent it from being
20 /// completed. 20 ///< completed.
21 Critical, ///< Major problems during execution that threathen the stability of the entire 21 Critical, ///< Major problems during execution that threaten the stability of the entire
22 /// application. 22 ///< application.
23 23
24 Count ///< Total number of logging levels 24 Count ///< Total number of logging levels
25}; 25};
@@ -49,7 +49,7 @@ enum class Class : ClassType {
49 Kernel, ///< The HLE implementation of the CTR kernel 49 Kernel, ///< The HLE implementation of the CTR kernel
50 Kernel_SVC, ///< Kernel system calls 50 Kernel_SVC, ///< Kernel system calls
51 Service, ///< HLE implementation of system services. Each major service 51 Service, ///< HLE implementation of system services. Each major service
52 /// should have its own subclass. 52 ///< should have its own subclass.
53 Service_ACC, ///< The ACC (Accounts) service 53 Service_ACC, ///< The ACC (Accounts) service
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
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index 9609cec7c..b6d9e57c8 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -15,6 +15,6 @@ struct Entry;
15std::string FormatLogMessage(const Entry& entry); 15std::string FormatLogMessage(const Entry& entry);
16/// Formats and prints a log entry to stderr. 16/// Formats and prints a log entry to stderr.
17void PrintMessage(const Entry& entry); 17void PrintMessage(const Entry& entry);
18/// Prints the same message as `PrintMessage`, but colored acoording to the severity level. 18/// Prints the same message as `PrintMessage`, but colored according to the severity level.
19void PrintColoredMessage(const Entry& entry); 19void PrintColoredMessage(const Entry& entry);
20} // namespace Log 20} // namespace Log
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index 09462ccee..9736fb12a 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -25,7 +25,7 @@
25// This is purposely not a full wrapper for virtualalloc/mmap, but it 25// This is purposely not a full wrapper for virtualalloc/mmap, but it
26// provides exactly the primitive operations that Dolphin needs. 26// provides exactly the primitive operations that Dolphin needs.
27 27
28void* AllocateExecutableMemory(size_t size, bool low) { 28void* AllocateExecutableMemory(std::size_t size, bool low) {
29#if defined(_WIN32) 29#if defined(_WIN32)
30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
31#else 31#else
@@ -74,7 +74,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
74 return ptr; 74 return ptr;
75} 75}
76 76
77void* AllocateMemoryPages(size_t size) { 77void* AllocateMemoryPages(std::size_t size) {
78#ifdef _WIN32 78#ifdef _WIN32
79 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); 79 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
80#else 80#else
@@ -90,7 +90,7 @@ void* AllocateMemoryPages(size_t size) {
90 return ptr; 90 return ptr;
91} 91}
92 92
93void* AllocateAlignedMemory(size_t size, size_t alignment) { 93void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
94#ifdef _WIN32 94#ifdef _WIN32
95 void* ptr = _aligned_malloc(size, alignment); 95 void* ptr = _aligned_malloc(size, alignment);
96#else 96#else
@@ -109,7 +109,7 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) {
109 return ptr; 109 return ptr;
110} 110}
111 111
112void FreeMemoryPages(void* ptr, size_t size) { 112void FreeMemoryPages(void* ptr, std::size_t size) {
113 if (ptr) { 113 if (ptr) {
114#ifdef _WIN32 114#ifdef _WIN32
115 if (!VirtualFree(ptr, 0, MEM_RELEASE)) 115 if (!VirtualFree(ptr, 0, MEM_RELEASE))
@@ -130,7 +130,7 @@ void FreeAlignedMemory(void* ptr) {
130 } 130 }
131} 131}
132 132
133void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { 133void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
134#ifdef _WIN32 134#ifdef _WIN32
135 DWORD oldValue; 135 DWORD oldValue;
136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) 136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
@@ -140,7 +140,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
140#endif 140#endif
141} 141}
142 142
143void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { 143void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
144#ifdef _WIN32 144#ifdef _WIN32
145 DWORD oldValue; 145 DWORD oldValue;
146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, 146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
diff --git a/src/common/memory_util.h b/src/common/memory_util.h
index 76ca5a30c..aad071979 100644
--- a/src/common/memory_util.h
+++ b/src/common/memory_util.h
@@ -7,13 +7,13 @@
7#include <cstddef> 7#include <cstddef>
8#include <string> 8#include <string>
9 9
10void* AllocateExecutableMemory(size_t size, bool low = true); 10void* AllocateExecutableMemory(std::size_t size, bool low = true);
11void* AllocateMemoryPages(size_t size); 11void* AllocateMemoryPages(std::size_t size);
12void FreeMemoryPages(void* ptr, size_t size); 12void FreeMemoryPages(void* ptr, std::size_t size);
13void* AllocateAlignedMemory(size_t size, size_t alignment); 13void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
14void FreeAlignedMemory(void* ptr); 14void FreeAlignedMemory(void* ptr);
15void WriteProtectMemory(void* ptr, size_t size, bool executable = false); 15void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
16void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); 16void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
17std::string MemUsage(); 17std::string MemUsage();
18 18
19inline int GetPageSize() { 19inline int GetPageSize() {
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 3fa8a3bc4..68cb86cd1 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -16,7 +16,7 @@
16// Call directly after the command or use the error num. 16// Call directly after the command or use the error num.
17// This function might change the error code. 17// This function might change the error code.
18std::string GetLastErrorMsg() { 18std::string GetLastErrorMsg() {
19 static const size_t buff_size = 255; 19 static const std::size_t buff_size = 255;
20 char err_str[buff_size]; 20 char err_str[buff_size];
21 21
22#ifdef _WIN32 22#ifdef _WIN32
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
new file mode 100644
index 000000000..abe3b4dc2
--- /dev/null
+++ b/src/common/ring_buffer.h
@@ -0,0 +1,119 @@
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 <algorithm>
8#include <array>
9#include <atomic>
10#include <cstddef>
11#include <cstring>
12#include <new>
13#include <type_traits>
14#include <vector>
15#include "common/common_types.h"
16
17namespace Common {
18
19/// SPSC ring buffer
20/// @tparam T Element type
21/// @tparam capacity Number of slots in ring buffer
22/// @tparam granularity Slot size in terms of number of elements
23template <typename T, std::size_t capacity, std::size_t granularity = 1>
24class RingBuffer {
25 /// A "slot" is made of `granularity` elements of `T`.
26 static constexpr std::size_t slot_size = granularity * sizeof(T);
27 // T must be safely memcpy-able and have a trivial default constructor.
28 static_assert(std::is_trivial_v<T>);
29 // Ensure capacity is sensible.
30 static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
31 static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
32 // Ensure lock-free.
33 static_assert(std::atomic_size_t::is_always_lock_free);
34
35public:
36 /// Pushes slots into the ring buffer
37 /// @param new_slots Pointer to the slots to push
38 /// @param slot_count Number of slots to push
39 /// @returns The number of slots actually pushed
40 std::size_t Push(const void* new_slots, std::size_t slot_count) {
41 const std::size_t write_index = m_write_index.load();
42 const std::size_t slots_free = capacity + m_read_index.load() - write_index;
43 const std::size_t push_count = std::min(slot_count, slots_free);
44
45 const std::size_t pos = write_index % capacity;
46 const std::size_t first_copy = std::min(capacity - pos, push_count);
47 const std::size_t second_copy = push_count - first_copy;
48
49 const char* in = static_cast<const char*>(new_slots);
50 std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
51 in += first_copy * slot_size;
52 std::memcpy(m_data.data(), in, second_copy * slot_size);
53
54 m_write_index.store(write_index + push_count);
55
56 return push_count;
57 }
58
59 std::size_t Push(const std::vector<T>& input) {
60 return Push(input.data(), input.size());
61 }
62
63 /// Pops slots from the ring buffer
64 /// @param output Where to store the popped slots
65 /// @param max_slots Maximum number of slots to pop
66 /// @returns The number of slots actually popped
67 std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
68 const std::size_t read_index = m_read_index.load();
69 const std::size_t slots_filled = m_write_index.load() - read_index;
70 const std::size_t pop_count = std::min(slots_filled, max_slots);
71
72 const std::size_t pos = read_index % capacity;
73 const std::size_t first_copy = std::min(capacity - pos, pop_count);
74 const std::size_t second_copy = pop_count - first_copy;
75
76 char* out = static_cast<char*>(output);
77 std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
78 out += first_copy * slot_size;
79 std::memcpy(out, m_data.data(), second_copy * slot_size);
80
81 m_read_index.store(read_index + pop_count);
82
83 return pop_count;
84 }
85
86 std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
87 std::vector<T> out(std::min(max_slots, capacity) * granularity);
88 const std::size_t count = Pop(out.data(), out.size() / granularity);
89 out.resize(count * granularity);
90 return out;
91 }
92
93 /// @returns Number of slots used
94 std::size_t Size() const {
95 return m_write_index.load() - m_read_index.load();
96 }
97
98 /// @returns Maximum size of ring buffer
99 constexpr std::size_t Capacity() const {
100 return capacity;
101 }
102
103private:
104 // It is important to align the below variables for performance reasons:
105 // Having them on the same cache-line would result in false-sharing between them.
106 // TODO: Remove this ifdef whenever clang and GCC support
107 // std::hardware_destructive_interference_size.
108#if defined(_MSC_VER) && _MSC_VER >= 1911
109 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
110 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
111#else
112 alignas(128) std::atomic_size_t m_read_index{0};
113 alignas(128) std::atomic_size_t m_write_index{0};
114#endif
115
116 std::array<T, granularity * capacity> m_data;
117};
118
119} // namespace Common
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 4083095d5..2b1727769 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -9,6 +9,8 @@
9#define GIT_DESC "@GIT_DESC@" 9#define GIT_DESC "@GIT_DESC@"
10#define BUILD_NAME "@REPO_NAME@" 10#define BUILD_NAME "@REPO_NAME@"
11#define BUILD_DATE "@BUILD_DATE@" 11#define BUILD_DATE "@BUILD_DATE@"
12#define BUILD_FULLNAME "@BUILD_FULLNAME@"
13#define BUILD_VERSION "@BUILD_VERSION@"
12 14
13namespace Common { 15namespace Common {
14 16
@@ -17,6 +19,8 @@ const char g_scm_branch[] = GIT_BRANCH;
17const char g_scm_desc[] = GIT_DESC; 19const char g_scm_desc[] = GIT_DESC;
18const char g_build_name[] = BUILD_NAME; 20const char g_build_name[] = BUILD_NAME;
19const char g_build_date[] = BUILD_DATE; 21const char g_build_date[] = BUILD_DATE;
22const char g_build_fullname[] = BUILD_FULLNAME;
23const char g_build_version[] = BUILD_VERSION;
20 24
21} // namespace 25} // namespace
22 26
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index db0f4a947..af9a9daed 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -11,5 +11,7 @@ extern const char g_scm_branch[];
11extern const char g_scm_desc[]; 11extern const char g_scm_desc[];
12extern const char g_build_name[]; 12extern const char g_build_name[];
13extern const char g_build_date[]; 13extern const char g_build_date[];
14extern const char g_build_fullname[];
15extern const char g_build_version[];
14 16
15} // namespace Common 17} // namespace Common
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 0ca663032..c9a5425a7 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -37,7 +37,7 @@ std::string ToUpper(std::string str) {
37} 37}
38 38
39// For Debugging. Read out an u8 array. 39// For Debugging. Read out an u8 array.
40std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) { 40std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
41 std::ostringstream oss; 41 std::ostringstream oss;
42 oss << std::setfill('0') << std::hex; 42 oss << std::setfill('0') << std::hex;
43 43
@@ -60,7 +60,7 @@ std::string StringFromBuffer(const std::vector<u8>& data) {
60 60
61// Turns " hej " into "hej". Also handles tabs. 61// Turns " hej " into "hej". Also handles tabs.
62std::string StripSpaces(const std::string& str) { 62std::string StripSpaces(const std::string& str) {
63 const size_t s = str.find_first_not_of(" \t\r\n"); 63 const std::size_t s = str.find_first_not_of(" \t\r\n");
64 64
65 if (str.npos != s) 65 if (str.npos != s)
66 return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); 66 return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
@@ -121,10 +121,10 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
121 if (full_path.empty()) 121 if (full_path.empty())
122 return false; 122 return false;
123 123
124 size_t dir_end = full_path.find_last_of("/" 124 std::size_t dir_end = full_path.find_last_of("/"
125// windows needs the : included for something like just "C:" to be considered a directory 125// windows needs the : included for something like just "C:" to be considered a directory
126#ifdef _WIN32 126#ifdef _WIN32
127 "\\:" 127 "\\:"
128#endif 128#endif
129 ); 129 );
130 if (std::string::npos == dir_end) 130 if (std::string::npos == dir_end)
@@ -132,7 +132,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
132 else 132 else
133 dir_end += 1; 133 dir_end += 1;
134 134
135 size_t fname_end = full_path.rfind('.'); 135 std::size_t fname_end = full_path.rfind('.');
136 if (fname_end < dir_end || std::string::npos == fname_end) 136 if (fname_end < dir_end || std::string::npos == fname_end)
137 fname_end = full_path.size(); 137 fname_end = full_path.size();
138 138
@@ -172,7 +172,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
172} 172}
173 173
174std::string TabsToSpaces(int tab_size, std::string in) { 174std::string TabsToSpaces(int tab_size, std::string in) {
175 size_t i = 0; 175 std::size_t i = 0;
176 176
177 while ((i = in.find('\t')) != std::string::npos) { 177 while ((i = in.find('\t')) != std::string::npos) {
178 in.replace(i, 1, tab_size, ' '); 178 in.replace(i, 1, tab_size, ' ');
@@ -182,7 +182,7 @@ std::string TabsToSpaces(int tab_size, std::string in) {
182} 182}
183 183
184std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { 184std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
185 size_t pos = 0; 185 std::size_t pos = 0;
186 186
187 if (src == dest) 187 if (src == dest)
188 return result; 188 return result;
@@ -280,22 +280,22 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
280 return {}; 280 return {};
281 } 281 }
282 282
283 const size_t in_bytes = sizeof(T) * input.size(); 283 const std::size_t in_bytes = sizeof(T) * input.size();
284 // Multiply by 4, which is the max number of bytes to encode a codepoint 284 // Multiply by 4, which is the max number of bytes to encode a codepoint
285 const size_t out_buffer_size = 4 * in_bytes; 285 const std::size_t out_buffer_size = 4 * in_bytes;
286 286
287 std::string out_buffer(out_buffer_size, '\0'); 287 std::string out_buffer(out_buffer_size, '\0');
288 288
289 auto src_buffer = &input[0]; 289 auto src_buffer = &input[0];
290 size_t src_bytes = in_bytes; 290 std::size_t src_bytes = in_bytes;
291 auto dst_buffer = &out_buffer[0]; 291 auto dst_buffer = &out_buffer[0];
292 size_t dst_bytes = out_buffer.size(); 292 std::size_t dst_bytes = out_buffer.size();
293 293
294 while (0 != src_bytes) { 294 while (0 != src_bytes) {
295 size_t const iconv_result = 295 std::size_t const iconv_result =
296 iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); 296 iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes);
297 297
298 if (static_cast<size_t>(-1) == iconv_result) { 298 if (static_cast<std::size_t>(-1) == iconv_result) {
299 if (EILSEQ == errno || EINVAL == errno) { 299 if (EILSEQ == errno || EINVAL == errno) {
300 // Try to skip the bad character 300 // Try to skip the bad character
301 if (0 != src_bytes) { 301 if (0 != src_bytes) {
@@ -326,22 +326,22 @@ std::u16string UTF8ToUTF16(const std::string& input) {
326 return {}; 326 return {};
327 } 327 }
328 328
329 const size_t in_bytes = sizeof(char) * input.size(); 329 const std::size_t in_bytes = sizeof(char) * input.size();
330 // Multiply by 4, which is the max number of bytes to encode a codepoint 330 // Multiply by 4, which is the max number of bytes to encode a codepoint
331 const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; 331 const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
332 332
333 std::u16string out_buffer(out_buffer_size, char16_t{}); 333 std::u16string out_buffer(out_buffer_size, char16_t{});
334 334
335 char* src_buffer = const_cast<char*>(&input[0]); 335 char* src_buffer = const_cast<char*>(&input[0]);
336 size_t src_bytes = in_bytes; 336 std::size_t src_bytes = in_bytes;
337 char* dst_buffer = (char*)(&out_buffer[0]); 337 char* dst_buffer = (char*)(&out_buffer[0]);
338 size_t dst_bytes = out_buffer.size(); 338 std::size_t dst_bytes = out_buffer.size();
339 339
340 while (0 != src_bytes) { 340 while (0 != src_bytes) {
341 size_t const iconv_result = 341 std::size_t const iconv_result =
342 iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); 342 iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes);
343 343
344 if (static_cast<size_t>(-1) == iconv_result) { 344 if (static_cast<std::size_t>(-1) == iconv_result) {
345 if (EILSEQ == errno || EINVAL == errno) { 345 if (EILSEQ == errno || EINVAL == errno) {
346 // Try to skip the bad character 346 // Try to skip the bad character
347 if (0 != src_bytes) { 347 if (0 != src_bytes) {
@@ -381,8 +381,8 @@ std::string SHIFTJISToUTF8(const std::string& input) {
381 381
382#endif 382#endif
383 383
384std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len) { 384std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) {
385 size_t len = 0; 385 std::size_t len = 0;
386 while (len < max_len && buffer[len] != '\0') 386 while (len < max_len && buffer[len] != '\0')
387 ++len; 387 ++len;
388 388
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 4a2143b59..dcca6bc38 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -19,7 +19,7 @@ std::string ToLower(std::string str);
19/// Make a string uppercase 19/// Make a string uppercase
20std::string ToUpper(std::string str); 20std::string ToUpper(std::string str);
21 21
22std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true); 22std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
23 23
24std::string StringFromBuffer(const std::vector<u8>& data); 24std::string StringFromBuffer(const std::vector<u8>& data);
25 25
@@ -118,7 +118,7 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
118 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't 118 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
119 * NUL-terminated then the string ends at max_len characters. 119 * NUL-terminated then the string ends at max_len characters.
120 */ 120 */
121std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); 121std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
122 122
123/** 123/**
124 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's 124 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
diff --git a/src/common/thread.h b/src/common/thread.h
index 9465e1de7..12a1c095c 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -60,12 +60,12 @@ private:
60 60
61class Barrier { 61class Barrier {
62public: 62public:
63 explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {} 63 explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {}
64 64
65 /// Blocks until all "count" threads have called Sync() 65 /// Blocks until all "count" threads have called Sync()
66 void Sync() { 66 void Sync() {
67 std::unique_lock<std::mutex> lk(mutex); 67 std::unique_lock<std::mutex> lk(mutex);
68 const size_t current_generation = generation; 68 const std::size_t current_generation = generation;
69 69
70 if (++waiting == count) { 70 if (++waiting == count) {
71 generation++; 71 generation++;
@@ -80,9 +80,9 @@ public:
80private: 80private:
81 std::condition_variable condvar; 81 std::condition_variable condvar;
82 std::mutex mutex; 82 std::mutex mutex;
83 const size_t count; 83 const std::size_t count;
84 size_t waiting; 84 std::size_t waiting;
85 size_t generation; // Incremented once each time the barrier is used 85 std::size_t generation; // Incremented once each time the barrier is used
86}; 86};
87 87
88void SleepCurrentThread(int ms); 88void SleepCurrentThread(int ms);
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index 927da9187..636a5c0f9 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -97,7 +97,7 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
97 Xbyak::util::xmm15, 97 Xbyak::util::xmm15,
98}); 98});
99 99
100constexpr size_t ABI_SHADOW_SPACE = 0x20; 100constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
101 101
102#else 102#else
103 103
@@ -147,22 +147,23 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
147 Xbyak::util::r15, 147 Xbyak::util::r15,
148}); 148});
149 149
150constexpr size_t ABI_SHADOW_SPACE = 0; 150constexpr std::size_t ABI_SHADOW_SPACE = 0;
151 151
152#endif 152#endif
153 153
154inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, 154inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
155 s32* out_subtraction, s32* out_xmm_offset) { 155 std::size_t needed_frame_size, s32* out_subtraction,
156 s32* out_xmm_offset) {
156 int count = (regs & ABI_ALL_GPRS).Count(); 157 int count = (regs & ABI_ALL_GPRS).Count();
157 rsp_alignment -= count * 8; 158 rsp_alignment -= count * 8;
158 size_t subtraction = 0; 159 std::size_t subtraction = 0;
159 int xmm_count = (regs & ABI_ALL_XMMS).Count(); 160 int xmm_count = (regs & ABI_ALL_XMMS).Count();
160 if (xmm_count) { 161 if (xmm_count) {
161 // If we have any XMMs to save, we must align the stack here. 162 // If we have any XMMs to save, we must align the stack here.
162 subtraction = rsp_alignment & 0xF; 163 subtraction = rsp_alignment & 0xF;
163 } 164 }
164 subtraction += 0x10 * xmm_count; 165 subtraction += 0x10 * xmm_count;
165 size_t xmm_base_subtraction = subtraction; 166 std::size_t xmm_base_subtraction = subtraction;
166 subtraction += needed_frame_size; 167 subtraction += needed_frame_size;
167 subtraction += ABI_SHADOW_SPACE; 168 subtraction += ABI_SHADOW_SPACE;
168 // Final alignment. 169 // Final alignment.
@@ -173,8 +174,9 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t n
173 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); 174 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
174} 175}
175 176
176inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, 177inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
177 size_t rsp_alignment, size_t needed_frame_size = 0) { 178 std::size_t rsp_alignment,
179 std::size_t needed_frame_size = 0) {
178 s32 subtraction, xmm_offset; 180 s32 subtraction, xmm_offset;
179 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); 181 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
180 182
@@ -195,7 +197,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet
195} 197}
196 198
197inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, 199inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
198 size_t rsp_alignment, size_t needed_frame_size = 0) { 200 std::size_t rsp_alignment,
201 std::size_t needed_frame_size = 0) {
199 s32 subtraction, xmm_offset; 202 s32 subtraction, xmm_offset;
200 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); 203 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
201 204
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index 02323a017..5cc8a8c76 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
34template <typename T> 34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { 35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); 36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
37 size_t addr = reinterpret_cast<size_t>(f); 37 std::size_t addr = reinterpret_cast<std::size_t>(f);
38 if (IsWithin2G(code, addr)) { 38 if (IsWithin2G(code, addr)) {
39 code.call(f); 39 code.call(f);
40 } else { 40 } else {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a74270a0f..26f727d96 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -35,8 +35,12 @@ add_library(core STATIC
35 file_sys/mode.h 35 file_sys/mode.h
36 file_sys/nca_metadata.cpp 36 file_sys/nca_metadata.cpp
37 file_sys/nca_metadata.h 37 file_sys/nca_metadata.h
38 file_sys/nca_patch.cpp
39 file_sys/nca_patch.h
38 file_sys/partition_filesystem.cpp 40 file_sys/partition_filesystem.cpp
39 file_sys/partition_filesystem.h 41 file_sys/partition_filesystem.h
42 file_sys/patch_manager.cpp
43 file_sys/patch_manager.h
40 file_sys/program_metadata.cpp 44 file_sys/program_metadata.cpp
41 file_sys/program_metadata.h 45 file_sys/program_metadata.h
42 file_sys/registered_cache.cpp 46 file_sys/registered_cache.cpp
@@ -49,6 +53,8 @@ add_library(core STATIC
49 file_sys/savedata_factory.h 53 file_sys/savedata_factory.h
50 file_sys/sdmc_factory.cpp 54 file_sys/sdmc_factory.cpp
51 file_sys/sdmc_factory.h 55 file_sys/sdmc_factory.h
56 file_sys/submission_package.cpp
57 file_sys/submission_package.h
52 file_sys/vfs.cpp 58 file_sys/vfs.cpp
53 file_sys/vfs.h 59 file_sys/vfs.h
54 file_sys/vfs_concat.cpp 60 file_sys/vfs_concat.cpp
@@ -359,6 +365,8 @@ add_library(core STATIC
359 loader/nro.h 365 loader/nro.h
360 loader/nso.cpp 366 loader/nso.cpp
361 loader/nso.h 367 loader/nso.h
368 loader/nsp.cpp
369 loader/nsp.h
362 loader/xci.cpp 370 loader/xci.cpp
363 loader/xci.h 371 loader/xci.h
364 memory.cpp 372 memory.cpp
@@ -380,7 +388,7 @@ add_library(core STATIC
380create_target_directory_groups(core) 388create_target_directory_groups(core)
381 389
382target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 390target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
383target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn) 391target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
384 392
385if (ARCHITECTURE_x86_64) 393if (ARCHITECTURE_x86_64)
386 target_sources(core PRIVATE 394 target_sources(core PRIVATE
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c368745b1..16d528994 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,11 +6,14 @@
6 6
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/vm_manager.h" 9
10namespace Kernel {
11enum class VMAPermission : u8;
12}
10 13
11namespace Core { 14namespace Core {
12 15
13/// Generic ARM11 CPU interface 16/// Generic ARMv8 CPU interface
14class ARM_Interface : NonCopyable { 17class ARM_Interface : NonCopyable {
15public: 18public:
16 virtual ~ARM_Interface() {} 19 virtual ~ARM_Interface() {}
@@ -19,9 +22,9 @@ public:
19 std::array<u64, 31> cpu_registers; 22 std::array<u64, 31> cpu_registers;
20 u64 sp; 23 u64 sp;
21 u64 pc; 24 u64 pc;
22 u64 cpsr; 25 u64 pstate;
23 std::array<u128, 32> fpu_registers; 26 std::array<u128, 32> vector_registers;
24 u64 fpscr; 27 u64 fpcr;
25 }; 28 };
26 29
27 /// Runs the CPU until an event happens 30 /// Runs the CPU until an event happens
@@ -31,11 +34,11 @@ public:
31 virtual void Step() = 0; 34 virtual void Step() = 0;
32 35
33 /// Maps a backing memory region for the CPU 36 /// Maps a backing memory region for the CPU
34 virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, 37 virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
35 Kernel::VMAPermission perms) = 0; 38 Kernel::VMAPermission perms) = 0;
36 39
37 /// Unmaps a region of memory that was previously mapped using MapBackingMemory 40 /// Unmaps a region of memory that was previously mapped using MapBackingMemory
38 virtual void UnmapMemory(VAddr address, size_t size) = 0; 41 virtual void UnmapMemory(VAddr address, std::size_t size) = 0;
39 42
40 /// Clear all instruction cache 43 /// Clear all instruction cache
41 virtual void ClearInstructionCache() = 0; 44 virtual void ClearInstructionCache() = 0;
@@ -69,42 +72,50 @@ public:
69 */ 72 */
70 virtual void SetReg(int index, u64 value) = 0; 73 virtual void SetReg(int index, u64 value) = 0;
71 74
72 virtual u128 GetExtReg(int index) const = 0;
73
74 virtual void SetExtReg(int index, u128 value) = 0;
75
76 /** 75 /**
77 * Gets the value of a VFP register 76 * Gets the value of a specified vector register.
78 * @param index Register index (0-31) 77 *
79 * @return Returns the value in the register 78 * @param index The index of the vector register.
79 * @return the value within the vector register.
80 */ 80 */
81 virtual u32 GetVFPReg(int index) const = 0; 81 virtual u128 GetVectorReg(int index) const = 0;
82 82
83 /** 83 /**
84 * Sets a VFP register to the given value 84 * Sets a given value into a vector register.
85 * @param index Register index (0-31) 85 *
86 * @param value Value to set register to 86 * @param index The index of the vector register.
87 * @param value The new value to place in the register.
87 */ 88 */
88 virtual void SetVFPReg(int index, u32 value) = 0; 89 virtual void SetVectorReg(int index, u128 value) = 0;
89 90
90 /** 91 /**
91 * Get the current CPSR register 92 * Get the current PSTATE register
92 * @return Returns the value of the CPSR register 93 * @return Returns the value of the PSTATE register
93 */ 94 */
94 virtual u32 GetCPSR() const = 0; 95 virtual u32 GetPSTATE() const = 0;
95 96
96 /** 97 /**
97 * Set the current CPSR register 98 * Set the current PSTATE register
98 * @param cpsr Value to set CPSR to 99 * @param pstate Value to set PSTATE to
99 */ 100 */
100 virtual void SetCPSR(u32 cpsr) = 0; 101 virtual void SetPSTATE(u32 pstate) = 0;
101 102
102 virtual VAddr GetTlsAddress() const = 0; 103 virtual VAddr GetTlsAddress() const = 0;
103 104
104 virtual void SetTlsAddress(VAddr address) = 0; 105 virtual void SetTlsAddress(VAddr address) = 0;
105 106
107 /**
108 * Gets the value within the TPIDR_EL0 (read/write software thread ID) register.
109 *
110 * @return the value within the register.
111 */
106 virtual u64 GetTPIDR_EL0() const = 0; 112 virtual u64 GetTPIDR_EL0() const = 0;
107 113
114 /**
115 * Sets a new value within the TPIDR_EL0 (read/write software thread ID) register.
116 *
117 * @param value The new value to place in the register.
118 */
108 virtual void SetTPIDR_EL0(u64 value) = 0; 119 virtual void SetTPIDR_EL0(u64 value) = 0;
109 120
110 /** 121 /**
@@ -119,6 +130,7 @@ public:
119 */ 130 */
120 virtual void LoadContext(const ThreadContext& ctx) = 0; 131 virtual void LoadContext(const ThreadContext& ctx) = 0;
121 132
133 /// Clears the exclusive monitor's state.
122 virtual void ClearExclusiveState() = 0; 134 virtual void ClearExclusiveState() = 0;
123 135
124 /// Prepare core for thread reschedule (if needed to correctly handle state) 136 /// Prepare core for thread reschedule (if needed to correctly handle state)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index de44ccebd..7be5a38de 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -7,12 +7,15 @@
7#include <dynarmic/A64/a64.h> 7#include <dynarmic/A64/a64.h>
8#include <dynarmic/A64/config.h> 8#include <dynarmic/A64/config.h>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/microprofile.h"
10#include "core/arm/dynarmic/arm_dynarmic.h" 11#include "core/arm/dynarmic/arm_dynarmic.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/core_cpu.h" 13#include "core/core_cpu.h"
13#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/gdbstub/gdbstub.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/svc.h" 17#include "core/hle/kernel/svc.h"
18#include "core/hle/kernel/vm_manager.h"
16#include "core/memory.h" 19#include "core/memory.h"
17 20
18namespace Core { 21namespace Core {
@@ -57,7 +60,7 @@ public:
57 Memory::Write64(vaddr + 8, value[1]); 60 Memory::Write64(vaddr + 8, value[1]);
58 } 61 }
59 62
60 void InterpreterFallback(u64 pc, size_t num_instructions) override { 63 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
61 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, 64 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
62 num_instructions, MemoryReadCode(pc)); 65 num_instructions, MemoryReadCode(pc));
63 66
@@ -78,9 +81,20 @@ public:
78 case Dynarmic::A64::Exception::SendEventLocal: 81 case Dynarmic::A64::Exception::SendEventLocal:
79 case Dynarmic::A64::Exception::Yield: 82 case Dynarmic::A64::Exception::Yield:
80 return; 83 return;
84 case Dynarmic::A64::Exception::Breakpoint:
85 if (GDBStub::IsServerEnabled()) {
86 parent.jit->HaltExecution();
87 parent.SetPC(pc);
88 Kernel::Thread* thread = Kernel::GetCurrentThread();
89 parent.SaveContext(thread->context);
90 GDBStub::Break();
91 GDBStub::SendTrap(thread, 5);
92 return;
93 }
94 [[fallthrough]];
81 default: 95 default:
82 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", 96 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})",
83 static_cast<size_t>(exception), pc); 97 static_cast<std::size_t>(exception), pc);
84 } 98 }
85 } 99 }
86 100
@@ -109,7 +123,7 @@ public:
109 } 123 }
110 124
111 ARM_Dynarmic& parent; 125 ARM_Dynarmic& parent;
112 size_t num_interpreted_instructions = 0; 126 std::size_t num_interpreted_instructions = 0;
113 u64 tpidrro_el0 = 0; 127 u64 tpidrro_el0 = 0;
114 u64 tpidr_el0 = 0; 128 u64 tpidr_el0 = 0;
115}; 129};
@@ -143,7 +157,10 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
143 return std::make_unique<Dynarmic::A64::Jit>(config); 157 return std::make_unique<Dynarmic::A64::Jit>(config);
144} 158}
145 159
160MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
161
146void ARM_Dynarmic::Run() { 162void ARM_Dynarmic::Run() {
163 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
147 ASSERT(Memory::GetCurrentPageTable() == current_page_table); 164 ASSERT(Memory::GetCurrentPageTable() == current_page_table);
148 165
149 jit->Run(); 166 jit->Run();
@@ -153,7 +170,8 @@ void ARM_Dynarmic::Step() {
153 cb->InterpreterFallback(jit->GetPC(), 1); 170 cb->InterpreterFallback(jit->GetPC(), 1);
154} 171}
155 172
156ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) 173ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
174 std::size_t core_index)
157 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 175 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
158 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { 176 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
159 ThreadContext ctx; 177 ThreadContext ctx;
@@ -164,12 +182,12 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
164 182
165ARM_Dynarmic::~ARM_Dynarmic() = default; 183ARM_Dynarmic::~ARM_Dynarmic() = default;
166 184
167void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory, 185void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
168 Kernel::VMAPermission perms) { 186 Kernel::VMAPermission perms) {
169 inner_unicorn.MapBackingMemory(address, size, memory, perms); 187 inner_unicorn.MapBackingMemory(address, size, memory, perms);
170} 188}
171 189
172void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) { 190void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) {
173 inner_unicorn.UnmapMemory(address, size); 191 inner_unicorn.UnmapMemory(address, size);
174} 192}
175 193
@@ -189,29 +207,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) {
189 jit->SetRegister(index, value); 207 jit->SetRegister(index, value);
190} 208}
191 209
192u128 ARM_Dynarmic::GetExtReg(int index) const { 210u128 ARM_Dynarmic::GetVectorReg(int index) const {
193 return jit->GetVector(index); 211 return jit->GetVector(index);
194} 212}
195 213
196void ARM_Dynarmic::SetExtReg(int index, u128 value) { 214void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
197 jit->SetVector(index, value); 215 jit->SetVector(index, value);
198} 216}
199 217
200u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const { 218u32 ARM_Dynarmic::GetPSTATE() const {
201 UNIMPLEMENTED();
202 return {};
203}
204
205void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
206 UNIMPLEMENTED();
207}
208
209u32 ARM_Dynarmic::GetCPSR() const {
210 return jit->GetPstate(); 219 return jit->GetPstate();
211} 220}
212 221
213void ARM_Dynarmic::SetCPSR(u32 cpsr) { 222void ARM_Dynarmic::SetPSTATE(u32 pstate) {
214 jit->SetPstate(cpsr); 223 jit->SetPstate(pstate);
215} 224}
216 225
217u64 ARM_Dynarmic::GetTlsAddress() const { 226u64 ARM_Dynarmic::GetTlsAddress() const {
@@ -234,18 +243,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
234 ctx.cpu_registers = jit->GetRegisters(); 243 ctx.cpu_registers = jit->GetRegisters();
235 ctx.sp = jit->GetSP(); 244 ctx.sp = jit->GetSP();
236 ctx.pc = jit->GetPC(); 245 ctx.pc = jit->GetPC();
237 ctx.cpsr = jit->GetPstate(); 246 ctx.pstate = jit->GetPstate();
238 ctx.fpu_registers = jit->GetVectors(); 247 ctx.vector_registers = jit->GetVectors();
239 ctx.fpscr = jit->GetFpcr(); 248 ctx.fpcr = jit->GetFpcr();
240} 249}
241 250
242void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { 251void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
243 jit->SetRegisters(ctx.cpu_registers); 252 jit->SetRegisters(ctx.cpu_registers);
244 jit->SetSP(ctx.sp); 253 jit->SetSP(ctx.sp);
245 jit->SetPC(ctx.pc); 254 jit->SetPC(ctx.pc);
246 jit->SetPstate(static_cast<u32>(ctx.cpsr)); 255 jit->SetPstate(static_cast<u32>(ctx.pstate));
247 jit->SetVectors(ctx.fpu_registers); 256 jit->SetVectors(ctx.vector_registers);
248 jit->SetFpcr(static_cast<u32>(ctx.fpscr)); 257 jit->SetFpcr(static_cast<u32>(ctx.fpcr));
249} 258}
250 259
251void ARM_Dynarmic::PrepareReschedule() { 260void ARM_Dynarmic::PrepareReschedule() {
@@ -265,10 +274,10 @@ void ARM_Dynarmic::PageTableChanged() {
265 current_page_table = Memory::GetCurrentPageTable(); 274 current_page_table = Memory::GetCurrentPageTable();
266} 275}
267 276
268DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {} 277DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
269DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; 278DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
270 279
271void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) { 280void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
272 // Size doesn't actually matter. 281 // Size doesn't actually matter.
273 monitor.Mark(core_index, addr, 16); 282 monitor.Mark(core_index, addr, 16);
274} 283}
@@ -277,30 +286,30 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
277 monitor.Clear(); 286 monitor.Clear();
278} 287}
279 288
280bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) { 289bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
281 return monitor.DoExclusiveOperation(core_index, vaddr, 1, 290 return monitor.DoExclusiveOperation(core_index, vaddr, 1,
282 [&] { Memory::Write8(vaddr, value); }); 291 [&] { Memory::Write8(vaddr, value); });
283} 292}
284 293
285bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) { 294bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
286 return monitor.DoExclusiveOperation(core_index, vaddr, 2, 295 return monitor.DoExclusiveOperation(core_index, vaddr, 2,
287 [&] { Memory::Write16(vaddr, value); }); 296 [&] { Memory::Write16(vaddr, value); });
288} 297}
289 298
290bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) { 299bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
291 return monitor.DoExclusiveOperation(core_index, vaddr, 4, 300 return monitor.DoExclusiveOperation(core_index, vaddr, 4,
292 [&] { Memory::Write32(vaddr, value); }); 301 [&] { Memory::Write32(vaddr, value); });
293} 302}
294 303
295bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) { 304bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
296 return monitor.DoExclusiveOperation(core_index, vaddr, 8, 305 return monitor.DoExclusiveOperation(core_index, vaddr, 8,
297 [&] { Memory::Write64(vaddr, value); }); 306 [&] { Memory::Write64(vaddr, value); });
298} 307}
299 308
300bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) { 309bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
301 return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { 310 return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
302 Memory::Write64(vaddr, value[0]); 311 Memory::Write64(vaddr + 0, value[0]);
303 Memory::Write64(vaddr, value[1]); 312 Memory::Write64(vaddr + 8, value[1]);
304 }); 313 });
305} 314}
306 315
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 3bdfd8cd9..4ee92ee27 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,6 +12,10 @@
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14 14
15namespace Memory {
16struct PageTable;
17}
18
15namespace Core { 19namespace Core {
16 20
17class ARM_Dynarmic_Callbacks; 21class ARM_Dynarmic_Callbacks;
@@ -19,24 +23,22 @@ class DynarmicExclusiveMonitor;
19 23
20class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
21public: 25public:
22 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index); 26 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index);
23 ~ARM_Dynarmic(); 27 ~ARM_Dynarmic();
24 28
25 void MapBackingMemory(VAddr address, size_t size, u8* memory, 29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
26 Kernel::VMAPermission perms) override; 30 Kernel::VMAPermission perms) override;
27 void UnmapMemory(u64 address, size_t size) override; 31 void UnmapMemory(u64 address, std::size_t size) override;
28 void SetPC(u64 pc) override; 32 void SetPC(u64 pc) override;
29 u64 GetPC() const override; 33 u64 GetPC() const override;
30 u64 GetReg(int index) const override; 34 u64 GetReg(int index) const override;
31 void SetReg(int index, u64 value) override; 35 void SetReg(int index, u64 value) override;
32 u128 GetExtReg(int index) const override; 36 u128 GetVectorReg(int index) const override;
33 void SetExtReg(int index, u128 value) override; 37 void SetVectorReg(int index, u128 value) override;
34 u32 GetVFPReg(int index) const override; 38 u32 GetPSTATE() const override;
35 void SetVFPReg(int index, u32 value) override; 39 void SetPSTATE(u32 pstate) override;
36 u32 GetCPSR() const override;
37 void Run() override; 40 void Run() override;
38 void Step() override; 41 void Step() override;
39 void SetCPSR(u32 cpsr) override;
40 VAddr GetTlsAddress() const override; 42 VAddr GetTlsAddress() const override;
41 void SetTlsAddress(VAddr address) override; 43 void SetTlsAddress(VAddr address) override;
42 void SetTPIDR_EL0(u64 value) override; 44 void SetTPIDR_EL0(u64 value) override;
@@ -59,7 +61,7 @@ private:
59 std::unique_ptr<Dynarmic::A64::Jit> jit; 61 std::unique_ptr<Dynarmic::A64::Jit> jit;
60 ARM_Unicorn inner_unicorn; 62 ARM_Unicorn inner_unicorn;
61 63
62 size_t core_index; 64 std::size_t core_index;
63 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; 65 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
64 66
65 Memory::PageTable* current_page_table = nullptr; 67 Memory::PageTable* current_page_table = nullptr;
@@ -67,17 +69,17 @@ private:
67 69
68class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 70class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
69public: 71public:
70 explicit DynarmicExclusiveMonitor(size_t core_count); 72 explicit DynarmicExclusiveMonitor(std::size_t core_count);
71 ~DynarmicExclusiveMonitor(); 73 ~DynarmicExclusiveMonitor();
72 74
73 void SetExclusive(size_t core_index, VAddr addr) override; 75 void SetExclusive(std::size_t core_index, VAddr addr) override;
74 void ClearExclusive() override; 76 void ClearExclusive() override;
75 77
76 bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override; 78 bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
77 bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override; 79 bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
78 bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override; 80 bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
79 bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override; 81 bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
80 bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override; 82 bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
81 83
82private: 84private:
83 friend class ARM_Dynarmic; 85 friend class ARM_Dynarmic;
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index 6f9b51573..f59aca667 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -12,14 +12,14 @@ class ExclusiveMonitor {
12public: 12public:
13 virtual ~ExclusiveMonitor(); 13 virtual ~ExclusiveMonitor();
14 14
15 virtual void SetExclusive(size_t core_index, VAddr addr) = 0; 15 virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0;
16 virtual void ClearExclusive() = 0; 16 virtual void ClearExclusive() = 0;
17 17
18 virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0; 18 virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
19 virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0; 19 virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
20 virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0; 20 virtual bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) = 0;
21 virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0; 21 virtual bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) = 0;
22 virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0; 22 virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0;
23}; 23};
24 24
25} // namespace Core 25} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 307f12198..e218a0b15 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -90,12 +90,12 @@ ARM_Unicorn::~ARM_Unicorn() {
90 CHECKED(uc_close(uc)); 90 CHECKED(uc_close(uc));
91} 91}
92 92
93void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory, 93void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
94 Kernel::VMAPermission perms) { 94 Kernel::VMAPermission perms) {
95 CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); 95 CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
96} 96}
97 97
98void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) { 98void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) {
99 CHECKED(uc_mem_unmap(uc, address, size)); 99 CHECKED(uc_mem_unmap(uc, address, size));
100} 100}
101 101
@@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) {
131 CHECKED(uc_reg_write(uc, treg, &val)); 131 CHECKED(uc_reg_write(uc, treg, &val));
132} 132}
133 133
134u128 ARM_Unicorn::GetExtReg(int /*index*/) const { 134u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
135 UNIMPLEMENTED(); 135 UNIMPLEMENTED();
136 static constexpr u128 res{}; 136 static constexpr u128 res{};
137 return res; 137 return res;
138} 138}
139 139
140void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) { 140void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
141 UNIMPLEMENTED(); 141 UNIMPLEMENTED();
142} 142}
143 143
144u32 ARM_Unicorn::GetVFPReg(int /*index*/) const { 144u32 ARM_Unicorn::GetPSTATE() const {
145 UNIMPLEMENTED();
146 return {};
147}
148
149void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) {
150 UNIMPLEMENTED();
151}
152
153u32 ARM_Unicorn::GetCPSR() const {
154 u64 nzcv{}; 145 u64 nzcv{};
155 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); 146 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
156 return static_cast<u32>(nzcv); 147 return static_cast<u32>(nzcv);
157} 148}
158 149
159void ARM_Unicorn::SetCPSR(u32 cpsr) { 150void ARM_Unicorn::SetPSTATE(u32 pstate) {
160 u64 nzcv = cpsr; 151 u64 nzcv = pstate;
161 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); 152 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
162} 153}
163 154
@@ -193,10 +184,10 @@ void ARM_Unicorn::Step() {
193 ExecuteInstructions(1); 184 ExecuteInstructions(1);
194} 185}
195 186
196MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); 187MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
197 188
198void ARM_Unicorn::ExecuteInstructions(int num_instructions) { 189void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
199 MICROPROFILE_SCOPE(ARM_Jit); 190 MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
200 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 191 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
201 CoreTiming::AddTicks(num_instructions); 192 CoreTiming::AddTicks(num_instructions);
202 if (GDBStub::IsServerEnabled()) { 193 if (GDBStub::IsServerEnabled()) {
@@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
219 210
220 CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); 211 CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
221 CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); 212 CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
222 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); 213 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
223 214
224 for (auto i = 0; i < 29; ++i) { 215 for (auto i = 0; i < 29; ++i) {
225 uregs[i] = UC_ARM64_REG_X0 + i; 216 uregs[i] = UC_ARM64_REG_X0 + i;
@@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
234 225
235 for (int i = 0; i < 32; ++i) { 226 for (int i = 0; i < 32; ++i) {
236 uregs[i] = UC_ARM64_REG_Q0 + i; 227 uregs[i] = UC_ARM64_REG_Q0 + i;
237 tregs[i] = &ctx.fpu_registers[i]; 228 tregs[i] = &ctx.vector_registers[i];
238 } 229 }
239 230
240 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); 231 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
@@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
246 237
247 CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); 238 CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
248 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); 239 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
249 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); 240 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
250 241
251 for (int i = 0; i < 29; ++i) { 242 for (int i = 0; i < 29; ++i) {
252 uregs[i] = UC_ARM64_REG_X0 + i; 243 uregs[i] = UC_ARM64_REG_X0 + i;
@@ -261,7 +252,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
261 252
262 for (auto i = 0; i < 32; ++i) { 253 for (auto i = 0; i < 32; ++i) {
263 uregs[i] = UC_ARM64_REG_Q0 + i; 254 uregs[i] = UC_ARM64_REG_Q0 + i;
264 tregs[i] = (void*)&ctx.fpu_registers[i]; 255 tregs[i] = (void*)&ctx.vector_registers[i];
265 } 256 }
266 257
267 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); 258 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index bd6b2f723..75761950b 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -15,19 +15,17 @@ class ARM_Unicorn final : public ARM_Interface {
15public: 15public:
16 ARM_Unicorn(); 16 ARM_Unicorn();
17 ~ARM_Unicorn(); 17 ~ARM_Unicorn();
18 void MapBackingMemory(VAddr address, size_t size, u8* memory, 18 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
19 Kernel::VMAPermission perms) override; 19 Kernel::VMAPermission perms) override;
20 void UnmapMemory(VAddr address, size_t size) override; 20 void UnmapMemory(VAddr address, std::size_t size) override;
21 void SetPC(u64 pc) override; 21 void SetPC(u64 pc) override;
22 u64 GetPC() const override; 22 u64 GetPC() const override;
23 u64 GetReg(int index) const override; 23 u64 GetReg(int index) const override;
24 void SetReg(int index, u64 value) override; 24 void SetReg(int index, u64 value) override;
25 u128 GetExtReg(int index) const override; 25 u128 GetVectorReg(int index) const override;
26 void SetExtReg(int index, u128 value) override; 26 void SetVectorReg(int index, u128 value) override;
27 u32 GetVFPReg(int index) const override; 27 u32 GetPSTATE() const override;
28 void SetVFPReg(int index, u32 value) override; 28 void SetPSTATE(u32 pstate) override;
29 u32 GetCPSR() const override;
30 void SetCPSR(u32 cpsr) override;
31 VAddr GetTlsAddress() const override; 29 VAddr GetTlsAddress() const override;
32 void SetTlsAddress(VAddr address) override; 30 void SetTlsAddress(VAddr address) override;
33 void SetTPIDR_EL0(u64 value) override; 31 void SetTPIDR_EL0(u64 value) override;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 2cfae18df..50f0a42fb 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,6 +14,9 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_cpu.h" 15#include "core/core_cpu.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/file_sys/mode.h"
18#include "core/file_sys/vfs_concat.h"
19#include "core/file_sys/vfs_real.h"
17#include "core/gdbstub/gdbstub.h" 20#include "core/gdbstub/gdbstub.h"
18#include "core/hle/kernel/client_port.h" 21#include "core/hle/kernel/client_port.h"
19#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
@@ -21,14 +24,11 @@
21#include "core/hle/kernel/scheduler.h" 24#include "core/hle/kernel/scheduler.h"
22#include "core/hle/kernel/thread.h" 25#include "core/hle/kernel/thread.h"
23#include "core/hle/service/service.h" 26#include "core/hle/service/service.h"
24#include "core/hle/service/sm/controller.h"
25#include "core/hle/service/sm/sm.h" 27#include "core/hle/service/sm/sm.h"
26#include "core/loader/loader.h" 28#include "core/loader/loader.h"
27#include "core/perf_stats.h" 29#include "core/perf_stats.h"
28#include "core/settings.h" 30#include "core/settings.h"
29#include "core/telemetry_session.h" 31#include "core/telemetry_session.h"
30#include "file_sys/vfs_concat.h"
31#include "file_sys/vfs_real.h"
32#include "video_core/debug_utils/debug_utils.h" 32#include "video_core/debug_utils/debug_utils.h"
33#include "video_core/gpu.h" 33#include "video_core/gpu.h"
34#include "video_core/renderer_base.h" 34#include "video_core/renderer_base.h"
@@ -136,11 +136,11 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 136 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 138
139 current_process = Kernel::Process::Create(kernel, "main"); 139 kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main"));
140 140
141 cpu_barrier = std::make_shared<CpuBarrier>(); 141 cpu_barrier = std::make_shared<CpuBarrier>();
142 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 142 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
143 for (size_t index = 0; index < cpu_cores.size(); ++index) { 143 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
144 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 144 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
145 } 145 }
146 146
@@ -161,7 +161,7 @@ struct System::Impl {
161 // CPU core 0 is run on the main thread 161 // CPU core 0 is run on the main thread
162 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 162 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
163 if (Settings::values.use_multi_core) { 163 if (Settings::values.use_multi_core) {
164 for (size_t index = 0; index < cpu_core_threads.size(); ++index) { 164 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
165 cpu_core_threads[index] = 165 cpu_core_threads[index] =
166 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 166 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
167 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; 167 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
@@ -202,7 +202,7 @@ struct System::Impl {
202 return init_result; 202 return init_result;
203 } 203 }
204 204
205 const Loader::ResultStatus load_result{app_loader->Load(current_process)}; 205 const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())};
206 if (load_result != Loader::ResultStatus::Success) { 206 if (load_result != Loader::ResultStatus::Success) {
207 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 207 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
208 Shutdown(); 208 Shutdown();
@@ -281,12 +281,11 @@ struct System::Impl {
281 std::unique_ptr<VideoCore::RendererBase> renderer; 281 std::unique_ptr<VideoCore::RendererBase> renderer;
282 std::unique_ptr<Tegra::GPU> gpu_core; 282 std::unique_ptr<Tegra::GPU> gpu_core;
283 std::shared_ptr<Tegra::DebugContext> debug_context; 283 std::shared_ptr<Tegra::DebugContext> debug_context;
284 Kernel::SharedPtr<Kernel::Process> current_process;
285 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 284 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
286 std::shared_ptr<CpuBarrier> cpu_barrier; 285 std::shared_ptr<CpuBarrier> cpu_barrier;
287 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 286 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
288 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 287 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
289 size_t active_core{}; ///< Active core, only used in single thread mode 288 std::size_t active_core{}; ///< Active core, only used in single thread mode
290 289
291 /// Service manager 290 /// Service manager
292 std::shared_ptr<Service::SM::ServiceManager> service_manager; 291 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -349,7 +348,7 @@ ARM_Interface& System::CurrentArmInterface() {
349 return CurrentCpuCore().ArmInterface(); 348 return CurrentCpuCore().ArmInterface();
350} 349}
351 350
352size_t System::CurrentCoreIndex() { 351std::size_t System::CurrentCoreIndex() {
353 return CurrentCpuCore().CoreIndex(); 352 return CurrentCpuCore().CoreIndex();
354} 353}
355 354
@@ -357,21 +356,25 @@ Kernel::Scheduler& System::CurrentScheduler() {
357 return *CurrentCpuCore().Scheduler(); 356 return *CurrentCpuCore().Scheduler();
358} 357}
359 358
360const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { 359const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) {
361 ASSERT(core_index < NUM_CPU_CORES); 360 ASSERT(core_index < NUM_CPU_CORES);
362 return impl->cpu_cores[core_index]->Scheduler(); 361 return impl->cpu_cores[core_index]->Scheduler();
363} 362}
364 363
365Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { 364Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
366 return impl->current_process; 365 return impl->kernel.CurrentProcess();
367} 366}
368 367
369ARM_Interface& System::ArmInterface(size_t core_index) { 368const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const {
369 return impl->kernel.CurrentProcess();
370}
371
372ARM_Interface& System::ArmInterface(std::size_t core_index) {
370 ASSERT(core_index < NUM_CPU_CORES); 373 ASSERT(core_index < NUM_CPU_CORES);
371 return impl->cpu_cores[core_index]->ArmInterface(); 374 return impl->cpu_cores[core_index]->ArmInterface();
372} 375}
373 376
374Cpu& System::CpuCore(size_t core_index) { 377Cpu& System::CpuCore(std::size_t core_index) {
375 ASSERT(core_index < NUM_CPU_CORES); 378 ASSERT(core_index < NUM_CPU_CORES);
376 return *impl->cpu_cores[core_index]; 379 return *impl->cpu_cores[core_index];
377} 380}
@@ -440,8 +443,8 @@ void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
440 impl->debug_context = std::move(context); 443 impl->debug_context = std::move(context);
441} 444}
442 445
443std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const { 446Tegra::DebugContext* System::GetGPUDebugContext() const {
444 return impl->debug_context; 447 return impl->debug_context.get();
445} 448}
446 449
447void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 450void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
diff --git a/src/core/core.h b/src/core/core.h
index eee1fecc1..f9a3e97e3 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -145,16 +145,16 @@ public:
145 ARM_Interface& CurrentArmInterface(); 145 ARM_Interface& CurrentArmInterface();
146 146
147 /// Gets the index of the currently running CPU core 147 /// Gets the index of the currently running CPU core
148 size_t CurrentCoreIndex(); 148 std::size_t CurrentCoreIndex();
149 149
150 /// Gets the scheduler for the CPU core that is currently running 150 /// Gets the scheduler for the CPU core that is currently running
151 Kernel::Scheduler& CurrentScheduler(); 151 Kernel::Scheduler& CurrentScheduler();
152 152
153 /// Gets an ARM interface to the CPU core with the specified index 153 /// Gets an ARM interface to the CPU core with the specified index
154 ARM_Interface& ArmInterface(size_t core_index); 154 ARM_Interface& ArmInterface(std::size_t core_index);
155 155
156 /// Gets a CPU interface to the CPU core with the specified index 156 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(size_t core_index); 157 Cpu& CpuCore(std::size_t core_index);
158 158
159 /// Gets the exclusive monitor 159 /// Gets the exclusive monitor
160 ExclusiveMonitor& Monitor(); 160 ExclusiveMonitor& Monitor();
@@ -172,11 +172,14 @@ public:
172 const VideoCore::RendererBase& Renderer() const; 172 const VideoCore::RendererBase& Renderer() const;
173 173
174 /// Gets the scheduler for the CPU core with the specified index 174 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index); 175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
176 176
177 /// Gets the current process 177 /// Provides a reference to the current process
178 Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); 178 Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
179 179
180 /// Provides a constant reference to the current process.
181 const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const;
182
180 /// Provides a reference to the kernel instance. 183 /// Provides a reference to the kernel instance.
181 Kernel::KernelCore& Kernel(); 184 Kernel::KernelCore& Kernel();
182 185
@@ -209,7 +212,7 @@ public:
209 212
210 void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context); 213 void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
211 214
212 std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const; 215 Tegra::DebugContext* GetGPUDebugContext() const;
213 216
214 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); 217 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
215 218
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index b042ee02b..21568ad50 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -9,6 +9,7 @@
9#ifdef ARCHITECTURE_x86_64 9#ifdef ARCHITECTURE_x86_64
10#include "core/arm/dynarmic/arm_dynarmic.h" 10#include "core/arm/dynarmic/arm_dynarmic.h"
11#endif 11#endif
12#include "core/arm/exclusive_monitor.h"
12#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
13#include "core/core_cpu.h" 14#include "core/core_cpu.h"
14#include "core/core_timing.h" 15#include "core/core_timing.h"
@@ -49,7 +50,7 @@ bool CpuBarrier::Rendezvous() {
49} 50}
50 51
51Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
52 std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) 53 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index)
53 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { 54 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
54 55
55 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.use_cpu_jit) {
@@ -66,7 +67,9 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
66 scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); 67 scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
67} 68}
68 69
69std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) { 70Cpu::~Cpu() = default;
71
72std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
70 if (Settings::values.use_cpu_jit) { 73 if (Settings::values.use_cpu_jit) {
71#ifdef ARCHITECTURE_x86_64 74#ifdef ARCHITECTURE_x86_64
72 return std::make_shared<DynarmicExclusiveMonitor>(num_cores); 75 return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 40ed34b47..685532965 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -6,11 +6,10 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <cstddef>
9#include <memory> 10#include <memory>
10#include <mutex> 11#include <mutex>
11#include <string>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/arm/exclusive_monitor.h"
14 13
15namespace Kernel { 14namespace Kernel {
16class Scheduler; 15class Scheduler;
@@ -19,6 +18,7 @@ class Scheduler;
19namespace Core { 18namespace Core {
20 19
21class ARM_Interface; 20class ARM_Interface;
21class ExclusiveMonitor;
22 22
23constexpr unsigned NUM_CPU_CORES{4}; 23constexpr unsigned NUM_CPU_CORES{4};
24 24
@@ -42,7 +42,8 @@ private:
42class Cpu { 42class Cpu {
43public: 43public:
44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
45 std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index); 45 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
46 ~Cpu();
46 47
47 void RunLoop(bool tight_loop = true); 48 void RunLoop(bool tight_loop = true);
48 49
@@ -66,11 +67,11 @@ public:
66 return core_index == 0; 67 return core_index == 0;
67 } 68 }
68 69
69 size_t CoreIndex() const { 70 std::size_t CoreIndex() const {
70 return core_index; 71 return core_index;
71 } 72 }
72 73
73 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores); 74 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
74 75
75private: 76private:
76 void Reschedule(); 77 void Reschedule();
@@ -80,7 +81,7 @@ private:
80 std::shared_ptr<Kernel::Scheduler> scheduler; 81 std::shared_ptr<Kernel::Scheduler> scheduler;
81 82
82 std::atomic<bool> reschedule_pending = false; 83 std::atomic<bool> reschedule_pending = false;
83 size_t core_index; 84 std::size_t core_index;
84}; 85};
85 86
86} // namespace Core 87} // namespace Core
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 72e4bed67..4be76bb43 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -10,9 +10,9 @@
10 10
11namespace Core::Crypto { 11namespace Core::Crypto {
12namespace { 12namespace {
13std::vector<u8> CalculateNintendoTweak(size_t sector_id) { 13std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
14 std::vector<u8> out(0x10); 14 std::vector<u8> out(0x10);
15 for (size_t i = 0xF; i <= 0xF; --i) { 15 for (std::size_t i = 0xF; i <= 0xF; --i) {
16 out[i] = sector_id & 0xFF; 16 out[i] = sector_id & 0xFF;
17 sector_id >>= 8; 17 sector_id >>= 8;
18 } 18 }
@@ -20,11 +20,14 @@ std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
20} 20}
21} // Anonymous namespace 21} // Anonymous namespace
22 22
23static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), 23static_assert(static_cast<std::size_t>(Mode::CTR) ==
24 static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR),
24 "CTR has incorrect value."); 25 "CTR has incorrect value.");
25static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), 26static_assert(static_cast<std::size_t>(Mode::ECB) ==
27 static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB),
26 "ECB has incorrect value."); 28 "ECB has incorrect value.");
27static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), 29static_assert(static_cast<std::size_t>(Mode::XTS) ==
30 static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS),
28 "XTS has incorrect value."); 31 "XTS has incorrect value.");
29 32
30// Structure to hide mbedtls types from header file 33// Structure to hide mbedtls types from header file
@@ -33,7 +36,7 @@ struct CipherContext {
33 mbedtls_cipher_context_t decryption_context; 36 mbedtls_cipher_context_t decryption_context;
34}; 37};
35 38
36template <typename Key, size_t KeySize> 39template <typename Key, std::size_t KeySize>
37Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) 40Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
38 : ctx(std::make_unique<CipherContext>()) { 41 : ctx(std::make_unique<CipherContext>()) {
39 mbedtls_cipher_init(&ctx->encryption_context); 42 mbedtls_cipher_init(&ctx->encryption_context);
@@ -54,26 +57,26 @@ Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
54 //"Failed to set key on mbedtls ciphers."); 57 //"Failed to set key on mbedtls ciphers.");
55} 58}
56 59
57template <typename Key, size_t KeySize> 60template <typename Key, std::size_t KeySize>
58AESCipher<Key, KeySize>::~AESCipher() { 61AESCipher<Key, KeySize>::~AESCipher() {
59 mbedtls_cipher_free(&ctx->encryption_context); 62 mbedtls_cipher_free(&ctx->encryption_context);
60 mbedtls_cipher_free(&ctx->decryption_context); 63 mbedtls_cipher_free(&ctx->decryption_context);
61} 64}
62 65
63template <typename Key, size_t KeySize> 66template <typename Key, std::size_t KeySize>
64void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { 67void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
65 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || 68 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, 69 mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
67 "Failed to set IV on mbedtls ciphers."); 70 "Failed to set IV on mbedtls ciphers.");
68} 71}
69 72
70template <typename Key, size_t KeySize> 73template <typename Key, std::size_t KeySize>
71void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { 74void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
72 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; 75 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
73 76
74 mbedtls_cipher_reset(context); 77 mbedtls_cipher_reset(context);
75 78
76 size_t written = 0; 79 std::size_t written = 0;
77 if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { 80 if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
78 mbedtls_cipher_update(context, src, size, dest, &written); 81 mbedtls_cipher_update(context, src, size, dest, &written);
79 if (written != size) { 82 if (written != size) {
@@ -82,11 +85,25 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
82 } 85 }
83 } else { 86 } else {
84 const auto block_size = mbedtls_cipher_get_block_size(context); 87 const auto block_size = mbedtls_cipher_get_block_size(context);
88 if (size < block_size) {
89 std::vector<u8> block(block_size);
90 std::memcpy(block.data(), src, size);
91 Transcode(block.data(), block.size(), block.data(), op);
92 std::memcpy(dest, block.data(), size);
93 return;
94 }
85 95
86 for (size_t offset = 0; offset < size; offset += block_size) { 96 for (std::size_t offset = 0; offset < size; offset += block_size) {
87 auto length = std::min<size_t>(block_size, size - offset); 97 auto length = std::min<std::size_t>(block_size, size - offset);
88 mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); 98 mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
89 if (written != length) { 99 if (written != length) {
100 if (length < block_size) {
101 std::vector<u8> block(block_size);
102 std::memcpy(block.data(), src + offset, length);
103 Transcode(block.data(), block.size(), block.data(), op);
104 std::memcpy(dest + offset, block.data(), length);
105 return;
106 }
90 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", 107 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
91 length, written); 108 length, written);
92 } 109 }
@@ -96,12 +113,12 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
96 mbedtls_cipher_finish(context, nullptr, nullptr); 113 mbedtls_cipher_finish(context, nullptr, nullptr);
97} 114}
98 115
99template <typename Key, size_t KeySize> 116template <typename Key, std::size_t KeySize>
100void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, 117void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* dest,
101 size_t sector_size, Op op) { 118 std::size_t sector_id, std::size_t sector_size, Op op) {
102 ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size."); 119 ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size.");
103 120
104 for (size_t i = 0; i < size; i += sector_size) { 121 for (std::size_t i = 0; i < size; i += sector_size) {
105 SetIV(CalculateNintendoTweak(sector_id++)); 122 SetIV(CalculateNintendoTweak(sector_id++));
106 Transcode<u8, u8>(src + i, sector_size, dest + i, op); 123 Transcode<u8, u8>(src + i, sector_size, dest + i, op);
107 } 124 }
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index 8ce9d6612..edc4ab910 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -25,7 +25,7 @@ enum class Op {
25 Decrypt, 25 Decrypt,
26}; 26};
27 27
28template <typename Key, size_t KeySize = sizeof(Key)> 28template <typename Key, std::size_t KeySize = sizeof(Key)>
29class AESCipher { 29class AESCipher {
30 static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); 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."); 31 static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
@@ -38,25 +38,25 @@ public:
38 void SetIV(std::vector<u8> iv); 38 void SetIV(std::vector<u8> iv);
39 39
40 template <typename Source, typename Dest> 40 template <typename Source, typename Dest>
41 void Transcode(const Source* src, size_t size, Dest* dest, Op op) const { 41 void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
42 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, 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."); 43 "Transcode source and destination types must be trivially copyable.");
44 Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); 44 Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
45 } 45 }
46 46
47 void Transcode(const u8* src, size_t size, u8* dest, Op op) const; 47 void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const;
48 48
49 template <typename Source, typename Dest> 49 template <typename Source, typename Dest>
50 void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, 50 void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id,
51 size_t sector_size, Op op) { 51 std::size_t sector_size, Op op) {
52 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, 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."); 53 "XTSTranscode source and destination types must be trivially copyable.");
54 XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, 54 XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
55 sector_size, op); 55 sector_size, op);
56 } 56 }
57 57
58 void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, 58 void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id,
59 Op op); 59 std::size_t sector_size, Op op);
60 60
61private: 61private:
62 std::unique_ptr<CipherContext> ctx; 62 std::unique_ptr<CipherContext> ctx;
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
index 3ea60dbd0..902841c77 100644
--- a/src/core/crypto/ctr_encryption_layer.cpp
+++ b/src/core/crypto/ctr_encryption_layer.cpp
@@ -8,11 +8,12 @@
8 8
9namespace Core::Crypto { 9namespace Core::Crypto {
10 10
11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) 11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
12 std::size_t base_offset)
12 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), 13 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
13 iv(16, 0) {} 14 iv(16, 0) {}
14 15
15size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { 16std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
16 if (length == 0) 17 if (length == 0)
17 return 0; 18 return 0;
18 19
@@ -21,14 +22,14 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
21 UpdateIV(base_offset + offset); 22 UpdateIV(base_offset + offset);
22 std::vector<u8> raw = base->ReadBytes(length, offset); 23 std::vector<u8> raw = base->ReadBytes(length, offset);
23 cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt); 24 cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
24 return raw.size(); 25 return length;
25 } 26 }
26 27
27 // offset does not fall on block boundary (0x10) 28 // offset does not fall on block boundary (0x10)
28 std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); 29 std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
29 UpdateIV(base_offset + offset - sector_offset); 30 UpdateIV(base_offset + offset - sector_offset);
30 cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); 31 cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
31 size_t read = 0x10 - sector_offset; 32 std::size_t read = 0x10 - sector_offset;
32 33
33 if (length + sector_offset < 0x10) { 34 if (length + sector_offset < 0x10) {
34 std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); 35 std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
@@ -43,9 +44,9 @@ void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
43 iv.assign(iv_.cbegin(), iv_.cbegin() + length); 44 iv.assign(iv_.cbegin(), iv_.cbegin() + length);
44} 45}
45 46
46void CTREncryptionLayer::UpdateIV(size_t offset) const { 47void CTREncryptionLayer::UpdateIV(std::size_t offset) const {
47 offset >>= 4; 48 offset >>= 4;
48 for (size_t i = 0; i < 8; ++i) { 49 for (std::size_t i = 0; i < 8; ++i) {
49 iv[16 - i - 1] = offset & 0xFF; 50 iv[16 - i - 1] = offset & 0xFF;
50 offset >>= 8; 51 offset >>= 8;
51 } 52 }
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
index 11b8683c7..a7bf810f4 100644
--- a/src/core/crypto/ctr_encryption_layer.h
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -14,20 +14,20 @@ namespace Core::Crypto {
14// Sits on top of a VirtualFile and provides CTR-mode AES decription. 14// Sits on top of a VirtualFile and provides CTR-mode AES decription.
15class CTREncryptionLayer : public EncryptionLayer { 15class CTREncryptionLayer : public EncryptionLayer {
16public: 16public:
17 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); 17 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
18 18
19 size_t Read(u8* data, size_t length, size_t offset) const override; 19 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
20 20
21 void SetIV(const std::vector<u8>& iv); 21 void SetIV(const std::vector<u8>& iv);
22 22
23private: 23private:
24 size_t base_offset; 24 std::size_t base_offset;
25 25
26 // Must be mutable as operations modify cipher contexts. 26 // Must be mutable as operations modify cipher contexts.
27 mutable AESCipher<Key128> cipher; 27 mutable AESCipher<Key128> cipher;
28 mutable std::vector<u8> iv; 28 mutable std::vector<u8> iv;
29 29
30 void UpdateIV(size_t offset) const; 30 void UpdateIV(std::size_t offset) const;
31}; 31};
32 32
33} // namespace Core::Crypto 33} // namespace Core::Crypto
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp
index 4204527e3..4c377d7d4 100644
--- a/src/core/crypto/encryption_layer.cpp
+++ b/src/core/crypto/encryption_layer.cpp
@@ -12,11 +12,11 @@ std::string EncryptionLayer::GetName() const {
12 return base->GetName(); 12 return base->GetName();
13} 13}
14 14
15size_t EncryptionLayer::GetSize() const { 15std::size_t EncryptionLayer::GetSize() const {
16 return base->GetSize(); 16 return base->GetSize();
17} 17}
18 18
19bool EncryptionLayer::Resize(size_t new_size) { 19bool EncryptionLayer::Resize(std::size_t new_size) {
20 return false; 20 return false;
21} 21}
22 22
@@ -32,7 +32,7 @@ bool EncryptionLayer::IsReadable() const {
32 return true; 32 return true;
33} 33}
34 34
35size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { 35std::size_t EncryptionLayer::Write(const u8* data, std::size_t length, std::size_t offset) {
36 return 0; 36 return 0;
37} 37}
38 38
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
index 7f05af9b4..53619cb38 100644
--- a/src/core/crypto/encryption_layer.h
+++ b/src/core/crypto/encryption_layer.h
@@ -15,15 +15,15 @@ class EncryptionLayer : public FileSys::VfsFile {
15public: 15public:
16 explicit EncryptionLayer(FileSys::VirtualFile base); 16 explicit EncryptionLayer(FileSys::VirtualFile base);
17 17
18 size_t Read(u8* data, size_t length, size_t offset) const override = 0; 18 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0;
19 19
20 std::string GetName() const override; 20 std::string GetName() const override;
21 size_t GetSize() const override; 21 std::size_t GetSize() const override;
22 bool Resize(size_t new_size) override; 22 bool Resize(std::size_t new_size) override;
23 std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; 23 std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
24 bool IsWritable() const override; 24 bool IsWritable() const override;
25 bool IsReadable() const override; 25 bool IsReadable() const override;
26 size_t Write(const u8* data, size_t length, size_t offset) override; 26 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
27 bool Rename(std::string_view name) override; 27 bool Rename(std::string_view name) override;
28 28
29protected: 29protected:
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 0b6c07de8..bf3a70944 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -8,12 +8,15 @@
8#include <locale> 8#include <locale>
9#include <sstream> 9#include <sstream>
10#include <string_view> 10#include <string_view>
11#include <tuple>
12#include <vector>
11#include "common/common_paths.h" 13#include "common/common_paths.h"
12#include "common/file_util.h" 14#include "common/file_util.h"
13#include "common/hex_util.h" 15#include "common/hex_util.h"
14#include "common/logging/log.h" 16#include "common/logging/log.h"
15#include "core/crypto/aes_util.h" 17#include "core/crypto/aes_util.h"
16#include "core/crypto/key_manager.h" 18#include "core/crypto/key_manager.h"
19#include "core/loader/loader.h"
17#include "core/settings.h" 20#include "core/settings.h"
18 21
19namespace Core::Crypto { 22namespace Core::Crypto {
@@ -51,7 +54,7 @@ boost::optional<Key128> DeriveSDSeed() {
51 return boost::none; 54 return boost::none;
52 55
53 std::array<u8, 0x10> buffer{}; 56 std::array<u8, 0x10> buffer{};
54 size_t offset = 0; 57 std::size_t offset = 0;
55 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 58 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
56 save_43.Seek(offset, SEEK_SET); 59 save_43.Seek(offset, SEEK_SET);
57 save_43.ReadBytes(buffer.data(), buffer.size()); 60 save_43.ReadBytes(buffer.data(), buffer.size());
@@ -102,7 +105,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
102 105
103 // Combine sources and seed 106 // Combine sources and seed
104 for (auto& source : sd_key_sources) { 107 for (auto& source : sd_key_sources) {
105 for (size_t i = 0; i < source.size(); ++i) 108 for (std::size_t i = 0; i < source.size(); ++i)
106 source[i] ^= sd_seed[i & 0xF]; 109 source[i] ^= sd_seed[i & 0xF];
107 } 110 }
108 111
@@ -204,7 +207,7 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
204 return s256_keys.at({id, field1, field2}); 207 return s256_keys.at({id, field1, field2});
205} 208}
206 209
207template <size_t Size> 210template <std::size_t Size>
208void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, 211void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
209 const std::array<u8, Size>& key) { 212 const std::array<u8, Size>& key) {
210 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 213 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
@@ -228,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
228} 231}
229 232
230void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { 233void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
231 const auto iter = std::find_if( 234 if (s128_keys.find({id, field1, field2}) != s128_keys.end())
235 return;
236 if (id == S128KeyType::Titlekey) {
237 Key128 rights_id;
238 std::memcpy(rights_id.data(), &field2, sizeof(u64));
239 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
240 WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
241 }
242 const auto iter2 = std::find_if(
232 s128_file_id.begin(), s128_file_id.end(), 243 s128_file_id.begin(), s128_file_id.end(),
233 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { 244 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
234 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 245 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
235 std::tie(id, field1, field2); 246 std::tie(id, field1, field2);
236 }); 247 });
237 if (iter != s128_file_id.end()) 248 if (iter2 != s128_file_id.end())
238 WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key); 249 WriteKeyToFile(false, iter2->first, key);
239 s128_keys[{id, field1, field2}] = key; 250 s128_keys[{id, field1, field2}] = key;
240} 251}
241 252
242void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { 253void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
254 if (s256_keys.find({id, field1, field2}) != s256_keys.end())
255 return;
243 const auto iter = std::find_if( 256 const auto iter = std::find_if(
244 s256_file_id.begin(), s256_file_id.end(), 257 s256_file_id.begin(), s256_file_id.end(),
245 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { 258 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 7ca3e6cbc..978eec8dc 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -6,16 +6,19 @@
6 6
7#include <array> 7#include <array>
8#include <string> 8#include <string>
9#include <string_view>
10#include <type_traits>
11#include <vector>
12#include <boost/container/flat_map.hpp> 9#include <boost/container/flat_map.hpp>
10#include <boost/optional.hpp>
13#include <fmt/format.h> 11#include <fmt/format.h>
14#include "common/common_types.h" 12#include "common/common_types.h"
15#include "core/loader/loader.h" 13
14namespace Loader {
15enum class ResultStatus : u16;
16}
16 17
17namespace Core::Crypto { 18namespace Core::Crypto {
18 19
20constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
21
19using Key128 = std::array<u8, 0x10>; 22using Key128 = std::array<u8, 0x10>;
20using Key256 = std::array<u8, 0x20>; 23using Key256 = std::array<u8, 0x20>;
21using SHA256Hash = std::array<u8, 0x20>; 24using SHA256Hash = std::array<u8, 0x20>;
@@ -105,7 +108,7 @@ private:
105 void LoadFromFile(const std::string& filename, bool is_title_keys); 108 void LoadFromFile(const std::string& filename, bool is_title_keys);
106 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 109 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
107 const std::string& filename, bool title); 110 const std::string& filename, bool title);
108 template <size_t Size> 111 template <std::size_t Size>
109 void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); 112 void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
110 113
111 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; 114 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp
index c10832cfe..8f0ba4ee7 100644
--- a/src/core/crypto/xts_encryption_layer.cpp
+++ b/src/core/crypto/xts_encryption_layer.cpp
@@ -14,7 +14,7 @@ constexpr u64 XTS_SECTOR_SIZE = 0x4000;
14XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) 14XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
15 : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} 15 : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
16 16
17size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { 17std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
18 if (length == 0) 18 if (length == 0)
19 return 0; 19 return 0;
20 20
@@ -46,7 +46,7 @@ size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
46 block.resize(XTS_SECTOR_SIZE); 46 block.resize(XTS_SECTOR_SIZE);
47 cipher.XTSTranscode(block.data(), block.size(), block.data(), 47 cipher.XTSTranscode(block.data(), block.size(), block.data(),
48 (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); 48 (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
49 const size_t read = XTS_SECTOR_SIZE - sector_offset; 49 const std::size_t read = XTS_SECTOR_SIZE - sector_offset;
50 50
51 if (length + sector_offset < XTS_SECTOR_SIZE) { 51 if (length + sector_offset < XTS_SECTOR_SIZE) {
52 std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); 52 std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
diff --git a/src/core/crypto/xts_encryption_layer.h b/src/core/crypto/xts_encryption_layer.h
index 7a1f1dc64..5f8f00fe7 100644
--- a/src/core/crypto/xts_encryption_layer.h
+++ b/src/core/crypto/xts_encryption_layer.h
@@ -15,7 +15,7 @@ class XTSEncryptionLayer : public EncryptionLayer {
15public: 15public:
16 XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key); 16 XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key);
17 17
18 size_t Read(u8* data, size_t length, size_t offset) const override; 18 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
19 19
20private: 20private:
21 // Must be mutable as operations modify cipher contexts. 21 // Must be mutable as operations modify cipher contexts.
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 08a7cea5a..205492897 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/file_sys/bis_factory.h" 5#include "core/file_sys/bis_factory.h"
6#include "core/file_sys/registered_cache.h"
6 7
7namespace FileSys { 8namespace FileSys {
8 9
@@ -13,6 +14,8 @@ BISFactory::BISFactory(VirtualDir nand_root_)
13 usrnand_cache(std::make_shared<RegisteredCache>( 14 usrnand_cache(std::make_shared<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 15 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
15 16
17BISFactory::~BISFactory() = default;
18
16std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 19std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
17 return sysnand_cache; 20 return sysnand_cache;
18} 21}
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index a970a5e2e..9523dd864 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -5,17 +5,20 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include "core/loader/loader.h" 8
9#include "registered_cache.h" 9#include "core/file_sys/vfs.h"
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13class RegisteredCache;
14
13/// File system interface to the Built-In Storage 15/// File system interface to the Built-In Storage
14/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND 16/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
15/// registered caches. 17/// registered caches.
16class BISFactory { 18class BISFactory {
17public: 19public:
18 explicit BISFactory(VirtualDir nand_root); 20 explicit BISFactory(VirtualDir nand_root);
21 ~BISFactory();
19 22
20 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
21 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index d61a2ebe1..edfc1bbd4 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -9,7 +9,10 @@
9 9
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/file_sys/card_image.h" 11#include "core/file_sys/card_image.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/partition_filesystem.h" 14#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/submission_package.h"
13#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
14#include "core/loader/loader.h" 17#include "core/loader/loader.h"
15 18
@@ -38,20 +41,25 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
38 41
39 for (XCIPartition partition : 42 for (XCIPartition partition :
40 {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { 43 {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
41 auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); 44 auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
42 if (raw != nullptr) 45 if (raw != nullptr)
43 partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); 46 partitions[static_cast<std::size_t>(partition)] =
47 std::make_shared<PartitionFilesystem>(raw);
44 } 48 }
45 49
46 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; 50 secure_partition = std::make_shared<NSP>(
51 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
47 52
48 auto result = AddNCAFromPartition(XCIPartition::Secure); 53 const auto secure_ncas = secure_partition->GetNCAsCollapsed();
49 if (result != Loader::ResultStatus::Success) { 54 std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
50 status = result;
51 return;
52 }
53 55
54 result = AddNCAFromPartition(XCIPartition::Update); 56 program =
57 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
58 program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
59 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
60 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
61
62 auto result = AddNCAFromPartition(XCIPartition::Update);
55 if (result != Loader::ResultStatus::Success) { 63 if (result != Loader::ResultStatus::Success) {
56 status = result; 64 status = result;
57 return; 65 return;
@@ -74,6 +82,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
74 status = Loader::ResultStatus::Success; 82 status = Loader::ResultStatus::Success;
75} 83}
76 84
85XCI::~XCI() = default;
86
77Loader::ResultStatus XCI::GetStatus() const { 87Loader::ResultStatus XCI::GetStatus() const {
78 return status; 88 return status;
79} 89}
@@ -83,7 +93,11 @@ Loader::ResultStatus XCI::GetProgramNCAStatus() const {
83} 93}
84 94
85VirtualDir XCI::GetPartition(XCIPartition partition) const { 95VirtualDir XCI::GetPartition(XCIPartition partition) const {
86 return partitions[static_cast<size_t>(partition)]; 96 return partitions[static_cast<std::size_t>(partition)];
97}
98
99std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
100 return secure_partition;
87} 101}
88 102
89VirtualDir XCI::GetSecurePartition() const { 103VirtualDir XCI::GetSecurePartition() const {
@@ -102,6 +116,20 @@ VirtualDir XCI::GetLogoPartition() const {
102 return GetPartition(XCIPartition::Logo); 116 return GetPartition(XCIPartition::Logo);
103} 117}
104 118
119u64 XCI::GetProgramTitleID() const {
120 return secure_partition->GetProgramTitleID();
121}
122
123std::shared_ptr<NCA> XCI::GetProgramNCA() const {
124 return program;
125}
126
127VirtualFile XCI::GetProgramNCAFile() const {
128 if (GetProgramNCA() == nullptr)
129 return nullptr;
130 return GetProgramNCA()->GetBaseFile();
131}
132
105const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { 133const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
106 return ncas; 134 return ncas;
107} 135}
@@ -141,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
141} 169}
142 170
143Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 171Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
144 if (partitions[static_cast<size_t>(part)] == nullptr) { 172 if (partitions[static_cast<std::size_t>(part)] == nullptr) {
145 return Loader::ResultStatus::ErrorXCIMissingPartition; 173 return Loader::ResultStatus::ErrorXCIMissingPartition;
146 } 174 }
147 175
148 for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { 176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
149 if (file->GetExtension() != "nca") 177 if (file->GetExtension() != "nca")
150 continue; 178 continue;
151 auto nca = std::make_shared<NCA>(file); 179 auto nca = std::make_shared<NCA>(file);
@@ -160,7 +188,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
160 } else { 188 } else {
161 const u16 error_id = static_cast<u16>(nca->GetStatus()); 189 const u16 error_id = static_cast<u16>(nca->GetStatus());
162 LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", 190 LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
163 partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, 191 partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
164 nca->GetStatus()); 192 nca->GetStatus());
165 } 193 }
166 } 194 }
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 54ab828d1..ce514dfa0 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -5,15 +5,22 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
8#include <vector> 9#include <vector>
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/file_sys/content_archive.h"
12#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
13#include "core/loader/loader.h" 13
14namespace Loader {
15enum class ResultStatus : u16;
16}
14 17
15namespace FileSys { 18namespace FileSys {
16 19
20class NCA;
21enum class NCAContentType : u8;
22class NSP;
23
17enum class GamecardSize : u8 { 24enum class GamecardSize : u8 {
18 S_1GB = 0xFA, 25 S_1GB = 0xFA,
19 S_2GB = 0xF8, 26 S_2GB = 0xF8,
@@ -57,6 +64,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
57class XCI : public ReadOnlyVfsDirectory { 64class XCI : public ReadOnlyVfsDirectory {
58public: 65public:
59 explicit XCI(VirtualFile file); 66 explicit XCI(VirtualFile file);
67 ~XCI() override;
60 68
61 Loader::ResultStatus GetStatus() const; 69 Loader::ResultStatus GetStatus() const;
62 Loader::ResultStatus GetProgramNCAStatus() const; 70 Loader::ResultStatus GetProgramNCAStatus() const;
@@ -64,11 +72,16 @@ public:
64 u8 GetFormatVersion() const; 72 u8 GetFormatVersion() const;
65 73
66 VirtualDir GetPartition(XCIPartition partition) const; 74 VirtualDir GetPartition(XCIPartition partition) const;
75 std::shared_ptr<NSP> GetSecurePartitionNSP() const;
67 VirtualDir GetSecurePartition() const; 76 VirtualDir GetSecurePartition() const;
68 VirtualDir GetNormalPartition() const; 77 VirtualDir GetNormalPartition() const;
69 VirtualDir GetUpdatePartition() const; 78 VirtualDir GetUpdatePartition() const;
70 VirtualDir GetLogoPartition() const; 79 VirtualDir GetLogoPartition() const;
71 80
81 u64 GetProgramTitleID() const;
82
83 std::shared_ptr<NCA> GetProgramNCA() const;
84 VirtualFile GetProgramNCAFile() const;
72 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; 85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
73 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; 86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
74 VirtualFile GetNCAFileByType(NCAContentType type) const; 87 VirtualFile GetNCAFileByType(NCAContentType type) const;
@@ -94,6 +107,8 @@ private:
94 Loader::ResultStatus program_nca_status; 107 Loader::ResultStatus program_nca_status;
95 108
96 std::vector<VirtualDir> partitions; 109 std::vector<VirtualDir> partitions;
110 std::shared_ptr<NSP> secure_partition;
111 std::shared_ptr<NCA> program;
97 std::vector<std::shared_ptr<NCA>> ncas; 112 std::vector<std::shared_ptr<NCA>> ncas;
98}; 113};
99} // namespace FileSys 114} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index e8b5d6ece..aa1b3c17d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -3,12 +3,17 @@
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 <cstring>
6#include <utility> 7#include <utility>
8
7#include <boost/optional.hpp> 9#include <boost/optional.hpp>
10
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "core/crypto/aes_util.h" 12#include "core/crypto/aes_util.h"
10#include "core/crypto/ctr_encryption_layer.h" 13#include "core/crypto/ctr_encryption_layer.h"
11#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/nca_patch.h"
16#include "core/file_sys/partition_filesystem.h"
12#include "core/file_sys/romfs.h" 17#include "core/file_sys/romfs.h"
13#include "core/file_sys/vfs_offset.h" 18#include "core/file_sys/vfs_offset.h"
14#include "core/loader/loader.h" 19#include "core/loader/loader.h"
@@ -64,10 +69,31 @@ struct RomFSSuperblock {
64}; 69};
65static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); 70static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
66 71
72struct BKTRHeader {
73 u64_le offset;
74 u64_le size;
75 u32_le magic;
76 INSERT_PADDING_BYTES(0x4);
77 u32_le number_entries;
78 INSERT_PADDING_BYTES(0x4);
79};
80static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
81
82struct BKTRSuperblock {
83 NCASectionHeaderBlock header_block;
84 IVFCHeader ivfc;
85 INSERT_PADDING_BYTES(0x18);
86 BKTRHeader relocation;
87 BKTRHeader subsection;
88 INSERT_PADDING_BYTES(0xC0);
89};
90static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
91
67union NCASectionHeader { 92union NCASectionHeader {
68 NCASectionRaw raw; 93 NCASectionRaw raw;
69 PFS0Superblock pfs0; 94 PFS0Superblock pfs0;
70 RomFSSuperblock romfs; 95 RomFSSuperblock romfs;
96 BKTRSuperblock bktr;
71}; 97};
72static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
73 99
@@ -100,7 +126,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
100 Core::Crypto::Key128 out; 126 Core::Crypto::Key128 out;
101 if (type == NCASectionCryptoType::XTS) 127 if (type == NCASectionCryptoType::XTS)
102 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); 128 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
103 else if (type == NCASectionCryptoType::CTR) 129 else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
104 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); 130 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
105 else 131 else
106 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", 132 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
@@ -150,6 +176,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
150 LOG_DEBUG(Crypto, "called with mode=NONE"); 176 LOG_DEBUG(Crypto, "called with mode=NONE");
151 return in; 177 return in;
152 case NCASectionCryptoType::CTR: 178 case NCASectionCryptoType::CTR:
179 // During normal BKTR decryption, this entire function is skipped. This is for the metadata,
180 // which uses the same CTR as usual.
181 case NCASectionCryptoType::BKTR:
153 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 182 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
154 { 183 {
155 boost::optional<Core::Crypto::Key128> key = boost::none; 184 boost::optional<Core::Crypto::Key128> key = boost::none;
@@ -186,7 +215,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
186 } 215 }
187} 216}
188 217
189NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
190 status = Loader::ResultStatus::Success; 221 status = Loader::ResultStatus::Success;
191 222
192 if (file == nullptr) { 223 if (file == nullptr) {
@@ -261,22 +292,21 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
261 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) { 292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
262 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; 293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
263 }) != sections.end(); 294 }) != sections.end();
295 ivfc_offset = 0;
264 296
265 for (std::ptrdiff_t i = 0; i < number_sections; ++i) { 297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
266 auto section = sections[i]; 298 auto section = sections[i];
267 299
268 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { 300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
269 const size_t romfs_offset = 301 const std::size_t base_offset =
270 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + 302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
271 section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; 303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
272 const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; 304 const std::size_t romfs_offset = base_offset + ivfc_offset;
273 auto dec = 305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
274 Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), 306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
275 romfs_offset); 307 auto dec = Decrypt(section, raw, romfs_offset);
276 if (dec != nullptr) { 308
277 files.push_back(std::move(dec)); 309 if (dec == nullptr) {
278 romfs = files.back();
279 } else {
280 if (status != Loader::ResultStatus::Success) 310 if (status != Loader::ResultStatus::Success)
281 return; 311 return;
282 if (has_rights_id) 312 if (has_rights_id)
@@ -285,6 +315,117 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
285 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; 315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
286 return; 316 return;
287 } 317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
288 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { 429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
289 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * 430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
290 MEDIA_OFFSET_MULTIPLIER) + 431 MEDIA_OFFSET_MULTIPLIER) +
@@ -300,6 +441,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
300 dirs.push_back(std::move(npfs)); 441 dirs.push_back(std::move(npfs));
301 if (IsDirectoryExeFS(dirs.back())) 442 if (IsDirectoryExeFS(dirs.back()))
302 exefs = dirs.back(); 443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
303 } 450 }
304 } else { 451 } else {
305 if (status != Loader::ResultStatus::Success) 452 if (status != Loader::ResultStatus::Success)
@@ -316,6 +463,8 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
316 status = Loader::ResultStatus::Success; 463 status = Loader::ResultStatus::Success;
317} 464}
318 465
466NCA::~NCA() = default;
467
319Loader::ResultStatus NCA::GetStatus() const { 468Loader::ResultStatus NCA::GetStatus() const {
320 return status; 469 return status;
321} 470}
@@ -345,11 +494,15 @@ NCAContentType NCA::GetType() const {
345} 494}
346 495
347u64 NCA::GetTitleId() const { 496u64 NCA::GetTitleId() const {
348 if (status != Loader::ResultStatus::Success) 497 if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS)
349 return {}; 498 return header.title_id | 0x800;
350 return header.title_id; 499 return header.title_id;
351} 500}
352 501
502bool NCA::IsUpdate() const {
503 return is_update;
504}
505
353VirtualFile NCA::GetRomFS() const { 506VirtualFile NCA::GetRomFS() const {
354 return romfs; 507 return romfs;
355} 508}
@@ -362,8 +515,8 @@ VirtualFile NCA::GetBaseFile() const {
362 return file; 515 return file;
363} 516}
364 517
365bool NCA::IsUpdate() const { 518u64 NCA::GetBaseIVFCOffset() const {
366 return is_update; 519 return ivfc_offset;
367} 520}
368 521
369bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 522bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index b961cfde7..f9f66cae9 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -12,10 +12,12 @@
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 "control_metadata.h"
16#include "core/crypto/key_manager.h" 15#include "core/crypto/key_manager.h"
17#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/vfs.h"
18#include "core/loader/loader.h" 17
18namespace Loader {
19enum class ResultStatus : u16;
20}
19 21
20namespace FileSys { 22namespace FileSys {
21 23
@@ -77,7 +79,10 @@ bool IsValidNCA(const NCAHeader& header);
77// After construction, use GetStatus to determine if the file is valid and ready to be used. 79// After construction, use GetStatus to determine if the file is valid and ready to be used.
78class NCA : public ReadOnlyVfsDirectory { 80class NCA : public ReadOnlyVfsDirectory {
79public: 81public:
80 explicit NCA(VirtualFile file); 82 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
83 u64 bktr_base_ivfc_offset = 0);
84 ~NCA() override;
85
81 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
82 87
83 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 88 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -87,13 +92,15 @@ public:
87 92
88 NCAContentType GetType() const; 93 NCAContentType GetType() const;
89 u64 GetTitleId() const; 94 u64 GetTitleId() const;
95 bool IsUpdate() const;
90 96
91 VirtualFile GetRomFS() const; 97 VirtualFile GetRomFS() const;
92 VirtualDir GetExeFS() const; 98 VirtualDir GetExeFS() const;
93 99
94 VirtualFile GetBaseFile() const; 100 VirtualFile GetBaseFile() const;
95 101
96 bool IsUpdate() const; 102 // Returns the base ivfc offset used in BKTR patching.
103 u64 GetBaseIVFCOffset() const;
97 104
98protected: 105protected:
99 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
@@ -110,14 +117,16 @@ private:
110 VirtualFile romfs = nullptr; 117 VirtualFile romfs = nullptr;
111 VirtualDir exefs = nullptr; 118 VirtualDir exefs = nullptr;
112 VirtualFile file; 119 VirtualFile file;
120 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset;
113 122
114 NCAHeader header{}; 123 NCAHeader header{};
115 bool has_rights_id{}; 124 bool has_rights_id{};
116 bool is_update{};
117 125
118 Loader::ResultStatus status{}; 126 Loader::ResultStatus status{};
119 127
120 bool encrypted; 128 bool encrypted;
129 bool is_update;
121 130
122 Core::Crypto::KeyManager keys; 131 Core::Crypto::KeyManager keys;
123}; 132};
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index ae21ad5b9..5b1177a03 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -8,6 +8,14 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11const std::array<const char*, 15> LANGUAGE_NAMES = {
12 "AmericanEnglish", "BritishEnglish", "Japanese",
13 "French", "German", "LatinAmericanSpanish",
14 "Spanish", "Italian", "Dutch",
15 "CanadianFrench", "Portugese", "Russian",
16 "Korean", "Taiwanese", "Chinese",
17};
18
11std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
12 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
13} 21}
@@ -20,8 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
20 file->ReadObject(raw.get()); 28 file->ReadObject(raw.get());
21} 29}
22 30
31NACP::~NACP() = default;
32
23const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 33const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
24 return raw->language_entries.at(static_cast<u8>(language)); 34 if (language != Language::Default) {
35 return raw->language_entries.at(static_cast<u8>(language));
36 }
37
38 for (const auto& language_entry : raw->language_entries) {
39 if (!language_entry.GetApplicationName().empty())
40 return language_entry;
41 }
42
43 // Fallback to English
44 return GetLanguageEntry(Language::AmericanEnglish);
25} 45}
26 46
27std::string NACP::GetApplicationName(Language language) const { 47std::string NACP::GetApplicationName(Language language) const {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 8c2cc1a2a..43d6f0719 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -8,6 +8,8 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/swap.h"
11#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
12 14
13namespace FileSys { 15namespace FileSys {
@@ -60,23 +62,22 @@ enum class Language : u8 {
60 Korean = 12, 62 Korean = 12,
61 Taiwanese = 13, 63 Taiwanese = 13,
62 Chinese = 14, 64 Chinese = 14,
65
66 Default = 255,
63}; 67};
64 68
65static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { 69extern const std::array<const char*, 15> LANGUAGE_NAMES;
66 "AmericanEnglish", "BritishEnglish", "Japanese",
67 "French", "German", "LatinAmericanSpanish",
68 "Spanish", "Italian", "Dutch",
69 "CanadianFrench", "Portugese", "Russian",
70 "Korean", "Taiwanese", "Chinese"};
71 70
72// A class representing the format used by NX metadata files, typically named Control.nacp. 71// A class representing the format used by NX metadata files, typically named Control.nacp.
73// These store application name, dev name, title id, and other miscellaneous data. 72// These store application name, dev name, title id, and other miscellaneous data.
74class NACP { 73class NACP {
75public: 74public:
76 explicit NACP(VirtualFile file); 75 explicit NACP(VirtualFile file);
77 const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const; 76 ~NACP();
78 std::string GetApplicationName(Language language = Language::AmericanEnglish) const; 77
79 std::string GetDeveloperName(Language language = Language::AmericanEnglish) const; 78 const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const;
80 u64 GetTitleId() const; 81 u64 GetTitleId() const;
81 std::string GetVersionString() const; 82 std::string GetVersionString() const;
82 83
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 3759e743a..12bb90ec8 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -25,7 +25,7 @@ enum EntryType : u8 {
25struct Entry { 25struct Entry {
26 Entry(std::string_view view, EntryType entry_type, u64 entry_size) 26 Entry(std::string_view view, EntryType entry_type, u64 entry_size)
27 : type{entry_type}, file_size{entry_size} { 27 : type{entry_type}, file_size{entry_size} {
28 const size_t copy_size = view.copy(filename, std::size(filename) - 1); 28 const std::size_t copy_size = view.copy(filename, std::size(filename) - 1);
29 filename[copy_size] = '\0'; 29 filename[copy_size] = '\0';
30 } 30 }
31 31
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 449244444..6f34b7836 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -3,20 +3,19 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_funcs.h" 6#include "common/common_types.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/swap.h" 8#include "common/swap.h"
9#include "content_archive.h"
10#include "core/file_sys/nca_metadata.h" 9#include "core/file_sys/nca_metadata.h"
11 10
12namespace FileSys { 11namespace FileSys {
13 12
14bool operator>=(TitleType lhs, TitleType rhs) { 13bool operator>=(TitleType lhs, TitleType rhs) {
15 return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs); 14 return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
16} 15}
17 16
18bool operator<=(TitleType lhs, TitleType rhs) { 17bool operator<=(TitleType lhs, TitleType rhs) {
19 return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs); 18 return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
20} 19}
21 20
22CNMT::CNMT(VirtualFile file) { 21CNMT::CNMT(VirtualFile file) {
@@ -52,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco
52 : header(std::move(header)), opt_header(std::move(opt_header)), 51 : header(std::move(header)), opt_header(std::move(opt_header)),
53 content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} 52 content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
54 53
54CNMT::~CNMT() = default;
55
55u64 CNMT::GetTitleID() const { 56u64 CNMT::GetTitleID() const {
56 return header.title_id; 57 return header.title_id;
57} 58}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index ce05b4c1d..a05d155f4 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
8#include <memory> 7#include <memory>
9#include <vector> 8#include <vector>
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
@@ -88,6 +87,7 @@ public:
88 explicit CNMT(VirtualFile file); 87 explicit CNMT(VirtualFile file);
89 CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, 88 CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
90 std::vector<MetaRecord> meta_records); 89 std::vector<MetaRecord> meta_records);
90 ~CNMT();
91 91
92 u64 GetTitleID() const; 92 u64 GetTitleID() const;
93 u32 GetTitleVersion() const; 93 u32 GetTitleVersion() const;
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
new file mode 100644
index 000000000..0090cc6c4
--- /dev/null
+++ b/src/core/file_sys/nca_patch.cpp
@@ -0,0 +1,210 @@
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 <cstddef>
7#include <cstring>
8
9#include "common/assert.h"
10#include "core/crypto/aes_util.h"
11#include "core/file_sys/nca_patch.h"
12
13namespace FileSys {
14
15BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
16 std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
17 std::vector<SubsectionBucket> subsection_buckets_, bool is_encrypted_,
18 Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_,
19 std::array<u8, 8> section_ctr_)
20 : relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)),
21 subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)),
22 base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
23 encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
24 section_ctr(section_ctr_) {
25 for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) {
26 relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
27 }
28
29 for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) {
30 subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
31 {0},
32 subsection_buckets[i + 1].entries[0].ctr});
33 }
34
35 relocation_buckets.back().entries.push_back({relocation.size, 0, 0});
36}
37
38BKTR::~BKTR() = default;
39
40std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
41 // Read out of bounds.
42 if (offset >= relocation.size)
43 return 0;
44 const auto relocation = GetRelocationEntry(offset);
45 const auto section_offset = offset - relocation.address_patch + relocation.address_source;
46 const auto bktr_read = relocation.from_patch;
47
48 const auto next_relocation = GetNextRelocationEntry(offset);
49
50 if (offset + length > next_relocation.address_patch) {
51 const u64 partition = next_relocation.address_patch - offset;
52 return Read(data, partition, offset) +
53 Read(data + partition, length - partition, offset + partition);
54 }
55
56 if (!bktr_read) {
57 ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative.");
58 return base_romfs->Read(data, length, section_offset - ivfc_offset);
59 }
60
61 if (!encrypted) {
62 return bktr_romfs->Read(data, length, section_offset);
63 }
64
65 const auto subsection = GetSubsectionEntry(section_offset);
66 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
67
68 // Calculate AES IV
69 std::vector<u8> iv(16);
70 auto subsection_ctr = subsection.ctr;
71 auto offset_iv = section_offset + base_offset;
72 for (std::size_t i = 0; i < section_ctr.size(); ++i)
73 iv[i] = section_ctr[0x8 - i - 1];
74 offset_iv >>= 4;
75 for (std::size_t i = 0; i < sizeof(u64); ++i) {
76 iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
77 offset_iv >>= 8;
78 }
79 for (std::size_t i = 0; i < sizeof(u32); ++i) {
80 iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
81 subsection_ctr >>= 8;
82 }
83 cipher.SetIV(iv);
84
85 const auto next_subsection = GetNextSubsectionEntry(section_offset);
86
87 if (section_offset + length > next_subsection.address_patch) {
88 const u64 partition = next_subsection.address_patch - section_offset;
89 return Read(data, partition, offset) +
90 Read(data + partition, length - partition, offset + partition);
91 }
92
93 const auto block_offset = section_offset & 0xF;
94 if (block_offset != 0) {
95 auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF);
96 cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt);
97 if (length + block_offset < 0x10) {
98 std::memcpy(data, block.data() + block_offset, std::min(length, block.size()));
99 return std::min(length, block.size());
100 }
101
102 const auto read = 0x10 - block_offset;
103 std::memcpy(data, block.data() + block_offset, read);
104 return read + Read(data + read, length - read, offset + read);
105 }
106
107 const auto raw_read = bktr_romfs->Read(data, length, section_offset);
108 cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt);
109 return raw_read;
110}
111
112template <bool Subsection, typename BlockType, typename BucketType>
113std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
114 BucketType buckets) const {
115 if constexpr (Subsection) {
116 const auto last_bucket = buckets[block.number_buckets - 1];
117 if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
118 return {block.number_buckets - 1, last_bucket.number_entries};
119 } else {
120 ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
121 }
122
123 std::size_t bucket_id = std::count_if(
124 block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
125 [&offset](u64 base_offset) { return base_offset <= offset; });
126
127 const auto bucket = buckets[bucket_id];
128
129 if (bucket.number_entries == 1)
130 return {bucket_id, 0};
131
132 std::size_t low = 0;
133 std::size_t mid = 0;
134 std::size_t high = bucket.number_entries - 1;
135 while (low <= high) {
136 mid = (low + high) / 2;
137 if (bucket.entries[mid].address_patch > offset) {
138 high = mid - 1;
139 } else {
140 if (mid == bucket.number_entries - 1 ||
141 bucket.entries[mid + 1].address_patch > offset) {
142 return {bucket_id, mid};
143 }
144
145 low = mid + 1;
146 }
147 }
148
149 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
150}
151
152RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
153 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
154 return relocation_buckets[res.first].entries[res.second];
155}
156
157RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const {
158 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
159 const auto bucket = relocation_buckets[res.first];
160 if (res.second + 1 < bucket.entries.size())
161 return bucket.entries[res.second + 1];
162 return relocation_buckets[res.first + 1].entries[0];
163}
164
165SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const {
166 const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
167 return subsection_buckets[res.first].entries[res.second];
168}
169
170SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const {
171 const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
172 const auto bucket = subsection_buckets[res.first];
173 if (res.second + 1 < bucket.entries.size())
174 return bucket.entries[res.second + 1];
175 return subsection_buckets[res.first + 1].entries[0];
176}
177
178std::string BKTR::GetName() const {
179 return base_romfs->GetName();
180}
181
182std::size_t BKTR::GetSize() const {
183 return relocation.size;
184}
185
186bool BKTR::Resize(std::size_t new_size) {
187 return false;
188}
189
190std::shared_ptr<VfsDirectory> BKTR::GetContainingDirectory() const {
191 return base_romfs->GetContainingDirectory();
192}
193
194bool BKTR::IsWritable() const {
195 return false;
196}
197
198bool BKTR::IsReadable() const {
199 return true;
200}
201
202std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) {
203 return 0;
204}
205
206bool BKTR::Rename(std::string_view name) {
207 return base_romfs->Rename(name);
208}
209
210} // namespace FileSys
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
new file mode 100644
index 000000000..8e64e8378
--- /dev/null
+++ b/src/core/file_sys/nca_patch.h
@@ -0,0 +1,150 @@
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 <memory>
9#include <vector>
10
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13#include "common/swap.h"
14#include "core/crypto/key_manager.h"
15
16namespace FileSys {
17
18#pragma pack(push, 1)
19struct RelocationEntry {
20 u64_le address_patch;
21 u64_le address_source;
22 u32 from_patch;
23};
24#pragma pack(pop)
25static_assert(sizeof(RelocationEntry) == 0x14, "RelocationEntry has incorrect size.");
26
27struct RelocationBucketRaw {
28 INSERT_PADDING_BYTES(4);
29 u32_le number_entries;
30 u64_le end_offset;
31 std::array<RelocationEntry, 0x332> relocation_entries;
32 INSERT_PADDING_BYTES(8);
33};
34static_assert(sizeof(RelocationBucketRaw) == 0x4000, "RelocationBucketRaw has incorrect size.");
35
36// Vector version of RelocationBucketRaw
37struct RelocationBucket {
38 u32 number_entries;
39 u64 end_offset;
40 std::vector<RelocationEntry> entries;
41};
42
43struct RelocationBlock {
44 INSERT_PADDING_BYTES(4);
45 u32_le number_buckets;
46 u64_le size;
47 std::array<u64, 0x7FE> base_offsets;
48};
49static_assert(sizeof(RelocationBlock) == 0x4000, "RelocationBlock has incorrect size.");
50
51struct SubsectionEntry {
52 u64_le address_patch;
53 INSERT_PADDING_BYTES(0x4);
54 u32_le ctr;
55};
56static_assert(sizeof(SubsectionEntry) == 0x10, "SubsectionEntry has incorrect size.");
57
58struct SubsectionBucketRaw {
59 INSERT_PADDING_BYTES(4);
60 u32_le number_entries;
61 u64_le end_offset;
62 std::array<SubsectionEntry, 0x3FF> subsection_entries;
63};
64static_assert(sizeof(SubsectionBucketRaw) == 0x4000, "SubsectionBucketRaw has incorrect size.");
65
66// Vector version of SubsectionBucketRaw
67struct SubsectionBucket {
68 u32 number_entries;
69 u64 end_offset;
70 std::vector<SubsectionEntry> entries;
71};
72
73struct SubsectionBlock {
74 INSERT_PADDING_BYTES(4);
75 u32_le number_buckets;
76 u64_le size;
77 std::array<u64, 0x7FE> base_offsets;
78};
79static_assert(sizeof(SubsectionBlock) == 0x4000, "SubsectionBlock has incorrect size.");
80
81inline RelocationBucket ConvertRelocationBucketRaw(RelocationBucketRaw raw) {
82 return {raw.number_entries,
83 raw.end_offset,
84 {raw.relocation_entries.begin(), raw.relocation_entries.begin() + raw.number_entries}};
85}
86
87inline SubsectionBucket ConvertSubsectionBucketRaw(SubsectionBucketRaw raw) {
88 return {raw.number_entries,
89 raw.end_offset,
90 {raw.subsection_entries.begin(), raw.subsection_entries.begin() + raw.number_entries}};
91}
92
93class BKTR : public VfsFile {
94public:
95 BKTR(VirtualFile base_romfs, VirtualFile bktr_romfs, RelocationBlock relocation,
96 std::vector<RelocationBucket> relocation_buckets, SubsectionBlock subsection,
97 std::vector<SubsectionBucket> subsection_buckets, bool is_encrypted,
98 Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr);
99 ~BKTR() override;
100
101 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
102
103 std::string GetName() const override;
104
105 std::size_t GetSize() const override;
106
107 bool Resize(std::size_t new_size) override;
108
109 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
110
111 bool IsWritable() const override;
112
113 bool IsReadable() const override;
114
115 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
116
117 bool Rename(std::string_view name) override;
118
119private:
120 template <bool Subsection, typename BlockType, typename BucketType>
121 std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
122 BucketType buckets) const;
123
124 RelocationEntry GetRelocationEntry(u64 offset) const;
125 RelocationEntry GetNextRelocationEntry(u64 offset) const;
126
127 SubsectionEntry GetSubsectionEntry(u64 offset) const;
128 SubsectionEntry GetNextSubsectionEntry(u64 offset) const;
129
130 RelocationBlock relocation;
131 std::vector<RelocationBucket> relocation_buckets;
132 SubsectionBlock subsection;
133 std::vector<SubsectionBucket> subsection_buckets;
134
135 // Should be the raw base romfs, decrypted.
136 VirtualFile base_romfs;
137 // Should be the raw BKTR romfs, (located at media_offset with size media_size).
138 VirtualFile bktr_romfs;
139
140 bool encrypted;
141 Core::Crypto::Key128 key;
142
143 // Base offset into NCA, used for IV calculation.
144 u64 base_offset;
145 // Distance between IVFC start and RomFS start, used for base reads
146 u64 ivfc_offset;
147 std::array<u8, 8> section_ctr;
148};
149
150} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index c377edc9c..5791c76ff 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -42,21 +42,21 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
42 42
43 is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); 43 is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
44 44
45 size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); 45 std::size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
46 size_t metadata_size = 46 std::size_t metadata_size =
47 sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; 47 sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
48 48
49 // Actually read in now... 49 // Actually read in now...
50 std::vector<u8> file_data = file->ReadBytes(metadata_size); 50 std::vector<u8> file_data = file->ReadBytes(metadata_size);
51 const size_t total_size = file_data.size(); 51 const std::size_t total_size = file_data.size();
52 52
53 if (total_size != metadata_size) { 53 if (total_size != metadata_size) {
54 status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; 54 status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
55 return; 55 return;
56 } 56 }
57 57
58 size_t entries_offset = sizeof(Header); 58 std::size_t entries_offset = sizeof(Header);
59 size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); 59 std::size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
60 content_offset = strtab_offset + pfs_header.strtab_size; 60 content_offset = strtab_offset + pfs_header.strtab_size;
61 for (u16 i = 0; i < pfs_header.num_entries; i++) { 61 for (u16 i = 0; i < pfs_header.num_entries; i++) {
62 FSEntry entry; 62 FSEntry entry;
@@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
72 status = Loader::ResultStatus::Success; 72 status = Loader::ResultStatus::Success;
73} 73}
74 74
75PartitionFilesystem::~PartitionFilesystem() = default;
76
75Loader::ResultStatus PartitionFilesystem::GetStatus() const { 77Loader::ResultStatus PartitionFilesystem::GetStatus() const {
76 return status; 78 return status;
77} 79}
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index be7bc32a8..739c63a7f 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -25,6 +25,8 @@ namespace FileSys {
25class PartitionFilesystem : public ReadOnlyVfsDirectory { 25class PartitionFilesystem : public ReadOnlyVfsDirectory {
26public: 26public:
27 explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); 27 explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
28 ~PartitionFilesystem() override;
29
28 Loader::ResultStatus GetStatus() const; 30 Loader::ResultStatus GetStatus() const;
29 31
30 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 32 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -79,7 +81,7 @@ private:
79 81
80 Header pfs_header{}; 82 Header pfs_header{};
81 bool is_hfs = false; 83 bool is_hfs = false;
82 size_t content_offset = 0; 84 std::size_t content_offset = 0;
83 85
84 std::vector<VirtualFile> pfs_files; 86 std::vector<VirtualFile> pfs_files;
85 std::vector<VirtualDir> pfs_dirs; 87 std::vector<VirtualDir> pfs_dirs;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
new file mode 100644
index 000000000..aebc69d52
--- /dev/null
+++ b/src/core/file_sys/patch_manager.cpp
@@ -0,0 +1,159 @@
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 <cstddef>
7
8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/romfs.h"
14#include "core/hle/service/filesystem/filesystem.h"
15#include "core/loader/loader.h"
16
17namespace FileSys {
18
19constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
20
21std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
22 std::array<u8, sizeof(u32)> bytes{};
23 bytes[0] = version % SINGLE_BYTE_MODULUS;
24 for (std::size_t i = 1; i < bytes.size(); ++i) {
25 version /= SINGLE_BYTE_MODULUS;
26 bytes[i] = version % SINGLE_BYTE_MODULUS;
27 }
28
29 if (format == TitleVersionFormat::FourElements)
30 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
31 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
32}
33
34constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
35 "Update",
36};
37
38std::string FormatPatchTypeName(PatchType type) {
39 return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type));
40}
41
42PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
43
44PatchManager::~PatchManager() = default;
45
46VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
47 LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
48
49 if (exefs == nullptr)
50 return exefs;
51
52 const auto installed = Service::FileSystem::GetUnionContents();
53
54 // Game Updates
55 const auto update_tid = GetUpdateTitleID(title_id);
56 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
57 if (update != nullptr) {
58 if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
59 update->GetExeFS() != nullptr) {
60 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
61 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
62 exefs = update->GetExeFS();
63 }
64 }
65
66 return exefs;
67}
68
69VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
70 ContentRecordType type) const {
71 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
72 static_cast<u8>(type));
73
74 if (romfs == nullptr)
75 return romfs;
76
77 const auto installed = Service::FileSystem::GetUnionContents();
78
79 // Game Updates
80 const auto update_tid = GetUpdateTitleID(title_id);
81 const auto update = installed->GetEntryRaw(update_tid, type);
82 if (update != nullptr) {
83 const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
84 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
85 new_nca->GetRomFS() != nullptr) {
86 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
87 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
88 romfs = new_nca->GetRomFS();
89 }
90 }
91
92 return romfs;
93}
94
95std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
96 std::map<PatchType, std::string> out;
97 const auto installed = Service::FileSystem::GetUnionContents();
98
99 const auto update_tid = GetUpdateTitleID(title_id);
100 PatchManager update{update_tid};
101 auto [nacp, discard_icon_file] = update.GetControlMetadata();
102
103 if (nacp != nullptr) {
104 out[PatchType::Update] = nacp->GetVersionString();
105 } else {
106 if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
107 const auto meta_ver = installed->GetEntryVersion(update_tid);
108 if (meta_ver == boost::none || meta_ver.get() == 0) {
109 out[PatchType::Update] = "";
110 } else {
111 out[PatchType::Update] =
112 FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
113 }
114 }
115 }
116
117 return out;
118}
119
120std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
121 const auto& installed{Service::FileSystem::GetUnionContents()};
122
123 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
124 if (base_control_nca == nullptr)
125 return {};
126
127 return ParseControlNCA(base_control_nca);
128}
129
130std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
131 const std::shared_ptr<NCA>& nca) const {
132 const auto base_romfs = nca->GetRomFS();
133 if (base_romfs == nullptr)
134 return {};
135
136 const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
137 if (romfs == nullptr)
138 return {};
139
140 const auto extracted = ExtractRomFS(romfs);
141 if (extracted == nullptr)
142 return {};
143
144 auto nacp_file = extracted->GetFile("control.nacp");
145 if (nacp_file == nullptr)
146 nacp_file = extracted->GetFile("Control.nacp");
147
148 const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
149
150 VirtualFile icon_file;
151 for (const auto& language : FileSys::LANGUAGE_NAMES) {
152 icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
153 if (icon_file != nullptr)
154 break;
155 }
156
157 return {nacp, icon_file};
158}
159} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
new file mode 100644
index 000000000..209cab1dc
--- /dev/null
+++ b/src/core/file_sys/patch_manager.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 <map>
8#include <memory>
9#include <string>
10#include "common/common_types.h"
11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs.h"
13
14namespace FileSys {
15
16class NCA;
17class NACP;
18
19enum class TitleVersionFormat : u8 {
20 ThreeElements, ///< vX.Y.Z
21 FourElements, ///< vX.Y.Z.W
22};
23
24std::string FormatTitleVersion(u32 version,
25 TitleVersionFormat format = TitleVersionFormat::ThreeElements);
26
27enum class PatchType {
28 Update,
29};
30
31std::string FormatPatchTypeName(PatchType type);
32
33// A centralized class to manage patches to games.
34class PatchManager {
35public:
36 explicit PatchManager(u64 title_id);
37 ~PatchManager();
38
39 // Currently tracked ExeFS patches:
40 // - Game Updates
41 VirtualDir PatchExeFS(VirtualDir exefs) const;
42
43 // Currently tracked RomFS patches:
44 // - Game Updates
45 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
46 ContentRecordType type = ContentRecordType::Program) const;
47
48 // Returns a vector of pairs between patch names and patch versions.
49 // i.e. Update v80 will return {Update, 80}
50 std::map<PatchType, std::string> GetPatchVersionNames() const;
51
52 // Given title_id of the program, attempts to get the control data of the update and parse it,
53 // falling back to the base control data.
54 std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const;
55
56 // Version of GetControlMetadata that takes an arbitrary NCA
57 std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA(
58 const std::shared_ptr<NCA>& nca) const;
59
60private:
61 u64 title_id;
62};
63
64} // namespace FileSys
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 279f987d4..02319ce0f 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -2,15 +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 "common/file_util.h" 5#include <cstddef>
6#include <cstring>
7#include <vector>
8
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
8#include "core/loader/loader.h" 11#include "core/loader/loader.h"
9 12
10namespace FileSys { 13namespace FileSys {
11 14
15ProgramMetadata::ProgramMetadata() = default;
16
17ProgramMetadata::~ProgramMetadata() = default;
18
12Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { 19Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
13 size_t total_size = static_cast<size_t>(file->GetSize()); 20 std::size_t total_size = static_cast<std::size_t>(file->GetSize());
14 if (total_size < sizeof(Header)) 21 if (total_size < sizeof(Header))
15 return Loader::ResultStatus::ErrorBadNPDMHeader; 22 return Loader::ResultStatus::ErrorBadNPDMHeader;
16 23
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 74a91052b..1143e36c4 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -5,12 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <string>
9#include <vector>
10#include "common/bit_field.h" 8#include "common/bit_field.h"
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "common/swap.h" 10#include "common/swap.h"
13#include "partition_filesystem.h" 11#include "core/file_sys/vfs.h"
14 12
15namespace Loader { 13namespace Loader {
16enum class ResultStatus : u16; 14enum class ResultStatus : u16;
@@ -38,6 +36,9 @@ enum class ProgramFilePermission : u64 {
38 */ 36 */
39class ProgramMetadata { 37class ProgramMetadata {
40public: 38public:
39 ProgramMetadata();
40 ~ProgramMetadata();
41
41 Loader::ResultStatus Load(VirtualFile file); 42 Loader::ResultStatus Load(VirtualFile file);
42 43
43 bool Is64BitProgram() const; 44 bool Is64BitProgram() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index dacf8568b..dad7ae10b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -5,13 +5,17 @@
5#include <regex> 5#include <regex>
6#include <mbedtls/sha256.h> 6#include <mbedtls/sha256.h>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/file_util.h"
8#include "common/hex_util.h" 9#include "common/hex_util.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "core/crypto/encryption_layer.h" 11#include "core/crypto/key_manager.h"
11#include "core/file_sys/card_image.h" 12#include "core/file_sys/card_image.h"
13#include "core/file_sys/content_archive.h"
12#include "core/file_sys/nca_metadata.h" 14#include "core/file_sys/nca_metadata.h"
13#include "core/file_sys/registered_cache.h" 15#include "core/file_sys/registered_cache.h"
16#include "core/file_sys/submission_package.h"
14#include "core/file_sys/vfs_concat.h" 17#include "core/file_sys/vfs_concat.h"
18#include "core/loader/loader.h"
15 19
16namespace FileSys { 20namespace FileSys {
17std::string RegisteredCacheEntry::DebugInfo() const { 21std::string RegisteredCacheEntry::DebugInfo() const {
@@ -58,11 +62,11 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
58 "" ///< Currently unknown 'DeltaTitle' 62 "" ///< Currently unknown 'DeltaTitle'
59 }; 63 };
60 64
61 auto index = static_cast<size_t>(type); 65 auto index = static_cast<std::size_t>(type);
62 // If the index is after the jump in TitleType, subtract it out. 66 // If the index is after the jump in TitleType, subtract it out.
63 if (index >= static_cast<size_t>(TitleType::Application)) { 67 if (index >= static_cast<std::size_t>(TitleType::Application)) {
64 index -= static_cast<size_t>(TitleType::Application) - 68 index -= static_cast<std::size_t>(TitleType::Application) -
65 static_cast<size_t>(TitleType::FirmwarePackageB); 69 static_cast<std::size_t>(TitleType::FirmwarePackageB);
66 } 70 }
67 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); 71 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
68} 72}
@@ -101,7 +105,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
101 } else { 105 } else {
102 std::vector<VirtualFile> concat; 106 std::vector<VirtualFile> concat;
103 // Since the files are a two-digit hex number, max is FF. 107 // Since the files are a two-digit hex number, max is FF.
104 for (size_t i = 0; i < 0x100; ++i) { 108 for (std::size_t i = 0; i < 0x100; ++i) {
105 auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); 109 auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
106 if (next != nullptr) { 110 if (next != nullptr) {
107 concat.push_back(std::move(next)); 111 concat.push_back(std::move(next));
@@ -276,6 +280,18 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const
276 return GetEntryUnparsed(entry.title_id, entry.type); 280 return GetEntryUnparsed(entry.title_id, entry.type);
277} 281}
278 282
283boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
284 const auto meta_iter = meta.find(title_id);
285 if (meta_iter != meta.end())
286 return meta_iter->second.GetTitleVersion();
287
288 const auto yuzu_meta_iter = yuzu_meta.find(title_id);
289 if (yuzu_meta_iter != yuzu_meta.end())
290 return yuzu_meta_iter->second.GetTitleVersion();
291
292 return boost::none;
293}
294
279VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { 295VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
280 const auto id = GetNcaIDFromMetadata(title_id, type); 296 const auto id = GetNcaIDFromMetadata(title_id, type);
281 if (id == boost::none) 297 if (id == boost::none)
@@ -355,17 +371,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
355 return out; 371 return out;
356} 372}
357 373
358static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) { 374static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
359 const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false)); 375 const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
360 const auto iter = 376 if (file == nullptr)
361 std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(), 377 return nullptr;
362 [&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; }); 378 return std::make_shared<NCA>(file);
363 return iter == xci->GetNCAs().end() ? nullptr : *iter;
364} 379}
365 380
366InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, 381InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
367 const VfsCopyFunction& copy) { 382 const VfsCopyFunction& copy) {
368 const auto& ncas = xci->GetNCAs(); 383 return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
384}
385
386InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
387 const VfsCopyFunction& copy) {
388 const auto& ncas = nsp->GetNCAsCollapsed();
369 const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { 389 const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
370 return nca->GetType() == NCAContentType::Meta; 390 return nca->GetType() == NCAContentType::Meta;
371 }); 391 });
@@ -389,7 +409,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw
389 const auto cnmt_file = section0->GetFiles()[0]; 409 const auto cnmt_file = section0->GetFiles()[0];
390 const CNMT cnmt(cnmt_file); 410 const CNMT cnmt(cnmt_file);
391 for (const auto& record : cnmt.GetContentRecords()) { 411 for (const auto& record : cnmt.GetContentRecords()) {
392 const auto nca = GetNCAFromXCIForID(xci, record.nca_id); 412 const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
393 if (nca == nullptr) 413 if (nca == nullptr)
394 return InstallResult::ErrorCopyFailed; 414 return InstallResult::ErrorCopyFailed;
395 const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); 415 const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
@@ -490,4 +510,107 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
490 kv.second.GetTitleID() == cnmt.GetTitleID(); 510 kv.second.GetTitleID() == cnmt.GetTitleID();
491 }) != yuzu_meta.end(); 511 }) != yuzu_meta.end();
492} 512}
513
514RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches)
515 : caches(std::move(caches)) {}
516
517void RegisteredCacheUnion::Refresh() {
518 for (const auto& c : caches)
519 c->Refresh();
520}
521
522bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
523 return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
524 return cache->HasEntry(title_id, type);
525 });
526}
527
528bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
529 return HasEntry(entry.title_id, entry.type);
530}
531
532boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
533 for (const auto& c : caches) {
534 const auto res = c->GetEntryVersion(title_id);
535 if (res != boost::none)
536 return res;
537 }
538
539 return boost::none;
540}
541
542VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
543 for (const auto& c : caches) {
544 const auto res = c->GetEntryUnparsed(title_id, type);
545 if (res != nullptr)
546 return res;
547 }
548
549 return nullptr;
550}
551
552VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
553 return GetEntryUnparsed(entry.title_id, entry.type);
554}
555
556VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
557 for (const auto& c : caches) {
558 const auto res = c->GetEntryRaw(title_id, type);
559 if (res != nullptr)
560 return res;
561 }
562
563 return nullptr;
564}
565
566VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
567 return GetEntryRaw(entry.title_id, entry.type);
568}
569
570std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
571 const auto raw = GetEntryRaw(title_id, type);
572 if (raw == nullptr)
573 return nullptr;
574 return std::make_shared<NCA>(raw);
575}
576
577std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
578 return GetEntry(entry.title_id, entry.type);
579}
580
581std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
582 std::vector<RegisteredCacheEntry> out;
583 for (const auto& c : caches) {
584 c->IterateAllMetadata<RegisteredCacheEntry>(
585 out,
586 [](const CNMT& c, const ContentRecord& r) {
587 return RegisteredCacheEntry{c.GetTitleID(), r.type};
588 },
589 [](const CNMT& c, const ContentRecord& r) { return true; });
590 }
591 return out;
592}
593
594std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
595 boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
596 boost::optional<u64> title_id) const {
597 std::vector<RegisteredCacheEntry> out;
598 for (const auto& c : caches) {
599 c->IterateAllMetadata<RegisteredCacheEntry>(
600 out,
601 [](const CNMT& c, const ContentRecord& r) {
602 return RegisteredCacheEntry{c.GetTitleID(), r.type};
603 },
604 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
605 if (title_type != boost::none && title_type.get() != c.GetType())
606 return false;
607 if (record_type != boost::none && record_type.get() != r.type)
608 return false;
609 if (title_id != boost::none && title_id.get() != c.GetTitleID())
610 return false;
611 return true;
612 });
613 }
614 return out;
615}
493} // namespace FileSys 616} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 7b8955dfa..f487b0cf0 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -11,15 +11,19 @@
11#include <string> 11#include <string>
12#include <vector> 12#include <vector>
13#include <boost/container/flat_map.hpp> 13#include <boost/container/flat_map.hpp>
14#include "common/common_funcs.h"
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "content_archive.h"
17#include "core/file_sys/nca_metadata.h"
18#include "core/file_sys/vfs.h" 15#include "core/file_sys/vfs.h"
19 16
20namespace FileSys { 17namespace FileSys {
21class XCI;
22class CNMT; 18class CNMT;
19class NCA;
20class NSP;
21class XCI;
22
23enum class ContentRecordType : u8;
24enum class TitleType : u8;
25
26struct ContentRecord;
23 27
24using NcaID = std::array<u8, 0x10>; 28using NcaID = std::array<u8, 0x10>;
25using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -39,6 +43,10 @@ struct RegisteredCacheEntry {
39 std::string DebugInfo() const; 43 std::string DebugInfo() const;
40}; 44};
41 45
46constexpr u64 GetUpdateTitleID(u64 base_title_id) {
47 return base_title_id | 0x800;
48}
49
42// boost flat_map requires operator< for O(log(n)) lookups. 50// boost flat_map requires operator< for O(log(n)) lookups.
43bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
44 52
@@ -56,6 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
56 * 4GB splitting can be ignored.) 64 * 4GB splitting can be ignored.)
57 */ 65 */
58class RegisteredCache { 66class RegisteredCache {
67 friend class RegisteredCacheUnion;
68
59public: 69public:
60 // Parsing function defines the conversion from raw file to NCA. If there are other steps 70 // Parsing function defines the conversion from raw file to NCA. If there are other steps
61 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 71 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
@@ -70,6 +80,8 @@ public:
70 bool HasEntry(u64 title_id, ContentRecordType type) const; 80 bool HasEntry(u64 title_id, ContentRecordType type) const;
71 bool HasEntry(RegisteredCacheEntry entry) const; 81 bool HasEntry(RegisteredCacheEntry entry) const;
72 82
83 boost::optional<u32> GetEntryVersion(u64 title_id) const;
84
73 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 85 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
74 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; 86 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
75 87
@@ -86,10 +98,12 @@ public:
86 boost::optional<ContentRecordType> record_type = boost::none, 98 boost::optional<ContentRecordType> record_type = boost::none,
87 boost::optional<u64> title_id = boost::none) const; 99 boost::optional<u64> title_id = boost::none) const;
88 100
89 // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there 101 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
90 // is a meta NCA and all of them are accessible. 102 // there is a meta NCA and all of them are accessible.
91 InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, 103 InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
92 const VfsCopyFunction& copy = &VfsRawCopy); 104 const VfsCopyFunction& copy = &VfsRawCopy);
105 InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
106 const VfsCopyFunction& copy = &VfsRawCopy);
93 107
94 // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this 108 // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
95 // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a 109 // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
@@ -125,4 +139,36 @@ private:
125 boost::container::flat_map<u64, CNMT> yuzu_meta; 139 boost::container::flat_map<u64, CNMT> yuzu_meta;
126}; 140};
127 141
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion {
144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches);
146
147 void Refresh();
148
149 bool HasEntry(u64 title_id, ContentRecordType type) const;
150 bool HasEntry(RegisteredCacheEntry entry) const;
151
152 boost::optional<u32> GetEntryVersion(u64 title_id) const;
153
154 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
155 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
156
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162
163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries.
165 std::vector<RegisteredCacheEntry> ListEntriesFilter(
166 boost::optional<TitleType> title_type = boost::none,
167 boost::optional<ContentRecordType> record_type = boost::none,
168 boost::optional<u64> title_id = boost::none) const;
169
170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches;
172};
173
128} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index e490c8ace..9f6e41cdf 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -49,7 +49,7 @@ struct FileEntry {
49static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); 49static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
50 50
51template <typename Entry> 51template <typename Entry>
52static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { 52static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
53 Entry entry{}; 53 Entry entry{};
54 if (file->ReadObject(&entry, offset) != sizeof(Entry)) 54 if (file->ReadObject(&entry, offset) != sizeof(Entry))
55 return {}; 55 return {};
@@ -59,8 +59,8 @@ static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t of
59 return {entry, string}; 59 return {entry, string};
60} 60}
61 61
62void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, 62void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
63 std::shared_ptr<VectorVfsDirectory> parent) { 63 u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
64 while (true) { 64 while (true) {
65 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); 65 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
66 66
@@ -74,8 +74,9 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t
74 } 74 }
75} 75}
76 76
77void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, 77void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
78 u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { 78 std::size_t data_offset, u32 this_dir_offset,
79 std::shared_ptr<VectorVfsDirectory> parent) {
79 while (true) { 80 while (true) {
80 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); 81 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
81 auto current = std::make_shared<VectorVfsDirectory>( 82 auto current = std::make_shared<VectorVfsDirectory>(
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 03a876d22..e54a7d7a9 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
10#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
11 12
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index eb4e6c865..3d1a3685e 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -2,11 +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 <algorithm>
6#include <memory> 5#include <memory>
6#include "common/assert.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/file_sys/content_archive.h"
9#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/patch_manager.h"
10#include "core/file_sys/registered_cache.h" 13#include "core/file_sys/registered_cache.h"
11#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
12#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -20,10 +23,19 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
20 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { 23 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
21 LOG_ERROR(Service_FS, "Unable to read RomFS!"); 24 LOG_ERROR(Service_FS, "Unable to read RomFS!");
22 } 25 }
26
27 updatable = app_loader.IsRomFSUpdatable();
28 ivfc_offset = app_loader.ReadRomFSIVFCOffset();
23} 29}
24 30
31RomFSFactory::~RomFSFactory() = default;
32
25ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 33ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
26 return MakeResult<VirtualFile>(file); 34 if (!updatable)
35 return MakeResult<VirtualFile>(file);
36
37 const PatchManager patch_manager(Core::CurrentProcess()->program_id);
38 return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
27} 39}
28 40
29ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { 41ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index f38ddc4f7..2cace8180 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -30,12 +30,15 @@ enum class StorageId : u8 {
30class RomFSFactory { 30class RomFSFactory {
31public: 31public:
32 explicit RomFSFactory(Loader::AppLoader& app_loader); 32 explicit RomFSFactory(Loader::AppLoader& app_loader);
33 ~RomFSFactory();
33 34
34 ResultVal<VirtualFile> OpenCurrentProcess(); 35 ResultVal<VirtualFile> OpenCurrentProcess();
35 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); 36 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
36 37
37private: 38private:
38 VirtualFile file; 39 VirtualFile file;
40 bool updatable;
41 u64 ivfc_offset;
39}; 42};
40 43
41} // namespace FileSys 44} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 952bd74b3..9b2c51bbd 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include "common/assert.h"
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "core/core.h" 9#include "core/core.h"
@@ -19,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const {
19 20
20SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} 21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
21 22
23SaveDataFactory::~SaveDataFactory() = default;
24
22ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { 25ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
23 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 26 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
24 if (meta.zero_1 != 0) { 27 if (meta.zero_1 != 0) {
@@ -84,10 +87,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
84 87
85 switch (space) { 88 switch (space) {
86 case SaveDataSpaceId::NandSystem: 89 case SaveDataSpaceId::NandSystem:
87 out = "/system/save/"; 90 out = "/system/";
88 break; 91 break;
89 case SaveDataSpaceId::NandUser: 92 case SaveDataSpaceId::NandUser:
90 out = "/user/save/"; 93 out = "/user/";
91 break; 94 break;
92 default: 95 default:
93 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
@@ -95,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
95 98
96 switch (type) { 99 switch (type) {
97 case SaveDataType::SystemSaveData: 100 case SaveDataType::SystemSaveData:
98 return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
99 case SaveDataType::SaveData: 102 case SaveDataType::SaveData:
100 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id);
105 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
101 title_id); 107 title_id);
102 default: 108 default:
103 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index c6f9549f0..d69ef6741 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_funcs.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/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
@@ -47,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr
47class SaveDataFactory { 48class SaveDataFactory {
48public: 49public:
49 explicit SaveDataFactory(VirtualDir dir); 50 explicit SaveDataFactory(VirtualDir dir);
51 ~SaveDataFactory();
50 52
51 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
52 54
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
new file mode 100644
index 000000000..11264878d
--- /dev/null
+++ b/src/core/file_sys/submission_package.cpp
@@ -0,0 +1,245 @@
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 <cstring>
7#include <string_view>
8
9#include <fmt/ostream.h>
10
11#include "common/hex_util.h"
12#include "common/logging/log.h"
13#include "core/crypto/key_manager.h"
14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/nca_metadata.h"
16#include "core/file_sys/partition_filesystem.h"
17#include "core/file_sys/submission_package.h"
18#include "core/loader/loader.h"
19
20namespace FileSys {
21NSP::NSP(VirtualFile file_)
22 : file(std::move(file_)), status{Loader::ResultStatus::Success},
23 pfs(std::make_shared<PartitionFilesystem>(file)) {
24 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
25 status = pfs->GetStatus();
26 return;
27 }
28
29 if (IsDirectoryExeFS(pfs)) {
30 extracted = true;
31 exefs = pfs;
32
33 const auto& files = pfs->GetFiles();
34 const auto romfs_iter =
35 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
36 return file->GetName().find(".romfs") != std::string::npos;
37 });
38 if (romfs_iter != files.end())
39 romfs = *romfs_iter;
40 return;
41 }
42
43 extracted = false;
44 const auto files = pfs->GetFiles();
45
46 Core::Crypto::KeyManager keys;
47 for (const auto& ticket_file : files) {
48 if (ticket_file->GetExtension() == "tik") {
49 if (ticket_file == nullptr ||
50 ticket_file->GetSize() <
51 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
52 continue;
53 }
54
55 Core::Crypto::Key128 key{};
56 ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
57 std::string_view name_only(ticket_file->GetName());
58 name_only.remove_suffix(4);
59 const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
60 u128 rights_id;
61 std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
62 keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
63 }
64 }
65
66 for (const auto& outer_file : files) {
67 if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
68 const auto nca = std::make_shared<NCA>(outer_file);
69 if (nca->GetStatus() != Loader::ResultStatus::Success) {
70 program_status[nca->GetTitleId()] = nca->GetStatus();
71 continue;
72 }
73
74 const auto section0 = nca->GetSubdirectories()[0];
75
76 for (const auto& inner_file : section0->GetFiles()) {
77 if (inner_file->GetExtension() != "cnmt")
78 continue;
79
80 const CNMT cnmt(inner_file);
81 auto& ncas_title = ncas[cnmt.GetTitleID()];
82
83 ncas_title[ContentRecordType::Meta] = nca;
84 for (const auto& rec : cnmt.GetContentRecords()) {
85 const auto id_string = Common::HexArrayToString(rec.nca_id, false);
86 const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
87 if (next_file == nullptr) {
88 LOG_WARNING(Service_FS,
89 "NCA with ID {}.nca is listed in content metadata, but cannot "
90 "be found in PFS. NSP appears to be corrupted.",
91 id_string);
92 continue;
93 }
94
95 auto next_nca = std::make_shared<NCA>(next_file);
96 if (next_nca->GetType() == NCAContentType::Program)
97 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
98 if (next_nca->GetStatus() == Loader::ResultStatus::Success)
99 ncas_title[rec.type] = std::move(next_nca);
100 }
101
102 break;
103 }
104 }
105 }
106}
107
108NSP::~NSP() = default;
109
110Loader::ResultStatus NSP::GetStatus() const {
111 return status;
112}
113
114Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
115 const auto iter = program_status.find(title_id);
116 if (iter == program_status.end())
117 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
118 return iter->second;
119}
120
121u64 NSP::GetFirstTitleID() const {
122 if (program_status.empty())
123 return 0;
124 return program_status.begin()->first;
125}
126
127u64 NSP::GetProgramTitleID() const {
128 const auto out = GetFirstTitleID();
129 if ((out & 0x800) == 0)
130 return out;
131
132 const auto ids = GetTitleIDs();
133 const auto iter =
134 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
135 return iter == ids.end() ? out : *iter;
136}
137
138std::vector<u64> NSP::GetTitleIDs() const {
139 std::vector<u64> out;
140 out.reserve(ncas.size());
141 for (const auto& kv : ncas)
142 out.push_back(kv.first);
143 return out;
144}
145
146bool NSP::IsExtractedType() const {
147 return extracted;
148}
149
150VirtualFile NSP::GetRomFS() const {
151 return romfs;
152}
153
154VirtualDir NSP::GetExeFS() const {
155 return exefs;
156}
157
158std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const {
159 if (extracted)
160 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
161 std::vector<std::shared_ptr<NCA>> out;
162 for (const auto& map : ncas) {
163 for (const auto& inner_map : map.second)
164 out.push_back(inner_map.second);
165 }
166 return out;
167}
168
169std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
170 if (extracted)
171 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
172 std::multimap<u64, std::shared_ptr<NCA>> out;
173 for (const auto& map : ncas) {
174 for (const auto& inner_map : map.second)
175 out.emplace(map.first, inner_map.second);
176 }
177 return out;
178}
179
180std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
181 return ncas;
182}
183
184std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
185 if (extracted)
186 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
187
188 const auto title_id_iter = ncas.find(title_id);
189 if (title_id_iter == ncas.end())
190 return nullptr;
191
192 const auto type_iter = title_id_iter->second.find(type);
193 if (type_iter == title_id_iter->second.end())
194 return nullptr;
195
196 return type_iter->second;
197}
198
199VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
200 if (extracted)
201 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
202 const auto nca = GetNCA(title_id, type);
203 if (nca != nullptr)
204 return nca->GetBaseFile();
205 return nullptr;
206}
207
208std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
209 if (extracted)
210 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
211 std::vector<Core::Crypto::Key128> out;
212 for (const auto& ticket_file : ticket_files) {
213 if (ticket_file == nullptr ||
214 ticket_file->GetSize() <
215 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
216 continue;
217 }
218
219 out.emplace_back();
220 ticket_file->Read(out.back().data(), out.back().size(),
221 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
222 }
223 return out;
224}
225
226std::vector<VirtualFile> NSP::GetFiles() const {
227 return pfs->GetFiles();
228}
229
230std::vector<VirtualDir> NSP::GetSubdirectories() const {
231 return pfs->GetSubdirectories();
232}
233
234std::string NSP::GetName() const {
235 return file->GetName();
236}
237
238VirtualDir NSP::GetParentDirectory() const {
239 return file->GetContainingDirectory();
240}
241
242bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
243 return false;
244}
245} // namespace FileSys
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
new file mode 100644
index 000000000..e85a2b76e
--- /dev/null
+++ b/src/core/file_sys/submission_package.h
@@ -0,0 +1,76 @@
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 <memory>
9#include <vector>
10#include "common/common_types.h"
11#include "core/file_sys/vfs.h"
12
13namespace Loader {
14enum class ResultStatus : u16;
15}
16
17namespace FileSys {
18
19class NCA;
20class PartitionFilesystem;
21
22enum class ContentRecordType : u8;
23
24class NSP : public ReadOnlyVfsDirectory {
25public:
26 explicit NSP(VirtualFile file);
27 ~NSP() override;
28
29 Loader::ResultStatus GetStatus() const;
30 Loader::ResultStatus GetProgramStatus(u64 title_id) const;
31 // Should only be used when one title id can be assured.
32 u64 GetFirstTitleID() const;
33 u64 GetProgramTitleID() const;
34 std::vector<u64> GetTitleIDs() const;
35
36 bool IsExtractedType() const;
37
38 // Common (Can be safely called on both types)
39 VirtualFile GetRomFS() const;
40 VirtualDir GetExeFS() const;
41
42 // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
43 std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
44 std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
45 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
46 std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
47 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
48 std::vector<Core::Crypto::Key128> GetTitlekey() const;
49
50 std::vector<VirtualFile> GetFiles() const override;
51
52 std::vector<VirtualDir> GetSubdirectories() const override;
53
54 std::string GetName() const override;
55
56 VirtualDir GetParentDirectory() const override;
57
58protected:
59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
60
61private:
62 VirtualFile file;
63
64 bool extracted;
65 Loader::ResultStatus status;
66 std::map<u64, Loader::ResultStatus> program_status;
67
68 std::shared_ptr<PartitionFilesystem> pfs;
69 // Map title id -> {map type -> NCA}
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files;
72
73 VirtualFile romfs;
74 VirtualDir exefs;
75};
76} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 146c839f4..d7b52abfd 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -167,18 +167,18 @@ std::string VfsFile::GetExtension() const {
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
169 169
170boost::optional<u8> VfsFile::ReadByte(size_t offset) const { 170boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 size_t size = Read(&out, 1, offset); 172 std::size_t size = Read(&out, 1, offset);
173 if (size == 1) 173 if (size == 1)
174 return out; 174 return out;
175 175
176 return boost::none; 176 return boost::none;
177} 177}
178 178
179std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { 179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
180 std::vector<u8> out(size); 180 std::vector<u8> out(size);
181 size_t read_size = Read(out.data(), size, offset); 181 std::size_t read_size = Read(out.data(), size, offset);
182 out.resize(read_size); 182 out.resize(read_size);
183 return out; 183 return out;
184} 184}
@@ -187,11 +187,11 @@ std::vector<u8> VfsFile::ReadAllBytes() const {
187 return ReadBytes(GetSize()); 187 return ReadBytes(GetSize());
188} 188}
189 189
190bool VfsFile::WriteByte(u8 data, size_t offset) { 190bool VfsFile::WriteByte(u8 data, std::size_t offset) {
191 return Write(&data, 1, offset) == 1; 191 return Write(&data, 1, offset) == 1;
192} 192}
193 193
194size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { 194std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) {
195 return Write(data.data(), data.size(), offset); 195 return Write(data.data(), data.size(), offset);
196} 196}
197 197
@@ -215,7 +215,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) co
215 } 215 }
216 216
217 auto dir = GetSubdirectory(vec[0]); 217 auto dir = GetSubdirectory(vec[0]);
218 for (size_t component = 1; component < vec.size() - 1; ++component) { 218 for (std::size_t component = 1; component < vec.size() - 1; ++component) {
219 if (dir == nullptr) { 219 if (dir == nullptr) {
220 return nullptr; 220 return nullptr;
221 } 221 }
@@ -249,7 +249,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_vie
249 } 249 }
250 250
251 auto dir = GetSubdirectory(vec[0]); 251 auto dir = GetSubdirectory(vec[0]);
252 for (size_t component = 1; component < vec.size(); ++component) { 252 for (std::size_t component = 1; component < vec.size(); ++component) {
253 if (dir == nullptr) { 253 if (dir == nullptr) {
254 return nullptr; 254 return nullptr;
255 } 255 }
@@ -286,7 +286,7 @@ bool VfsDirectory::IsRoot() const {
286 return GetParentDirectory() == nullptr; 286 return GetParentDirectory() == nullptr;
287} 287}
288 288
289size_t VfsDirectory::GetSize() const { 289std::size_t VfsDirectory::GetSize() const {
290 const auto& files = GetFiles(); 290 const auto& files = GetFiles();
291 const auto sum_sizes = [](const auto& range) { 291 const auto sum_sizes = [](const auto& range) {
292 return std::accumulate(range.begin(), range.end(), 0ULL, 292 return std::accumulate(range.begin(), range.end(), 0ULL,
@@ -434,13 +434,13 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
434 return false; 434 return false;
435} 435}
436 436
437bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { 437bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) {
438 if (file1->GetSize() != file2->GetSize()) 438 if (file1->GetSize() != file2->GetSize())
439 return false; 439 return false;
440 440
441 std::vector<u8> f1_v(block_size); 441 std::vector<u8> f1_v(block_size);
442 std::vector<u8> f2_v(block_size); 442 std::vector<u8> f2_v(block_size);
443 for (size_t i = 0; i < file1->GetSize(); i += block_size) { 443 for (std::size_t i = 0; i < file1->GetSize(); i += block_size) {
444 auto f1_vs = file1->Read(f1_v.data(), block_size, i); 444 auto f1_vs = file1->Read(f1_v.data(), block_size, i);
445 auto f2_vs = file2->Read(f2_v.data(), block_size, i); 445 auto f2_vs = file2->Read(f2_v.data(), block_size, i);
446 446
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 5142a3e86..74489b452 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -92,9 +92,9 @@ public:
92 // Retrieves the extension of the file name. 92 // Retrieves the extension of the file name.
93 virtual std::string GetExtension() const; 93 virtual std::string GetExtension() const;
94 // Retrieves the size of the file. 94 // Retrieves the size of the file.
95 virtual size_t GetSize() const = 0; 95 virtual std::size_t GetSize() const = 0;
96 // Resizes the file to new_size. Returns whether or not the operation was successful. 96 // Resizes the file to new_size. Returns whether or not the operation was successful.
97 virtual bool Resize(size_t new_size) = 0; 97 virtual bool Resize(std::size_t new_size) = 0;
98 // Gets a pointer to the directory containing this file, returning nullptr if there is none. 98 // Gets a pointer to the directory containing this file, returning nullptr if there is none.
99 virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; 99 virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
100 100
@@ -105,15 +105,15 @@ public:
105 105
106 // The primary method of reading from the file. Reads length bytes into data starting at offset 106 // The primary method of reading from the file. Reads length bytes into data starting at offset
107 // into file. Returns number of bytes successfully read. 107 // into file. Returns number of bytes successfully read.
108 virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; 108 virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
109 // The primary method of writing to the file. Writes length bytes from data starting at offset 109 // The primary method of writing to the file. Writes length bytes from data starting at offset
110 // into file. Returns number of bytes successfully written. 110 // into file. Returns number of bytes successfully written.
111 virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; 111 virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
112 112
113 // Reads exactly one byte at the offset provided, returning boost::none on error. 113 // Reads exactly one byte at the offset provided, returning boost::none on error.
114 virtual boost::optional<u8> ReadByte(size_t offset = 0) const; 114 virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
115 // Reads size bytes starting at offset in file into a vector. 115 // Reads size bytes starting at offset in file into a vector.
116 virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; 116 virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
117 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), 117 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
118 // 0)' 118 // 0)'
119 virtual std::vector<u8> ReadAllBytes() const; 119 virtual std::vector<u8> ReadAllBytes() const;
@@ -121,7 +121,7 @@ public:
121 // Reads an array of type T, size number_elements starting at offset. 121 // Reads an array of type T, size number_elements starting at offset.
122 // Returns the number of bytes (sizeof(T)*number_elements) read successfully. 122 // Returns the number of bytes (sizeof(T)*number_elements) read successfully.
123 template <typename T> 123 template <typename T>
124 size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { 124 std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
125 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 125 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
126 126
127 return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); 127 return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
@@ -130,7 +130,7 @@ public:
130 // Reads size bytes into the memory starting at data starting at offset into the file. 130 // Reads size bytes into the memory starting at data starting at offset into the file.
131 // Returns the number of bytes read successfully. 131 // Returns the number of bytes read successfully.
132 template <typename T> 132 template <typename T>
133 size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { 133 std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
134 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 134 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
135 return Read(reinterpret_cast<u8*>(data), size, offset); 135 return Read(reinterpret_cast<u8*>(data), size, offset);
136 } 136 }
@@ -138,22 +138,22 @@ public:
138 // Reads one object of type T starting at offset in file. 138 // Reads one object of type T starting at offset in file.
139 // Returns the number of bytes read successfully (sizeof(T)). 139 // Returns the number of bytes read successfully (sizeof(T)).
140 template <typename T> 140 template <typename T>
141 size_t ReadObject(T* data, size_t offset = 0) const { 141 std::size_t ReadObject(T* data, std::size_t offset = 0) const {
142 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 142 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
143 return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); 143 return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
144 } 144 }
145 145
146 // Writes exactly one byte to offset in file and retuns whether or not the byte was written 146 // Writes exactly one byte to offset in file and retuns whether or not the byte was written
147 // successfully. 147 // successfully.
148 virtual bool WriteByte(u8 data, size_t offset = 0); 148 virtual bool WriteByte(u8 data, std::size_t offset = 0);
149 // Writes a vector of bytes to offset in file and returns the number of bytes successfully 149 // Writes a vector of bytes to offset in file and returns the number of bytes successfully
150 // written. 150 // written.
151 virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0); 151 virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);
152 152
153 // Writes an array of type T, size number_elements to offset in file. 153 // Writes an array of type T, size number_elements to offset in file.
154 // Returns the number of bytes (sizeof(T)*number_elements) written successfully. 154 // Returns the number of bytes (sizeof(T)*number_elements) written successfully.
155 template <typename T> 155 template <typename T>
156 size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) { 156 std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
157 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 157 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
158 return Write(data, number_elements * sizeof(T), offset); 158 return Write(data, number_elements * sizeof(T), offset);
159 } 159 }
@@ -161,7 +161,7 @@ public:
161 // Writes size bytes starting at memory location data to offset in file. 161 // Writes size bytes starting at memory location data to offset in file.
162 // Returns the number of bytes written successfully. 162 // Returns the number of bytes written successfully.
163 template <typename T> 163 template <typename T>
164 size_t WriteBytes(const T* data, size_t size, size_t offset = 0) { 164 std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
165 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 165 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
166 return Write(reinterpret_cast<const u8*>(data), size, offset); 166 return Write(reinterpret_cast<const u8*>(data), size, offset);
167 } 167 }
@@ -169,7 +169,7 @@ public:
169 // Writes one object of type T to offset in file. 169 // Writes one object of type T to offset in file.
170 // Returns the number of bytes written successfully (sizeof(T)). 170 // Returns the number of bytes written successfully (sizeof(T)).
171 template <typename T> 171 template <typename T>
172 size_t WriteObject(const T& data, size_t offset = 0) { 172 std::size_t WriteObject(const T& data, std::size_t offset = 0) {
173 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); 173 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
174 return Write(&data, sizeof(T), offset); 174 return Write(&data, sizeof(T), offset);
175 } 175 }
@@ -221,7 +221,7 @@ public:
221 // Returns the name of the directory. 221 // Returns the name of the directory.
222 virtual std::string GetName() const = 0; 222 virtual std::string GetName() const = 0;
223 // Returns the total size of all files and subdirectories in this directory. 223 // Returns the total size of all files and subdirectories in this directory.
224 virtual size_t GetSize() const; 224 virtual std::size_t GetSize() const;
225 // Returns the parent directory of this directory. Returns nullptr if this directory is root or 225 // Returns the parent directory of this directory. Returns nullptr if this directory is root or
226 // has no parent. 226 // has no parent.
227 virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; 227 virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
@@ -311,7 +311,7 @@ public:
311}; 311};
312 312
313// Compare the two files, byte-for-byte, in increments specificed by block_size 313// Compare the two files, byte-for-byte, in increments specificed by block_size
314bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); 314bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200);
315 315
316// A method that copies the raw data between two different implementations of VirtualFile. If you 316// A method that copies the raw data between two different implementations of VirtualFile. If you
317// are using the same implementation, it is probably better to use the Copy method in the parent 317// are using the same implementation, it is probably better to use the Copy method in the parent
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index e6bf586a3..dc7a279a9 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -20,13 +20,15 @@ VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
20 20
21ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) 21ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
22 : name(std::move(name)) { 22 : name(std::move(name)) {
23 size_t next_offset = 0; 23 std::size_t next_offset = 0;
24 for (const auto& file : files_) { 24 for (const auto& file : files_) {
25 files[next_offset] = file; 25 files[next_offset] = file;
26 next_offset += file->GetSize(); 26 next_offset += file->GetSize();
27 } 27 }
28} 28}
29 29
30ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
31
30std::string ConcatenatedVfsFile::GetName() const { 32std::string ConcatenatedVfsFile::GetName() const {
31 if (files.empty()) 33 if (files.empty())
32 return ""; 34 return "";
@@ -35,13 +37,13 @@ std::string ConcatenatedVfsFile::GetName() const {
35 return files.begin()->second->GetName(); 37 return files.begin()->second->GetName();
36} 38}
37 39
38size_t ConcatenatedVfsFile::GetSize() const { 40std::size_t ConcatenatedVfsFile::GetSize() const {
39 if (files.empty()) 41 if (files.empty())
40 return 0; 42 return 0;
41 return files.rbegin()->first + files.rbegin()->second->GetSize(); 43 return files.rbegin()->first + files.rbegin()->second->GetSize();
42} 44}
43 45
44bool ConcatenatedVfsFile::Resize(size_t new_size) { 46bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
45 return false; 47 return false;
46} 48}
47 49
@@ -59,7 +61,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
59 return true; 61 return true;
60} 62}
61 63
62size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { 64std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
63 auto entry = files.end(); 65 auto entry = files.end();
64 for (auto iter = files.begin(); iter != files.end(); ++iter) { 66 for (auto iter = files.begin(); iter != files.end(); ++iter) {
65 if (iter->first > offset) { 67 if (iter->first > offset) {
@@ -84,7 +86,7 @@ size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const {
84 return entry->second->Read(data, length, offset - entry->first); 86 return entry->second->Read(data, length, offset - entry->first);
85} 87}
86 88
87size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) { 89std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
88 return 0; 90 return 0;
89} 91}
90 92
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 686d32515..717d04bdc 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -22,14 +22,16 @@ class ConcatenatedVfsFile : public VfsFile {
22 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); 22 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
23 23
24public: 24public:
25 ~ConcatenatedVfsFile() override;
26
25 std::string GetName() const override; 27 std::string GetName() const override;
26 size_t GetSize() const override; 28 std::size_t GetSize() const override;
27 bool Resize(size_t new_size) override; 29 bool Resize(std::size_t new_size) override;
28 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; 30 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
29 bool IsWritable() const override; 31 bool IsWritable() const override;
30 bool IsReadable() const override; 32 bool IsReadable() const override;
31 size_t Read(u8* data, size_t length, size_t offset) const override; 33 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
32 size_t Write(const u8* data, size_t length, size_t offset) override; 34 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
33 bool Rename(std::string_view name) override; 35 bool Rename(std::string_view name) override;
34 36
35private: 37private:
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 847cde2f5..a4c6719a0 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -9,20 +9,22 @@
9 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_, std::size_t size_, std::size_t offset_,
13 std::string name_, VirtualDir parent_) 13 std::string name_, VirtualDir parent_)
14 : file(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 parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
16 16
17OffsetVfsFile::~OffsetVfsFile() = default;
18
17std::string OffsetVfsFile::GetName() const { 19std::string OffsetVfsFile::GetName() const {
18 return name.empty() ? file->GetName() : name; 20 return name.empty() ? file->GetName() : name;
19} 21}
20 22
21size_t OffsetVfsFile::GetSize() const { 23std::size_t OffsetVfsFile::GetSize() const {
22 return size; 24 return size;
23} 25}
24 26
25bool OffsetVfsFile::Resize(size_t new_size) { 27bool OffsetVfsFile::Resize(std::size_t new_size) {
26 if (offset + new_size < file->GetSize()) { 28 if (offset + new_size < file->GetSize()) {
27 size = new_size; 29 size = new_size;
28 } else { 30 } else {
@@ -47,22 +49,22 @@ bool OffsetVfsFile::IsReadable() const {
47 return file->IsReadable(); 49 return file->IsReadable();
48} 50}
49 51
50size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { 52std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const {
51 return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); 53 return file->Read(data, TrimToFit(length, r_offset), offset + r_offset);
52} 54}
53 55
54size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { 56std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) {
55 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); 57 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
56} 58}
57 59
58boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { 60boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
59 if (r_offset < size) 61 if (r_offset < size)
60 return file->ReadByte(offset + r_offset); 62 return file->ReadByte(offset + r_offset);
61 63
62 return boost::none; 64 return boost::none;
63} 65}
64 66
65std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { 67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
66 return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); 68 return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset);
67} 69}
68 70
@@ -70,14 +72,14 @@ std::vector<u8> OffsetVfsFile::ReadAllBytes() const {
70 return file->ReadBytes(size, offset); 72 return file->ReadBytes(size, offset);
71} 73}
72 74
73bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { 75bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) {
74 if (r_offset < size) 76 if (r_offset < size)
75 return file->WriteByte(data, offset + r_offset); 77 return file->WriteByte(data, offset + r_offset);
76 78
77 return false; 79 return false;
78} 80}
79 81
80size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) { 82std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) {
81 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); 83 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
82} 84}
83 85
@@ -85,12 +87,12 @@ bool OffsetVfsFile::Rename(std::string_view name) {
85 return file->Rename(name); 87 return file->Rename(name);
86} 88}
87 89
88size_t OffsetVfsFile::GetOffset() const { 90std::size_t OffsetVfsFile::GetOffset() const {
89 return offset; 91 return offset;
90} 92}
91 93
92size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { 94std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const {
93 return std::clamp(r_size, size_t{0}, size - r_offset); 95 return std::clamp(r_size, std::size_t{0}, size - r_offset);
94} 96}
95 97
96} // namespace FileSys 98} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index cb92d1570..8062702a7 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -17,33 +17,34 @@ namespace FileSys {
17// the size of this wrapper. 17// the size of this wrapper.
18class OffsetVfsFile : public VfsFile { 18class OffsetVfsFile : public VfsFile {
19public: 19public:
20 OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, 20 OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0,
21 std::string new_name = "", VirtualDir new_parent = nullptr); 21 std::string new_name = "", VirtualDir new_parent = nullptr);
22 ~OffsetVfsFile() override;
22 23
23 std::string GetName() const override; 24 std::string GetName() const override;
24 size_t GetSize() const override; 25 std::size_t GetSize() const override;
25 bool Resize(size_t new_size) override; 26 bool Resize(std::size_t new_size) override;
26 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; 27 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
27 bool IsWritable() const override; 28 bool IsWritable() const override;
28 bool IsReadable() const override; 29 bool IsReadable() const override;
29 size_t Read(u8* data, size_t length, size_t offset) const override; 30 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
30 size_t Write(const u8* data, size_t length, size_t offset) override; 31 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
31 boost::optional<u8> ReadByte(size_t offset) const override; 32 boost::optional<u8> ReadByte(std::size_t offset) const override;
32 std::vector<u8> ReadBytes(size_t size, size_t offset) const override; 33 std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
33 std::vector<u8> ReadAllBytes() const override; 34 std::vector<u8> ReadAllBytes() const override;
34 bool WriteByte(u8 data, size_t offset) override; 35 bool WriteByte(u8 data, std::size_t offset) override;
35 size_t WriteBytes(const std::vector<u8>& data, size_t offset) override; 36 std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override;
36 37
37 bool Rename(std::string_view name) override; 38 bool Rename(std::string_view name) override;
38 39
39 size_t GetOffset() const; 40 std::size_t GetOffset() const;
40 41
41private: 42private:
42 size_t TrimToFit(size_t r_size, size_t r_offset) const; 43 std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const;
43 44
44 std::shared_ptr<VfsFile> file; 45 std::shared_ptr<VfsFile> file;
45 size_t offset; 46 std::size_t offset;
46 size_t size; 47 std::size_t size;
47 std::string name; 48 std::string name;
48 VirtualDir parent; 49 VirtualDir parent;
49}; 50};
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 2b8ac7103..5e242e20f 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -8,6 +8,7 @@
8#include <utility> 8#include <utility>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_paths.h" 10#include "common/common_paths.h"
11#include "common/file_util.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "core/file_sys/vfs_real.h" 13#include "core/file_sys/vfs_real.h"
13 14
@@ -39,6 +40,7 @@ static std::string ModeFlagsToString(Mode mode) {
39} 40}
40 41
41RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} 42RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
43RealVfsFilesystem::~RealVfsFilesystem() = default;
42 44
43std::string RealVfsFilesystem::GetName() const { 45std::string RealVfsFilesystem::GetName() const {
44 return "Real"; 46 return "Real";
@@ -219,15 +221,17 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF
219 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), 221 parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
220 perms(perms_) {} 222 perms(perms_) {}
221 223
224RealVfsFile::~RealVfsFile() = default;
225
222std::string RealVfsFile::GetName() const { 226std::string RealVfsFile::GetName() const {
223 return path_components.back(); 227 return path_components.back();
224} 228}
225 229
226size_t RealVfsFile::GetSize() const { 230std::size_t RealVfsFile::GetSize() const {
227 return backing->GetSize(); 231 return backing->GetSize();
228} 232}
229 233
230bool RealVfsFile::Resize(size_t new_size) { 234bool RealVfsFile::Resize(std::size_t new_size) {
231 return backing->Resize(new_size); 235 return backing->Resize(new_size);
232} 236}
233 237
@@ -243,13 +247,13 @@ bool RealVfsFile::IsReadable() const {
243 return (perms & Mode::ReadWrite) != 0; 247 return (perms & Mode::ReadWrite) != 0;
244} 248}
245 249
246size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { 250std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
247 if (!backing->Seek(offset, SEEK_SET)) 251 if (!backing->Seek(offset, SEEK_SET))
248 return 0; 252 return 0;
249 return backing->ReadBytes(data, length); 253 return backing->ReadBytes(data, length);
250} 254}
251 255
252size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { 256std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
253 if (!backing->Seek(offset, SEEK_SET)) 257 if (!backing->Seek(offset, SEEK_SET))
254 return 0; 258 return 0;
255 return backing->WriteBytes(data, length); 259 return backing->WriteBytes(data, length);
@@ -312,6 +316,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
312 FileUtil::CreateDir(path); 316 FileUtil::CreateDir(path);
313} 317}
314 318
319RealVfsDirectory::~RealVfsDirectory() = default;
320
315std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { 321std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
316 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); 322 const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
317 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) 323 if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 989803d43..681c43e82 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -6,15 +6,19 @@
6 6
7#include <string_view> 7#include <string_view>
8#include <boost/container/flat_map.hpp> 8#include <boost/container/flat_map.hpp>
9#include "common/file_util.h"
10#include "core/file_sys/mode.h" 9#include "core/file_sys/mode.h"
11#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
12 11
12namespace FileUtil {
13class IOFile;
14}
15
13namespace FileSys { 16namespace FileSys {
14 17
15class RealVfsFilesystem : public VfsFilesystem { 18class RealVfsFilesystem : public VfsFilesystem {
16public: 19public:
17 RealVfsFilesystem(); 20 RealVfsFilesystem();
21 ~RealVfsFilesystem() override;
18 22
19 std::string GetName() const override; 23 std::string GetName() const override;
20 bool IsReadable() const override; 24 bool IsReadable() const override;
@@ -40,21 +44,23 @@ class RealVfsFile : public VfsFile {
40 friend class RealVfsDirectory; 44 friend class RealVfsDirectory;
41 friend class RealVfsFilesystem; 45 friend class RealVfsFilesystem;
42 46
43 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
44 const std::string& path, Mode perms = Mode::Read);
45
46public: 47public:
48 ~RealVfsFile() override;
49
47 std::string GetName() const override; 50 std::string GetName() const override;
48 size_t GetSize() const override; 51 std::size_t GetSize() const override;
49 bool Resize(size_t new_size) override; 52 bool Resize(std::size_t new_size) override;
50 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; 53 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
51 bool IsWritable() const override; 54 bool IsWritable() const override;
52 bool IsReadable() const override; 55 bool IsReadable() const override;
53 size_t Read(u8* data, size_t length, size_t offset) const override; 56 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
54 size_t Write(const u8* data, size_t length, size_t offset) override; 57 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
55 bool Rename(std::string_view name) override; 58 bool Rename(std::string_view name) override;
56 59
57private: 60private:
61 RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
62 const std::string& path, Mode perms = Mode::Read);
63
58 bool Close(); 64 bool Close();
59 65
60 RealVfsFilesystem& base; 66 RealVfsFilesystem& base;
@@ -70,9 +76,9 @@ private:
70class RealVfsDirectory : public VfsDirectory { 76class RealVfsDirectory : public VfsDirectory {
71 friend class RealVfsFilesystem; 77 friend class RealVfsFilesystem;
72 78
73 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
74
75public: 79public:
80 ~RealVfsDirectory() override;
81
76 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; 82 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
77 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; 83 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
78 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; 84 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
@@ -97,6 +103,8 @@ protected:
97 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 103 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
98 104
99private: 105private:
106 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
107
100 template <typename T, typename R> 108 template <typename T, typename R>
101 std::vector<std::shared_ptr<R>> IterateEntries() const; 109 std::vector<std::shared_ptr<R>> IterateEntries() const;
102 110
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 98e7c4598..ec7f735b5 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -13,6 +13,8 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
13 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), 13 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
14 name(std::move(name_)) {} 14 name(std::move(name_)) {}
15 15
16VectorVfsDirectory::~VectorVfsDirectory() = default;
17
16std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { 18std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
17 return files; 19 return files;
18} 20}
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 179f62e4b..cba44a7a6 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -15,6 +15,7 @@ public:
15 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, 15 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
16 std::vector<VirtualDir> dirs = {}, std::string name = "", 16 std::vector<VirtualDir> dirs = {}, std::string name = "",
17 VirtualDir parent = nullptr); 17 VirtualDir parent = nullptr);
18 ~VectorVfsDirectory() override;
18 19
19 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 20 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
20 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 21 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 552835738..b2b164368 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -10,6 +10,7 @@
10#include <mbedtls/md.h> 10#include <mbedtls/md.h>
11#include <mbedtls/sha256.h> 11#include <mbedtls/sha256.h>
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/file_util.h"
13#include "common/hex_util.h" 14#include "common/hex_util.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "core/crypto/aes_util.h" 16#include "core/crypto/aes_util.h"
@@ -24,14 +25,11 @@ namespace FileSys {
24constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; 25constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
25 26
26template <typename SourceData, typename SourceKey, typename Destination> 27template <typename SourceData, typename SourceKey, typename Destination>
27static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length, 28static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t key_length,
28 const SourceData* data, size_t data_length) { 29 const SourceData* data, std::size_t data_length) {
29 mbedtls_md_context_t context; 30 mbedtls_md_context_t context;
30 mbedtls_md_init(&context); 31 mbedtls_md_init(&context);
31 32
32 const auto key_f = reinterpret_cast<const u8*>(key);
33 const std::vector<u8> key_v(key_f, key_f + key_length);
34
35 if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) || 33 if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
36 mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) || 34 mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
37 mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) || 35 mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
@@ -44,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_
44 return true; 42 return true;
45} 43}
46 44
47NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { 45NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
48 std::string path = FileUtil::SanitizePath(file->GetFullPath()); 46 std::string path = FileUtil::SanitizePath(file->GetFullPath());
49 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", 47 static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
50 std::regex_constants::ECMAScript | 48 std::regex_constants::ECMAScript |
@@ -64,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA
64} 62}
65 63
66NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) 64NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
67 : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { 65 : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
68 Core::Crypto::SHA256Hash hash{}; 66 Core::Crypto::SHA256Hash hash{};
69 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); 67 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
70 status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], 68 status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
71 Common::HexArrayToString(nca_id, false))); 69 Common::HexArrayToString(nca_id, false)));
72} 70}
73 71
72NAX::~NAX() = default;
73
74Loader::ResultStatus NAX::Parse(std::string_view path) { 74Loader::ResultStatus NAX::Parse(std::string_view path) {
75 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) 75 if (file->ReadObject(header.get()) != sizeof(NAXHeader))
76 return Loader::ResultStatus::ErrorBadNAXHeader; 76 return Loader::ResultStatus::ErrorBadNAXHeader;
@@ -90,7 +90,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) {
90 90
91 const auto enc_keys = header->key_area; 91 const auto enc_keys = header->key_area;
92 92
93 size_t i = 0; 93 std::size_t i = 0;
94 for (; i < sd_keys.size(); ++i) { 94 for (; i < sd_keys.size(); ++i) {
95 std::array<Core::Crypto::Key128, 2> nax_keys{}; 95 std::array<Core::Crypto::Key128, 2> nax_keys{};
96 if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(), 96 if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(),
@@ -98,7 +98,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) {
98 return Loader::ResultStatus::ErrorNAXKeyHMACFailed; 98 return Loader::ResultStatus::ErrorNAXKeyHMACFailed;
99 } 99 }
100 100
101 for (size_t j = 0; j < nax_keys.size(); ++j) { 101 for (std::size_t j = 0; j < nax_keys.size(); ++j) {
102 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j], 102 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j],
103 Core::Crypto::Mode::ECB); 103 Core::Crypto::Mode::ECB);
104 cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(), 104 cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(),
@@ -137,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const {
137 return dec_file; 137 return dec_file;
138} 138}
139 139
140std::shared_ptr<NCA> NAX::AsNCA() const { 140std::unique_ptr<NCA> NAX::AsNCA() const {
141 if (type == NAXContentType::NCA) 141 if (type == NAXContentType::NCA)
142 return std::make_shared<NCA>(GetDecrypted()); 142 return std::make_unique<NCA>(GetDecrypted());
143 return nullptr; 143 return nullptr;
144} 144}
145 145
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 55d2154a6..8fedd8585 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory {
33public: 33public:
34 explicit NAX(VirtualFile file); 34 explicit NAX(VirtualFile file);
35 explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); 35 explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
36 ~NAX() override;
36 37
37 Loader::ResultStatus GetStatus() const; 38 Loader::ResultStatus GetStatus() const;
38 39
39 VirtualFile GetDecrypted() const; 40 VirtualFile GetDecrypted() const;
40 41
41 std::shared_ptr<NCA> AsNCA() const; 42 std::unique_ptr<NCA> AsNCA() const;
42 43
43 NAXContentType GetContentType() const; 44 NAXContentType GetContentType() const;
44 45
@@ -60,7 +61,7 @@ private:
60 61
61 VirtualFile file; 62 VirtualFile file;
62 Loader::ResultStatus status; 63 Loader::ResultStatus status;
63 NAXContentType type; 64 NAXContentType type{};
64 65
65 VirtualFile dec_file; 66 VirtualFile dec_file;
66 67
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 332e5c3d0..0ecdd9f82 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -65,9 +65,9 @@ constexpr u32 MSG_WAITALL = 8;
65constexpr u32 LR_REGISTER = 30; 65constexpr u32 LR_REGISTER = 30;
66constexpr u32 SP_REGISTER = 31; 66constexpr u32 SP_REGISTER = 31;
67constexpr u32 PC_REGISTER = 32; 67constexpr u32 PC_REGISTER = 32;
68constexpr u32 CPSR_REGISTER = 33; 68constexpr u32 PSTATE_REGISTER = 33;
69constexpr u32 UC_ARM64_REG_Q0 = 34; 69constexpr u32 UC_ARM64_REG_Q0 = 34;
70constexpr u32 FPSCR_REGISTER = 66; 70constexpr u32 FPCR_REGISTER = 66;
71 71
72// TODO/WiP - Used while working on support for FPU 72// TODO/WiP - Used while working on support for FPU
73constexpr u32 TODO_DUMMY_REG_997 = 997; 73constexpr u32 TODO_DUMMY_REG_997 = 997;
@@ -116,7 +116,7 @@ constexpr char target_xml[] =
116 116
117 <reg name="pc" bitsize="64" type="code_ptr"/> 117 <reg name="pc" bitsize="64" type="code_ptr"/>
118 118
119 <flags id="cpsr_flags" size="4"> 119 <flags id="pstate_flags" size="4">
120 <field name="SP" start="0" end="0"/> 120 <field name="SP" start="0" end="0"/>
121 <field name="" start="1" end="1"/> 121 <field name="" start="1" end="1"/>
122 <field name="EL" start="2" end="3"/> 122 <field name="EL" start="2" end="3"/>
@@ -135,7 +135,7 @@ constexpr char target_xml[] =
135 <field name="Z" start="30" end="30"/> 135 <field name="Z" start="30" end="30"/>
136 <field name="N" start="31" end="31"/> 136 <field name="N" start="31" end="31"/>
137 </flags> 137 </flags>
138 <reg name="cpsr" bitsize="32" type="cpsr_flags"/> 138 <reg name="pstate" bitsize="32" type="pstate_flags"/>
139 </feature> 139 </feature>
140 <feature name="org.gnu.gdb.aarch64.fpu"> 140 <feature name="org.gnu.gdb.aarch64.fpu">
141 </feature> 141 </feature>
@@ -227,10 +227,10 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
227 return thread->context.sp; 227 return thread->context.sp;
228 } else if (id == PC_REGISTER) { 228 } else if (id == PC_REGISTER) {
229 return thread->context.pc; 229 return thread->context.pc;
230 } else if (id == CPSR_REGISTER) { 230 } else if (id == PSTATE_REGISTER) {
231 return thread->context.cpsr; 231 return thread->context.pstate;
232 } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { 232 } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
233 return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0]; 233 return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0];
234 } else { 234 } else {
235 return 0; 235 return 0;
236 } 236 }
@@ -247,10 +247,10 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
247 thread->context.sp = val; 247 thread->context.sp = val;
248 } else if (id == PC_REGISTER) { 248 } else if (id == PC_REGISTER) {
249 thread->context.pc = val; 249 thread->context.pc = val;
250 } else if (id == CPSR_REGISTER) { 250 } else if (id == PSTATE_REGISTER) {
251 thread->context.cpsr = val; 251 thread->context.pstate = val;
252 } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { 252 } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
253 thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val; 253 thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
254 } 254 }
255} 255}
256 256
@@ -292,7 +292,7 @@ static u8 NibbleToHex(u8 n) {
292 * @param src Pointer to array of output hex string characters. 292 * @param src Pointer to array of output hex string characters.
293 * @param len Length of src array. 293 * @param len Length of src array.
294 */ 294 */
295static u32 HexToInt(const u8* src, size_t len) { 295static u32 HexToInt(const u8* src, std::size_t len) {
296 u32 output = 0; 296 u32 output = 0;
297 while (len-- > 0) { 297 while (len-- > 0) {
298 output = (output << 4) | HexCharToValue(src[0]); 298 output = (output << 4) | HexCharToValue(src[0]);
@@ -307,7 +307,7 @@ static u32 HexToInt(const u8* src, size_t len) {
307 * @param src Pointer to array of output hex string characters. 307 * @param src Pointer to array of output hex string characters.
308 * @param len Length of src array. 308 * @param len Length of src array.
309 */ 309 */
310static u64 HexToLong(const u8* src, size_t len) { 310static u64 HexToLong(const u8* src, std::size_t len) {
311 u64 output = 0; 311 u64 output = 0;
312 while (len-- > 0) { 312 while (len-- > 0) {
313 output = (output << 4) | HexCharToValue(src[0]); 313 output = (output << 4) | HexCharToValue(src[0]);
@@ -323,7 +323,7 @@ static u64 HexToLong(const u8* src, size_t len) {
323 * @param src Pointer to array of u8 bytes. 323 * @param src Pointer to array of u8 bytes.
324 * @param len Length of src array. 324 * @param len Length of src array.
325 */ 325 */
326static void MemToGdbHex(u8* dest, const u8* src, size_t len) { 326static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) {
327 while (len-- > 0) { 327 while (len-- > 0) {
328 u8 tmp = *src++; 328 u8 tmp = *src++;
329 *dest++ = NibbleToHex(tmp >> 4); 329 *dest++ = NibbleToHex(tmp >> 4);
@@ -338,7 +338,7 @@ static void MemToGdbHex(u8* dest, const u8* src, size_t len) {
338 * @param src Pointer to array of output hex string characters. 338 * @param src Pointer to array of output hex string characters.
339 * @param len Length of src array. 339 * @param len Length of src array.
340 */ 340 */
341static void GdbHexToMem(u8* dest, const u8* src, size_t len) { 341static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) {
342 while (len-- > 0) { 342 while (len-- > 0) {
343 *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); 343 *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]);
344 src += 2; 344 src += 2;
@@ -406,7 +406,7 @@ static u64 GdbHexToLong(const u8* src) {
406/// Read a byte from the gdb client. 406/// Read a byte from the gdb client.
407static u8 ReadByte() { 407static u8 ReadByte() {
408 u8 c; 408 u8 c;
409 size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); 409 std::size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
410 if (received_size != 1) { 410 if (received_size != 1) {
411 LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); 411 LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size);
412 Shutdown(); 412 Shutdown();
@@ -416,7 +416,7 @@ static u8 ReadByte() {
416} 416}
417 417
418/// Calculate the checksum of the current command buffer. 418/// Calculate the checksum of the current command buffer.
419static u8 CalculateChecksum(const u8* buffer, size_t length) { 419static u8 CalculateChecksum(const u8* buffer, std::size_t length) {
420 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); 420 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
421} 421}
422 422
@@ -518,7 +518,7 @@ bool CheckBreakpoint(VAddr addr, BreakpointType type) {
518 * @param packet Packet to be sent to client. 518 * @param packet Packet to be sent to client.
519 */ 519 */
520static void SendPacket(const char packet) { 520static void SendPacket(const char packet) {
521 size_t sent_size = send(gdbserver_socket, &packet, 1, 0); 521 std::size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
522 if (sent_size != 1) { 522 if (sent_size != 1) {
523 LOG_ERROR(Debug_GDBStub, "send failed"); 523 LOG_ERROR(Debug_GDBStub, "send failed");
524 } 524 }
@@ -781,11 +781,11 @@ static void ReadRegister() {
781 LongToGdbHex(reply, RegRead(id, current_thread)); 781 LongToGdbHex(reply, RegRead(id, current_thread));
782 } else if (id == PC_REGISTER) { 782 } else if (id == PC_REGISTER) {
783 LongToGdbHex(reply, RegRead(id, current_thread)); 783 LongToGdbHex(reply, RegRead(id, current_thread));
784 } else if (id == CPSR_REGISTER) { 784 } else if (id == PSTATE_REGISTER) {
785 IntToGdbHex(reply, (u32)RegRead(id, current_thread)); 785 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
786 } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { 786 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
787 LongToGdbHex(reply, RegRead(id, current_thread)); 787 LongToGdbHex(reply, RegRead(id, current_thread));
788 } else if (id == FPSCR_REGISTER) { 788 } else if (id == FPCR_REGISTER) {
789 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); 789 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
790 } else { 790 } else {
791 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); 791 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
@@ -811,7 +811,7 @@ static void ReadRegisters() {
811 811
812 bufptr += 16; 812 bufptr += 16;
813 813
814 IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); 814 IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread)));
815 815
816 bufptr += 8; 816 bufptr += 8;
817 817
@@ -843,11 +843,11 @@ static void WriteRegister() {
843 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 843 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
844 } else if (id == PC_REGISTER) { 844 } else if (id == PC_REGISTER) {
845 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 845 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
846 } else if (id == CPSR_REGISTER) { 846 } else if (id == PSTATE_REGISTER) {
847 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); 847 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
848 } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { 848 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
849 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 849 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
850 } else if (id == FPSCR_REGISTER) { 850 } else if (id == FPCR_REGISTER) {
851 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); 851 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
852 } else { 852 } else {
853 RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); 853 RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
@@ -866,16 +866,16 @@ static void WriteRegisters() {
866 if (command_buffer[0] != 'G') 866 if (command_buffer[0] != 'G')
867 return SendReply("E01"); 867 return SendReply("E01");
868 868
869 for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { 869 for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) {
870 if (reg <= SP_REGISTER) { 870 if (reg <= SP_REGISTER) {
871 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); 871 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
872 } else if (reg == PC_REGISTER) { 872 } else if (reg == PC_REGISTER) {
873 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); 873 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
874 } else if (reg == CPSR_REGISTER) { 874 } else if (reg == PSTATE_REGISTER) {
875 RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); 875 RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
876 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) { 876 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
877 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); 877 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
878 } else if (reg == FPSCR_REGISTER) { 878 } else if (reg == FPCR_REGISTER) {
879 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); 879 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
880 } else { 880 } else {
881 UNIMPLEMENTED(); 881 UNIMPLEMENTED();
@@ -995,7 +995,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
995 breakpoint.addr = addr; 995 breakpoint.addr = addr;
996 breakpoint.len = len; 996 breakpoint.len = len;
997 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); 997 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
998 static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}}; 998 static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}};
999 Memory::WriteBlock(addr, btrap.data(), btrap.size()); 999 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1000 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 1000 Core::System::GetInstance().InvalidateCpuInstructionCaches();
1001 p.insert({addr, breakpoint}); 1001 p.insert({addr, breakpoint});
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index eaa5395ac..419f45896 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -12,7 +12,7 @@
12namespace IPC { 12namespace IPC {
13 13
14/// Size of the command buffer area, in 32-bit words. 14/// Size of the command buffer area, in 32-bit words.
15constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); 15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
16 16
17// These errors are commonly returned by invalid IPC translations, so alias them here for 17// These errors are commonly returned by invalid IPC translations, so alias them here for
18// convenience. 18// convenience.
@@ -153,7 +153,7 @@ struct DataPayloadHeader {
153 u32_le magic; 153 u32_le magic;
154 INSERT_PADDING_WORDS(1); 154 INSERT_PADDING_WORDS(1);
155}; 155};
156static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect"); 156static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
157 157
158struct DomainMessageHeader { 158struct DomainMessageHeader {
159 enum class CommandType : u32_le { 159 enum class CommandType : u32_le {
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 0f3ffdb60..a4bfe2eb0 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -152,8 +152,8 @@ public:
152 } 152 }
153 153
154 void ValidateHeader() { 154 void ValidateHeader() {
155 const size_t num_domain_objects = context->NumDomainObjects(); 155 const std::size_t num_domain_objects = context->NumDomainObjects();
156 const size_t num_move_objects = context->NumMoveObjects(); 156 const std::size_t num_move_objects = context->NumMoveObjects();
157 ASSERT_MSG(!num_domain_objects || !num_move_objects, 157 ASSERT_MSG(!num_domain_objects || !num_move_objects,
158 "cannot move normal handles and domain objects"); 158 "cannot move normal handles and domain objects");
159 ASSERT_MSG((index - datapayload_index) == normal_params_size, 159 ASSERT_MSG((index - datapayload_index) == normal_params_size,
@@ -290,13 +290,6 @@ public:
290 Skip(CommandIdSize, false); 290 Skip(CommandIdSize, false);
291 } 291 }
292 292
293 ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
294 u32 num_handles_to_move,
295 ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const {
296 return ResponseBuilder{*context, normal_params_size, num_handles_to_copy,
297 num_handles_to_move, flags};
298 }
299
300 template <typename T> 293 template <typename T>
301 T Pop(); 294 T Pop();
302 295
@@ -329,10 +322,10 @@ public:
329 T PopRaw(); 322 T PopRaw();
330 323
331 template <typename T> 324 template <typename T>
332 Kernel::SharedPtr<T> GetMoveObject(size_t index); 325 Kernel::SharedPtr<T> GetMoveObject(std::size_t index);
333 326
334 template <typename T> 327 template <typename T>
335 Kernel::SharedPtr<T> GetCopyObject(size_t index); 328 Kernel::SharedPtr<T> GetCopyObject(std::size_t index);
336 329
337 template <class T> 330 template <class T>
338 std::shared_ptr<T> PopIpcInterface() { 331 std::shared_ptr<T> PopIpcInterface() {
@@ -406,12 +399,12 @@ void RequestParser::Pop(First& first_value, Other&... other_values) {
406} 399}
407 400
408template <typename T> 401template <typename T>
409Kernel::SharedPtr<T> RequestParser::GetMoveObject(size_t index) { 402Kernel::SharedPtr<T> RequestParser::GetMoveObject(std::size_t index) {
410 return context->GetMoveObject<T>(index); 403 return context->GetMoveObject<T>(index);
411} 404}
412 405
413template <typename T> 406template <typename T>
414Kernel::SharedPtr<T> RequestParser::GetCopyObject(size_t index) { 407Kernel::SharedPtr<T> RequestParser::GetCopyObject(std::size_t index) {
415 return context->GetCopyObject<T>(index); 408 return context->GetCopyObject<T>(index);
416} 409}
417 410
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 6657accd5..93577591f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -35,16 +35,17 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
35 35
36// Gets the threads waiting on an address. 36// Gets the threads waiting on an address.
37static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { 37static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
38 const auto RetrieveWaitingThreads = 38 const auto RetrieveWaitingThreads = [](std::size_t core_index,
39 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { 39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 40 VAddr arb_addr) {
41 auto& thread_list = scheduler->GetThreadList(); 41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 42 auto& thread_list = scheduler->GetThreadList();
43 for (auto& thread : thread_list) { 43
44 if (thread->arb_wait_address == arb_addr) 44 for (auto& thread : thread_list) {
45 waiting_threads.push_back(thread); 45 if (thread->arb_wait_address == arb_addr)
46 } 46 waiting_threads.push_back(thread);
47 }; 47 }
48 };
48 49
49 // Retrieve all threads that are waiting for this address. 50 // Retrieve all threads that are waiting for this address.
50 std::vector<SharedPtr<Thread>> threads; 51 std::vector<SharedPtr<Thread>> threads;
@@ -66,12 +67,12 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
66static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { 67static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
67 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process 68 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
68 // them all. 69 // them all.
69 size_t last = waiting_threads.size(); 70 std::size_t last = waiting_threads.size();
70 if (num_to_wake > 0) 71 if (num_to_wake > 0)
71 last = num_to_wake; 72 last = num_to_wake;
72 73
73 // Signal the waiting threads. 74 // Signal the waiting threads.
74 for (size_t i = 0; i < last; i++) { 75 for (std::size_t i = 0; i < last; i++) {
75 ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); 76 ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb);
76 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); 77 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
77 waiting_threads[i]->arb_wait_address = 0; 78 waiting_threads[i]->arb_wait_address = 0;
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 4054d5db6..8c2be2681 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -17,10 +17,12 @@ enum {
17 17
18 // Confirmed Switch OS error codes 18 // Confirmed Switch OS error codes
19 MaxConnectionsReached = 7, 19 MaxConnectionsReached = 7,
20 InvalidSize = 101,
20 InvalidAddress = 102, 21 InvalidAddress = 102,
21 HandleTableFull = 105, 22 HandleTableFull = 105,
22 InvalidMemoryState = 106, 23 InvalidMemoryState = 106,
23 InvalidMemoryPermissions = 108, 24 InvalidMemoryPermissions = 108,
25 InvalidThreadPriority = 112,
24 InvalidProcessorId = 113, 26 InvalidProcessorId = 113,
25 InvalidHandle = 114, 27 InvalidHandle = 114,
26 InvalidCombination = 116, 28 InvalidCombination = 116,
@@ -28,6 +30,7 @@ enum {
28 SynchronizationCanceled = 118, 30 SynchronizationCanceled = 118,
29 TooLarge = 119, 31 TooLarge = 119,
30 InvalidEnumValue = 120, 32 InvalidEnumValue = 120,
33 NoSuchEntry = 121,
31 InvalidState = 125, 34 InvalidState = 125,
32 ResourceLimitExceeded = 132, 35 ResourceLimitExceeded = 132,
33}; 36};
@@ -36,7 +39,7 @@ enum {
36// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 39// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
37// double check that the code matches before re-using the constant. 40// double check that the code matches before re-using the constant.
38 41
39// TODO(bunnei): Replace these with correct errors for Switch OS 42// TODO(bunnei): Replace -1 with correct errors for Switch OS
40constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 43constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
41constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); 44constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
42constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 45constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
@@ -53,15 +56,17 @@ constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::In
53constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 56constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
54 ErrCodes::InvalidMemoryPermissions); 57 ErrCodes::InvalidMemoryPermissions);
55constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 58constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
59constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
60constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
56constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 61constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
62constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
63 ErrCodes::InvalidThreadPriority);
57constexpr ResultCode ERR_INVALID_POINTER(-1); 64constexpr ResultCode ERR_INVALID_POINTER(-1);
58constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); 65constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
59constexpr ResultCode ERR_NOT_AUTHORIZED(-1); 66constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
60/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. 67/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
61constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); 68constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
62constexpr ResultCode ERR_NOT_FOUND(-1); 69constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
63constexpr ResultCode ERR_OUT_OF_RANGE(-1);
64constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(-1);
65constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); 70constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
66/// Returned when Accept() is called on a port with no sessions to be accepted. 71/// Returned when Accept() is called on a port with no sessions to be accepted.
67constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); 72constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 3a079b9a9..5ee5c05e3 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -65,7 +65,7 @@ ResultCode HandleTable::Close(Handle handle) {
65} 65}
66 66
67bool HandleTable::IsValid(Handle handle) const { 67bool HandleTable::IsValid(Handle handle) const {
68 size_t slot = GetSlot(handle); 68 std::size_t slot = GetSlot(handle);
69 u16 generation = GetGeneration(handle); 69 u16 generation = GetGeneration(handle);
70 70
71 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; 71 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index cac928adb..9e2f33e8a 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -93,7 +93,7 @@ private:
93 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further 93 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
94 * reduced by ExHeader values, but this is not emulated here. 94 * reduced by ExHeader values, but this is not emulated here.
95 */ 95 */
96 static const size_t MAX_COUNT = 4096; 96 static const std::size_t MAX_COUNT = 4096;
97 97
98 static u16 GetSlot(Handle handle) { 98 static u16 GetSlot(Handle handle) {
99 return handle >> 15; 99 return handle >> 15;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 7264be906..72fb9d250 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -42,9 +42,9 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
42 Kernel::SharedPtr<Kernel::Event> event) { 42 Kernel::SharedPtr<Kernel::Event> event) {
43 43
44 // Put the client thread to sleep until the wait event is signaled or the timeout expires. 44 // Put the client thread to sleep until the wait event is signaled or the timeout expires.
45 thread->wakeup_callback = 45 thread->wakeup_callback = [context = *this, callback](
46 [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread, 46 ThreadWakeupReason reason, SharedPtr<Thread> thread,
47 SharedPtr<WaitObject> object, size_t index) mutable -> bool { 47 SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
48 ASSERT(thread->status == ThreadStatus::WaitHLEEvent); 48 ASSERT(thread->status == ThreadStatus::WaitHLEEvent);
49 callback(thread, context, reason); 49 callback(thread, context, reason);
50 context.WriteToOutgoingCommandBuffer(*thread); 50 context.WriteToOutgoingCommandBuffer(*thread);
@@ -199,8 +199,8 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
199 } 199 }
200 200
201 // The data_size already includes the payload header, the padding and the domain header. 201 // The data_size already includes the payload header, the padding and the domain header.
202 size_t size = data_payload_offset + command_header->data_size - 202 std::size_t size = data_payload_offset + command_header->data_size -
203 sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; 203 sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
204 if (domain_message_header) 204 if (domain_message_header)
205 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); 205 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
206 std::copy_n(src_cmdbuf, size, cmd_buf.begin()); 206 std::copy_n(src_cmdbuf, size, cmd_buf.begin());
@@ -217,8 +217,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
217 ParseCommandBuffer(cmd_buf.data(), false); 217 ParseCommandBuffer(cmd_buf.data(), false);
218 218
219 // The data_size already includes the payload header, the padding and the domain header. 219 // The data_size already includes the payload header, the padding and the domain header.
220 size_t size = data_payload_offset + command_header->data_size - 220 std::size_t size = data_payload_offset + command_header->data_size -
221 sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; 221 sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
222 if (domain_message_header) 222 if (domain_message_header)
223 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); 223 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
224 224
@@ -229,7 +229,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
229 "Handle descriptor bit set but no handles to translate"); 229 "Handle descriptor bit set but no handles to translate");
230 // We write the translated handles at a specific offset in the command buffer, this space 230 // We write the translated handles at a specific offset in the command buffer, this space
231 // was already reserved when writing the header. 231 // was already reserved when writing the header.
232 size_t current_offset = 232 std::size_t current_offset =
233 (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); 233 (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
234 ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); 234 ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
235 235
@@ -258,7 +258,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
258 ASSERT(domain_message_header->num_objects == domain_objects.size()); 258 ASSERT(domain_message_header->num_objects == domain_objects.size());
259 // Write the domain objects to the command buffer, these go after the raw untranslated data. 259 // Write the domain objects to the command buffer, these go after the raw untranslated data.
260 // TODO(Subv): This completely ignores C buffers. 260 // TODO(Subv): This completely ignores C buffers.
261 size_t domain_offset = size - domain_message_header->num_objects; 261 std::size_t domain_offset = size - domain_message_header->num_objects;
262 auto& request_handlers = server_session->domain_request_handlers; 262 auto& request_handlers = server_session->domain_request_handlers;
263 263
264 for (auto& object : domain_objects) { 264 for (auto& object : domain_objects) {
@@ -291,14 +291,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
291 return buffer; 291 return buffer;
292} 292}
293 293
294size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { 294std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
295 int buffer_index) const {
295 if (size == 0) { 296 if (size == 0) {
296 LOG_WARNING(Core, "skip empty buffer write"); 297 LOG_WARNING(Core, "skip empty buffer write");
297 return 0; 298 return 0;
298 } 299 }
299 300
300 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; 301 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
301 const size_t buffer_size{GetWriteBufferSize(buffer_index)}; 302 const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
302 if (size > buffer_size) { 303 if (size > buffer_size) {
303 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, 304 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
304 buffer_size); 305 buffer_size);
@@ -314,13 +315,13 @@ size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffe
314 return size; 315 return size;
315} 316}
316 317
317size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { 318std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
318 const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; 319 const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
319 return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() 320 return is_buffer_a ? BufferDescriptorA()[buffer_index].Size()
320 : BufferDescriptorX()[buffer_index].Size(); 321 : BufferDescriptorX()[buffer_index].Size();
321} 322}
322 323
323size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { 324std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
324 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; 325 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
325 return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() 326 return is_buffer_b ? BufferDescriptorB()[buffer_index].Size()
326 : BufferDescriptorC()[buffer_index].Size(); 327 : BufferDescriptorC()[buffer_index].Size();
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index f0d07f1b6..894479ee0 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -170,7 +170,7 @@ public:
170 std::vector<u8> ReadBuffer(int buffer_index = 0) const; 170 std::vector<u8> ReadBuffer(int buffer_index = 0) const;
171 171
172 /// Helper function to write a buffer using the appropriate buffer descriptor 172 /// Helper function to write a buffer using the appropriate buffer descriptor
173 size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const; 173 std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const;
174 174
175 /* Helper function to write a buffer using the appropriate buffer descriptor 175 /* Helper function to write a buffer using the appropriate buffer descriptor
176 * 176 *
@@ -182,7 +182,7 @@ public:
182 */ 182 */
183 template <typename ContiguousContainer, 183 template <typename ContiguousContainer,
184 typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> 184 typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
185 size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { 185 std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const {
186 using ContiguousType = typename ContiguousContainer::value_type; 186 using ContiguousType = typename ContiguousContainer::value_type;
187 187
188 static_assert(std::is_trivially_copyable_v<ContiguousType>, 188 static_assert(std::is_trivially_copyable_v<ContiguousType>,
@@ -193,19 +193,19 @@ public:
193 } 193 }
194 194
195 /// Helper function to get the size of the input buffer 195 /// Helper function to get the size of the input buffer
196 size_t GetReadBufferSize(int buffer_index = 0) const; 196 std::size_t GetReadBufferSize(int buffer_index = 0) const;
197 197
198 /// Helper function to get the size of the output buffer 198 /// Helper function to get the size of the output buffer
199 size_t GetWriteBufferSize(int buffer_index = 0) const; 199 std::size_t GetWriteBufferSize(int buffer_index = 0) const;
200 200
201 template <typename T> 201 template <typename T>
202 SharedPtr<T> GetCopyObject(size_t index) { 202 SharedPtr<T> GetCopyObject(std::size_t index) {
203 ASSERT(index < copy_objects.size()); 203 ASSERT(index < copy_objects.size());
204 return DynamicObjectCast<T>(copy_objects[index]); 204 return DynamicObjectCast<T>(copy_objects[index]);
205 } 205 }
206 206
207 template <typename T> 207 template <typename T>
208 SharedPtr<T> GetMoveObject(size_t index) { 208 SharedPtr<T> GetMoveObject(std::size_t index) {
209 ASSERT(index < move_objects.size()); 209 ASSERT(index < move_objects.size());
210 return DynamicObjectCast<T>(move_objects[index]); 210 return DynamicObjectCast<T>(move_objects[index]);
211 } 211 }
@@ -223,7 +223,7 @@ public:
223 } 223 }
224 224
225 template <typename T> 225 template <typename T>
226 std::shared_ptr<T> GetDomainRequestHandler(size_t index) const { 226 std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
227 return std::static_pointer_cast<T>(domain_request_handlers[index]); 227 return std::static_pointer_cast<T>(domain_request_handlers[index]);
228 } 228 }
229 229
@@ -240,15 +240,15 @@ public:
240 domain_objects.clear(); 240 domain_objects.clear();
241 } 241 }
242 242
243 size_t NumMoveObjects() const { 243 std::size_t NumMoveObjects() const {
244 return move_objects.size(); 244 return move_objects.size();
245 } 245 }
246 246
247 size_t NumCopyObjects() const { 247 std::size_t NumCopyObjects() const {
248 return copy_objects.size(); 248 return copy_objects.size();
249 } 249 }
250 250
251 size_t NumDomainObjects() const { 251 std::size_t NumDomainObjects() const {
252 return domain_objects.size(); 252 return domain_objects.size();
253 } 253 }
254 254
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 615d7901a..3e0800a71 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -13,6 +13,7 @@
13 13
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/hle/kernel/client_port.h"
16#include "core/hle/kernel/handle_table.h" 17#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
@@ -115,6 +116,7 @@ struct KernelCore::Impl {
115 next_thread_id = 1; 116 next_thread_id = 1;
116 117
117 process_list.clear(); 118 process_list.clear();
119 current_process.reset();
118 120
119 handle_table.Clear(); 121 handle_table.Clear();
120 resource_limits.fill(nullptr); 122 resource_limits.fill(nullptr);
@@ -124,6 +126,8 @@ struct KernelCore::Impl {
124 126
125 timer_callback_handle_table.Clear(); 127 timer_callback_handle_table.Clear();
126 timer_callback_event_type = nullptr; 128 timer_callback_event_type = nullptr;
129
130 named_ports.clear();
127 } 131 }
128 132
129 void InitializeResourceLimits(KernelCore& kernel) { 133 void InitializeResourceLimits(KernelCore& kernel) {
@@ -203,6 +207,7 @@ struct KernelCore::Impl {
203 207
204 // Lists all processes that exist in the current session. 208 // Lists all processes that exist in the current session.
205 std::vector<SharedPtr<Process>> process_list; 209 std::vector<SharedPtr<Process>> process_list;
210 SharedPtr<Process> current_process;
206 211
207 Kernel::HandleTable handle_table; 212 Kernel::HandleTable handle_table;
208 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
@@ -217,6 +222,10 @@ struct KernelCore::Impl {
217 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, 222 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
218 // allowing us to simply use a pool index or similar. 223 // allowing us to simply use a pool index or similar.
219 Kernel::HandleTable thread_wakeup_callback_handle_table; 224 Kernel::HandleTable thread_wakeup_callback_handle_table;
225
226 /// Map of named ports managed by the kernel, which can be retrieved using
227 /// the ConnectToPort SVC.
228 NamedPortTable named_ports;
220}; 229};
221 230
222KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} 231KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
@@ -257,6 +266,35 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
257 impl->process_list.push_back(std::move(process)); 266 impl->process_list.push_back(std::move(process));
258} 267}
259 268
269void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) {
270 impl->current_process = std::move(process);
271}
272
273SharedPtr<Process>& KernelCore::CurrentProcess() {
274 return impl->current_process;
275}
276
277const SharedPtr<Process>& KernelCore::CurrentProcess() const {
278 return impl->current_process;
279}
280
281void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
282 impl->named_ports.emplace(std::move(name), std::move(port));
283}
284
285KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
286 return impl->named_ports.find(name);
287}
288
289KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
290 const std::string& name) const {
291 return impl->named_ports.find(name);
292}
293
294bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
295 return port != impl->named_ports.cend();
296}
297
260u32 KernelCore::CreateNewObjectID() { 298u32 KernelCore::CreateNewObjectID() {
261 return impl->next_object_id++; 299 return impl->next_object_id++;
262} 300}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 089e959ac..c0771ecf0 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string>
8#include <unordered_map>
7#include "core/hle/kernel/object.h" 9#include "core/hle/kernel/object.h"
8 10
9template <typename T> 11template <typename T>
@@ -15,6 +17,7 @@ struct EventType;
15 17
16namespace Kernel { 18namespace Kernel {
17 19
20class ClientPort;
18class HandleTable; 21class HandleTable;
19class Process; 22class Process;
20class ResourceLimit; 23class ResourceLimit;
@@ -25,6 +28,9 @@ enum class ResourceLimitCategory : u8;
25 28
26/// Represents a single instance of the kernel. 29/// Represents a single instance of the kernel.
27class KernelCore { 30class KernelCore {
31private:
32 using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
33
28public: 34public:
29 KernelCore(); 35 KernelCore();
30 ~KernelCore(); 36 ~KernelCore();
@@ -59,6 +65,27 @@ public:
59 /// Adds the given shared pointer to an internal list of active processes. 65 /// Adds the given shared pointer to an internal list of active processes.
60 void AppendNewProcess(SharedPtr<Process> process); 66 void AppendNewProcess(SharedPtr<Process> process);
61 67
68 /// Makes the given process the new current process.
69 void MakeCurrentProcess(SharedPtr<Process> process);
70
71 /// Retrieves a reference to the current process.
72 SharedPtr<Process>& CurrentProcess();
73
74 /// Retrieves a const reference to the current process.
75 const SharedPtr<Process>& CurrentProcess() const;
76
77 /// Adds a port to the named port table
78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
79
80 /// Finds a port within the named port table with the given name.
81 NamedPortTable::iterator FindNamedPort(const std::string& name);
82
83 /// Finds a port within the named port table with the given name.
84 NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
85
86 /// Determines whether or not the given port is a valid named port.
87 bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
88
62private: 89private:
63 friend class Object; 90 friend class Object;
64 friend class Process; 91 friend class Process;
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 36bf0b677..81675eac5 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/object.h" 16#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
18#include "core/hle/result.h" 18#include "core/hle/result.h"
19#include "core/memory.h"
19 20
20namespace Kernel { 21namespace Kernel {
21 22
@@ -62,7 +63,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho
62 Handle requesting_thread_handle) { 63 Handle requesting_thread_handle) {
63 // The mutex address must be 4-byte aligned 64 // The mutex address must be 4-byte aligned
64 if ((address % sizeof(u32)) != 0) { 65 if ((address % sizeof(u32)) != 0) {
65 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); 66 return ERR_INVALID_ADDRESS;
66 } 67 }
67 68
68 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); 69 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
@@ -100,7 +101,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho
100ResultCode Mutex::Release(VAddr address) { 101ResultCode Mutex::Release(VAddr address) {
101 // The mutex address must be 4-byte aligned 102 // The mutex address must be 4-byte aligned
102 if ((address % sizeof(u32)) != 0) { 103 if ((address % sizeof(u32)) != 0) {
103 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); 104 return ERR_INVALID_ADDRESS;
104 } 105 }
105 106
106 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); 107 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index b025e323f..914bbe0a1 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -40,8 +40,8 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
40 return process; 40 return process;
41} 41}
42 42
43void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { 43void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
44 for (size_t i = 0; i < len; ++i) { 44 for (std::size_t i = 0; i < len; ++i) {
45 u32 descriptor = kernel_caps[i]; 45 u32 descriptor = kernel_caps[i];
46 u32 type = descriptor >> 20; 46 u32 type = descriptor >> 20;
47 47
@@ -125,7 +125,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
125 vm_manager.LogLayout(); 125 vm_manager.LogLayout();
126 status = ProcessStatus::Running; 126 status = ProcessStatus::Running;
127 127
128 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this); 128 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
129} 129}
130 130
131void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 131void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
@@ -211,7 +211,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
211 "Shared memory exceeds bounds of mapped block"); 211 "Shared memory exceeds bounds of mapped block");
212 212
213 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; 213 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
214 size_t backing_block_offset = vma->second.offset + vma_offset; 214 std::size_t backing_block_offset = vma->second.offset + vma_offset;
215 215
216 CASCADE_RESULT(auto new_vma, 216 CASCADE_RESULT(auto new_vma,
217 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, 217 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 1587d40c1..81538f70c 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -59,7 +59,7 @@ class ResourceLimit;
59 59
60struct CodeSet final : public Object { 60struct CodeSet final : public Object {
61 struct Segment { 61 struct Segment {
62 size_t offset = 0; 62 std::size_t offset = 0;
63 VAddr addr = 0; 63 VAddr addr = 0;
64 u32 size = 0; 64 u32 size = 0;
65 }; 65 };
@@ -164,7 +164,7 @@ public:
164 * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them 164 * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
165 * to this process. 165 * to this process.
166 */ 166 */
167 void ParseKernelCaps(const u32* kernel_caps, size_t len); 167 void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
168 168
169 /** 169 /**
170 * Applies address space changes and launches the process main thread. 170 * Applies address space changes and launches the process main thread.
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 2c729afe3..2c06bb7ce 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -119,7 +119,7 @@ public:
119 /// Backing memory for this shared memory block. 119 /// Backing memory for this shared memory block.
120 std::shared_ptr<std::vector<u8>> backing_block; 120 std::shared_ptr<std::vector<u8>> backing_block;
121 /// Offset into the backing block for this shared memory. 121 /// Offset into the backing block for this shared memory.
122 size_t backing_block_offset; 122 std::size_t backing_block_offset;
123 /// Size of the memory block. Page-aligned. 123 /// Size of the memory block. Page-aligned.
124 u64 size; 124 u64 size;
125 /// Permission restrictions applied to the process which created the block. 125 /// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 5da71cff0..371fc439e 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -35,10 +35,21 @@
35#include "core/hle/service/service.h" 35#include "core/hle/service/service.h"
36 36
37namespace Kernel { 37namespace Kernel {
38namespace {
39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0;
41}
42} // Anonymous namespace
38 43
39/// Set the process heap to a given Size. It can both extend and shrink the heap. 44/// Set the process heap to a given Size. It can both extend and shrink the heap.
40static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { 45static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
41 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); 46 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
47
48 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 4GB.
49 if ((heap_size & 0xFFFFFFFE001FFFFF) != 0) {
50 return ERR_INVALID_SIZE;
51 }
52
42 auto& process = *Core::CurrentProcess(); 53 auto& process = *Core::CurrentProcess();
43 CASCADE_RESULT(*heap_addr, 54 CASCADE_RESULT(*heap_addr,
44 process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); 55 process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
@@ -56,6 +67,15 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state
56static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 67static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
57 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 68 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
58 src_addr, size); 69 src_addr, size);
70
71 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
72 return ERR_INVALID_ADDRESS;
73 }
74
75 if (size == 0 || !Is4KBAligned(size)) {
76 return ERR_INVALID_SIZE;
77 }
78
59 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 79 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
60} 80}
61 81
@@ -63,24 +83,36 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
63static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 83static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
64 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 84 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
65 src_addr, size); 85 src_addr, size);
86
87 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
88 return ERR_INVALID_ADDRESS;
89 }
90
91 if (size == 0 || !Is4KBAligned(size)) {
92 return ERR_INVALID_SIZE;
93 }
94
66 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 95 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
67} 96}
68 97
69/// Connect to an OS service given the port name, returns the handle to the port to out 98/// Connect to an OS service given the port name, returns the handle to the port to out
70static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { 99static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
71 if (!Memory::IsValidVirtualAddress(port_name_address)) 100 if (!Memory::IsValidVirtualAddress(port_name_address)) {
72 return ERR_NOT_FOUND; 101 return ERR_NOT_FOUND;
102 }
73 103
74 static constexpr std::size_t PortNameMaxLength = 11; 104 static constexpr std::size_t PortNameMaxLength = 11;
75 // Read 1 char beyond the max allowed port name to detect names that are too long. 105 // Read 1 char beyond the max allowed port name to detect names that are too long.
76 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); 106 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
77 if (port_name.size() > PortNameMaxLength) 107 if (port_name.size() > PortNameMaxLength) {
78 return ERR_PORT_NAME_TOO_LONG; 108 return ERR_PORT_NAME_TOO_LONG;
109 }
79 110
80 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 111 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
81 112
82 auto it = Service::g_kernel_named_ports.find(port_name); 113 auto& kernel = Core::System::GetInstance().Kernel();
83 if (it == Service::g_kernel_named_ports.end()) { 114 auto it = kernel.FindNamedPort(port_name);
115 if (!kernel.IsValidNamedPort(it)) {
84 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); 116 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
85 return ERR_NOT_FOUND; 117 return ERR_NOT_FOUND;
86 } 118 }
@@ -91,7 +123,6 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
91 CASCADE_RESULT(client_session, client_port->Connect()); 123 CASCADE_RESULT(client_session, client_port->Connect());
92 124
93 // Return the client session 125 // Return the client session
94 auto& kernel = Core::System::GetInstance().Kernel();
95 CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); 126 CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
96 return RESULT_SUCCESS; 127 return RESULT_SUCCESS;
97} 128}
@@ -144,7 +175,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
144 175
145/// Default thread wakeup callback for WaitSynchronization 176/// Default thread wakeup callback for WaitSynchronization
146static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, 177static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
147 SharedPtr<WaitObject> object, size_t index) { 178 SharedPtr<WaitObject> object, std::size_t index) {
148 ASSERT(thread->status == ThreadStatus::WaitSynchAny); 179 ASSERT(thread->status == ThreadStatus::WaitSynchAny);
149 180
150 if (reason == ThreadWakeupReason::Timeout) { 181 if (reason == ThreadWakeupReason::Timeout) {
@@ -249,6 +280,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
249 "requesting_current_thread_handle=0x{:08X}", 280 "requesting_current_thread_handle=0x{:08X}",
250 holding_thread_handle, mutex_addr, requesting_thread_handle); 281 holding_thread_handle, mutex_addr, requesting_thread_handle);
251 282
283 if (Memory::IsKernelVirtualAddress(mutex_addr)) {
284 return ERR_INVALID_ADDRESS_STATE;
285 }
286
252 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 287 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
253 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 288 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
254 requesting_thread_handle); 289 requesting_thread_handle);
@@ -258,6 +293,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
258static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 293static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
259 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); 294 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
260 295
296 if (Memory::IsKernelVirtualAddress(mutex_addr)) {
297 return ERR_INVALID_ADDRESS_STATE;
298 }
299
261 return Mutex::Release(mutex_addr); 300 return Mutex::Release(mutex_addr);
262} 301}
263 302
@@ -271,7 +310,11 @@ static void Break(u64 reason, u64 info1, u64 info2) {
271} 310}
272 311
273/// Used to output a message on a debug hardware unit - does nothing on a retail unit 312/// Used to output a message on a debug hardware unit - does nothing on a retail unit
274static void OutputDebugString(VAddr address, s32 len) { 313static void OutputDebugString(VAddr address, u64 len) {
314 if (len == 0) {
315 return;
316 }
317
275 std::string str(len, '\0'); 318 std::string str(len, '\0');
276 Memory::ReadBlock(address, str.data(), str.size()); 319 Memory::ReadBlock(address, str.data(), str.size());
277 LOG_DEBUG(Debug_Emulated, "{}", str); 320 LOG_DEBUG(Debug_Emulated, "{}", str);
@@ -376,7 +419,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
376/// Sets the priority for the specified thread 419/// Sets the priority for the specified thread
377static ResultCode SetThreadPriority(Handle handle, u32 priority) { 420static ResultCode SetThreadPriority(Handle handle, u32 priority) {
378 if (priority > THREADPRIO_LOWEST) { 421 if (priority > THREADPRIO_LOWEST) {
379 return ERR_OUT_OF_RANGE; 422 return ERR_INVALID_THREAD_PRIORITY;
380 } 423 }
381 424
382 auto& kernel = Core::System::GetInstance().Kernel(); 425 auto& kernel = Core::System::GetInstance().Kernel();
@@ -409,35 +452,43 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
409 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 452 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
410 shared_memory_handle, addr, size, permissions); 453 shared_memory_handle, addr, size, permissions);
411 454
455 if (!Is4KBAligned(addr)) {
456 return ERR_INVALID_ADDRESS;
457 }
458
459 if (size == 0 || !Is4KBAligned(size)) {
460 return ERR_INVALID_SIZE;
461 }
462
463 const auto permissions_type = static_cast<MemoryPermission>(permissions);
464 if (permissions_type != MemoryPermission::Read &&
465 permissions_type != MemoryPermission::ReadWrite) {
466 LOG_ERROR(Kernel_SVC, "Invalid permissions=0x{:08X}", permissions);
467 return ERR_INVALID_MEMORY_PERMISSIONS;
468 }
469
412 auto& kernel = Core::System::GetInstance().Kernel(); 470 auto& kernel = Core::System::GetInstance().Kernel();
413 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 471 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
414 if (!shared_memory) { 472 if (!shared_memory) {
415 return ERR_INVALID_HANDLE; 473 return ERR_INVALID_HANDLE;
416 } 474 }
417 475
418 MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions); 476 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
419 switch (permissions_type) { 477 MemoryPermission::DontCare);
420 case MemoryPermission::Read:
421 case MemoryPermission::Write:
422 case MemoryPermission::ReadWrite:
423 case MemoryPermission::Execute:
424 case MemoryPermission::ReadExecute:
425 case MemoryPermission::WriteExecute:
426 case MemoryPermission::ReadWriteExecute:
427 case MemoryPermission::DontCare:
428 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
429 MemoryPermission::DontCare);
430 default:
431 LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions);
432 }
433
434 return RESULT_SUCCESS;
435} 478}
436 479
437static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 480static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
438 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 481 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
439 shared_memory_handle, addr, size); 482 shared_memory_handle, addr, size);
440 483
484 if (!Is4KBAligned(addr)) {
485 return ERR_INVALID_ADDRESS;
486 }
487
488 if (size == 0 || !Is4KBAligned(size)) {
489 return ERR_INVALID_SIZE;
490 }
491
441 auto& kernel = Core::System::GetInstance().Kernel(); 492 auto& kernel = Core::System::GetInstance().Kernel();
442 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 493 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
443 494
@@ -518,10 +569,10 @@ static void ExitProcess() {
518/// Creates a new thread 569/// Creates a new thread
519static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 570static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
520 u32 priority, s32 processor_id) { 571 u32 priority, s32 processor_id) {
521 std::string name = fmt::format("unknown-{:X}", entry_point); 572 std::string name = fmt::format("thread-{:X}", entry_point);
522 573
523 if (priority > THREADPRIO_LOWEST) { 574 if (priority > THREADPRIO_LOWEST) {
524 return ERR_OUT_OF_RANGE; 575 return ERR_INVALID_THREAD_PRIORITY;
525 } 576 }
526 577
527 SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; 578 SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
@@ -542,8 +593,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
542 case THREADPROCESSORID_3: 593 case THREADPROCESSORID_3:
543 break; 594 break;
544 default: 595 default:
545 ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id); 596 LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
546 break; 597 return ERR_INVALID_PROCESSOR_ID;
547 } 598 }
548 599
549 auto& kernel = Core::System::GetInstance().Kernel(); 600 auto& kernel = Core::System::GetInstance().Kernel();
@@ -641,16 +692,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
641 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", 692 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
642 condition_variable_addr, target); 693 condition_variable_addr, target);
643 694
644 auto RetrieveWaitingThreads = 695 auto RetrieveWaitingThreads = [](std::size_t core_index,
645 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { 696 std::vector<SharedPtr<Thread>>& waiting_threads,
646 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 697 VAddr condvar_addr) {
647 auto& thread_list = scheduler->GetThreadList(); 698 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
699 auto& thread_list = scheduler->GetThreadList();
648 700
649 for (auto& thread : thread_list) { 701 for (auto& thread : thread_list) {
650 if (thread->condvar_wait_address == condvar_addr) 702 if (thread->condvar_wait_address == condvar_addr)
651 waiting_threads.push_back(thread); 703 waiting_threads.push_back(thread);
652 } 704 }
653 }; 705 };
654 706
655 // Retrieve a list of all threads that are waiting for this condition variable. 707 // Retrieve a list of all threads that are waiting for this condition variable.
656 std::vector<SharedPtr<Thread>> waiting_threads; 708 std::vector<SharedPtr<Thread>> waiting_threads;
@@ -666,7 +718,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
666 718
667 // Only process up to 'target' threads, unless 'target' is -1, in which case process 719 // Only process up to 'target' threads, unless 'target' is -1, in which case process
668 // them all. 720 // them all.
669 size_t last = waiting_threads.size(); 721 std::size_t last = waiting_threads.size();
670 if (target != -1) 722 if (target != -1)
671 last = target; 723 last = target;
672 724
@@ -674,12 +726,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
674 if (last > waiting_threads.size()) 726 if (last > waiting_threads.size())
675 return RESULT_SUCCESS; 727 return RESULT_SUCCESS;
676 728
677 for (size_t index = 0; index < last; ++index) { 729 for (std::size_t index = 0; index < last; ++index) {
678 auto& thread = waiting_threads[index]; 730 auto& thread = waiting_threads[index];
679 731
680 ASSERT(thread->condvar_wait_address == condition_variable_addr); 732 ASSERT(thread->condvar_wait_address == condition_variable_addr);
681 733
682 size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); 734 std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
683 735
684 auto& monitor = Core::System::GetInstance().Monitor(); 736 auto& monitor = Core::System::GetInstance().Monitor();
685 737
@@ -892,12 +944,28 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
892 LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, 944 LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
893 local_permissions, remote_permissions); 945 local_permissions, remote_permissions);
894 946
947 // Size must be a multiple of 4KB and be less than or equal to
948 // approx. 8 GB (actually (1GB - 512B) * 8)
949 if (size == 0 || (size & 0xFFFFFFFE00000FFF) != 0) {
950 return ERR_INVALID_SIZE;
951 }
952
953 const auto local_perms = static_cast<MemoryPermission>(local_permissions);
954 if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) {
955 return ERR_INVALID_MEMORY_PERMISSIONS;
956 }
957
958 const auto remote_perms = static_cast<MemoryPermission>(remote_permissions);
959 if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite &&
960 remote_perms != MemoryPermission::DontCare) {
961 return ERR_INVALID_MEMORY_PERMISSIONS;
962 }
963
895 auto& kernel = Core::System::GetInstance().Kernel(); 964 auto& kernel = Core::System::GetInstance().Kernel();
896 auto& handle_table = kernel.HandleTable(); 965 auto& handle_table = kernel.HandleTable();
897 auto shared_mem_handle = 966 auto shared_mem_handle =
898 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 967 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
899 static_cast<MemoryPermission>(local_permissions), 968 local_perms, remote_perms);
900 static_cast<MemoryPermission>(remote_permissions));
901 969
902 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); 970 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
903 return RESULT_SUCCESS; 971 return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 79c3fe31b..fea9ba5ea 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -13,7 +13,9 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16#define PARAM(n) Core::CurrentArmInterface().GetReg(n) 16static inline u64 Param(int n) {
17 return Core::CurrentArmInterface().GetReg(n);
18}
17 19
18/** 20/**
19 * HLE a function return from the current ARM userland process 21 * HLE a function return from the current ARM userland process
@@ -28,23 +30,23 @@ static inline void FuncReturn(u64 res) {
28 30
29template <ResultCode func(u64)> 31template <ResultCode func(u64)>
30void SvcWrap() { 32void SvcWrap() {
31 FuncReturn(func(PARAM(0)).raw); 33 FuncReturn(func(Param(0)).raw);
32} 34}
33 35
34template <ResultCode func(u32)> 36template <ResultCode func(u32)>
35void SvcWrap() { 37void SvcWrap() {
36 FuncReturn(func((u32)PARAM(0)).raw); 38 FuncReturn(func((u32)Param(0)).raw);
37} 39}
38 40
39template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
40void SvcWrap() { 42void SvcWrap() {
41 FuncReturn(func((u32)PARAM(0), (u32)PARAM(1)).raw); 43 FuncReturn(func((u32)Param(0), (u32)Param(1)).raw);
42} 44}
43 45
44template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
45void SvcWrap() { 47void SvcWrap() {
46 u32 param_1 = 0; 48 u32 param_1 = 0;
47 u32 retval = func(&param_1, (u32)PARAM(1)).raw; 49 u32 retval = func(&param_1, (u32)Param(1)).raw;
48 Core::CurrentArmInterface().SetReg(1, param_1); 50 Core::CurrentArmInterface().SetReg(1, param_1);
49 FuncReturn(retval); 51 FuncReturn(retval);
50} 52}
@@ -52,39 +54,39 @@ void SvcWrap() {
52template <ResultCode func(u32*, u64)> 54template <ResultCode func(u32*, u64)>
53void SvcWrap() { 55void SvcWrap() {
54 u32 param_1 = 0; 56 u32 param_1 = 0;
55 u32 retval = func(&param_1, PARAM(1)).raw; 57 u32 retval = func(&param_1, Param(1)).raw;
56 Core::CurrentArmInterface().SetReg(1, param_1); 58 Core::CurrentArmInterface().SetReg(1, param_1);
57 FuncReturn(retval); 59 FuncReturn(retval);
58} 60}
59 61
60template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
61void SvcWrap() { 63void SvcWrap() {
62 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); 64 FuncReturn(func(Param(0), (s32)Param(1)).raw);
63} 65}
64 66
65template <ResultCode func(u64*, u64)> 67template <ResultCode func(u64*, u64)>
66void SvcWrap() { 68void SvcWrap() {
67 u64 param_1 = 0; 69 u64 param_1 = 0;
68 u32 retval = func(&param_1, PARAM(1)).raw; 70 u32 retval = func(&param_1, Param(1)).raw;
69 Core::CurrentArmInterface().SetReg(1, param_1); 71 Core::CurrentArmInterface().SetReg(1, param_1);
70 FuncReturn(retval); 72 FuncReturn(retval);
71} 73}
72 74
73template <ResultCode func(u32, u64)> 75template <ResultCode func(u32, u64)>
74void SvcWrap() { 76void SvcWrap() {
75 FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw); 77 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw);
76} 78}
77 79
78template <ResultCode func(u32, u32, u64)> 80template <ResultCode func(u32, u32, u64)>
79void SvcWrap() { 81void SvcWrap() {
80 FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); 82 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw);
81} 83}
82 84
83template <ResultCode func(u32, u32*, u64*)> 85template <ResultCode func(u32, u32*, u64*)>
84void SvcWrap() { 86void SvcWrap() {
85 u32 param_1 = 0; 87 u32 param_1 = 0;
86 u64 param_2 = 0; 88 u64 param_2 = 0;
87 ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), &param_1, &param_2); 89 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2);
88 Core::CurrentArmInterface().SetReg(1, param_1); 90 Core::CurrentArmInterface().SetReg(1, param_1);
89 Core::CurrentArmInterface().SetReg(2, param_2); 91 Core::CurrentArmInterface().SetReg(2, param_2);
90 FuncReturn(retval.raw); 92 FuncReturn(retval.raw);
@@ -93,46 +95,46 @@ void SvcWrap() {
93template <ResultCode func(u64, u64, u32, u32)> 95template <ResultCode func(u64, u64, u32, u32)>
94void SvcWrap() { 96void SvcWrap() {
95 FuncReturn( 97 FuncReturn(
96 func(PARAM(0), PARAM(1), (u32)(PARAM(3) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw); 98 func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw);
97} 99}
98 100
99template <ResultCode func(u32, u64, u32)> 101template <ResultCode func(u32, u64, u32)>
100void SvcWrap() { 102void SvcWrap() {
101 FuncReturn(func((u32)PARAM(0), PARAM(1), (u32)PARAM(2)).raw); 103 FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw);
102} 104}
103 105
104template <ResultCode func(u64, u64, u64)> 106template <ResultCode func(u64, u64, u64)>
105void SvcWrap() { 107void SvcWrap() {
106 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); 108 FuncReturn(func(Param(0), Param(1), Param(2)).raw);
107} 109}
108 110
109template <ResultCode func(u32, u64, u64, u32)> 111template <ResultCode func(u32, u64, u64, u32)>
110void SvcWrap() { 112void SvcWrap() {
111 FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); 113 FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw);
112} 114}
113 115
114template <ResultCode func(u32, u64, u64)> 116template <ResultCode func(u32, u64, u64)>
115void SvcWrap() { 117void SvcWrap() {
116 FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); 118 FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw);
117} 119}
118 120
119template <ResultCode func(u32*, u64, u64, s64)> 121template <ResultCode func(u32*, u64, u64, s64)>
120void SvcWrap() { 122void SvcWrap() {
121 u32 param_1 = 0; 123 u32 param_1 = 0;
122 ResultCode retval = func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); 124 ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3));
123 Core::CurrentArmInterface().SetReg(1, param_1); 125 Core::CurrentArmInterface().SetReg(1, param_1);
124 FuncReturn(retval.raw); 126 FuncReturn(retval.raw);
125} 127}
126 128
127template <ResultCode func(u64, u64, u32, s64)> 129template <ResultCode func(u64, u64, u32, s64)>
128void SvcWrap() { 130void SvcWrap() {
129 FuncReturn(func(PARAM(0), PARAM(1), (u32)PARAM(2), (s64)PARAM(3)).raw); 131 FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw);
130} 132}
131 133
132template <ResultCode func(u64*, u64, u64, u64)> 134template <ResultCode func(u64*, u64, u64, u64)>
133void SvcWrap() { 135void SvcWrap() {
134 u64 param_1 = 0; 136 u64 param_1 = 0;
135 u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3)).raw; 137 u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw;
136 Core::CurrentArmInterface().SetReg(1, param_1); 138 Core::CurrentArmInterface().SetReg(1, param_1);
137 FuncReturn(retval); 139 FuncReturn(retval);
138} 140}
@@ -141,7 +143,7 @@ template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
141void SvcWrap() { 143void SvcWrap() {
142 u32 param_1 = 0; 144 u32 param_1 = 0;
143 u32 retval = 145 u32 retval =
144 func(&param_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) 146 func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF))
145 .raw; 147 .raw;
146 Core::CurrentArmInterface().SetReg(1, param_1); 148 Core::CurrentArmInterface().SetReg(1, param_1);
147 FuncReturn(retval); 149 FuncReturn(retval);
@@ -151,13 +153,13 @@ template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
151void SvcWrap() { 153void SvcWrap() {
152 MemoryInfo memory_info = {}; 154 MemoryInfo memory_info = {};
153 PageInfo page_info = {}; 155 PageInfo page_info = {};
154 u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; 156 u32 retval = func(&memory_info, &page_info, Param(2)).raw;
155 157
156 Memory::Write64(PARAM(0), memory_info.base_address); 158 Memory::Write64(Param(0), memory_info.base_address);
157 Memory::Write64(PARAM(0) + 8, memory_info.size); 159 Memory::Write64(Param(0) + 8, memory_info.size);
158 Memory::Write32(PARAM(0) + 16, memory_info.type); 160 Memory::Write32(Param(0) + 16, memory_info.type);
159 Memory::Write32(PARAM(0) + 20, memory_info.attributes); 161 Memory::Write32(Param(0) + 20, memory_info.attributes);
160 Memory::Write32(PARAM(0) + 24, memory_info.permission); 162 Memory::Write32(Param(0) + 24, memory_info.permission);
161 163
162 FuncReturn(retval); 164 FuncReturn(retval);
163} 165}
@@ -165,7 +167,7 @@ void SvcWrap() {
165template <ResultCode func(u32*, u64, u64, u32)> 167template <ResultCode func(u32*, u64, u64, u32)>
166void SvcWrap() { 168void SvcWrap() {
167 u32 param_1 = 0; 169 u32 param_1 = 0;
168 u32 retval = func(&param_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; 170 u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw;
169 Core::CurrentArmInterface().SetReg(1, param_1); 171 Core::CurrentArmInterface().SetReg(1, param_1);
170 FuncReturn(retval); 172 FuncReturn(retval);
171} 173}
@@ -174,7 +176,7 @@ template <ResultCode func(Handle*, u64, u32, u32)>
174void SvcWrap() { 176void SvcWrap() {
175 u32 param_1 = 0; 177 u32 param_1 = 0;
176 u32 retval = 178 u32 retval =
177 func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; 179 func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw;
178 Core::CurrentArmInterface().SetReg(1, param_1); 180 Core::CurrentArmInterface().SetReg(1, param_1);
179 FuncReturn(retval); 181 FuncReturn(retval);
180} 182}
@@ -182,14 +184,14 @@ void SvcWrap() {
182template <ResultCode func(u64, u32, s32, s64)> 184template <ResultCode func(u64, u32, s32, s64)>
183void SvcWrap() { 185void SvcWrap() {
184 FuncReturn( 186 FuncReturn(
185 func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) 187 func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3))
186 .raw); 188 .raw);
187} 189}
188 190
189template <ResultCode func(u64, u32, s32, s32)> 191template <ResultCode func(u64, u32, s32, s32)>
190void SvcWrap() { 192void SvcWrap() {
191 FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), 193 FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF),
192 (s32)(PARAM(3) & 0xFFFFFFFF)) 194 (s32)(Param(3) & 0xFFFFFFFF))
193 .raw); 195 .raw);
194} 196}
195 197
@@ -219,20 +221,17 @@ void SvcWrap() {
219 221
220template <void func(s64)> 222template <void func(s64)>
221void SvcWrap() { 223void SvcWrap() {
222 func((s64)PARAM(0)); 224 func((s64)Param(0));
223} 225}
224 226
225template <void func(u64, s32 len)> 227template <void func(u64, u64 len)>
226void SvcWrap() { 228void SvcWrap() {
227 func(PARAM(0), (s32)(PARAM(1) & 0xFFFFFFFF)); 229 func(Param(0), Param(1));
228} 230}
229 231
230template <void func(u64, u64, u64)> 232template <void func(u64, u64, u64)>
231void SvcWrap() { 233void SvcWrap() {
232 func(PARAM(0), PARAM(1), PARAM(2)); 234 func(Param(0), Param(1), Param(2));
233} 235}
234 236
235#undef PARAM
236#undef FuncReturn
237
238} // namespace Kernel 237} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 3d10d9af2..c2d7535c9 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -217,8 +217,8 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
217 context.cpu_registers[0] = arg; 217 context.cpu_registers[0] = arg;
218 context.pc = entry_point; 218 context.pc = entry_point;
219 context.sp = stack_top; 219 context.sp = stack_top;
220 context.cpsr = 0; 220 context.pstate = 0;
221 context.fpscr = 0; 221 context.fpcr = 0;
222} 222}
223 223
224ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 224ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
@@ -227,12 +227,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
227 // Check if priority is in ranged. Lowest priority -> highest priority id. 227 // Check if priority is in ranged. Lowest priority -> highest priority id.
228 if (priority > THREADPRIO_LOWEST) { 228 if (priority > THREADPRIO_LOWEST) {
229 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 229 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
230 return ERR_OUT_OF_RANGE; 230 return ERR_INVALID_THREAD_PRIORITY;
231 } 231 }
232 232
233 if (processor_id > THREADPROCESSORID_MAX) { 233 if (processor_id > THREADPROCESSORID_MAX) {
234 LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); 234 LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
235 return ERR_OUT_OF_RANGE_KERNEL; 235 return ERR_INVALID_PROCESSOR_ID;
236 } 236 }
237 237
238 // TODO(yuriks): Other checks, returning 0xD9001BEA 238 // TODO(yuriks): Other checks, returning 0xD9001BEA
@@ -275,7 +275,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
275 available_slot = 0; // Use the first slot in the new page 275 available_slot = 0; // Use the first slot in the new page
276 276
277 // Allocate some memory from the end of the linear heap for this region. 277 // Allocate some memory from the end of the linear heap for this region.
278 const size_t offset = thread->tls_memory->size(); 278 const std::size_t offset = thread->tls_memory->size();
279 thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); 279 thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
280 280
281 auto& vm_manager = owner_process->vm_manager; 281 auto& vm_manager = owner_process->vm_manager;
@@ -311,13 +311,13 @@ void Thread::BoostPriority(u32 priority) {
311} 311}
312 312
313SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 313SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
314 SharedPtr<Process> owner_process) { 314 Process& owner_process) {
315 // Setup page table so we can write to memory 315 // Setup page table so we can write to memory
316 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); 316 SetCurrentPageTable(&owner_process.vm_manager.page_table);
317 317
318 // Initialize new "main" thread 318 // Initialize new "main" thread
319 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, 319 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
320 Memory::STACK_AREA_VADDR_END, std::move(owner_process)); 320 Memory::STACK_AREA_VADDR_END, &owner_process);
321 321
322 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 322 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
323 323
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 20f50458b..91e9b79ec 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -15,6 +15,12 @@
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
17 17
18namespace Kernel {
19
20class KernelCore;
21class Process;
22class Scheduler;
23
18enum ThreadPriority : u32 { 24enum ThreadPriority : u32 {
19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 25 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 26 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
@@ -54,12 +60,6 @@ enum class ThreadWakeupReason {
54 Timeout // The thread was woken up due to a wait timeout. 60 Timeout // The thread was woken up due to a wait timeout.
55}; 61};
56 62
57namespace Kernel {
58
59class KernelCore;
60class Process;
61class Scheduler;
62
63class Thread final : public WaitObject { 63class Thread final : public WaitObject {
64public: 64public:
65 /** 65 /**
@@ -254,7 +254,7 @@ public:
254 Handle callback_handle; 254 Handle callback_handle;
255 255
256 using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, 256 using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
257 SharedPtr<WaitObject> object, size_t index); 257 SharedPtr<WaitObject> object, std::size_t index);
258 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread 258 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
259 // was waiting via WaitSynchronizationN then the object will be the last object that became 259 // was waiting via WaitSynchronizationN then the object will be the last object that became
260 // available. In case of a timeout, the object will be nullptr. 260 // available. In case of a timeout, the object will be nullptr.
@@ -281,7 +281,7 @@ private:
281 * @return A shared pointer to the main thread 281 * @return A shared pointer to the main thread
282 */ 282 */
283SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 283SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
284 SharedPtr<Process> owner_process); 284 Process& owner_process);
285 285
286/** 286/**
287 * Gets the current thread 287 * Gets the current thread
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 479cacb62..608cbd57b 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -86,7 +86,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
86 86
87ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, 87ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
88 std::shared_ptr<std::vector<u8>> block, 88 std::shared_ptr<std::vector<u8>> block,
89 size_t offset, u64 size, 89 std::size_t offset, u64 size,
90 MemoryState state) { 90 MemoryState state) {
91 ASSERT(block != nullptr); 91 ASSERT(block != nullptr);
92 ASSERT(offset + size <= block->size()); 92 ASSERT(offset + size <= block->size());
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 98bd04bea..de75036c0 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -81,7 +81,7 @@ struct VirtualMemoryArea {
81 /// Memory block backing this VMA. 81 /// Memory block backing this VMA.
82 std::shared_ptr<std::vector<u8>> backing_block = nullptr; 82 std::shared_ptr<std::vector<u8>> backing_block = nullptr;
83 /// Offset into the backing_memory the mapping starts from. 83 /// Offset into the backing_memory the mapping starts from.
84 size_t offset = 0; 84 std::size_t offset = 0;
85 85
86 // Settings for type = BackingMemory 86 // Settings for type = BackingMemory
87 /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. 87 /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
@@ -147,7 +147,7 @@ public:
147 * @param state MemoryState tag to attach to the VMA. 147 * @param state MemoryState tag to attach to the VMA.
148 */ 148 */
149 ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, 149 ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
150 size_t offset, u64 size, MemoryState state); 150 std::size_t offset, u64 size, MemoryState state);
151 151
152 /** 152 /**
153 * Maps an unmanaged host memory pointer at a given address. 153 * Maps an unmanaged host memory pointer at a given address.
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index eef00b729..b190ceb98 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -81,7 +81,7 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
81 } 81 }
82 } 82 }
83 83
84 size_t index = thread->GetWaitObjectIndex(this); 84 std::size_t index = thread->GetWaitObjectIndex(this);
85 85
86 for (auto& object : thread->wait_objects) 86 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get()); 87 object->RemoveWaitingThread(thread.get());
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1502dbf55..e61748ca3 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -34,7 +34,7 @@ public:
34 static const FunctionInfo functions[] = { 34 static const FunctionInfo functions[] = {
35 {0, &IProfile::Get, "Get"}, 35 {0, &IProfile::Get, "Get"},
36 {1, &IProfile::GetBase, "GetBase"}, 36 {1, &IProfile::GetBase, "GetBase"},
37 {10, nullptr, "GetImageSize"}, 37 {10, &IProfile::GetImageSize, "GetImageSize"},
38 {11, &IProfile::LoadImage, "LoadImage"}, 38 {11, &IProfile::LoadImage, "LoadImage"},
39 }; 39 };
40 RegisterHandlers(functions); 40 RegisterHandlers(functions);
@@ -93,6 +93,14 @@ private:
93 rb.Push<u32>(jpeg_size); 93 rb.Push<u32>(jpeg_size);
94 } 94 }
95 95
96 void GetImageSize(Kernel::HLERequestContext& ctx) {
97 LOG_WARNING(Service_ACC, "(STUBBED) called");
98 constexpr u32 jpeg_size = 107;
99 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(RESULT_SUCCESS);
101 rb.Push<u32>(jpeg_size);
102 }
103
96 const ProfileManager& profile_manager; 104 const ProfileManager& profile_manager;
97 UUID user_id; ///< The user id this profile refers to. 105 UUID user_id; ///< The user id this profile refers to.
98}; 106};
@@ -122,11 +130,10 @@ private:
122 130
123 void GetAccountId(Kernel::HLERequestContext& ctx) { 131 void GetAccountId(Kernel::HLERequestContext& ctx) {
124 LOG_WARNING(Service_ACC, "(STUBBED) called"); 132 LOG_WARNING(Service_ACC, "(STUBBED) called");
125 // TODO(Subv): Find out what this actually does and implement it. Stub it as an error for 133 // Should return a nintendo account ID
126 // now since we do not implement NNID. Returning a bogus id here will cause games to send 134 IPC::ResponseBuilder rb{ctx, 4};
127 // invalid IPC requests after ListOpenUsers is called. 135 rb.Push(RESULT_SUCCESS);
128 IPC::ResponseBuilder rb{ctx, 2}; 136 rb.PushRaw<u64>(1);
129 rb.Push(ResultCode(-1));
130 } 137 }
131}; 138};
132 139
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index 9bd595a37..e84d9f7cf 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -18,4 +18,6 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
18 RegisterHandlers(functions); 18 RegisterHandlers(functions);
19} 19}
20 20
21ACC_AA::~ACC_AA() = default;
22
21} // namespace Service::Account 23} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h
index 2e08c781a..9edb0421b 100644
--- a/src/core/hle/service/acc/acc_aa.h
+++ b/src/core/hle/service/acc/acc_aa.h
@@ -12,6 +12,7 @@ class ACC_AA final : public Module::Interface {
12public: 12public:
13 explicit ACC_AA(std::shared_ptr<Module> module, 13 explicit ACC_AA(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager); 14 std::shared_ptr<ProfileManager> profile_manager);
15 ~ACC_AA() override;
15}; 16};
16 17
17} // namespace Service::Account 18} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 0218ee859..ad455c3a7 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -51,4 +51,6 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
51 RegisterHandlers(functions); 51 RegisterHandlers(functions);
52} 52}
53 53
54ACC_SU::~ACC_SU() = default;
55
54} // namespace Service::Account 56} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h
index 79a47d88d..fcced063a 100644
--- a/src/core/hle/service/acc/acc_su.h
+++ b/src/core/hle/service/acc/acc_su.h
@@ -6,14 +6,13 @@
6 6
7#include "core/hle/service/acc/acc.h" 7#include "core/hle/service/acc/acc.h"
8 8
9namespace Service { 9namespace Service::Account {
10namespace Account {
11 10
12class ACC_SU final : public Module::Interface { 11class ACC_SU final : public Module::Interface {
13public: 12public:
14 explicit ACC_SU(std::shared_ptr<Module> module, 13 explicit ACC_SU(std::shared_ptr<Module> module,
15 std::shared_ptr<ProfileManager> profile_manager); 14 std::shared_ptr<ProfileManager> profile_manager);
15 ~ACC_SU() override;
16}; 16};
17 17
18} // namespace Account 18} // namespace Service::Account
19} // namespace Service
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 84a4d05b8..72d4adf35 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -31,4 +31,6 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
31 RegisterHandlers(functions); 31 RegisterHandlers(functions);
32} 32}
33 33
34ACC_U0::~ACC_U0() = default;
35
34} // namespace Service::Account 36} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index e8a114f99..a1290e0bd 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -12,6 +12,7 @@ class ACC_U0 final : public Module::Interface {
12public: 12public:
13 explicit ACC_U0(std::shared_ptr<Module> module, 13 explicit ACC_U0(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager); 14 std::shared_ptr<ProfileManager> profile_manager);
15 ~ACC_U0() override;
15}; 16};
16 17
17} // namespace Service::Account 18} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 495693949..d480f08e5 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -38,4 +38,6 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
38 RegisterHandlers(functions); 38 RegisterHandlers(functions);
39} 39}
40 40
41ACC_U1::~ACC_U1() = default;
42
41} // namespace Service::Account 43} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h
index a77520e6f..9e79daee3 100644
--- a/src/core/hle/service/acc/acc_u1.h
+++ b/src/core/hle/service/acc/acc_u1.h
@@ -12,6 +12,7 @@ class ACC_U1 final : public Module::Interface {
12public: 12public:
13 explicit ACC_U1(std::shared_ptr<Module> module, 13 explicit ACC_U1(std::shared_ptr<Module> module,
14 std::shared_ptr<ProfileManager> profile_manager); 14 std::shared_ptr<ProfileManager> profile_manager);
15 ~ACC_U1() override;
15}; 16};
16 17
17} // namespace Service::Account 18} // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index e0b03d763..bcb3475db 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -25,13 +25,15 @@ const UUID& UUID::Generate() {
25ProfileManager::ProfileManager() { 25ProfileManager::ProfileManager() {
26 // TODO(ogniK): Create the default user we have for now until loading/saving users is added 26 // TODO(ogniK): Create the default user we have for now until loading/saving users is added
27 auto user_uuid = UUID{1, 0}; 27 auto user_uuid = UUID{1, 0};
28 CreateNewUser(user_uuid, Settings::values.username); 28 ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
29 OpenUser(user_uuid); 29 OpenUser(user_uuid);
30} 30}
31 31
32ProfileManager::~ProfileManager() = default;
33
32/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 34/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
33/// internal management of the users profiles 35/// internal management of the users profiles
34boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { 36boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
35 if (user_count >= MAX_USERS) { 37 if (user_count >= MAX_USERS) {
36 return boost::none; 38 return boost::none;
37 } 39 }
@@ -40,7 +42,7 @@ boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
40} 42}
41 43
42/// Deletes a specific profile based on it's profile index 44/// Deletes a specific profile based on it's profile index
43bool ProfileManager::RemoveProfileAtIndex(size_t index) { 45bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
44 if (index >= MAX_USERS || index >= user_count) { 46 if (index >= MAX_USERS || index >= user_count) {
45 return false; 47 return false;
46 } 48 }
@@ -89,7 +91,8 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
89/// specifically by allowing an std::string for the username. This is required specifically since 91/// specifically by allowing an std::string for the username. This is required specifically since
90/// we're loading a string straight from the config 92/// we're loading a string straight from the config
91ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { 93ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
92 ProfileUsername username_output; 94 ProfileUsername username_output{};
95
93 if (username.size() > username_output.size()) { 96 if (username.size() > username_output.size()) {
94 std::copy_n(username.begin(), username_output.size(), username_output.begin()); 97 std::copy_n(username.begin(), username_output.size(), username_output.begin());
95 } else { 98 } else {
@@ -99,7 +102,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
99} 102}
100 103
101/// Returns a users profile index based on their user id. 104/// Returns a users profile index based on their user id.
102boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 105boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
103 if (!uuid) { 106 if (!uuid) {
104 return boost::none; 107 return boost::none;
105 } 108 }
@@ -108,16 +111,17 @@ boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
108 if (iter == profiles.end()) { 111 if (iter == profiles.end()) {
109 return boost::none; 112 return boost::none;
110 } 113 }
111 return static_cast<size_t>(std::distance(profiles.begin(), iter)); 114 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
112} 115}
113 116
114/// Returns a users profile index based on their profile 117/// Returns a users profile index based on their profile
115boost::optional<size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { 118boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
116 return GetUserIndex(user.user_uuid); 119 return GetUserIndex(user.user_uuid);
117} 120}
118 121
119/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 122/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
120bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const { 123bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
124 ProfileBase& profile) const {
121 if (index == boost::none || index >= MAX_USERS) { 125 if (index == boost::none || index >= MAX_USERS) {
122 return false; 126 return false;
123 } 127 }
@@ -141,14 +145,16 @@ bool ProfileManager::GetProfileBase(const ProfileInfo& user, ProfileBase& profil
141 145
142/// Returns the current user count on the system. We keep a variable which tracks the count so we 146/// Returns the current user count on the system. We keep a variable which tracks the count so we
143/// don't have to loop the internal profile array every call. 147/// don't have to loop the internal profile array every call.
144size_t ProfileManager::GetUserCount() const { 148
149std::size_t ProfileManager::GetUserCount() const {
145 return user_count; 150 return user_count;
146} 151}
147 152
148/// Lists the current "opened" users on the system. Users are typically not open until they sign 153/// Lists the current "opened" users on the system. Users are typically not open until they sign
149/// into something or pick a profile. As of right now users should all be open until qlaunch is 154/// into something or pick a profile. As of right now users should all be open until qlaunch is
150/// booting 155/// booting
151size_t ProfileManager::GetOpenUserCount() const { 156
157std::size_t ProfileManager::GetOpenUserCount() const {
152 return std::count_if(profiles.begin(), profiles.end(), 158 return std::count_if(profiles.begin(), profiles.end(),
153 [](const ProfileInfo& p) { return p.is_open; }); 159 [](const ProfileInfo& p) { return p.is_open; });
154} 160}
@@ -204,7 +210,7 @@ UUID ProfileManager::GetLastOpenedUser() const {
204} 210}
205 211
206/// Return the users profile base and the unknown arbitary data. 212/// Return the users profile base and the unknown arbitary data.
207bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, 213bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
208 ProfileData& data) const { 214 ProfileData& data) const {
209 if (GetProfileBase(index, profile)) { 215 if (GetProfileBase(index, profile)) {
210 data = profiles[index.get()].data; 216 data = profiles[index.get()].data;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 52967844d..bffd4cf4d 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -12,8 +12,8 @@
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13 13
14namespace Service::Account { 14namespace Service::Account {
15constexpr size_t MAX_USERS = 8; 15constexpr std::size_t MAX_USERS = 8;
16constexpr size_t MAX_DATA = 128; 16constexpr std::size_t MAX_DATA = 128;
17constexpr u128 INVALID_UUID{{0, 0}}; 17constexpr u128 INVALID_UUID{{0, 0}};
18 18
19struct UUID { 19struct UUID {
@@ -82,21 +82,23 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
82class ProfileManager { 82class ProfileManager {
83public: 83public:
84 ProfileManager(); // TODO(ogniK): Load from system save 84 ProfileManager(); // TODO(ogniK): Load from system save
85 ~ProfileManager();
86
85 ResultCode AddUser(const ProfileInfo& user); 87 ResultCode AddUser(const ProfileInfo& user);
86 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); 88 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
87 ResultCode CreateNewUser(UUID uuid, const std::string& username); 89 ResultCode CreateNewUser(UUID uuid, const std::string& username);
88 boost::optional<size_t> GetUserIndex(const UUID& uuid) const; 90 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
89 boost::optional<size_t> GetUserIndex(const ProfileInfo& user) const; 91 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
90 bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const; 92 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
91 bool GetProfileBase(UUID uuid, ProfileBase& profile) const; 93 bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
92 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; 94 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
93 bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, 95 bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
94 ProfileData& data) const; 96 ProfileData& data) const;
95 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; 97 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
96 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, 98 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
97 ProfileData& data) const; 99 ProfileData& data) const;
98 size_t GetUserCount() const; 100 std::size_t GetUserCount() const;
99 size_t GetOpenUserCount() const; 101 std::size_t GetOpenUserCount() const;
100 bool UserExists(UUID uuid) const; 102 bool UserExists(UUID uuid) const;
101 void OpenUser(UUID uuid); 103 void OpenUser(UUID uuid);
102 void CloseUser(UUID uuid); 104 void CloseUser(UUID uuid);
@@ -108,9 +110,9 @@ public:
108 110
109private: 111private:
110 std::array<ProfileInfo, MAX_USERS> profiles{}; 112 std::array<ProfileInfo, MAX_USERS> profiles{};
111 size_t user_count = 0; 113 std::size_t user_count = 0;
112 boost::optional<size_t> AddToProfiles(const ProfileInfo& profile); 114 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
113 bool RemoveProfileAtIndex(size_t index); 115 bool RemoveProfileAtIndex(std::size_t index);
114 UUID last_opened_user{INVALID_UUID}; 116 UUID last_opened_user{INVALID_UUID};
115}; 117};
116 118
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 818c03e0f..69bfce1c1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -20,6 +20,7 @@
20#include "core/hle/service/nvflinger/nvflinger.h" 20#include "core/hle/service/nvflinger/nvflinger.h"
21#include "core/hle/service/pm/pm.h" 21#include "core/hle/service/pm/pm.h"
22#include "core/hle/service/set/set.h" 22#include "core/hle/service/set/set.h"
23#include "core/hle/service/vi/vi.h"
23#include "core/settings.h" 24#include "core/settings.h"
24 25
25namespace Service::AM { 26namespace Service::AM {
@@ -35,6 +36,8 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
35 RegisterHandlers(functions); 36 RegisterHandlers(functions);
36} 37}
37 38
39IWindowController::~IWindowController() = default;
40
38void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { 41void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
39 LOG_WARNING(Service_AM, "(STUBBED) called"); 42 LOG_WARNING(Service_AM, "(STUBBED) called");
40 IPC::ResponseBuilder rb{ctx, 4}; 43 IPC::ResponseBuilder rb{ctx, 4};
@@ -61,6 +64,8 @@ IAudioController::IAudioController() : ServiceFramework("IAudioController") {
61 RegisterHandlers(functions); 64 RegisterHandlers(functions);
62} 65}
63 66
67IAudioController::~IAudioController() = default;
68
64void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 69void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
65 LOG_WARNING(Service_AM, "(STUBBED) called"); 70 LOG_WARNING(Service_AM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 2}; 71 IPC::ResponseBuilder rb{ctx, 2};
@@ -116,7 +121,10 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
116 RegisterHandlers(functions); 121 RegisterHandlers(functions);
117} 122}
118 123
124IDisplayController::~IDisplayController() = default;
125
119IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} 126IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
127IDebugFunctions::~IDebugFunctions() = default;
120 128
121ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 129ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
122 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { 130 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
@@ -165,6 +173,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
165 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent"); 173 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
166} 174}
167 175
176ISelfController::~ISelfController() = default;
177
168void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 178void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
169 // Takes 3 input u8s with each field located immediately after the previous u8, these are 179 // Takes 3 input u8s with each field located immediately after the previous u8, these are
170 // bool flags. No output. 180 // bool flags. No output.
@@ -325,7 +335,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
325 {51, nullptr, "SetVrModeEnabled"}, 335 {51, nullptr, "SetVrModeEnabled"},
326 {52, nullptr, "SwitchLcdBacklight"}, 336 {52, nullptr, "SwitchLcdBacklight"},
327 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 337 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
328 {60, nullptr, "GetDefaultDisplayResolution"}, 338 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
329 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, 339 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
330 "GetDefaultDisplayResolutionChangeEvent"}, 340 "GetDefaultDisplayResolutionChangeEvent"},
331 {62, nullptr, "GetHdcpAuthenticationState"}, 341 {62, nullptr, "GetHdcpAuthenticationState"},
@@ -337,6 +347,8 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
337 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); 347 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
338} 348}
339 349
350ICommonStateGetter::~ICommonStateGetter() = default;
351
340void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { 352void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
341 IPC::ResponseBuilder rb{ctx, 3}; 353 IPC::ResponseBuilder rb{ctx, 3};
342 rb.Push(RESULT_SUCCESS); 354 rb.Push(RESULT_SUCCESS);
@@ -382,6 +394,21 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLEReque
382 LOG_WARNING(Service_AM, "(STUBBED) called"); 394 LOG_WARNING(Service_AM, "(STUBBED) called");
383} 395}
384 396
397void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
398 IPC::ResponseBuilder rb{ctx, 4};
399 rb.Push(RESULT_SUCCESS);
400
401 if (Settings::values.use_docked_mode) {
402 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
403 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
404 } else {
405 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
406 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
407 }
408
409 LOG_DEBUG(Service_AM, "called");
410}
411
385void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 412void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
386 const bool use_docked_mode{Settings::values.use_docked_mode}; 413 const bool use_docked_mode{Settings::values.use_docked_mode};
387 IPC::ResponseBuilder rb{ctx, 3}; 414 IPC::ResponseBuilder rb{ctx, 3};
@@ -435,7 +462,7 @@ private:
435 462
436 std::memcpy(&buffer[offset], data.data(), data.size()); 463 std::memcpy(&buffer[offset], data.data(), data.size());
437 464
438 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 465 IPC::ResponseBuilder rb{ctx, 2};
439 rb.Push(RESULT_SUCCESS); 466 rb.Push(RESULT_SUCCESS);
440 467
441 LOG_DEBUG(Service_AM, "called, offset={}", offset); 468 LOG_DEBUG(Service_AM, "called, offset={}", offset);
@@ -445,13 +472,13 @@ private:
445 IPC::RequestParser rp{ctx}; 472 IPC::RequestParser rp{ctx};
446 473
447 const u64 offset{rp.Pop<u64>()}; 474 const u64 offset{rp.Pop<u64>()};
448 const size_t size{ctx.GetWriteBufferSize()}; 475 const std::size_t size{ctx.GetWriteBufferSize()};
449 476
450 ASSERT(offset + size <= buffer.size()); 477 ASSERT(offset + size <= buffer.size());
451 478
452 ctx.WriteBuffer(buffer.data() + offset, size); 479 ctx.WriteBuffer(buffer.data() + offset, size);
453 480
454 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 481 IPC::ResponseBuilder rb{ctx, 2};
455 rb.Push(RESULT_SUCCESS); 482 rb.Push(RESULT_SUCCESS);
456 483
457 LOG_DEBUG(Service_AM, "called, offset={}", offset); 484 LOG_DEBUG(Service_AM, "called, offset={}", offset);
@@ -541,7 +568,7 @@ private:
541 IPC::RequestParser rp{ctx}; 568 IPC::RequestParser rp{ctx};
542 storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); 569 storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
543 570
544 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; 571 IPC::ResponseBuilder rb{ctx, 2};
545 rb.Push(RESULT_SUCCESS); 572 rb.Push(RESULT_SUCCESS);
546 573
547 LOG_DEBUG(Service_AM, "called"); 574 LOG_DEBUG(Service_AM, "called");
@@ -573,6 +600,8 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
573 RegisterHandlers(functions); 600 RegisterHandlers(functions);
574} 601}
575 602
603ILibraryAppletCreator::~ILibraryAppletCreator() = default;
604
576void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 605void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
577 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 606 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
578 607
@@ -587,7 +616,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
587 const u64 size{rp.Pop<u64>()}; 616 const u64 size{rp.Pop<u64>()};
588 std::vector<u8> buffer(size); 617 std::vector<u8> buffer(size);
589 618
590 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)}; 619 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
591 rb.Push(RESULT_SUCCESS); 620 rb.Push(RESULT_SUCCESS);
592 rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); 621 rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
593 622
@@ -638,6 +667,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 RegisterHandlers(functions); 667 RegisterHandlers(functions);
639} 668}
640 669
670IApplicationFunctions::~IApplicationFunctions() = default;
671
641void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 672void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
642 constexpr std::array<u8, 0x88> data{{ 673 constexpr std::array<u8, 0x88> data{{
643 0xca, 0x97, 0x94, 0xc7, // Magic 674 0xca, 0x97, 0x94, 0xc7, // Magic
@@ -760,6 +791,8 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
760 RegisterHandlers(functions); 791 RegisterHandlers(functions);
761} 792}
762 793
794IHomeMenuFunctions::~IHomeMenuFunctions() = default;
795
763void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { 796void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) {
764 IPC::ResponseBuilder rb{ctx, 2}; 797 IPC::ResponseBuilder rb{ctx, 2};
765 rb.Push(RESULT_SUCCESS); 798 rb.Push(RESULT_SUCCESS);
@@ -783,6 +816,8 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
783 RegisterHandlers(functions); 816 RegisterHandlers(functions);
784} 817}
785 818
819IGlobalStateController::~IGlobalStateController() = default;
820
786IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { 821IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
787 static const FunctionInfo functions[] = { 822 static const FunctionInfo functions[] = {
788 {0, nullptr, "CreateApplication"}, 823 {0, nullptr, "CreateApplication"},
@@ -793,6 +828,8 @@ IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreat
793 RegisterHandlers(functions); 828 RegisterHandlers(functions);
794} 829}
795 830
831IApplicationCreator::~IApplicationCreator() = default;
832
796IProcessWindingController::IProcessWindingController() 833IProcessWindingController::IProcessWindingController()
797 : ServiceFramework("IProcessWindingController") { 834 : ServiceFramework("IProcessWindingController") {
798 static const FunctionInfo functions[] = { 835 static const FunctionInfo functions[] = {
@@ -807,4 +844,6 @@ IProcessWindingController::IProcessWindingController()
807 }; 844 };
808 RegisterHandlers(functions); 845 RegisterHandlers(functions);
809} 846}
847
848IProcessWindingController::~IProcessWindingController() = default;
810} // namespace Service::AM 849} // namespace Service::AM
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 9e8bb4e43..b39b0d838 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -42,6 +42,7 @@ enum SystemLanguage {
42class IWindowController final : public ServiceFramework<IWindowController> { 42class IWindowController final : public ServiceFramework<IWindowController> {
43public: 43public:
44 IWindowController(); 44 IWindowController();
45 ~IWindowController() override;
45 46
46private: 47private:
47 void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); 48 void GetAppletResourceUserId(Kernel::HLERequestContext& ctx);
@@ -51,6 +52,7 @@ private:
51class IAudioController final : public ServiceFramework<IAudioController> { 52class IAudioController final : public ServiceFramework<IAudioController> {
52public: 53public:
53 IAudioController(); 54 IAudioController();
55 ~IAudioController() override;
54 56
55private: 57private:
56 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); 58 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
@@ -63,16 +65,19 @@ private:
63class IDisplayController final : public ServiceFramework<IDisplayController> { 65class IDisplayController final : public ServiceFramework<IDisplayController> {
64public: 66public:
65 IDisplayController(); 67 IDisplayController();
68 ~IDisplayController() override;
66}; 69};
67 70
68class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { 71class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
69public: 72public:
70 IDebugFunctions(); 73 IDebugFunctions();
74 ~IDebugFunctions() override;
71}; 75};
72 76
73class ISelfController final : public ServiceFramework<ISelfController> { 77class ISelfController final : public ServiceFramework<ISelfController> {
74public: 78public:
75 explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 79 explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
80 ~ISelfController() override;
76 81
77private: 82private:
78 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); 83 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
@@ -98,6 +103,7 @@ private:
98class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 103class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
99public: 104public:
100 ICommonStateGetter(); 105 ICommonStateGetter();
106 ~ICommonStateGetter() override;
101 107
102private: 108private:
103 enum class FocusState : u8 { 109 enum class FocusState : u8 {
@@ -117,6 +123,7 @@ private:
117 void GetOperationMode(Kernel::HLERequestContext& ctx); 123 void GetOperationMode(Kernel::HLERequestContext& ctx);
118 void GetPerformanceMode(Kernel::HLERequestContext& ctx); 124 void GetPerformanceMode(Kernel::HLERequestContext& ctx);
119 void GetBootMode(Kernel::HLERequestContext& ctx); 125 void GetBootMode(Kernel::HLERequestContext& ctx);
126 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
120 127
121 Kernel::SharedPtr<Kernel::Event> event; 128 Kernel::SharedPtr<Kernel::Event> event;
122}; 129};
@@ -124,6 +131,7 @@ private:
124class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 131class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
125public: 132public:
126 ILibraryAppletCreator(); 133 ILibraryAppletCreator();
134 ~ILibraryAppletCreator() override;
127 135
128private: 136private:
129 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 137 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
@@ -133,6 +141,7 @@ private:
133class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 141class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
134public: 142public:
135 IApplicationFunctions(); 143 IApplicationFunctions();
144 ~IApplicationFunctions() override;
136 145
137private: 146private:
138 void PopLaunchParameter(Kernel::HLERequestContext& ctx); 147 void PopLaunchParameter(Kernel::HLERequestContext& ctx);
@@ -150,6 +159,7 @@ private:
150class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 159class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
151public: 160public:
152 IHomeMenuFunctions(); 161 IHomeMenuFunctions();
162 ~IHomeMenuFunctions() override;
153 163
154private: 164private:
155 void RequestToGetForeground(Kernel::HLERequestContext& ctx); 165 void RequestToGetForeground(Kernel::HLERequestContext& ctx);
@@ -158,16 +168,19 @@ private:
158class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { 168class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
159public: 169public:
160 IGlobalStateController(); 170 IGlobalStateController();
171 ~IGlobalStateController() override;
161}; 172};
162 173
163class IApplicationCreator final : public ServiceFramework<IApplicationCreator> { 174class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
164public: 175public:
165 IApplicationCreator(); 176 IApplicationCreator();
177 ~IApplicationCreator() override;
166}; 178};
167 179
168class IProcessWindingController final : public ServiceFramework<IProcessWindingController> { 180class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
169public: 181public:
170 IProcessWindingController(); 182 IProcessWindingController();
183 ~IProcessWindingController() override;
171}; 184};
172 185
173/// Registers all AM services with the specified service manager. 186/// Registers all AM services with the specified service manager.
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 7cebc918a..4296c255e 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -222,4 +222,6 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
222 RegisterHandlers(functions); 222 RegisterHandlers(functions);
223} 223}
224 224
225AppletAE::~AppletAE() = default;
226
225} // namespace Service::AM 227} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index bdc57b9bc..1ed77baa4 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -18,7 +18,7 @@ namespace AM {
18class AppletAE final : public ServiceFramework<AppletAE> { 18class AppletAE final : public ServiceFramework<AppletAE> {
19public: 19public:
20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
21 ~AppletAE() = default; 21 ~AppletAE() override;
22 22
23private: 23private:
24 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); 24 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index beea7d19b..e45cf6e20 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -103,4 +103,6 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
103 RegisterHandlers(functions); 103 RegisterHandlers(functions);
104} 104}
105 105
106AppletOE::~AppletOE() = default;
107
106} // namespace Service::AM 108} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index c52e2a322..60cfdfd9d 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -18,7 +18,7 @@ namespace AM {
18class AppletOE final : public ServiceFramework<AppletOE> { 18class AppletOE final : public ServiceFramework<AppletOE> {
19public: 19public:
20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
21 ~AppletOE() = default; 21 ~AppletOE() override;
22 22
23private: 23private:
24 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 24 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
index af46e9494..0e3088bc8 100644
--- a/src/core/hle/service/am/idle.cpp
+++ b/src/core/hle/service/am/idle.cpp
@@ -21,4 +21,6 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
21 RegisterHandlers(functions); 21 RegisterHandlers(functions);
22} 22}
23 23
24IdleSys::~IdleSys() = default;
25
24} // namespace Service::AM 26} // namespace Service::AM
diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h
index 1eb68d2c9..c44e856b1 100644
--- a/src/core/hle/service/am/idle.h
+++ b/src/core/hle/service/am/idle.h
@@ -11,6 +11,7 @@ namespace Service::AM {
11class IdleSys final : public ServiceFramework<IdleSys> { 11class IdleSys final : public ServiceFramework<IdleSys> {
12public: 12public:
13 explicit IdleSys(); 13 explicit IdleSys();
14 ~IdleSys() override;
14}; 15};
15 16
16} // namespace Service::AM 17} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 447fe8669..1c37f849f 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -39,4 +39,6 @@ OMM::OMM() : ServiceFramework{"omm"} {
39 RegisterHandlers(functions); 39 RegisterHandlers(functions);
40} 40}
41 41
42OMM::~OMM() = default;
43
42} // namespace Service::AM 44} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h
index 49e5d331c..59dc91b72 100644
--- a/src/core/hle/service/am/omm.h
+++ b/src/core/hle/service/am/omm.h
@@ -11,6 +11,7 @@ namespace Service::AM {
11class OMM final : public ServiceFramework<OMM> { 11class OMM final : public ServiceFramework<OMM> {
12public: 12public:
13 explicit OMM(); 13 explicit OMM();
14 ~OMM() override;
14}; 15};
15 16
16} // namespace Service::AM 17} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
index a05d433d0..003ee8667 100644
--- a/src/core/hle/service/am/spsm.cpp
+++ b/src/core/hle/service/am/spsm.cpp
@@ -27,4 +27,6 @@ SPSM::SPSM() : ServiceFramework{"spsm"} {
27 RegisterHandlers(functions); 27 RegisterHandlers(functions);
28} 28}
29 29
30SPSM::~SPSM() = default;
31
30} // namespace Service::AM 32} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h
index 57dde62e1..3a0b979fa 100644
--- a/src/core/hle/service/am/spsm.h
+++ b/src/core/hle/service/am/spsm.h
@@ -11,6 +11,7 @@ namespace Service::AM {
11class SPSM final : public ServiceFramework<SPSM> { 11class SPSM final : public ServiceFramework<SPSM> {
12public: 12public:
13 explicit SPSM(); 13 explicit SPSM();
14 ~SPSM() override;
14}; 15};
15 16
16} // namespace Service::AM 17} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 6e7438580..d9eeac9ec 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -23,6 +23,8 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u") {
23 RegisterHandlers(functions); 23 RegisterHandlers(functions);
24} 24}
25 25
26AOC_U::~AOC_U() = default;
27
26void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { 28void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
27 IPC::ResponseBuilder rb{ctx, 4}; 29 IPC::ResponseBuilder rb{ctx, 4};
28 rb.Push(RESULT_SUCCESS); 30 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 17d48ef30..29ce8f488 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -11,7 +11,7 @@ namespace Service::AOC {
11class AOC_U final : public ServiceFramework<AOC_U> { 11class AOC_U final : public ServiceFramework<AOC_U> {
12public: 12public:
13 AOC_U(); 13 AOC_U();
14 ~AOC_U() = default; 14 ~AOC_U() override;
15 15
16private: 16private:
17 void CountAddOnContent(Kernel::HLERequestContext& ctx); 17 void CountAddOnContent(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 4109cb7f7..f3c09bbb1 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -9,6 +9,9 @@
9 9
10namespace Service::APM { 10namespace Service::APM {
11 11
12Module::Module() = default;
13Module::~Module() = default;
14
12void InstallInterfaces(SM::ServiceManager& service_manager) { 15void InstallInterfaces(SM::ServiceManager& service_manager) {
13 auto module_ = std::make_shared<Module>(); 16 auto module_ = std::make_shared<Module>();
14 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); 17 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h
index 90a80d51b..4d7d5bb7c 100644
--- a/src/core/hle/service/apm/apm.h
+++ b/src/core/hle/service/apm/apm.h
@@ -15,8 +15,8 @@ enum class PerformanceMode : u8 {
15 15
16class Module final { 16class Module final {
17public: 17public:
18 Module() = default; 18 Module();
19 ~Module() = default; 19 ~Module();
20}; 20};
21 21
22/// Registers all AM services with the specified service manager. 22/// Registers all AM services with the specified service manager.
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index 4cd8132f5..c22bd3859 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -70,6 +70,8 @@ APM::APM(std::shared_ptr<Module> apm, const char* name)
70 RegisterHandlers(functions); 70 RegisterHandlers(functions);
71} 71}
72 72
73APM::~APM() = default;
74
73void APM::OpenSession(Kernel::HLERequestContext& ctx) { 75void APM::OpenSession(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 76 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
75 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
@@ -93,6 +95,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
93 RegisterHandlers(functions); 95 RegisterHandlers(functions);
94} 96}
95 97
98APM_Sys::~APM_Sys() = default;
99
96void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { 100void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
97 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 101 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
98 rb.Push(RESULT_SUCCESS); 102 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index d14264ad7..773541aa4 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -11,7 +11,7 @@ namespace Service::APM {
11class APM final : public ServiceFramework<APM> { 11class APM final : public ServiceFramework<APM> {
12public: 12public:
13 explicit APM(std::shared_ptr<Module> apm, const char* name); 13 explicit APM(std::shared_ptr<Module> apm, const char* name);
14 ~APM() = default; 14 ~APM() override;
15 15
16private: 16private:
17 void OpenSession(Kernel::HLERequestContext& ctx); 17 void OpenSession(Kernel::HLERequestContext& ctx);
@@ -22,6 +22,7 @@ private:
22class APM_Sys final : public ServiceFramework<APM_Sys> { 22class APM_Sys final : public ServiceFramework<APM_Sys> {
23public: 23public:
24 explicit APM_Sys(); 24 explicit APM_Sys();
25 ~APM_Sys() override;
25 26
26private: 27private:
27 void GetPerformanceEvent(Kernel::HLERequestContext& ctx); 28 void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index 37c3fdcac..b6b71f966 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -42,4 +42,6 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
42 RegisterHandlers(functions); 42 RegisterHandlers(functions);
43} 43}
44 44
45AudCtl::~AudCtl() = default;
46
45} // namespace Service::Audio 47} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
index ed837bdf2..9d2d9e83b 100644
--- a/src/core/hle/service/audio/audctl.h
+++ b/src/core/hle/service/audio/audctl.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudCtl final : public ServiceFramework<AudCtl> { 11class AudCtl final : public ServiceFramework<AudCtl> {
12public: 12public:
13 explicit AudCtl(); 13 explicit AudCtl();
14 ~AudCtl() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp
index b08c21a20..8fff3e4b4 100644
--- a/src/core/hle/service/audio/auddbg.cpp
+++ b/src/core/hle/service/audio/auddbg.cpp
@@ -17,4 +17,6 @@ AudDbg::AudDbg(const char* name) : ServiceFramework{name} {
17 RegisterHandlers(functions); 17 RegisterHandlers(functions);
18} 18}
19 19
20AudDbg::~AudDbg() = default;
21
20} // namespace Service::Audio 22} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h
index a2f540b75..6689f4759 100644
--- a/src/core/hle/service/audio/auddbg.h
+++ b/src/core/hle/service/audio/auddbg.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudDbg final : public ServiceFramework<AudDbg> { 11class AudDbg final : public ServiceFramework<AudDbg> {
12public: 12public:
13 explicit AudDbg(const char* name); 13 explicit AudDbg(const char* name);
14 ~AudDbg() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
index a70d5bca4..ddd12f35e 100644
--- a/src/core/hle/service/audio/audin_a.cpp
+++ b/src/core/hle/service/audio/audin_a.cpp
@@ -19,4 +19,6 @@ AudInA::AudInA() : ServiceFramework{"audin:a"} {
19 RegisterHandlers(functions); 19 RegisterHandlers(functions);
20} 20}
21 21
22AudInA::~AudInA() = default;
23
22} // namespace Service::Audio 24} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h
index e4c75510f..e7623bc29 100644
--- a/src/core/hle/service/audio/audin_a.h
+++ b/src/core/hle/service/audio/audin_a.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudInA final : public ServiceFramework<AudInA> { 11class AudInA final : public ServiceFramework<AudInA> {
12public: 12public:
13 explicit AudInA(); 13 explicit AudInA();
14 ~AudInA() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index cbc49e55e..657010312 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -41,4 +41,6 @@ AudInU::AudInU() : ServiceFramework("audin:u") {
41 RegisterHandlers(functions); 41 RegisterHandlers(functions);
42} 42}
43 43
44AudInU::~AudInU() = default;
45
44} // namespace Service::Audio 46} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 2e65efb5b..0538b9560 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -15,7 +15,7 @@ namespace Service::Audio {
15class AudInU final : public ServiceFramework<AudInU> { 15class AudInU final : public ServiceFramework<AudInU> {
16public: 16public:
17 explicit AudInU(); 17 explicit AudInU();
18 ~AudInU() = default; 18 ~AudInU() override;
19}; 19};
20 20
21} // namespace Service::Audio 21} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index 6b5e15633..128df7db5 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/service/audio/audren_u.h" 15#include "core/hle/service/audio/audren_u.h"
16#include "core/hle/service/audio/codecctl.h" 16#include "core/hle/service/audio/codecctl.h"
17#include "core/hle/service/audio/hwopus.h" 17#include "core/hle/service/audio/hwopus.h"
18#include "core/hle/service/service.h"
18 19
19namespace Service::Audio { 20namespace Service::Audio {
20 21
diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h
index 95e5691f7..f5bd3bf5f 100644
--- a/src/core/hle/service/audio/audio.h
+++ b/src/core/hle/service/audio/audio.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/service.h" 7namespace Service::SM {
8class ServiceManager;
9}
8 10
9namespace Service::Audio { 11namespace Service::Audio {
10 12
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
index bf8d40157..85febbca3 100644
--- a/src/core/hle/service/audio/audout_a.cpp
+++ b/src/core/hle/service/audio/audout_a.cpp
@@ -21,4 +21,6 @@ AudOutA::AudOutA() : ServiceFramework{"audout:a"} {
21 RegisterHandlers(functions); 21 RegisterHandlers(functions);
22} 22}
23 23
24AudOutA::~AudOutA() = default;
25
24} // namespace Service::Audio 26} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h
index 91a069152..d65b66e8e 100644
--- a/src/core/hle/service/audio/audout_a.h
+++ b/src/core/hle/service/audio/audout_a.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudOutA final : public ServiceFramework<AudOutA> { 11class AudOutA final : public ServiceFramework<AudOutA> {
12public: 12public:
13 explicit AudOutA(); 13 explicit AudOutA();
14 ~AudOutA() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 5f370bbdf..ff1edefbb 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -3,15 +3,20 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <cstring>
6#include <vector> 7#include <vector>
7 8
9#include "audio_core/audio_out.h"
8#include "audio_core/codec.h" 10#include "audio_core/codec.h"
11#include "common/common_funcs.h"
9#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/swap.h"
10#include "core/core.h" 14#include "core/core.h"
11#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/hle_ipc.h" 17#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/service/audio/audout_u.h" 18#include "core/hle/service/audio/audout_u.h"
19#include "core/memory.h"
15 20
16namespace Service::Audio { 21namespace Service::Audio {
17 22
@@ -25,6 +30,18 @@ enum {
25constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; 30constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
26constexpr int DefaultSampleRate{48000}; 31constexpr int DefaultSampleRate{48000};
27 32
33struct AudoutParams {
34 s32_le sample_rate;
35 u16_le channel_count;
36 INSERT_PADDING_BYTES(2);
37};
38static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
39
40enum class AudioState : u32 {
41 Started,
42 Stopped,
43};
44
28class IAudioOut final : public ServiceFramework<IAudioOut> { 45class IAudioOut final : public ServiceFramework<IAudioOut> {
29public: 46public:
30 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) 47 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
@@ -173,7 +190,7 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
173 190
174 ctx.WriteBuffer(DefaultDevice); 191 ctx.WriteBuffer(DefaultDevice);
175 192
176 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 193 IPC::ResponseBuilder rb{ctx, 3};
177 194
178 rb.Push(RESULT_SUCCESS); 195 rb.Push(RESULT_SUCCESS);
179 rb.Push<u32>(1); // Amount of audio devices 196 rb.Push<u32>(1); // Amount of audio devices
@@ -218,4 +235,6 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") {
218 audio_core = std::make_unique<AudioCore::AudioOut>(); 235 audio_core = std::make_unique<AudioCore::AudioOut>();
219} 236}
220 237
238AudOutU::~AudOutU() = default;
239
221} // namespace Service::Audio 240} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index fd491f65d..dcaf64708 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,33 +4,24 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_out.h"
8#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
9 8
9namespace AudioCore {
10class AudioOut;
11}
12
10namespace Kernel { 13namespace Kernel {
11class HLERequestContext; 14class HLERequestContext;
12} 15}
13 16
14namespace Service::Audio { 17namespace Service::Audio {
15 18
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
28class IAudioOut; 19class IAudioOut;
29 20
30class AudOutU final : public ServiceFramework<AudOutU> { 21class AudOutU final : public ServiceFramework<AudOutU> {
31public: 22public:
32 AudOutU(); 23 AudOutU();
33 ~AudOutU() = default; 24 ~AudOutU() override;
34 25
35private: 26private:
36 std::shared_ptr<IAudioOut> audio_out_interface; 27 std::shared_ptr<IAudioOut> audio_out_interface;
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp
index 016eabf53..ce1bfb48d 100644
--- a/src/core/hle/service/audio/audrec_a.cpp
+++ b/src/core/hle/service/audio/audrec_a.cpp
@@ -17,4 +17,6 @@ AudRecA::AudRecA() : ServiceFramework{"audrec:a"} {
17 RegisterHandlers(functions); 17 RegisterHandlers(functions);
18} 18}
19 19
20AudRecA::~AudRecA() = default;
21
20} // namespace Service::Audio 22} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h
index 9685047f2..384d24c69 100644
--- a/src/core/hle/service/audio/audrec_a.h
+++ b/src/core/hle/service/audio/audrec_a.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudRecA final : public ServiceFramework<AudRecA> { 11class AudRecA final : public ServiceFramework<AudRecA> {
12public: 12public:
13 explicit AudRecA(); 13 explicit AudRecA();
14 ~AudRecA() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp
index 74909415c..34974afa9 100644
--- a/src/core/hle/service/audio/audrec_u.cpp
+++ b/src/core/hle/service/audio/audrec_u.cpp
@@ -36,4 +36,6 @@ AudRecU::AudRecU() : ServiceFramework("audrec:u") {
36 RegisterHandlers(functions); 36 RegisterHandlers(functions);
37} 37}
38 38
39AudRecU::~AudRecU() = default;
40
39} // namespace Service::Audio 41} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h
index 46daa33a4..ca3d638e8 100644
--- a/src/core/hle/service/audio/audrec_u.h
+++ b/src/core/hle/service/audio/audrec_u.h
@@ -15,7 +15,7 @@ namespace Service::Audio {
15class AudRecU final : public ServiceFramework<AudRecU> { 15class AudRecU final : public ServiceFramework<AudRecU> {
16public: 16public:
17 explicit AudRecU(); 17 explicit AudRecU();
18 ~AudRecU() = default; 18 ~AudRecU() override;
19}; 19};
20 20
21} // namespace Service::Audio 21} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
index 616ff3dc4..edb66d985 100644
--- a/src/core/hle/service/audio/audren_a.cpp
+++ b/src/core/hle/service/audio/audren_a.cpp
@@ -23,4 +23,6 @@ AudRenA::AudRenA() : ServiceFramework{"audren:a"} {
23 RegisterHandlers(functions); 23 RegisterHandlers(functions);
24} 24}
25 25
26AudRenA::~AudRenA() = default;
27
26} // namespace Service::Audio 28} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h
index 5ecf2e184..81fef0ffe 100644
--- a/src/core/hle/service/audio/audren_a.h
+++ b/src/core/hle/service/audio/audren_a.h
@@ -11,6 +11,7 @@ namespace Service::Audio {
11class AudRenA final : public ServiceFramework<AudRenA> { 11class AudRenA final : public ServiceFramework<AudRenA> {
12public: 12public:
13 explicit AudRenA(); 13 explicit AudRenA();
14 ~AudRenA() override;
14}; 15};
15 16
16} // namespace Service::Audio 17} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 016db7c82..06ac6372d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.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 <algorithm>
5#include <array> 6#include <array>
7#include <memory>
6 8
9#include "audio_core/audio_renderer.h"
7#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/common_funcs.h"
8#include "common/logging/log.h" 12#include "common/logging/log.h"
9#include "core/core_timing.h"
10#include "core/core_timing_util.h"
11#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/event.h" 14#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/hle_ipc.h" 15#include "core/hle/kernel/hle_ipc.h"
@@ -135,7 +137,7 @@ private:
135 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 137 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
136 ctx.WriteBuffer(audio_interface); 138 ctx.WriteBuffer(audio_interface);
137 139
138 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 140 IPC::ResponseBuilder rb{ctx, 3};
139 rb.Push(RESULT_SUCCESS); 141 rb.Push(RESULT_SUCCESS);
140 rb.Push<u32>(1); 142 rb.Push<u32>(1);
141 } 143 }
@@ -149,7 +151,7 @@ private:
149 auto file_buffer = ctx.ReadBuffer(); 151 auto file_buffer = ctx.ReadBuffer();
150 auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); 152 auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
151 153
152 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 154 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(RESULT_SUCCESS); 155 rb.Push(RESULT_SUCCESS);
154 } 156 }
155 157
@@ -160,7 +162,7 @@ private:
160 constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; 162 constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
161 ctx.WriteBuffer(audio_interface); 163 ctx.WriteBuffer(audio_interface);
162 164
163 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 165 IPC::ResponseBuilder rb{ctx, 3};
164 rb.Push(RESULT_SUCCESS); 166 rb.Push(RESULT_SUCCESS);
165 rb.Push<u32>(1); 167 rb.Push<u32>(1);
166 } 168 }
@@ -198,6 +200,8 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
198 RegisterHandlers(functions); 200 RegisterHandlers(functions);
199} 201}
200 202
203AudRenU::~AudRenU() = default;
204
201void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 205void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
202 IPC::RequestParser rp{ctx}; 206 IPC::RequestParser rp{ctx};
203 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 207 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 8600ac6e4..c6bc3a90a 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_renderer.h"
8#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
9 8
10namespace Kernel { 9namespace Kernel {
@@ -16,7 +15,7 @@ namespace Service::Audio {
16class AudRenU final : public ServiceFramework<AudRenU> { 15class AudRenU final : public ServiceFramework<AudRenU> {
17public: 16public:
18 explicit AudRenU(); 17 explicit AudRenU();
19 ~AudRenU() = default; 18 ~AudRenU() override;
20 19
21private: 20private:
22 void OpenAudioRenderer(Kernel::HLERequestContext& ctx); 21 void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp
index 212c8d448..c6864146d 100644
--- a/src/core/hle/service/audio/codecctl.cpp
+++ b/src/core/hle/service/audio/codecctl.cpp
@@ -28,4 +28,6 @@ CodecCtl::CodecCtl() : ServiceFramework("codecctl") {
28 RegisterHandlers(functions); 28 RegisterHandlers(functions);
29} 29}
30 30
31CodecCtl::~CodecCtl() = default;
32
31} // namespace Service::Audio 33} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h
index d9ac29b67..2fe75b6e2 100644
--- a/src/core/hle/service/audio/codecctl.h
+++ b/src/core/hle/service/audio/codecctl.h
@@ -15,7 +15,7 @@ namespace Service::Audio {
15class CodecCtl final : public ServiceFramework<CodecCtl> { 15class CodecCtl final : public ServiceFramework<CodecCtl> {
16public: 16public:
17 explicit CodecCtl(); 17 explicit CodecCtl();
18 ~CodecCtl() = default; 18 ~CodecCtl() override;
19}; 19};
20 20
21} // namespace Service::Audio 21} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 371cd4997..fc6067e59 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -3,7 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <memory>
7#include <vector>
8
6#include <opus.h> 9#include <opus.h>
10
11#include "common/common_funcs.h"
7#include "common/logging/log.h" 12#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
@@ -56,7 +61,7 @@ private:
56 61
57 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 62 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
58 std::vector<opus_int16>& output) { 63 std::vector<opus_int16>& output) {
59 size_t raw_output_sz = output.size() * sizeof(opus_int16); 64 std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
60 if (sizeof(OpusHeader) > input.size()) 65 if (sizeof(OpusHeader) > input.size())
61 return false; 66 return false;
62 OpusHeader hdr{}; 67 OpusHeader hdr{};
@@ -91,7 +96,7 @@ private:
91 u32 channel_count; 96 u32 channel_count;
92}; 97};
93 98
94static size_t WorkerBufferSize(u32 channel_count) { 99static std::size_t WorkerBufferSize(u32 channel_count) {
95 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 100 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
96 return opus_decoder_get_size(static_cast<int>(channel_count)); 101 return opus_decoder_get_size(static_cast<int>(channel_count));
97} 102}
@@ -124,7 +129,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
124 "Invalid sample rate"); 129 "Invalid sample rate");
125 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 130 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
126 131
127 size_t worker_sz = WorkerBufferSize(channel_count); 132 std::size_t worker_sz = WorkerBufferSize(channel_count);
128 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); 133 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
129 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 134 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
130 static_cast<OpusDecoder*>(operator new(worker_sz))}; 135 static_cast<OpusDecoder*>(operator new(worker_sz))};
@@ -151,4 +156,6 @@ HwOpus::HwOpus() : ServiceFramework("hwopus") {
151 RegisterHandlers(functions); 156 RegisterHandlers(functions);
152} 157}
153 158
159HwOpus::~HwOpus() = default;
160
154} // namespace Service::Audio 161} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index 5258d59f3..602ede8ba 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -11,7 +11,7 @@ namespace Service::Audio {
11class HwOpus final : public ServiceFramework<HwOpus> { 11class HwOpus final : public ServiceFramework<HwOpus> {
12public: 12public:
13 explicit HwOpus(); 13 explicit HwOpus();
14 ~HwOpus() = default; 14 ~HwOpus() override;
15 15
16private: 16private:
17 void OpenOpusDecoder(Kernel::HLERequestContext& ctx); 17 void OpenOpusDecoder(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 20ce692dc..179aa4949 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -13,4 +13,6 @@ BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
13 }; 13 };
14 RegisterHandlers(functions); 14 RegisterHandlers(functions);
15} 15}
16
17BCAT::~BCAT() = default;
16} // namespace Service::BCAT 18} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index 6632996a0..802bd689a 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -11,6 +11,7 @@ namespace Service::BCAT {
11class BCAT final : public Module::Interface { 11class BCAT final : public Module::Interface {
12public: 12public:
13 explicit BCAT(std::shared_ptr<Module> module, const char* name); 13 explicit BCAT(std::shared_ptr<Module> module, const char* name);
14 ~BCAT() override;
14}; 15};
15 16
16} // namespace Service::BCAT 17} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 35e024c3d..6e7b795fb 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -42,6 +42,8 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
42Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 42Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
43 : ServiceFramework(name), module(std::move(module)) {} 43 : ServiceFramework(name), module(std::move(module)) {}
44 44
45Module::Interface::~Interface() = default;
46
45void InstallInterfaces(SM::ServiceManager& service_manager) { 47void InstallInterfaces(SM::ServiceManager& service_manager) {
46 auto module = std::make_shared<Module>(); 48 auto module = std::make_shared<Module>();
47 std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); 49 std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h
index 62f6f5f9d..f0d63cab0 100644
--- a/src/core/hle/service/bcat/module.h
+++ b/src/core/hle/service/bcat/module.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void CreateBcatService(Kernel::HLERequestContext& ctx); 18 void CreateBcatService(Kernel::HLERequestContext& ctx);
18 19
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 299b9474f..b436ce4e6 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -13,6 +13,8 @@ namespace Service::Fatal {
13Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 13Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
14 : ServiceFramework(name), module(std::move(module)) {} 14 : ServiceFramework(name), module(std::move(module)) {}
15 15
16Module::Interface::~Interface() = default;
17
16void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { 18void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
17 IPC::RequestParser rp(ctx); 19 IPC::RequestParser rp(ctx);
18 u32 error_code = rp.Pop<u32>(); 20 u32 error_code = rp.Pop<u32>();
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index ca607e236..4d9a5be52 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); 18 void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
18 void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); 19 void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index a5254ac2f..9e5f872ff 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -9,4 +9,6 @@ namespace Service::Fatal {
9Fatal_P::Fatal_P(std::shared_ptr<Module> module) 9Fatal_P::Fatal_P(std::shared_ptr<Module> module)
10 : Module::Interface(std::move(module), "fatal:p") {} 10 : Module::Interface(std::move(module), "fatal:p") {}
11 11
12Fatal_P::~Fatal_P() = default;
13
12} // namespace Service::Fatal 14} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h
index bfd8c8b74..6e9c5979f 100644
--- a/src/core/hle/service/fatal/fatal_p.h
+++ b/src/core/hle/service/fatal/fatal_p.h
@@ -11,6 +11,7 @@ namespace Service::Fatal {
11class Fatal_P final : public Module::Interface { 11class Fatal_P final : public Module::Interface {
12public: 12public:
13 explicit Fatal_P(std::shared_ptr<Module> module); 13 explicit Fatal_P(std::shared_ptr<Module> module);
14 ~Fatal_P() override;
14}; 15};
15 16
16} // namespace Service::Fatal 17} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index f0631329e..befc307cf 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -15,4 +15,6 @@ Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(m
15 RegisterHandlers(functions); 15 RegisterHandlers(functions);
16} 16}
17 17
18Fatal_U::~Fatal_U() = default;
19
18} // namespace Service::Fatal 20} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h
index 9b1a9e97a..72cb6d076 100644
--- a/src/core/hle/service/fatal/fatal_u.h
+++ b/src/core/hle/service/fatal/fatal_u.h
@@ -11,6 +11,7 @@ namespace Service::Fatal {
11class Fatal_U final : public Module::Interface { 11class Fatal_U final : public Module::Interface {
12public: 12public:
13 explicit Fatal_U(std::shared_ptr<Module> module); 13 explicit Fatal_U(std::shared_ptr<Module> module);
14 ~Fatal_U() override;
14}; 15};
15 16
16} // namespace Service::Fatal 17} // namespace Service::Fatal
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 881c39e31..d349ee686 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -10,6 +10,7 @@
10#include "core/file_sys/bis_factory.h" 10#include "core/file_sys/bis_factory.h"
11#include "core/file_sys/errors.h" 11#include "core/file_sys/errors.h"
12#include "core/file_sys/mode.h" 12#include "core/file_sys/mode.h"
13#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
14#include "core/file_sys/savedata_factory.h" 15#include "core/file_sys/savedata_factory.h"
15#include "core/file_sys/sdmc_factory.h" 16#include "core/file_sys/sdmc_factory.h"
@@ -39,6 +40,8 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
39VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) 40VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
40 : backing(std::move(backing_)) {} 41 : backing(std::move(backing_)) {}
41 42
43VfsDirectoryServiceWrapper::~VfsDirectoryServiceWrapper() = default;
44
42std::string VfsDirectoryServiceWrapper::GetName() const { 45std::string VfsDirectoryServiceWrapper::GetName() const {
43 return backing->GetName(); 46 return backing->GetName();
44} 47}
@@ -60,17 +63,20 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
60 63
61ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { 64ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
62 std::string path(FileUtil::SanitizePath(path_)); 65 std::string path(FileUtil::SanitizePath(path_));
63 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
64 if (path.empty()) { 66 if (path.empty()) {
65 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... 67 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
66 return RESULT_SUCCESS; 68 return RESULT_SUCCESS;
67 } 69 }
68 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) 70
71 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
72 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
69 return FileSys::ERROR_PATH_NOT_FOUND; 73 return FileSys::ERROR_PATH_NOT_FOUND;
74 }
70 if (!dir->DeleteFile(FileUtil::GetFilename(path))) { 75 if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
71 // TODO(DarkLordZach): Find a better error code for this 76 // TODO(DarkLordZach): Find a better error code for this
72 return ResultCode(-1); 77 return ResultCode(-1);
73 } 78 }
79
74 return RESULT_SUCCESS; 80 return RESULT_SUCCESS;
75} 81}
76 82
@@ -191,7 +197,7 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
191 auto dir = GetDirectoryRelativeWrapped(backing, path); 197 auto dir = GetDirectoryRelativeWrapped(backing, path);
192 if (dir == nullptr) { 198 if (dir == nullptr) {
193 // TODO(DarkLordZach): Find a better error code for this 199 // TODO(DarkLordZach): Find a better error code for this
194 return ResultCode(-1); 200 return FileSys::ERROR_PATH_NOT_FOUND;
195 } 201 }
196 return MakeResult(dir); 202 return MakeResult(dir);
197} 203}
@@ -304,6 +310,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
304 return sdmc_factory->Open(); 310 return sdmc_factory->Open();
305} 311}
306 312
313std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
314 return std::make_shared<FileSys::RegisteredCacheUnion>(
315 std::vector<std::shared_ptr<FileSys::RegisteredCache>>{
316 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
317}
318
307std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { 319std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
308 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 320 LOG_TRACE(Service_FS, "Opening System NAND Contents");
309 321
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 9ba0e2eab..aab65a2b8 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -13,6 +13,7 @@
13namespace FileSys { 13namespace FileSys {
14class BISFactory; 14class BISFactory;
15class RegisteredCache; 15class RegisteredCache;
16class RegisteredCacheUnion;
16class RomFSFactory; 17class RomFSFactory;
17class SaveDataFactory; 18class SaveDataFactory;
18class SDMCFactory; 19class SDMCFactory;
@@ -45,6 +46,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
45 FileSys::SaveDataDescriptor save_struct); 46 FileSys::SaveDataDescriptor save_struct);
46ResultVal<FileSys::VirtualDir> OpenSDMC(); 47ResultVal<FileSys::VirtualDir> OpenSDMC();
47 48
49std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
50
48std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); 51std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
49std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
50std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
@@ -61,6 +64,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::Virtu
61class VfsDirectoryServiceWrapper { 64class VfsDirectoryServiceWrapper {
62public: 65public:
63 explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); 66 explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
67 ~VfsDirectoryServiceWrapper();
64 68
65 /** 69 /**
66 * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) 70 * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp
index 0ab9c2606..fb487d5bc 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.cpp
+++ b/src/core/hle/service/filesystem/fsp_ldr.cpp
@@ -19,4 +19,6 @@ FSP_LDR::FSP_LDR() : ServiceFramework{"fsp:ldr"} {
19 RegisterHandlers(functions); 19 RegisterHandlers(functions);
20} 20}
21 21
22FSP_LDR::~FSP_LDR() = default;
23
22} // namespace Service::FileSystem 24} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h
index fa8e11b4c..8210b7729 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.h
+++ b/src/core/hle/service/filesystem/fsp_ldr.h
@@ -11,6 +11,7 @@ namespace Service::FileSystem {
11class FSP_LDR final : public ServiceFramework<FSP_LDR> { 11class FSP_LDR final : public ServiceFramework<FSP_LDR> {
12public: 12public:
13 explicit FSP_LDR(); 13 explicit FSP_LDR();
14 ~FSP_LDR() override;
14}; 15};
15 16
16} // namespace Service::FileSystem 17} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp
index 32b0ae454..378201610 100644
--- a/src/core/hle/service/filesystem/fsp_pr.cpp
+++ b/src/core/hle/service/filesystem/fsp_pr.cpp
@@ -20,4 +20,6 @@ FSP_PR::FSP_PR() : ServiceFramework{"fsp:pr"} {
20 RegisterHandlers(functions); 20 RegisterHandlers(functions);
21} 21}
22 22
23FSP_PR::~FSP_PR() = default;
24
23} // namespace Service::FileSystem 25} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h
index 62edcd08a..556ae5ce9 100644
--- a/src/core/hle/service/filesystem/fsp_pr.h
+++ b/src/core/hle/service/filesystem/fsp_pr.h
@@ -11,6 +11,7 @@ namespace Service::FileSystem {
11class FSP_PR final : public ServiceFramework<FSP_PR> { 11class FSP_PR final : public ServiceFramework<FSP_PR> {
12public: 12public:
13 explicit FSP_PR(); 13 explicit FSP_PR();
14 ~FSP_PR() override;
14}; 15};
15 16
16} // namespace Service::FileSystem 17} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 5759299fe..cabaf5a55 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -26,6 +26,17 @@
26 26
27namespace Service::FileSystem { 27namespace Service::FileSystem {
28 28
29enum class FileSystemType : u8 {
30 Invalid0 = 0,
31 Invalid1 = 1,
32 Logo = 2,
33 ContentControl = 3,
34 ContentManual = 4,
35 ContentMeta = 5,
36 ContentData = 6,
37 ApplicationPackage = 7,
38};
39
29class IStorage final : public ServiceFramework<IStorage> { 40class IStorage final : public ServiceFramework<IStorage> {
30public: 41public:
31 explicit IStorage(FileSys::VirtualFile backend_) 42 explicit IStorage(FileSys::VirtualFile backend_)
@@ -420,7 +431,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
420 {0, nullptr, "MountContent"}, 431 {0, nullptr, "MountContent"},
421 {1, &FSP_SRV::Initialize, "Initialize"}, 432 {1, &FSP_SRV::Initialize, "Initialize"},
422 {2, nullptr, "OpenDataFileSystemByCurrentProcess"}, 433 {2, nullptr, "OpenDataFileSystemByCurrentProcess"},
423 {7, nullptr, "OpenFileSystemWithPatch"}, 434 {7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
424 {8, nullptr, "OpenFileSystemWithId"}, 435 {8, nullptr, "OpenFileSystemWithId"},
425 {9, nullptr, "OpenDataFileSystemByApplicationId"}, 436 {9, nullptr, "OpenDataFileSystemByApplicationId"},
426 {11, nullptr, "OpenBisFileSystem"}, 437 {11, nullptr, "OpenBisFileSystem"},
@@ -444,7 +455,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
444 {34, nullptr, "GetCacheStorageSize"}, 455 {34, nullptr, "GetCacheStorageSize"},
445 {51, &FSP_SRV::MountSaveData, "MountSaveData"}, 456 {51, &FSP_SRV::MountSaveData, "MountSaveData"},
446 {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"}, 457 {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
447 {53, nullptr, "OpenReadOnlySaveDataFileSystem"}, 458 {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
448 {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, 459 {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
449 {58, nullptr, "ReadSaveDataFileSystemExtraData"}, 460 {58, nullptr, "ReadSaveDataFileSystemExtraData"},
450 {59, nullptr, "WriteSaveDataFileSystemExtraData"}, 461 {59, nullptr, "WriteSaveDataFileSystemExtraData"},
@@ -509,6 +520,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
509 RegisterHandlers(functions); 520 RegisterHandlers(functions);
510} 521}
511 522
523FSP_SRV::~FSP_SRV() = default;
524
512void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { 525void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
513 LOG_WARNING(Service_FS, "(STUBBED) called"); 526 LOG_WARNING(Service_FS, "(STUBBED) called");
514 527
@@ -516,6 +529,16 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
516 rb.Push(RESULT_SUCCESS); 529 rb.Push(RESULT_SUCCESS);
517} 530}
518 531
532void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
533 IPC::RequestParser rp{ctx};
534
535 const auto type = rp.PopRaw<FileSystemType>();
536 const auto title_id = rp.PopRaw<u64>();
537
538 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
539 rb.Push(ResultCode(-1));
540}
541
519void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { 542void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
520 LOG_DEBUG(Service_FS, "called"); 543 LOG_DEBUG(Service_FS, "called");
521 544
@@ -563,6 +586,11 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
563 rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); 586 rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
564} 587}
565 588
589void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
590 LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
591 MountSaveData(ctx);
592}
593
566void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 594void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
567 LOG_WARNING(Service_FS, "(STUBBED) called"); 595 LOG_WARNING(Service_FS, "(STUBBED) called");
568 596
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index f073ac523..4aa0358cb 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -16,13 +16,15 @@ namespace Service::FileSystem {
16class FSP_SRV final : public ServiceFramework<FSP_SRV> { 16class FSP_SRV final : public ServiceFramework<FSP_SRV> {
17public: 17public:
18 explicit FSP_SRV(); 18 explicit FSP_SRV();
19 ~FSP_SRV() = default; 19 ~FSP_SRV() override;
20 20
21private: 21private:
22 void Initialize(Kernel::HLERequestContext& ctx); 22 void Initialize(Kernel::HLERequestContext& ctx);
23 void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
23 void MountSdCard(Kernel::HLERequestContext& ctx); 24 void MountSdCard(Kernel::HLERequestContext& ctx);
24 void CreateSaveData(Kernel::HLERequestContext& ctx); 25 void CreateSaveData(Kernel::HLERequestContext& ctx);
25 void MountSaveData(Kernel::HLERequestContext& ctx); 26 void MountSaveData(Kernel::HLERequestContext& ctx);
27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
26 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 28 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
27 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
28 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); 30 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index f2b0e509a..d9225d624 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -118,6 +118,8 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
118Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 118Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
119 : ServiceFramework(name), module(std::move(module)) {} 119 : ServiceFramework(name), module(std::move(module)) {}
120 120
121Module::Interface::~Interface() = default;
122
121void InstallInterfaces(SM::ServiceManager& service_manager) { 123void InstallInterfaces(SM::ServiceManager& service_manager) {
122 auto module = std::make_shared<Module>(); 124 auto module = std::make_shared<Module>();
123 std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); 125 std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index c1b36518a..e762840cb 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void CreateFriendService(Kernel::HLERequestContext& ctx); 18 void CreateFriendService(Kernel::HLERequestContext& ctx);
18 19
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 27c6a09e2..5a6840af5 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -16,4 +16,6 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name)
16 RegisterHandlers(functions); 16 RegisterHandlers(functions);
17} 17}
18 18
19Friend::~Friend() = default;
20
19} // namespace Service::Friend 21} // namespace Service::Friend
diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h
index 89dae8471..1963def39 100644
--- a/src/core/hle/service/friend/interface.h
+++ b/src/core/hle/service/friend/interface.h
@@ -11,6 +11,7 @@ namespace Service::Friend {
11class Friend final : public Module::Interface { 11class Friend final : public Module::Interface {
12public: 12public:
13 explicit Friend(std::shared_ptr<Module> module, const char* name); 13 explicit Friend(std::shared_ptr<Module> module, const char* name);
14 ~Friend() override;
14}; 15};
15 16
16} // namespace Service::Friend 17} // namespace Service::Friend
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 0d31abe8b..7c6b0a4e6 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6#include "common/logging/log.h" 5#include "common/logging/log.h"
7#include "core/core.h" 6#include "core/core.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
@@ -78,7 +77,7 @@ private:
78 SharedMemory mem{}; 77 SharedMemory mem{};
79 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory)); 78 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
80 79
81 if (is_device_reload_pending.exchange(false)) 80 if (Settings::values.is_device_reload_pending.exchange(false))
82 LoadInputDevices(); 81 LoadInputDevices();
83 82
84 // Set up controllers as neon red+blue Joy-Con attached to console 83 // Set up controllers as neon red+blue Joy-Con attached to console
@@ -90,7 +89,7 @@ private:
90 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; 89 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
91 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; 90 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
92 91
93 for (size_t controller = 0; controller < mem.controllers.size(); controller++) { 92 for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
94 for (auto& layout : mem.controllers[controller].layouts) { 93 for (auto& layout : mem.controllers[controller].layouts) {
95 layout.header.num_entries = HID_NUM_ENTRIES; 94 layout.header.num_entries = HID_NUM_ENTRIES;
96 layout.header.max_entry_index = HID_NUM_ENTRIES - 1; 95 layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
@@ -267,7 +266,6 @@ private:
267 CoreTiming::EventType* pad_update_event; 266 CoreTiming::EventType* pad_update_event;
268 267
269 // Stored input state info 268 // Stored input state info
270 std::atomic<bool> is_device_reload_pending{true};
271 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 269 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
272 buttons; 270 buttons;
273 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; 271 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
@@ -315,7 +313,7 @@ public:
315 {64, nullptr, "DeactivateJoySixAxisSensor"}, 313 {64, nullptr, "DeactivateJoySixAxisSensor"},
316 {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, 314 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
317 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, 315 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
318 {67, nullptr, "StopSixAxisSensor"}, 316 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
319 {68, nullptr, "IsSixAxisSensorFusionEnabled"}, 317 {68, nullptr, "IsSixAxisSensorFusionEnabled"},
320 {69, nullptr, "EnableSixAxisSensorFusion"}, 318 {69, nullptr, "EnableSixAxisSensorFusion"},
321 {70, nullptr, "SetSixAxisSensorFusionParameters"}, 319 {70, nullptr, "SetSixAxisSensorFusionParameters"},
@@ -331,7 +329,7 @@ public:
331 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 329 {80, nullptr, "GetGyroscopeZeroDriftMode"},
332 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 330 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
333 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 331 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
334 {91, nullptr, "ActivateGesture"}, 332 {91, &Hid::ActivateGesture, "ActivateGesture"},
335 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 333 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
336 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 334 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
337 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, 335 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
@@ -340,7 +338,7 @@ public:
340 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, 338 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
341 {107, &Hid::DisconnectNpad, "DisconnectNpad"}, 339 {107, &Hid::DisconnectNpad, "DisconnectNpad"},
342 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, 340 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
343 {109, nullptr, "ActivateNpadWithRevision"}, 341 {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
344 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, 342 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
345 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, 343 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
346 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, 344 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
@@ -366,8 +364,8 @@ public:
366 {208, nullptr, "GetActualVibrationGcErmCommand"}, 364 {208, nullptr, "GetActualVibrationGcErmCommand"},
367 {209, nullptr, "BeginPermitVibrationSession"}, 365 {209, nullptr, "BeginPermitVibrationSession"},
368 {210, nullptr, "EndPermitVibrationSession"}, 366 {210, nullptr, "EndPermitVibrationSession"},
369 {300, nullptr, "ActivateConsoleSixAxisSensor"}, 367 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
370 {301, nullptr, "StartConsoleSixAxisSensor"}, 368 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
371 {302, nullptr, "StopConsoleSixAxisSensor"}, 369 {302, nullptr, "StopConsoleSixAxisSensor"},
372 {303, nullptr, "ActivateSevenSixAxisSensor"}, 370 {303, nullptr, "ActivateSevenSixAxisSensor"},
373 {304, nullptr, "StartSevenSixAxisSensor"}, 371 {304, nullptr, "StartSevenSixAxisSensor"},
@@ -581,6 +579,36 @@ private:
581 rb.Push(RESULT_SUCCESS); 579 rb.Push(RESULT_SUCCESS);
582 LOG_WARNING(Service_HID, "(STUBBED) called"); 580 LOG_WARNING(Service_HID, "(STUBBED) called");
583 } 581 }
582
583 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
584 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(RESULT_SUCCESS);
586 LOG_WARNING(Service_HID, "(STUBBED) called");
587 }
588
589 void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
590 IPC::ResponseBuilder rb{ctx, 2};
591 rb.Push(RESULT_SUCCESS);
592 LOG_WARNING(Service_HID, "(STUBBED) called");
593 }
594
595 void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
596 IPC::ResponseBuilder rb{ctx, 2};
597 rb.Push(RESULT_SUCCESS);
598 LOG_WARNING(Service_HID, "(STUBBED) called");
599 }
600
601 void ActivateGesture(Kernel::HLERequestContext& ctx) {
602 IPC::ResponseBuilder rb{ctx, 2};
603 rb.Push(RESULT_SUCCESS);
604 LOG_WARNING(Service_HID, "(STUBBED) called");
605 }
606
607 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
608 IPC::ResponseBuilder rb{ctx, 2};
609 rb.Push(RESULT_SUCCESS);
610 LOG_WARNING(Service_HID, "(STUBBED) called");
611 }
584}; 612};
585 613
586class HidDbg final : public ServiceFramework<HidDbg> { 614class HidDbg final : public ServiceFramework<HidDbg> {
@@ -797,7 +825,9 @@ public:
797 } 825 }
798}; 826};
799 827
800void ReloadInputDevices() {} 828void ReloadInputDevices() {
829 Settings::values.is_device_reload_pending.store(true);
830}
801 831
802void InstallInterfaces(SM::ServiceManager& service_manager) { 832void InstallInterfaces(SM::ServiceManager& service_manager) {
803 std::make_shared<Hid>()->InstallAsService(service_manager); 833 std::make_shared<Hid>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index aaf311912..e587ad0d8 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -33,6 +33,8 @@ IRS::IRS() : ServiceFramework{"irs"} {
33 RegisterHandlers(functions); 33 RegisterHandlers(functions);
34} 34}
35 35
36IRS::~IRS() = default;
37
36IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} { 38IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} {
37 // clang-format off 39 // clang-format off
38 static const FunctionInfo functions[] = { 40 static const FunctionInfo functions[] = {
@@ -46,4 +48,6 @@ IRS_SYS::IRS_SYS() : ServiceFramework{"irs:sys"} {
46 RegisterHandlers(functions); 48 RegisterHandlers(functions);
47} 49}
48 50
51IRS_SYS::~IRS_SYS() = default;
52
49} // namespace Service::HID 53} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index a8be701c7..6fb16b45d 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -11,11 +11,13 @@ namespace Service::HID {
11class IRS final : public ServiceFramework<IRS> { 11class IRS final : public ServiceFramework<IRS> {
12public: 12public:
13 explicit IRS(); 13 explicit IRS();
14 ~IRS() override;
14}; 15};
15 16
16class IRS_SYS final : public ServiceFramework<IRS_SYS> { 17class IRS_SYS final : public ServiceFramework<IRS_SYS> {
17public: 18public:
18 explicit IRS_SYS(); 19 explicit IRS_SYS();
20 ~IRS_SYS() override;
19}; 21};
20 22
21} // namespace Service::HID 23} // namespace Service::HID
diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp
index 49f733f60..c8e9125f6 100644
--- a/src/core/hle/service/hid/xcd.cpp
+++ b/src/core/hle/service/hid/xcd.cpp
@@ -34,4 +34,6 @@ XCD_SYS::XCD_SYS() : ServiceFramework{"xcd:sys"} {
34 RegisterHandlers(functions); 34 RegisterHandlers(functions);
35} 35}
36 36
37XCD_SYS::~XCD_SYS() = default;
38
37} // namespace Service::HID 39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h
index 232a044df..fd506d303 100644
--- a/src/core/hle/service/hid/xcd.h
+++ b/src/core/hle/service/hid/xcd.h
@@ -11,6 +11,7 @@ namespace Service::HID {
11class XCD_SYS final : public ServiceFramework<XCD_SYS> { 11class XCD_SYS final : public ServiceFramework<XCD_SYS> {
12public: 12public:
13 explicit XCD_SYS(); 13 explicit XCD_SYS();
14 ~XCD_SYS() override;
14}; 15};
15 16
16} // namespace Service::HID 17} // namespace Service::HID
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 098da2a41..c89157a4d 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -99,7 +99,7 @@ private:
99 std::string thread; 99 std::string thread;
100 while (addr < end_addr) { 100 while (addr < end_addr) {
101 const Field field{static_cast<Field>(Memory::Read8(addr++))}; 101 const Field field{static_cast<Field>(Memory::Read8(addr++))};
102 const size_t length{Memory::Read8(addr++)}; 102 const std::size_t length{Memory::Read8(addr++)};
103 103
104 if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { 104 if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
105 ++addr; 105 ++addr;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 4f7543af5..f8d2127d9 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -14,6 +14,8 @@ namespace Service::NFP {
14Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 14Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
15 : ServiceFramework(name), module(std::move(module)) {} 15 : ServiceFramework(name), module(std::move(module)) {}
16 16
17Module::Interface::~Interface() = default;
18
17class IUser final : public ServiceFramework<IUser> { 19class IUser final : public ServiceFramework<IUser> {
18public: 20public:
19 IUser() : ServiceFramework("IUser") { 21 IUser() : ServiceFramework("IUser") {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 0cd7be3d5..77df343c4 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void CreateUserInterface(Kernel::HLERequestContext& ctx); 18 void CreateUserInterface(Kernel::HLERequestContext& ctx);
18 19
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index b608fe693..784a87c1b 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -14,4 +14,6 @@ NFP_User::NFP_User(std::shared_ptr<Module> module)
14 RegisterHandlers(functions); 14 RegisterHandlers(functions);
15} 15}
16 16
17NFP_User::~NFP_User() = default;
18
17} // namespace Service::NFP 19} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 700043114..65d9aaf48 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -11,6 +11,7 @@ namespace Service::NFP {
11class NFP_User final : public Module::Interface { 11class NFP_User final : public Module::Interface {
12public: 12public:
13 explicit NFP_User(std::shared_ptr<Module> module); 13 explicit NFP_User(std::shared_ptr<Module> module);
14 ~NFP_User() override;
14}; 15};
15 16
16} // namespace Service::NFP 17} // namespace Service::NFP
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index ed4f5f539..10611ed6a 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -31,7 +31,7 @@ public:
31 {1, &IRequest::GetResult, "GetResult"}, 31 {1, &IRequest::GetResult, "GetResult"},
32 {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, 32 {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
33 {3, &IRequest::Cancel, "Cancel"}, 33 {3, &IRequest::Cancel, "Cancel"},
34 {4, nullptr, "Submit"}, 34 {4, &IRequest::Submit, "Submit"},
35 {5, nullptr, "SetRequirement"}, 35 {5, nullptr, "SetRequirement"},
36 {6, nullptr, "SetRequirementPreset"}, 36 {6, nullptr, "SetRequirementPreset"},
37 {8, nullptr, "SetPriority"}, 37 {8, nullptr, "SetPriority"},
@@ -61,6 +61,12 @@ public:
61 } 61 }
62 62
63private: 63private:
64 void Submit(Kernel::HLERequestContext& ctx) {
65 LOG_WARNING(Service_NIFM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(RESULT_SUCCESS);
68 }
69
64 void GetRequestState(Kernel::HLERequestContext& ctx) { 70 void GetRequestState(Kernel::HLERequestContext& ctx) {
65 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 71 LOG_WARNING(Service_NIFM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 3}; 72 IPC::ResponseBuilder rb{ctx, 3};
@@ -114,10 +120,11 @@ public:
114 120
115private: 121private:
116 void GetClientId(Kernel::HLERequestContext& ctx) { 122 void GetClientId(Kernel::HLERequestContext& ctx) {
123 static constexpr u32 client_id = 1;
117 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 124 LOG_WARNING(Service_NIFM, "(STUBBED) called");
118 IPC::ResponseBuilder rb{ctx, 4}; 125 IPC::ResponseBuilder rb{ctx, 4};
119 rb.Push(RESULT_SUCCESS); 126 rb.Push(RESULT_SUCCESS);
120 rb.Push<u64>(0); 127 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
121 } 128 }
122 void CreateScanRequest(Kernel::HLERequestContext& ctx) { 129 void CreateScanRequest(Kernel::HLERequestContext& ctx) {
123 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 130 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -141,10 +148,16 @@ private:
141 rb.Push(RESULT_SUCCESS); 148 rb.Push(RESULT_SUCCESS);
142 } 149 }
143 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 150 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
144 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 151 ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size");
152 u128 uuid{};
153 auto buffer = ctx.ReadBuffer();
154 std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
155
156 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
145 157
146 rb.Push(RESULT_SUCCESS); 158 rb.Push(RESULT_SUCCESS);
147 rb.PushIpcInterface<INetworkProfile>(); 159 rb.PushIpcInterface<INetworkProfile>();
160 rb.PushRaw<u128>(uuid);
148 161
149 LOG_DEBUG(Service_NIFM, "called"); 162 LOG_DEBUG(Service_NIFM, "called");
150 } 163 }
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index bd05b0a70..c1737defa 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -2,6 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <ctime>
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h"
5#include "core/hle/service/nim/nim.h" 9#include "core/hle/service/nim/nim.h"
6#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h" 11#include "core/hle/service/sm/sm.h"
@@ -100,19 +104,111 @@ public:
100 } 104 }
101}; 105};
102 106
107class IEnsureNetworkClockAvailabilityService final
108 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
109public:
110 IEnsureNetworkClockAvailabilityService()
111 : ServiceFramework("IEnsureNetworkClockAvailabilityService") {
112 static const FunctionInfo functions[] = {
113 {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
114 {1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent,
115 "GetFinishNotificationEvent"},
116 {2, &IEnsureNetworkClockAvailabilityService::GetResult, "GetResult"},
117 {3, &IEnsureNetworkClockAvailabilityService::Cancel, "Cancel"},
118 {4, &IEnsureNetworkClockAvailabilityService::IsProcessing, "IsProcessing"},
119 {5, &IEnsureNetworkClockAvailabilityService::GetServerTime, "GetServerTime"},
120 };
121 RegisterHandlers(functions);
122
123 auto& kernel = Core::System::GetInstance().Kernel();
124 finished_event =
125 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
126 "IEnsureNetworkClockAvailabilityService:FinishEvent");
127 }
128
129private:
130 Kernel::SharedPtr<Kernel::Event> finished_event;
131
132 void StartTask(Kernel::HLERequestContext& ctx) {
133 // No need to connect to the internet, just finish the task straight away.
134 finished_event->Signal();
135 IPC::ResponseBuilder rb{ctx, 2};
136 rb.Push(RESULT_SUCCESS);
137 LOG_DEBUG(Service_NIM, "called");
138 }
139
140 void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(finished_event);
144 LOG_DEBUG(Service_NIM, "called");
145 }
146
147 void GetResult(Kernel::HLERequestContext& ctx) {
148 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(RESULT_SUCCESS);
150 LOG_DEBUG(Service_NIM, "called");
151 }
152
153 void Cancel(Kernel::HLERequestContext& ctx) {
154 finished_event->Clear();
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(RESULT_SUCCESS);
157 LOG_DEBUG(Service_NIM, "called");
158 }
159
160 void IsProcessing(Kernel::HLERequestContext& ctx) {
161 IPC::ResponseBuilder rb{ctx, 3};
162 rb.Push(RESULT_SUCCESS);
163 rb.PushRaw<u32>(0); // We instantly process the request
164 LOG_DEBUG(Service_NIM, "called");
165 }
166
167 void GetServerTime(Kernel::HLERequestContext& ctx) {
168 const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>(
169 std::chrono::system_clock::now().time_since_epoch())
170 .count()};
171 IPC::ResponseBuilder rb{ctx, 4};
172 rb.Push(RESULT_SUCCESS);
173 rb.PushRaw<s64>(server_time);
174 LOG_DEBUG(Service_NIM, "called");
175 }
176};
177
103class NTC final : public ServiceFramework<NTC> { 178class NTC final : public ServiceFramework<NTC> {
104public: 179public:
105 explicit NTC() : ServiceFramework{"ntc"} { 180 explicit NTC() : ServiceFramework{"ntc"} {
106 // clang-format off 181 // clang-format off
107 static const FunctionInfo functions[] = { 182 static const FunctionInfo functions[] = {
108 {0, nullptr, "OpenEnsureNetworkClockAvailabilityService"}, 183 {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
109 {100, nullptr, "SuspendAutonomicTimeCorrection"}, 184 {100, &NTC::SuspendAutonomicTimeCorrection, "SuspendAutonomicTimeCorrection"},
110 {101, nullptr, "ResumeAutonomicTimeCorrection"}, 185 {101, &NTC::ResumeAutonomicTimeCorrection, "ResumeAutonomicTimeCorrection"},
111 }; 186 };
112 // clang-format on 187 // clang-format on
113 188
114 RegisterHandlers(functions); 189 RegisterHandlers(functions);
115 } 190 }
191
192private:
193 void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) {
194 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
195 rb.Push(RESULT_SUCCESS);
196 rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
197 LOG_DEBUG(Service_NIM, "called");
198 }
199
200 // TODO(ogniK): Do we need these?
201 void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
202 IPC::ResponseBuilder rb{ctx, 2};
203 rb.Push(RESULT_SUCCESS);
204 LOG_WARNING(Service_NIM, "(STUBBED) called");
205 }
206
207 void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
208 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(RESULT_SUCCESS);
210 LOG_WARNING(Service_NIM, "(STUBBED) called");
211 }
116}; 212};
117 213
118void InstallInterfaces(SM::ServiceManager& sm) { 214void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 51638793d..1069d103f 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -2,12 +2,30 @@
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 <cstring>
7#include <vector>
8
9#include <FontChineseSimplified.h>
10#include <FontChineseTraditional.h>
11#include <FontExtendedChineseSimplified.h>
12#include <FontKorean.h>
13#include <FontNintendoExtended.h>
14#include <FontStandard.h>
15
16#include "common/assert.h"
5#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/common_types.h"
6#include "common/file_util.h" 19#include "common/file_util.h"
20#include "common/logging/log.h"
21#include "common/swap.h"
7#include "core/core.h" 22#include "core/core.h"
8#include "core/file_sys/bis_factory.h" 23#include "core/file_sys/content_archive.h"
24#include "core/file_sys/nca_metadata.h"
25#include "core/file_sys/registered_cache.h"
9#include "core/file_sys/romfs.h" 26#include "core/file_sys/romfs.h"
10#include "core/hle/ipc_helpers.h" 27#include "core/hle/ipc_helpers.h"
28#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/ns/pl_u.h" 30#include "core/hle/service/ns/pl_u.h"
13 31
@@ -26,49 +44,41 @@ struct FontRegion {
26 u32 size; 44 u32 size;
27}; 45};
28 46
29static constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ 47constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
30 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), 48 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
31 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), 49 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
32 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), 50 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
33 std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), 51 std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
34 std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), 52 std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
35 std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), 53 std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
36 std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf")}; 54 std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
55};
37 56
38static constexpr std::array<const char*, 7> SHARED_FONTS_TTF{"FontStandard.ttf", 57constexpr std::array<const char*, 7> SHARED_FONTS_TTF{
39 "FontChineseSimplified.ttf", 58 "FontStandard.ttf",
40 "FontExtendedChineseSimplified.ttf", 59 "FontChineseSimplified.ttf",
41 "FontChineseTraditional.ttf", 60 "FontExtendedChineseSimplified.ttf",
42 "FontKorean.ttf", 61 "FontChineseTraditional.ttf",
43 "FontNintendoExtended.ttf", 62 "FontKorean.ttf",
44 "FontNintendoExtended2.ttf"}; 63 "FontNintendoExtended.ttf",
64 "FontNintendoExtended2.ttf",
65};
45 66
46// The below data is specific to shared font data dumped from Switch on f/w 2.2 67// The below data is specific to shared font data dumped from Switch on f/w 2.2
47// Virtual address and offsets/sizes likely will vary by dump 68// Virtual address and offsets/sizes likely will vary by dump
48static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; 69constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
49static constexpr u32 EXPECTED_RESULT{ 70constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
50 0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be 71constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
51static constexpr u32 EXPECTED_MAGIC{ 72constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
52 0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be 73constexpr FontRegion EMPTY_REGION{0, 0};
53static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
54static constexpr FontRegion EMPTY_REGION{0, 0};
55std::vector<FontRegion>
56 SHARED_FONT_REGIONS{}; // Automatically populated based on shared_fonts dump or system archives
57
58const FontRegion& GetSharedFontRegion(size_t index) {
59 if (index >= SHARED_FONT_REGIONS.size() || SHARED_FONT_REGIONS.empty()) {
60 // No font fallback
61 return EMPTY_REGION;
62 }
63 return SHARED_FONT_REGIONS.at(index);
64}
65 74
66enum class LoadState : u32 { 75enum class LoadState : u32 {
67 Loading = 0, 76 Loading = 0,
68 Done = 1, 77 Done = 1,
69}; 78};
70 79
71void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, size_t& offset) { 80static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
81 std::size_t& offset) {
72 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, 82 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
73 "Shared fonts exceeds 17mb!"); 83 "Shared fonts exceeds 17mb!");
74 ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); 84 ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
@@ -85,7 +95,7 @@ void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, s
85} 95}
86 96
87static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, 97static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output,
88 size_t& offset) { 98 std::size_t& offset) {
89 ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); 99 ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
90 const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; 100 const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
91 std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header 101 std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header
@@ -95,28 +105,52 @@ static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& out
95 offset += input.size() + (sizeof(u32) * 2); 105 offset += input.size() + (sizeof(u32) * 2);
96} 106}
97 107
108// Helper function to make BuildSharedFontsRawRegions a bit nicer
98static u32 GetU32Swapped(const u8* data) { 109static u32 GetU32Swapped(const u8* data) {
99 u32 value; 110 u32 value;
100 std::memcpy(&value, data, sizeof(value)); 111 std::memcpy(&value, data, sizeof(value));
101 return Common::swap32(value); // Helper function to make BuildSharedFontsRawRegions a bit nicer 112 return Common::swap32(value);
102} 113}
103 114
104void BuildSharedFontsRawRegions(const std::vector<u8>& input) { 115struct PL_U::Impl {
105 unsigned cur_offset = 0; // As we can derive the xor key we can just populate the offsets based 116 const FontRegion& GetSharedFontRegion(std::size_t index) const {
106 // on the shared memory dump 117 if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
107 for (size_t i = 0; i < SHARED_FONTS.size(); i++) { 118 // No font fallback
108 // Out of shared fonts/Invalid font 119 return EMPTY_REGION;
109 if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) 120 }
110 break; 121 return shared_font_regions.at(index);
111 const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^
112 EXPECTED_MAGIC; // Derive key withing inverse xor
113 const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
114 SHARED_FONT_REGIONS.push_back(FontRegion{cur_offset + 8, SIZE});
115 cur_offset += SIZE + 8;
116 } 122 }
117}
118 123
119PL_U::PL_U() : ServiceFramework("pl:u") { 124 void BuildSharedFontsRawRegions(const std::vector<u8>& input) {
125 // As we can derive the xor key we can just populate the offsets
126 // based on the shared memory dump
127 unsigned cur_offset = 0;
128
129 for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) {
130 // Out of shared fonts/invalid font
131 if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) {
132 break;
133 }
134
135 // Derive key withing inverse xor
136 const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC;
137 const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
138 shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE});
139 cur_offset += SIZE + 8;
140 }
141 }
142
143 /// Handle to shared memory region designated for a shared font
144 Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
145
146 /// Backing memory for the shared font data
147 std::shared_ptr<std::vector<u8>> shared_font;
148
149 // Automatically populated based on shared_fonts dump or system archives.
150 std::vector<FontRegion> shared_font_regions;
151};
152
153PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
120 static const FunctionInfo functions[] = { 154 static const FunctionInfo functions[] = {
121 {0, &PL_U::RequestLoad, "RequestLoad"}, 155 {0, &PL_U::RequestLoad, "RequestLoad"},
122 {1, &PL_U::GetLoadState, "GetLoadState"}, 156 {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -128,11 +162,11 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
128 RegisterHandlers(functions); 162 RegisterHandlers(functions);
129 // Attempt to load shared font data from disk 163 // Attempt to load shared font data from disk
130 const auto nand = FileSystem::GetSystemNANDContents(); 164 const auto nand = FileSystem::GetSystemNANDContents();
131 size_t offset = 0; 165 std::size_t offset = 0;
132 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
133 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
134 FileSys::ContentRecordType::Data)) { 168 FileSys::ContentRecordType::Data)) {
135 shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); 169 impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE);
136 for (auto font : SHARED_FONTS) { 170 for (auto font : SHARED_FONTS) {
137 const auto nca = 171 const auto nca =
138 nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); 172 nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
@@ -168,12 +202,12 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
168 static_cast<u32>(offset + 8), 202 static_cast<u32>(offset + 8),
169 static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 203 static_cast<u32>((font_data_u32.size() * sizeof(u32)) -
170 8)}; // Font offset and size do not account for the header 204 8)}; // Font offset and size do not account for the header
171 DecryptSharedFont(font_data_u32, *shared_font, offset); 205 DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
172 SHARED_FONT_REGIONS.push_back(region); 206 impl->shared_font_regions.push_back(region);
173 } 207 }
174 208
175 } else { 209 } else {
176 shared_font = std::make_shared<std::vector<u8>>( 210 impl->shared_font = std::make_shared<std::vector<u8>>(
177 SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size 211 SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
178 212
179 const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); 213 const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir);
@@ -197,8 +231,8 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
197 static_cast<u32>(offset + 8), 231 static_cast<u32>(offset + 8),
198 static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account 232 static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account
199 // for the header 233 // for the header
200 EncryptSharedFont(ttf_bytes, *shared_font, offset); 234 EncryptSharedFont(ttf_bytes, *impl->shared_font, offset);
201 SHARED_FONT_REGIONS.push_back(region); 235 impl->shared_font_regions.push_back(region);
202 } else { 236 } else {
203 LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf); 237 LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf);
204 } 238 }
@@ -213,14 +247,35 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
213 if (file.IsOpen()) { 247 if (file.IsOpen()) {
214 // Read shared font data 248 // Read shared font data
215 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); 249 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
216 file.ReadBytes(shared_font->data(), shared_font->size()); 250 file.ReadBytes(impl->shared_font->data(), impl->shared_font->size());
217 BuildSharedFontsRawRegions(*shared_font); 251 impl->BuildSharedFontsRawRegions(*impl->shared_font);
218 } else { 252 } else {
219 LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); 253 LOG_WARNING(Service_NS,
254 "Shared Font file missing. Loading open source replacement from memory");
255
256 // clang-format off
257 const std::vector<std::vector<u8>> open_source_shared_fonts_ttf = {
258 {std::begin(FontChineseSimplified), std::end(FontChineseSimplified)},
259 {std::begin(FontChineseTraditional), std::end(FontChineseTraditional)},
260 {std::begin(FontExtendedChineseSimplified), std::end(FontExtendedChineseSimplified)},
261 {std::begin(FontKorean), std::end(FontKorean)},
262 {std::begin(FontNintendoExtended), std::end(FontNintendoExtended)},
263 {std::begin(FontStandard), std::end(FontStandard)},
264 };
265 // clang-format on
266
267 for (const std::vector<u8>& font_ttf : open_source_shared_fonts_ttf) {
268 const FontRegion region{static_cast<u32>(offset + 8),
269 static_cast<u32>(font_ttf.size())};
270 EncryptSharedFont(font_ttf, *impl->shared_font, offset);
271 impl->shared_font_regions.push_back(region);
272 }
220 } 273 }
221 } 274 }
222} 275}
223 276
277PL_U::~PL_U() = default;
278
224void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { 279void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
225 IPC::RequestParser rp{ctx}; 280 IPC::RequestParser rp{ctx};
226 const u32 shared_font_type{rp.Pop<u32>()}; 281 const u32 shared_font_type{rp.Pop<u32>()};
@@ -247,7 +302,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
247 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 302 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
248 IPC::ResponseBuilder rb{ctx, 3}; 303 IPC::ResponseBuilder rb{ctx, 3};
249 rb.Push(RESULT_SUCCESS); 304 rb.Push(RESULT_SUCCESS);
250 rb.Push<u32>(GetSharedFontRegion(font_id).size); 305 rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
251} 306}
252 307
253void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { 308void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
@@ -257,17 +312,18 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
257 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 312 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
258 IPC::ResponseBuilder rb{ctx, 3}; 313 IPC::ResponseBuilder rb{ctx, 3};
259 rb.Push(RESULT_SUCCESS); 314 rb.Push(RESULT_SUCCESS);
260 rb.Push<u32>(GetSharedFontRegion(font_id).offset); 315 rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
261} 316}
262 317
263void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { 318void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
264 // Map backing memory for the font data 319 // Map backing memory for the font data
265 Core::CurrentProcess()->vm_manager.MapMemoryBlock( 320 Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
266 SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); 321 SHARED_FONT_MEM_SIZE,
322 Kernel::MemoryState::Shared);
267 323
268 // Create shared font memory object 324 // Create shared font memory object
269 auto& kernel = Core::System::GetInstance().Kernel(); 325 auto& kernel = Core::System::GetInstance().Kernel();
270 shared_font_mem = Kernel::SharedMemory::Create( 326 impl->shared_font_mem = Kernel::SharedMemory::Create(
271 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, 327 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
272 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, 328 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
273 "PL_U:shared_font_mem"); 329 "PL_U:shared_font_mem");
@@ -275,7 +331,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
275 LOG_DEBUG(Service_NS, "called"); 331 LOG_DEBUG(Service_NS, "called");
276 IPC::ResponseBuilder rb{ctx, 2, 1}; 332 IPC::ResponseBuilder rb{ctx, 2, 1};
277 rb.Push(RESULT_SUCCESS); 333 rb.Push(RESULT_SUCCESS);
278 rb.PushCopyObjects(shared_font_mem); 334 rb.PushCopyObjects(impl->shared_font_mem);
279} 335}
280 336
281void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { 337void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
@@ -288,9 +344,9 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
288 std::vector<u32> font_sizes; 344 std::vector<u32> font_sizes;
289 345
290 // TODO(ogniK): Have actual priority order 346 // TODO(ogniK): Have actual priority order
291 for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) { 347 for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) {
292 font_codes.push_back(static_cast<u32>(i)); 348 font_codes.push_back(static_cast<u32>(i));
293 auto region = GetSharedFontRegion(i); 349 auto region = impl->GetSharedFontRegion(i);
294 font_offsets.push_back(region.offset); 350 font_offsets.push_back(region.offset);
295 font_sizes.push_back(region.size); 351 font_sizes.push_back(region.size);
296 } 352 }
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index fcc2acab7..253f26a2a 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
10 9
11namespace Service::NS { 10namespace Service::NS {
@@ -13,7 +12,7 @@ namespace Service::NS {
13class PL_U final : public ServiceFramework<PL_U> { 12class PL_U final : public ServiceFramework<PL_U> {
14public: 13public:
15 PL_U(); 14 PL_U();
16 ~PL_U() = default; 15 ~PL_U() override;
17 16
18private: 17private:
19 void RequestLoad(Kernel::HLERequestContext& ctx); 18 void RequestLoad(Kernel::HLERequestContext& ctx);
@@ -23,11 +22,8 @@ private:
23 void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); 22 void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
24 void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx); 23 void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx);
25 24
26 /// Handle to shared memory region designated for a shared font 25 struct Impl;
27 Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; 26 std::unique_ptr<Impl> impl;
28
29 /// Backing memory for the shared font data
30 std::shared_ptr<std::vector<u8>> shared_font;
31}; 27};
32 28
33} // namespace Service::NS 29} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 0b37098e1..92acc57b1 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -13,6 +13,9 @@
13 13
14namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
15 15
16nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
17nvdisp_disp0 ::~nvdisp_disp0() = default;
18
16u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 19u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
17 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 20 UNIMPLEMENTED_MSG("Unimplemented ioctl");
18 return 0; 21 return 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 6f0697b58..a45086e45 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -17,8 +17,8 @@ class nvmap;
17 17
18class nvdisp_disp0 final : public nvdevice { 18class nvdisp_disp0 final : public nvdevice {
19public: 19public:
20 explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 20 explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() = default; 21 ~nvdisp_disp0();
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
24 24
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 75487c4e8..d8b8037a8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <utility>
7
6#include "common/assert.h" 8#include "common/assert.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
@@ -14,6 +16,9 @@
14 16
15namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
16 18
19nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
20nvhost_as_gpu::~nvhost_as_gpu() = default;
21
17u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 22u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 23 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 24 command.raw, input.size(), output.size());
@@ -66,7 +71,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
66} 71}
67 72
68u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 73u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
69 size_t num_entries = input.size() / sizeof(IoctlRemapEntry); 74 std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
70 75
71 LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); 76 LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
72 77
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 9f8999d9c..eb14b1da8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <unordered_map> 8#include <unordered_map>
9#include <utility>
10#include <vector> 9#include <vector>
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "common/swap.h" 11#include "common/swap.h"
@@ -18,8 +17,8 @@ class nvmap;
18 17
19class nvhost_as_gpu final : public nvdevice { 18class nvhost_as_gpu final : public nvdevice {
20public: 19public:
21 explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 20 explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev);
22 ~nvhost_as_gpu() override = default; 21 ~nvhost_as_gpu() override;
23 22
24 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
25 24
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 5685eb2be..b39fb9ef9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -11,6 +11,9 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14nvhost_ctrl::nvhost_ctrl() = default;
15nvhost_ctrl::~nvhost_ctrl() = default;
16
14u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 17u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
15 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
16 command.raw, input.size(), output.size()); 19 command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 6b496e9fe..6d0de2212 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13 13
14class nvhost_ctrl final : public nvdevice { 14class nvhost_ctrl final : public nvdevice {
15public: 15public:
16 nvhost_ctrl() = default; 16 nvhost_ctrl();
17 ~nvhost_ctrl() override = default; 17 ~nvhost_ctrl() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 20
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 ae421247d..7a88ae029 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -9,6 +9,9 @@
9 9
10namespace Service::Nvidia::Devices { 10namespace Service::Nvidia::Devices {
11 11
12nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default;
13nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
14
12u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 15u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
13 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 16 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
14 command.raw, input.size(), output.size()); 17 command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index f09113e67..3bbf028ad 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13 13
14class nvhost_ctrl_gpu final : public nvdevice { 14class nvhost_ctrl_gpu final : public nvdevice {
15public: 15public:
16 nvhost_ctrl_gpu() = default; 16 nvhost_ctrl_gpu();
17 ~nvhost_ctrl_gpu() override = default; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 20
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 4cdf7f613..874d5e1c3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -8,11 +8,15 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/memory.h" 10#include "core/memory.h"
11#include "video_core/command_processor.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
17nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
18nvhost_gpu::~nvhost_gpu() = default;
19
16u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
18 command.raw, input.size(), output.size()); 22 command.raw, input.size(), output.size());
@@ -134,17 +138,16 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
134 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 138 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
135 params.address, params.num_entries, params.flags); 139 params.address, params.num_entries, params.flags);
136 140
137 ASSERT_MSG(input.size() == 141 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
138 sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(IoctlGpfifoEntry), 142 params.num_entries * sizeof(Tegra::CommandListHeader),
139 "Incorrect input size"); 143 "Incorrect input size");
140 144
141 std::vector<IoctlGpfifoEntry> entries(params.num_entries); 145 std::vector<Tegra::CommandListHeader> entries(params.num_entries);
142 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], 146 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
143 params.num_entries * sizeof(IoctlGpfifoEntry)); 147 params.num_entries * sizeof(Tegra::CommandListHeader));
144 for (auto entry : entries) { 148
145 Tegra::GPUVAddr va_addr = entry.Address(); 149 Core::System::GetInstance().GPU().ProcessCommandLists(entries);
146 Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); 150
147 }
148 params.fence_out.id = 0; 151 params.fence_out.id = 0;
149 params.fence_out.value = 0; 152 params.fence_out.value = 0;
150 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 153 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
@@ -160,14 +163,12 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
160 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 163 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
161 params.address, params.num_entries, params.flags); 164 params.address, params.num_entries, params.flags);
162 165
163 std::vector<IoctlGpfifoEntry> entries(params.num_entries); 166 std::vector<Tegra::CommandListHeader> entries(params.num_entries);
164 Memory::ReadBlock(params.address, entries.data(), 167 Memory::ReadBlock(params.address, entries.data(),
165 params.num_entries * sizeof(IoctlGpfifoEntry)); 168 params.num_entries * sizeof(Tegra::CommandListHeader));
169
170 Core::System::GetInstance().GPU().ProcessCommandLists(entries);
166 171
167 for (auto entry : entries) {
168 Tegra::GPUVAddr va_addr = entry.Address();
169 Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
170 }
171 params.fence_out.id = 0; 172 params.fence_out.id = 0;
172 params.fence_out.value = 0; 173 params.fence_out.value = 0;
173 std::memcpy(output.data(), &params, output.size()); 174 std::memcpy(output.data(), &params, output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 03b7356d0..62beb5c0c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -10,7 +10,6 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
13#include "video_core/memory_manager.h"
14 13
15namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
16 15
@@ -21,8 +20,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
21 20
22class nvhost_gpu final : public nvdevice { 21class nvhost_gpu final : public nvdevice {
23public: 22public:
24 explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 23 explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev);
25 ~nvhost_gpu() override = default; 24 ~nvhost_gpu() override;
26 25
27 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 26 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
28 27
@@ -151,22 +150,6 @@ private:
151 }; 150 };
152 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); 151 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
153 152
154 struct IoctlGpfifoEntry {
155 u32_le entry0; // gpu_va_lo
156 union {
157 u32_le entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F)
158 BitField<0, 8, u32_le> gpu_va_hi;
159 BitField<8, 2, u32_le> unk1;
160 BitField<10, 21, u32_le> sz;
161 BitField<31, 1, u32_le> unk2;
162 };
163
164 Tegra::GPUVAddr Address() const {
165 return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0;
166 }
167 };
168 static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size");
169
170 struct IoctlSubmitGpfifo { 153 struct IoctlSubmitGpfifo {
171 u64_le address; // pointer to gpfifo entry structs 154 u64_le address; // pointer to gpfifo entry structs
172 u32_le num_entries; // number of fence objects being submitted 155 u32_le num_entries; // number of fence objects being submitted
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 364619e67..46dbbc37c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -10,6 +10,9 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_nvdec::nvhost_nvdec() = default;
14nvhost_nvdec::~nvhost_nvdec() = default;
15
13u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
14 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
15 command.raw, input.size(), output.size()); 18 command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 6ad74421b..0e7b284f8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13 13
14class nvhost_nvdec final : public nvdevice { 14class nvhost_nvdec final : public nvdevice {
15public: 15public:
16 nvhost_nvdec() = default; 16 nvhost_nvdec();
17 ~nvhost_nvdec() override = default; 17 ~nvhost_nvdec() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 20
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 51f01077b..c67f934f6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -10,6 +10,9 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_nvjpg::nvhost_nvjpg() = default;
14nvhost_nvjpg::~nvhost_nvjpg() = default;
15
13u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
14 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
15 command.raw, input.size(), output.size()); 18 command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 2b0eb43ee..89fd5e95e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13 13
14class nvhost_nvjpg final : public nvdevice { 14class nvhost_nvjpg final : public nvdevice {
15public: 15public:
16 nvhost_nvjpg() = default; 16 nvhost_nvjpg();
17 ~nvhost_nvjpg() override = default; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 20
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index fcb488d50..727b9fee4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,6 +10,9 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_vic::nvhost_vic() = default;
14nvhost_vic::~nvhost_vic() = default;
15
13u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
14 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
15 command.raw, input.size(), output.size()); 18 command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index c7d681e52..fc24c3f9c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13 13
14class nvhost_vic final : public nvdevice { 14class nvhost_vic final : public nvdevice {
15public: 15public:
16 nvhost_vic() = default; 16 nvhost_vic();
17 ~nvhost_vic() override = default; 17 ~nvhost_vic() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 20
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index e9305bfb3..a2287cc1b 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,9 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14nvmap::nvmap() = default;
15nvmap::~nvmap() = default;
16
14VAddr nvmap::GetObjectAddress(u32 handle) const { 17VAddr nvmap::GetObjectAddress(u32 handle) const {
15 auto object = GetObject(handle); 18 auto object = GetObject(handle);
16 ASSERT(object); 19 ASSERT(object);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index f2eec6409..396230c19 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -16,8 +16,8 @@ namespace Service::Nvidia::Devices {
16 16
17class nvmap final : public nvdevice { 17class nvmap final : public nvdevice {
18public: 18public:
19 nvmap() = default; 19 nvmap();
20 ~nvmap() override = default; 20 ~nvmap() override;
21 21
22 /// Returns the allocated address of an nvmap object given its handle. 22 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 23 VAddr GetObjectAddress(u32 handle) const;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 634ab9196..ac3859353 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -112,4 +112,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
112 query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event"); 112 query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event");
113} 113}
114 114
115NVDRV::~NVDRV() = default;
116
115} // namespace Service::Nvidia 117} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 1c3529bb6..d340893c2 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -14,7 +14,7 @@ namespace Service::Nvidia {
14class NVDRV final : public ServiceFramework<NVDRV> { 14class NVDRV final : public ServiceFramework<NVDRV> {
15public: 15public:
16 NVDRV(std::shared_ptr<Module> nvdrv, const char* name); 16 NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
17 ~NVDRV() = default; 17 ~NVDRV();
18 18
19private: 19private:
20 void Open(Kernel::HLERequestContext& ctx); 20 void Open(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 2de39822f..6e4b8f2c6 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -45,6 +45,8 @@ Module::Module() {
45 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); 45 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>();
46} 46}
47 47
48Module::~Module() = default;
49
48u32 Module::Open(const std::string& device_name) { 50u32 Module::Open(const std::string& device_name) {
49 ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", 51 ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}",
50 device_name); 52 device_name);
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 99eb1128a..53564f696 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -30,7 +30,7 @@ static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
30class Module final { 30class Module final {
31public: 31public:
32 Module(); 32 Module();
33 ~Module() = default; 33 ~Module();
34 34
35 /// Returns a pointer to one of the available devices, identified by its name. 35 /// Returns a pointer to one of the available devices, identified by its name.
36 template <typename T> 36 template <typename T>
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index 0e8e21bad..b7b8b7a1b 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -16,6 +16,8 @@ NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
16 RegisterHandlers(functions); 16 RegisterHandlers(functions);
17} 17}
18 18
19NVMEMP::~NVMEMP() = default;
20
19void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) { 21void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
20 UNIMPLEMENTED(); 22 UNIMPLEMENTED();
21} 23}
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h
index dfdcabf4a..5a4dfc1f9 100644
--- a/src/core/hle/service/nvdrv/nvmemp.h
+++ b/src/core/hle/service/nvdrv/nvmemp.h
@@ -11,7 +11,7 @@ namespace Service::Nvidia {
11class NVMEMP final : public ServiceFramework<NVMEMP> { 11class NVMEMP final : public ServiceFramework<NVMEMP> {
12public: 12public:
13 NVMEMP(); 13 NVMEMP();
14 ~NVMEMP() = default; 14 ~NVMEMP();
15 15
16private: 16private:
17 void Cmd0(Kernel::HLERequestContext& ctx); 17 void Cmd0(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 8d8962276..fd98d541d 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,8 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 10#include "core/hle/service/nvflinger/buffer_queue.h"
11 11
12namespace Service { 12namespace Service::NVFlinger {
13namespace NVFlinger {
14 13
15BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { 14BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
16 auto& kernel = Core::System::GetInstance().Kernel(); 15 auto& kernel = Core::System::GetInstance().Kernel();
@@ -18,6 +17,8 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
18 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); 17 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
19} 18}
20 19
20BufferQueue::~BufferQueue() = default;
21
21void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 22void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
22 Buffer buffer{}; 23 Buffer buffer{};
23 buffer.slot = slot; 24 buffer.slot = slot;
@@ -102,5 +103,4 @@ u32 BufferQueue::Query(QueryType type) {
102 return 0; 103 return 0;
103} 104}
104 105
105} // namespace NVFlinger 106} // namespace Service::NVFlinger
106} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index db2e17c0c..50b767732 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -15,8 +15,7 @@ namespace CoreTiming {
15struct EventType; 15struct EventType;
16} 16}
17 17
18namespace Service { 18namespace Service::NVFlinger {
19namespace NVFlinger {
20 19
21struct IGBPBuffer { 20struct IGBPBuffer {
22 u32_le magic; 21 u32_le magic;
@@ -46,7 +45,7 @@ public:
46 }; 45 };
47 46
48 BufferQueue(u32 id, u64 layer_id); 47 BufferQueue(u32 id, u64 layer_id);
49 ~BufferQueue() = default; 48 ~BufferQueue();
50 49
51 enum class BufferTransformFlags : u32 { 50 enum class BufferTransformFlags : u32 {
52 /// No transform flags are set 51 /// No transform flags are set
@@ -98,5 +97,4 @@ private:
98 Kernel::SharedPtr<Kernel::Event> buffer_wait_event; 97 Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
99}; 98};
100 99
101} // namespace NVFlinger 100} // namespace Service::NVFlinger
102} // namespace Service
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 06040da6f..d47b6f659 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -23,7 +23,7 @@
23 23
24namespace Service::NVFlinger { 24namespace Service::NVFlinger {
25 25
26constexpr size_t SCREEN_REFRESH_RATE = 60; 26constexpr std::size_t SCREEN_REFRESH_RATE = 60;
27constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 27constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
28 28
29NVFlinger::NVFlinger() { 29NVFlinger::NVFlinger() {
@@ -160,10 +160,13 @@ void NVFlinger::Compose() {
160} 160}
161 161
162Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} 162Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
163Layer::~Layer() = default;
163 164
164Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { 165Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
165 auto& kernel = Core::System::GetInstance().Kernel(); 166 auto& kernel = Core::System::GetInstance().Kernel();
166 vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event"); 167 vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event");
167} 168}
168 169
170Display::~Display() = default;
171
169} // namespace Service::NVFlinger 172} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index f7112949f..3dc69e69b 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -26,7 +26,7 @@ class BufferQueue;
26 26
27struct Layer { 27struct Layer {
28 Layer(u64 id, std::shared_ptr<BufferQueue> queue); 28 Layer(u64 id, std::shared_ptr<BufferQueue> queue);
29 ~Layer() = default; 29 ~Layer();
30 30
31 u64 id; 31 u64 id;
32 std::shared_ptr<BufferQueue> buffer_queue; 32 std::shared_ptr<BufferQueue> buffer_queue;
@@ -34,7 +34,7 @@ struct Layer {
34 34
35struct Display { 35struct Display {
36 Display(u64 id, std::string name); 36 Display(u64 id, std::string name);
37 ~Display() = default; 37 ~Display();
38 38
39 u64 id; 39 u64 id;
40 std::string name; 40 std::string name;
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 6cc3b1992..4fd185f69 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -142,6 +142,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
142Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 142Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
143 : ServiceFramework(name), module(std::move(module)) {} 143 : ServiceFramework(name), module(std::move(module)) {}
144 144
145Module::Interface::~Interface() = default;
146
145void InstallInterfaces(SM::ServiceManager& service_manager) { 147void InstallInterfaces(SM::ServiceManager& service_manager) {
146 auto module = std::make_shared<Module>(); 148 auto module = std::make_shared<Module>();
147 std::make_shared<PCTL>(module, "pctl")->InstallAsService(service_manager); 149 std::make_shared<PCTL>(module, "pctl")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/module.h
index e7d492760..3e449110d 100644
--- a/src/core/hle/service/pctl/module.h
+++ b/src/core/hle/service/pctl/module.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void CreateService(Kernel::HLERequestContext& ctx); 18 void CreateService(Kernel::HLERequestContext& ctx);
18 void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx); 19 void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp
index de2741d66..af9d1433a 100644
--- a/src/core/hle/service/pctl/pctl.cpp
+++ b/src/core/hle/service/pctl/pctl.cpp
@@ -14,4 +14,6 @@ PCTL::PCTL(std::shared_ptr<Module> module, const char* name)
14 }; 14 };
15 RegisterHandlers(functions); 15 RegisterHandlers(functions);
16} 16}
17
18PCTL::~PCTL() = default;
17} // namespace Service::PCTL 19} // namespace Service::PCTL
diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h
index 8ddf69128..c33ea80b6 100644
--- a/src/core/hle/service/pctl/pctl.h
+++ b/src/core/hle/service/pctl/pctl.h
@@ -11,6 +11,7 @@ namespace Service::PCTL {
11class PCTL final : public Module::Interface { 11class PCTL final : public Module::Interface {
12public: 12public:
13 explicit PCTL(std::shared_ptr<Module> module, const char* name); 13 explicit PCTL(std::shared_ptr<Module> module, const char* name);
14 ~PCTL() override;
14}; 15};
15 16
16} // namespace Service::PCTL 17} // namespace Service::PCTL
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 3c43b8d8c..6a9eccfb5 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -1,36 +1,47 @@
1#include <cinttypes> 1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
2#include "common/logging/log.h" 5#include "common/logging/log.h"
3#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
4#include "core/hle/kernel/event.h"
5#include "core/hle/service/prepo/prepo.h" 7#include "core/hle/service/prepo/prepo.h"
8#include "core/hle/service/service.h"
6 9
7namespace Service::PlayReport { 10namespace Service::PlayReport {
8PlayReport::PlayReport(const char* name) : ServiceFramework(name) {
9 static const FunctionInfo functions[] = {
10 {10100, nullptr, "SaveReport"},
11 {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"},
12 {10200, nullptr, "RequestImmediateTransmission"},
13 {10300, nullptr, "GetTransmissionStatus"},
14 {20100, nullptr, "SaveSystemReport"},
15 {20200, nullptr, "SetOperationMode"},
16 {20101, nullptr, "SaveSystemReportWithUser"},
17 {30100, nullptr, "ClearStorage"},
18 {40100, nullptr, "IsUserAgreementCheckEnabled"},
19 {40101, nullptr, "SetUserAgreementCheckEnabled"},
20 {90100, nullptr, "GetStorageUsage"},
21 {90200, nullptr, "GetStatistics"},
22 {90201, nullptr, "GetThroughputHistory"},
23 {90300, nullptr, "GetLastUploadError"},
24 };
25 RegisterHandlers(functions);
26};
27 11
28void PlayReport::SaveReportWithUser(Kernel::HLERequestContext& ctx) { 12class PlayReport final : public ServiceFramework<PlayReport> {
29 // TODO(ogniK): Do we want to add play report? 13public:
30 LOG_WARNING(Service_PREPO, "(STUBBED) called"); 14 explicit PlayReport(const char* name) : ServiceFramework{name} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {10100, nullptr, "SaveReport"},
18 {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"},
19 {10200, nullptr, "RequestImmediateTransmission"},
20 {10300, nullptr, "GetTransmissionStatus"},
21 {20100, nullptr, "SaveSystemReport"},
22 {20200, nullptr, "SetOperationMode"},
23 {20101, nullptr, "SaveSystemReportWithUser"},
24 {30100, nullptr, "ClearStorage"},
25 {40100, nullptr, "IsUserAgreementCheckEnabled"},
26 {40101, nullptr, "SetUserAgreementCheckEnabled"},
27 {90100, nullptr, "GetStorageUsage"},
28 {90200, nullptr, "GetStatistics"},
29 {90201, nullptr, "GetThroughputHistory"},
30 {90300, nullptr, "GetLastUploadError"},
31 };
32 // clang-format on
33
34 RegisterHandlers(functions);
35 }
36
37private:
38 void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
39 // TODO(ogniK): Do we want to add play report?
40 LOG_WARNING(Service_PREPO, "(STUBBED) called");
31 41
32 IPC::ResponseBuilder rb{ctx, 2}; 42 IPC::ResponseBuilder rb{ctx, 2};
33 rb.Push(RESULT_SUCCESS); 43 rb.Push(RESULT_SUCCESS);
44 }
34}; 45};
35 46
36void InstallInterfaces(SM::ServiceManager& service_manager) { 47void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h
index f5a6aba6d..0e7b01331 100644
--- a/src/core/hle/service/prepo/prepo.h
+++ b/src/core/hle/service/prepo/prepo.h
@@ -4,22 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7namespace Service::SM {
8#include <string> 8class ServiceManager;
9#include "core/hle/kernel/event.h" 9}
10#include "core/hle/service/service.h"
11 10
12namespace Service::PlayReport { 11namespace Service::PlayReport {
13 12
14class PlayReport final : public ServiceFramework<PlayReport> {
15public:
16 explicit PlayReport(const char* name);
17 ~PlayReport() = default;
18
19private:
20 void SaveReportWithUser(Kernel::HLERequestContext& ctx);
21};
22
23void InstallInterfaces(SM::ServiceManager& service_manager); 13void InstallInterfaces(SM::ServiceManager& service_manager);
24 14
25} // namespace Service::PlayReport 15} // namespace Service::PlayReport
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 8fb907072..62f049660 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
14#include "core/hle/kernel/handle_table.h" 14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/server_port.h" 17#include "core/hle/kernel/server_port.h"
17#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
@@ -49,6 +50,7 @@
49#include "core/hle/service/nim/nim.h" 50#include "core/hle/service/nim/nim.h"
50#include "core/hle/service/ns/ns.h" 51#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h" 52#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/nvflinger.h"
52#include "core/hle/service/pcie/pcie.h" 54#include "core/hle/service/pcie/pcie.h"
53#include "core/hle/service/pctl/pctl.h" 55#include "core/hle/service/pctl/pctl.h"
54#include "core/hle/service/pcv/pcv.h" 56#include "core/hle/service/pcv/pcv.h"
@@ -57,7 +59,6 @@
57#include "core/hle/service/psc/psc.h" 59#include "core/hle/service/psc/psc.h"
58#include "core/hle/service/service.h" 60#include "core/hle/service/service.h"
59#include "core/hle/service/set/settings.h" 61#include "core/hle/service/set/settings.h"
60#include "core/hle/service/sm/controller.h"
61#include "core/hle/service/sm/sm.h" 62#include "core/hle/service/sm/sm.h"
62#include "core/hle/service/sockets/sockets.h" 63#include "core/hle/service/sockets/sockets.h"
63#include "core/hle/service/spl/module.h" 64#include "core/hle/service/spl/module.h"
@@ -73,8 +74,6 @@ using Kernel::SharedPtr;
73 74
74namespace Service { 75namespace Service {
75 76
76std::unordered_map<std::string, SharedPtr<ClientPort>> g_kernel_named_ports;
77
78/** 77/**
79 * Creates a function string for logging, complete with the name (or header code, depending 78 * Creates a function string for logging, complete with the name (or header code, depending
80 * on what's passed in) the port name, and all the cmd_buff arguments. 79 * on what's passed in) the port name, and all the cmd_buff arguments.
@@ -114,7 +113,7 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
114 std::tie(server_port, client_port) = 113 std::tie(server_port, client_port) =
115 ServerPort::CreatePortPair(kernel, max_sessions, service_name); 114 ServerPort::CreatePortPair(kernel, max_sessions, service_name);
116 server_port->SetHleHandler(shared_from_this()); 115 server_port->SetHleHandler(shared_from_this());
117 AddNamedPort(service_name, std::move(client_port)); 116 kernel.AddNamedPort(service_name, std::move(client_port));
118} 117}
119 118
120Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { 119Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
@@ -130,9 +129,9 @@ Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
130 return client_port; 129 return client_port;
131} 130}
132 131
133void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { 132void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
134 handlers.reserve(handlers.size() + n); 133 handlers.reserve(handlers.size() + n);
135 for (size_t i = 0; i < n; ++i) { 134 for (std::size_t i = 0; i < n; ++i) {
136 // Usually this array is sorted by id already, so hint to insert at the end 135 // Usually this array is sorted by id already, so hint to insert at the end
137 handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); 136 handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
138 } 137 }
@@ -197,11 +196,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
197//////////////////////////////////////////////////////////////////////////////////////////////////// 196////////////////////////////////////////////////////////////////////////////////////////////////////
198// Module interface 197// Module interface
199 198
200// TODO(yuriks): Move to kernel
201void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
202 g_kernel_named_ports.emplace(std::move(name), std::move(port));
203}
204
205/// Initialize ServiceManager 199/// Initialize ServiceManager
206void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { 200void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
207 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
@@ -264,7 +258,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
264 258
265/// Shutdown ServiceManager 259/// Shutdown ServiceManager
266void Shutdown() { 260void Shutdown() {
267 g_kernel_named_ports.clear();
268 LOG_DEBUG(Service, "shutdown OK"); 261 LOG_DEBUG(Service, "shutdown OK");
269} 262}
270} // namespace Service 263} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index cd9c74f3d..2fc57a82e 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -6,7 +6,6 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <string> 8#include <string>
9#include <unordered_map>
10#include <boost/container/flat_map.hpp> 9#include <boost/container/flat_map.hpp>
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "core/hle/kernel/hle_ipc.h" 11#include "core/hle/kernel/hle_ipc.h"
@@ -89,7 +88,7 @@ private:
89 ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); 88 ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker);
90 ~ServiceFrameworkBase(); 89 ~ServiceFrameworkBase();
91 90
92 void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); 91 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
93 void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); 92 void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
94 93
95 /// Identifier string used to connect to the service. 94 /// Identifier string used to connect to the service.
@@ -153,7 +152,7 @@ protected:
153 : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} 152 : ServiceFrameworkBase(service_name, max_sessions, Invoker) {}
154 153
155 /// Registers handlers in the service. 154 /// Registers handlers in the service.
156 template <size_t N> 155 template <std::size_t N>
157 void RegisterHandlers(const FunctionInfo (&functions)[N]) { 156 void RegisterHandlers(const FunctionInfo (&functions)[N]) {
158 RegisterHandlers(functions, N); 157 RegisterHandlers(functions, N);
159 } 158 }
@@ -162,7 +161,7 @@ protected:
162 * Registers handlers in the service. Usually prefer using the other RegisterHandlers 161 * Registers handlers in the service. Usually prefer using the other RegisterHandlers
163 * overload in order to avoid needing to specify the array size. 162 * overload in order to avoid needing to specify the array size.
164 */ 163 */
165 void RegisterHandlers(const FunctionInfo* functions, size_t n) { 164 void RegisterHandlers(const FunctionInfo* functions, std::size_t n) {
166 RegisterHandlersBase(functions, n); 165 RegisterHandlersBase(functions, n);
167 } 166 }
168 167
@@ -187,10 +186,4 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm,
187/// Shutdown ServiceManager 186/// Shutdown ServiceManager
188void Shutdown(); 187void Shutdown();
189 188
190/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
191extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports;
192
193/// Adds a port to the named port table
194void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port);
195
196} // namespace Service 189} // namespace Service
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 92b0640e8..9e5af7839 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -32,21 +32,21 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
32 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
33}}; 33}};
34 34
35constexpr size_t pre4_0_0_max_entries = 0xF; 35constexpr std::size_t pre4_0_0_max_entries = 0xF;
36constexpr size_t post4_0_0_max_entries = 0x40; 36constexpr std::size_t post4_0_0_max_entries = 0x40;
37 37
38LanguageCode GetLanguageCodeFromIndex(size_t index) { 38LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
39 return available_language_codes.at(index); 39 return available_language_codes.at(index);
40} 40}
41 41
42template <size_t size> 42template <std::size_t size>
43static std::array<LanguageCode, size> MakeLanguageCodeSubset() { 43static std::array<LanguageCode, size> MakeLanguageCodeSubset() {
44 std::array<LanguageCode, size> arr; 44 std::array<LanguageCode, size> arr;
45 std::copy_n(available_language_codes.begin(), size, arr.begin()); 45 std::copy_n(available_language_codes.begin(), size, arr.begin());
46 return arr; 46 return arr;
47} 47}
48 48
49static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, size_t max_size) { 49static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) {
50 IPC::ResponseBuilder rb{ctx, 3}; 50 IPC::ResponseBuilder rb{ctx, 3};
51 rb.Push(RESULT_SUCCESS); 51 rb.Push(RESULT_SUCCESS);
52 if (available_language_codes.size() > max_size) 52 if (available_language_codes.size() > max_size)
@@ -112,4 +112,6 @@ SET::SET() : ServiceFramework("set") {
112 RegisterHandlers(functions); 112 RegisterHandlers(functions);
113} 113}
114 114
115SET::~SET() = default;
116
115} // namespace Service::Set 117} // namespace Service::Set
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 669e740b7..266f13e46 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -28,12 +28,12 @@ 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); 31LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
32 32
33class SET final : public ServiceFramework<SET> { 33class SET final : public ServiceFramework<SET> {
34public: 34public:
35 explicit SET(); 35 explicit SET();
36 ~SET() = default; 36 ~SET() override;
37 37
38private: 38private:
39 void GetLanguageCode(Kernel::HLERequestContext& ctx); 39 void GetLanguageCode(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 7066ef725..5af356d10 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -44,4 +44,6 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
44 RegisterHandlers(functions); 44 RegisterHandlers(functions);
45} 45}
46 46
47SET_CAL::~SET_CAL() = default;
48
47} // namespace Service::Set 49} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h
index bb50336aa..583036eac 100644
--- a/src/core/hle/service/set/set_cal.h
+++ b/src/core/hle/service/set/set_cal.h
@@ -11,7 +11,7 @@ namespace Service::Set {
11class SET_CAL final : public ServiceFramework<SET_CAL> { 11class SET_CAL final : public ServiceFramework<SET_CAL> {
12public: 12public:
13 explicit SET_CAL(); 13 explicit SET_CAL();
14 ~SET_CAL() = default; 14 ~SET_CAL();
15}; 15};
16 16
17} // namespace Service::Set 17} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp
index c9f938716..cac6af86d 100644
--- a/src/core/hle/service/set/set_fd.cpp
+++ b/src/core/hle/service/set/set_fd.cpp
@@ -20,4 +20,6 @@ SET_FD::SET_FD() : ServiceFramework("set:fd") {
20 RegisterHandlers(functions); 20 RegisterHandlers(functions);
21} 21}
22 22
23SET_FD::~SET_FD() = default;
24
23} // namespace Service::Set 25} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h
index dbd850bc7..216e65f1f 100644
--- a/src/core/hle/service/set/set_fd.h
+++ b/src/core/hle/service/set/set_fd.h
@@ -11,7 +11,7 @@ namespace Service::Set {
11class SET_FD final : public ServiceFramework<SET_FD> { 11class SET_FD final : public ServiceFramework<SET_FD> {
12public: 12public:
13 explicit SET_FD(); 13 explicit SET_FD();
14 ~SET_FD() = default; 14 ~SET_FD() override;
15}; 15};
16 16
17} // namespace Service::Set 17} // namespace Service::Set
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 1cef73216..cdf328a26 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -57,4 +57,6 @@ Controller::Controller() : ServiceFramework("IpcController") {
57 RegisterHandlers(functions); 57 RegisterHandlers(functions);
58} 58}
59 59
60Controller::~Controller() = default;
61
60} // namespace Service::SM 62} // namespace Service::SM
diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h
index a4de52cd2..dc66c9e37 100644
--- a/src/core/hle/service/sm/controller.h
+++ b/src/core/hle/service/sm/controller.h
@@ -11,7 +11,7 @@ namespace Service::SM {
11class Controller final : public ServiceFramework<Controller> { 11class Controller final : public ServiceFramework<Controller> {
12public: 12public:
13 Controller(); 13 Controller();
14 ~Controller() = default; 14 ~Controller() override;
15 15
16private: 16private:
17 void ConvertSessionToDomain(Kernel::HLERequestContext& ctx); 17 void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index b240d7eed..464e79d01 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -15,6 +15,11 @@
15 15
16namespace Service::SM { 16namespace Service::SM {
17 17
18constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
21
22ServiceManager::ServiceManager() = default;
18ServiceManager::~ServiceManager() = default; 23ServiceManager::~ServiceManager() = default;
19 24
20void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 25void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
@@ -23,10 +28,10 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
23 28
24static ResultCode ValidateServiceName(const std::string& name) { 29static ResultCode ValidateServiceName(const std::string& name) {
25 if (name.size() <= 0 || name.size() > 8) { 30 if (name.size() <= 0 || name.size() > 8) {
26 return ERR_INVALID_NAME_SIZE; 31 return ERR_INVALID_NAME;
27 } 32 }
28 if (name.find('\0') != std::string::npos) { 33 if (name.find('\0') != std::string::npos) {
29 return ERR_NAME_CONTAINS_NUL; 34 return ERR_INVALID_NAME;
30 } 35 }
31 return RESULT_SUCCESS; 36 return RESULT_SUCCESS;
32} 37}
@@ -103,7 +108,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
103 108
104 auto client_port = service_manager->GetServicePort(name); 109 auto client_port = service_manager->GetServicePort(name);
105 if (client_port.Failed()) { 110 if (client_port.Failed()) {
106 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 111 IPC::ResponseBuilder rb{ctx, 2};
107 rb.Push(client_port.Code()); 112 rb.Push(client_port.Code());
108 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); 113 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw);
109 if (name.length() == 0) 114 if (name.length() == 0)
@@ -116,8 +121,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
116 ASSERT(session.Succeeded()); 121 ASSERT(session.Succeeded());
117 if (session.Succeeded()) { 122 if (session.Succeeded()) {
118 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); 123 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId());
119 IPC::ResponseBuilder rb = 124 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
120 rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles);
121 rb.Push(session.Code()); 125 rb.Push(session.Code());
122 rb.PushMoveObjects(std::move(session).Unwrap()); 126 rb.PushMoveObjects(std::move(session).Unwrap());
123 } 127 }
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index e8ea62f08..da2c51082 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -36,16 +36,11 @@ private:
36 std::shared_ptr<ServiceManager> service_manager; 36 std::shared_ptr<ServiceManager> service_manager;
37}; 37};
38 38
39constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1);
40constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
41constexpr ResultCode ERR_INVALID_NAME_SIZE(-1);
42constexpr ResultCode ERR_NAME_CONTAINS_NUL(-1);
43constexpr ResultCode ERR_ALREADY_REGISTERED(-1);
44
45class ServiceManager { 39class ServiceManager {
46public: 40public:
47 static void InstallInterfaces(std::shared_ptr<ServiceManager> self); 41 static void InstallInterfaces(std::shared_ptr<ServiceManager> self);
48 42
43 ServiceManager();
49 ~ServiceManager(); 44 ~ServiceManager();
50 45
51 ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, 46 ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 3211a8346..4342f3b2d 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -109,6 +109,8 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
109 RegisterHandlers(functions); 109 RegisterHandlers(functions);
110} 110}
111 111
112BSD::~BSD() = default;
113
112BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} { 114BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} {
113 // clang-format off 115 // clang-format off
114 static const FunctionInfo functions[] = { 116 static const FunctionInfo functions[] = {
@@ -131,4 +133,6 @@ BSDCFG::BSDCFG() : ServiceFramework{"bsdcfg"} {
131 RegisterHandlers(functions); 133 RegisterHandlers(functions);
132} 134}
133 135
136BSDCFG::~BSDCFG() = default;
137
134} // namespace Service::Sockets 138} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index c1da59b24..0fe0e65c6 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -12,7 +12,7 @@ namespace Service::Sockets {
12class BSD final : public ServiceFramework<BSD> { 12class BSD final : public ServiceFramework<BSD> {
13public: 13public:
14 explicit BSD(const char* name); 14 explicit BSD(const char* name);
15 ~BSD() = default; 15 ~BSD() override;
16 16
17private: 17private:
18 void RegisterClient(Kernel::HLERequestContext& ctx); 18 void RegisterClient(Kernel::HLERequestContext& ctx);
@@ -29,6 +29,7 @@ private:
29class BSDCFG final : public ServiceFramework<BSDCFG> { 29class BSDCFG final : public ServiceFramework<BSDCFG> {
30public: 30public:
31 explicit BSDCFG(); 31 explicit BSDCFG();
32 ~BSDCFG() override;
32}; 33};
33 34
34} // namespace Service::Sockets 35} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp
index d53c25eec..abbeb4c50 100644
--- a/src/core/hle/service/sockets/ethc.cpp
+++ b/src/core/hle/service/sockets/ethc.cpp
@@ -21,6 +21,8 @@ ETHC_C::ETHC_C() : ServiceFramework{"ethc:c"} {
21 RegisterHandlers(functions); 21 RegisterHandlers(functions);
22} 22}
23 23
24ETHC_C::~ETHC_C() = default;
25
24ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} { 26ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} {
25 // clang-format off 27 // clang-format off
26 static const FunctionInfo functions[] = { 28 static const FunctionInfo functions[] = {
@@ -35,4 +37,6 @@ ETHC_I::ETHC_I() : ServiceFramework{"ethc:i"} {
35 RegisterHandlers(functions); 37 RegisterHandlers(functions);
36} 38}
37 39
40ETHC_I::~ETHC_I() = default;
41
38} // namespace Service::Sockets 42} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h
index 9a3c88100..da2c7f741 100644
--- a/src/core/hle/service/sockets/ethc.h
+++ b/src/core/hle/service/sockets/ethc.h
@@ -11,11 +11,13 @@ namespace Service::Sockets {
11class ETHC_C final : public ServiceFramework<ETHC_C> { 11class ETHC_C final : public ServiceFramework<ETHC_C> {
12public: 12public:
13 explicit ETHC_C(); 13 explicit ETHC_C();
14 ~ETHC_C() override;
14}; 15};
15 16
16class ETHC_I final : public ServiceFramework<ETHC_I> { 17class ETHC_I final : public ServiceFramework<ETHC_I> {
17public: 18public:
18 explicit ETHC_I(); 19 explicit ETHC_I();
20 ~ETHC_I() override;
19}; 21};
20 22
21} // namespace Service::Sockets 23} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index 8682dc2e0..e6d73065e 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -29,4 +29,6 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
29 RegisterHandlers(functions); 29 RegisterHandlers(functions);
30} 30}
31 31
32NSD::~NSD() = default;
33
32} // namespace Service::Sockets 34} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h
index 3b7edfc43..d842e3232 100644
--- a/src/core/hle/service/sockets/nsd.h
+++ b/src/core/hle/service/sockets/nsd.h
@@ -12,7 +12,7 @@ namespace Service::Sockets {
12class NSD final : public ServiceFramework<NSD> { 12class NSD final : public ServiceFramework<NSD> {
13public: 13public:
14 explicit NSD(const char* name); 14 explicit NSD(const char* name);
15 ~NSD() = default; 15 ~NSD() override;
16}; 16};
17 17
18} // namespace Service::Sockets 18} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index d235c4cfd..13ab1d31e 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -34,4 +34,6 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
34 RegisterHandlers(functions); 34 RegisterHandlers(functions);
35} 35}
36 36
37SFDNSRES::~SFDNSRES() = default;
38
37} // namespace Service::Sockets 39} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index 62c7e35bf..eda432903 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -12,7 +12,7 @@ namespace Service::Sockets {
12class SFDNSRES final : public ServiceFramework<SFDNSRES> { 12class SFDNSRES final : public ServiceFramework<SFDNSRES> {
13public: 13public:
14 explicit SFDNSRES(); 14 explicit SFDNSRES();
15 ~SFDNSRES() = default; 15 ~SFDNSRES() override;
16 16
17private: 17private:
18 void GetAddrInfo(Kernel::HLERequestContext& ctx); 18 void GetAddrInfo(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp
index b9e6b799d..674928798 100644
--- a/src/core/hle/service/spl/csrng.cpp
+++ b/src/core/hle/service/spl/csrng.cpp
@@ -13,4 +13,6 @@ CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(modul
13 RegisterHandlers(functions); 13 RegisterHandlers(functions);
14} 14}
15 15
16CSRNG::~CSRNG() = default;
17
16} // namespace Service::SPL 18} // namespace Service::SPL
diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h
index 3f849b5a7..764d5ceb0 100644
--- a/src/core/hle/service/spl/csrng.h
+++ b/src/core/hle/service/spl/csrng.h
@@ -11,6 +11,7 @@ namespace Service::SPL {
11class CSRNG final : public Module::Interface { 11class CSRNG final : public Module::Interface {
12public: 12public:
13 explicit CSRNG(std::shared_ptr<Module> module); 13 explicit CSRNG(std::shared_ptr<Module> module);
14 ~CSRNG() override;
14}; 15};
15 16
16} // namespace Service::SPL 17} // namespace Service::SPL
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 3f5a342a7..44a6717d0 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -16,10 +16,12 @@ namespace Service::SPL {
16Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 16Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
17 : ServiceFramework(name), module(std::move(module)) {} 17 : ServiceFramework(name), module(std::move(module)) {}
18 18
19Module::Interface::~Interface() = default;
20
19void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { 21void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
20 IPC::RequestParser rp{ctx}; 22 IPC::RequestParser rp{ctx};
21 23
22 size_t size = ctx.GetWriteBufferSize(); 24 std::size_t size = ctx.GetWriteBufferSize();
23 25
24 std::vector<u8> data(size); 26 std::vector<u8> data(size);
25 std::generate(data.begin(), data.end(), std::rand); 27 std::generate(data.begin(), data.end(), std::rand);
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index f24d998e8..48fda6099 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -13,6 +13,7 @@ public:
13 class Interface : public ServiceFramework<Interface> { 13 class Interface : public ServiceFramework<Interface> {
14 public: 14 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override;
16 17
17 void GetRandomBytes(Kernel::HLERequestContext& ctx); 18 void GetRandomBytes(Kernel::HLERequestContext& ctx);
18 19
diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp
index bb1e03342..70cb41905 100644
--- a/src/core/hle/service/spl/spl.cpp
+++ b/src/core/hle/service/spl/spl.cpp
@@ -42,4 +42,6 @@ SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module),
42 RegisterHandlers(functions); 42 RegisterHandlers(functions);
43} 43}
44 44
45SPL::~SPL() = default;
46
45} // namespace Service::SPL 47} // namespace Service::SPL
diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h
index 69c4c1747..3637d1623 100644
--- a/src/core/hle/service/spl/spl.h
+++ b/src/core/hle/service/spl/spl.h
@@ -11,6 +11,7 @@ namespace Service::SPL {
11class SPL final : public Module::Interface { 11class SPL final : public Module::Interface {
12public: 12public:
13 explicit SPL(std::shared_ptr<Module> module); 13 explicit SPL(std::shared_ptr<Module> module);
14 ~SPL() override;
14}; 15};
15 16
16} // namespace Service::SPL 17} // namespace Service::SPL
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 40aea6090..fe0a318ee 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -3,6 +3,9 @@
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" 5#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h"
7#include "core/hle/service/service.h"
8#include "core/hle/service/sm/sm.h"
6#include "core/hle/service/ssl/ssl.h" 9#include "core/hle/service/ssl/ssl.h"
7 10
8namespace Service::SSL { 11namespace Service::SSL {
@@ -68,7 +71,7 @@ private:
68 LOG_WARNING(Service_SSL, "(STUBBED) called"); 71 LOG_WARNING(Service_SSL, "(STUBBED) called");
69 IPC::RequestParser rp{ctx}; 72 IPC::RequestParser rp{ctx};
70 73
71 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 74 IPC::ResponseBuilder rb{ctx, 2};
72 rb.Push(RESULT_SUCCESS); 75 rb.Push(RESULT_SUCCESS);
73 } 76 }
74 77
@@ -81,36 +84,43 @@ private:
81 } 84 }
82}; 85};
83 86
84void SSL::CreateContext(Kernel::HLERequestContext& ctx) { 87class SSL final : public ServiceFramework<SSL> {
85 LOG_WARNING(Service_SSL, "(STUBBED) called"); 88public:
89 explicit SSL() : ServiceFramework{"ssl"} {
90 // clang-format off
91 static const FunctionInfo functions[] = {
92 {0, &SSL::CreateContext, "CreateContext"},
93 {1, nullptr, "GetContextCount"},
94 {2, nullptr, "GetCertificates"},
95 {3, nullptr, "GetCertificateBufSize"},
96 {4, nullptr, "DebugIoctl"},
97 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
98 {6, nullptr, "FlushSessionCache"},
99 };
100 // clang-format on
86 101
87 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 102 RegisterHandlers(functions);
88 rb.Push(RESULT_SUCCESS); 103 }
89 rb.PushIpcInterface<ISslContext>();
90}
91 104
92SSL::SSL() : ServiceFramework("ssl") { 105private:
93 static const FunctionInfo functions[] = { 106 void CreateContext(Kernel::HLERequestContext& ctx) {
94 {0, &SSL::CreateContext, "CreateContext"}, 107 LOG_WARNING(Service_SSL, "(STUBBED) called");
95 {1, nullptr, "GetContextCount"},
96 {2, nullptr, "GetCertificates"},
97 {3, nullptr, "GetCertificateBufSize"},
98 {4, nullptr, "DebugIoctl"},
99 {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
100 {6, nullptr, "FlushSessionCache"},
101 };
102 RegisterHandlers(functions);
103}
104 108
105void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) { 109 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
106 LOG_WARNING(Service_SSL, "(STUBBED) called"); 110 rb.Push(RESULT_SUCCESS);
107 IPC::RequestParser rp{ctx}; 111 rb.PushIpcInterface<ISslContext>();
108 u32 unk1 = rp.Pop<u32>(); // Probably minor/major? 112 }
109 u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
110 113
111 IPC::ResponseBuilder rb{ctx, 2}; 114 void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
112 rb.Push(RESULT_SUCCESS); 115 LOG_WARNING(Service_SSL, "(STUBBED) called");
113} 116 IPC::RequestParser rp{ctx};
117 u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
118 u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
119
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(RESULT_SUCCESS);
122 }
123};
114 124
115void InstallInterfaces(SM::ServiceManager& service_manager) { 125void InstallInterfaces(SM::ServiceManager& service_manager) {
116 std::make_shared<SSL>()->InstallAsService(service_manager); 126 std::make_shared<SSL>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h
index 8fef13022..5cb04c3b9 100644
--- a/src/core/hle/service/ssl/ssl.h
+++ b/src/core/hle/service/ssl/ssl.h
@@ -4,20 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/service.h" 7namespace Service::SM {
8class ServiceManager;
9}
8 10
9namespace Service::SSL { 11namespace Service::SSL {
10 12
11class SSL final : public ServiceFramework<SSL> {
12public:
13 explicit SSL();
14 ~SSL() = default;
15
16private:
17 void CreateContext(Kernel::HLERequestContext& ctx);
18 void SetInterfaceVersion(Kernel::HLERequestContext& ctx);
19};
20
21/// Registers all SSL services with the specified service manager. 13/// Registers all SSL services with the specified service manager.
22void InstallInterfaces(SM::ServiceManager& service_manager); 14void InstallInterfaces(SM::ServiceManager& service_manager);
23 15
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 048d5b077..18a5d71d5 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -29,4 +29,6 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
29 RegisterHandlers(functions); 29 RegisterHandlers(functions);
30} 30}
31 31
32Time::~Time() = default;
33
32} // namespace Service::Time 34} // namespace Service::Time
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index 183a53db1..cd6b44dec 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -11,6 +11,7 @@ namespace Service::Time {
11class Time final : public Module::Interface { 11class Time final : public Module::Interface {
12public: 12public:
13 explicit Time(std::shared_ptr<Module> time, const char* name); 13 explicit Time(std::shared_ptr<Module> time, const char* name);
14 ~Time() override;
14}; 15};
15 16
16} // namespace Service::Time 17} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 2172c681b..28fd8debc 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -210,6 +210,8 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
211 : ServiceFramework(name), time(std::move(time)) {} 211 : ServiceFramework(name), time(std::move(time)) {}
212 212
213Module::Interface::~Interface() = default;
214
213void InstallInterfaces(SM::ServiceManager& service_manager) { 215void InstallInterfaces(SM::ServiceManager& service_manager) {
214 auto time = std::make_shared<Module>(); 216 auto time = std::make_shared<Module>();
215 std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); 217 std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 8dde28a94..5659ecad3 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -58,6 +58,7 @@ public:
58 class Interface : public ServiceFramework<Interface> { 58 class Interface : public ServiceFramework<Interface> {
59 public: 59 public:
60 explicit Interface(std::shared_ptr<Module> time, const char* name); 60 explicit Interface(std::shared_ptr<Module> time, const char* name);
61 ~Interface() override;
61 62
62 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); 63 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
63 void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); 64 void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 993f1e65a..2ee60f1ec 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -4,25 +4,28 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <cstring>
7#include <memory> 8#include <memory>
8#include <type_traits> 9#include <type_traits>
9#include <utility> 10#include <utility>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include "common/alignment.h" 12#include "common/alignment.h"
13#include "common/assert.h"
14#include "common/common_funcs.h"
15#include "common/logging/log.h"
12#include "common/math_util.h" 16#include "common/math_util.h"
13#include "common/scope_exit.h" 17#include "common/swap.h"
14#include "core/core_timing.h" 18#include "core/core_timing.h"
15#include "core/hle/ipc_helpers.h" 19#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h" 20#include "core/hle/kernel/event.h"
17#include "core/hle/service/nvdrv/nvdrv.h" 21#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue.h" 22#include "core/hle/service/nvflinger/buffer_queue.h"
23#include "core/hle/service/nvflinger/nvflinger.h"
19#include "core/hle/service/vi/vi.h" 24#include "core/hle/service/vi/vi.h"
20#include "core/hle/service/vi/vi_m.h" 25#include "core/hle/service/vi/vi_m.h"
21#include "core/hle/service/vi/vi_s.h" 26#include "core/hle/service/vi/vi_s.h"
22#include "core/hle/service/vi/vi_u.h" 27#include "core/hle/service/vi/vi_u.h"
23#include "core/settings.h" 28#include "core/settings.h"
24#include "video_core/renderer_base.h"
25#include "video_core/video_core.h"
26 29
27namespace Service::VI { 30namespace Service::VI {
28 31
@@ -38,7 +41,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
38class Parcel { 41class Parcel {
39public: 42public:
40 // This default size was chosen arbitrarily. 43 // This default size was chosen arbitrarily.
41 static constexpr size_t DefaultBufferSize = 0x40; 44 static constexpr std::size_t DefaultBufferSize = 0x40;
42 Parcel() : buffer(DefaultBufferSize) {} 45 Parcel() : buffer(DefaultBufferSize) {}
43 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} 46 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
44 virtual ~Parcel() = default; 47 virtual ~Parcel() = default;
@@ -66,7 +69,7 @@ public:
66 return val; 69 return val;
67 } 70 }
68 71
69 std::vector<u8> ReadBlock(size_t length) { 72 std::vector<u8> ReadBlock(std::size_t length) {
70 ASSERT(read_index + length <= buffer.size()); 73 ASSERT(read_index + length <= buffer.size());
71 const u8* const begin = buffer.data() + read_index; 74 const u8* const begin = buffer.data() + read_index;
72 const u8* const end = begin + length; 75 const u8* const end = begin + length;
@@ -156,8 +159,8 @@ private:
156 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); 159 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
157 160
158 std::vector<u8> buffer; 161 std::vector<u8> buffer;
159 size_t read_index = 0; 162 std::size_t read_index = 0;
160 size_t write_index = 0; 163 std::size_t write_index = 0;
161}; 164};
162 165
163class NativeWindow : public Parcel { 166class NativeWindow : public Parcel {
@@ -514,7 +517,7 @@ private:
514 ctx.SleepClientThread( 517 ctx.SleepClientThread(
515 Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, 518 Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
516 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 519 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
517 ThreadWakeupReason reason) { 520 Kernel::ThreadWakeupReason reason) {
518 // Repeat TransactParcel DequeueBuffer when a buffer is available 521 // Repeat TransactParcel DequeueBuffer when a buffer is available
519 auto buffer_queue = nv_flinger->GetBufferQueue(id); 522 auto buffer_queue = nv_flinger->GetBufferQueue(id);
520 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 523 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
@@ -647,7 +650,7 @@ private:
647 u64 layer_id = rp.Pop<u64>(); 650 u64 layer_id = rp.Pop<u64>();
648 u64 z_value = rp.Pop<u64>(); 651 u64 z_value = rp.Pop<u64>();
649 652
650 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 653 IPC::ResponseBuilder rb{ctx, 2};
651 rb.Push(RESULT_SUCCESS); 654 rb.Push(RESULT_SUCCESS);
652 } 655 }
653 656
@@ -655,7 +658,7 @@ private:
655 IPC::RequestParser rp{ctx}; 658 IPC::RequestParser rp{ctx};
656 u64 layer_id = rp.Pop<u64>(); 659 u64 layer_id = rp.Pop<u64>();
657 bool visibility = rp.Pop<bool>(); 660 bool visibility = rp.Pop<bool>();
658 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 661 IPC::ResponseBuilder rb{ctx, 2};
659 rb.Push(RESULT_SUCCESS); 662 rb.Push(RESULT_SUCCESS);
660 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, 663 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
661 visibility); 664 visibility);
@@ -744,7 +747,7 @@ private:
744 IPC::RequestParser rp{ctx}; 747 IPC::RequestParser rp{ctx};
745 u64 display = rp.Pop<u64>(); 748 u64 display = rp.Pop<u64>();
746 749
747 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 750 IPC::ResponseBuilder rb{ctx, 2};
748 rb.Push(RESULT_SUCCESS); 751 rb.Push(RESULT_SUCCESS);
749 } 752 }
750 753
@@ -758,7 +761,7 @@ private:
758 761
759 u64 layer_id = nv_flinger->CreateLayer(display); 762 u64 layer_id = nv_flinger->CreateLayer(display);
760 763
761 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); 764 IPC::ResponseBuilder rb{ctx, 4};
762 rb.Push(RESULT_SUCCESS); 765 rb.Push(RESULT_SUCCESS);
763 rb.Push(layer_id); 766 rb.Push(layer_id);
764 } 767 }
@@ -769,7 +772,7 @@ private:
769 u32 stack = rp.Pop<u32>(); 772 u32 stack = rp.Pop<u32>();
770 u64 layer_id = rp.Pop<u64>(); 773 u64 layer_id = rp.Pop<u64>();
771 774
772 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 775 IPC::ResponseBuilder rb{ctx, 2};
773 rb.Push(RESULT_SUCCESS); 776 rb.Push(RESULT_SUCCESS);
774 } 777 }
775 778
@@ -777,7 +780,7 @@ private:
777 IPC::RequestParser rp{ctx}; 780 IPC::RequestParser rp{ctx};
778 u64 layer_id = rp.Pop<u64>(); 781 u64 layer_id = rp.Pop<u64>();
779 bool visibility = rp.Pop<bool>(); 782 bool visibility = rp.Pop<bool>();
780 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 783 IPC::ResponseBuilder rb{ctx, 2};
781 rb.Push(RESULT_SUCCESS); 784 rb.Push(RESULT_SUCCESS);
782 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, 785 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
783 visibility); 786 visibility);
@@ -834,7 +837,7 @@ private:
834 837
835 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); 838 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
836 839
837 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); 840 IPC::ResponseBuilder rb{ctx, 4};
838 rb.Push(RESULT_SUCCESS); 841 rb.Push(RESULT_SUCCESS);
839 rb.Push<u64>(nv_flinger->OpenDisplay(name)); 842 rb.Push<u64>(nv_flinger->OpenDisplay(name));
840 } 843 }
@@ -844,7 +847,7 @@ private:
844 IPC::RequestParser rp{ctx}; 847 IPC::RequestParser rp{ctx};
845 u64 display_id = rp.Pop<u64>(); 848 u64 display_id = rp.Pop<u64>();
846 849
847 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 850 IPC::ResponseBuilder rb{ctx, 2};
848 rb.Push(RESULT_SUCCESS); 851 rb.Push(RESULT_SUCCESS);
849 } 852 }
850 853
@@ -853,7 +856,7 @@ private:
853 IPC::RequestParser rp{ctx}; 856 IPC::RequestParser rp{ctx};
854 u64 display_id = rp.Pop<u64>(); 857 u64 display_id = rp.Pop<u64>();
855 858
856 IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); 859 IPC::ResponseBuilder rb{ctx, 6};
857 rb.Push(RESULT_SUCCESS); 860 rb.Push(RESULT_SUCCESS);
858 861
859 if (Settings::values.use_docked_mode) { 862 if (Settings::values.use_docked_mode) {
@@ -871,7 +874,7 @@ private:
871 u32 scaling_mode = rp.Pop<u32>(); 874 u32 scaling_mode = rp.Pop<u32>();
872 u64 unknown = rp.Pop<u64>(); 875 u64 unknown = rp.Pop<u64>();
873 876
874 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 877 IPC::ResponseBuilder rb{ctx, 2};
875 rb.Push(RESULT_SUCCESS); 878 rb.Push(RESULT_SUCCESS);
876 } 879 }
877 880
@@ -879,7 +882,7 @@ private:
879 IPC::RequestParser rp{ctx}; 882 IPC::RequestParser rp{ctx};
880 DisplayInfo display_info; 883 DisplayInfo display_info;
881 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 884 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
882 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); 885 IPC::ResponseBuilder rb{ctx, 4};
883 rb.Push(RESULT_SUCCESS); 886 rb.Push(RESULT_SUCCESS);
884 rb.Push<u64>(1); 887 rb.Push<u64>(1);
885 LOG_WARNING(Service_VI, "(STUBBED) called"); 888 LOG_WARNING(Service_VI, "(STUBBED) called");
@@ -900,7 +903,7 @@ private:
900 u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); 903 u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
901 904
902 NativeWindow native_window{buffer_queue_id}; 905 NativeWindow native_window{buffer_queue_id};
903 IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); 906 IPC::ResponseBuilder rb{ctx, 4};
904 rb.Push(RESULT_SUCCESS); 907 rb.Push(RESULT_SUCCESS);
905 rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); 908 rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
906 } 909 }
@@ -919,7 +922,7 @@ private:
919 u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); 922 u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
920 923
921 NativeWindow native_window{buffer_queue_id}; 924 NativeWindow native_window{buffer_queue_id};
922 IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); 925 IPC::ResponseBuilder rb{ctx, 6};
923 rb.Push(RESULT_SUCCESS); 926 rb.Push(RESULT_SUCCESS);
924 rb.Push(layer_id); 927 rb.Push(layer_id);
925 rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); 928 rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
@@ -931,7 +934,7 @@ private:
931 IPC::RequestParser rp{ctx}; 934 IPC::RequestParser rp{ctx};
932 u64 layer_id = rp.Pop<u64>(); 935 u64 layer_id = rp.Pop<u64>();
933 936
934 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); 937 IPC::ResponseBuilder rb{ctx, 2};
935 rb.Push(RESULT_SUCCESS); 938 rb.Push(RESULT_SUCCESS);
936 } 939 }
937 940
@@ -942,7 +945,7 @@ private:
942 945
943 auto vsync_event = nv_flinger->GetVsyncEvent(display_id); 946 auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
944 947
945 IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0); 948 IPC::ResponseBuilder rb{ctx, 2, 1};
946 rb.Push(RESULT_SUCCESS); 949 rb.Push(RESULT_SUCCESS);
947 rb.PushCopyObjects(vsync_event); 950 rb.PushCopyObjects(vsync_event);
948 } 951 }
@@ -984,6 +987,8 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
984 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 987 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
985 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} 988 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
986 989
990Module::Interface::~Interface() = default;
991
987void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { 992void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
988 LOG_WARNING(Service_VI, "(STUBBED) called"); 993 LOG_WARNING(Service_VI, "(STUBBED) called");
989 994
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 92f5b6059..e3963502a 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -4,11 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/nvflinger/nvflinger.h"
8#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
9 8
10namespace CoreTiming { 9namespace Service::NVFlinger {
11struct EventType; 10class NVFlinger;
12} 11}
13 12
14namespace Service::VI { 13namespace Service::VI {
@@ -26,6 +25,7 @@ public:
26 public: 25 public:
27 explicit Interface(std::shared_ptr<Module> module, const char* name, 26 explicit Interface(std::shared_ptr<Module> module, const char* name,
28 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
28 ~Interface() override;
29 29
30 void GetDisplayService(Kernel::HLERequestContext& ctx); 30 void GetDisplayService(Kernel::HLERequestContext& ctx);
31 31
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index d47da565b..207c06b16 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -15,4 +15,6 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
15 RegisterHandlers(functions); 15 RegisterHandlers(functions);
16} 16}
17 17
18VI_M::~VI_M() = default;
19
18} // namespace Service::VI 20} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 6abb9b3a3..487d58d50 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -11,6 +11,7 @@ namespace Service::VI {
11class VI_M final : public Module::Interface { 11class VI_M final : public Module::Interface {
12public: 12public:
13 explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 13 explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_M() override;
14}; 15};
15 16
16} // namespace Service::VI 17} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 8f82e797f..920e6a1f6 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -15,4 +15,6 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
15 RegisterHandlers(functions); 15 RegisterHandlers(functions);
16} 16}
17 17
18VI_S::~VI_S() = default;
19
18} // namespace Service::VI 20} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 8f16f804f..bbc31148f 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -11,6 +11,7 @@ namespace Service::VI {
11class VI_S final : public Module::Interface { 11class VI_S final : public Module::Interface {
12public: 12public:
13 explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 13 explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_S() override;
14}; 15};
15 16
16} // namespace Service::VI 17} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index b84aed1d5..d81e410d6 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -14,4 +14,6 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
14 RegisterHandlers(functions); 14 RegisterHandlers(functions);
15} 15}
16 16
17VI_U::~VI_U() = default;
18
17} // namespace Service::VI 19} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index e9b4f76b2..b92f28c92 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -11,6 +11,7 @@ namespace Service::VI {
11class VI_U final : public Module::Interface { 11class VI_U final : public Module::Interface {
12public: 12public:
13 explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 13 explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_U() override;
14}; 15};
15 16
16} // namespace Service::VI 17} // namespace Service::VI
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 921b899e2..2b8f78136 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -9,6 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
11#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/romfs_factory.h" 13#include "core/file_sys/romfs_factory.h"
13#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
14#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
@@ -21,10 +22,19 @@
21 22
22namespace Loader { 23namespace Loader {
23 24
24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_) 25AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
25 : AppLoader(std::move(file_)) { 26 bool override_update)
27 : AppLoader(std::move(file_)), override_update(override_update) {
26 const auto dir = file->GetContainingDirectory(); 28 const auto dir = file->GetContainingDirectory();
27 29
30 // Title ID
31 const auto npdm = dir->GetFile("main.npdm");
32 if (npdm != nullptr) {
33 const auto res = metadata.Load(npdm);
34 if (res == ResultStatus::Success)
35 title_id = metadata.GetTitleID();
36 }
37
28 // Icon 38 // Icon
29 FileSys::VirtualFile icon_file = nullptr; 39 FileSys::VirtualFile icon_file = nullptr;
30 for (const auto& language : FileSys::LANGUAGE_NAMES) { 40 for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -61,14 +71,14 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
61 71
62 if (nacp_file != nullptr) { 72 if (nacp_file != nullptr) {
63 FileSys::NACP nacp(nacp_file); 73 FileSys::NACP nacp(nacp_file);
64 title_id = nacp.GetTitleId();
65 name = nacp.GetApplicationName(); 74 name = nacp.GetApplicationName();
66 } 75 }
67} 76}
68 77
69AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( 78AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
70 FileSys::VirtualDir directory) 79 FileSys::VirtualDir directory, bool override_update)
71 : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {} 80 : AppLoader(directory->GetFile("main")), dir(std::move(directory)),
81 override_update(override_update) {}
72 82
73FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { 83FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
74 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { 84 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
@@ -90,7 +100,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
90 dir = file->GetContainingDirectory(); 100 dir = file->GetContainingDirectory();
91 } 101 }
92 102
93 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); 103 // Read meta to determine title ID
104 FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
94 if (npdm == nullptr) 105 if (npdm == nullptr)
95 return ResultStatus::ErrorMissingNPDM; 106 return ResultStatus::ErrorMissingNPDM;
96 107
@@ -98,6 +109,21 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
98 if (result != ResultStatus::Success) { 109 if (result != ResultStatus::Success) {
99 return result; 110 return result;
100 } 111 }
112
113 if (override_update) {
114 const FileSys::PatchManager patch_manager(metadata.GetTitleID());
115 dir = patch_manager.PatchExeFS(dir);
116 }
117
118 // Reread in case PatchExeFS affected the main.npdm
119 npdm = dir->GetFile("main.npdm");
120 if (npdm == nullptr)
121 return ResultStatus::ErrorMissingNPDM;
122
123 ResultStatus result2 = metadata.Load(npdm);
124 if (result2 != ResultStatus::Success) {
125 return result2;
126 }
101 metadata.Print(); 127 metadata.Print();
102 128
103 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; 129 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
@@ -159,8 +185,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buff
159} 185}
160 186
161ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { 187ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
162 if (name.empty())
163 return ResultStatus::ErrorNoControl;
164 out_program_id = title_id; 188 out_program_id = title_id;
165 return ResultStatus::Success; 189 return ResultStatus::Success;
166} 190}
@@ -172,4 +196,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title)
172 return ResultStatus::Success; 196 return ResultStatus::Success;
173} 197}
174 198
199bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
200 return false;
201}
202
175} // namespace Loader 203} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index b20804f75..8a0dc1b1e 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -20,10 +20,12 @@ namespace Loader {
20 */ 20 */
21class AppLoader_DeconstructedRomDirectory final : public AppLoader { 21class AppLoader_DeconstructedRomDirectory final : public AppLoader {
22public: 22public:
23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); 23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file,
24 bool override_update = false);
24 25
25 // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' 26 // Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
26 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory); 27 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory,
28 bool override_update = false);
27 29
28 /** 30 /**
29 * Returns the type of the file 31 * Returns the type of the file
@@ -42,6 +44,7 @@ public:
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 45 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadTitle(std::string& title) override; 46 ResultStatus ReadTitle(std::string& title) override;
47 bool IsRomFSUpdatable() const override;
45 48
46private: 49private:
47 FileSys::ProgramMetadata metadata; 50 FileSys::ProgramMetadata metadata;
@@ -51,6 +54,7 @@ private:
51 std::vector<u8> icon_data; 54 std::vector<u8> icon_data;
52 std::string name; 55 std::string name;
53 u64 title_id{}; 56 u64 title_id{};
57 bool override_update;
54}; 58};
55 59
56} // namespace Loader 60} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 120e1e133..0e2af20b4 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -300,7 +300,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
300 } 300 }
301 301
302 std::vector<u8> program_image(total_image_size); 302 std::vector<u8> program_image(total_image_size);
303 size_t current_image_position = 0; 303 std::size_t current_image_position = 0;
304 304
305 auto& kernel = Core::System::GetInstance().Kernel(); 305 auto& kernel = Core::System::GetInstance().Kernel();
306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); 306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index c13fb49b8..f2a183ba1 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -5,9 +5,9 @@
5#include <memory> 5#include <memory>
6#include <ostream> 6#include <ostream>
7#include <string> 7#include <string>
8#include "common/file_util.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "common/string_util.h" 10#include "common/string_util.h"
10#include "core/file_sys/vfs_real.h"
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/loader/deconstructed_rom_directory.h" 12#include "core/loader/deconstructed_rom_directory.h"
13#include "core/loader/elf.h" 13#include "core/loader/elf.h"
@@ -15,6 +15,7 @@
15#include "core/loader/nca.h" 15#include "core/loader/nca.h"
16#include "core/loader/nro.h" 16#include "core/loader/nro.h"
17#include "core/loader/nso.h" 17#include "core/loader/nso.h"
18#include "core/loader/nsp.h"
18#include "core/loader/xci.h" 19#include "core/loader/xci.h"
19 20
20namespace Loader { 21namespace Loader {
@@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
34 CHECK_TYPE(NCA) 35 CHECK_TYPE(NCA)
35 CHECK_TYPE(XCI) 36 CHECK_TYPE(XCI)
36 CHECK_TYPE(NAX) 37 CHECK_TYPE(NAX)
38 CHECK_TYPE(NSP)
37 39
38#undef CHECK_TYPE 40#undef CHECK_TYPE
39 41
@@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) {
59 return FileType::NCA; 61 return FileType::NCA;
60 if (extension == "xci") 62 if (extension == "xci")
61 return FileType::XCI; 63 return FileType::XCI;
64 if (extension == "nsp")
65 return FileType::NSP;
62 66
63 return FileType::Unknown; 67 return FileType::Unknown;
64} 68}
@@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) {
77 return "XCI"; 81 return "XCI";
78 case FileType::NAX: 82 case FileType::NAX:
79 return "NAX"; 83 return "NAX";
84 case FileType::NSP:
85 return "NSP";
80 case FileType::DeconstructedRomDirectory: 86 case FileType::DeconstructedRomDirectory:
81 return "Directory"; 87 return "Directory";
82 case FileType::Error: 88 case FileType::Error:
@@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
87 return "unknown"; 93 return "unknown";
88} 94}
89 95
90constexpr std::array<const char*, 49> RESULT_MESSAGES{ 96constexpr std::array<const char*, 58> RESULT_MESSAGES{
91 "The operation completed successfully.", 97 "The operation completed successfully.",
92 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
93 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -137,13 +143,25 @@ constexpr std::array<const char*, 49> RESULT_MESSAGES{
137 "The AES Key Generation Source could not be found.", 143 "The AES Key Generation Source could not be found.",
138 "The SD Save Key Source could not be found.", 144 "The SD Save Key Source could not be found.",
139 "The SD NCA Key Source could not be found.", 145 "The SD NCA Key Source could not be found.",
146 "The NSP file is missing a Program-type NCA.",
147 "The BKTR-type NCA has a bad BKTR header.",
148 "The BKTR Subsection entry is not located immediately after the Relocation entry.",
149 "The BKTR Subsection entry is not at the end of the media block.",
150 "The BKTR-type NCA has a bad Relocation block.",
151 "The BKTR-type NCA has a bad Subsection block.",
152 "The BKTR-type NCA has a bad Relocation bucket.",
153 "The BKTR-type NCA has a bad Subsection bucket.",
154 "The BKTR-type NCA is missing the base RomFS.",
140}; 155};
141 156
142std::ostream& operator<<(std::ostream& os, ResultStatus status) { 157std::ostream& operator<<(std::ostream& os, ResultStatus status) {
143 os << RESULT_MESSAGES.at(static_cast<size_t>(status)); 158 os << RESULT_MESSAGES.at(static_cast<std::size_t>(status));
144 return os; 159 return os;
145} 160}
146 161
162AppLoader::AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
163AppLoader::~AppLoader() = default;
164
147/** 165/**
148 * Get a loader for a file with a specific type 166 * Get a loader for a file with a specific type
149 * @param file The file to load 167 * @param file The file to load
@@ -179,6 +197,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
179 case FileType::NAX: 197 case FileType::NAX:
180 return std::make_unique<AppLoader_NAX>(std::move(file)); 198 return std::make_unique<AppLoader_NAX>(std::move(file));
181 199
200 // NX NSP (Nintendo Submission Package) file format
201 case FileType::NSP:
202 return std::make_unique<AppLoader_NSP>(std::move(file));
203
182 // NX deconstructed ROM directory. 204 // NX deconstructed ROM directory.
183 case FileType::DeconstructedRomDirectory: 205 case FileType::DeconstructedRomDirectory:
184 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); 206 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 885fee84c..843c4bb91 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <iosfwd> 7#include <iosfwd>
9#include <memory> 8#include <memory>
10#include <string> 9#include <string>
@@ -12,7 +11,6 @@
12#include <vector> 11#include <vector>
13#include <boost/optional.hpp> 12#include <boost/optional.hpp>
14#include "common/common_types.h" 13#include "common/common_types.h"
15#include "common/file_util.h"
16#include "core/file_sys/vfs.h" 14#include "core/file_sys/vfs.h"
17#include "core/hle/kernel/object.h" 15#include "core/hle/kernel/object.h"
18 16
@@ -31,6 +29,7 @@ enum class FileType {
31 NSO, 29 NSO,
32 NRO, 30 NRO,
33 NCA, 31 NCA,
32 NSP,
34 XCI, 33 XCI,
35 NAX, 34 NAX,
36 DeconstructedRomDirectory, 35 DeconstructedRomDirectory,
@@ -107,6 +106,15 @@ enum class ResultStatus : u16 {
107 ErrorMissingAESKeyGenerationSource, 106 ErrorMissingAESKeyGenerationSource,
108 ErrorMissingSDSaveKeySource, 107 ErrorMissingSDSaveKeySource,
109 ErrorMissingSDNCAKeySource, 108 ErrorMissingSDNCAKeySource,
109 ErrorNSPMissingProgramNCA,
110 ErrorBadBKTRHeader,
111 ErrorBKTRSubsectionNotAfterRelocation,
112 ErrorBKTRSubsectionNotAtEnd,
113 ErrorBadRelocationBlock,
114 ErrorBadSubsectionBlock,
115 ErrorBadRelocationBuckets,
116 ErrorBadSubsectionBuckets,
117 ErrorMissingBKTRBaseRomFS,
110}; 118};
111 119
112std::ostream& operator<<(std::ostream& os, ResultStatus status); 120std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -114,8 +122,8 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
114/// Interface for loading an application 122/// Interface for loading an application
115class AppLoader : NonCopyable { 123class AppLoader : NonCopyable {
116public: 124public:
117 explicit AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {} 125 explicit AppLoader(FileSys::VirtualFile file);
118 virtual ~AppLoader() {} 126 virtual ~AppLoader();
119 127
120 /** 128 /**
121 * Returns the type of this file 129 * Returns the type of this file
@@ -197,13 +205,22 @@ public:
197 } 205 }
198 206
199 /** 207 /**
200 * Get the update RomFS of the application 208 * Get whether or not updates can be applied to the RomFS.
201 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 209 * By default, this is true, however for formats where it cannot be guaranteed that the RomFS is
202 * @param file The file containing the RomFS 210 * the base game it should be set to false.
203 * @return ResultStatus result of function 211 * @return bool whether or not updatable.
204 */ 212 */
205 virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) { 213 virtual bool IsRomFSUpdatable() const {
206 return ResultStatus::ErrorNotImplemented; 214 return true;
215 }
216
217 /**
218 * Gets the difference between the start of the IVFC header and the start of level 6 (RomFS)
219 * data. Needed for bktr patching.
220 * @return IVFC offset for romfs.
221 */
222 virtual u64 ReadRomFSIVFCOffset() const {
223 return 0;
207 } 224 }
208 225
209 /** 226 /**
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index b46d81c02..5d4380684 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -11,6 +11,20 @@
11#include "core/loader/nca.h" 11#include "core/loader/nca.h"
12 12
13namespace Loader { 13namespace Loader {
14namespace {
15FileType IdentifyTypeImpl(const FileSys::NAX& nax) {
16 if (nax.GetStatus() != ResultStatus::Success) {
17 return FileType::Error;
18 }
19
20 const auto nca = nax.AsNCA();
21 if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
22 return FileType::Error;
23 }
24
25 return FileType::NAX;
26}
27} // Anonymous namespace
14 28
15AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) 29AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
16 : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)), 30 : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)),
@@ -19,14 +33,12 @@ AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
19AppLoader_NAX::~AppLoader_NAX() = default; 33AppLoader_NAX::~AppLoader_NAX() = default;
20 34
21FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { 35FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) {
22 FileSys::NAX nax(file); 36 const FileSys::NAX nax(file);
23 37 return IdentifyTypeImpl(nax);
24 if (nax.GetStatus() == ResultStatus::Success && nax.AsNCA() != nullptr && 38}
25 nax.AsNCA()->GetStatus() == ResultStatus::Success) {
26 return FileType::NAX;
27 }
28 39
29 return FileType::Error; 40FileType AppLoader_NAX::GetFileType() {
41 return IdentifyTypeImpl(*nax);
30} 42}
31 43
32ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { 44ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index 4dbae2918..56605fe45 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -31,9 +31,7 @@ public:
31 */ 31 */
32 static FileType IdentifyType(const FileSys::VirtualFile& file); 32 static FileType IdentifyType(const FileSys::VirtualFile& file);
33 33
34 FileType GetFileType() override { 34 FileType GetFileType() override;
35 return IdentifyType(file);
36 }
37 35
38 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 36 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
39 37
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index c036a8a1c..6aaffae59 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -48,7 +48,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
48 if (exefs == nullptr) 48 if (exefs == nullptr)
49 return ResultStatus::ErrorNoExeFS; 49 return ResultStatus::ErrorNoExeFS;
50 50
51 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); 51 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
52 52
53 const auto load_result = directory_loader->Load(process); 53 const auto load_result = directory_loader->Load(process);
54 if (load_result != ResultStatus::Success) 54 if (load_result != ResultStatus::Success)
@@ -71,6 +71,12 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
71 return ResultStatus::Success; 71 return ResultStatus::Success;
72} 72}
73 73
74u64 AppLoader_NCA::ReadRomFSIVFCOffset() const {
75 if (nca == nullptr)
76 return 0;
77 return nca->GetBaseIVFCOffset();
78}
79
74ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { 80ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
75 if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) 81 if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
76 return ResultStatus::ErrorNotInitialized; 82 return ResultStatus::ErrorNotInitialized;
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 326f84857..10be197c4 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -37,6 +37,7 @@ public:
37 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 37 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
38 38
39 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 39 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
40 u64 ReadRomFSIVFCOffset() const override;
40 ResultStatus ReadProgramId(u64& out_program_id) override; 41 ResultStatus ReadProgramId(u64& out_program_id) override;
41 42
42private: 43private:
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 77026b850..c49ec34ab 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -191,7 +191,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
191 process->svc_access_mask.set(); 191 process->svc_access_mask.set();
192 process->resource_limit = 192 process->resource_limit =
193 kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); 193 kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
194 process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 194 process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
195 195
196 is_loaded = true; 196 is_loaded = true;
197 return ResultStatus::Success; 197 return ResultStatus::Success;
@@ -232,4 +232,9 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
232 title = nacp->GetApplicationName(); 232 title = nacp->GetApplicationName();
233 return ResultStatus::Success; 233 return ResultStatus::Success;
234} 234}
235
236bool AppLoader_NRO::IsRomFSUpdatable() const {
237 return false;
238}
239
235} // namespace Loader 240} // namespace Loader
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index bb01c9e25..96d2de305 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -39,6 +39,7 @@ public:
39 ResultStatus ReadProgramId(u64& out_program_id) override; 39 ResultStatus ReadProgramId(u64& out_program_id) override;
40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
41 ResultStatus ReadTitle(std::string& title) override; 41 ResultStatus ReadTitle(std::string& title) override;
42 bool IsRomFSUpdatable() const override;
42 43
43private: 44private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 45 bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 082a95d40..3c6306818 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -157,7 +157,8 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
157 process->svc_access_mask.set(); 157 process->svc_access_mask.set();
158 process->resource_limit = 158 process->resource_limit =
159 kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); 159 kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
160 process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 160 process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT,
161 Memory::DEFAULT_STACK_SIZE);
161 162
162 is_loaded = true; 163 is_loaded = true;
163 return ResultStatus::Success; 164 return ResultStatus::Success;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
new file mode 100644
index 000000000..291a9876d
--- /dev/null
+++ b/src/core/loader/nsp.cpp
@@ -0,0 +1,125 @@
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/common_types.h"
8#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/patch_manager.h"
13#include "core/file_sys/registered_cache.h"
14#include "core/file_sys/romfs.h"
15#include "core/file_sys/submission_package.h"
16#include "core/hle/kernel/process.h"
17#include "core/loader/deconstructed_rom_directory.h"
18#include "core/loader/nca.h"
19#include "core/loader/nsp.h"
20
21namespace Loader {
22
23AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
24 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
25 title_id(nsp->GetProgramTitleID()) {
26
27 if (nsp->GetStatus() != ResultStatus::Success)
28 return;
29 if (nsp->IsExtractedType())
30 return;
31
32 const auto control_nca =
33 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
34 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
35 return;
36
37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca);
39}
40
41AppLoader_NSP::~AppLoader_NSP() = default;
42
43FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
44 FileSys::NSP nsp(file);
45
46 if (nsp.GetStatus() == ResultStatus::Success) {
47 // Extracted Type case
48 if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
49 FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
50 return FileType::NSP;
51 }
52
53 // Non-Ectracted Type case
54 if (!nsp.IsExtractedType() &&
55 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
56 AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
57 nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
58 return FileType::NSP;
59 }
60 }
61
62 return FileType::Error;
63}
64
65ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) {
66 if (is_loaded) {
67 return ResultStatus::ErrorAlreadyLoaded;
68 }
69
70 if (nsp->IsExtractedType()) {
71 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
72 } else {
73 if (title_id == 0)
74 return ResultStatus::ErrorNSPMissingProgramNCA;
75
76 secondary_loader = std::make_unique<AppLoader_NCA>(
77 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
78
79 if (nsp->GetStatus() != ResultStatus::Success)
80 return nsp->GetStatus();
81
82 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
83 return nsp->GetProgramStatus(title_id);
84
85 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
86 if (!Core::Crypto::KeyManager::KeyFileExists(false))
87 return ResultStatus::ErrorMissingProductionKeyFile;
88 return ResultStatus::ErrorNSPMissingProgramNCA;
89 }
90 }
91
92 const auto result = secondary_loader->Load(process);
93 if (result != ResultStatus::Success)
94 return result;
95
96 is_loaded = true;
97
98 return ResultStatus::Success;
99}
100
101ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) {
102 return secondary_loader->ReadRomFS(dir);
103}
104
105ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
106 if (title_id == 0)
107 return ResultStatus::ErrorNotInitialized;
108 out_program_id = title_id;
109 return ResultStatus::Success;
110}
111
112ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
113 if (icon_file == nullptr)
114 return ResultStatus::ErrorNoControl;
115 buffer = icon_file->ReadAllBytes();
116 return ResultStatus::Success;
117}
118
119ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
120 if (nacp_file == nullptr)
121 return ResultStatus::ErrorNoControl;
122 title = nacp_file->GetApplicationName();
123 return ResultStatus::Success;
124}
125} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
new file mode 100644
index 000000000..7ef810499
--- /dev/null
+++ b/src/core/loader/nsp.h
@@ -0,0 +1,54 @@
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/vfs.h"
10#include "core/loader/loader.h"
11
12namespace FileSys {
13class NACP;
14class NSP;
15} // namespace FileSys
16
17namespace Loader {
18
19class AppLoader_NCA;
20
21/// Loads an XCI file
22class AppLoader_NSP final : public AppLoader {
23public:
24 explicit AppLoader_NSP(FileSys::VirtualFile file);
25 ~AppLoader_NSP() override;
26
27 /**
28 * Returns the type of the file
29 * @param file std::shared_ptr<VfsFile> open file
30 * @return FileType found, or FileType::Error if this loader doesn't know it
31 */
32 static FileType IdentifyType(const FileSys::VirtualFile& file);
33
34 FileType GetFileType() override {
35 return IdentifyType(file);
36 }
37
38 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
41 ResultStatus ReadProgramId(u64& out_program_id) override;
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadTitle(std::string& title) override;
44
45private:
46 std::unique_ptr<FileSys::NSP> nsp;
47 std::unique_ptr<AppLoader> secondary_loader;
48
49 FileSys::VirtualFile icon_file;
50 std::shared_ptr<FileSys::NACP> nacp_file;
51 u64 title_id;
52};
53
54} // namespace Loader
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 9dc4d1f35..16509229f 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -8,7 +8,9 @@
8#include "core/file_sys/card_image.h" 8#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h"
11#include "core/file_sys/romfs.h" 12#include "core/file_sys/romfs.h"
13#include "core/file_sys/submission_package.h"
12#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
13#include "core/loader/nca.h" 15#include "core/loader/nca.h"
14#include "core/loader/xci.h" 16#include "core/loader/xci.h"
@@ -17,25 +19,16 @@ namespace Loader {
17 19
18AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) 20AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
19 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 21 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
20 nca_loader(std::make_unique<AppLoader_NCA>( 22 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
21 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
22 if (xci->GetStatus() != ResultStatus::Success) 23 if (xci->GetStatus() != ResultStatus::Success)
23 return; 24 return;
25
24 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); 26 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
25 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) 27 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
26 return; 28 return;
27 const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); 29
28 if (romfs == nullptr) 30 std::tie(nacp_file, icon_file) =
29 return; 31 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca);
30 for (const auto& language : FileSys::LANGUAGE_NAMES) {
31 icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
32 if (icon_file != nullptr)
33 break;
34 }
35 const auto nacp_raw = romfs->GetFile("control.nacp");
36 if (nacp_raw == nullptr)
37 return;
38 nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
39} 32}
40 33
41AppLoader_XCI::~AppLoader_XCI() = default; 34AppLoader_XCI::~AppLoader_XCI() = default;
@@ -64,11 +57,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
64 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 57 if (xci->GetProgramNCAStatus() != ResultStatus::Success)
65 return xci->GetProgramNCAStatus(); 58 return xci->GetProgramNCAStatus();
66 59
67 const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program); 60 const auto nca = xci->GetProgramNCA();
68 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false)) 61 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
69 return ResultStatus::ErrorMissingProductionKeyFile; 62 return ResultStatus::ErrorMissingProductionKeyFile;
70 63
71 auto result = nca_loader->Load(process); 64 const auto result = nca_loader->Load(process);
72 if (result != ResultStatus::Success) 65 if (result != ResultStatus::Success)
73 return result; 66 return result;
74 67
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 0e4e0157c..316b46820 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -370,16 +370,16 @@ u64 Read64(const VAddr addr) {
370} 370}
371 371
372void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, 372void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
373 const size_t size) { 373 const std::size_t size) {
374 auto& page_table = process.vm_manager.page_table; 374 auto& page_table = process.vm_manager.page_table;
375 375
376 size_t remaining_size = size; 376 std::size_t remaining_size = size;
377 size_t page_index = src_addr >> PAGE_BITS; 377 std::size_t page_index = src_addr >> PAGE_BITS;
378 size_t page_offset = src_addr & PAGE_MASK; 378 std::size_t page_offset = src_addr & PAGE_MASK;
379 379
380 while (remaining_size > 0) { 380 while (remaining_size > 0) {
381 const size_t copy_amount = 381 const std::size_t copy_amount =
382 std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); 382 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
383 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 383 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
384 384
385 switch (page_table.attributes[page_index]) { 385 switch (page_table.attributes[page_index]) {
@@ -414,7 +414,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
414 } 414 }
415} 415}
416 416
417void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { 417void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
418 ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); 418 ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size);
419} 419}
420 420
@@ -435,15 +435,15 @@ void Write64(const VAddr addr, const u64 data) {
435} 435}
436 436
437void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, 437void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
438 const size_t size) { 438 const std::size_t size) {
439 auto& page_table = process.vm_manager.page_table; 439 auto& page_table = process.vm_manager.page_table;
440 size_t remaining_size = size; 440 std::size_t remaining_size = size;
441 size_t page_index = dest_addr >> PAGE_BITS; 441 std::size_t page_index = dest_addr >> PAGE_BITS;
442 size_t page_offset = dest_addr & PAGE_MASK; 442 std::size_t page_offset = dest_addr & PAGE_MASK;
443 443
444 while (remaining_size > 0) { 444 while (remaining_size > 0) {
445 const size_t copy_amount = 445 const std::size_t copy_amount =
446 std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); 446 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
447 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 447 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
448 448
449 switch (page_table.attributes[page_index]) { 449 switch (page_table.attributes[page_index]) {
@@ -477,19 +477,19 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
477 } 477 }
478} 478}
479 479
480void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { 480void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
481 WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); 481 WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size);
482} 482}
483 483
484void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) { 484void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
485 auto& page_table = process.vm_manager.page_table; 485 auto& page_table = process.vm_manager.page_table;
486 size_t remaining_size = size; 486 std::size_t remaining_size = size;
487 size_t page_index = dest_addr >> PAGE_BITS; 487 std::size_t page_index = dest_addr >> PAGE_BITS;
488 size_t page_offset = dest_addr & PAGE_MASK; 488 std::size_t page_offset = dest_addr & PAGE_MASK;
489 489
490 while (remaining_size > 0) { 490 while (remaining_size > 0) {
491 const size_t copy_amount = 491 const std::size_t copy_amount =
492 std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); 492 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
493 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 493 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
494 494
495 switch (page_table.attributes[page_index]) { 495 switch (page_table.attributes[page_index]) {
@@ -522,15 +522,16 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size
522 } 522 }
523} 523}
524 524
525void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) { 525void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
526 const std::size_t size) {
526 auto& page_table = process.vm_manager.page_table; 527 auto& page_table = process.vm_manager.page_table;
527 size_t remaining_size = size; 528 std::size_t remaining_size = size;
528 size_t page_index = src_addr >> PAGE_BITS; 529 std::size_t page_index = src_addr >> PAGE_BITS;
529 size_t page_offset = src_addr & PAGE_MASK; 530 std::size_t page_offset = src_addr & PAGE_MASK;
530 531
531 while (remaining_size > 0) { 532 while (remaining_size > 0) {
532 const size_t copy_amount = 533 const std::size_t copy_amount =
533 std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); 534 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
534 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 535 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
535 536
536 switch (page_table.attributes[page_index]) { 537 switch (page_table.attributes[page_index]) {
@@ -565,7 +566,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
565 } 566 }
566} 567}
567 568
568void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { 569void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
569 CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); 570 CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size);
570} 571}
571 572
diff --git a/src/core/memory.h b/src/core/memory.h
index f06e04a75..2a27c0251 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -22,11 +22,11 @@ namespace Memory {
22 * Page size used by the ARM architecture. This is the smallest granularity with which memory can 22 * Page size used by the ARM architecture. This is the smallest granularity with which memory can
23 * be mapped. 23 * be mapped.
24 */ 24 */
25constexpr size_t PAGE_BITS = 12; 25constexpr std::size_t PAGE_BITS = 12;
26constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; 26constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
27constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 27constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28constexpr size_t ADDRESS_SPACE_BITS = 36; 28constexpr std::size_t ADDRESS_SPACE_BITS = 36;
29constexpr size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS); 29constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS);
30 30
31enum class PageType : u8 { 31enum class PageType : u8 {
32 /// Page is unmapped and should cause an access error. 32 /// Page is unmapped and should cause an access error.
@@ -154,13 +154,13 @@ void Write16(VAddr addr, u16 data);
154void Write32(VAddr addr, u32 data); 154void Write32(VAddr addr, u32 data);
155void Write64(VAddr addr, u64 data); 155void Write64(VAddr addr, u64 data);
156 156
157void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); 157void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size);
158void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); 158void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
159void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, 159void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
160 size_t size); 160 std::size_t size);
161void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); 161void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
162void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); 162void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
163void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 163void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
164 164
165u8* GetPointer(VAddr vaddr); 165u8* GetPointer(VAddr vaddr);
166 166
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h
index e8ea19333..0269c7ff1 100644
--- a/src/core/memory_hook.h
+++ b/src/core/memory_hook.h
@@ -32,14 +32,14 @@ public:
32 virtual boost::optional<u32> Read32(VAddr addr) = 0; 32 virtual boost::optional<u32> Read32(VAddr addr) = 0;
33 virtual boost::optional<u64> Read64(VAddr addr) = 0; 33 virtual boost::optional<u64> Read64(VAddr addr) = 0;
34 34
35 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) = 0; 35 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
36 36
37 virtual bool Write8(VAddr addr, u8 data) = 0; 37 virtual bool Write8(VAddr addr, u8 data) = 0;
38 virtual bool Write16(VAddr addr, u16 data) = 0; 38 virtual bool Write16(VAddr addr, u16 data) = 0;
39 virtual bool Write32(VAddr addr, u32 data) = 0; 39 virtual bool Write32(VAddr addr, u32 data) = 0;
40 virtual bool Write64(VAddr addr, u64 data) = 0; 40 virtual bool Write64(VAddr addr, u64 data) = 0;
41 41
42 virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) = 0; 42 virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
43}; 43};
44 44
45using MemoryHookPointer = std::shared_ptr<MemoryHook>; 45using MemoryHookPointer = std::shared_ptr<MemoryHook>;
diff --git a/src/core/settings.h b/src/core/settings.h
index ed6f42471..0318d019c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <string> 9#include <string>
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -120,6 +121,7 @@ struct Values {
120 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 121 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
121 std::string motion_device; 122 std::string motion_device;
122 std::string touch_device; 123 std::string touch_device;
124 std::atomic_bool is_device_reload_pending{true};
123 125
124 // Core 126 // Core
125 bool use_cpu_jit; 127 bool use_cpu_jit;
@@ -127,6 +129,8 @@ struct Values {
127 129
128 // Data Storage 130 // Data Storage
129 bool use_virtual_sd; 131 bool use_virtual_sd;
132 std::string nand_dir;
133 std::string sdmc_dir;
130 134
131 // Renderer 135 // Renderer
132 float resolution_factor; 136 float resolution_factor;
@@ -144,6 +148,7 @@ struct Values {
144 148
145 // Audio 149 // Audio
146 std::string sink_id; 150 std::string sink_id;
151 bool enable_audio_stretching;
147 std::string audio_device_id; 152 std::string audio_device_id;
148 float volume; 153 float volume;
149 154
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 65571b948..b0df154ca 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -7,6 +7,8 @@
7#include "common/file_util.h" 7#include "common/file_util.h"
8 8
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h"
10#include "core/loader/loader.h" 12#include "core/loader/loader.h"
11#include "core/settings.h" 13#include "core/settings.h"
12#include "core/telemetry_session.h" 14#include "core/telemetry_session.h"
@@ -88,12 +90,28 @@ TelemetrySession::TelemetrySession() {
88 std::chrono::system_clock::now().time_since_epoch()) 90 std::chrono::system_clock::now().time_since_epoch())
89 .count()}; 91 .count()};
90 AddField(Telemetry::FieldType::Session, "Init_Time", init_time); 92 AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
91 std::string program_name; 93
92 const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)}; 94 u64 program_id{};
95 const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
93 if (res == Loader::ResultStatus::Success) { 96 if (res == Loader::ResultStatus::Success) {
94 AddField(Telemetry::FieldType::Session, "ProgramName", program_name); 97 AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
98
99 std::string name;
100 System::GetInstance().GetAppLoader().ReadTitle(name);
101
102 if (name.empty()) {
103 auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
104 if (nacp != nullptr)
105 name = nacp->GetApplicationName();
106 }
107
108 if (!name.empty())
109 AddField(Telemetry::FieldType::Session, "ProgramName", name);
95 } 110 }
96 111
112 AddField(Telemetry::FieldType::Session, "ProgramFormat",
113 static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType()));
114
97 // Log application information 115 // Log application information
98 Telemetry::AppendBuildInfo(field_collection); 116 Telemetry::AppendBuildInfo(field_collection);
99 117
@@ -102,6 +120,9 @@ TelemetrySession::TelemetrySession() {
102 Telemetry::AppendOSInfo(field_collection); 120 Telemetry::AppendOSInfo(field_collection);
103 121
104 // Log user configuration information 122 // Log user configuration information
123 AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
124 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
125 Settings::values.enable_audio_stretching);
105 AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); 126 AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit);
106 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", 127 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
107 Settings::values.use_multi_core); 128 Settings::values.use_multi_core);
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
index af032f0c9..73cacb47f 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -76,7 +76,7 @@ void Recorder::Finish(const std::string& filename) {
76 try { 76 try {
77 // Open file and write header 77 // Open file and write header
78 FileUtil::IOFile file(filename, "wb"); 78 FileUtil::IOFile file(filename, "wb");
79 size_t written = file.WriteObject(header); 79 std::size_t written = file.WriteObject(header);
80 if (written != 1 || file.Tell() != initial.gpu_registers) 80 if (written != 1 || file.Tell() != initial.gpu_registers)
81 throw "Failed to write header"; 81 throw "Failed to write header";
82 82
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b12623d55..37f572853 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <thread>
6#include "common/param_package.h" 7#include "common/param_package.h"
7#include "input_common/analog_from_button.h" 8#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h" 9#include "input_common/keyboard.h"
@@ -17,6 +18,10 @@ namespace InputCommon {
17static std::shared_ptr<Keyboard> keyboard; 18static std::shared_ptr<Keyboard> keyboard;
18static std::shared_ptr<MotionEmu> motion_emu; 19static std::shared_ptr<MotionEmu> motion_emu;
19 20
21#ifdef HAVE_SDL2
22static std::thread poll_thread;
23#endif
24
20void Init() { 25void Init() {
21 keyboard = std::make_shared<Keyboard>(); 26 keyboard = std::make_shared<Keyboard>();
22 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 27 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -30,6 +35,12 @@ void Init() {
30#endif 35#endif
31} 36}
32 37
38void StartJoystickEventHandler() {
39#ifdef HAVE_SDL2
40 poll_thread = std::thread(SDL::PollLoop);
41#endif
42}
43
33void Shutdown() { 44void Shutdown() {
34 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 45 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
35 keyboard.reset(); 46 keyboard.reset();
@@ -39,6 +50,7 @@ void Shutdown() {
39 50
40#ifdef HAVE_SDL2 51#ifdef HAVE_SDL2
41 SDL::Shutdown(); 52 SDL::Shutdown();
53 poll_thread.join();
42#endif 54#endif
43} 55}
44 56
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 77a0ce90b..9eb13106e 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -20,6 +20,8 @@ void Init();
20/// Deregisters all built-in input device factories and shuts them down. 20/// Deregisters all built-in input device factories and shuts them down.
21void Shutdown(); 21void Shutdown();
22 22
23void StartJoystickEventHandler();
24
23class Keyboard; 25class Keyboard;
24 26
25/// Gets the keyboard button device factory. 27/// Gets the keyboard button device factory.
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index d1b960fd7..faf3c1fa3 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -2,15 +2,24 @@
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 <atomic>
5#include <cmath> 7#include <cmath>
8#include <functional>
9#include <iterator>
10#include <mutex>
6#include <string> 11#include <string>
12#include <thread>
7#include <tuple> 13#include <tuple>
8#include <unordered_map> 14#include <unordered_map>
9#include <utility> 15#include <utility>
16#include <vector>
10#include <SDL.h> 17#include <SDL.h>
18#include "common/assert.h"
11#include "common/logging/log.h" 19#include "common/logging/log.h"
12#include "common/math_util.h" 20#include "common/math_util.h"
13#include "common/param_package.h" 21#include "common/param_package.h"
22#include "common/threadsafe_queue.h"
14#include "input_common/main.h" 23#include "input_common/main.h"
15#include "input_common/sdl/sdl.h" 24#include "input_common/sdl/sdl.h"
16 25
@@ -21,33 +30,51 @@ namespace SDL {
21class SDLJoystick; 30class SDLJoystick;
22class SDLButtonFactory; 31class SDLButtonFactory;
23class SDLAnalogFactory; 32class SDLAnalogFactory;
24static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list; 33
34/// Map of GUID of a list of corresponding virtual Joysticks
35static std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
36static std::mutex joystick_map_mutex;
37
25static std::shared_ptr<SDLButtonFactory> button_factory; 38static std::shared_ptr<SDLButtonFactory> button_factory;
26static std::shared_ptr<SDLAnalogFactory> analog_factory; 39static std::shared_ptr<SDLAnalogFactory> analog_factory;
27 40
28static bool initialized = false; 41/// Used by the Pollers during config
42static std::atomic<bool> polling;
43static Common::SPSCQueue<SDL_Event> event_queue;
44
45static std::atomic<bool> initialized = false;
46
47static std::string GetGUID(SDL_Joystick* joystick) {
48 SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
49 char guid_str[33];
50 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
51 return guid_str;
52}
29 53
30class SDLJoystick { 54class SDLJoystick {
31public: 55public:
32 explicit SDLJoystick(int joystick_index) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
33 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { 57 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
34 if (!joystick) { 58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
35 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 59
36 } 60 void SetButton(int button, bool value) {
61 std::lock_guard<std::mutex> lock(mutex);
62 state.buttons[button] = value;
37 } 63 }
38 64
39 bool GetButton(int button) const { 65 bool GetButton(int button) const {
40 if (!joystick) 66 std::lock_guard<std::mutex> lock(mutex);
41 return {}; 67 return state.buttons.at(button);
42 SDL_JoystickUpdate(); 68 }
43 return SDL_JoystickGetButton(joystick.get(), button) == 1; 69
70 void SetAxis(int axis, Sint16 value) {
71 std::lock_guard<std::mutex> lock(mutex);
72 state.axes[axis] = value;
44 } 73 }
45 74
46 float GetAxis(int axis) const { 75 float GetAxis(int axis) const {
47 if (!joystick) 76 std::lock_guard<std::mutex> lock(mutex);
48 return {}; 77 return state.axes.at(axis) / 32767.0f;
49 SDL_JoystickUpdate();
50 return SDL_JoystickGetAxis(joystick.get(), axis) / 32767.0f;
51 } 78 }
52 79
53 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { 80 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
@@ -67,18 +94,213 @@ public:
67 return std::make_tuple(x, y); 94 return std::make_tuple(x, y);
68 } 95 }
69 96
97 void SetHat(int hat, Uint8 direction) {
98 std::lock_guard<std::mutex> lock(mutex);
99 state.hats[hat] = direction;
100 }
101
70 bool GetHatDirection(int hat, Uint8 direction) const { 102 bool GetHatDirection(int hat, Uint8 direction) const {
71 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; 103 std::lock_guard<std::mutex> lock(mutex);
104 return (state.hats.at(hat) & direction) != 0;
105 }
106 /**
107 * The guid of the joystick
108 */
109 const std::string& GetGUID() const {
110 return guid;
111 }
112
113 /**
114 * The number of joystick from the same type that were connected before this joystick
115 */
116 int GetPort() const {
117 return port;
118 }
119
120 SDL_Joystick* GetSDLJoystick() const {
121 return sdl_joystick.get();
72 } 122 }
73 123
74 SDL_JoystickID GetJoystickID() const { 124 void SetSDLJoystick(SDL_Joystick* joystick,
75 return SDL_JoystickInstanceID(joystick.get()); 125 decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
126 sdl_joystick =
127 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
76 } 128 }
77 129
78private: 130private:
79 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; 131 struct State {
132 std::unordered_map<int, bool> buttons;
133 std::unordered_map<int, Sint16> axes;
134 std::unordered_map<int, Uint8> hats;
135 } state;
136 std::string guid;
137 int port;
138 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
139 mutable std::mutex mutex;
80}; 140};
81 141
142/**
143 * Get the nth joystick with the corresponding GUID
144 */
145static std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port) {
146 std::lock_guard<std::mutex> lock(joystick_map_mutex);
147 const auto it = joystick_map.find(guid);
148 if (it != joystick_map.end()) {
149 while (it->second.size() <= port) {
150 auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
151 [](SDL_Joystick*) {});
152 it->second.emplace_back(std::move(joystick));
153 }
154 return it->second[port];
155 }
156 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
157 return joystick_map[guid].emplace_back(std::move(joystick));
158}
159
160/**
161 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
162 * it to a SDLJoystick with the same guid and that port
163 */
164static std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
165 std::lock_guard<std::mutex> lock(joystick_map_mutex);
166 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
167 const std::string guid = GetGUID(sdl_joystick);
168 auto map_it = joystick_map.find(guid);
169 if (map_it != joystick_map.end()) {
170 auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
171 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
172 return sdl_joystick == joystick->GetSDLJoystick();
173 });
174 if (vec_it != map_it->second.end()) {
175 // This is the common case: There is already an existing SDL_Joystick maped to a
176 // SDLJoystick. return the SDLJoystick
177 return *vec_it;
178 }
179 // Search for a SDLJoystick without a mapped SDL_Joystick...
180 auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
181 [](const std::shared_ptr<SDLJoystick>& joystick) {
182 return !joystick->GetSDLJoystick();
183 });
184 if (nullptr_it != map_it->second.end()) {
185 // ... and map it
186 (*nullptr_it)->SetSDLJoystick(sdl_joystick);
187 return *nullptr_it;
188 }
189 // There is no SDLJoystick without a mapped SDL_Joystick
190 // Create a new SDLJoystick
191 auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
192 return map_it->second.emplace_back(std::move(joystick));
193 }
194 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
195 return joystick_map[guid].emplace_back(std::move(joystick));
196}
197
198void InitJoystick(int joystick_index) {
199 std::lock_guard<std::mutex> lock(joystick_map_mutex);
200 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
201 if (!sdl_joystick) {
202 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
203 return;
204 }
205 std::string guid = GetGUID(sdl_joystick);
206 if (joystick_map.find(guid) == joystick_map.end()) {
207 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
208 joystick_map[guid].emplace_back(std::move(joystick));
209 return;
210 }
211 auto& joystick_guid_list = joystick_map[guid];
212 const auto it = std::find_if(
213 joystick_guid_list.begin(), joystick_guid_list.end(),
214 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
215 if (it != joystick_guid_list.end()) {
216 (*it)->SetSDLJoystick(sdl_joystick);
217 return;
218 }
219 auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
220 joystick_guid_list.emplace_back(std::move(joystick));
221}
222
223void CloseJoystick(SDL_Joystick* sdl_joystick) {
224 std::lock_guard<std::mutex> lock(joystick_map_mutex);
225 std::string guid = GetGUID(sdl_joystick);
226 // This call to guid is save since the joystick is guranteed to be in that map
227 auto& joystick_guid_list = joystick_map[guid];
228 const auto joystick_it =
229 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
230 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
231 return joystick->GetSDLJoystick() == sdl_joystick;
232 });
233 (*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
234}
235
236void HandleGameControllerEvent(const SDL_Event& event) {
237 switch (event.type) {
238 case SDL_JOYBUTTONUP: {
239 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
240 if (joystick) {
241 joystick->SetButton(event.jbutton.button, false);
242 }
243 break;
244 }
245 case SDL_JOYBUTTONDOWN: {
246 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
247 if (joystick) {
248 joystick->SetButton(event.jbutton.button, true);
249 }
250 break;
251 }
252 case SDL_JOYHATMOTION: {
253 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
254 if (joystick) {
255 joystick->SetHat(event.jhat.hat, event.jhat.value);
256 }
257 break;
258 }
259 case SDL_JOYAXISMOTION: {
260 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
261 if (joystick) {
262 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
263 }
264 break;
265 }
266 case SDL_JOYDEVICEREMOVED:
267 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
268 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
269 break;
270 case SDL_JOYDEVICEADDED:
271 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
272 InitJoystick(event.jdevice.which);
273 break;
274 }
275}
276
277void CloseSDLJoysticks() {
278 std::lock_guard<std::mutex> lock(joystick_map_mutex);
279 joystick_map.clear();
280}
281
282void PollLoop() {
283 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
284 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
285 return;
286 }
287
288 SDL_Event event;
289 while (initialized) {
290 // Wait for 10 ms or until an event happens
291 if (SDL_WaitEventTimeout(&event, 10)) {
292 // Don't handle the event if we are configuring
293 if (polling) {
294 event_queue.Push(event);
295 } else {
296 HandleGameControllerEvent(event);
297 }
298 }
299 }
300 CloseSDLJoysticks();
301 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
302}
303
82class SDLButton final : public Input::ButtonDevice { 304class SDLButton final : public Input::ButtonDevice {
83public: 305public:
84 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) 306 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
@@ -144,22 +366,14 @@ private:
144 int axis_y; 366 int axis_y;
145}; 367};
146 368
147static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) {
148 std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock();
149 if (!joystick) {
150 joystick = std::make_shared<SDLJoystick>(joystick_index);
151 joystick_list[joystick_index] = joystick;
152 }
153 return joystick;
154}
155
156/// A button device factory that creates button devices from SDL joystick 369/// A button device factory that creates button devices from SDL joystick
157class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 370class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
158public: 371public:
159 /** 372 /**
160 * Creates a button device from a joystick button 373 * Creates a button device from a joystick button
161 * @param params contains parameters for creating the device: 374 * @param params contains parameters for creating the device:
162 * - "joystick": the index of the joystick to bind 375 * - "guid": the guid of the joystick to bind
376 * - "port": the nth joystick of the same type to bind
163 * - "button"(optional): the index of the button to bind 377 * - "button"(optional): the index of the button to bind
164 * - "hat"(optional): the index of the hat to bind as direction buttons 378 * - "hat"(optional): the index of the hat to bind as direction buttons
165 * - "axis"(optional): the index of the axis to bind 379 * - "axis"(optional): the index of the axis to bind
@@ -167,12 +381,15 @@ public:
167 * "down", "left" or "right" 381 * "down", "left" or "right"
168 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is 382 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
169 * triggered if the axis value crosses 383 * triggered if the axis value crosses
170 * - "direction"(only used for axis): "+" means the button is triggered when the axis value 384 * - "direction"(only used for axis): "+" means the button is triggered when the axis
171 * is greater than the threshold; "-" means the button is triggered when the axis value 385 * value is greater than the threshold; "-" means the button is triggered when the axis
172 * is smaller than the threshold 386 * value is smaller than the threshold
173 */ 387 */
174 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { 388 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
175 const int joystick_index = params.Get("joystick", 0); 389 const std::string guid = params.Get("guid", "0");
390 const int port = params.Get("port", 0);
391
392 auto joystick = GetSDLJoystickByGUID(guid, port);
176 393
177 if (params.Has("hat")) { 394 if (params.Has("hat")) {
178 const int hat = params.Get("hat", 0); 395 const int hat = params.Get("hat", 0);
@@ -189,8 +406,9 @@ public:
189 } else { 406 } else {
190 direction = 0; 407 direction = 0;
191 } 408 }
192 return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat, 409 // This is necessary so accessing GetHat with hat won't crash
193 direction); 410 joystick->SetHat(hat, SDL_HAT_CENTERED);
411 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
194 } 412 }
195 413
196 if (params.Has("axis")) { 414 if (params.Has("axis")) {
@@ -206,12 +424,15 @@ public:
206 trigger_if_greater = true; 424 trigger_if_greater = true;
207 LOG_ERROR(Input, "Unknown direction '{}'", direction_name); 425 LOG_ERROR(Input, "Unknown direction '{}'", direction_name);
208 } 426 }
209 return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, 427 // This is necessary so accessing GetAxis with axis won't crash
210 trigger_if_greater); 428 joystick->SetAxis(axis, 0);
429 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
211 } 430 }
212 431
213 const int button = params.Get("button", 0); 432 const int button = params.Get("button", 0);
214 return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); 433 // This is necessary so accessing GetButton with button won't crash
434 joystick->SetButton(button, false);
435 return std::make_unique<SDLButton>(joystick, button);
215 } 436 }
216}; 437};
217 438
@@ -221,27 +442,32 @@ public:
221 /** 442 /**
222 * Creates analog device from joystick axes 443 * Creates analog device from joystick axes
223 * @param params contains parameters for creating the device: 444 * @param params contains parameters for creating the device:
224 * - "joystick": the index of the joystick to bind 445 * - "guid": the guid of the joystick to bind
446 * - "port": the nth joystick of the same type
225 * - "axis_x": the index of the axis to be bind as x-axis 447 * - "axis_x": the index of the axis to be bind as x-axis
226 * - "axis_y": the index of the axis to be bind as y-axis 448 * - "axis_y": the index of the axis to be bind as y-axis
227 */ 449 */
228 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { 450 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
229 const int joystick_index = params.Get("joystick", 0); 451 const std::string guid = params.Get("guid", "0");
452 const int port = params.Get("port", 0);
230 const int axis_x = params.Get("axis_x", 0); 453 const int axis_x = params.Get("axis_x", 0);
231 const int axis_y = params.Get("axis_y", 1); 454 const int axis_y = params.Get("axis_y", 1);
232 return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y); 455
456 auto joystick = GetSDLJoystickByGUID(guid, port);
457
458 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
459 joystick->SetAxis(axis_x, 0);
460 joystick->SetAxis(axis_y, 0);
461 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y);
233 } 462 }
234}; 463};
235 464
236void Init() { 465void Init() {
237 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { 466 using namespace Input;
238 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 467 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
239 } else { 468 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
240 using namespace Input; 469 polling = false;
241 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); 470 initialized = true;
242 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
243 initialized = true;
244 }
245} 471}
246 472
247void Shutdown() { 473void Shutdown() {
@@ -249,30 +475,17 @@ void Shutdown() {
249 using namespace Input; 475 using namespace Input;
250 UnregisterFactory<ButtonDevice>("sdl"); 476 UnregisterFactory<ButtonDevice>("sdl");
251 UnregisterFactory<AnalogDevice>("sdl"); 477 UnregisterFactory<AnalogDevice>("sdl");
252 SDL_QuitSubSystem(SDL_INIT_JOYSTICK); 478 initialized = false;
253 } 479 }
254} 480}
255 481
256/**
257 * This function converts a joystick ID used in SDL events to the device index. This is necessary
258 * because Citra opens joysticks using their indices, not their IDs.
259 */
260static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
261 int num_joysticks = SDL_NumJoysticks();
262 for (int i = 0; i < num_joysticks; i++) {
263 auto joystick = GetJoystick(i);
264 if (joystick->GetJoystickID() == id) {
265 return i;
266 }
267 }
268 return -1;
269}
270
271Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { 482Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
272 Common::ParamPackage params({{"engine", "sdl"}}); 483 Common::ParamPackage params({{"engine", "sdl"}});
273 switch (event.type) { 484 switch (event.type) {
274 case SDL_JOYAXISMOTION: 485 case SDL_JOYAXISMOTION: {
275 params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which)); 486 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
487 params.Set("port", joystick->GetPort());
488 params.Set("guid", joystick->GetGUID());
276 params.Set("axis", event.jaxis.axis); 489 params.Set("axis", event.jaxis.axis);
277 if (event.jaxis.value > 0) { 490 if (event.jaxis.value > 0) {
278 params.Set("direction", "+"); 491 params.Set("direction", "+");
@@ -282,12 +495,18 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
282 params.Set("threshold", "-0.5"); 495 params.Set("threshold", "-0.5");
283 } 496 }
284 break; 497 break;
285 case SDL_JOYBUTTONUP: 498 }
286 params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which)); 499 case SDL_JOYBUTTONUP: {
500 auto joystick = GetSDLJoystickBySDLID(event.jbutton.which);
501 params.Set("port", joystick->GetPort());
502 params.Set("guid", joystick->GetGUID());
287 params.Set("button", event.jbutton.button); 503 params.Set("button", event.jbutton.button);
288 break; 504 break;
289 case SDL_JOYHATMOTION: 505 }
290 params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which)); 506 case SDL_JOYHATMOTION: {
507 auto joystick = GetSDLJoystickBySDLID(event.jhat.which);
508 params.Set("port", joystick->GetPort());
509 params.Set("guid", joystick->GetGUID());
291 params.Set("hat", event.jhat.hat); 510 params.Set("hat", event.jhat.hat);
292 switch (event.jhat.value) { 511 switch (event.jhat.value) {
293 case SDL_HAT_UP: 512 case SDL_HAT_UP:
@@ -307,6 +526,7 @@ Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
307 } 526 }
308 break; 527 break;
309 } 528 }
529 }
310 return params; 530 return params;
311} 531}
312 532
@@ -315,31 +535,20 @@ namespace Polling {
315class SDLPoller : public InputCommon::Polling::DevicePoller { 535class SDLPoller : public InputCommon::Polling::DevicePoller {
316public: 536public:
317 void Start() override { 537 void Start() override {
318 // SDL joysticks must be opened, otherwise they don't generate events 538 event_queue.Clear();
319 SDL_JoystickUpdate(); 539 polling = true;
320 int num_joysticks = SDL_NumJoysticks();
321 for (int i = 0; i < num_joysticks; i++) {
322 joysticks_opened.emplace_back(GetJoystick(i));
323 }
324 // Empty event queue to get rid of old events. citra-qt doesn't use the queue
325 SDL_Event dummy;
326 while (SDL_PollEvent(&dummy)) {
327 }
328 } 540 }
329 541
330 void Stop() override { 542 void Stop() override {
331 joysticks_opened.clear(); 543 polling = false;
332 } 544 }
333
334private:
335 std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
336}; 545};
337 546
338class SDLButtonPoller final : public SDLPoller { 547class SDLButtonPoller final : public SDLPoller {
339public: 548public:
340 Common::ParamPackage GetNextInput() override { 549 Common::ParamPackage GetNextInput() override {
341 SDL_Event event; 550 SDL_Event event;
342 while (SDL_PollEvent(&event)) { 551 while (event_queue.Pop(event)) {
343 switch (event.type) { 552 switch (event.type) {
344 case SDL_JOYAXISMOTION: 553 case SDL_JOYAXISMOTION:
345 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 554 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
@@ -367,7 +576,7 @@ public:
367 576
368 Common::ParamPackage GetNextInput() override { 577 Common::ParamPackage GetNextInput() override {
369 SDL_Event event; 578 SDL_Event event;
370 while (SDL_PollEvent(&event)) { 579 while (event_queue.Pop(event)) {
371 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 580 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
372 continue; 581 continue;
373 } 582 }
@@ -384,8 +593,10 @@ public:
384 } 593 }
385 Common::ParamPackage params; 594 Common::ParamPackage params;
386 if (analog_xaxis != -1 && analog_yaxis != -1) { 595 if (analog_xaxis != -1 && analog_yaxis != -1) {
596 auto joystick = GetSDLJoystickBySDLID(event.jaxis.which);
387 params.Set("engine", "sdl"); 597 params.Set("engine", "sdl");
388 params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick)); 598 params.Set("port", joystick->GetPort());
599 params.Set("guid", joystick->GetGUID());
389 params.Set("axis_x", analog_xaxis); 600 params.Set("axis_x", analog_xaxis);
390 params.Set("axis_y", analog_yaxis); 601 params.Set("axis_y", analog_yaxis);
391 analog_xaxis = -1; 602 analog_xaxis = -1;
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 7934099d4..0206860d3 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -28,6 +28,15 @@ void Init();
28/// Unresisters SDL device factories and shut them down. 28/// Unresisters SDL device factories and shut them down.
29void Shutdown(); 29void Shutdown();
30 30
31/// Needs to be called before SDL_QuitSubSystem.
32void CloseSDLJoysticks();
33
34/// Handle SDL_Events for joysticks from SDL_PollEvent
35void HandleGameControllerEvent(const SDL_Event& event);
36
37/// A Loop that calls HandleGameControllerEvent until Shutdown is called
38void PollLoop();
39
31/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
32Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); 41Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
33 42
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 4d74bb395..37f09ce5f 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,16 +1,15 @@
1add_executable(tests 1add_executable(tests
2 common/param_package.cpp 2 common/param_package.cpp
3 common/ring_buffer.cpp
3 core/arm/arm_test_common.cpp 4 core/arm/arm_test_common.cpp
4 core/arm/arm_test_common.h 5 core/arm/arm_test_common.h
5 core/core_timing.cpp 6 core/core_timing.cpp
6 glad.cpp
7 tests.cpp 7 tests.cpp
8) 8)
9 9
10create_target_directory_groups(tests) 10create_target_directory_groups(tests)
11 11
12target_link_libraries(tests PRIVATE common core) 12target_link_libraries(tests PRIVATE common core)
13target_link_libraries(tests PRIVATE glad) # To support linker work-around
14target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) 13target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
15 14
16add_test(NAME tests COMMAND tests) 15add_test(NAME tests COMMAND tests)
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
new file mode 100644
index 000000000..c883c4d56
--- /dev/null
+++ b/src/tests/common/ring_buffer.cpp
@@ -0,0 +1,130 @@
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 <cstddef>
8#include <numeric>
9#include <thread>
10#include <vector>
11#include <catch2/catch.hpp>
12#include "common/ring_buffer.h"
13
14namespace Common {
15
16TEST_CASE("RingBuffer: Basic Tests", "[common]") {
17 RingBuffer<char, 4, 1> buf;
18
19 // Pushing values into a ring buffer with space should succeed.
20 for (std::size_t i = 0; i < 4; i++) {
21 const char elem = static_cast<char>(i);
22 const std::size_t count = buf.Push(&elem, 1);
23 REQUIRE(count == 1);
24 }
25
26 REQUIRE(buf.Size() == 4);
27
28 // Pushing values into a full ring buffer should fail.
29 {
30 const char elem = static_cast<char>(42);
31 const std::size_t count = buf.Push(&elem, 1);
32 REQUIRE(count == 0);
33 }
34
35 REQUIRE(buf.Size() == 4);
36
37 // Popping multiple values from a ring buffer with values should succeed.
38 {
39 const std::vector<char> popped = buf.Pop(2);
40 REQUIRE(popped.size() == 2);
41 REQUIRE(popped[0] == 0);
42 REQUIRE(popped[1] == 1);
43 }
44
45 REQUIRE(buf.Size() == 2);
46
47 // Popping a single value from a ring buffer with values should succeed.
48 {
49 const std::vector<char> popped = buf.Pop(1);
50 REQUIRE(popped.size() == 1);
51 REQUIRE(popped[0] == 2);
52 }
53
54 REQUIRE(buf.Size() == 1);
55
56 // Pushing more values than space available should partially suceed.
57 {
58 std::vector<char> to_push(6);
59 std::iota(to_push.begin(), to_push.end(), 88);
60 const std::size_t count = buf.Push(to_push);
61 REQUIRE(count == 3);
62 }
63
64 REQUIRE(buf.Size() == 4);
65
66 // Doing an unlimited pop should pop all values.
67 {
68 const std::vector<char> popped = buf.Pop();
69 REQUIRE(popped.size() == 4);
70 REQUIRE(popped[0] == 3);
71 REQUIRE(popped[1] == 88);
72 REQUIRE(popped[2] == 89);
73 REQUIRE(popped[3] == 90);
74 }
75
76 REQUIRE(buf.Size() == 0);
77}
78
79TEST_CASE("RingBuffer: Threaded Test", "[common]") {
80 RingBuffer<char, 4, 2> buf;
81 const char seed = 42;
82 const std::size_t count = 1000000;
83 std::size_t full = 0;
84 std::size_t empty = 0;
85
86 const auto next_value = [](std::array<char, 2>& value) {
87 value[0] += 1;
88 value[1] += 2;
89 };
90
91 std::thread producer{[&] {
92 std::array<char, 2> value = {seed, seed};
93 std::size_t i = 0;
94 while (i < count) {
95 if (const std::size_t c = buf.Push(&value[0], 1); c > 0) {
96 REQUIRE(c == 1);
97 i++;
98 next_value(value);
99 } else {
100 full++;
101 std::this_thread::yield();
102 }
103 }
104 }};
105
106 std::thread consumer{[&] {
107 std::array<char, 2> value = {seed, seed};
108 std::size_t i = 0;
109 while (i < count) {
110 if (const std::vector<char> v = buf.Pop(1); v.size() > 0) {
111 REQUIRE(v.size() == 2);
112 REQUIRE(v[0] == value[0]);
113 REQUIRE(v[1] == value[1]);
114 i++;
115 next_value(value);
116 } else {
117 empty++;
118 std::this_thread::yield();
119 }
120 }
121 }};
122
123 producer.join();
124 consumer.join();
125
126 REQUIRE(buf.Size() == 0);
127 printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty);
128}
129
130} // namespace Common
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 038d57b3a..7c69fc26e 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -87,11 +87,11 @@ boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
87 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; 87 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32;
88} 88}
89 89
90bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) { 90bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) {
91 VAddr addr = src_addr; 91 VAddr addr = src_addr;
92 u8* data = static_cast<u8*>(dest_buffer); 92 u8* data = static_cast<u8*>(dest_buffer);
93 93
94 for (size_t i = 0; i < size; i++, addr++, data++) { 94 for (std::size_t i = 0; i < size; i++, addr++, data++) {
95 *data = *Read8(addr); 95 *data = *Read8(addr);
96 } 96 }
97 97
@@ -126,11 +126,12 @@ bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) {
126 return true; 126 return true;
127} 127}
128 128
129bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) { 129bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer,
130 std::size_t size) {
130 VAddr addr = dest_addr; 131 VAddr addr = dest_addr;
131 const u8* data = static_cast<const u8*>(src_buffer); 132 const u8* data = static_cast<const u8*>(src_buffer);
132 133
133 for (size_t i = 0; i < size; i++, addr++, data++) { 134 for (std::size_t i = 0; i < size; i++, addr++, data++) {
134 env->write_records.emplace_back(8, addr, *data); 135 env->write_records.emplace_back(8, addr, *data);
135 if (env->mutable_memory) 136 if (env->mutable_memory)
136 env->SetMemory8(addr, *data); 137 env->SetMemory8(addr, *data);
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index e4b6df194..5de8dab4e 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -19,8 +19,8 @@ struct PageTable;
19namespace ArmTests { 19namespace ArmTests {
20 20
21struct WriteRecord { 21struct WriteRecord {
22 WriteRecord(size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} 22 WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
23 size_t size; 23 std::size_t size;
24 VAddr addr; 24 VAddr addr;
25 u64 data; 25 u64 data;
26 bool operator==(const WriteRecord& o) const { 26 bool operator==(const WriteRecord& o) const {
@@ -71,14 +71,14 @@ private:
71 boost::optional<u32> Read32(VAddr addr) override; 71 boost::optional<u32> Read32(VAddr addr) override;
72 boost::optional<u64> Read64(VAddr addr) override; 72 boost::optional<u64> Read64(VAddr addr) override;
73 73
74 bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) override; 74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
75 75
76 bool Write8(VAddr addr, u8 data) override; 76 bool Write8(VAddr addr, u8 data) override;
77 bool Write16(VAddr addr, u16 data) override; 77 bool Write16(VAddr addr, u16 data) override;
78 bool Write32(VAddr addr, u32 data) override; 78 bool Write32(VAddr addr, u32 data) override;
79 bool Write64(VAddr addr, u64 data) override; 79 bool Write64(VAddr addr, u64 data) override;
80 80
81 bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) override; 81 bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override;
82 82
83 std::unordered_map<VAddr, u8> data; 83 std::unordered_map<VAddr, u8> data;
84 }; 84 };
diff --git a/src/tests/glad.cpp b/src/tests/glad.cpp
deleted file mode 100644
index 1797c0e3d..000000000
--- a/src/tests/glad.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch2/catch.hpp>
6#include <glad/glad.h>
7
8// This is not an actual test, but a work-around for issue #2183.
9// If tests uses functions in core but doesn't explicitly use functions in glad, the linker of macOS
10// will error about undefined references from video_core to glad. So we explicitly use a glad
11// function here to shut up the linker.
12TEST_CASE("glad fake test", "[dummy]") {
13 REQUIRE(&gladLoadGL != nullptr);
14}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index aa5bc3bbe..f5ae57039 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -5,6 +5,8 @@ add_library(video_core STATIC
5 debug_utils/debug_utils.h 5 debug_utils/debug_utils.h
6 engines/fermi_2d.cpp 6 engines/fermi_2d.cpp
7 engines/fermi_2d.h 7 engines/fermi_2d.h
8 engines/kepler_memory.cpp
9 engines/kepler_memory.h
8 engines/maxwell_3d.cpp 10 engines/maxwell_3d.cpp
9 engines/maxwell_3d.h 11 engines/maxwell_3d.h
10 engines/maxwell_compute.cpp 12 engines/maxwell_compute.cpp
@@ -12,6 +14,7 @@ add_library(video_core STATIC
12 engines/maxwell_dma.cpp 14 engines/maxwell_dma.cpp
13 engines/maxwell_dma.h 15 engines/maxwell_dma.h
14 engines/shader_bytecode.h 16 engines/shader_bytecode.h
17 engines/shader_header.h
15 gpu.cpp 18 gpu.cpp
16 gpu.h 19 gpu.h
17 macro_interpreter.cpp 20 macro_interpreter.cpp
@@ -22,6 +25,8 @@ add_library(video_core STATIC
22 rasterizer_interface.h 25 rasterizer_interface.h
23 renderer_base.cpp 26 renderer_base.cpp
24 renderer_base.h 27 renderer_base.h
28 renderer_opengl/gl_buffer_cache.cpp
29 renderer_opengl/gl_buffer_cache.h
25 renderer_opengl/gl_rasterizer.cpp 30 renderer_opengl/gl_rasterizer.cpp
26 renderer_opengl/gl_rasterizer.h 31 renderer_opengl/gl_rasterizer.h
27 renderer_opengl/gl_rasterizer_cache.cpp 32 renderer_opengl/gl_rasterizer_cache.cpp
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index dc485e811..f1aa6091b 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -14,6 +14,7 @@
14#include "core/tracer/recorder.h" 14#include "core/tracer/recorder.h"
15#include "video_core/command_processor.h" 15#include "video_core/command_processor.h"
16#include "video_core/engines/fermi_2d.h" 16#include "video_core/engines/fermi_2d.h"
17#include "video_core/engines/kepler_memory.h"
17#include "video_core/engines/maxwell_3d.h" 18#include "video_core/engines/maxwell_3d.h"
18#include "video_core/engines/maxwell_compute.h" 19#include "video_core/engines/maxwell_compute.h"
19#include "video_core/engines/maxwell_dma.h" 20#include "video_core/engines/maxwell_dma.h"
@@ -28,98 +29,109 @@ enum class BufferMethods {
28 CountBufferMethods = 0x40, 29 CountBufferMethods = 0x40,
29}; 30};
30 31
31void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) { 32MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB(128, 128, 192));
32 LOG_TRACE(HW_GPU,
33 "Processing method {:08X} on subchannel {} value "
34 "{:08X} remaining params {}",
35 method, subchannel, value, remaining_params);
36
37 if (method == static_cast<u32>(BufferMethods::BindObject)) {
38 // Bind the current subchannel to the desired engine id.
39 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
40 bound_engines[subchannel] = static_cast<EngineID>(value);
41 return;
42 }
43 33
44 if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { 34void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
45 // TODO(Subv): Research and implement these methods. 35 MICROPROFILE_SCOPE(ProcessCommandLists);
46 LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
47 return;
48 }
49 36
50 ASSERT(bound_engines.find(subchannel) != bound_engines.end()); 37 auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) {
51 38 LOG_TRACE(HW_GPU,
52 const EngineID engine = bound_engines[subchannel]; 39 "Processing method {:08X} on subchannel {} value "
53 40 "{:08X} remaining params {}",
54 switch (engine) { 41 method, subchannel, value, remaining_params);
55 case EngineID::FERMI_TWOD_A:
56 fermi_2d->WriteReg(method, value);
57 break;
58 case EngineID::MAXWELL_B:
59 maxwell_3d->WriteReg(method, value, remaining_params);
60 break;
61 case EngineID::MAXWELL_COMPUTE_B:
62 maxwell_compute->WriteReg(method, value);
63 break;
64 case EngineID::MAXWELL_DMA_COPY_A:
65 maxwell_dma->WriteReg(method, value);
66 break;
67 default:
68 UNIMPLEMENTED_MSG("Unimplemented engine");
69 }
70}
71 42
72void GPU::ProcessCommandList(GPUVAddr address, u32 size) { 43 ASSERT(subchannel < bound_engines.size());
73 const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); 44
74 VAddr current_addr = *head_address; 45 if (method == static_cast<u32>(BufferMethods::BindObject)) {
75 while (current_addr < *head_address + size * sizeof(CommandHeader)) { 46 // Bind the current subchannel to the desired engine id.
76 const CommandHeader header = {Memory::Read32(current_addr)}; 47 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
77 current_addr += sizeof(u32); 48 bound_engines[subchannel] = static_cast<EngineID>(value);
78 49 return;
79 switch (header.mode.Value()) {
80 case SubmissionMode::IncreasingOld:
81 case SubmissionMode::Increasing: {
82 // Increase the method value with each argument.
83 for (unsigned i = 0; i < header.arg_count; ++i) {
84 WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
85 header.arg_count - i - 1);
86 current_addr += sizeof(u32);
87 }
88 break;
89 } 50 }
90 case SubmissionMode::NonIncreasingOld: 51
91 case SubmissionMode::NonIncreasing: { 52 if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
92 // Use the same method value for all arguments. 53 // TODO(Subv): Research and implement these methods.
93 for (unsigned i = 0; i < header.arg_count; ++i) { 54 LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
94 WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), 55 return;
95 header.arg_count - i - 1); 56 }
96 current_addr += sizeof(u32); 57
97 } 58 const EngineID engine = bound_engines[subchannel];
59
60 switch (engine) {
61 case EngineID::FERMI_TWOD_A:
62 fermi_2d->WriteReg(method, value);
63 break;
64 case EngineID::MAXWELL_B:
65 maxwell_3d->WriteReg(method, value, remaining_params);
98 break; 66 break;
67 case EngineID::MAXWELL_COMPUTE_B:
68 maxwell_compute->WriteReg(method, value);
69 break;
70 case EngineID::MAXWELL_DMA_COPY_A:
71 maxwell_dma->WriteReg(method, value);
72 break;
73 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
74 kepler_memory->WriteReg(method, value);
75 break;
76 default:
77 UNIMPLEMENTED_MSG("Unimplemented engine");
99 } 78 }
100 case SubmissionMode::IncreaseOnce: { 79 };
101 ASSERT(header.arg_count.Value() >= 1);
102 80
103 // Use the original method for the first argument and then the next method for all other 81 for (auto entry : commands) {
104 // arguments. 82 Tegra::GPUVAddr address = entry.Address();
105 WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), 83 u32 size = entry.sz;
106 header.arg_count - 1); 84 const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
85 VAddr current_addr = *head_address;
86 while (current_addr < *head_address + size * sizeof(CommandHeader)) {
87 const CommandHeader header = {Memory::Read32(current_addr)};
107 current_addr += sizeof(u32); 88 current_addr += sizeof(u32);
108 89
109 for (unsigned i = 1; i < header.arg_count; ++i) { 90 switch (header.mode.Value()) {
110 WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), 91 case SubmissionMode::IncreasingOld:
111 header.arg_count - i - 1); 92 case SubmissionMode::Increasing: {
93 // Increase the method value with each argument.
94 for (unsigned i = 0; i < header.arg_count; ++i) {
95 WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
96 header.arg_count - i - 1);
97 current_addr += sizeof(u32);
98 }
99 break;
100 }
101 case SubmissionMode::NonIncreasingOld:
102 case SubmissionMode::NonIncreasing: {
103 // Use the same method value for all arguments.
104 for (unsigned i = 0; i < header.arg_count; ++i) {
105 WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
106 header.arg_count - i - 1);
107 current_addr += sizeof(u32);
108 }
109 break;
110 }
111 case SubmissionMode::IncreaseOnce: {
112 ASSERT(header.arg_count.Value() >= 1);
113
114 // Use the original method for the first argument and then the next method for all
115 // other arguments.
116 WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
117 header.arg_count - 1);
112 current_addr += sizeof(u32); 118 current_addr += sizeof(u32);
119
120 for (unsigned i = 1; i < header.arg_count; ++i) {
121 WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
122 header.arg_count - i - 1);
123 current_addr += sizeof(u32);
124 }
125 break;
126 }
127 case SubmissionMode::Inline: {
128 // The register value is stored in the bits 16-28 as an immediate
129 WriteReg(header.method, header.subchannel, header.inline_data, 0);
130 break;
131 }
132 default:
133 UNIMPLEMENTED();
113 } 134 }
114 break;
115 }
116 case SubmissionMode::Inline: {
117 // The register value is stored in the bits 16-28 as an immediate
118 WriteReg(header.method, header.subchannel, header.inline_data, 0);
119 break;
120 }
121 default:
122 UNIMPLEMENTED();
123 } 135 }
124 } 136 }
125} 137}
diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h
index a01153e0b..bd766e77a 100644
--- a/src/video_core/command_processor.h
+++ b/src/video_core/command_processor.h
@@ -7,6 +7,7 @@
7#include <type_traits> 7#include <type_traits>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/memory_manager.h"
10 11
11namespace Tegra { 12namespace Tegra {
12 13
@@ -19,6 +20,22 @@ enum class SubmissionMode : u32 {
19 IncreaseOnce = 5 20 IncreaseOnce = 5
20}; 21};
21 22
23struct CommandListHeader {
24 u32 entry0; // gpu_va_lo
25 union {
26 u32 entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F)
27 BitField<0, 8, u32> gpu_va_hi;
28 BitField<8, 2, u32> unk1;
29 BitField<10, 21, u32> sz;
30 BitField<31, 1, u32> unk2;
31 };
32
33 GPUVAddr Address() const {
34 return (static_cast<GPUVAddr>(gpu_va_hi) << 32) | entry0;
35 }
36};
37static_assert(sizeof(CommandListHeader) == 8, "CommandListHeader is incorrect size");
38
22union CommandHeader { 39union CommandHeader {
23 u32 hex; 40 u32 hex;
24 41
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index dcf9ef8b9..021b83eaa 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -26,7 +26,7 @@ public:
26 void WriteReg(u32 method, u32 value); 26 void WriteReg(u32 method, u32 value);
27 27
28 struct Regs { 28 struct Regs {
29 static constexpr size_t NUM_REGS = 0x258; 29 static constexpr std::size_t NUM_REGS = 0x258;
30 30
31 struct Surface { 31 struct Surface {
32 RenderTargetFormat format; 32 RenderTargetFormat format;
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
new file mode 100644
index 000000000..66ae6332d
--- /dev/null
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -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#include "common/logging/log.h"
6#include "core/memory.h"
7#include "video_core/engines/kepler_memory.h"
8
9namespace Tegra::Engines {
10
11KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
12KeplerMemory::~KeplerMemory() = default;
13
14void KeplerMemory::WriteReg(u32 method, u32 value) {
15 ASSERT_MSG(method < Regs::NUM_REGS,
16 "Invalid KeplerMemory register, increase the size of the Regs structure");
17
18 regs.reg_array[method] = value;
19
20 switch (method) {
21 case KEPLERMEMORY_REG_INDEX(exec): {
22 state.write_offset = 0;
23 break;
24 }
25 case KEPLERMEMORY_REG_INDEX(data): {
26 ProcessData(value);
27 break;
28 }
29 }
30}
31
32void KeplerMemory::ProcessData(u32 data) {
33 ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported");
34 ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0);
35
36 GPUVAddr address = regs.dest.Address();
37 VAddr dest_address =
38 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
39
40 Memory::Write32(dest_address, data);
41
42 state.write_offset++;
43}
44
45} // namespace Tegra::Engines
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
new file mode 100644
index 000000000..b0d0078cf
--- /dev/null
+++ b/src/video_core/engines/kepler_memory.h
@@ -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#pragma once
6
7#include <array>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13
14namespace Tegra::Engines {
15
16#define KEPLERMEMORY_REG_INDEX(field_name) \
17 (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32))
18
19class KeplerMemory final {
20public:
21 KeplerMemory(MemoryManager& memory_manager);
22 ~KeplerMemory();
23
24 /// Write the value to the register identified by method.
25 void WriteReg(u32 method, u32 value);
26
27 struct Regs {
28 static constexpr size_t NUM_REGS = 0x7F;
29
30 union {
31 struct {
32 INSERT_PADDING_WORDS(0x60);
33
34 u32 line_length_in;
35 u32 line_count;
36
37 struct {
38 u32 address_high;
39 u32 address_low;
40 u32 pitch;
41 u32 block_dimensions;
42 u32 width;
43 u32 height;
44 u32 depth;
45 u32 z;
46 u32 x;
47 u32 y;
48
49 GPUVAddr Address() const {
50 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
51 address_low);
52 }
53 } dest;
54
55 struct {
56 union {
57 BitField<0, 1, u32> linear;
58 };
59 } exec;
60
61 u32 data;
62
63 INSERT_PADDING_WORDS(0x11);
64 };
65 std::array<u32, NUM_REGS> reg_array;
66 };
67 } regs{};
68
69 struct {
70 u32 write_offset = 0;
71 } state{};
72
73private:
74 MemoryManager& memory_manager;
75
76 void ProcessData(u32 data);
77};
78
79#define ASSERT_REG_POSITION(field_name, position) \
80 static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \
81 "Field " #field_name " has invalid position")
82
83ASSERT_REG_POSITION(line_length_in, 0x60);
84ASSERT_REG_POSITION(line_count, 0x61);
85ASSERT_REG_POSITION(dest, 0x62);
86ASSERT_REG_POSITION(exec, 0x6C);
87ASSERT_REG_POSITION(data, 0x6D);
88#undef ASSERT_REG_POSITION
89
90} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 68ff1e86b..8afd26fe9 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -5,6 +5,7 @@
5#include <cinttypes> 5#include <cinttypes>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/core_timing.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/debug_utils/debug_utils.h" 10#include "video_core/debug_utils/debug_utils.h"
10#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
@@ -134,8 +135,6 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
134 break; 135 break;
135 } 136 }
136 137
137 rasterizer.NotifyMaxwellRegisterChanged(method);
138
139 if (debug_context) { 138 if (debug_context) {
140 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); 139 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
141 } 140 }
@@ -194,8 +193,8 @@ void Maxwell3D::ProcessQueryGet() {
194 // wait queues. 193 // wait queues.
195 LongQueryResult query_result{}; 194 LongQueryResult query_result{};
196 query_result.value = result; 195 query_result.value = result;
197 // TODO(Subv): Generate a real GPU timestamp and write it here instead of 0 196 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
198 query_result.timestamp = 0; 197 query_result.timestamp = CoreTiming::GetTicks();
199 Memory::WriteBlock(*address, &query_result, sizeof(query_result)); 198 Memory::WriteBlock(*address, &query_result, sizeof(query_result));
200 } 199 }
201 break; 200 break;
@@ -249,8 +248,8 @@ void Maxwell3D::DrawArrays() {
249 248
250void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { 249void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
251 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. 250 // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
252 auto& shader = state.shader_stages[static_cast<size_t>(stage)]; 251 auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
253 auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)]; 252 auto& bind_data = regs.cb_bind[static_cast<std::size_t>(stage)];
254 253
255 auto& buffer = shader.const_buffers[bind_data.index]; 254 auto& buffer = shader.const_buffers[bind_data.index];
256 255
@@ -292,10 +291,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
292 tic_entry.header_version == Texture::TICHeaderVersion::Pitch, 291 tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
293 "TIC versions other than BlockLinear or Pitch are unimplemented"); 292 "TIC versions other than BlockLinear or Pitch are unimplemented");
294 293
295 ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) ||
296 (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap),
297 "Texture types other than Texture2D are unimplemented");
298
299 auto r_type = tic_entry.r_type.Value(); 294 auto r_type = tic_entry.r_type.Value();
300 auto g_type = tic_entry.g_type.Value(); 295 auto g_type = tic_entry.g_type.Value();
301 auto b_type = tic_entry.b_type.Value(); 296 auto b_type = tic_entry.b_type.Value();
@@ -321,14 +316,14 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
321std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { 316std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const {
322 std::vector<Texture::FullTextureInfo> textures; 317 std::vector<Texture::FullTextureInfo> textures;
323 318
324 auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)]; 319 auto& fragment_shader = state.shader_stages[static_cast<std::size_t>(stage)];
325 auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; 320 auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index];
326 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); 321 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
327 322
328 GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; 323 GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size;
329 324
330 // Offset into the texture constbuffer where the texture info begins. 325 // Offset into the texture constbuffer where the texture info begins.
331 static constexpr size_t TextureInfoOffset = 0x20; 326 static constexpr std::size_t TextureInfoOffset = 0x20;
332 327
333 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; 328 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
334 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { 329 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
@@ -365,8 +360,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
365 return textures; 360 return textures;
366} 361}
367 362
368Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { 363Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
369 auto& shader = state.shader_stages[static_cast<size_t>(stage)]; 364 std::size_t offset) const {
365 auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
370 auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; 366 auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index];
371 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); 367 ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
372 368
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 92bfda053..b81b0723d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -34,17 +34,17 @@ public:
34 /// Register structure of the Maxwell3D engine. 34 /// Register structure of the Maxwell3D engine.
35 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. 35 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
36 struct Regs { 36 struct Regs {
37 static constexpr size_t NUM_REGS = 0xE00; 37 static constexpr std::size_t NUM_REGS = 0xE00;
38 38
39 static constexpr size_t NumRenderTargets = 8; 39 static constexpr std::size_t NumRenderTargets = 8;
40 static constexpr size_t NumViewports = 16; 40 static constexpr std::size_t NumViewports = 16;
41 static constexpr size_t NumCBData = 16; 41 static constexpr std::size_t NumCBData = 16;
42 static constexpr size_t NumVertexArrays = 32; 42 static constexpr std::size_t NumVertexArrays = 32;
43 static constexpr size_t NumVertexAttributes = 32; 43 static constexpr std::size_t NumVertexAttributes = 32;
44 static constexpr size_t MaxShaderProgram = 6; 44 static constexpr std::size_t MaxShaderProgram = 6;
45 static constexpr size_t MaxShaderStage = 5; 45 static constexpr std::size_t MaxShaderStage = 5;
46 // Maximum number of const buffers per shader stage. 46 // Maximum number of const buffers per shader stage.
47 static constexpr size_t MaxConstBuffers = 18; 47 static constexpr std::size_t MaxConstBuffers = 18;
48 48
49 enum class QueryMode : u32 { 49 enum class QueryMode : u32 {
50 Write = 0, 50 Write = 0,
@@ -127,6 +127,7 @@ public:
127 BitField<21, 6, Size> size; 127 BitField<21, 6, Size> size;
128 BitField<27, 3, Type> type; 128 BitField<27, 3, Type> type;
129 BitField<31, 1, u32> bgra; 129 BitField<31, 1, u32> bgra;
130 u32 hex;
130 }; 131 };
131 132
132 u32 ComponentCount() const { 133 u32 ComponentCount() const {
@@ -262,6 +263,10 @@ public:
262 bool IsValid() const { 263 bool IsValid() const {
263 return size != Size::Invalid; 264 return size != Size::Invalid;
264 } 265 }
266
267 bool operator<(const VertexAttribute& other) const {
268 return hex < other.hex;
269 }
265 }; 270 };
266 271
267 enum class PrimitiveTopology : u32 { 272 enum class PrimitiveTopology : u32 {
@@ -438,9 +443,9 @@ public:
438 } 443 }
439 }; 444 };
440 445
441 bool IsShaderConfigEnabled(size_t index) const { 446 bool IsShaderConfigEnabled(std::size_t index) const {
442 // The VertexB is always enabled. 447 // The VertexB is always enabled.
443 if (index == static_cast<size_t>(Regs::ShaderProgram::VertexB)) { 448 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
444 return true; 449 return true;
445 } 450 }
446 return shader_config[index].enable != 0; 451 return shader_config[index].enable != 0;
@@ -528,7 +533,11 @@ public:
528 u32 stencil_back_mask; 533 u32 stencil_back_mask;
529 u32 stencil_back_func_mask; 534 u32 stencil_back_func_mask;
530 535
531 INSERT_PADDING_WORDS(0x20); 536 INSERT_PADDING_WORDS(0x13);
537
538 u32 rt_separate_frag_data;
539
540 INSERT_PADDING_WORDS(0xC);
532 541
533 struct { 542 struct {
534 u32 address_high; 543 u32 address_high;
@@ -545,14 +554,29 @@ public:
545 554
546 INSERT_PADDING_WORDS(0x5B); 555 INSERT_PADDING_WORDS(0x5B);
547 556
548 VertexAttribute vertex_attrib_format[NumVertexAttributes]; 557 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
549 558
550 INSERT_PADDING_WORDS(0xF); 559 INSERT_PADDING_WORDS(0xF);
551 560
552 struct { 561 struct {
553 union { 562 union {
554 BitField<0, 4, u32> count; 563 BitField<0, 4, u32> count;
564 BitField<4, 3, u32> map_0;
565 BitField<7, 3, u32> map_1;
566 BitField<10, 3, u32> map_2;
567 BitField<13, 3, u32> map_3;
568 BitField<16, 3, u32> map_4;
569 BitField<19, 3, u32> map_5;
570 BitField<22, 3, u32> map_6;
571 BitField<25, 3, u32> map_7;
555 }; 572 };
573
574 u32 GetMap(std::size_t index) const {
575 const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3,
576 map_4, map_5, map_6, map_7};
577 ASSERT(index < maps.size());
578 return maps[index];
579 }
556 } rt_control; 580 } rt_control;
557 581
558 INSERT_PADDING_WORDS(0x2); 582 INSERT_PADDING_WORDS(0x2);
@@ -901,7 +925,7 @@ public:
901 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; 925 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const;
902 926
903 /// Returns the texture information for a specific texture in a specific shader stage. 927 /// Returns the texture information for a specific texture in a specific shader stage.
904 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; 928 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
905 929
906private: 930private:
907 VideoCore::RasterizerInterface& rasterizer; 931 VideoCore::RasterizerInterface& rasterizer;
@@ -963,8 +987,9 @@ ASSERT_REG_POSITION(clear_stencil, 0x368);
963ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 987ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
964ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 988ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
965ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 989ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
990ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
966ASSERT_REG_POSITION(zeta, 0x3F8); 991ASSERT_REG_POSITION(zeta, 0x3F8);
967ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); 992ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
968ASSERT_REG_POSITION(rt_control, 0x487); 993ASSERT_REG_POSITION(rt_control, 0x487);
969ASSERT_REG_POSITION(zeta_width, 0x48a); 994ASSERT_REG_POSITION(zeta_width, 0x48a);
970ASSERT_REG_POSITION(zeta_height, 0x48b); 995ASSERT_REG_POSITION(zeta_height, 0x48b);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 6e740713f..aa7481b8c 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -41,7 +41,6 @@ void MaxwellDMA::HandleCopy() {
41 41
42 // TODO(Subv): Perform more research and implement all features of this engine. 42 // TODO(Subv): Perform more research and implement all features of this engine.
43 ASSERT(regs.exec.enable_swizzle == 0); 43 ASSERT(regs.exec.enable_swizzle == 0);
44 ASSERT(regs.exec.enable_2d == 1);
45 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 44 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
46 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); 45 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
47 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); 46 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
@@ -51,10 +50,19 @@ void MaxwellDMA::HandleCopy() {
51 ASSERT(regs.dst_params.pos_y == 0); 50 ASSERT(regs.dst_params.pos_y == 0);
52 51
53 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { 52 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
54 Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count); 53 std::size_t copy_size = regs.x_count;
54
55 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
56 // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count).
57 if (regs.exec.enable_2d) {
58 copy_size = copy_size * regs.y_count;
59 }
60
61 Memory::CopyBlock(dest_cpu, source_cpu, copy_size);
55 return; 62 return;
56 } 63 }
57 64
65 ASSERT(regs.exec.enable_2d == 1);
58 u8* src_buffer = Memory::GetPointer(source_cpu); 66 u8* src_buffer = Memory::GetPointer(source_cpu);
59 u8* dst_buffer = Memory::GetPointer(dest_cpu); 67 u8* dst_buffer = Memory::GetPointer(dest_cpu);
60 68
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 7882f16e0..311ccb616 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -23,7 +23,7 @@ public:
23 void WriteReg(u32 method, u32 value); 23 void WriteReg(u32 method, u32 value);
24 24
25 struct Regs { 25 struct Regs {
26 static constexpr size_t NUM_REGS = 0x1D6; 26 static constexpr std::size_t NUM_REGS = 0x1D6;
27 27
28 struct Parameters { 28 struct Parameters {
29 union { 29 union {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 3e4efbe0c..7e1de0fa1 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -20,10 +20,10 @@ namespace Tegra::Shader {
20 20
21struct Register { 21struct Register {
22 /// Number of registers 22 /// Number of registers
23 static constexpr size_t NumRegisters = 256; 23 static constexpr std::size_t NumRegisters = 256;
24 24
25 /// Register 255 is special cased to always be 0 25 /// Register 255 is special cased to always be 0
26 static constexpr size_t ZeroIndex = 255; 26 static constexpr std::size_t ZeroIndex = 255;
27 27
28 enum class Size : u64 { 28 enum class Size : u64 {
29 Byte = 0, 29 Byte = 0,
@@ -67,6 +67,13 @@ private:
67 u64 value{}; 67 u64 value{};
68}; 68};
69 69
70enum class AttributeSize : u64 {
71 Word = 0,
72 DoubleWord = 1,
73 TripleWord = 2,
74 QuadWord = 3,
75};
76
70union Attribute { 77union Attribute {
71 Attribute() = default; 78 Attribute() = default;
72 79
@@ -76,6 +83,7 @@ union Attribute {
76 Position = 7, 83 Position = 7,
77 Attribute_0 = 8, 84 Attribute_0 = 8,
78 Attribute_31 = 39, 85 Attribute_31 = 39,
86 PointCoord = 46,
79 // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex 87 // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
80 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval 88 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
81 // shader. 89 // shader.
@@ -86,9 +94,10 @@ union Attribute {
86 }; 94 };
87 95
88 union { 96 union {
97 BitField<20, 10, u64> immediate;
89 BitField<22, 2, u64> element; 98 BitField<22, 2, u64> element;
90 BitField<24, 6, Index> index; 99 BitField<24, 6, Index> index;
91 BitField<47, 3, u64> size; 100 BitField<47, 3, AttributeSize> size;
92 } fmt20; 101 } fmt20;
93 102
94 union { 103 union {
@@ -231,6 +240,41 @@ enum class FlowCondition : u64 {
231 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? 240 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
232}; 241};
233 242
243enum class ControlCode : u64 {
244 F = 0,
245 LT = 1,
246 EQ = 2,
247 LE = 3,
248 GT = 4,
249 NE = 5,
250 GE = 6,
251 Num = 7,
252 Nan = 8,
253 LTU = 9,
254 EQU = 10,
255 LEU = 11,
256 GTU = 12,
257 NEU = 13,
258 GEU = 14,
259 //
260 OFF = 16,
261 LO = 17,
262 SFF = 18,
263 LS = 19,
264 HI = 20,
265 SFT = 21,
266 HS = 22,
267 OFT = 23,
268 CSM_TA = 24,
269 CSM_TR = 25,
270 CSM_MX = 26,
271 FCSM_TA = 27,
272 FCSM_TR = 28,
273 FCSM_MX = 29,
274 RLE = 30,
275 RGT = 31,
276};
277
234enum class PredicateResultMode : u64 { 278enum class PredicateResultMode : u64 {
235 None = 0x0, 279 None = 0x0,
236 NotZero = 0x3, 280 NotZero = 0x3,
@@ -243,7 +287,47 @@ enum class TextureType : u64 {
243 TextureCube = 3, 287 TextureCube = 3,
244}; 288};
245 289
246enum class IpaMode : u64 { Pass = 0, None = 1, Constant = 2, Sc = 3 }; 290enum class TextureQueryType : u64 {
291 Dimension = 1,
292 TextureType = 2,
293 SamplePosition = 5,
294 Filter = 16,
295 LevelOfDetail = 18,
296 Wrap = 20,
297 BorderColor = 22,
298};
299
300enum class TextureProcessMode : u64 {
301 None = 0,
302 LZ = 1, // Unknown, appears to be the same as none.
303 LB = 2, // Load Bias.
304 LL = 3, // Load LOD (LevelOfDetail)
305 LBA = 6, // Load Bias. The A is unknown, does not appear to differ with LB
306 LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL
307};
308
309enum class TextureMiscMode : u64 {
310 DC,
311 AOFFI, // Uses Offset
312 NDV,
313 NODEP,
314 MZ,
315 PTP,
316};
317
318enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
319enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
320
321struct IpaMode {
322 IpaInterpMode interpolation_mode;
323 IpaSampleMode sampling_mode;
324 inline bool operator==(const IpaMode& a) {
325 return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode);
326 }
327 inline bool operator!=(const IpaMode& a) {
328 return !((*this) == a);
329 }
330};
247 331
248union Instruction { 332union Instruction {
249 Instruction& operator=(const Instruction& instr) { 333 Instruction& operator=(const Instruction& instr) {
@@ -328,10 +412,16 @@ union Instruction {
328 } alu; 412 } alu;
329 413
330 union { 414 union {
331 BitField<54, 3, IpaMode> mode; 415 BitField<51, 1, u64> saturate;
416 BitField<52, 2, IpaSampleMode> sample_mode;
417 BitField<54, 2, IpaInterpMode> interp_mode;
332 } ipa; 418 } ipa;
333 419
334 union { 420 union {
421 BitField<39, 2, u64> tab5cb8_2;
422 BitField<41, 3, u64> tab5c68_1;
423 BitField<44, 2, u64> tab5c68_0;
424 BitField<47, 1, u64> cc;
335 BitField<48, 1, u64> negate_b; 425 BitField<48, 1, u64> negate_b;
336 } fmul; 426 } fmul;
337 427
@@ -395,12 +485,54 @@ union Instruction {
395 } bfe; 485 } bfe;
396 486
397 union { 487 union {
488 BitField<48, 3, u64> pred48;
489
490 union {
491 BitField<20, 20, u64> entry_a;
492 BitField<39, 5, u64> entry_b;
493 BitField<45, 1, u64> neg;
494 BitField<46, 1, u64> uses_cc;
495 } imm;
496
497 union {
498 BitField<20, 14, u64> cb_index;
499 BitField<34, 5, u64> cb_offset;
500 BitField<56, 1, u64> neg;
501 BitField<57, 1, u64> uses_cc;
502 } hi;
503
504 union {
505 BitField<20, 14, u64> cb_index;
506 BitField<34, 5, u64> cb_offset;
507 BitField<39, 5, u64> entry_a;
508 BitField<45, 1, u64> neg;
509 BitField<46, 1, u64> uses_cc;
510 } rz;
511
512 union {
513 BitField<39, 5, u64> entry_a;
514 BitField<45, 1, u64> neg;
515 BitField<46, 1, u64> uses_cc;
516 } r1;
517
518 union {
519 BitField<28, 8, u64> entry_a;
520 BitField<37, 1, u64> neg;
521 BitField<38, 1, u64> uses_cc;
522 } r2;
523
524 } lea;
525
526 union {
398 BitField<0, 5, FlowCondition> cond; 527 BitField<0, 5, FlowCondition> cond;
399 } flow; 528 } flow;
400 529
401 union { 530 union {
531 BitField<47, 1, u64> cc;
402 BitField<48, 1, u64> negate_b; 532 BitField<48, 1, u64> negate_b;
403 BitField<49, 1, u64> negate_c; 533 BitField<49, 1, u64> negate_c;
534 BitField<51, 2, u64> tab5980_1;
535 BitField<53, 2, u64> tab5980_0;
404 } ffma; 536 } ffma;
405 537
406 union { 538 union {
@@ -446,6 +578,27 @@ union Instruction {
446 } psetp; 578 } psetp;
447 579
448 union { 580 union {
581 BitField<12, 3, u64> pred12;
582 BitField<15, 1, u64> neg_pred12;
583 BitField<24, 2, PredOperation> cond;
584 BitField<29, 3, u64> pred29;
585 BitField<32, 1, u64> neg_pred29;
586 BitField<39, 3, u64> pred39;
587 BitField<42, 1, u64> neg_pred39;
588 BitField<44, 1, u64> bf;
589 BitField<45, 2, PredOperation> op;
590 } pset;
591
592 union {
593 BitField<0, 3, u64> pred0;
594 BitField<3, 3, u64> pred3;
595 BitField<8, 5, ControlCode> cc; // flag in cc
596 BitField<39, 3, u64> pred39;
597 BitField<42, 1, u64> neg_pred39;
598 BitField<45, 4, PredOperation> op; // op with pred39
599 } csetp;
600
601 union {
449 BitField<39, 3, u64> pred39; 602 BitField<39, 3, u64> pred39;
450 BitField<42, 1, u64> neg_pred; 603 BitField<42, 1, u64> neg_pred;
451 BitField<43, 1, u64> neg_a; 604 BitField<43, 1, u64> neg_a;
@@ -490,25 +643,127 @@ union Instruction {
490 BitField<28, 1, u64> array; 643 BitField<28, 1, u64> array;
491 BitField<29, 2, TextureType> texture_type; 644 BitField<29, 2, TextureType> texture_type;
492 BitField<31, 4, u64> component_mask; 645 BitField<31, 4, u64> component_mask;
646 BitField<49, 1, u64> nodep_flag;
647 BitField<50, 1, u64> dc_flag;
648 BitField<54, 1, u64> aoffi_flag;
649 BitField<55, 3, TextureProcessMode> process_mode;
493 650
494 bool IsComponentEnabled(size_t component) const { 651 bool IsComponentEnabled(std::size_t component) const {
495 return ((1ull << component) & component_mask) != 0; 652 return ((1ull << component) & component_mask) != 0;
496 } 653 }
654
655 TextureProcessMode GetTextureProcessMode() const {
656 return process_mode;
657 }
658
659 bool UsesMiscMode(TextureMiscMode mode) const {
660 switch (mode) {
661 case TextureMiscMode::DC:
662 return dc_flag != 0;
663 case TextureMiscMode::NODEP:
664 return nodep_flag != 0;
665 case TextureMiscMode::AOFFI:
666 return aoffi_flag != 0;
667 default:
668 break;
669 }
670 return false;
671 }
497 } tex; 672 } tex;
498 673
499 union { 674 union {
675 BitField<22, 6, TextureQueryType> query_type;
676 BitField<31, 4, u64> component_mask;
677 BitField<49, 1, u64> nodep_flag;
678
679 bool UsesMiscMode(TextureMiscMode mode) const {
680 switch (mode) {
681 case TextureMiscMode::NODEP:
682 return nodep_flag != 0;
683 default:
684 break;
685 }
686 return false;
687 }
688 } txq;
689
690 union {
500 BitField<28, 1, u64> array; 691 BitField<28, 1, u64> array;
501 BitField<29, 2, TextureType> texture_type; 692 BitField<29, 2, TextureType> texture_type;
693 BitField<31, 4, u64> component_mask;
694 BitField<35, 1, u64> ndv_flag;
695 BitField<49, 1, u64> nodep_flag;
696
697 bool IsComponentEnabled(std::size_t component) const {
698 return ((1ull << component) & component_mask) != 0;
699 }
700
701 bool UsesMiscMode(TextureMiscMode mode) const {
702 switch (mode) {
703 case TextureMiscMode::NDV:
704 return (ndv_flag != 0);
705 case TextureMiscMode::NODEP:
706 return (nodep_flag != 0);
707 default:
708 break;
709 }
710 return false;
711 }
712 } tmml;
713
714 union {
715 BitField<28, 1, u64> array;
716 BitField<29, 2, TextureType> texture_type;
717 BitField<35, 1, u64> ndv_flag;
718 BitField<49, 1, u64> nodep_flag;
719 BitField<50, 1, u64> dc_flag;
720 BitField<54, 2, u64> info;
502 BitField<56, 2, u64> component; 721 BitField<56, 2, u64> component;
722
723 bool UsesMiscMode(TextureMiscMode mode) const {
724 switch (mode) {
725 case TextureMiscMode::NDV:
726 return ndv_flag != 0;
727 case TextureMiscMode::NODEP:
728 return nodep_flag != 0;
729 case TextureMiscMode::DC:
730 return dc_flag != 0;
731 case TextureMiscMode::AOFFI:
732 return info == 1;
733 case TextureMiscMode::PTP:
734 return info == 2;
735 default:
736 break;
737 }
738 return false;
739 }
503 } tld4; 740 } tld4;
504 741
505 union { 742 union {
743 BitField<49, 1, u64> nodep_flag;
744 BitField<50, 1, u64> dc_flag;
745 BitField<51, 1, u64> aoffi_flag;
506 BitField<52, 2, u64> component; 746 BitField<52, 2, u64> component;
747
748 bool UsesMiscMode(TextureMiscMode mode) const {
749 switch (mode) {
750 case TextureMiscMode::DC:
751 return dc_flag != 0;
752 case TextureMiscMode::NODEP:
753 return nodep_flag != 0;
754 case TextureMiscMode::AOFFI:
755 return aoffi_flag != 0;
756 default:
757 break;
758 }
759 return false;
760 }
507 } tld4s; 761 } tld4s;
508 762
509 union { 763 union {
510 BitField<0, 8, Register> gpr0; 764 BitField<0, 8, Register> gpr0;
511 BitField<28, 8, Register> gpr28; 765 BitField<28, 8, Register> gpr28;
766 BitField<49, 1, u64> nodep_flag;
512 BitField<50, 3, u64> component_mask_selector; 767 BitField<50, 3, u64> component_mask_selector;
513 BitField<53, 4, u64> texture_info; 768 BitField<53, 4, u64> texture_info;
514 769
@@ -528,6 +783,37 @@ union Instruction {
528 UNREACHABLE(); 783 UNREACHABLE();
529 } 784 }
530 785
786 TextureProcessMode GetTextureProcessMode() const {
787 switch (texture_info) {
788 case 0:
789 case 2:
790 case 6:
791 case 8:
792 case 9:
793 case 11:
794 return TextureProcessMode::LZ;
795 case 3:
796 case 5:
797 case 13:
798 return TextureProcessMode::LL;
799 default:
800 break;
801 }
802 return TextureProcessMode::None;
803 }
804
805 bool UsesMiscMode(TextureMiscMode mode) const {
806 switch (mode) {
807 case TextureMiscMode::DC:
808 return (texture_info >= 4 && texture_info <= 6) || texture_info == 9;
809 case TextureMiscMode::NODEP:
810 return nodep_flag != 0;
811 default:
812 break;
813 }
814 return false;
815 }
816
531 bool IsArrayTexture() const { 817 bool IsArrayTexture() const {
532 // TEXS only supports Texture2D arrays. 818 // TEXS only supports Texture2D arrays.
533 return texture_info >= 7 && texture_info <= 9; 819 return texture_info >= 7 && texture_info <= 9;
@@ -537,7 +823,7 @@ union Instruction {
537 return gpr28.Value() != Register::ZeroIndex; 823 return gpr28.Value() != Register::ZeroIndex;
538 } 824 }
539 825
540 bool IsComponentEnabled(size_t component) const { 826 bool IsComponentEnabled(std::size_t component) const {
541 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ 827 static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{
542 {}, 828 {},
543 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, 829 {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
@@ -545,7 +831,7 @@ union Instruction {
545 {0x7, 0xb, 0xd, 0xe, 0xf}, 831 {0x7, 0xb, 0xd, 0xe, 0xf},
546 }}; 832 }};
547 833
548 size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; 834 std::size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U};
549 index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; 835 index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0;
550 836
551 u32 mask = mask_lut[index][component_mask_selector]; 837 u32 mask = mask_lut[index][component_mask_selector];
@@ -556,6 +842,7 @@ union Instruction {
556 } texs; 842 } texs;
557 843
558 union { 844 union {
845 BitField<49, 1, u64> nodep_flag;
559 BitField<53, 4, u64> texture_info; 846 BitField<53, 4, u64> texture_info;
560 847
561 TextureType GetTextureType() const { 848 TextureType GetTextureType() const {
@@ -576,6 +863,26 @@ union Instruction {
576 UNREACHABLE(); 863 UNREACHABLE();
577 } 864 }
578 865
866 TextureProcessMode GetTextureProcessMode() const {
867 if (texture_info == 1 || texture_info == 5 || texture_info == 12)
868 return TextureProcessMode::LL;
869 return TextureProcessMode::LZ;
870 }
871
872 bool UsesMiscMode(TextureMiscMode mode) const {
873 switch (mode) {
874 case TextureMiscMode::AOFFI:
875 return texture_info == 12 || texture_info == 4;
876 case TextureMiscMode::MZ:
877 return texture_info == 5;
878 case TextureMiscMode::NODEP:
879 return nodep_flag != 0;
880 default:
881 break;
882 }
883 return false;
884 }
885
579 bool IsArrayTexture() const { 886 bool IsArrayTexture() const {
580 // TEXS only supports Texture2D arrays. 887 // TEXS only supports Texture2D arrays.
581 return texture_info == 8; 888 return texture_info == 8;
@@ -618,6 +925,7 @@ union Instruction {
618 BitField<36, 5, u64> index; 925 BitField<36, 5, u64> index;
619 } cbuf36; 926 } cbuf36;
620 927
928 BitField<47, 1, u64> generates_cc;
621 BitField<61, 1, u64> is_b_imm; 929 BitField<61, 1, u64> is_b_imm;
622 BitField<60, 1, u64> is_b_gpr; 930 BitField<60, 1, u64> is_b_gpr;
623 BitField<59, 1, u64> is_c_gpr; 931 BitField<59, 1, u64> is_c_gpr;
@@ -647,11 +955,13 @@ public:
647 LDG, // Load from global memory 955 LDG, // Load from global memory
648 STG, // Store in global memory 956 STG, // Store in global memory
649 TEX, 957 TEX,
650 TEXQ, // Texture Query 958 TXQ, // Texture Query
651 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations 959 TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
652 TLDS, // Texture Load with scalar/non-vec4 source/destinations 960 TLDS, // Texture Load with scalar/non-vec4 source/destinations
653 TLD4, // Texture Load 4 961 TLD4, // Texture Load 4
654 TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations 962 TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
963 TMML_B, // Texture Mip Map Level
964 TMML, // Texture Mip Map Level
655 EXIT, 965 EXIT,
656 IPA, 966 IPA,
657 FFMA_IMM, // Fused Multiply and Add 967 FFMA_IMM, // Fused Multiply and Add
@@ -676,6 +986,11 @@ public:
676 ISCADD_C, // Scale and Add 986 ISCADD_C, // Scale and Add
677 ISCADD_R, 987 ISCADD_R,
678 ISCADD_IMM, 988 ISCADD_IMM,
989 LEA_R1,
990 LEA_R2,
991 LEA_RZ,
992 LEA_IMM,
993 LEA_HI,
679 POPC_C, 994 POPC_C,
680 POPC_R, 995 POPC_R,
681 POPC_IMM, 996 POPC_IMM,
@@ -734,6 +1049,8 @@ public:
734 ISET_C, 1049 ISET_C,
735 ISET_IMM, 1050 ISET_IMM,
736 PSETP, 1051 PSETP,
1052 PSET,
1053 CSETP,
737 XMAD_IMM, 1054 XMAD_IMM,
738 XMAD_CR, 1055 XMAD_CR,
739 XMAD_RC, 1056 XMAD_RC,
@@ -757,6 +1074,7 @@ public:
757 IntegerSet, 1074 IntegerSet,
758 IntegerSetPredicate, 1075 IntegerSetPredicate,
759 PredicateSetPredicate, 1076 PredicateSetPredicate,
1077 PredicateSetRegister,
760 Conversion, 1078 Conversion,
761 Xmad, 1079 Xmad,
762 Unknown, 1080 Unknown,
@@ -821,7 +1139,7 @@ public:
821private: 1139private:
822 struct Detail { 1140 struct Detail {
823 private: 1141 private:
824 static constexpr size_t opcode_bitsize = 16; 1142 static constexpr std::size_t opcode_bitsize = 16;
825 1143
826 /** 1144 /**
827 * Generates the mask and the expected value after masking from a given bitstring. 1145 * Generates the mask and the expected value after masking from a given bitstring.
@@ -830,8 +1148,8 @@ private:
830 */ 1148 */
831 static auto GetMaskAndExpect(const char* const bitstring) { 1149 static auto GetMaskAndExpect(const char* const bitstring) {
832 u16 mask = 0, expect = 0; 1150 u16 mask = 0, expect = 0;
833 for (size_t i = 0; i < opcode_bitsize; i++) { 1151 for (std::size_t i = 0; i < opcode_bitsize; i++) {
834 const size_t bit_position = opcode_bitsize - i - 1; 1152 const std::size_t bit_position = opcode_bitsize - i - 1;
835 switch (bitstring[i]) { 1153 switch (bitstring[i]) {
836 case '0': 1154 case '0':
837 mask |= 1 << bit_position; 1155 mask |= 1 << bit_position;
@@ -871,11 +1189,13 @@ private:
871 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), 1189 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
872 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1190 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
873 INST("110000----111---", Id::TEX, Type::Memory, "TEX"), 1191 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
874 INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), 1192 INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"),
875 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), 1193 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
876 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), 1194 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
877 INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), 1195 INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"),
878 INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"), 1196 INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"),
1197 INST("110111110110----", Id::TMML_B, Type::Memory, "TMML_B"),
1198 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
879 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 1199 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
880 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1200 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
881 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1201 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
@@ -906,6 +1226,11 @@ private:
906 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), 1226 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
907 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), 1227 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
908 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), 1228 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
1229 INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
1230 INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
1231 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
1232 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"),
1233 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"),
909 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 1234 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
910 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 1235 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
911 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 1236 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -960,7 +1285,9 @@ private:
960 INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"), 1285 INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"),
961 INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), 1286 INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"),
962 INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), 1287 INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"),
1288 INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"),
963 INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), 1289 INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
1290 INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"),
964 INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), 1291 INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"),
965 INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), 1292 INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"),
966 INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), 1293 INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"),
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
new file mode 100644
index 000000000..a885ee3cf
--- /dev/null
+++ b/src/video_core/engines/shader_header.h
@@ -0,0 +1,103 @@
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 "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Tegra::Shader {
12
13enum class OutputTopology : u32 {
14 PointList = 1,
15 LineStrip = 6,
16 TriangleStrip = 7,
17};
18
19// Documentation in:
20// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture
21struct Header {
22 union {
23 BitField<0, 5, u32> sph_type;
24 BitField<5, 5, u32> version;
25 BitField<10, 4, u32> shader_type;
26 BitField<14, 1, u32> mrt_enable;
27 BitField<15, 1, u32> kills_pixels;
28 BitField<16, 1, u32> does_global_store;
29 BitField<17, 4, u32> sass_version;
30 BitField<21, 5, u32> reserved;
31 BitField<26, 1, u32> does_load_or_store;
32 BitField<27, 1, u32> does_fp64;
33 BitField<28, 4, u32> stream_out_mask;
34 } common0;
35
36 union {
37 BitField<0, 24, u32> shader_local_memory_low_size;
38 BitField<24, 8, u32> per_patch_attribute_count;
39 } common1;
40
41 union {
42 BitField<0, 24, u32> shader_local_memory_high_size;
43 BitField<24, 8, u32> threads_per_input_primitive;
44 } common2;
45
46 union {
47 BitField<0, 24, u32> shader_local_memory_crs_size;
48 BitField<24, 4, OutputTopology> output_topology;
49 BitField<28, 4, u32> reserved;
50 } common3;
51
52 union {
53 BitField<0, 12, u32> max_output_vertices;
54 BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders.
55 BitField<24, 4, u32> reserved;
56 BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
57 } common4;
58
59 union {
60 struct {
61 INSERT_PADDING_BYTES(3); // ImapSystemValuesA
62 INSERT_PADDING_BYTES(1); // ImapSystemValuesB
63 INSERT_PADDING_BYTES(16); // ImapGenericVector[32]
64 INSERT_PADDING_BYTES(2); // ImapColor
65 INSERT_PADDING_BYTES(2); // ImapSystemValuesC
66 INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10]
67 INSERT_PADDING_BYTES(1); // ImapReserved
68 INSERT_PADDING_BYTES(3); // OmapSystemValuesA
69 INSERT_PADDING_BYTES(1); // OmapSystemValuesB
70 INSERT_PADDING_BYTES(16); // OmapGenericVector[32]
71 INSERT_PADDING_BYTES(2); // OmapColor
72 INSERT_PADDING_BYTES(2); // OmapSystemValuesC
73 INSERT_PADDING_BYTES(5); // OmapFixedFncTexture[10]
74 INSERT_PADDING_BYTES(1); // OmapReserved
75 } vtg;
76
77 struct {
78 INSERT_PADDING_BYTES(3); // ImapSystemValuesA
79 INSERT_PADDING_BYTES(1); // ImapSystemValuesB
80 INSERT_PADDING_BYTES(32); // ImapGenericVector[32]
81 INSERT_PADDING_BYTES(2); // ImapColor
82 INSERT_PADDING_BYTES(2); // ImapSystemValuesC
83 INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10]
84 INSERT_PADDING_BYTES(2); // ImapReserved
85 struct {
86 u32 target;
87 union {
88 BitField<0, 1, u32> sample_mask;
89 BitField<1, 1, u32> depth;
90 BitField<2, 30, u32> reserved;
91 };
92 } omap;
93 bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
94 const u32 bit = render_target * 4 + component;
95 return omap.target & (1 << bit);
96 }
97 } ps;
98 };
99};
100
101static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
102
103} // namespace Tegra::Shader
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index e6d8e65c6..baa8b63b7 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "video_core/engines/fermi_2d.h" 6#include "video_core/engines/fermi_2d.h"
7#include "video_core/engines/kepler_memory.h"
7#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
8#include "video_core/engines/maxwell_compute.h" 9#include "video_core/engines/maxwell_compute.h"
9#include "video_core/engines/maxwell_dma.h" 10#include "video_core/engines/maxwell_dma.h"
@@ -27,6 +28,7 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
27 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); 28 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
28 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
29 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
31 kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager);
30} 32}
31 33
32GPU::~GPU() = default; 34GPU::~GPU() = default;
@@ -66,6 +68,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
66 case RenderTargetFormat::RGBA8_UINT: 68 case RenderTargetFormat::RGBA8_UINT:
67 case RenderTargetFormat::RGB10_A2_UNORM: 69 case RenderTargetFormat::RGB10_A2_UNORM:
68 case RenderTargetFormat::BGRA8_UNORM: 70 case RenderTargetFormat::BGRA8_UNORM:
71 case RenderTargetFormat::BGRA8_SRGB:
69 case RenderTargetFormat::RG16_UNORM: 72 case RenderTargetFormat::RG16_UNORM:
70 case RenderTargetFormat::RG16_SNORM: 73 case RenderTargetFormat::RG16_SNORM:
71 case RenderTargetFormat::RG16_UINT: 74 case RenderTargetFormat::RG16_UINT:
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 2c3dbd97b..5cc1e19ca 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -4,8 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 11#include "core/hle/service/nvflinger/buffer_queue.h"
11#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
@@ -26,6 +27,7 @@ enum class RenderTargetFormat : u32 {
26 RG32_FLOAT = 0xCB, 27 RG32_FLOAT = 0xCB,
27 RG32_UINT = 0xCD, 28 RG32_UINT = 0xCD,
28 BGRA8_UNORM = 0xCF, 29 BGRA8_UNORM = 0xCF,
30 BGRA8_SRGB = 0xD0,
29 RGB10_A2_UNORM = 0xD1, 31 RGB10_A2_UNORM = 0xD1,
30 RGBA8_UNORM = 0xD5, 32 RGBA8_UNORM = 0xD5,
31 RGBA8_SRGB = 0xD6, 33 RGBA8_SRGB = 0xD6,
@@ -40,6 +42,7 @@ enum class RenderTargetFormat : u32 {
40 R32_UINT = 0xE4, 42 R32_UINT = 0xE4,
41 R32_FLOAT = 0xE5, 43 R32_FLOAT = 0xE5,
42 B5G6R5_UNORM = 0xE8, 44 B5G6R5_UNORM = 0xE8,
45 BGR5A1_UNORM = 0xE9,
43 RG8_UNORM = 0xEA, 46 RG8_UNORM = 0xEA,
44 RG8_SNORM = 0xEB, 47 RG8_SNORM = 0xEB,
45 R16_UNORM = 0xEE, 48 R16_UNORM = 0xEE,
@@ -67,6 +70,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
67/// Returns the number of bytes per pixel of each depth format. 70/// Returns the number of bytes per pixel of each depth format.
68u32 DepthFormatBytesPerPixel(DepthFormat format); 71u32 DepthFormatBytesPerPixel(DepthFormat format);
69 72
73struct CommandListHeader;
70class DebugContext; 74class DebugContext;
71 75
72/** 76/**
@@ -99,6 +103,7 @@ class Fermi2D;
99class Maxwell3D; 103class Maxwell3D;
100class MaxwellCompute; 104class MaxwellCompute;
101class MaxwellDMA; 105class MaxwellDMA;
106class KeplerMemory;
102} // namespace Engines 107} // namespace Engines
103 108
104enum class EngineID { 109enum class EngineID {
@@ -115,7 +120,7 @@ public:
115 ~GPU(); 120 ~GPU();
116 121
117 /// Processes a command list stored at the specified address in GPU memory. 122 /// Processes a command list stored at the specified address in GPU memory.
118 void ProcessCommandList(GPUVAddr address, u32 size); 123 void ProcessCommandLists(const std::vector<CommandListHeader>& commands);
119 124
120 /// Returns a reference to the Maxwell3D GPU engine. 125 /// Returns a reference to the Maxwell3D GPU engine.
121 Engines::Maxwell3D& Maxwell3D(); 126 Engines::Maxwell3D& Maxwell3D();
@@ -130,13 +135,10 @@ public:
130 const Tegra::MemoryManager& MemoryManager() const; 135 const Tegra::MemoryManager& MemoryManager() const;
131 136
132private: 137private:
133 /// Writes a single register in the engine bound to the specified subchannel
134 void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
135
136 std::unique_ptr<Tegra::MemoryManager> memory_manager; 138 std::unique_ptr<Tegra::MemoryManager> memory_manager;
137 139
138 /// Mapping of command subchannels to their bound engine ids. 140 /// Mapping of command subchannels to their bound engine ids.
139 std::unordered_map<u32, EngineID> bound_engines; 141 std::array<EngineID, 8> bound_engines = {};
140 142
141 /// 3D engine 143 /// 3D engine
142 std::unique_ptr<Engines::Maxwell3D> maxwell_3d; 144 std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
@@ -146,6 +148,8 @@ private:
146 std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; 148 std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
147 /// DMA engine 149 /// DMA engine
148 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; 150 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
151 /// Inline memory engine
152 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
149}; 153};
150 154
151} // namespace Tegra 155} // namespace Tegra
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index 7d836b816..cee0baaf3 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -152,7 +152,7 @@ private:
152 boost::optional<u32> 152 boost::optional<u32>
153 delayed_pc; ///< Program counter to execute at after the delay slot is executed. 153 delayed_pc; ///< Program counter to execute at after the delay slot is executed.
154 154
155 static constexpr size_t NumMacroRegisters = 8; 155 static constexpr std::size_t NumMacroRegisters = 8;
156 156
157 /// General purpose macro registers. 157 /// General purpose macro registers.
158 std::array<u32, NumMacroRegisters> registers = {}; 158 std::array<u32, NumMacroRegisters> registers = {};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 9d78e8b6b..cd819d69f 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -20,9 +20,6 @@ public:
20 /// Clear the current framebuffer 20 /// Clear the current framebuffer
21 virtual void Clear() = 0; 21 virtual void Clear() = 0;
22 22
23 /// Notify rasterizer that the specified Maxwell register has been changed
24 virtual void NotifyMaxwellRegisterChanged(u32 method) = 0;
25
26 /// Notify rasterizer that all caches should be flushed to Switch memory 23 /// Notify rasterizer that all caches should be flushed to Switch memory
27 virtual void FlushAll() = 0; 24 virtual void FlushAll() = 0;
28 25
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index be17a2b9c..0df3725c2 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -19,6 +19,7 @@ void RendererBase::RefreshBaseSettings() {
19 UpdateCurrentFramebufferLayout(); 19 UpdateCurrentFramebufferLayout();
20 20
21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit; 21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
22 renderer_settings.set_background_color = true;
22} 23}
23 24
24void RendererBase::UpdateCurrentFramebufferLayout() { 25void RendererBase::UpdateCurrentFramebufferLayout() {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 2a357f9d0..2cd0738ff 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -19,6 +19,7 @@ namespace VideoCore {
19 19
20struct RendererSettings { 20struct RendererSettings {
21 std::atomic_bool use_framelimiter{false}; 21 std::atomic_bool use_framelimiter{false};
22 std::atomic_bool set_background_color{false};
22}; 23};
23 24
24class RendererBase : NonCopyable { 25class RendererBase : NonCopyable {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
new file mode 100644
index 000000000..578aca789
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -0,0 +1,93 @@
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 <cstring>
6#include <memory>
7
8#include "common/alignment.h"
9#include "core/core.h"
10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_buffer_cache.h"
12
13namespace OpenGL {
14
15OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {}
16
17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
18 std::size_t alignment, bool cache) {
19 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
20 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
21
22 // Cache management is a big overhead, so only cache entries with a given size.
23 // TODO: Figure out which size is the best for given games.
24 cache &= size >= 2048;
25
26 if (cache) {
27 auto entry = TryGet(*cpu_addr);
28 if (entry) {
29 if (entry->size >= size && entry->alignment == alignment) {
30 return entry->offset;
31 }
32 Unregister(entry);
33 }
34 }
35
36 AlignBuffer(alignment);
37 GLintptr uploaded_offset = buffer_offset;
38
39 Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
40
41 buffer_ptr += size;
42 buffer_offset += size;
43
44 if (cache) {
45 auto entry = std::make_shared<CachedBufferEntry>();
46 entry->offset = uploaded_offset;
47 entry->size = size;
48 entry->alignment = alignment;
49 entry->addr = *cpu_addr;
50 Register(entry);
51 }
52
53 return uploaded_offset;
54}
55
56GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size,
57 std::size_t alignment) {
58 AlignBuffer(alignment);
59 std::memcpy(buffer_ptr, raw_pointer, size);
60 GLintptr uploaded_offset = buffer_offset;
61
62 buffer_ptr += size;
63 buffer_offset += size;
64 return uploaded_offset;
65}
66
67void OGLBufferCache::Map(std::size_t max_size) {
68 bool invalidate;
69 std::tie(buffer_ptr, buffer_offset_base, invalidate) =
70 stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4);
71 buffer_offset = buffer_offset_base;
72
73 if (invalidate) {
74 InvalidateAll();
75 }
76}
77void OGLBufferCache::Unmap() {
78 stream_buffer.Unmap(buffer_offset - buffer_offset_base);
79}
80
81GLuint OGLBufferCache::GetHandle() const {
82 return stream_buffer.GetHandle();
83}
84
85void OGLBufferCache::AlignBuffer(std::size_t alignment) {
86 // Align the offset, not the mapped pointer
87 GLintptr offset_aligned =
88 static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment));
89 buffer_ptr += offset_aligned - buffer_offset;
90 buffer_offset = offset_aligned;
91}
92
93} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
new file mode 100644
index 000000000..6c18461f4
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -0,0 +1,57 @@
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 <cstddef>
8#include <memory>
9
10#include "common/common_types.h"
11#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h"
13#include "video_core/renderer_opengl/gl_stream_buffer.h"
14
15namespace OpenGL {
16
17struct CachedBufferEntry final {
18 VAddr GetAddr() const {
19 return addr;
20 }
21
22 std::size_t GetSizeInBytes() const {
23 return size;
24 }
25
26 VAddr addr;
27 std::size_t size;
28 GLintptr offset;
29 std::size_t alignment;
30};
31
32class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
33public:
34 explicit OGLBufferCache(std::size_t size);
35
36 GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
37 bool cache = true);
38
39 GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4);
40
41 void Map(std::size_t max_size);
42 void Unmap();
43
44 GLuint GetHandle() const;
45
46protected:
47 void AlignBuffer(std::size_t alignment);
48
49private:
50 OGLStreamBuffer stream_buffer;
51
52 u8* buffer_ptr = nullptr;
53 GLintptr buffer_offset = 0;
54 GLintptr buffer_offset_base = 0;
55};
56
57} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7ce969f73..70fb54507 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <memory> 7#include <memory>
7#include <string> 8#include <string>
8#include <string_view> 9#include <string_view>
@@ -33,16 +34,19 @@ using PixelFormat = SurfaceParams::PixelFormat;
33using SurfaceType = SurfaceParams::SurfaceType; 34using SurfaceType = SurfaceParams::SurfaceType;
34 35
35MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); 36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
36MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192)); 37MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192)); 38MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
39MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
40MICROPROFILE_DEFINE(OpenGL_Texture, "OpenGL", "Texture Setup", MP_RGB(128, 128, 192));
41MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(128, 128, 192));
38MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); 42MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
39MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); 43MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
40MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 44MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
41 45
42RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 46RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
43 : emu_window{window}, screen_info{info}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) { 47 : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) {
44 // Create sampler objects 48 // Create sampler objects
45 for (size_t i = 0; i < texture_samplers.size(); ++i) { 49 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
46 texture_samplers[i].Create(); 50 texture_samplers[i].Create();
47 state.texture_units[i].sampler = texture_samplers[i].sampler.handle; 51 state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
48 } 52 }
@@ -55,6 +59,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
55 59
56 if (extension == "GL_ARB_direct_state_access") { 60 if (extension == "GL_ARB_direct_state_access") {
57 has_ARB_direct_state_access = true; 61 has_ARB_direct_state_access = true;
62 } else if (extension == "GL_ARB_multi_bind") {
63 has_ARB_multi_bind = true;
58 } else if (extension == "GL_ARB_separate_shader_objects") { 64 } else if (extension == "GL_ARB_separate_shader_objects") {
59 has_ARB_separate_shader_objects = true; 65 has_ARB_separate_shader_objects = true;
60 } else if (extension == "GL_ARB_vertex_attrib_binding") { 66 } else if (extension == "GL_ARB_vertex_attrib_binding") {
@@ -67,28 +73,13 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
67 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 73 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
68 state.clip_distance[0] = true; 74 state.clip_distance[0] = true;
69 75
70 // Generate VAO and UBO
71 sw_vao.Create();
72 uniform_buffer.Create();
73
74 state.draw.vertex_array = sw_vao.handle;
75 state.draw.uniform_buffer = uniform_buffer.handle;
76 state.Apply();
77
78 // Create render framebuffer 76 // Create render framebuffer
79 framebuffer.Create(); 77 framebuffer.Create();
80 78
81 hw_vao.Create();
82
83 state.draw.vertex_buffer = stream_buffer.GetHandle();
84
85 shader_program_manager = std::make_unique<GLShader::ProgramManager>(); 79 shader_program_manager = std::make_unique<GLShader::ProgramManager>();
86 state.draw.shader_program = 0; 80 state.draw.shader_program = 0;
87 state.draw.vertex_array = hw_vao.handle;
88 state.Apply(); 81 state.Apply();
89 82
90 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle());
91
92 glEnable(GL_BLEND); 83 glEnable(GL_BLEND);
93 84
94 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); 85 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
@@ -98,14 +89,60 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
98 89
99RasterizerOpenGL::~RasterizerOpenGL() {} 90RasterizerOpenGL::~RasterizerOpenGL() {}
100 91
101std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, 92void RasterizerOpenGL::SetupVertexArrays() {
102 GLintptr buffer_offset) {
103 MICROPROFILE_SCOPE(OpenGL_VAO); 93 MICROPROFILE_SCOPE(OpenGL_VAO);
104 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 94 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
105 const auto& regs = gpu.regs; 95 const auto& regs = gpu.regs;
106 96
107 state.draw.vertex_array = hw_vao.handle; 97 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
108 state.draw.vertex_buffer = stream_buffer.GetHandle(); 98 auto& VAO = iter->second;
99
100 if (is_cache_miss) {
101 VAO.Create();
102 state.draw.vertex_array = VAO.handle;
103 state.Apply();
104
105 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
106 // around.
107 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
108
109 // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
110 // Enables the first 16 vertex attributes always, as we don't know which ones are actually
111 // used until shader time. Note, Tegra technically supports 32, but we're capping this to 16
112 // for now to avoid OpenGL errors.
113 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
114 // assume every shader uses them all.
115 for (unsigned index = 0; index < 16; ++index) {
116 const auto& attrib = regs.vertex_attrib_format[index];
117
118 // Ignore invalid attributes.
119 if (!attrib.IsValid())
120 continue;
121
122 const auto& buffer = regs.vertex_array[attrib.buffer];
123 LOG_TRACE(HW_GPU,
124 "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
125 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
126 attrib.offset.Value(), attrib.IsNormalized());
127
128 ASSERT(buffer.IsEnabled());
129
130 glEnableVertexAttribArray(index);
131 if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
132 attrib.type ==
133 Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
134 glVertexAttribIFormat(index, attrib.ComponentCount(),
135 MaxwellToGL::VertexType(attrib), attrib.offset);
136 } else {
137 glVertexAttribFormat(index, attrib.ComponentCount(),
138 MaxwellToGL::VertexType(attrib),
139 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
140 }
141 glVertexAttribBinding(index, attrib.buffer);
142 }
143 }
144 state.draw.vertex_array = VAO.handle;
145 state.draw.vertex_buffer = buffer_cache.GetHandle();
109 state.Apply(); 146 state.Apply();
110 147
111 // Upload all guest vertex arrays sequentially to our buffer 148 // Upload all guest vertex arrays sequentially to our buffer
@@ -117,77 +154,35 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
117 Tegra::GPUVAddr start = vertex_array.StartAddress(); 154 Tegra::GPUVAddr start = vertex_array.StartAddress();
118 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 155 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
119 156
120 if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
121 start += vertex_array.stride * (gpu.state.current_instance / vertex_array.divisor);
122 }
123
124 ASSERT(end > start); 157 ASSERT(end > start);
125 u64 size = end - start + 1; 158 const u64 size = end - start + 1;
126 159 const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
127 GLintptr vertex_buffer_offset;
128 std::tie(array_ptr, buffer_offset, vertex_buffer_offset) =
129 UploadMemory(array_ptr, buffer_offset, start, size);
130 160
131 // Bind the vertex array to the buffer at the current offset. 161 // Bind the vertex array to the buffer at the current offset.
132 glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, 162 glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
133 vertex_array.stride); 163 vertex_array.stride);
134 164
135 if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { 165 if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
136 // Tell OpenGL that this is an instanced vertex buffer to prevent accessing different 166 // Enable vertex buffer instancing with the specified divisor.
137 // indexes on each vertex. We do the instance indexing manually by incrementing the 167 glVertexBindingDivisor(index, vertex_array.divisor);
138 // start address of the vertex buffer.
139 glVertexBindingDivisor(index, 1);
140 } else { 168 } else {
141 // Disable the vertex buffer instancing. 169 // Disable the vertex buffer instancing.
142 glVertexBindingDivisor(index, 0); 170 glVertexBindingDivisor(index, 0);
143 } 171 }
144 } 172 }
145
146 // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
147 // Enables the first 16 vertex attributes always, as we don't know which ones are actually used
148 // until shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now
149 // to avoid OpenGL errors.
150 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
151 // assume every shader uses them all.
152 for (unsigned index = 0; index < 16; ++index) {
153 auto& attrib = regs.vertex_attrib_format[index];
154
155 // Ignore invalid attributes.
156 if (!attrib.IsValid())
157 continue;
158
159 auto& buffer = regs.vertex_array[attrib.buffer];
160 LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
161 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
162 attrib.offset.Value(), attrib.IsNormalized());
163
164 ASSERT(buffer.IsEnabled());
165
166 glEnableVertexAttribArray(index);
167 if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
168 attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
169 glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
170 attrib.offset);
171 } else {
172 glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
173 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
174 }
175 glVertexAttribBinding(index, attrib.buffer);
176 }
177
178 return {array_ptr, buffer_offset};
179} 173}
180 174
181std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { 175void RasterizerOpenGL::SetupShaders() {
182 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 176 MICROPROFILE_SCOPE(OpenGL_Shader);
177 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
183 178
184 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL 179 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL
185 // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. 180 // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
186 u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; 181 u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
187 u32 current_texture_bindpoint = 0; 182 u32 current_texture_bindpoint = 0;
188 183
189 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 184 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
190 auto& shader_config = gpu.regs.shader_config[index]; 185 const auto& shader_config = gpu.regs.shader_config[index];
191 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; 186 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)};
192 187
193 // Skip stages that are not enabled 188 // Skip stages that are not enabled
@@ -195,21 +190,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
195 continue; 190 continue;
196 } 191 }
197 192
198 std::tie(buffer_ptr, buffer_offset) = 193 const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
199 AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment));
200
201 const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
202 194
203 GLShader::MaxwellUniformData ubo{}; 195 GLShader::MaxwellUniformData ubo{};
204 ubo.SetFromRegs(gpu.state.shader_stages[stage]); 196 ubo.SetFromRegs(gpu.state.shader_stages[stage]);
205 std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); 197 const GLintptr offset = buffer_cache.UploadHostMemory(
198 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
206 199
207 // Bind the buffer 200 // Bind the buffer
208 glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset, 201 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo));
209 sizeof(ubo));
210
211 buffer_ptr += sizeof(ubo);
212 buffer_offset += sizeof(ubo);
213 202
214 Shader shader{shader_cache.GetStageProgram(program)}; 203 Shader shader{shader_cache.GetStageProgram(program)};
215 204
@@ -230,9 +219,8 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
230 } 219 }
231 220
232 // Configure the const buffers for this shader stage. 221 // Configure the const buffers for this shader stage.
233 std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = 222 current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
234 SetupConstBuffers(buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage), 223 shader, current_constbuffer_bindpoint);
235 shader, current_constbuffer_bindpoint);
236 224
237 // Configure the textures for this shader stage. 225 // Configure the textures for this shader stage.
238 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, 226 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
@@ -245,15 +233,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
245 } 233 }
246 } 234 }
247 235
248 shader_program_manager->UseTrivialGeometryShader(); 236 state.Apply();
249 237
250 return {buffer_ptr, buffer_offset}; 238 shader_program_manager->UseTrivialGeometryShader();
251} 239}
252 240
253size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 241std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
254 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 242 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
255 243
256 size_t size = 0; 244 std::size_t size = 0;
257 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 245 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
258 if (!regs.vertex_array[index].IsEnabled()) 246 if (!regs.vertex_array[index].IsEnabled())
259 continue; 247 continue;
@@ -309,60 +297,80 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
309 cached_pages.add({pages_interval, delta}); 297 cached_pages.add({pages_interval, delta});
310} 298}
311 299
312std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, 300void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb,
313 bool using_depth_fb, 301 bool preserve_contents,
314 bool preserve_contents) { 302 boost::optional<std::size_t> single_color_target) {
303 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
315 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 304 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
316 305
317 if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) { 306 Surface depth_surface;
318 LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured"); 307 if (using_depth_fb) {
319 using_color_fb = false; 308 depth_surface = res_cache.GetDepthBufferSurface(preserve_contents);
320 } 309 }
321 310
322 const bool has_stencil = regs.stencil_enable; 311 // TODO(bunnei): Figure out how the below register works. According to envytools, this should be
323 const bool write_color_fb = 312 // used to enable multiple render targets. However, it is left unset on all games that I have
324 state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || 313 // tested.
325 state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; 314 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
326 315
327 const bool write_depth_fb = 316 // Bind the framebuffer surfaces
328 (state.depth.test_enabled && state.depth.write_mask == GL_TRUE) || 317 state.draw.draw_framebuffer = framebuffer.handle;
329 (has_stencil && (state.stencil.front.write_mask || state.stencil.back.write_mask)); 318 state.Apply();
330
331 Surface color_surface;
332 Surface depth_surface;
333 MathUtil::Rectangle<u32> surfaces_rect;
334 std::tie(color_surface, depth_surface, surfaces_rect) =
335 res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, preserve_contents);
336 319
337 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 320 if (using_color_fb) {
338 const MathUtil::Rectangle<u32> draw_rect{ 321 if (single_color_target) {
339 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, 322 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
340 surfaces_rect.left, surfaces_rect.right)), // Left 323 Surface color_surface =
341 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top, 324 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
342 surfaces_rect.bottom, surfaces_rect.top)), // Top 325 glFramebufferTexture2D(
343 static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right, 326 GL_DRAW_FRAMEBUFFER,
344 surfaces_rect.left, surfaces_rect.right)), // Right 327 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
345 static_cast<u32>( 328 color_surface != nullptr ? color_surface->Texture().handle : 0, 0);
346 std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom, 329 glDrawBuffer(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target));
347 surfaces_rect.bottom, surfaces_rect.top))}; // Bottom 330 } else {
331 // Multiple color attachments are enabled
332 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
333 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
334 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
335 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
336 glFramebufferTexture2D(
337 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
338 GL_TEXTURE_2D, color_surface != nullptr ? color_surface->Texture().handle : 0,
339 0);
340 }
341 glDrawBuffers(regs.rt_control.count, buffers.data());
342 }
343 } else {
344 // No color attachments are enabled - zero out all of them
345 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
346 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
347 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D,
348 0, 0);
349 }
350 glDrawBuffer(GL_NONE);
351 }
348 352
349 // Bind the framebuffer surfaces 353 if (depth_surface) {
350 BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); 354 if (regs.stencil_enable) {
355 // Attach both depth and stencil
356 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
357 depth_surface->Texture().handle, 0);
358 } else {
359 // Attach depth
360 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
361 depth_surface->Texture().handle, 0);
362 // Clear stencil attachment
363 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
364 }
365 } else {
366 // Clear both depth and stencil attachment
367 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
368 0);
369 }
351 370
352 SyncViewport(surfaces_rect); 371 SyncViewport();
353 372
354 // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable
355 // scissor test to prevent drawing outside of the framebuffer region
356 state.scissor.enabled = true;
357 state.scissor.x = draw_rect.left;
358 state.scissor.y = draw_rect.bottom;
359 state.scissor.width = draw_rect.GetWidth();
360 state.scissor.height = draw_rect.GetHeight();
361 state.Apply(); 373 state.Apply();
362
363 // Only return the surface to be marked as dirty if writing to it is enabled.
364 return std::make_pair(write_color_fb ? color_surface : nullptr,
365 write_depth_fb ? depth_surface : nullptr);
366} 374}
367 375
368void RasterizerOpenGL::Clear() { 376void RasterizerOpenGL::Clear() {
@@ -370,32 +378,24 @@ void RasterizerOpenGL::Clear() {
370 SCOPE_EXIT({ prev_state.Apply(); }); 378 SCOPE_EXIT({ prev_state.Apply(); });
371 379
372 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 380 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
373 bool use_color_fb = false; 381 bool use_color{};
374 bool use_depth_fb = false; 382 bool use_depth{};
383 bool use_stencil{};
375 384
376 OpenGLState clear_state; 385 OpenGLState clear_state;
377 clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer; 386 clear_state.draw.draw_framebuffer = framebuffer.handle;
378 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; 387 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
379 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; 388 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
380 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; 389 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
381 clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; 390 clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
382 391
383 GLbitfield clear_mask{};
384 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 392 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
385 regs.clear_buffers.A) { 393 regs.clear_buffers.A) {
386 if (regs.clear_buffers.RT == 0) { 394 use_color = true;
387 // We only support clearing the first color attachment for now
388 clear_mask |= GL_COLOR_BUFFER_BIT;
389 use_color_fb = true;
390 } else {
391 // TODO(subv): Add support for the other color attachments
392 LOG_CRITICAL(HW_GPU, "Clear unimplemented for RT {}", regs.clear_buffers.RT);
393 }
394 } 395 }
395 if (regs.clear_buffers.Z) { 396 if (regs.clear_buffers.Z) {
396 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 397 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
397 use_depth_fb = true; 398 use_depth = true;
398 clear_mask |= GL_DEPTH_BUFFER_BIT;
399 399
400 // Always enable the depth write when clearing the depth buffer. The depth write mask is 400 // Always enable the depth write when clearing the depth buffer. The depth write mask is
401 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. 401 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
@@ -404,59 +404,33 @@ void RasterizerOpenGL::Clear() {
404 } 404 }
405 if (regs.clear_buffers.S) { 405 if (regs.clear_buffers.S) {
406 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); 406 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
407 use_depth_fb = true; 407 use_stencil = true;
408 clear_mask |= GL_STENCIL_BUFFER_BIT;
409 clear_state.stencil.test_enabled = true; 408 clear_state.stencil.test_enabled = true;
410 } 409 }
411 410
412 if (!use_color_fb && !use_depth_fb) { 411 if (!use_color && !use_depth && !use_stencil) {
413 // No color surface nor depth/stencil surface are enabled 412 // No color surface nor depth/stencil surface are enabled
414 return; 413 return;
415 } 414 }
416 415
417 if (clear_mask == 0) {
418 // No clear mask is enabled
419 return;
420 }
421
422 ScopeAcquireGLContext acquire_context{emu_window}; 416 ScopeAcquireGLContext acquire_context{emu_window};
423 417
424 auto [dirty_color_surface, dirty_depth_surface] = 418 ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
425 ConfigureFramebuffers(use_color_fb, use_depth_fb, false); 419 regs.clear_buffers.RT.Value());
426 420
427 clear_state.Apply(); 421 clear_state.Apply();
428 422
429 glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], 423 if (use_color) {
430 regs.clear_color[3]); 424 glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
431 glClearDepth(regs.clear_depth); 425 }
432 glClearStencil(regs.clear_stencil);
433
434 glClear(clear_mask);
435}
436
437std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset,
438 size_t alignment) {
439 // Align the offset, not the mapped pointer
440 GLintptr offset_aligned =
441 static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment));
442 return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned};
443}
444
445std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr,
446 GLintptr buffer_offset,
447 Tegra::GPUVAddr gpu_addr,
448 size_t size, size_t alignment) {
449 std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment);
450 GLintptr uploaded_offset = buffer_offset;
451
452 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
453 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
454 Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
455
456 buffer_ptr += size;
457 buffer_offset += size;
458 426
459 return {buffer_ptr, buffer_offset, uploaded_offset}; 427 if (use_depth && use_stencil) {
428 glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil);
429 } else if (use_depth) {
430 glClearBufferfv(GL_DEPTH, 0, &regs.clear_depth);
431 } else if (use_stencil) {
432 glClearBufferiv(GL_STENCIL, 0, &regs.clear_stencil);
433 }
460} 434}
461 435
462void RasterizerOpenGL::DrawArrays() { 436void RasterizerOpenGL::DrawArrays() {
@@ -464,12 +438,12 @@ void RasterizerOpenGL::DrawArrays() {
464 return; 438 return;
465 439
466 MICROPROFILE_SCOPE(OpenGL_Drawing); 440 MICROPROFILE_SCOPE(OpenGL_Drawing);
467 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 441 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
442 const auto& regs = gpu.regs;
468 443
469 ScopeAcquireGLContext acquire_context{emu_window}; 444 ScopeAcquireGLContext acquire_context{emu_window};
470 445
471 auto [dirty_color_surface, dirty_depth_surface] = 446 ConfigureFramebuffers();
472 ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0, true);
473 447
474 SyncDepthTestState(); 448 SyncDepthTestState();
475 SyncStencilTestState(); 449 SyncStencilTestState();
@@ -482,43 +456,46 @@ void RasterizerOpenGL::DrawArrays() {
482 456
483 // Draw the vertex batch 457 // Draw the vertex batch
484 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 458 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
485 const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; 459 const u64 index_buffer_size{static_cast<u64>(regs.index_array.count) *
460 static_cast<u64>(regs.index_array.FormatSizeInBytes())};
486 461
487 state.draw.vertex_buffer = stream_buffer.GetHandle(); 462 state.draw.vertex_buffer = buffer_cache.GetHandle();
488 state.Apply(); 463 state.Apply();
489 464
490 size_t buffer_size = CalculateVertexArraysSize(); 465 std::size_t buffer_size = CalculateVertexArraysSize();
491 466
492 if (is_indexed) { 467 if (is_indexed) {
493 buffer_size = Common::AlignUp<size_t>(buffer_size, 4) + index_buffer_size; 468 buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size;
494 } 469 }
495 470
496 // Uniform space for the 5 shader stages 471 // Uniform space for the 5 shader stages
497 buffer_size = 472 buffer_size =
498 Common::AlignUp<size_t>(buffer_size, 4) + 473 Common::AlignUp<std::size_t>(buffer_size, 4) +
499 (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; 474 (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage;
500 475
501 // Add space for at least 18 constant buffers 476 // Add space for at least 18 constant buffers
502 buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); 477 buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment);
503 478
504 u8* buffer_ptr; 479 buffer_cache.Map(buffer_size);
505 GLintptr buffer_offset;
506 std::tie(buffer_ptr, buffer_offset, std::ignore) =
507 stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4);
508 u8* buffer_ptr_base = buffer_ptr;
509 480
510 std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); 481 SetupVertexArrays();
511 482
512 // If indexed mode, copy the index buffer 483 // If indexed mode, copy the index buffer
513 GLintptr index_buffer_offset = 0; 484 GLintptr index_buffer_offset = 0;
514 if (is_indexed) { 485 if (is_indexed) {
515 std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory( 486 MICROPROFILE_SCOPE(OpenGL_Index);
516 buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size); 487
488 // Adjust the index buffer offset so it points to the first desired index.
489 auto index_start = regs.index_array.StartAddress();
490 index_start += static_cast<size_t>(regs.index_array.first) *
491 static_cast<size_t>(regs.index_array.FormatSizeInBytes());
492
493 index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size);
517 } 494 }
518 495
519 std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); 496 SetupShaders();
520 497
521 stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); 498 buffer_cache.Unmap();
522 499
523 shader_program_manager->ApplyTo(state); 500 shader_program_manager->ApplyTo(state);
524 state.Apply(); 501 state.Apply();
@@ -527,14 +504,26 @@ void RasterizerOpenGL::DrawArrays() {
527 if (is_indexed) { 504 if (is_indexed) {
528 const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; 505 const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
529 506
530 // Adjust the index buffer offset so it points to the first desired index. 507 if (gpu.state.current_instance > 0) {
531 index_buffer_offset += regs.index_array.first * regs.index_array.FormatSizeInBytes(); 508 glDrawElementsInstancedBaseVertexBaseInstance(
532 509 primitive_mode, regs.index_array.count,
533 glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, 510 MaxwellToGL::IndexFormat(regs.index_array.format),
534 MaxwellToGL::IndexFormat(regs.index_array.format), 511 reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex,
535 reinterpret_cast<const void*>(index_buffer_offset), base_vertex); 512 gpu.state.current_instance);
513 } else {
514 glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
515 MaxwellToGL::IndexFormat(regs.index_array.format),
516 reinterpret_cast<const void*>(index_buffer_offset),
517 base_vertex);
518 }
536 } else { 519 } else {
537 glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); 520 if (gpu.state.current_instance > 0) {
521 glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first,
522 regs.vertex_buffer.count, 1,
523 gpu.state.current_instance);
524 } else {
525 glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
526 }
538 } 527 }
539 528
540 // Disable scissor test 529 // Disable scissor test
@@ -549,24 +538,18 @@ void RasterizerOpenGL::DrawArrays() {
549 state.Apply(); 538 state.Apply();
550} 539}
551 540
552void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {} 541void RasterizerOpenGL::FlushAll() {}
553 542
554void RasterizerOpenGL::FlushAll() { 543void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {}
555 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
556}
557
558void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
559 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
560}
561 544
562void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 545void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
563 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 546 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
564 res_cache.InvalidateRegion(addr, size); 547 res_cache.InvalidateRegion(addr, size);
565 shader_cache.InvalidateRegion(addr, size); 548 shader_cache.InvalidateRegion(addr, size);
549 buffer_cache.InvalidateRegion(addr, size);
566} 550}
567 551
568void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 552void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
569 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
570 InvalidateRegion(addr, size); 553 InvalidateRegion(addr, size);
571} 554}
572 555
@@ -614,7 +597,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
614void RasterizerOpenGL::SamplerInfo::Create() { 597void RasterizerOpenGL::SamplerInfo::Create() {
615 sampler.Create(); 598 sampler.Create();
616 mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; 599 mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
617 wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap; 600 wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
618 601
619 // default is GL_LINEAR_MIPMAP_LINEAR 602 // default is GL_LINEAR_MIPMAP_LINEAR
620 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 603 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -622,7 +605,7 @@ void RasterizerOpenGL::SamplerInfo::Create() {
622} 605}
623 606
624void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 607void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
625 GLuint s = sampler.handle; 608 const GLuint s = sampler.handle;
626 609
627 if (mag_filter != config.mag_filter) { 610 if (mag_filter != config.mag_filter) {
628 mag_filter = config.mag_filter; 611 mag_filter = config.mag_filter;
@@ -641,8 +624,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
641 wrap_v = config.wrap_v; 624 wrap_v = config.wrap_v;
642 glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); 625 glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
643 } 626 }
627 if (wrap_p != config.wrap_p) {
628 wrap_p = config.wrap_p;
629 glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
630 }
644 631
645 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) { 632 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
633 wrap_p == Tegra::Texture::WrapMode::Border) {
646 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 634 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
647 config.border_color_b, config.border_color_a}}; 635 config.border_color_b, config.border_color_a}};
648 if (border_color != new_border_color) { 636 if (border_color != new_border_color) {
@@ -652,26 +640,35 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
652 } 640 }
653} 641}
654 642
655std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_ptr, 643u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
656 GLintptr buffer_offset, 644 u32 current_bindpoint) {
657 Maxwell::ShaderStage stage, 645 MICROPROFILE_SCOPE(OpenGL_UBO);
658 Shader& shader,
659 u32 current_bindpoint) {
660 const auto& gpu = Core::System::GetInstance().GPU(); 646 const auto& gpu = Core::System::GetInstance().GPU();
661 const auto& maxwell3d = gpu.Maxwell3D(); 647 const auto& maxwell3d = gpu.Maxwell3D();
662 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; 648 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
663 const auto& entries = shader->GetShaderEntries().const_buffer_entries; 649 const auto& entries = shader->GetShaderEntries().const_buffer_entries;
664 650
651 constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers;
652 std::array<GLuint, max_binds> bind_buffers;
653 std::array<GLintptr, max_binds> bind_offsets;
654 std::array<GLsizeiptr, max_binds> bind_sizes;
655
656 ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points.");
657
665 // Upload only the enabled buffers from the 16 constbuffers of each shader stage 658 // Upload only the enabled buffers from the 16 constbuffers of each shader stage
666 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 659 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
667 const auto& used_buffer = entries[bindpoint]; 660 const auto& used_buffer = entries[bindpoint];
668 const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; 661 const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()];
669 662
670 if (!buffer.enabled) { 663 if (!buffer.enabled) {
664 // With disabled buffers set values as zero to unbind them
665 bind_buffers[bindpoint] = 0;
666 bind_offsets[bindpoint] = 0;
667 bind_sizes[bindpoint] = 0;
671 continue; 668 continue;
672 } 669 }
673 670
674 size_t size = 0; 671 std::size_t size = 0;
675 672
676 if (used_buffer.IsIndirect()) { 673 if (used_buffer.IsIndirect()) {
677 // Buffer is accessed indirectly, so upload the entire thing 674 // Buffer is accessed indirectly, so upload the entire thing
@@ -692,26 +689,28 @@ std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_pt
692 size = Common::AlignUp(size, sizeof(GLvec4)); 689 size = Common::AlignUp(size, sizeof(GLvec4));
693 ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); 690 ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
694 691
695 GLintptr const_buffer_offset; 692 GLintptr const_buffer_offset = buffer_cache.UploadMemory(
696 std::tie(buffer_ptr, buffer_offset, const_buffer_offset) = 693 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
697 UploadMemory(buffer_ptr, buffer_offset, buffer.address, size,
698 static_cast<size_t>(uniform_buffer_alignment));
699
700 glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint,
701 stream_buffer.GetHandle(), const_buffer_offset, size);
702 694
703 // Now configure the bindpoint of the buffer inside the shader 695 // Now configure the bindpoint of the buffer inside the shader
704 glUniformBlockBinding(shader->GetProgramHandle(), 696 glUniformBlockBinding(shader->GetProgramHandle(),
705 shader->GetProgramResourceIndex(used_buffer.GetName()), 697 shader->GetProgramResourceIndex(used_buffer),
706 current_bindpoint + bindpoint); 698 current_bindpoint + bindpoint);
699
700 // Prepare values for multibind
701 bind_buffers[bindpoint] = buffer_cache.GetHandle();
702 bind_offsets[bindpoint] = const_buffer_offset;
703 bind_sizes[bindpoint] = size;
707 } 704 }
708 705
709 state.Apply(); 706 glBindBuffersRange(GL_UNIFORM_BUFFER, current_bindpoint, static_cast<GLsizei>(entries.size()),
707 bind_buffers.data(), bind_offsets.data(), bind_sizes.data());
710 708
711 return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())}; 709 return current_bindpoint + static_cast<u32>(entries.size());
712} 710}
713 711
714u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { 712u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
713 MICROPROFILE_SCOPE(OpenGL_Texture);
715 const auto& gpu = Core::System::GetInstance().GPU(); 714 const auto& gpu = Core::System::GetInstance().GPU();
716 const auto& maxwell3d = gpu.Maxwell3D(); 715 const auto& maxwell3d = gpu.Maxwell3D();
717 const auto& entries = shader->GetShaderEntries().texture_samplers; 716 const auto& entries = shader->GetShaderEntries().texture_samplers;
@@ -721,24 +720,25 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
721 720
722 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 721 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
723 const auto& entry = entries[bindpoint]; 722 const auto& entry = entries[bindpoint];
724 u32 current_bindpoint = current_unit + bindpoint; 723 const u32 current_bindpoint = current_unit + bindpoint;
725 724
726 // Bind the uniform to the sampler. 725 // Bind the uniform to the sampler.
727 726
728 glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry.GetName()), 727 glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
729 current_bindpoint); 728 current_bindpoint);
730 729
731 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); 730 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
732 731
733 if (!texture.enabled) { 732 if (!texture.enabled) {
734 state.texture_units[current_bindpoint].texture_2d = 0; 733 state.texture_units[current_bindpoint].texture = 0;
735 continue; 734 continue;
736 } 735 }
737 736
738 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 737 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
739 Surface surface = res_cache.GetTextureSurface(texture); 738 Surface surface = res_cache.GetTextureSurface(texture);
740 if (surface != nullptr) { 739 if (surface != nullptr) {
741 state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle; 740 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
741 state.texture_units[current_bindpoint].target = surface->Target();
742 state.texture_units[current_bindpoint].swizzle.r = 742 state.texture_units[current_bindpoint].swizzle.r =
743 MaxwellToGL::SwizzleSource(texture.tic.x_source); 743 MaxwellToGL::SwizzleSource(texture.tic.x_source);
744 state.texture_units[current_bindpoint].swizzle.g = 744 state.texture_units[current_bindpoint].swizzle.g =
@@ -749,47 +749,19 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
749 MaxwellToGL::SwizzleSource(texture.tic.w_source); 749 MaxwellToGL::SwizzleSource(texture.tic.w_source);
750 } else { 750 } else {
751 // Can occur when texture addr is null or its memory is unmapped/invalid 751 // Can occur when texture addr is null or its memory is unmapped/invalid
752 state.texture_units[current_bindpoint].texture_2d = 0; 752 state.texture_units[current_bindpoint].texture = 0;
753 } 753 }
754 } 754 }
755 755
756 state.Apply();
757
758 return current_unit + static_cast<u32>(entries.size()); 756 return current_unit + static_cast<u32>(entries.size());
759} 757}
760 758
761void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, 759void RasterizerOpenGL::SyncViewport() {
762 const Surface& depth_surface, bool has_stencil) {
763 state.draw.draw_framebuffer = framebuffer.handle;
764 state.Apply();
765
766 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
767 color_surface != nullptr ? color_surface->Texture().handle : 0, 0);
768 if (depth_surface != nullptr) {
769 if (has_stencil) {
770 // attach both depth and stencil
771 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
772 depth_surface->Texture().handle, 0);
773 } else {
774 // attach depth
775 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
776 depth_surface->Texture().handle, 0);
777 // clear stencil attachment
778 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
779 }
780 } else {
781 // clear both depth and stencil attachment
782 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
783 0);
784 }
785}
786
787void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) {
788 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 760 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
789 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 761 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
790 762
791 state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left; 763 state.viewport.x = viewport_rect.left;
792 state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom; 764 state.viewport.y = viewport_rect.bottom;
793 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); 765 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
794 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); 766 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
795} 767}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 30045ebff..bf9560bdc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -6,19 +6,23 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <map>
9#include <memory> 10#include <memory>
10#include <tuple> 11#include <tuple>
11#include <utility> 12#include <utility>
12#include <vector> 13#include <vector>
13 14
14#include <boost/icl/interval_map.hpp> 15#include <boost/icl/interval_map.hpp>
16#include <boost/optional.hpp>
15#include <boost/range/iterator_range.hpp> 17#include <boost/range/iterator_range.hpp>
16#include <glad/glad.h> 18#include <glad/glad.h>
17 19
18#include "common/common_types.h" 20#include "common/common_types.h"
19#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
20#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/rasterizer_cache.h"
21#include "video_core/rasterizer_interface.h" 24#include "video_core/rasterizer_interface.h"
25#include "video_core/renderer_opengl/gl_buffer_cache.h"
22#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 26#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
23#include "video_core/renderer_opengl/gl_resource_manager.h" 27#include "video_core/renderer_opengl/gl_resource_manager.h"
24#include "video_core/renderer_opengl/gl_shader_cache.h" 28#include "video_core/renderer_opengl/gl_shader_cache.h"
@@ -42,7 +46,6 @@ public:
42 46
43 void DrawArrays() override; 47 void DrawArrays() override;
44 void Clear() override; 48 void Clear() override;
45 void NotifyMaxwellRegisterChanged(u32 method) override;
46 void FlushAll() override; 49 void FlushAll() override;
47 void FlushRegion(VAddr addr, u64 size) override; 50 void FlushRegion(VAddr addr, u64 size) override;
48 void InvalidateRegion(VAddr addr, u64 size) override; 51 void InvalidateRegion(VAddr addr, u64 size) override;
@@ -70,7 +73,7 @@ public:
70 }; 73 };
71 74
72 /// Maximum supported size that a constbuffer can have in bytes. 75 /// Maximum supported size that a constbuffer can have in bytes.
73 static constexpr size_t MaxConstbufferSize = 0x10000; 76 static constexpr std::size_t MaxConstbufferSize = 0x10000;
74 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, 77 static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
75 "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); 78 "The maximum size of a constbuffer must be a multiple of the size of GLvec4");
76 79
@@ -90,17 +93,20 @@ private:
90 Tegra::Texture::TextureFilter min_filter; 93 Tegra::Texture::TextureFilter min_filter;
91 Tegra::Texture::WrapMode wrap_u; 94 Tegra::Texture::WrapMode wrap_u;
92 Tegra::Texture::WrapMode wrap_v; 95 Tegra::Texture::WrapMode wrap_v;
96 Tegra::Texture::WrapMode wrap_p;
93 GLvec4 border_color; 97 GLvec4 border_color;
94 }; 98 };
95 99
96 /// Configures the color and depth framebuffer states and returns the dirty <Color, Depth> 100 /**
97 /// surfaces if writing was enabled. 101 * Configures the color and depth framebuffer states.
98 std::pair<Surface, Surface> ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 102 * @param use_color_fb If true, configure color framebuffers.
99 bool preserve_contents); 103 * @param using_depth_fb If true, configure the depth/stencil framebuffer.
100 104 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
101 /// Binds the framebuffer color and depth surface 105 * @param single_color_target Specifies if a single color buffer target should be used.
102 void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, 106 */
103 bool has_stencil); 107 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true,
108 bool preserve_contents = true,
109 boost::optional<std::size_t> single_color_target = {});
104 110
105 /* 111 /*
106 * Configures the current constbuffers to use for the draw command. 112 * Configures the current constbuffers to use for the draw command.
@@ -109,9 +115,8 @@ private:
109 * @param current_bindpoint The offset at which to start counting new buffer bindpoints. 115 * @param current_bindpoint The offset at which to start counting new buffer bindpoints.
110 * @returns The next available bindpoint for use in the next shader stage. 116 * @returns The next available bindpoint for use in the next shader stage.
111 */ 117 */
112 std::tuple<u8*, GLintptr, u32> SetupConstBuffers( 118 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
113 u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 119 u32 current_bindpoint);
114 Shader& shader, u32 current_bindpoint);
115 120
116 /* 121 /*
117 * Configures the current textures to use for the draw command. 122 * Configures the current textures to use for the draw command.
@@ -124,7 +129,7 @@ private:
124 u32 current_unit); 129 u32 current_unit);
125 130
126 /// Syncs the viewport to match the guest state 131 /// Syncs the viewport to match the guest state
127 void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect); 132 void SyncViewport();
128 133
129 /// Syncs the clip enabled status to match the guest state 134 /// Syncs the clip enabled status to match the guest state
130 void SyncClipEnabled(); 135 void SyncClipEnabled();
@@ -154,6 +159,7 @@ private:
154 void SyncLogicOpState(); 159 void SyncLogicOpState();
155 160
156 bool has_ARB_direct_state_access = false; 161 bool has_ARB_direct_state_access = false;
162 bool has_ARB_multi_bind = false;
157 bool has_ARB_separate_shader_objects = false; 163 bool has_ARB_separate_shader_objects = false;
158 bool has_ARB_vertex_attrib_binding = false; 164 bool has_ARB_vertex_attrib_binding = false;
159 165
@@ -167,28 +173,23 @@ private:
167 ScreenInfo& screen_info; 173 ScreenInfo& screen_info;
168 174
169 std::unique_ptr<GLShader::ProgramManager> shader_program_manager; 175 std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
170 OGLVertexArray sw_vao; 176 std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
171 OGLVertexArray hw_vao; 177 Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
178 OGLVertexArray>
179 vertex_array_cache;
172 180
173 std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; 181 std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
174 182
175 static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 183 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
176 OGLStreamBuffer stream_buffer; 184 OGLBufferCache buffer_cache;
177 OGLBuffer uniform_buffer;
178 OGLFramebuffer framebuffer; 185 OGLFramebuffer framebuffer;
179 GLint uniform_buffer_alignment; 186 GLint uniform_buffer_alignment;
180 187
181 size_t CalculateVertexArraysSize() const; 188 std::size_t CalculateVertexArraysSize() const;
182
183 std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset);
184
185 std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset);
186 189
187 std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); 190 void SetupVertexArrays();
188 191
189 std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset, 192 void SetupShaders();
190 Tegra::GPUVAddr gpu_addr, size_t size,
191 size_t alignment = 4);
192 193
193 enum class AccelDraw { Disabled, Arrays, Indexed }; 194 enum class AccelDraw { Disabled, Arrays, Indexed };
194 AccelDraw accelerate_draw = AccelDraw::Disabled; 195 AccelDraw accelerate_draw = AccelDraw::Disabled;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 1965ab7d5..86682d7cb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/logging/log.h"
10#include "common/microprofile.h" 11#include "common/microprofile.h"
11#include "common/scope_exit.h" 12#include "common/scope_exit.h"
12#include "core/core.h" 13#include "core/core.h"
@@ -52,14 +53,30 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
52 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); 53 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
53 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); 54 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
54 params.unaligned_height = config.tic.Height(); 55 params.unaligned_height = config.tic.Height();
56 params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
57
58 switch (params.target) {
59 case SurfaceTarget::Texture1D:
60 case SurfaceTarget::Texture2D:
61 params.depth = 1;
62 break;
63 case SurfaceTarget::Texture3D:
64 case SurfaceTarget::Texture2DArray:
65 params.depth = config.tic.Depth();
66 break;
67 default:
68 LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
69 UNREACHABLE();
70 params.depth = 1;
71 break;
72 }
73
55 params.size_in_bytes = params.SizeInBytes(); 74 params.size_in_bytes = params.SizeInBytes();
56 params.cache_width = Common::AlignUp(params.width, 16);
57 params.cache_height = Common::AlignUp(params.height, 16);
58 return params; 75 return params;
59} 76}
60 77
61/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer( 78/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
62 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) { 79 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
63 SurfaceParams params{}; 80 SurfaceParams params{};
64 params.addr = TryGetCpuAddr(config.Address()); 81 params.addr = TryGetCpuAddr(config.Address());
65 params.is_tiled = true; 82 params.is_tiled = true;
@@ -70,9 +87,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
70 params.width = config.width; 87 params.width = config.width;
71 params.height = config.height; 88 params.height = config.height;
72 params.unaligned_height = config.height; 89 params.unaligned_height = config.height;
90 params.target = SurfaceTarget::Texture2D;
91 params.depth = 1;
73 params.size_in_bytes = params.SizeInBytes(); 92 params.size_in_bytes = params.SizeInBytes();
74 params.cache_width = Common::AlignUp(params.width, 16);
75 params.cache_height = Common::AlignUp(params.height, 16);
76 return params; 93 return params;
77} 94}
78 95
@@ -86,13 +103,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
86 params.pixel_format = PixelFormatFromDepthFormat(format); 103 params.pixel_format = PixelFormatFromDepthFormat(format);
87 params.component_type = ComponentTypeFromDepthFormat(format); 104 params.component_type = ComponentTypeFromDepthFormat(format);
88 params.type = GetFormatType(params.pixel_format); 105 params.type = GetFormatType(params.pixel_format);
89 params.size_in_bytes = params.SizeInBytes();
90 params.width = zeta_width; 106 params.width = zeta_width;
91 params.height = zeta_height; 107 params.height = zeta_height;
92 params.unaligned_height = zeta_height; 108 params.unaligned_height = zeta_height;
109 params.target = SurfaceTarget::Texture2D;
110 params.depth = 1;
93 params.size_in_bytes = params.SizeInBytes(); 111 params.size_in_bytes = params.SizeInBytes();
94 params.cache_width = Common::AlignUp(params.width, 16);
95 params.cache_height = Common::AlignUp(params.height, 16);
96 return params; 112 return params;
97} 113}
98 114
@@ -100,7 +116,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
100 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U 116 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
101 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S 117 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
102 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI 118 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
103 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U 119 {GL_RGB8, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U
104 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, 120 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm,
105 false}, // A2B10G10R10U 121 false}, // A2B10G10R10U
106 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U 122 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U
@@ -151,6 +167,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
151 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 167 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
152 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 168 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
153 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 169 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
170 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
154 171
155 // Depth formats 172 // Depth formats
156 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 173 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -166,8 +183,28 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
166 ComponentType::Float, false}, // Z32FS8 183 ComponentType::Float, false}, // Z32FS8
167}}; 184}};
168 185
186static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) {
187 switch (target) {
188 case SurfaceParams::SurfaceTarget::Texture1D:
189 return GL_TEXTURE_1D;
190 case SurfaceParams::SurfaceTarget::Texture2D:
191 return GL_TEXTURE_2D;
192 case SurfaceParams::SurfaceTarget::Texture3D:
193 return GL_TEXTURE_3D;
194 case SurfaceParams::SurfaceTarget::Texture1DArray:
195 return GL_TEXTURE_1D_ARRAY;
196 case SurfaceParams::SurfaceTarget::Texture2DArray:
197 return GL_TEXTURE_2D_ARRAY;
198 case SurfaceParams::SurfaceTarget::TextureCubemap:
199 return GL_TEXTURE_CUBE_MAP;
200 }
201 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
202 UNREACHABLE();
203 return {};
204}
205
169static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { 206static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
170 ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); 207 ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size());
171 auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; 208 auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)];
172 ASSERT(component_type == format.component_type); 209 ASSERT(component_type == format.component_type);
173 210
@@ -177,6 +214,7 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
177static bool IsPixelFormatASTC(PixelFormat format) { 214static bool IsPixelFormatASTC(PixelFormat format) {
178 switch (format) { 215 switch (format) {
179 case PixelFormat::ASTC_2D_4X4: 216 case PixelFormat::ASTC_2D_4X4:
217 case PixelFormat::ASTC_2D_8X8:
180 return true; 218 return true;
181 default: 219 default:
182 return false; 220 return false;
@@ -187,6 +225,8 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
187 switch (format) { 225 switch (format) {
188 case PixelFormat::ASTC_2D_4X4: 226 case PixelFormat::ASTC_2D_4X4:
189 return {4, 4}; 227 return {4, 4};
228 case PixelFormat::ASTC_2D_8X8:
229 return {8, 8};
190 default: 230 default:
191 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); 231 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
192 UNREACHABLE(); 232 UNREACHABLE();
@@ -220,7 +260,8 @@ static bool IsFormatBCn(PixelFormat format) {
220} 260}
221 261
222template <bool morton_to_gl, PixelFormat format> 262template <bool morton_to_gl, PixelFormat format>
223void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) { 263void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size,
264 VAddr addr) {
224 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 265 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
225 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 266 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
226 267
@@ -230,18 +271,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
230 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 271 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
231 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 272 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
232 addr, tile_size, bytes_per_pixel, stride, height, block_height); 273 addr, tile_size, bytes_per_pixel, stride, height, block_height);
233 const size_t size_to_copy{std::min(gl_buffer.size(), data.size())}; 274 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
234 gl_buffer.assign(data.begin(), data.begin() + size_to_copy); 275 memcpy(gl_buffer, data.data(), size_to_copy);
235 } else { 276 } else {
236 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 277 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
237 // check the configuration for this and perform more generic un/swizzle 278 // check the configuration for this and perform more generic un/swizzle
238 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 279 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
239 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, 280 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
240 Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl); 281 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
241 } 282 }
242} 283}
243 284
244static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), 285static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
245 SurfaceParams::MaxPixelFormat> 286 SurfaceParams::MaxPixelFormat>
246 morton_to_gl_fns = { 287 morton_to_gl_fns = {
247 // clang-format off 288 // clang-format off
@@ -290,6 +331,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
290 MortonCopy<true, PixelFormat::RG8S>, 331 MortonCopy<true, PixelFormat::RG8S>,
291 MortonCopy<true, PixelFormat::RG32UI>, 332 MortonCopy<true, PixelFormat::RG32UI>,
292 MortonCopy<true, PixelFormat::R32UI>, 333 MortonCopy<true, PixelFormat::R32UI>,
334 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
293 MortonCopy<true, PixelFormat::Z32F>, 335 MortonCopy<true, PixelFormat::Z32F>,
294 MortonCopy<true, PixelFormat::Z16>, 336 MortonCopy<true, PixelFormat::Z16>,
295 MortonCopy<true, PixelFormat::Z24S8>, 337 MortonCopy<true, PixelFormat::Z24S8>,
@@ -298,7 +340,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
298 // clang-format on 340 // clang-format on
299}; 341};
300 342
301static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), 343static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
302 SurfaceParams::MaxPixelFormat> 344 SurfaceParams::MaxPixelFormat>
303 gl_to_morton_fns = { 345 gl_to_morton_fns = {
304 // clang-format off 346 // clang-format off
@@ -349,6 +391,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
349 MortonCopy<false, PixelFormat::RG8S>, 391 MortonCopy<false, PixelFormat::RG8S>,
350 MortonCopy<false, PixelFormat::RG32UI>, 392 MortonCopy<false, PixelFormat::RG32UI>,
351 MortonCopy<false, PixelFormat::R32UI>, 393 MortonCopy<false, PixelFormat::R32UI>,
394 nullptr,
352 MortonCopy<false, PixelFormat::Z32F>, 395 MortonCopy<false, PixelFormat::Z32F>,
353 MortonCopy<false, PixelFormat::Z16>, 396 MortonCopy<false, PixelFormat::Z16>,
354 MortonCopy<false, PixelFormat::Z24S8>, 397 MortonCopy<false, PixelFormat::Z24S8>,
@@ -357,33 +400,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
357 // clang-format on 400 // clang-format on
358}; 401};
359 402
360// Allocate an uninitialized texture of appropriate size and format for the surface
361static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
362 u32 height) {
363 OpenGLState cur_state = OpenGLState::GetCurState();
364
365 // Keep track of previous texture bindings
366 GLuint old_tex = cur_state.texture_units[0].texture_2d;
367 cur_state.texture_units[0].texture_2d = texture;
368 cur_state.Apply();
369 glActiveTexture(GL_TEXTURE0);
370
371 if (!format_tuple.compressed) {
372 // Only pre-create the texture for non-compressed textures.
373 glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
374 format_tuple.format, format_tuple.type, nullptr);
375 }
376
377 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
380 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
381
382 // Restore previous texture bindings
383 cur_state.texture_units[0].texture_2d = old_tex;
384 cur_state.Apply();
385}
386
387static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, 403static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
388 const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, 404 const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
389 GLuint read_fb_handle, GLuint draw_fb_handle) { 405 GLuint read_fb_handle, GLuint draw_fb_handle) {
@@ -438,12 +454,53 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
438 return true; 454 return true;
439} 455}
440 456
441CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { 457CachedSurface::CachedSurface(const SurfaceParams& params)
458 : params(params), gl_target(SurfaceTargetToGL(params.target)) {
442 texture.Create(); 459 texture.Create();
443 const auto& rect{params.GetRect()}; 460 const auto& rect{params.GetRect()};
444 AllocateSurfaceTexture(texture.handle, 461
445 GetFormatTuple(params.pixel_format, params.component_type), 462 // Keep track of previous texture bindings
463 OpenGLState cur_state = OpenGLState::GetCurState();
464 const auto& old_tex = cur_state.texture_units[0];
465 SCOPE_EXIT({
466 cur_state.texture_units[0] = old_tex;
467 cur_state.Apply();
468 });
469
470 cur_state.texture_units[0].texture = texture.handle;
471 cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
472 cur_state.Apply();
473 glActiveTexture(GL_TEXTURE0);
474
475 const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
476 if (!format_tuple.compressed) {
477 // Only pre-create the texture for non-compressed textures.
478 switch (params.target) {
479 case SurfaceParams::SurfaceTarget::Texture1D:
480 glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format,
481 rect.GetWidth());
482 break;
483 case SurfaceParams::SurfaceTarget::Texture2D:
484 glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format,
446 rect.GetWidth(), rect.GetHeight()); 485 rect.GetWidth(), rect.GetHeight());
486 break;
487 case SurfaceParams::SurfaceTarget::Texture3D:
488 case SurfaceParams::SurfaceTarget::Texture2DArray:
489 glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format,
490 rect.GetWidth(), rect.GetHeight(), params.depth);
491 break;
492 default:
493 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
494 static_cast<u32>(params.target));
495 UNREACHABLE();
496 glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(),
497 rect.GetHeight());
498 }
499 }
500
501 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
502 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
503 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
447} 504}
448 505
449static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 506static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@@ -461,10 +518,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
461 518
462 S8Z24 input_pixel{}; 519 S8Z24 input_pixel{};
463 Z24S8 output_pixel{}; 520 Z24S8 output_pixel{};
464 const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 521 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
465 for (size_t y = 0; y < height; ++y) { 522 for (std::size_t y = 0; y < height; ++y) {
466 for (size_t x = 0; x < width; ++x) { 523 for (std::size_t x = 0; x < width; ++x) {
467 const size_t offset{bpp * (y * width + x)}; 524 const std::size_t offset{bpp * (y * width + x)};
468 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 525 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
469 output_pixel.s8.Assign(input_pixel.s8); 526 output_pixel.s8.Assign(input_pixel.s8);
470 output_pixel.z24.Assign(input_pixel.z24); 527 output_pixel.z24.Assign(input_pixel.z24);
@@ -474,10 +531,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
474} 531}
475 532
476static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 533static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
477 const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 534 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)};
478 for (size_t y = 0; y < height; ++y) { 535 for (std::size_t y = 0; y < height; ++y) {
479 for (size_t x = 0; x < width; ++x) { 536 for (std::size_t x = 0; x < width; ++x) {
480 const size_t offset{bpp * (y * width + x)}; 537 const std::size_t offset{bpp * (y * width + x)};
481 const u8 temp{data[offset]}; 538 const u8 temp{data[offset]};
482 data[offset] = data[offset + 1]; 539 data[offset] = data[offset + 1];
483 data[offset + 1] = temp; 540 data[offset + 1] = temp;
@@ -493,7 +550,8 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
493static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 550static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
494 u32 width, u32 height) { 551 u32 width, u32 height) {
495 switch (pixel_format) { 552 switch (pixel_format) {
496 case PixelFormat::ASTC_2D_4X4: { 553 case PixelFormat::ASTC_2D_4X4:
554 case PixelFormat::ASTC_2D_8X8: {
497 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 555 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
498 u32 block_width{}; 556 u32 block_width{};
499 u32 block_height{}; 557 u32 block_height{};
@@ -514,23 +572,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
514 } 572 }
515} 573}
516 574
517/**
518 * Helper function to perform software conversion (as needed) when flushing a buffer to Switch
519 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
520 * typical desktop GPUs.
521 */
522static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format,
523 u32 /*width*/, u32 /*height*/) {
524 switch (pixel_format) {
525 case PixelFormat::ASTC_2D_4X4:
526 case PixelFormat::S8Z24:
527 LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}",
528 static_cast<u32>(pixel_format));
529 UNREACHABLE();
530 break;
531 }
532}
533
534MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 575MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
535void CachedSurface::LoadGLBuffer() { 576void CachedSurface::LoadGLBuffer() {
536 ASSERT(params.type != SurfaceType::Fill); 577 ASSERT(params.type != SurfaceType::Fill);
@@ -545,13 +586,25 @@ void CachedSurface::LoadGLBuffer() {
545 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 586 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
546 587
547 if (params.is_tiled) { 588 if (params.is_tiled) {
548 gl_buffer.resize(copy_size); 589 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
590 // this for 3D textures, etc.
591 switch (params.target) {
592 case SurfaceParams::SurfaceTarget::Texture2D:
593 // Pass impl. to the fallback code below
594 break;
595 default:
596 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
597 static_cast<u32>(params.target));
598 UNREACHABLE();
599 }
549 600
550 morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( 601 gl_buffer.resize(static_cast<std::size_t>(params.depth) * copy_size);
551 params.width, params.block_height, params.height, gl_buffer, params.addr); 602 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
603 params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
604 params.addr);
552 } else { 605 } else {
553 const u8* const texture_src_data_end = texture_src_data + copy_size; 606 const u8* const texture_src_data_end{texture_src_data +
554 607 (static_cast<std::size_t>(params.depth) * copy_size)};
555 gl_buffer.assign(texture_src_data, texture_src_data_end); 608 gl_buffer.assign(texture_src_data, texture_src_data_end);
556 } 609 }
557 610
@@ -560,23 +613,7 @@ void CachedSurface::LoadGLBuffer() {
560 613
561MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 614MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
562void CachedSurface::FlushGLBuffer() { 615void CachedSurface::FlushGLBuffer() {
563 u8* const dst_buffer = Memory::GetPointer(params.addr); 616 ASSERT_MSG(false, "Unimplemented");
564
565 ASSERT(dst_buffer);
566 ASSERT(gl_buffer.size() ==
567 params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
568
569 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
570
571 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
572 params.height);
573
574 if (!params.is_tiled) {
575 std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
576 } else {
577 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
578 params.width, params.block_height, params.height, gl_buffer, params.addr);
579 }
580} 617}
581 618
582MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 619MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -586,22 +623,30 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
586 623
587 MICROPROFILE_SCOPE(OpenGL_TextureUL); 624 MICROPROFILE_SCOPE(OpenGL_TextureUL);
588 625
589 ASSERT(gl_buffer.size() == 626 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
590 params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); 627 GetGLBytesPerPixel(params.pixel_format) * params.depth);
591 628
592 const auto& rect{params.GetRect()}; 629 const auto& rect{params.GetRect()};
593 630
594 // Load data from memory to the surface 631 // Load data from memory to the surface
595 GLint x0 = static_cast<GLint>(rect.left); 632 const GLint x0 = static_cast<GLint>(rect.left);
596 GLint y0 = static_cast<GLint>(rect.bottom); 633 const GLint y0 = static_cast<GLint>(rect.bottom);
597 size_t buffer_offset = (y0 * params.width + x0) * GetGLBytesPerPixel(params.pixel_format); 634 const std::size_t buffer_offset =
635 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
636 static_cast<std::size_t>(x0)) *
637 GetGLBytesPerPixel(params.pixel_format);
598 638
599 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 639 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
600 GLuint target_tex = texture.handle; 640 const GLuint target_tex = texture.handle;
601 OpenGLState cur_state = OpenGLState::GetCurState(); 641 OpenGLState cur_state = OpenGLState::GetCurState();
602 642
603 GLuint old_tex = cur_state.texture_units[0].texture_2d; 643 const auto& old_tex = cur_state.texture_units[0];
604 cur_state.texture_units[0].texture_2d = target_tex; 644 SCOPE_EXIT({
645 cur_state.texture_units[0] = old_tex;
646 cur_state.Apply();
647 });
648 cur_state.texture_units[0].texture = target_tex;
649 cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
605 cur_state.Apply(); 650 cur_state.Apply();
606 651
607 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 652 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
@@ -610,136 +655,102 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
610 655
611 glActiveTexture(GL_TEXTURE0); 656 glActiveTexture(GL_TEXTURE0);
612 if (tuple.compressed) { 657 if (tuple.compressed) {
613 glCompressedTexImage2D( 658 switch (params.target) {
614 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 659 case SurfaceParams::SurfaceTarget::Texture2D:
615 static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), 660 glCompressedTexImage2D(
616 &gl_buffer[buffer_offset]); 661 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
662 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
663 static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]);
664 break;
665 case SurfaceParams::SurfaceTarget::Texture3D:
666 case SurfaceParams::SurfaceTarget::Texture2DArray:
667 glCompressedTexImage3D(
668 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
669 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
670 static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes),
671 &gl_buffer[buffer_offset]);
672 break;
673 default:
674 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
675 static_cast<u32>(params.target));
676 UNREACHABLE();
677 glCompressedTexImage2D(
678 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
679 static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
680 &gl_buffer[buffer_offset]);
681 }
617 } else { 682 } else {
618 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
619 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
620 &gl_buffer[buffer_offset]);
621 }
622
623 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
624
625 cur_state.texture_units[0].texture_2d = old_tex;
626 cur_state.Apply();
627}
628
629MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
630void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
631 if (params.type == SurfaceType::Fill)
632 return;
633
634 MICROPROFILE_SCOPE(OpenGL_TextureDL);
635
636 gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
637
638 OpenGLState state = OpenGLState::GetCurState();
639 OpenGLState prev_state = state;
640 SCOPE_EXIT({ prev_state.Apply(); });
641
642 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
643
644 // Ensure no bad interactions with GL_PACK_ALIGNMENT
645 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
646 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
647
648 const auto& rect{params.GetRect()};
649 size_t buffer_offset =
650 (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format);
651
652 state.UnbindTexture(texture.handle);
653 state.draw.read_framebuffer = read_fb_handle;
654 state.Apply();
655 683
656 if (params.type == SurfaceType::ColorTexture) { 684 switch (params.target) {
657 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 685 case SurfaceParams::SurfaceTarget::Texture1D:
658 texture.handle, 0); 686 glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0,
659 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 687 static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
660 0); 688 &gl_buffer[buffer_offset]);
661 } else if (params.type == SurfaceType::Depth) { 689 break;
662 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); 690 case SurfaceParams::SurfaceTarget::Texture2D:
663 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 691 glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0,
664 texture.handle, 0); 692 static_cast<GLsizei>(rect.GetWidth()),
665 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); 693 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
666 } else { 694 &gl_buffer[buffer_offset]);
667 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); 695 break;
668 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 696 case SurfaceParams::SurfaceTarget::Texture3D:
669 texture.handle, 0); 697 case SurfaceParams::SurfaceTarget::Texture2DArray:
698 glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0,
699 static_cast<GLsizei>(rect.GetWidth()),
700 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
701 tuple.type, &gl_buffer[buffer_offset]);
702 break;
703 default:
704 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
705 static_cast<u32>(params.target));
706 UNREACHABLE();
707 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
708 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
709 &gl_buffer[buffer_offset]);
710 }
670 } 711 }
671 glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
672 static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
673 tuple.format, tuple.type, &gl_buffer[buffer_offset]);
674 712
675 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 713 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
676} 714}
677 715
678RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 716RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
679 read_framebuffer.Create(); 717 read_framebuffer.Create();
680 draw_framebuffer.Create(); 718 draw_framebuffer.Create();
719 copy_pbo.Create();
681} 720}
682 721
683Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { 722Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
684 return GetSurface(SurfaceParams::CreateForTexture(config)); 723 return GetSurface(SurfaceParams::CreateForTexture(config));
685} 724}
686 725
687SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb, 726Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
688 bool using_depth_fb, 727 const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
689 bool preserve_contents) { 728 if (!regs.zeta.Address() || !regs.zeta_enable) {
690 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 729 return {};
730 }
691 731
692 // TODO(bunnei): This is hard corded to use just the first render buffer 732 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
693 LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); 733 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)};
694 734
695 // get color and depth surfaces 735 return GetSurface(depth_params, preserve_contents);
696 SurfaceParams color_params{}; 736}
697 SurfaceParams depth_params{};
698 737
699 if (using_color_fb) { 738Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool preserve_contents) {
700 color_params = SurfaceParams::CreateForFramebuffer(regs.rt[0]); 739 const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
701 }
702 740
703 if (using_depth_fb) { 741 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
704 depth_params = SurfaceParams::CreateForDepthBuffer(regs.zeta_width, regs.zeta_height,
705 regs.zeta.Address(), regs.zeta.format);
706 }
707 742
708 MathUtil::Rectangle<u32> color_rect{}; 743 if (index >= regs.rt_control.count) {
709 Surface color_surface; 744 return {};
710 if (using_color_fb) {
711 color_surface = GetSurface(color_params, preserve_contents);
712 if (color_surface) {
713 color_rect = color_surface->GetSurfaceParams().GetRect();
714 }
715 } 745 }
716 746
717 MathUtil::Rectangle<u32> depth_rect{}; 747 if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
718 Surface depth_surface; 748 return {};
719 if (using_depth_fb) {
720 depth_surface = GetSurface(depth_params, preserve_contents);
721 if (depth_surface) {
722 depth_rect = depth_surface->GetSurfaceParams().GetRect();
723 }
724 } 749 }
725 750
726 MathUtil::Rectangle<u32> fb_rect{}; 751 const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)};
727 if (color_surface && depth_surface) {
728 fb_rect = color_rect;
729 // Color and Depth surfaces must have the same dimensions and offsets
730 if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top ||
731 color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) {
732 color_surface = GetSurface(color_params);
733 depth_surface = GetSurface(depth_params);
734 fb_rect = color_surface->GetSurfaceParams().GetRect();
735 }
736 } else if (color_surface) {
737 fb_rect = color_rect;
738 } else if (depth_surface) {
739 fb_rect = depth_rect;
740 }
741 752
742 return std::make_tuple(color_surface, depth_surface, fb_rect); 753 return GetSurface(color_params, preserve_contents);
743} 754}
744 755
745void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 756void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
@@ -748,7 +759,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
748} 759}
749 760
750void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { 761void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
751 surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
752 surface->FlushGLBuffer(); 762 surface->FlushGLBuffer();
753} 763}
754 764
@@ -806,27 +816,26 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
806 // Get a new surface with the new parameters, and blit the previous surface to it 816 // Get a new surface with the new parameters, and blit the previous surface to it
807 Surface new_surface{GetUncachedSurface(new_params)}; 817 Surface new_surface{GetUncachedSurface(new_params)};
808 818
809 // If format is unchanged, we can do a faster blit without reinterpreting pixel data 819 if (params.pixel_format == new_params.pixel_format ||
810 if (params.pixel_format == new_params.pixel_format) { 820 !Settings::values.use_accurate_framebuffers) {
821 // If the format is the same, just do a framebuffer blit. This is significantly faster than
822 // using PBOs. The is also likely less accurate, as textures will be converted rather than
823 // reinterpreted.
824
811 BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, 825 BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
812 new_surface->GetSurfaceParams().GetRect(), params.type, 826 params.GetRect(), params.type, read_framebuffer.handle,
813 read_framebuffer.handle, draw_framebuffer.handle); 827 draw_framebuffer.handle);
814 return new_surface; 828 } else {
815 } 829 // When use_accurate_framebuffers setting is enabled, perform a more accurate surface copy,
830 // where pixels are reinterpreted as a new format (without conversion). This code path uses
831 // OpenGL PBOs and is quite slow.
816 832
817 // When using accurate framebuffers, always copy old data to new surface, regardless of format
818 if (Settings::values.use_accurate_framebuffers) {
819 auto source_format = GetFormatTuple(params.pixel_format, params.component_type); 833 auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
820 auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type); 834 auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
821 835
822 size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); 836 std::size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
823 837
824 // Use a Pixel Buffer Object to download the previous texture and then upload it to the new 838 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle);
825 // one using the new format.
826 OGLBuffer pbo;
827 pbo.Create();
828
829 glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
830 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 839 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
831 if (source_format.compressed) { 840 if (source_format.compressed) {
832 glGetCompressedTextureImage(surface->Texture().handle, 0, 841 glGetCompressedTextureImage(surface->Texture().handle, 0,
@@ -845,10 +854,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
845 // of the data in this case. Games like Super Mario Odyssey seem to hit this case 854 // of the data in this case. Games like Super Mario Odyssey seem to hit this case
846 // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer 855 // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
847 // but it doesn't clear it beforehand, the texture is already full of zeros. 856 // but it doesn't clear it beforehand, the texture is already full of zeros.
848 LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during " 857 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
849 "reinterpretation but the texture is tiled."); 858 "reinterpretation but the texture is tiled.");
850 } 859 }
851 size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); 860 std::size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
852 std::vector<u8> data(remaining_size); 861 std::vector<u8> data(remaining_size);
853 Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size()); 862 Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size());
854 glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, 863 glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size,
@@ -859,21 +868,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
859 868
860 const auto& dest_rect{new_params.GetRect()}; 869 const auto& dest_rect{new_params.GetRect()};
861 870
862 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle); 871 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle);
863 if (dest_format.compressed) { 872 if (dest_format.compressed) {
864 glCompressedTexSubImage2D( 873 LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
865 GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()), 874 UNREACHABLE();
866 static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
867 static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
868 } else { 875 } else {
869 glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0, 876 switch (new_params.target) {
870 static_cast<GLsizei>(dest_rect.GetWidth()), 877 case SurfaceParams::SurfaceTarget::Texture1D:
871 static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, 878 glTextureSubImage1D(new_surface->Texture().handle, 0, 0,
872 dest_format.type, nullptr); 879 static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format,
880 dest_format.type, nullptr);
881 break;
882 case SurfaceParams::SurfaceTarget::Texture2D:
883 glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
884 static_cast<GLsizei>(dest_rect.GetWidth()),
885 static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
886 dest_format.type, nullptr);
887 break;
888 case SurfaceParams::SurfaceTarget::Texture3D:
889 case SurfaceParams::SurfaceTarget::Texture2DArray:
890 glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0,
891 static_cast<GLsizei>(dest_rect.GetWidth()),
892 static_cast<GLsizei>(dest_rect.GetHeight()),
893 static_cast<GLsizei>(new_params.depth), dest_format.format,
894 dest_format.type, nullptr);
895 break;
896 default:
897 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
898 static_cast<u32>(params.target));
899 UNREACHABLE();
900 }
873 } 901 }
874 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 902 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
875
876 pbo.Release();
877 } 903 }
878 904
879 return new_surface; 905 return new_surface;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index aad75f200..d7a4bc37f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -70,19 +70,20 @@ struct SurfaceParams {
70 RG8S = 42, 70 RG8S = 42,
71 RG32UI = 43, 71 RG32UI = 43,
72 R32UI = 44, 72 R32UI = 44,
73 ASTC_2D_8X8 = 45,
73 74
74 MaxColorFormat, 75 MaxColorFormat,
75 76
76 // Depth formats 77 // Depth formats
77 Z32F = 45, 78 Z32F = 46,
78 Z16 = 46, 79 Z16 = 47,
79 80
80 MaxDepthFormat, 81 MaxDepthFormat,
81 82
82 // DepthStencil formats 83 // DepthStencil formats
83 Z24S8 = 47, 84 Z24S8 = 48,
84 S8Z24 = 48, 85 S8Z24 = 49,
85 Z32FS8 = 49, 86 Z32FS8 = 50,
86 87
87 MaxDepthStencilFormat, 88 MaxDepthStencilFormat,
88 89
@@ -90,7 +91,7 @@ struct SurfaceParams {
90 Invalid = 255, 91 Invalid = 255,
91 }; 92 };
92 93
93 static constexpr size_t MaxPixelFormat = static_cast<size_t>(PixelFormat::Max); 94 static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
94 95
95 enum class ComponentType { 96 enum class ComponentType {
96 Invalid = 0, 97 Invalid = 0,
@@ -109,6 +110,33 @@ struct SurfaceParams {
109 Invalid = 4, 110 Invalid = 4,
110 }; 111 };
111 112
113 enum class SurfaceTarget {
114 Texture1D,
115 Texture2D,
116 Texture3D,
117 Texture1DArray,
118 Texture2DArray,
119 TextureCubemap,
120 };
121
122 static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
123 switch (texture_type) {
124 case Tegra::Texture::TextureType::Texture1D:
125 return SurfaceTarget::Texture1D;
126 case Tegra::Texture::TextureType::Texture2D:
127 case Tegra::Texture::TextureType::Texture2DNoMipmap:
128 return SurfaceTarget::Texture2D;
129 case Tegra::Texture::TextureType::Texture1DArray:
130 return SurfaceTarget::Texture1DArray;
131 case Tegra::Texture::TextureType::Texture2DArray:
132 return SurfaceTarget::Texture2DArray;
133 default:
134 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
135 UNREACHABLE();
136 return SurfaceTarget::Texture2D;
137 }
138 }
139
112 /** 140 /**
113 * Gets the compression factor for the specified PixelFormat. This applies to just the 141 * Gets the compression factor for the specified PixelFormat. This applies to just the
114 * "compressed width" and "compressed height", not the overall compression factor of a 142 * "compressed width" and "compressed height", not the overall compression factor of a
@@ -165,6 +193,7 @@ struct SurfaceParams {
165 1, // RG8S 193 1, // RG8S
166 1, // RG32UI 194 1, // RG32UI
167 1, // R32UI 195 1, // R32UI
196 4, // ASTC_2D_8X8
168 1, // Z32F 197 1, // Z32F
169 1, // Z16 198 1, // Z16
170 1, // Z24S8 199 1, // Z24S8
@@ -172,8 +201,8 @@ struct SurfaceParams {
172 1, // Z32FS8 201 1, // Z32FS8
173 }}; 202 }};
174 203
175 ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); 204 ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
176 return compression_factor_table[static_cast<size_t>(format)]; 205 return compression_factor_table[static_cast<std::size_t>(format)];
177 } 206 }
178 207
179 static constexpr u32 GetFormatBpp(PixelFormat format) { 208 static constexpr u32 GetFormatBpp(PixelFormat format) {
@@ -226,6 +255,7 @@ struct SurfaceParams {
226 16, // RG8S 255 16, // RG8S
227 64, // RG32UI 256 64, // RG32UI
228 32, // R32UI 257 32, // R32UI
258 16, // ASTC_2D_8X8
229 32, // Z32F 259 32, // Z32F
230 16, // Z16 260 16, // Z16
231 32, // Z24S8 261 32, // Z24S8
@@ -233,8 +263,8 @@ struct SurfaceParams {
233 64, // Z32FS8 263 64, // Z32FS8
234 }}; 264 }};
235 265
236 ASSERT(static_cast<size_t>(format) < bpp_table.size()); 266 ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
237 return bpp_table[static_cast<size_t>(format)]; 267 return bpp_table[static_cast<std::size_t>(format)];
238 } 268 }
239 269
240 u32 GetFormatBpp() const { 270 u32 GetFormatBpp() const {
@@ -270,6 +300,7 @@ struct SurfaceParams {
270 return PixelFormat::ABGR8S; 300 return PixelFormat::ABGR8S;
271 case Tegra::RenderTargetFormat::RGBA8_UINT: 301 case Tegra::RenderTargetFormat::RGBA8_UINT:
272 return PixelFormat::ABGR8UI; 302 return PixelFormat::ABGR8UI;
303 case Tegra::RenderTargetFormat::BGRA8_SRGB:
273 case Tegra::RenderTargetFormat::BGRA8_UNORM: 304 case Tegra::RenderTargetFormat::BGRA8_UNORM:
274 return PixelFormat::BGRA8; 305 return PixelFormat::BGRA8;
275 case Tegra::RenderTargetFormat::RGB10_A2_UNORM: 306 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
@@ -288,6 +319,8 @@ struct SurfaceParams {
288 return PixelFormat::R11FG11FB10F; 319 return PixelFormat::R11FG11FB10F;
289 case Tegra::RenderTargetFormat::B5G6R5_UNORM: 320 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
290 return PixelFormat::B5G6R5U; 321 return PixelFormat::B5G6R5U;
322 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
323 return PixelFormat::A1B5G5R5U;
291 case Tegra::RenderTargetFormat::RGBA32_UINT: 324 case Tegra::RenderTargetFormat::RGBA32_UINT:
292 return PixelFormat::RGBA32UI; 325 return PixelFormat::RGBA32UI;
293 case Tegra::RenderTargetFormat::R8_UNORM: 326 case Tegra::RenderTargetFormat::R8_UNORM:
@@ -494,6 +527,8 @@ struct SurfaceParams {
494 return PixelFormat::BC6H_SF16; 527 return PixelFormat::BC6H_SF16;
495 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 528 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
496 return PixelFormat::ASTC_2D_4X4; 529 return PixelFormat::ASTC_2D_4X4;
530 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
531 return PixelFormat::ASTC_2D_8X8;
497 case Tegra::Texture::TextureFormat::R16_G16: 532 case Tegra::Texture::TextureFormat::R16_G16:
498 switch (component_type) { 533 switch (component_type) {
499 case Tegra::Texture::ComponentType::FLOAT: 534 case Tegra::Texture::ComponentType::FLOAT:
@@ -542,11 +577,13 @@ struct SurfaceParams {
542 case Tegra::RenderTargetFormat::RGBA8_UNORM: 577 case Tegra::RenderTargetFormat::RGBA8_UNORM:
543 case Tegra::RenderTargetFormat::RGBA8_SRGB: 578 case Tegra::RenderTargetFormat::RGBA8_SRGB:
544 case Tegra::RenderTargetFormat::BGRA8_UNORM: 579 case Tegra::RenderTargetFormat::BGRA8_UNORM:
580 case Tegra::RenderTargetFormat::BGRA8_SRGB:
545 case Tegra::RenderTargetFormat::RGB10_A2_UNORM: 581 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
546 case Tegra::RenderTargetFormat::R8_UNORM: 582 case Tegra::RenderTargetFormat::R8_UNORM:
547 case Tegra::RenderTargetFormat::RG16_UNORM: 583 case Tegra::RenderTargetFormat::RG16_UNORM:
548 case Tegra::RenderTargetFormat::R16_UNORM: 584 case Tegra::RenderTargetFormat::R16_UNORM:
549 case Tegra::RenderTargetFormat::B5G6R5_UNORM: 585 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
586 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
550 case Tegra::RenderTargetFormat::RG8_UNORM: 587 case Tegra::RenderTargetFormat::RG8_UNORM:
551 case Tegra::RenderTargetFormat::RGBA16_UNORM: 588 case Tegra::RenderTargetFormat::RGBA16_UNORM:
552 return ComponentType::UNorm; 589 return ComponentType::UNorm;
@@ -607,16 +644,18 @@ struct SurfaceParams {
607 } 644 }
608 645
609 static SurfaceType GetFormatType(PixelFormat pixel_format) { 646 static SurfaceType GetFormatType(PixelFormat pixel_format) {
610 if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) { 647 if (static_cast<std::size_t>(pixel_format) <
648 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
611 return SurfaceType::ColorTexture; 649 return SurfaceType::ColorTexture;
612 } 650 }
613 651
614 if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxDepthFormat)) { 652 if (static_cast<std::size_t>(pixel_format) <
653 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
615 return SurfaceType::Depth; 654 return SurfaceType::Depth;
616 } 655 }
617 656
618 if (static_cast<size_t>(pixel_format) < 657 if (static_cast<std::size_t>(pixel_format) <
619 static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) { 658 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
620 return SurfaceType::DepthStencil; 659 return SurfaceType::DepthStencil;
621 } 660 }
622 661
@@ -630,20 +669,19 @@ struct SurfaceParams {
630 MathUtil::Rectangle<u32> GetRect() const; 669 MathUtil::Rectangle<u32> GetRect() const;
631 670
632 /// Returns the size of this surface in bytes, adjusted for compression 671 /// Returns the size of this surface in bytes, adjusted for compression
633 size_t SizeInBytes() const { 672 std::size_t SizeInBytes() const {
634 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 673 const u32 compression_factor{GetCompressionFactor(pixel_format)};
635 ASSERT(width % compression_factor == 0); 674 ASSERT(width % compression_factor == 0);
636 ASSERT(height % compression_factor == 0); 675 ASSERT(height % compression_factor == 0);
637 return (width / compression_factor) * (height / compression_factor) * 676 return (width / compression_factor) * (height / compression_factor) *
638 GetFormatBpp(pixel_format) / CHAR_BIT; 677 GetFormatBpp(pixel_format) * depth / CHAR_BIT;
639 } 678 }
640 679
641 /// Creates SurfaceParams from a texture configuration 680 /// Creates SurfaceParams from a texture configuration
642 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); 681 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
643 682
644 /// Creates SurfaceParams from a framebuffer configuration 683 /// Creates SurfaceParams from a framebuffer configuration
645 static SurfaceParams CreateForFramebuffer( 684 static SurfaceParams CreateForFramebuffer(std::size_t index);
646 const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config);
647 685
648 /// Creates SurfaceParams for a depth buffer configuration 686 /// Creates SurfaceParams for a depth buffer configuration
649 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 687 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
@@ -652,8 +690,8 @@ struct SurfaceParams {
652 690
653 /// Checks if surfaces are compatible for caching 691 /// Checks if surfaces are compatible for caching
654 bool IsCompatibleSurface(const SurfaceParams& other) const { 692 bool IsCompatibleSurface(const SurfaceParams& other) const {
655 return std::tie(pixel_format, type, cache_width, cache_height) == 693 return std::tie(pixel_format, type, width, height) ==
656 std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height); 694 std::tie(other.pixel_format, other.type, other.width, other.height);
657 } 695 }
658 696
659 VAddr addr; 697 VAddr addr;
@@ -664,12 +702,10 @@ struct SurfaceParams {
664 SurfaceType type; 702 SurfaceType type;
665 u32 width; 703 u32 width;
666 u32 height; 704 u32 height;
705 u32 depth;
667 u32 unaligned_height; 706 u32 unaligned_height;
668 size_t size_in_bytes; 707 std::size_t size_in_bytes;
669 708 SurfaceTarget target;
670 // Parameters used for caching only
671 u32 cache_width;
672 u32 cache_height;
673}; 709};
674 710
675}; // namespace OpenGL 711}; // namespace OpenGL
@@ -685,7 +721,7 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
685namespace std { 721namespace std {
686template <> 722template <>
687struct hash<SurfaceReserveKey> { 723struct hash<SurfaceReserveKey> {
688 size_t operator()(const SurfaceReserveKey& k) const { 724 std::size_t operator()(const SurfaceReserveKey& k) const {
689 return k.Hash(); 725 return k.Hash();
690 } 726 }
691}; 727};
@@ -701,7 +737,7 @@ public:
701 return params.addr; 737 return params.addr;
702 } 738 }
703 739
704 size_t GetSizeInBytes() const { 740 std::size_t GetSizeInBytes() const {
705 return params.size_in_bytes; 741 return params.size_in_bytes;
706 } 742 }
707 743
@@ -709,6 +745,10 @@ public:
709 return texture; 745 return texture;
710 } 746 }
711 747
748 GLenum Target() const {
749 return gl_target;
750 }
751
712 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { 752 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
713 if (format == SurfaceParams::PixelFormat::Invalid) 753 if (format == SurfaceParams::PixelFormat::Invalid)
714 return 0; 754 return 0;
@@ -724,14 +764,14 @@ public:
724 void LoadGLBuffer(); 764 void LoadGLBuffer();
725 void FlushGLBuffer(); 765 void FlushGLBuffer();
726 766
727 // Upload/Download data in gl_buffer in/to this surface's texture 767 // Upload data in gl_buffer to this surface's texture
728 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); 768 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
729 void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
730 769
731private: 770private:
732 OGLTexture texture; 771 OGLTexture texture;
733 std::vector<u8> gl_buffer; 772 std::vector<u8> gl_buffer;
734 SurfaceParams params; 773 SurfaceParams params;
774 GLenum gl_target;
735}; 775};
736 776
737class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 777class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -741,9 +781,11 @@ public:
741 /// Get a surface based on the texture configuration 781 /// Get a surface based on the texture configuration
742 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); 782 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
743 783
744 /// Get the color and depth surfaces based on the framebuffer configuration 784 /// Get the depth surface based on the framebuffer configuration
745 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, 785 Surface GetDepthBufferSurface(bool preserve_contents);
746 bool preserve_contents); 786
787 /// Get the color surface based on the framebuffer configuration and the specified render target
788 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
747 789
748 /// Flushes the surface to Switch memory 790 /// Flushes the surface to Switch memory
749 void FlushSurface(const Surface& surface); 791 void FlushSurface(const Surface& surface);
@@ -774,6 +816,10 @@ private:
774 816
775 OGLFramebuffer read_framebuffer; 817 OGLFramebuffer read_framebuffer;
776 OGLFramebuffer draw_framebuffer; 818 OGLFramebuffer draw_framebuffer;
819
820 /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
821 /// using the new format.
822 OGLBuffer copy_pbo;
777}; 823};
778 824
779} // namespace OpenGL 825} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index ac9adfd83..894fe6eae 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -13,8 +13,8 @@ namespace OpenGL {
13 13
14/// Gets the address for the specified shader stage program 14/// Gets the address for the specified shader stage program
15static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 15static VAddr GetShaderAddress(Maxwell::ShaderProgram program) {
16 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 16 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
17 auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)]; 17 const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)];
18 return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + 18 return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() +
19 shader_config.offset); 19 shader_config.offset);
20} 20}
@@ -28,7 +28,7 @@ static GLShader::ProgramCode GetShaderCode(VAddr addr) {
28 28
29/// Helper function to set shader uniform block bindings for a single shader stage 29/// Helper function to set shader uniform block bindings for a single shader stage
30static void SetShaderUniformBlockBinding(GLuint shader, const char* name, 30static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
31 Maxwell::ShaderStage binding, size_t expected_size) { 31 Maxwell::ShaderStage binding, std::size_t expected_size) {
32 const GLuint ub_index = glGetUniformBlockIndex(shader, name); 32 const GLuint ub_index = glGetUniformBlockIndex(shader, name);
33 if (ub_index == GL_INVALID_INDEX) { 33 if (ub_index == GL_INVALID_INDEX) {
34 return; 34 return;
@@ -36,7 +36,7 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
36 36
37 GLint ub_size = 0; 37 GLint ub_size = 0;
38 glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); 38 glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
39 ASSERT_MSG(static_cast<size_t>(ub_size) == expected_size, 39 ASSERT_MSG(static_cast<std::size_t>(ub_size) == expected_size,
40 "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); 40 "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size);
41 glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); 41 glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
42} 42}
@@ -85,23 +85,23 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
85 SetShaderUniformBlockBindings(program.handle); 85 SetShaderUniformBlockBindings(program.handle);
86} 86}
87 87
88GLuint CachedShader::GetProgramResourceIndex(const std::string& name) { 88GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
89 auto search{resource_cache.find(name)}; 89 const auto search{resource_cache.find(buffer.GetHash())};
90 if (search == resource_cache.end()) { 90 if (search == resource_cache.end()) {
91 const GLuint index{ 91 const GLuint index{
92 glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, name.c_str())}; 92 glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())};
93 resource_cache[name] = index; 93 resource_cache[buffer.GetHash()] = index;
94 return index; 94 return index;
95 } 95 }
96 96
97 return search->second; 97 return search->second;
98} 98}
99 99
100GLint CachedShader::GetUniformLocation(const std::string& name) { 100GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
101 auto search{uniform_cache.find(name)}; 101 const auto search{uniform_cache.find(sampler.GetHash())};
102 if (search == uniform_cache.end()) { 102 if (search == uniform_cache.end()) {
103 const GLint index{glGetUniformLocation(program.handle, name.c_str())}; 103 const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())};
104 uniform_cache[name] = index; 104 uniform_cache[sampler.GetHash()] = index;
105 return index; 105 return index;
106 } 106 }
107 107
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 759987604..9bafe43a9 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -4,8 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <unordered_map>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/rasterizer_cache.h" 11#include "video_core/rasterizer_cache.h"
@@ -28,7 +28,7 @@ public:
28 } 28 }
29 29
30 /// Gets the size of the shader in guest memory, required for cache management 30 /// Gets the size of the shader in guest memory, required for cache management
31 size_t GetSizeInBytes() const { 31 std::size_t GetSizeInBytes() const {
32 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 32 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
33 } 33 }
34 34
@@ -43,10 +43,10 @@ public:
43 } 43 }
44 44
45 /// Gets the GL program resource location for the specified resource, caching as needed 45 /// Gets the GL program resource location for the specified resource, caching as needed
46 GLuint GetProgramResourceIndex(const std::string& name); 46 GLuint GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer);
47 47
48 /// Gets the GL uniform location for the specified resource, caching as needed 48 /// Gets the GL uniform location for the specified resource, caching as needed
49 GLint GetUniformLocation(const std::string& name); 49 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
50 50
51private: 51private:
52 VAddr addr; 52 VAddr addr;
@@ -55,8 +55,8 @@ private:
55 GLShader::ShaderEntries entries; 55 GLShader::ShaderEntries entries;
56 OGLProgram program; 56 OGLProgram program;
57 57
58 std::unordered_map<std::string, GLuint> resource_cache; 58 std::map<u32, GLuint> resource_cache;
59 std::unordered_map<std::string, GLint> uniform_cache; 59 std::map<u32, GLint> uniform_cache;
60}; 60};
61 61
62class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 62class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 391c92d47..b3e95187e 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -12,6 +12,7 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/engines/shader_bytecode.h" 14#include "video_core/engines/shader_bytecode.h"
15#include "video_core/engines/shader_header.h"
15#include "video_core/renderer_opengl/gl_rasterizer.h" 16#include "video_core/renderer_opengl/gl_rasterizer.h"
16#include "video_core/renderer_opengl/gl_shader_decompiler.h" 17#include "video_core/renderer_opengl/gl_shader_decompiler.h"
17 18
@@ -26,7 +27,7 @@ using Tegra::Shader::Sampler;
26using Tegra::Shader::SubOp; 27using Tegra::Shader::SubOp;
27 28
28constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 29constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
29constexpr u32 PROGRAM_HEADER_SIZE = 0x50; 30constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
30 31
31class DecompileFail : public std::runtime_error { 32class DecompileFail : public std::runtime_error {
32public: 33public:
@@ -113,7 +114,7 @@ private:
113 114
114 /// Scans a range of code for labels and determines the exit method. 115 /// Scans a range of code for labels and determines the exit method.
115 ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) { 116 ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) {
116 auto [iter, inserted] = 117 const auto [iter, inserted] =
117 exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined); 118 exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined);
118 ExitMethod& exit_method = iter->second; 119 ExitMethod& exit_method = iter->second;
119 if (!inserted) 120 if (!inserted)
@@ -131,22 +132,22 @@ private:
131 if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) { 132 if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
132 return exit_method = ExitMethod::AlwaysEnd; 133 return exit_method = ExitMethod::AlwaysEnd;
133 } else { 134 } else {
134 ExitMethod not_met = Scan(offset + 1, end, labels); 135 const ExitMethod not_met = Scan(offset + 1, end, labels);
135 return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met); 136 return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met);
136 } 137 }
137 } 138 }
138 case OpCode::Id::BRA: { 139 case OpCode::Id::BRA: {
139 u32 target = offset + instr.bra.GetBranchTarget(); 140 const u32 target = offset + instr.bra.GetBranchTarget();
140 labels.insert(target); 141 labels.insert(target);
141 ExitMethod no_jmp = Scan(offset + 1, end, labels); 142 const ExitMethod no_jmp = Scan(offset + 1, end, labels);
142 ExitMethod jmp = Scan(target, end, labels); 143 const ExitMethod jmp = Scan(target, end, labels);
143 return exit_method = ParallelExit(no_jmp, jmp); 144 return exit_method = ParallelExit(no_jmp, jmp);
144 } 145 }
145 case OpCode::Id::SSY: { 146 case OpCode::Id::SSY: {
146 // The SSY instruction uses a similar encoding as the BRA instruction. 147 // The SSY instruction uses a similar encoding as the BRA instruction.
147 ASSERT_MSG(instr.bra.constant_buffer == 0, 148 ASSERT_MSG(instr.bra.constant_buffer == 0,
148 "Constant buffer SSY is not supported"); 149 "Constant buffer SSY is not supported");
149 u32 target = offset + instr.bra.GetBranchTarget(); 150 const u32 target = offset + instr.bra.GetBranchTarget();
150 labels.insert(target); 151 labels.insert(target);
151 // Continue scanning for an exit method. 152 // Continue scanning for an exit method.
152 break; 153 break;
@@ -189,7 +190,7 @@ public:
189 190
190private: 191private:
191 void AppendIndentation() { 192 void AppendIndentation() {
192 shader_source.append(static_cast<size_t>(scope) * 4, ' '); 193 shader_source.append(static_cast<std::size_t>(scope) * 4, ' ');
193 } 194 }
194 195
195 std::string shader_source; 196 std::string shader_source;
@@ -208,7 +209,7 @@ public:
208 UnsignedInteger, 209 UnsignedInteger,
209 }; 210 };
210 211
211 GLSLRegister(size_t index, const std::string& suffix) : index{index}, suffix{suffix} {} 212 GLSLRegister(std::size_t index, const std::string& suffix) : index{index}, suffix{suffix} {}
212 213
213 /// Gets the GLSL type string for a register 214 /// Gets the GLSL type string for a register
214 static std::string GetTypeString() { 215 static std::string GetTypeString() {
@@ -226,15 +227,23 @@ public:
226 } 227 }
227 228
228 /// Returns the index of the register 229 /// Returns the index of the register
229 size_t GetIndex() const { 230 std::size_t GetIndex() const {
230 return index; 231 return index;
231 } 232 }
232 233
233private: 234private:
234 const size_t index; 235 const std::size_t index;
235 const std::string& suffix; 236 const std::string& suffix;
236}; 237};
237 238
239enum class InternalFlag : u64 {
240 ZeroFlag = 0,
241 CarryFlag = 1,
242 OverflowFlag = 2,
243 NaNFlag = 3,
244 Amount
245};
246
238/** 247/**
239 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state 248 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state
240 * of all registers (e.g. whether they are currently being used as Floats or Integers), and 249 * of all registers (e.g. whether they are currently being used as Floats or Integers), and
@@ -247,6 +256,7 @@ public:
247 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) 256 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix)
248 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { 257 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} {
249 BuildRegisterList(); 258 BuildRegisterList();
259 BuildInputList();
250 } 260 }
251 261
252 /** 262 /**
@@ -327,13 +337,19 @@ public:
327 void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, 337 void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
328 const std::string& value, u64 dest_num_components, 338 const std::string& value, u64 dest_num_components,
329 u64 value_num_components, bool is_saturated = false, 339 u64 value_num_components, bool is_saturated = false,
330 u64 dest_elem = 0, Register::Size size = Register::Size::Word) { 340 u64 dest_elem = 0, Register::Size size = Register::Size::Word,
341 bool sets_cc = false) {
331 ASSERT_MSG(!is_saturated, "Unimplemented"); 342 ASSERT_MSG(!is_saturated, "Unimplemented");
332 343
333 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; 344 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
334 345
335 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', 346 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
336 dest_num_components, value_num_components, dest_elem); 347 dest_num_components, value_num_components, dest_elem);
348
349 if (sets_cc) {
350 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
351 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
352 }
337 } 353 }
338 354
339 /** 355 /**
@@ -343,12 +359,33 @@ public:
343 * @param elem The element to use for the operation. 359 * @param elem The element to use for the operation.
344 * @param attribute The input attribute to use as the source value. 360 * @param attribute The input attribute to use as the source value.
345 */ 361 */
346 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute) { 362 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
347 std::string dest = GetRegisterAsFloat(reg); 363 const Tegra::Shader::IpaMode& input_mode) {
348 std::string src = GetInputAttribute(attribute) + GetSwizzle(elem); 364 const std::string dest = GetRegisterAsFloat(reg);
365 const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
349 shader.AddLine(dest + " = " + src + ';'); 366 shader.AddLine(dest + " = " + src + ';');
350 } 367 }
351 368
369 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const {
370 switch (cc) {
371 case Tegra::Shader::ControlCode::NEU:
372 return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')';
373 default:
374 LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc));
375 UNREACHABLE();
376 return "false";
377 }
378 }
379
380 std::string GetInternalFlag(const InternalFlag ii) const {
381 const u32 code = static_cast<u32>(ii);
382 return "internalFlag_" + std::to_string(code) + suffix;
383 }
384
385 void SetInternalFlag(const InternalFlag ii, const std::string& value) const {
386 shader.AddLine(GetInternalFlag(ii) + " = " + value + ';');
387 }
388
352 /** 389 /**
353 * Writes code that does a output attribute assignment to register operation. Output attributes 390 * Writes code that does a output attribute assignment to register operation. Output attributes
354 * are stored as floats, so this may require conversion. 391 * are stored as floats, so this may require conversion.
@@ -357,8 +394,8 @@ public:
357 * @param reg The register to use as the source value. 394 * @param reg The register to use as the source value.
358 */ 395 */
359 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { 396 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
360 std::string dest = GetOutputAttribute(attribute); 397 const std::string dest = GetOutputAttribute(attribute);
361 std::string src = GetRegisterAsFloat(reg); 398 const std::string src = GetRegisterAsFloat(reg);
362 399
363 if (!dest.empty()) { 400 if (!dest.empty()) {
364 // Can happen with unknown/unimplemented output attributes, in which case we ignore the 401 // Can happen with unknown/unimplemented output attributes, in which case we ignore the
@@ -391,9 +428,9 @@ public:
391 GLSLRegister::Type type) { 428 GLSLRegister::Type type) {
392 declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage); 429 declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage);
393 430
394 std::string final_offset = fmt::format("({} + {})", index_str, offset / 4); 431 const std::string final_offset = fmt::format("({} + {})", index_str, offset / 4);
395 std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" + 432 const std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" +
396 final_offset + " % 4]"; 433 final_offset + " % 4]";
397 434
398 if (type == GLSLRegister::Type::Float) { 435 if (type == GLSLRegister::Type::Float) {
399 return value; 436 return value;
@@ -412,12 +449,19 @@ public:
412 } 449 }
413 declarations.AddNewLine(); 450 declarations.AddNewLine();
414 451
415 for (const auto& index : declr_input_attribute) { 452 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
453 const InternalFlag code = static_cast<InternalFlag>(ii);
454 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
455 }
456 declarations.AddNewLine();
457
458 for (const auto element : declr_input_attribute) {
416 // TODO(bunnei): Use proper number of elements for these 459 // TODO(bunnei): Use proper number of elements for these
417 declarations.AddLine("layout(location = " + 460 u32 idx =
418 std::to_string(static_cast<u32>(index) - 461 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
419 static_cast<u32>(Attribute::Index::Attribute_0)) + 462 declarations.AddLine("layout(location = " + std::to_string(idx) + ")" +
420 ") in vec4 " + GetInputAttribute(index) + ';'); 463 GetInputFlags(element.first) + "in vec4 " +
464 GetInputAttribute(element.first, element.second) + ';');
421 } 465 }
422 declarations.AddNewLine(); 466 declarations.AddNewLine();
423 467
@@ -440,13 +484,12 @@ public:
440 } 484 }
441 declarations.AddNewLine(); 485 declarations.AddNewLine();
442 486
443 // Append the sampler2D array for the used textures. 487 const auto& samplers = GetSamplers();
444 size_t num_samplers = GetSamplers().size(); 488 for (const auto& sampler : samplers) {
445 if (num_samplers > 0) { 489 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
446 declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + 490 ';');
447 std::to_string(num_samplers) + "];");
448 declarations.AddNewLine();
449 } 491 }
492 declarations.AddNewLine();
450 } 493 }
451 494
452 /// Returns a list of constant buffer declarations 495 /// Returns a list of constant buffer declarations
@@ -458,27 +501,29 @@ public:
458 } 501 }
459 502
460 /// Returns a list of samplers used in the shader 503 /// Returns a list of samplers used in the shader
461 std::vector<SamplerEntry> GetSamplers() const { 504 const std::vector<SamplerEntry>& GetSamplers() const {
462 return used_samplers; 505 return used_samplers;
463 } 506 }
464 507
465 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if 508 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
466 /// necessary. 509 /// necessary.
467 std::string AccessSampler(const Sampler& sampler) { 510 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
468 size_t offset = static_cast<size_t>(sampler.index.Value()); 511 bool is_array) {
512 const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
469 513
470 // If this sampler has already been used, return the existing mapping. 514 // If this sampler has already been used, return the existing mapping.
471 auto itr = 515 const auto itr =
472 std::find_if(used_samplers.begin(), used_samplers.end(), 516 std::find_if(used_samplers.begin(), used_samplers.end(),
473 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); 517 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
474 518
475 if (itr != used_samplers.end()) { 519 if (itr != used_samplers.end()) {
520 ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
476 return itr->GetName(); 521 return itr->GetName();
477 } 522 }
478 523
479 // Otherwise create a new mapping for this sampler 524 // Otherwise create a new mapping for this sampler
480 size_t next_index = used_samplers.size(); 525 const std::size_t next_index = used_samplers.size();
481 SamplerEntry entry{stage, offset, next_index}; 526 const SamplerEntry entry{stage, offset, next_index, type, is_array};
482 used_samplers.emplace_back(entry); 527 used_samplers.emplace_back(entry);
483 return entry.GetName(); 528 return entry.GetName();
484 } 529 }
@@ -527,16 +572,29 @@ private:
527 void BuildRegisterList() { 572 void BuildRegisterList() {
528 regs.reserve(Register::NumRegisters); 573 regs.reserve(Register::NumRegisters);
529 574
530 for (size_t index = 0; index < Register::NumRegisters; ++index) { 575 for (std::size_t index = 0; index < Register::NumRegisters; ++index) {
531 regs.emplace_back(index, suffix); 576 regs.emplace_back(index, suffix);
532 } 577 }
533 } 578 }
534 579
580 void BuildInputList() {
581 const u32 size = static_cast<u32>(Attribute::Index::Attribute_31) -
582 static_cast<u32>(Attribute::Index::Attribute_0) + 1;
583 declr_input_attribute.reserve(size);
584 }
585
535 /// Generates code representing an input attribute register. 586 /// Generates code representing an input attribute register.
536 std::string GetInputAttribute(Attribute::Index attribute) { 587 std::string GetInputAttribute(Attribute::Index attribute,
588 const Tegra::Shader::IpaMode& input_mode) {
537 switch (attribute) { 589 switch (attribute) {
538 case Attribute::Index::Position: 590 case Attribute::Index::Position:
539 return "position"; 591 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
592 return "position";
593 } else {
594 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
595 }
596 case Attribute::Index::PointCoord:
597 return "vec4(gl_PointCoord.x, gl_PointCoord.y, 0, 0)";
540 case Attribute::Index::TessCoordInstanceIDVertexID: 598 case Attribute::Index::TessCoordInstanceIDVertexID:
541 // TODO(Subv): Find out what the values are for the first two elements when inside a 599 // TODO(Subv): Find out what the values are for the first two elements when inside a
542 // vertex shader, and what's the value of the fourth element when inside a Tess Eval 600 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
@@ -552,7 +610,14 @@ private:
552 static_cast<u32>(Attribute::Index::Attribute_0)}; 610 static_cast<u32>(Attribute::Index::Attribute_0)};
553 if (attribute >= Attribute::Index::Attribute_0 && 611 if (attribute >= Attribute::Index::Attribute_0 &&
554 attribute <= Attribute::Index::Attribute_31) { 612 attribute <= Attribute::Index::Attribute_31) {
555 declr_input_attribute.insert(attribute); 613 if (declr_input_attribute.count(attribute) == 0) {
614 declr_input_attribute[attribute] = input_mode;
615 } else {
616 if (declr_input_attribute[attribute] != input_mode) {
617 LOG_CRITICAL(HW_GPU, "Same Input multiple input modes");
618 UNREACHABLE();
619 }
620 }
556 return "input_attribute_" + std::to_string(index); 621 return "input_attribute_" + std::to_string(index);
557 } 622 }
558 623
@@ -563,6 +628,49 @@ private:
563 return "vec4(0, 0, 0, 0)"; 628 return "vec4(0, 0, 0, 0)";
564 } 629 }
565 630
631 std::string GetInputFlags(const Attribute::Index attribute) {
632 const Tegra::Shader::IpaSampleMode sample_mode =
633 declr_input_attribute[attribute].sampling_mode;
634 const Tegra::Shader::IpaInterpMode interp_mode =
635 declr_input_attribute[attribute].interpolation_mode;
636 std::string out;
637 switch (interp_mode) {
638 case Tegra::Shader::IpaInterpMode::Flat: {
639 out += "flat ";
640 break;
641 }
642 case Tegra::Shader::IpaInterpMode::Linear: {
643 out += "noperspective ";
644 break;
645 }
646 case Tegra::Shader::IpaInterpMode::Perspective: {
647 // Default, Smooth
648 break;
649 }
650 default: {
651 LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode));
652 UNREACHABLE();
653 }
654 }
655 switch (sample_mode) {
656 case Tegra::Shader::IpaSampleMode::Centroid: {
657 // Note not implemented, it can be implemented with the "centroid " keyword in glsl;
658 LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented");
659 UNREACHABLE();
660 break;
661 }
662 case Tegra::Shader::IpaSampleMode::Default: {
663 // Default, n/a
664 break;
665 }
666 default: {
667 LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode));
668 UNREACHABLE();
669 }
670 }
671 return out;
672 }
673
566 /// Generates code representing an output attribute register. 674 /// Generates code representing an output attribute register.
567 std::string GetOutputAttribute(Attribute::Index attribute) { 675 std::string GetOutputAttribute(Attribute::Index attribute) {
568 switch (attribute) { 676 switch (attribute) {
@@ -593,7 +701,7 @@ private:
593 ShaderWriter& shader; 701 ShaderWriter& shader;
594 ShaderWriter& declarations; 702 ShaderWriter& declarations;
595 std::vector<GLSLRegister> regs; 703 std::vector<GLSLRegister> regs;
596 std::set<Attribute::Index> declr_input_attribute; 704 std::unordered_map<Attribute::Index, Tegra::Shader::IpaMode> declr_input_attribute;
597 std::set<Attribute::Index> declr_output_attribute; 705 std::set<Attribute::Index> declr_output_attribute;
598 std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; 706 std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
599 std::vector<SamplerEntry> used_samplers; 707 std::vector<SamplerEntry> used_samplers;
@@ -607,7 +715,7 @@ public:
607 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) 715 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix)
608 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 716 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
609 stage(stage), suffix(suffix) { 717 stage(stage), suffix(suffix) {
610 718 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
611 Generate(suffix); 719 Generate(suffix);
612 } 720 }
613 721
@@ -621,26 +729,9 @@ public:
621 } 729 }
622 730
623private: 731private:
624 // Shader program header for a Fragment Shader.
625 struct FragmentHeader {
626 INSERT_PADDING_WORDS(5);
627 INSERT_PADDING_WORDS(13);
628 u32 enabled_color_outputs;
629 union {
630 BitField<0, 1, u32> writes_samplemask;
631 BitField<1, 1, u32> writes_depth;
632 };
633
634 bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
635 u32 bit = render_target * 4 + component;
636 return enabled_color_outputs & (1 << bit);
637 }
638 };
639 static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong");
640
641 /// Gets the Subroutine object corresponding to the specified address. 732 /// Gets the Subroutine object corresponding to the specified address.
642 const Subroutine& GetSubroutine(u32 begin, u32 end) const { 733 const Subroutine& GetSubroutine(u32 begin, u32 end) const {
643 auto iter = subroutines.find(Subroutine{begin, end, suffix}); 734 const auto iter = subroutines.find(Subroutine{begin, end, suffix});
644 ASSERT(iter != subroutines.end()); 735 ASSERT(iter != subroutines.end());
645 return *iter; 736 return *iter;
646 } 737 }
@@ -656,8 +747,8 @@ private:
656 } 747 }
657 748
658 /// Generates code representing a texture sampler. 749 /// Generates code representing a texture sampler.
659 std::string GetSampler(const Sampler& sampler) { 750 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
660 return regs.AccessSampler(sampler); 751 return regs.AccessSampler(sampler, type, is_array);
661 } 752 }
662 753
663 /** 754 /**
@@ -685,7 +776,7 @@ private:
685 // Can't assign to the constant predicate. 776 // Can't assign to the constant predicate.
686 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 777 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
687 778
688 std::string variable = 'p' + std::to_string(pred) + '_' + suffix; 779 const std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
689 shader.AddLine(variable + " = " + value + ';'); 780 shader.AddLine(variable + " = " + value + ';');
690 declr_predicates.insert(std::move(variable)); 781 declr_predicates.insert(std::move(variable));
691 } 782 }
@@ -795,7 +886,7 @@ private:
795 */ 886 */
796 bool IsSchedInstruction(u32 offset) const { 887 bool IsSchedInstruction(u32 offset) const {
797 // sched instructions appear once every 4 instructions. 888 // sched instructions appear once every 4 instructions.
798 static constexpr size_t SchedPeriod = 4; 889 static constexpr std::size_t SchedPeriod = 4;
799 u32 absolute_offset = offset - main_offset; 890 u32 absolute_offset = offset - main_offset;
800 891
801 return (absolute_offset % SchedPeriod) == 0; 892 return (absolute_offset % SchedPeriod) == 0;
@@ -863,7 +954,7 @@ private:
863 std::string result; 954 std::string result;
864 result += '('; 955 result += '(';
865 956
866 for (size_t i = 0; i < shift_amounts.size(); ++i) { 957 for (std::size_t i = 0; i < shift_amounts.size(); ++i) {
867 if (i) 958 if (i)
868 result += '|'; 959 result += '|';
869 result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] + 960 result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] +
@@ -887,7 +978,7 @@ private:
887 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle 978 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
888 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 979 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
889 980
890 size_t written_components = 0; 981 std::size_t written_components = 0;
891 for (u32 component = 0; component < 4; ++component) { 982 for (u32 component = 0; component < 4; ++component) {
892 if (!instr.texs.IsComponentEnabled(component)) { 983 if (!instr.texs.IsComponentEnabled(component)) {
893 continue; 984 continue;
@@ -941,10 +1032,8 @@ private:
941 /// Writes the output values from a fragment shader to the corresponding GLSL output variables. 1032 /// Writes the output values from a fragment shader to the corresponding GLSL output variables.
942 void EmitFragmentOutputsWrite() { 1033 void EmitFragmentOutputsWrite() {
943 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 1034 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
944 FragmentHeader header;
945 std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE);
946 1035
947 ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented"); 1036 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
948 1037
949 // Write the color outputs using the data in the shader registers, disabled 1038 // Write the color outputs using the data in the shader registers, disabled
950 // rendertargets/components are skipped in the register assignment. 1039 // rendertargets/components are skipped in the register assignment.
@@ -953,18 +1042,22 @@ private:
953 ++render_target) { 1042 ++render_target) {
954 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1043 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
955 for (u32 component = 0; component < 4; ++component) { 1044 for (u32 component = 0; component < 4; ++component) {
956 if (header.IsColorComponentOutputEnabled(render_target, component)) { 1045 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
957 shader.AddLine(fmt::format("color[{}][{}] = {};", render_target, component, 1046 shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component,
958 regs.GetRegisterAsFloat(current_reg))); 1047 regs.GetRegisterAsFloat(current_reg)));
959 ++current_reg; 1048 ++current_reg;
960 } 1049 }
961 } 1050 }
962 } 1051 }
963 1052
964 if (header.writes_depth) { 1053 if (header.ps.omap.depth) {
965 // The depth output is always 2 registers after the last color output, and current_reg 1054 // The depth output is always 2 registers after the last color output, and current_reg
966 // already contains one past the last color register. 1055 // already contains one past the last color register.
967 shader.AddLine("gl_FragDepth = " + regs.GetRegisterAsFloat(current_reg + 1) + ';'); 1056
1057 shader.AddLine(
1058 "gl_FragDepth = " +
1059 regs.GetRegisterAsFloat(static_cast<Tegra::Shader::Register>(current_reg) + 1) +
1060 ';');
968 } 1061 }
969 } 1062 }
970 1063
@@ -1038,6 +1131,15 @@ private:
1038 case OpCode::Id::FMUL_R: 1131 case OpCode::Id::FMUL_R:
1039 case OpCode::Id::FMUL_IMM: { 1132 case OpCode::Id::FMUL_IMM: {
1040 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. 1133 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
1134 ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented",
1135 instr.fmul.tab5cb8_2.Value());
1136 ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented",
1137 instr.fmul.tab5c68_1.Value());
1138 ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented",
1139 instr.fmul.tab5c68_0
1140 .Value()); // SMO typical sends 1 here which seems to be the default
1141 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented");
1142
1041 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1143 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1042 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1144 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1043 instr.alu.saturate_d); 1145 instr.alu.saturate_d);
@@ -1357,7 +1459,7 @@ private:
1357 if (instr.alu_integer.negate_b) 1459 if (instr.alu_integer.negate_b)
1358 op_b = "-(" + op_b + ')'; 1460 op_b = "-(" + op_b + ')';
1359 1461
1360 std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); 1462 const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
1361 1463
1362 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1464 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1363 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1465 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
@@ -1375,7 +1477,7 @@ private:
1375 case OpCode::Id::SEL_C: 1477 case OpCode::Id::SEL_C:
1376 case OpCode::Id::SEL_R: 1478 case OpCode::Id::SEL_R:
1377 case OpCode::Id::SEL_IMM: { 1479 case OpCode::Id::SEL_IMM: {
1378 std::string condition = 1480 const std::string condition =
1379 GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0); 1481 GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0);
1380 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1482 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1381 '(' + condition + ") ? " + op_a + " : " + op_b, 1, 1); 1483 '(' + condition + ") ? " + op_a + " : " + op_b, 1, 1);
@@ -1397,8 +1499,9 @@ private:
1397 case OpCode::Id::LOP3_C: 1499 case OpCode::Id::LOP3_C:
1398 case OpCode::Id::LOP3_R: 1500 case OpCode::Id::LOP3_R:
1399 case OpCode::Id::LOP3_IMM: { 1501 case OpCode::Id::LOP3_IMM: {
1400 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1502 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1401 std::string lut; 1503 std::string lut;
1504
1402 if (opcode->GetId() == OpCode::Id::LOP3_R) { 1505 if (opcode->GetId() == OpCode::Id::LOP3_R) {
1403 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; 1506 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')';
1404 } else { 1507 } else {
@@ -1413,15 +1516,80 @@ private:
1413 case OpCode::Id::IMNMX_IMM: { 1516 case OpCode::Id::IMNMX_IMM: {
1414 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, 1517 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None,
1415 "Unimplemented"); 1518 "Unimplemented");
1416 std::string condition = 1519 const std::string condition =
1417 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); 1520 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
1418 std::string parameters = op_a + ',' + op_b; 1521 const std::string parameters = op_a + ',' + op_b;
1419 regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, 1522 regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
1420 '(' + condition + ") ? min(" + parameters + ") : max(" + 1523 '(' + condition + ") ? min(" + parameters + ") : max(" +
1421 parameters + ')', 1524 parameters + ')',
1422 1, 1); 1525 1, 1);
1423 break; 1526 break;
1424 } 1527 }
1528 case OpCode::Id::LEA_R2:
1529 case OpCode::Id::LEA_R1:
1530 case OpCode::Id::LEA_IMM:
1531 case OpCode::Id::LEA_RZ:
1532 case OpCode::Id::LEA_HI: {
1533 std::string op_c;
1534
1535 switch (opcode->GetId()) {
1536 case OpCode::Id::LEA_R2: {
1537 op_a = regs.GetRegisterAsInteger(instr.gpr20);
1538 op_b = regs.GetRegisterAsInteger(instr.gpr39);
1539 op_c = std::to_string(instr.lea.r2.entry_a);
1540 break;
1541 }
1542
1543 case OpCode::Id::LEA_R1: {
1544 const bool neg = instr.lea.r1.neg != 0;
1545 op_a = regs.GetRegisterAsInteger(instr.gpr8);
1546 if (neg)
1547 op_a = "-(" + op_a + ')';
1548 op_b = regs.GetRegisterAsInteger(instr.gpr20);
1549 op_c = std::to_string(instr.lea.r1.entry_a);
1550 break;
1551 }
1552
1553 case OpCode::Id::LEA_IMM: {
1554 const bool neg = instr.lea.imm.neg != 0;
1555 op_b = regs.GetRegisterAsInteger(instr.gpr8);
1556 if (neg)
1557 op_b = "-(" + op_b + ')';
1558 op_a = std::to_string(instr.lea.imm.entry_a);
1559 op_c = std::to_string(instr.lea.imm.entry_b);
1560 break;
1561 }
1562
1563 case OpCode::Id::LEA_RZ: {
1564 const bool neg = instr.lea.rz.neg != 0;
1565 op_b = regs.GetRegisterAsInteger(instr.gpr8);
1566 if (neg)
1567 op_b = "-(" + op_b + ')';
1568 op_a = regs.GetUniform(instr.lea.rz.cb_index, instr.lea.rz.cb_offset,
1569 GLSLRegister::Type::Integer);
1570 op_c = std::to_string(instr.lea.rz.entry_a);
1571
1572 break;
1573 }
1574
1575 case OpCode::Id::LEA_HI:
1576 default: {
1577 op_b = regs.GetRegisterAsInteger(instr.gpr8);
1578 op_a = std::to_string(instr.lea.imm.entry_a);
1579 op_c = std::to_string(instr.lea.imm.entry_b);
1580 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", opcode->GetName());
1581 UNREACHABLE();
1582 }
1583 }
1584 if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) {
1585 LOG_ERROR(HW_GPU, "Unhandled LEA Predicate");
1586 UNREACHABLE();
1587 }
1588 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
1589 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
1590
1591 break;
1592 }
1425 default: { 1593 default: {
1426 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 1594 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
1427 opcode->GetName()); 1595 opcode->GetName());
@@ -1432,10 +1600,16 @@ private:
1432 break; 1600 break;
1433 } 1601 }
1434 case OpCode::Type::Ffma: { 1602 case OpCode::Type::Ffma: {
1435 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1603 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1436 std::string op_b = instr.ffma.negate_b ? "-" : ""; 1604 std::string op_b = instr.ffma.negate_b ? "-" : "";
1437 std::string op_c = instr.ffma.negate_c ? "-" : ""; 1605 std::string op_c = instr.ffma.negate_c ? "-" : "";
1438 1606
1607 ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented");
1608 ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented",
1609 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO
1610 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented",
1611 instr.ffma.tab5980_1.Value());
1612
1439 switch (opcode->GetId()) { 1613 switch (opcode->GetId()) {
1440 case OpCode::Id::FFMA_CR: { 1614 case OpCode::Id::FFMA_CR: {
1441 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 1615 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
@@ -1486,7 +1660,8 @@ private:
1486 } 1660 }
1487 1661
1488 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1662 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1489 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); 1663 1, instr.alu.saturate_d, 0, instr.conversion.dest_size,
1664 instr.generates_cc.Value() != 0);
1490 break; 1665 break;
1491 } 1666 }
1492 case OpCode::Id::I2F_R: 1667 case OpCode::Id::I2F_R:
@@ -1616,9 +1791,34 @@ private:
1616 case OpCode::Type::Memory: { 1791 case OpCode::Type::Memory: {
1617 switch (opcode->GetId()) { 1792 switch (opcode->GetId()) {
1618 case OpCode::Id::LD_A: { 1793 case OpCode::Id::LD_A: {
1619 ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); 1794 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
1620 regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element, 1795 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
1621 instr.attribute.fmt20.index); 1796 "Indirect attribute loads are not supported");
1797 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0,
1798 "Unaligned attribute loads are not supported");
1799
1800 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
1801 Tegra::Shader::IpaSampleMode::Default};
1802
1803 u64 next_element = instr.attribute.fmt20.element;
1804 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
1805
1806 const auto LoadNextElement = [&](u32 reg_offset) {
1807 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
1808 static_cast<Attribute::Index>(next_index),
1809 input_mode);
1810
1811 // Load the next attribute element into the following register. If the element
1812 // to load goes beyond the vec4 size, load the first element of the next
1813 // attribute.
1814 next_element = (next_element + 1) % 4;
1815 next_index = next_index + (next_element == 0 ? 1 : 0);
1816 };
1817
1818 const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
1819 for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
1820 LoadNextElement(reg_offset);
1821 }
1622 break; 1822 break;
1623 } 1823 }
1624 case OpCode::Id::LD_C: { 1824 case OpCode::Id::LD_C: {
@@ -1632,7 +1832,7 @@ private:
1632 shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + 1832 shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
1633 " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); 1833 " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);");
1634 1834
1635 std::string op_a = 1835 const std::string op_a =
1636 regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index", 1836 regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index",
1637 GLSLRegister::Type::Float); 1837 GLSLRegister::Type::Float);
1638 1838
@@ -1642,7 +1842,7 @@ private:
1642 break; 1842 break;
1643 1843
1644 case Tegra::Shader::UniformType::Double: { 1844 case Tegra::Shader::UniformType::Double: {
1645 std::string op_b = 1845 const std::string op_b =
1646 regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, 1846 regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4,
1647 "index", GLSLRegister::Type::Float); 1847 "index", GLSLRegister::Type::Float);
1648 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 1848 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
@@ -1660,25 +1860,111 @@ private:
1660 break; 1860 break;
1661 } 1861 }
1662 case OpCode::Id::ST_A: { 1862 case OpCode::Id::ST_A: {
1663 ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); 1863 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
1664 regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index, 1864 "Indirect attribute loads are not supported");
1665 instr.attribute.fmt20.element, instr.gpr0); 1865 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0,
1866 "Unaligned attribute loads are not supported");
1867
1868 u64 next_element = instr.attribute.fmt20.element;
1869 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
1870
1871 const auto StoreNextElement = [&](u32 reg_offset) {
1872 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
1873 next_element,
1874 instr.gpr0.Value() + reg_offset);
1875
1876 // Load the next attribute element into the following register. If the element
1877 // to load goes beyond the vec4 size, load the first element of the next
1878 // attribute.
1879 next_element = (next_element + 1) % 4;
1880 next_index = next_index + (next_element == 0 ? 1 : 0);
1881 };
1882
1883 const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
1884 for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
1885 StoreNextElement(reg_offset);
1886 }
1887
1666 break; 1888 break;
1667 } 1889 }
1668 case OpCode::Id::TEX: { 1890 case OpCode::Id::TEX: {
1669 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1891 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
1670 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 1892 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
1671 const std::string sampler = GetSampler(instr.sampler); 1893 std::string coord;
1672 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 1894
1895 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
1896 "NODEP is not implemented");
1897 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
1898 "AOFFI is not implemented");
1899 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
1900 "DC is not implemented");
1901
1902 switch (texture_type) {
1903 case Tegra::Shader::TextureType::Texture1D: {
1904 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1905 coord = "float coords = " + x + ';';
1906 break;
1907 }
1908 case Tegra::Shader::TextureType::Texture2D: {
1909 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1910 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1911 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1912 break;
1913 }
1914 default:
1915 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
1916 static_cast<u32>(texture_type));
1917 UNREACHABLE();
1918
1919 // Fallback to interpreting as a 2D texture for now
1920 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1921 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1922 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1923 texture_type = Tegra::Shader::TextureType::Texture2D;
1924 }
1925 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
1926 // or lod.
1927 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20);
1928
1929 const std::string sampler = GetSampler(instr.sampler, texture_type, false);
1673 // Add an extra scope and declare the texture coords inside to prevent 1930 // Add an extra scope and declare the texture coords inside to prevent
1674 // overwriting them in case they are used as outputs of the texs instruction. 1931 // overwriting them in case they are used as outputs of the texs instruction.
1932
1675 shader.AddLine("{"); 1933 shader.AddLine("{");
1676 ++shader.scope; 1934 ++shader.scope;
1677 shader.AddLine(coord); 1935 shader.AddLine(coord);
1678 const std::string texture = "texture(" + sampler + ", coords)"; 1936 std::string texture;
1679 1937
1680 size_t dest_elem{}; 1938 switch (instr.tex.process_mode) {
1681 for (size_t elem = 0; elem < 4; ++elem) { 1939 case Tegra::Shader::TextureProcessMode::None: {
1940 texture = "texture(" + sampler + ", coords)";
1941 break;
1942 }
1943 case Tegra::Shader::TextureProcessMode::LZ: {
1944 texture = "textureLod(" + sampler + ", coords, 0.0)";
1945 break;
1946 }
1947 case Tegra::Shader::TextureProcessMode::LB:
1948 case Tegra::Shader::TextureProcessMode::LBA: {
1949 // TODO: Figure if A suffix changes the equation at all.
1950 texture = "texture(" + sampler + ", coords, " + op_c + ')';
1951 break;
1952 }
1953 case Tegra::Shader::TextureProcessMode::LL:
1954 case Tegra::Shader::TextureProcessMode::LLA: {
1955 // TODO: Figure if A suffix changes the equation at all.
1956 texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
1957 break;
1958 }
1959 default: {
1960 texture = "texture(" + sampler + ", coords)";
1961 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
1962 static_cast<u32>(instr.tex.process_mode.Value()));
1963 UNREACHABLE();
1964 }
1965 }
1966 std::size_t dest_elem{};
1967 for (std::size_t elem = 0; elem < 4; ++elem) {
1682 if (!instr.tex.IsComponentEnabled(elem)) { 1968 if (!instr.tex.IsComponentEnabled(elem)) {
1683 // Skip disabled components 1969 // Skip disabled components
1684 continue; 1970 continue;
@@ -1691,20 +1977,77 @@ private:
1691 break; 1977 break;
1692 } 1978 }
1693 case OpCode::Id::TEXS: { 1979 case OpCode::Id::TEXS: {
1694 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1980 std::string coord;
1695 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); 1981 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
1696 const std::string sampler = GetSampler(instr.sampler); 1982 bool is_array{instr.texs.IsArrayTexture()};
1697 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 1983
1984 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
1985 "NODEP is not implemented");
1986 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
1987 "DC is not implemented");
1988
1989 switch (texture_type) {
1990 case Tegra::Shader::TextureType::Texture2D: {
1991 if (is_array) {
1992 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1993 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1994 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
1995 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
1996 } else {
1997 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1998 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
1999 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2000 }
2001 break;
2002 }
2003 default:
2004 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
2005 static_cast<u32>(texture_type));
2006 UNREACHABLE();
1698 2007
2008 // Fallback to interpreting as a 2D texture for now
2009 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2010 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2011 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2012 texture_type = Tegra::Shader::TextureType::Texture2D;
2013 is_array = false;
2014 }
2015 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
1699 const std::string texture = "texture(" + sampler + ", coords)"; 2016 const std::string texture = "texture(" + sampler + ", coords)";
1700 WriteTexsInstruction(instr, coord, texture); 2017 WriteTexsInstruction(instr, coord, texture);
1701 break; 2018 break;
1702 } 2019 }
1703 case OpCode::Id::TLDS: { 2020 case OpCode::Id::TLDS: {
1704 const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 2021 ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
1705 const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20); 2022 ASSERT(instr.tlds.IsArrayTexture() == false);
1706 const std::string sampler = GetSampler(instr.sampler); 2023 std::string coord;
1707 const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");"; 2024
2025 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2026 "NODEP is not implemented");
2027 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2028 "AOFFI is not implemented");
2029 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2030 "MZ is not implemented");
2031
2032 switch (instr.tlds.GetTextureType()) {
2033 case Tegra::Shader::TextureType::Texture2D: {
2034 if (instr.tlds.IsArrayTexture()) {
2035 LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture");
2036 UNREACHABLE();
2037 } else {
2038 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2039 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2040 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2041 }
2042 break;
2043 }
2044 default:
2045 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
2046 static_cast<u32>(instr.tlds.GetTextureType()));
2047 UNREACHABLE();
2048 }
2049 const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
2050 instr.tlds.IsArrayTexture());
1708 const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2051 const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
1709 WriteTexsInstruction(instr, coord, texture); 2052 WriteTexsInstruction(instr, coord, texture);
1710 break; 2053 break;
@@ -1712,12 +2055,23 @@ private:
1712 case OpCode::Id::TLD4: { 2055 case OpCode::Id::TLD4: {
1713 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); 2056 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
1714 ASSERT(instr.tld4.array == 0); 2057 ASSERT(instr.tld4.array == 0);
1715 std::string coord{}; 2058 std::string coord;
2059
2060 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2061 "NODEP is not implemented");
2062 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2063 "AOFFI is not implemented");
2064 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
2065 "DC is not implemented");
2066 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2067 "NDV is not implemented");
2068 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2069 "PTP is not implemented");
1716 2070
1717 switch (instr.tld4.texture_type) { 2071 switch (instr.tld4.texture_type) {
1718 case Tegra::Shader::TextureType::Texture2D: { 2072 case Tegra::Shader::TextureType::Texture2D: {
1719 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2073 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
1720 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2074 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
1721 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2075 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
1722 break; 2076 break;
1723 } 2077 }
@@ -1727,7 +2081,8 @@ private:
1727 UNREACHABLE(); 2081 UNREACHABLE();
1728 } 2082 }
1729 2083
1730 const std::string sampler = GetSampler(instr.sampler); 2084 const std::string sampler =
2085 GetSampler(instr.sampler, instr.tld4.texture_type, false);
1731 // Add an extra scope and declare the texture coords inside to prevent 2086 // Add an extra scope and declare the texture coords inside to prevent
1732 // overwriting them in case they are used as outputs of the texs instruction. 2087 // overwriting them in case they are used as outputs of the texs instruction.
1733 shader.AddLine("{"); 2088 shader.AddLine("{");
@@ -1736,8 +2091,8 @@ private:
1736 const std::string texture = "textureGather(" + sampler + ", coords, " + 2091 const std::string texture = "textureGather(" + sampler + ", coords, " +
1737 std::to_string(instr.tld4.component) + ')'; 2092 std::to_string(instr.tld4.component) + ')';
1738 2093
1739 size_t dest_elem{}; 2094 std::size_t dest_elem{};
1740 for (size_t elem = 0; elem < 4; ++elem) { 2095 for (std::size_t elem = 0; elem < 4; ++elem) {
1741 if (!instr.tex.IsComponentEnabled(elem)) { 2096 if (!instr.tex.IsComponentEnabled(elem)) {
1742 // Skip disabled components 2097 // Skip disabled components
1743 continue; 2098 continue;
@@ -1750,16 +2105,100 @@ private:
1750 break; 2105 break;
1751 } 2106 }
1752 case OpCode::Id::TLD4S: { 2107 case OpCode::Id::TLD4S: {
2108 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2109 "NODEP is not implemented");
2110 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2111 "AOFFI is not implemented");
2112 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
2113 "DC is not implemented");
2114
1753 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2115 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1754 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); 2116 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
1755 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 2117 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
1756 const std::string sampler = GetSampler(instr.sampler); 2118 const std::string sampler =
2119 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
1757 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 2120 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
1758 const std::string texture = "textureGather(" + sampler + ", coords, " + 2121 const std::string texture = "textureGather(" + sampler + ", coords, " +
1759 std::to_string(instr.tld4s.component) + ')'; 2122 std::to_string(instr.tld4s.component) + ')';
1760 WriteTexsInstruction(instr, coord, texture); 2123 WriteTexsInstruction(instr, coord, texture);
1761 break; 2124 break;
1762 } 2125 }
2126 case OpCode::Id::TXQ: {
2127 ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2128 "NODEP is not implemented");
2129
2130 // TODO: the new commits on the texture refactor, change the way samplers work.
2131 // Sadly, not all texture instructions specify the type of texture their sampler
2132 // uses. This must be fixed at a later instance.
2133 const std::string sampler =
2134 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
2135 switch (instr.txq.query_type) {
2136 case Tegra::Shader::TextureQueryType::Dimension: {
2137 const std::string texture = "textureQueryLevels(" + sampler + ')';
2138 regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1);
2139 break;
2140 }
2141 default: {
2142 LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}",
2143 static_cast<u32>(instr.txq.query_type.Value()));
2144 UNREACHABLE();
2145 }
2146 }
2147 break;
2148 }
2149 case OpCode::Id::TMML: {
2150 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2151 "NODEP is not implemented");
2152 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2153 "NDV is not implemented");
2154
2155 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
2156 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2157 const bool is_array = instr.tmml.array != 0;
2158 auto texture_type = instr.tmml.texture_type.Value();
2159 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
2160
2161 // TODO: add coordinates for different samplers once other texture types are
2162 // implemented.
2163 std::string coord;
2164 switch (texture_type) {
2165 case Tegra::Shader::TextureType::Texture1D: {
2166 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2167 coord = "float coords = " + x + ';';
2168 break;
2169 }
2170 case Tegra::Shader::TextureType::Texture2D: {
2171 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2172 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2173 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2174 break;
2175 }
2176 default:
2177 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
2178 static_cast<u32>(texture_type));
2179 UNREACHABLE();
2180
2181 // Fallback to interpreting as a 2D texture for now
2182 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2183 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2184 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2185 texture_type = Tegra::Shader::TextureType::Texture2D;
2186 }
2187 // Add an extra scope and declare the texture coords inside to prevent
2188 // overwriting them in case they are used as outputs of the texs instruction.
2189 shader.AddLine('{');
2190 ++shader.scope;
2191 shader.AddLine(coord);
2192 const std::string texture = "textureQueryLod(" + sampler + ", coords)";
2193 const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);";
2194 shader.AddLine(tmp);
2195
2196 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1);
2197 regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1);
2198 --shader.scope;
2199 shader.AddLine('}');
2200 break;
2201 }
1763 default: { 2202 default: {
1764 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); 2203 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
1765 UNREACHABLE(); 2204 UNREACHABLE();
@@ -1799,12 +2238,12 @@ private:
1799 // We can't use the constant predicate as destination. 2238 // We can't use the constant predicate as destination.
1800 ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); 2239 ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
1801 2240
1802 std::string second_pred = 2241 const std::string second_pred =
1803 GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0); 2242 GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0);
1804 2243
1805 std::string combiner = GetPredicateCombiner(instr.fsetp.op); 2244 const std::string combiner = GetPredicateCombiner(instr.fsetp.op);
1806 2245
1807 std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b); 2246 const std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b);
1808 // Set the primary predicate to the result of Predicate OP SecondPredicate 2247 // Set the primary predicate to the result of Predicate OP SecondPredicate
1809 SetPredicate(instr.fsetp.pred3, 2248 SetPredicate(instr.fsetp.pred3,
1810 '(' + predicate + ") " + combiner + " (" + second_pred + ')'); 2249 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1818,7 +2257,8 @@ private:
1818 break; 2257 break;
1819 } 2258 }
1820 case OpCode::Type::IntegerSetPredicate: { 2259 case OpCode::Type::IntegerSetPredicate: {
1821 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed); 2260 const std::string op_a =
2261 regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed);
1822 std::string op_b; 2262 std::string op_b;
1823 2263
1824 if (instr.is_b_imm) { 2264 if (instr.is_b_imm) {
@@ -1835,12 +2275,12 @@ private:
1835 // We can't use the constant predicate as destination. 2275 // We can't use the constant predicate as destination.
1836 ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); 2276 ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
1837 2277
1838 std::string second_pred = 2278 const std::string second_pred =
1839 GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0); 2279 GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0);
1840 2280
1841 std::string combiner = GetPredicateCombiner(instr.isetp.op); 2281 const std::string combiner = GetPredicateCombiner(instr.isetp.op);
1842 2282
1843 std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b); 2283 const std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b);
1844 // Set the primary predicate to the result of Predicate OP SecondPredicate 2284 // Set the primary predicate to the result of Predicate OP SecondPredicate
1845 SetPredicate(instr.isetp.pred3, 2285 SetPredicate(instr.isetp.pred3,
1846 '(' + predicate + ") " + combiner + " (" + second_pred + ')'); 2286 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1853,32 +2293,80 @@ private:
1853 } 2293 }
1854 break; 2294 break;
1855 } 2295 }
2296 case OpCode::Type::PredicateSetRegister: {
2297 const std::string op_a =
2298 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
2299 const std::string op_b =
2300 GetPredicateCondition(instr.pset.pred29, instr.pset.neg_pred29 != 0);
2301
2302 const std::string second_pred =
2303 GetPredicateCondition(instr.pset.pred39, instr.pset.neg_pred39 != 0);
2304
2305 const std::string combiner = GetPredicateCombiner(instr.pset.op);
2306
2307 const std::string predicate =
2308 '(' + op_a + ") " + GetPredicateCombiner(instr.pset.cond) + " (" + op_b + ')';
2309 const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')';
2310 if (instr.pset.bf == 0) {
2311 const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0";
2312 regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1);
2313 } else {
2314 const std::string value = '(' + result + ") ? 1.0 : 0.0";
2315 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
2316 }
2317
2318 break;
2319 }
1856 case OpCode::Type::PredicateSetPredicate: { 2320 case OpCode::Type::PredicateSetPredicate: {
1857 std::string op_a = 2321 switch (opcode->GetId()) {
1858 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); 2322 case OpCode::Id::PSETP: {
1859 std::string op_b = 2323 const std::string op_a =
1860 GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); 2324 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
2325 const std::string op_b =
2326 GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0);
1861 2327
1862 // We can't use the constant predicate as destination. 2328 // We can't use the constant predicate as destination.
1863 ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); 2329 ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
1864 2330
1865 std::string second_pred = 2331 const std::string second_pred =
1866 GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); 2332 GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0);
1867 2333
1868 std::string combiner = GetPredicateCombiner(instr.psetp.op); 2334 const std::string combiner = GetPredicateCombiner(instr.psetp.op);
1869 2335
1870 std::string predicate = 2336 const std::string predicate =
1871 '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; 2337 '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')';
1872 2338
1873 // Set the primary predicate to the result of Predicate OP SecondPredicate 2339 // Set the primary predicate to the result of Predicate OP SecondPredicate
1874 SetPredicate(instr.psetp.pred3, 2340 SetPredicate(instr.psetp.pred3,
1875 '(' + predicate + ") " + combiner + " (" + second_pred + ')'); 2341 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
1876 2342
1877 if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2343 if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
1878 // Set the secondary predicate to the result of !Predicate OP SecondPredicate, 2344 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
1879 // if enabled 2345 // if enabled
1880 SetPredicate(instr.psetp.pred0, 2346 SetPredicate(instr.psetp.pred0,
1881 "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); 2347 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
2348 }
2349 break;
2350 }
2351 case OpCode::Id::CSETP: {
2352 const std::string pred =
2353 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2354 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2355 const std::string controlCode = regs.GetControlCode(instr.csetp.cc);
2356 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2357 SetPredicate(instr.csetp.pred3,
2358 '(' + controlCode + ") " + combiner + " (" + pred + ')');
2359 }
2360 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2361 SetPredicate(instr.csetp.pred0,
2362 "!(" + controlCode + ") " + combiner + " (" + pred + ')');
2363 }
2364 break;
2365 }
2366 default: {
2367 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName());
2368 UNREACHABLE();
2369 }
1882 } 2370 }
1883 break; 2371 break;
1884 } 2372 }
@@ -1893,7 +2381,7 @@ private:
1893 std::string op_b = instr.fset.neg_b ? "-" : ""; 2381 std::string op_b = instr.fset.neg_b ? "-" : "";
1894 2382
1895 if (instr.is_b_imm) { 2383 if (instr.is_b_imm) {
1896 std::string imm = GetImmediate19(instr); 2384 const std::string imm = GetImmediate19(instr);
1897 if (instr.fset.neg_imm) 2385 if (instr.fset.neg_imm)
1898 op_b += "(-" + imm + ')'; 2386 op_b += "(-" + imm + ')';
1899 else 2387 else
@@ -1913,13 +2401,14 @@ private:
1913 2401
1914 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the 2402 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
1915 // condition is true, and to 0 otherwise. 2403 // condition is true, and to 0 otherwise.
1916 std::string second_pred = 2404 const std::string second_pred =
1917 GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0); 2405 GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
1918 2406
1919 std::string combiner = GetPredicateCombiner(instr.fset.op); 2407 const std::string combiner = GetPredicateCombiner(instr.fset.op);
1920 2408
1921 std::string predicate = "((" + GetPredicateComparison(instr.fset.cond, op_a, op_b) + 2409 const std::string predicate = "((" +
1922 ") " + combiner + " (" + second_pred + "))"; 2410 GetPredicateComparison(instr.fset.cond, op_a, op_b) +
2411 ") " + combiner + " (" + second_pred + "))";
1923 2412
1924 if (instr.fset.bf) { 2413 if (instr.fset.bf) {
1925 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); 2414 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -1930,7 +2419,7 @@ private:
1930 break; 2419 break;
1931 } 2420 }
1932 case OpCode::Type::IntegerSet: { 2421 case OpCode::Type::IntegerSet: {
1933 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed); 2422 const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed);
1934 2423
1935 std::string op_b; 2424 std::string op_b;
1936 2425
@@ -1947,13 +2436,14 @@ private:
1947 2436
1948 // The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the 2437 // The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
1949 // condition is true, and to 0 otherwise. 2438 // condition is true, and to 0 otherwise.
1950 std::string second_pred = 2439 const std::string second_pred =
1951 GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0); 2440 GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0);
1952 2441
1953 std::string combiner = GetPredicateCombiner(instr.iset.op); 2442 const std::string combiner = GetPredicateCombiner(instr.iset.op);
1954 2443
1955 std::string predicate = "((" + GetPredicateComparison(instr.iset.cond, op_a, op_b) + 2444 const std::string predicate = "((" +
1956 ") " + combiner + " (" + second_pred + "))"; 2445 GetPredicateComparison(instr.iset.cond, op_a, op_b) +
2446 ") " + combiner + " (" + second_pred + "))";
1957 2447
1958 if (instr.iset.bf) { 2448 if (instr.iset.bf) {
1959 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); 2449 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -2103,45 +2593,22 @@ private:
2103 case OpCode::Id::BRA: { 2593 case OpCode::Id::BRA: {
2104 ASSERT_MSG(instr.bra.constant_buffer == 0, 2594 ASSERT_MSG(instr.bra.constant_buffer == 0,
2105 "BRA with constant buffers are not implemented"); 2595 "BRA with constant buffers are not implemented");
2106 u32 target = offset + instr.bra.GetBranchTarget(); 2596 const u32 target = offset + instr.bra.GetBranchTarget();
2107 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 2597 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
2108 break; 2598 break;
2109 } 2599 }
2110 case OpCode::Id::IPA: { 2600 case OpCode::Id::IPA: {
2111 const auto& attribute = instr.attribute.fmt28; 2601 const auto& attribute = instr.attribute.fmt28;
2112 const auto& reg = instr.gpr0; 2602 const auto& reg = instr.gpr0;
2113 switch (instr.ipa.mode) {
2114 case Tegra::Shader::IpaMode::Pass:
2115 if (stage == Maxwell3D::Regs::ShaderStage::Fragment &&
2116 attribute.index == Attribute::Index::Position) {
2117 switch (attribute.element) {
2118 case 0:
2119 shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.x;");
2120 break;
2121 case 1:
2122 shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.y;");
2123 break;
2124 case 2:
2125 shader.AddLine(regs.GetRegisterAsFloat(reg) + " = gl_FragCoord.z;");
2126 break;
2127 case 3:
2128 shader.AddLine(regs.GetRegisterAsFloat(reg) + " = 1.0;");
2129 break;
2130 }
2131 } else {
2132 regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
2133 }
2134 break;
2135 case Tegra::Shader::IpaMode::None:
2136 regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
2137 break;
2138 default:
2139 LOG_CRITICAL(HW_GPU, "Unhandled IPA mode: {}",
2140 static_cast<u32>(instr.ipa.mode.Value()));
2141 UNREACHABLE();
2142 regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
2143 }
2144 2603
2604 Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
2605 instr.ipa.sample_mode.Value()};
2606 regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index,
2607 input_mode);
2608
2609 if (instr.ipa.saturate) {
2610 regs.SetRegisterToFloat(reg, 0, regs.GetRegisterAsFloat(reg), 1, 1, true);
2611 }
2145 break; 2612 break;
2146 } 2613 }
2147 case OpCode::Id::SSY: { 2614 case OpCode::Id::SSY: {
@@ -2150,7 +2617,7 @@ private:
2150 // has a similar structure to the BRA opcode. 2617 // has a similar structure to the BRA opcode.
2151 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 2618 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
2152 2619
2153 u32 target = offset + instr.bra.GetBranchTarget(); 2620 const u32 target = offset + instr.bra.GetBranchTarget();
2154 EmitPushToSSYStack(target); 2621 EmitPushToSSYStack(target);
2155 break; 2622 break;
2156 } 2623 }
@@ -2244,10 +2711,10 @@ private:
2244 shader.AddLine("case " + std::to_string(label) + "u: {"); 2711 shader.AddLine("case " + std::to_string(label) + "u: {");
2245 ++shader.scope; 2712 ++shader.scope;
2246 2713
2247 auto next_it = labels.lower_bound(label + 1); 2714 const auto next_it = labels.lower_bound(label + 1);
2248 u32 next_label = next_it == labels.end() ? subroutine.end : *next_it; 2715 const u32 next_label = next_it == labels.end() ? subroutine.end : *next_it;
2249 2716
2250 u32 compile_end = CompileRange(label, next_label); 2717 const u32 compile_end = CompileRange(label, next_label);
2251 if (compile_end > next_label && compile_end != PROGRAM_END) { 2718 if (compile_end > next_label && compile_end != PROGRAM_END) {
2252 // This happens only when there is a label inside a IF/LOOP block 2719 // This happens only when there is a label inside a IF/LOOP block
2253 shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }"); 2720 shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }");
@@ -2289,6 +2756,7 @@ private:
2289private: 2756private:
2290 const std::set<Subroutine>& subroutines; 2757 const std::set<Subroutine>& subroutines;
2291 const ProgramCode& program_code; 2758 const ProgramCode& program_code;
2759 Tegra::Shader::Header header;
2292 const u32 main_offset; 2760 const u32 main_offset;
2293 Maxwell3D::Regs::ShaderStage stage; 2761 Maxwell3D::Regs::ShaderStage stage;
2294 const std::string& suffix; 2762 const std::string& suffix;
@@ -2310,7 +2778,8 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
2310 Maxwell3D::Regs::ShaderStage stage, 2778 Maxwell3D::Regs::ShaderStage stage,
2311 const std::string& suffix) { 2779 const std::string& suffix) {
2312 try { 2780 try {
2313 auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 2781 const auto subroutines =
2782 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
2314 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); 2783 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix);
2315 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 2784 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
2316 } catch (const DecompileFail& exception) { 2785 } catch (const DecompileFail& exception) {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 6ca05945e..b0466c18f 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -42,6 +42,7 @@ layout (std140) uniform vs_config {
42}; 42};
43 43
44void main() { 44void main() {
45 position = vec4(0.0, 0.0, 0.0, 0.0);
45 exec_vertex(); 46 exec_vertex();
46)"; 47)";
47 48
@@ -87,7 +88,14 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
87 .get_value_or({}); 88 .get_value_or({});
88 out += R"( 89 out += R"(
89in vec4 position; 90in vec4 position;
90layout(location = 0) out vec4 color[8]; 91layout(location = 0) out vec4 FragColor0;
92layout(location = 1) out vec4 FragColor1;
93layout(location = 2) out vec4 FragColor2;
94layout(location = 3) out vec4 FragColor3;
95layout(location = 4) out vec4 FragColor4;
96layout(location = 5) out vec4 FragColor5;
97layout(location = 6) out vec4 FragColor6;
98layout(location = 7) out vec4 FragColor7;
91 99
92layout (std140) uniform fs_config { 100layout (std140) uniform fs_config {
93 vec4 viewport_flip; 101 vec4 viewport_flip;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index c788099d4..d53b93ad5 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -9,10 +9,11 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/shader_bytecode.h"
12 13
13namespace OpenGL::GLShader { 14namespace OpenGL::GLShader {
14 15
15constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; 16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
16using ProgramCode = std::vector<u64>; 17using ProgramCode = std::vector<u64>;
17 18
18class ConstBufferEntry { 19class ConstBufferEntry {
@@ -50,7 +51,11 @@ public:
50 } 51 }
51 52
52 std::string GetName() const { 53 std::string GetName() const {
53 return BufferBaseNames[static_cast<size_t>(stage)] + std::to_string(index); 54 return BufferBaseNames[static_cast<std::size_t>(stage)] + std::to_string(index);
55 }
56
57 u32 GetHash() const {
58 return (static_cast<u32>(stage) << 16) | index;
54 } 59 }
55 60
56private: 61private:
@@ -69,14 +74,15 @@ class SamplerEntry {
69 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 74 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
70 75
71public: 76public:
72 SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) 77 SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index,
73 : offset(offset), stage(stage), sampler_index(index) {} 78 Tegra::Shader::TextureType type, bool is_array)
79 : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
74 80
75 size_t GetOffset() const { 81 std::size_t GetOffset() const {
76 return offset; 82 return offset;
77 } 83 }
78 84
79 size_t GetIndex() const { 85 std::size_t GetIndex() const {
80 return sampler_index; 86 return sampler_index;
81 } 87 }
82 88
@@ -85,23 +91,63 @@ public:
85 } 91 }
86 92
87 std::string GetName() const { 93 std::string GetName() const {
88 return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + 94 return std::string(TextureSamplerNames[static_cast<std::size_t>(stage)]) + '_' +
89 std::to_string(sampler_index) + ']'; 95 std::to_string(sampler_index);
96 }
97
98 std::string GetTypeString() const {
99 using Tegra::Shader::TextureType;
100 std::string glsl_type;
101
102 switch (type) {
103 case TextureType::Texture1D:
104 glsl_type = "sampler1D";
105 break;
106 case TextureType::Texture2D:
107 glsl_type = "sampler2D";
108 break;
109 case TextureType::Texture3D:
110 glsl_type = "sampler3D";
111 break;
112 case TextureType::TextureCube:
113 glsl_type = "samplerCube";
114 break;
115 default:
116 UNIMPLEMENTED();
117 }
118 if (is_array)
119 glsl_type += "Array";
120 return glsl_type;
121 }
122
123 Tegra::Shader::TextureType GetType() const {
124 return type;
125 }
126
127 bool IsArray() const {
128 return is_array;
129 }
130
131 u32 GetHash() const {
132 return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index);
90 } 133 }
91 134
92 static std::string GetArrayName(Maxwell::ShaderStage stage) { 135 static std::string GetArrayName(Maxwell::ShaderStage stage) {
93 return TextureSamplerNames[static_cast<size_t>(stage)]; 136 return TextureSamplerNames[static_cast<std::size_t>(stage)];
94 } 137 }
95 138
96private: 139private:
97 static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { 140 static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
98 "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", 141 "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
99 }; 142 };
143
100 /// Offset in TSC memory from which to read the sampler object, as specified by the sampling 144 /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
101 /// instruction. 145 /// instruction.
102 size_t offset; 146 std::size_t offset;
103 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. 147 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
104 size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. 148 std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
149 Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
150 bool is_array; ///< Whether the texture is being sampled as an array texture or not.
105}; 151};
106 152
107struct ShaderEntries { 153struct ShaderEntries {
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 533e42caa..b86cd96e8 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -12,7 +12,7 @@
12namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
13 13
14/// Number of OpenGL texture samplers that can be used in the fragment shader 14/// Number of OpenGL texture samplers that can be used in the fragment shader
15static constexpr size_t NumTextureSamplers = 32; 15static constexpr std::size_t NumTextureSamplers = 32;
16 16
17using Tegra::Engines::Maxwell3D; 17using Tegra::Engines::Maxwell3D;
18 18
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 5781d9d16..5f3fe067e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -25,7 +25,7 @@ GLuint LoadShader(const char* source, GLenum type) {
25 default: 25 default:
26 UNREACHABLE(); 26 UNREACHABLE();
27 } 27 }
28 GLuint shader_id = glCreateShader(type); 28 const GLuint shader_id = glCreateShader(type);
29 glShaderSource(shader_id, 1, &source, nullptr); 29 glShaderSource(shader_id, 1, &source, nullptr);
30 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 30 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
31 glCompileShader(shader_id); 31 glCompileShader(shader_id);
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 60a4defd1..af99132ba 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -200,9 +200,9 @@ void OpenGLState::Apply() const {
200 const auto& texture_unit = texture_units[i]; 200 const auto& texture_unit = texture_units[i];
201 const auto& cur_state_texture_unit = cur_state.texture_units[i]; 201 const auto& cur_state_texture_unit = cur_state.texture_units[i];
202 202
203 if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) { 203 if (texture_unit.texture != cur_state_texture_unit.texture) {
204 glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); 204 glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum());
205 glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d); 205 glBindTexture(texture_unit.target, texture_unit.texture);
206 } 206 }
207 if (texture_unit.sampler != cur_state_texture_unit.sampler) { 207 if (texture_unit.sampler != cur_state_texture_unit.sampler) {
208 glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); 208 glBindSampler(static_cast<GLuint>(i), texture_unit.sampler);
@@ -214,7 +214,7 @@ void OpenGLState::Apply() const {
214 texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) { 214 texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) {
215 std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g, 215 std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g,
216 texture_unit.swizzle.b, texture_unit.swizzle.a}; 216 texture_unit.swizzle.b, texture_unit.swizzle.a};
217 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); 217 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
218 } 218 }
219 } 219 }
220 220
@@ -272,7 +272,7 @@ void OpenGLState::Apply() const {
272 } 272 }
273 273
274 // Clip distance 274 // Clip distance
275 for (size_t i = 0; i < clip_distance.size(); ++i) { 275 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
276 if (clip_distance[i] != cur_state.clip_distance[i]) { 276 if (clip_distance[i] != cur_state.clip_distance[i]) {
277 if (clip_distance[i]) { 277 if (clip_distance[i]) {
278 glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); 278 glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
@@ -287,7 +287,7 @@ void OpenGLState::Apply() const {
287 287
288OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { 288OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
289 for (auto& unit : texture_units) { 289 for (auto& unit : texture_units) {
290 if (unit.texture_2d == handle) { 290 if (unit.texture == handle) {
291 unit.Unbind(); 291 unit.Unbind();
292 } 292 }
293 } 293 }
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 46e96a97d..e3e24b9e7 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -94,8 +94,9 @@ public:
94 94
95 // 3 texture units - one for each that is used in PICA fragment shader emulation 95 // 3 texture units - one for each that is used in PICA fragment shader emulation
96 struct TextureUnit { 96 struct TextureUnit {
97 GLuint texture_2d; // GL_TEXTURE_BINDING_2D 97 GLuint texture; // GL_TEXTURE_BINDING_2D
98 GLuint sampler; // GL_SAMPLER_BINDING 98 GLuint sampler; // GL_SAMPLER_BINDING
99 GLenum target;
99 struct { 100 struct {
100 GLint r; // GL_TEXTURE_SWIZZLE_R 101 GLint r; // GL_TEXTURE_SWIZZLE_R
101 GLint g; // GL_TEXTURE_SWIZZLE_G 102 GLint g; // GL_TEXTURE_SWIZZLE_G
@@ -104,7 +105,7 @@ public:
104 } swizzle; 105 } swizzle;
105 106
106 void Unbind() { 107 void Unbind() {
107 texture_2d = 0; 108 texture = 0;
108 swizzle.r = GL_RED; 109 swizzle.r = GL_RED;
109 swizzle.g = GL_GREEN; 110 swizzle.g = GL_GREEN;
110 swizzle.b = GL_BLUE; 111 swizzle.b = GL_BLUE;
@@ -114,6 +115,7 @@ public:
114 void Reset() { 115 void Reset() {
115 Unbind(); 116 Unbind();
116 sampler = 0; 117 sampler = 0;
118 target = GL_TEXTURE_2D;
117 } 119 }
118 }; 120 };
119 std::array<TextureUnit, 32> texture_units; 121 std::array<TextureUnit, 32> texture_units;
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index e565afcee..664f3ca20 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -29,7 +29,7 @@ OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coh
29 if (GLAD_GL_ARB_buffer_storage) { 29 if (GLAD_GL_ARB_buffer_storage) {
30 persistent = true; 30 persistent = true;
31 coherent = prefer_coherent; 31 coherent = prefer_coherent;
32 GLbitfield flags = 32 const GLbitfield flags =
33 GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); 33 GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
34 glBufferStorage(gl_target, allocate_size, nullptr, flags); 34 glBufferStorage(gl_target, allocate_size, nullptr, flags);
35 mapped_ptr = static_cast<u8*>(glMapBufferRange( 35 mapped_ptr = static_cast<u8*>(glMapBufferRange(
@@ -61,7 +61,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
61 mapped_size = size; 61 mapped_size = size;
62 62
63 if (alignment > 0) { 63 if (alignment > 0) {
64 buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); 64 buffer_pos = Common::AlignUp<std::size_t>(buffer_pos, alignment);
65 } 65 }
66 66
67 bool invalidate = false; 67 bool invalidate = false;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 411a73d50..96d916b07 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
177 Memory::GetPointer(framebuffer_addr), 177 Memory::GetPointer(framebuffer_addr),
178 gl_framebuffer_data.data(), true); 178 gl_framebuffer_data.data(), true);
179 179
180 state.texture_units[0].texture_2d = screen_info.texture.resource.handle; 180 state.texture_units[0].texture = screen_info.texture.resource.handle;
181 state.Apply(); 181 state.Apply();
182 182
183 glActiveTexture(GL_TEXTURE0); 183 glActiveTexture(GL_TEXTURE0);
@@ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
194 194
195 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 195 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
196 196
197 state.texture_units[0].texture_2d = 0; 197 state.texture_units[0].texture = 0;
198 state.Apply(); 198 state.Apply();
199 } 199 }
200} 200}
@@ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
205 */ 205 */
206void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 206void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
207 const TextureInfo& texture) { 207 const TextureInfo& texture) {
208 state.texture_units[0].texture_2d = texture.resource.handle; 208 state.texture_units[0].texture = texture.resource.handle;
209 state.Apply(); 209 state.Apply();
210 210
211 glActiveTexture(GL_TEXTURE0); 211 glActiveTexture(GL_TEXTURE0);
@@ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
214 // Update existing texture 214 // Update existing texture
215 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); 215 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
216 216
217 state.texture_units[0].texture_2d = 0; 217 state.texture_units[0].texture = 0;
218 state.Apply(); 218 state.Apply();
219} 219}
220 220
@@ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() {
260 // Allocation of storage is deferred until the first frame, when we 260 // Allocation of storage is deferred until the first frame, when we
261 // know the framebuffer size. 261 // know the framebuffer size.
262 262
263 state.texture_units[0].texture_2d = screen_info.texture.resource.handle; 263 state.texture_units[0].texture = screen_info.texture.resource.handle;
264 state.Apply(); 264 state.Apply();
265 265
266 glActiveTexture(GL_TEXTURE0); 266 glActiveTexture(GL_TEXTURE0);
@@ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() {
272 272
273 screen_info.display_texture = screen_info.texture.resource.handle; 273 screen_info.display_texture = screen_info.texture.resource.handle;
274 274
275 state.texture_units[0].texture_2d = 0; 275 state.texture_units[0].texture = 0;
276 state.Apply(); 276 state.Apply();
277 277
278 // Clear screen to black 278 // Clear screen to black
@@ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
305 UNREACHABLE(); 305 UNREACHABLE();
306 } 306 }
307 307
308 state.texture_units[0].texture_2d = texture.resource.handle; 308 state.texture_units[0].texture = texture.resource.handle;
309 state.Apply(); 309 state.Apply();
310 310
311 glActiveTexture(GL_TEXTURE0); 311 glActiveTexture(GL_TEXTURE0);
312 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, 312 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
313 texture.gl_format, texture.gl_type, nullptr); 313 texture.gl_format, texture.gl_type, nullptr);
314 314
315 state.texture_units[0].texture_2d = 0; 315 state.texture_units[0].texture = 0;
316 state.Apply(); 316 state.Apply();
317} 317}
318 318
@@ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
354 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), 354 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
355 }}; 355 }};
356 356
357 state.texture_units[0].texture_2d = screen_info.display_texture; 357 state.texture_units[0].texture = screen_info.display_texture;
358 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; 358 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
359 state.Apply(); 359 state.Apply();
360 360
361 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); 361 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
362 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 362 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
363 363
364 state.texture_units[0].texture_2d = 0; 364 state.texture_units[0].texture = 0;
365 state.Apply(); 365 state.Apply();
366} 366}
367 367
@@ -369,6 +369,12 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
369 * Draws the emulated screens to the emulator window. 369 * Draws the emulated screens to the emulator window.
370 */ 370 */
371void RendererOpenGL::DrawScreen() { 371void RendererOpenGL::DrawScreen() {
372 if (renderer_settings.set_background_color) {
373 // Update background color before drawing
374 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
375 0.0f);
376 }
377
372 const auto& layout = render_window.GetFramebufferLayout(); 378 const auto& layout = render_window.GetFramebufferLayout();
373 const auto& screen = layout.screen; 379 const auto& screen = layout.screen;
374 380
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 272294c62..20ba6d4f6 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -46,6 +46,48 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
46 } 46 }
47} 47}
48 48
49template <std::size_t N, std::size_t M>
50struct alignas(64) SwizzleTable {
51 constexpr SwizzleTable() {
52 for (u32 y = 0; y < N; ++y) {
53 for (u32 x = 0; x < M; ++x) {
54 const u32 x2 = x * 16;
55 values[y][x] = static_cast<u16>(((x2 % 64) / 32) * 256 + ((y % 8) / 2) * 64 +
56 ((x2 % 32) / 16) * 32 + (y % 2) * 16);
57 }
58 }
59 }
60 const std::array<u16, M>& operator[](std::size_t index) const {
61 return values[index];
62 }
63 std::array<std::array<u16, M>, N> values{};
64};
65
66constexpr auto swizzle_table = SwizzleTable<8, 4>();
67
68void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_data,
69 u8* unswizzled_data, bool unswizzle, u32 block_height) {
70 std::array<u8*, 2> data_ptrs;
71 const std::size_t stride{width * bytes_per_pixel};
72 const std::size_t image_width_in_gobs{(stride + 63) / 64};
73 const std::size_t copy_size{16};
74 for (std::size_t y = 0; y < height; ++y) {
75 const std::size_t initial_gob =
76 (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
77 (y % (8 * block_height) / 8) * 512;
78 const std::size_t pixel_base{y * width * bytes_per_pixel};
79 const auto& table = swizzle_table[y % 8];
80 for (std::size_t xb = 0; xb < stride; xb += copy_size) {
81 const std::size_t gob_address{initial_gob + (xb / 64) * 512 * block_height};
82 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]};
83 const std::size_t pixel_index{xb + pixel_base};
84 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
85 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
86 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
87 }
88 }
89}
90
49u32 BytesPerPixel(TextureFormat format) { 91u32 BytesPerPixel(TextureFormat format) {
50 switch (format) { 92 switch (format) {
51 case TextureFormat::DXT1: 93 case TextureFormat::DXT1:
@@ -63,6 +105,7 @@ u32 BytesPerPixel(TextureFormat format) {
63 case TextureFormat::R32_G32_B32: 105 case TextureFormat::R32_G32_B32:
64 return 12; 106 return 12;
65 case TextureFormat::ASTC_2D_4X4: 107 case TextureFormat::ASTC_2D_4X4:
108 case TextureFormat::ASTC_2D_8X8:
66 case TextureFormat::A8R8G8B8: 109 case TextureFormat::A8R8G8B8:
67 case TextureFormat::A2B10G10R10: 110 case TextureFormat::A2B10G10R10:
68 case TextureFormat::BF10GF11RF11: 111 case TextureFormat::BF10GF11RF11:
@@ -91,8 +134,13 @@ u32 BytesPerPixel(TextureFormat format) {
91std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 134std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
92 u32 height, u32 block_height) { 135 u32 height, u32 block_height) {
93 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 136 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
94 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 137 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
95 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 138 FastSwizzleData(width / tile_size, height / tile_size, bytes_per_pixel,
139 Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
140 } else {
141 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
142 Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
143 }
96 return unswizzled_data; 144 return unswizzled_data;
97} 145}
98 146
@@ -111,6 +159,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
111 case TextureFormat::BC6H_UF16: 159 case TextureFormat::BC6H_UF16:
112 case TextureFormat::BC6H_SF16: 160 case TextureFormat::BC6H_SF16:
113 case TextureFormat::ASTC_2D_4X4: 161 case TextureFormat::ASTC_2D_4X4:
162 case TextureFormat::ASTC_2D_8X8:
114 case TextureFormat::A8R8G8B8: 163 case TextureFormat::A8R8G8B8:
115 case TextureFormat::A2B10G10R10: 164 case TextureFormat::A2B10G10R10:
116 case TextureFormat::A1B5G5R5: 165 case TextureFormat::A1B5G5R5:
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index c6bd2f4b9..c2fb824b2 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -170,8 +170,12 @@ struct TICEntry {
170 BitField<0, 16, u32> width_minus_1; 170 BitField<0, 16, u32> width_minus_1;
171 BitField<23, 4, TextureType> texture_type; 171 BitField<23, 4, TextureType> texture_type;
172 }; 172 };
173 u16 height_minus_1; 173 union {
174 INSERT_PADDING_BYTES(10); 174 BitField<0, 16, u32> height_minus_1;
175 BitField<16, 15, u32> depth_minus_1;
176 };
177
178 INSERT_PADDING_BYTES(8);
175 179
176 GPUVAddr Address() const { 180 GPUVAddr Address() const {
177 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); 181 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
@@ -192,6 +196,10 @@ struct TICEntry {
192 return height_minus_1 + 1; 196 return height_minus_1 + 1;
193 } 197 }
194 198
199 u32 Depth() const {
200 return depth_minus_1 + 1;
201 }
202
195 u32 BlockHeight() const { 203 u32 BlockHeight() const {
196 ASSERT(header_version == TICHeaderVersion::BlockLinear || 204 ASSERT(header_version == TICHeaderVersion::BlockLinear ||
197 header_version == TICHeaderVersion::BlockLinearColorKey); 205 header_version == TICHeaderVersion::BlockLinearColorKey);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index ea9ea69e4..f48b69809 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -9,6 +9,8 @@ add_executable(yuzu
9 about_dialog.h 9 about_dialog.h
10 bootmanager.cpp 10 bootmanager.cpp
11 bootmanager.h 11 bootmanager.h
12 compatibility_list.cpp
13 compatibility_list.h
12 configuration/config.cpp 14 configuration/config.cpp
13 configuration/config.h 15 configuration/config.h
14 configuration/configure_audio.cpp 16 configuration/configure_audio.cpp
@@ -43,6 +45,8 @@ add_executable(yuzu
43 game_list.cpp 45 game_list.cpp
44 game_list.h 46 game_list.h
45 game_list_p.h 47 game_list_p.h
48 game_list_worker.cpp
49 game_list_worker.h
46 hotkeys.cpp 50 hotkeys.cpp
47 hotkeys.h 51 hotkeys.h
48 main.cpp 52 main.cpp
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index a81ad2888..3efa65a38 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -11,7 +11,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
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( 13 ui->labelBuildInfo->setText(
14 ui->labelBuildInfo->text().arg(Common::g_build_name, Common::g_scm_branch, 14 ui->labelBuildInfo->text().arg(Common::g_build_fullname, Common::g_scm_branch,
15 Common::g_scm_desc, QString(Common::g_build_date).left(10))); 15 Common::g_scm_desc, QString(Common::g_build_date).left(10)));
16} 16}
17 17
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 159b2c32b..4e4c108ab 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -112,6 +112,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
112 setWindowTitle(QString::fromStdString(window_title)); 112 setWindowTitle(QString::fromStdString(window_title));
113 113
114 InputCommon::Init(); 114 InputCommon::Init();
115 InputCommon::StartJoystickEventHandler();
115} 116}
116 117
117GRenderWindow::~GRenderWindow() { 118GRenderWindow::~GRenderWindow() {
@@ -256,6 +257,7 @@ void GRenderWindow::InitRenderTarget() {
256 QGLFormat fmt; 257 QGLFormat fmt;
257 fmt.setVersion(3, 3); 258 fmt.setVersion(3, 3);
258 fmt.setProfile(QGLFormat::CoreProfile); 259 fmt.setProfile(QGLFormat::CoreProfile);
260 fmt.setSwapInterval(false);
259 261
260 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X 262 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
261 fmt.setOption(QGL::NoDeprecatedFunctions); 263 fmt.setOption(QGL::NoDeprecatedFunctions);
diff --git a/src/yuzu/compatibility_list.cpp b/src/yuzu/compatibility_list.cpp
new file mode 100644
index 000000000..2d2cfd03c
--- /dev/null
+++ b/src/yuzu/compatibility_list.cpp
@@ -0,0 +1,18 @@
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 <fmt/format.h>
8
9#include "yuzu/compatibility_list.h"
10
11CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
12 const CompatibilityList& compatibility_list, u64 program_id) {
13 return std::find_if(compatibility_list.begin(), compatibility_list.end(),
14 [program_id](const auto& element) {
15 std::string pid = fmt::format("{:016X}", program_id);
16 return element.first == pid;
17 });
18}
diff --git a/src/yuzu/compatibility_list.h b/src/yuzu/compatibility_list.h
new file mode 100644
index 000000000..bc0175bd3
--- /dev/null
+++ b/src/yuzu/compatibility_list.h
@@ -0,0 +1,17 @@
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 <unordered_map>
9
10#include <QString>
11
12#include "common/common_types.h"
13
14using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>;
15
16CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
17 const CompatibilityList& compatibility_list, u64 program_id);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 60b6d6d44..d229225b4 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -95,6 +95,8 @@ void Config::ReadValues() {
95 95
96 qt_config->beginGroup("Audio"); 96 qt_config->beginGroup("Audio");
97 Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); 97 Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
98 Settings::values.enable_audio_stretching =
99 qt_config->value("enable_audio_stretching", true).toBool();
98 Settings::values.audio_device_id = 100 Settings::values.audio_device_id =
99 qt_config->value("output_device", "auto").toString().toStdString(); 101 qt_config->value("output_device", "auto").toString().toStdString();
100 Settings::values.volume = qt_config->value("volume", 1).toFloat(); 102 Settings::values.volume = qt_config->value("volume", 1).toFloat();
@@ -102,6 +104,20 @@ void Config::ReadValues() {
102 104
103 qt_config->beginGroup("Data Storage"); 105 qt_config->beginGroup("Data Storage");
104 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 106 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
107 FileUtil::GetUserPath(
108 FileUtil::UserPath::NANDDir,
109 qt_config
110 ->value("nand_directory",
111 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)))
112 .toString()
113 .toStdString());
114 FileUtil::GetUserPath(
115 FileUtil::UserPath::SDMCDir,
116 qt_config
117 ->value("sdmc_directory",
118 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
119 .toString()
120 .toStdString());
105 qt_config->endGroup(); 121 qt_config->endGroup();
106 122
107 qt_config->beginGroup("System"); 123 qt_config->beginGroup("System");
@@ -216,12 +232,17 @@ void Config::SaveValues() {
216 232
217 qt_config->beginGroup("Audio"); 233 qt_config->beginGroup("Audio");
218 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); 234 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
235 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
219 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); 236 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
220 qt_config->setValue("volume", Settings::values.volume); 237 qt_config->setValue("volume", Settings::values.volume);
221 qt_config->endGroup(); 238 qt_config->endGroup();
222 239
223 qt_config->beginGroup("Data Storage"); 240 qt_config->beginGroup("Data Storage");
224 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 241 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
242 qt_config->setValue("nand_directory",
243 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
244 qt_config->setValue("sdmc_directory",
245 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
225 qt_config->endGroup(); 246 qt_config->endGroup();
226 247
227 qt_config->beginGroup("System"); 248 qt_config->beginGroup("System");
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fbb813f6c..6ea59f2a3 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -46,6 +46,8 @@ void ConfigureAudio::setConfiguration() {
46 } 46 }
47 ui->output_sink_combo_box->setCurrentIndex(new_sink_index); 47 ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
48 48
49 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
50
49 // The device list cannot be pre-populated (nor listed) until the output sink is known. 51 // The device list cannot be pre-populated (nor listed) until the output sink is known.
50 updateAudioDevices(new_sink_index); 52 updateAudioDevices(new_sink_index);
51 53
@@ -67,6 +69,7 @@ void ConfigureAudio::applyConfiguration() {
67 Settings::values.sink_id = 69 Settings::values.sink_id =
68 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) 70 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
69 .toStdString(); 71 .toStdString();
72 Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
70 Settings::values.audio_device_id = 73 Settings::values.audio_device_id =
71 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) 74 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
72 .toStdString(); 75 .toStdString();
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index ef67890dc..a29a0e265 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -31,6 +31,16 @@
31 </item> 31 </item>
32 </layout> 32 </layout>
33 </item> 33 </item>
34 <item>
35 <widget class="QCheckBox" name="toggle_audio_stretching">
36 <property name="toolTip">
37 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
38 </property>
39 <property name="text">
40 <string>Enable audio stretching</string>
41 </property>
42 </widget>
43 </item>
34 <item> 44 <item>
35 <layout class="QHBoxLayout"> 45 <layout class="QHBoxLayout">
36 <item> 46 <item>
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 1ae3423cf..8743ce982 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -2,47 +2,51 @@
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 <array>
6#include <utility>
7
8#include "common/common_types.h"
6#include "core/settings.h" 9#include "core/settings.h"
7#include "ui_configure_gamelist.h" 10#include "ui_configure_gamelist.h"
8#include "ui_settings.h"
9#include "yuzu/configuration/configure_gamelist.h" 11#include "yuzu/configuration/configure_gamelist.h"
12#include "yuzu/ui_settings.h"
13
14namespace {
15constexpr std::array<std::pair<u32, const char*>, 5> default_icon_sizes{{
16 std::make_pair(0, QT_TR_NOOP("None")),
17 std::make_pair(32, QT_TR_NOOP("Small (32x32)")),
18 std::make_pair(64, QT_TR_NOOP("Standard (64x64)")),
19 std::make_pair(128, QT_TR_NOOP("Large (128x128)")),
20 std::make_pair(256, QT_TR_NOOP("Full Size (256x256)")),
21}};
22
23constexpr std::array<const char*, 4> row_text_names{{
24 QT_TR_NOOP("Filename"),
25 QT_TR_NOOP("Filetype"),
26 QT_TR_NOOP("Title ID"),
27 QT_TR_NOOP("Title Name"),
28}};
29} // Anonymous namespace
10 30
11ConfigureGameList::ConfigureGameList(QWidget* parent) 31ConfigureGameList::ConfigureGameList(QWidget* parent)
12 : QWidget(parent), ui(new Ui::ConfigureGameList) { 32 : QWidget(parent), ui(new Ui::ConfigureGameList) {
13 ui->setupUi(this); 33 ui->setupUi(this);
14 34
15 static const std::vector<std::pair<u32, std::string>> default_icon_sizes{ 35 InitializeIconSizeComboBox();
16 std::make_pair(0, "None"), std::make_pair(32, "Small"), 36 InitializeRowComboBoxes();
17 std::make_pair(64, "Standard"), std::make_pair(128, "Large"),
18 std::make_pair(256, "Full Size"),
19 };
20
21 for (const auto& size : default_icon_sizes) {
22 ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" +
23 std::to_string(size.first) + "x" +
24 std::to_string(size.first) + ")"),
25 size.first);
26 }
27
28 static const std::vector<std::string> row_text_names{
29 "Filename",
30 "Filetype",
31 "Title ID",
32 "Title Name",
33 };
34
35 for (size_t i = 0; i < row_text_names.size(); ++i) {
36 ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
37 QVariant::fromValue(i));
38 ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
39 QVariant::fromValue(i));
40 }
41 37
42 this->setConfiguration(); 38 this->setConfiguration();
43} 39}
44 40
45ConfigureGameList::~ConfigureGameList() {} 41ConfigureGameList::~ConfigureGameList() = default;
42
43void ConfigureGameList::applyConfiguration() {
44 UISettings::values.show_unknown = ui->show_unknown->isChecked();
45 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
46 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
47 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
48 Settings::Apply();
49}
46 50
47void ConfigureGameList::setConfiguration() { 51void ConfigureGameList::setConfiguration() {
48 ui->show_unknown->setChecked(UISettings::values.show_unknown); 52 ui->show_unknown->setChecked(UISettings::values.show_unknown);
@@ -54,10 +58,39 @@ void ConfigureGameList::setConfiguration() {
54 ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id)); 58 ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id));
55} 59}
56 60
57void ConfigureGameList::applyConfiguration() { 61void ConfigureGameList::changeEvent(QEvent* event) {
58 UISettings::values.show_unknown = ui->show_unknown->isChecked(); 62 if (event->type() == QEvent::LanguageChange) {
59 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 63 RetranslateUI();
60 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 64 return;
61 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 65 }
62 Settings::Apply(); 66
67 QWidget::changeEvent(event);
68}
69
70void ConfigureGameList::RetranslateUI() {
71 ui->retranslateUi(this);
72
73 for (int i = 0; i < ui->icon_size_combobox->count(); i++) {
74 ui->icon_size_combobox->setItemText(i, tr(default_icon_sizes[i].second));
75 }
76
77 for (int i = 0; i < ui->row_1_text_combobox->count(); i++) {
78 const QString name = tr(row_text_names[i]);
79
80 ui->row_1_text_combobox->setItemText(i, name);
81 ui->row_2_text_combobox->setItemText(i, name);
82 }
83}
84
85void ConfigureGameList::InitializeIconSizeComboBox() {
86 for (const auto& size : default_icon_sizes) {
87 ui->icon_size_combobox->addItem(size.second, size.first);
88 }
89}
90
91void ConfigureGameList::InitializeRowComboBoxes() {
92 for (std::size_t i = 0; i < row_text_names.size(); ++i) {
93 ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i));
94 ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i));
95 }
63} 96}
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
index 94fba6373..ff7406c60 100644
--- a/src/yuzu/configuration/configure_gamelist.h
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -23,6 +23,11 @@ public:
23private: 23private:
24 void setConfiguration(); 24 void setConfiguration();
25 25
26private: 26 void changeEvent(QEvent*) override;
27 void RetranslateUI();
28
29 void InitializeIconSizeComboBox();
30 void InitializeRowComboBoxes();
31
27 std::unique_ptr<Ui::ConfigureGameList> ui; 32 std::unique_ptr<Ui::ConfigureGameList> ui;
28}; 33};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index d8caee1e8..9292d9a42 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -20,7 +20,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
20 this->setConfiguration(); 20 this->setConfiguration();
21 21
22 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 22 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
23 ui->use_multi_core->setEnabled(!Core::System::GetInstance().IsPoweredOn());
24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 23 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
25} 24}
26 25
@@ -31,7 +30,6 @@ void ConfigureGeneral::setConfiguration() {
31 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 30 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
32 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
33 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
34 ui->use_multi_core->setChecked(Settings::values.use_multi_core);
35 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
36} 34}
37 35
@@ -46,6 +44,5 @@ void ConfigureGeneral::applyConfiguration() {
46 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 44 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
47 45
48 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 46 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
49 Settings::values.use_multi_core = ui->use_multi_core->isChecked();
50 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 47 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
51} 48}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 233adbe27..1775c4d40 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -58,13 +58,6 @@
58 </property> 58 </property>
59 </widget> 59 </widget>
60 </item> 60 </item>
61 <item>
62 <widget class="QCheckBox" name="use_multi_core">
63 <property name="text">
64 <string>Enable multi-core</string>
65 </property>
66 </widget>
67 </item>
68 </layout> 61 </layout>
69 </item> 62 </item>
70 </layout> 63 </layout>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index ee1287028..839d58f59 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.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 <QColorDialog>
5#include "core/core.h" 6#include "core/core.h"
6#include "core/settings.h" 7#include "core/settings.h"
7#include "ui_configure_graphics.h" 8#include "ui_configure_graphics.h"
@@ -16,6 +17,14 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
16 ui->frame_limit->setEnabled(Settings::values.use_frame_limit); 17 ui->frame_limit->setEnabled(Settings::values.use_frame_limit);
17 connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, 18 connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit,
18 &QSpinBox::setEnabled); 19 &QSpinBox::setEnabled);
20 connect(ui->bg_button, &QPushButton::clicked, this, [this] {
21 const QColor new_bg_color = QColorDialog::getColor(bg_color);
22 if (!new_bg_color.isValid())
23 return;
24 bg_color = new_bg_color;
25 ui->bg_button->setStyleSheet(
26 QString("QPushButton { background-color: %1 }").arg(bg_color.name()));
27 });
19} 28}
20 29
21ConfigureGraphics::~ConfigureGraphics() = default; 30ConfigureGraphics::~ConfigureGraphics() = default;
@@ -65,6 +74,10 @@ void ConfigureGraphics::setConfiguration() {
65 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 74 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
66 ui->frame_limit->setValue(Settings::values.frame_limit); 75 ui->frame_limit->setValue(Settings::values.frame_limit);
67 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 76 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers);
77 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
78 Settings::values.bg_blue);
79 ui->bg_button->setStyleSheet(
80 QString("QPushButton { background-color: %1 }").arg(bg_color.name()));
68} 81}
69 82
70void ConfigureGraphics::applyConfiguration() { 83void ConfigureGraphics::applyConfiguration() {
@@ -73,4 +86,7 @@ void ConfigureGraphics::applyConfiguration() {
73 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 86 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
74 Settings::values.frame_limit = ui->frame_limit->value(); 87 Settings::values.frame_limit = ui->frame_limit->value();
75 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 88 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked();
89 Settings::values.bg_red = static_cast<float>(bg_color.redF());
90 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
91 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
76} 92}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 5497a55f7..9bda26fd6 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -25,4 +25,5 @@ private:
25 25
26private: 26private:
27 std::unique_ptr<Ui::ConfigureGraphics> ui; 27 std::unique_ptr<Ui::ConfigureGraphics> ui;
28 QColor bg_color;
28}; 29};
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 3bc18c26e..8fc00af1b 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -96,6 +96,27 @@
96 </item> 96 </item>
97 </layout> 97 </layout>
98 </item> 98 </item>
99 <item>
100 <layout class="QHBoxLayout" name="horizontalLayout_6">
101 <item>
102 <widget class="QLabel" name="bg_label">
103 <property name="text">
104 <string>Background Color:</string>
105 </property>
106 </widget>
107 </item>
108 <item>
109 <widget class="QPushButton" name="bg_button">
110 <property name="maximumSize">
111 <size>
112 <width>40</width>
113 <height>16777215</height>
114 </size>
115 </property>
116 </widget>
117 </item>
118 </layout>
119 </item>
99 </layout> 120 </layout>
100 </widget> 121 </widget>
101 </item> 122 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 5e7badedf..d29abb74b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -1,4 +1,4 @@
1// Copyright 2016 Citra Emulator Project 1// Copyright 2016 Citra Emulator Project
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
@@ -53,19 +53,18 @@ static QString ButtonToText(const Common::ParamPackage& param) {
53 } else if (param.Get("engine", "") == "keyboard") { 53 } else if (param.Get("engine", "") == "keyboard") {
54 return getKeyName(param.Get("code", 0)); 54 return getKeyName(param.Get("code", 0));
55 } else if (param.Get("engine", "") == "sdl") { 55 } else if (param.Get("engine", "") == "sdl") {
56 QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str());
57 if (param.Has("hat")) { 56 if (param.Has("hat")) {
58 text += QString(QObject::tr(" Hat %1 %2")) 57 return QString(QObject::tr("Hat %1 %2"))
59 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); 58 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
60 } 59 }
61 if (param.Has("axis")) { 60 if (param.Has("axis")) {
62 text += QString(QObject::tr(" Axis %1%2")) 61 return QString(QObject::tr("Axis %1%2"))
63 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); 62 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
64 } 63 }
65 if (param.Has("button")) { 64 if (param.Has("button")) {
66 text += QString(QObject::tr(" Button %1")).arg(param.Get("button", "").c_str()); 65 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
67 } 66 }
68 return text; 67 return QString();
69 } else { 68 } else {
70 return QObject::tr("[unknown]"); 69 return QObject::tr("[unknown]");
71 } 70 }
@@ -81,13 +80,12 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
81 return QString(QObject::tr("[unused]")); 80 return QString(QObject::tr("[unused]"));
82 } 81 }
83 82
84 QString text = QString(QObject::tr("Joystick %1")).arg(param.Get("joystick", "").c_str());
85 if (dir == "left" || dir == "right") { 83 if (dir == "left" || dir == "right") {
86 text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_x", "").c_str()); 84 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
87 } else if (dir == "up" || dir == "down") { 85 } else if (dir == "up" || dir == "down") {
88 text += QString(QObject::tr(" Axis %1")).arg(param.Get("axis_y", "").c_str()); 86 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
89 } 87 }
90 return text; 88 return QString();
91 } else { 89 } else {
92 return QObject::tr("[unknown]"); 90 return QObject::tr("[unknown]");
93 } 91 }
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index fe682b3b8..b5c88f944 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -42,7 +42,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
42 tr("Finished primitive batch")}, 42 tr("Finished primitive batch")},
43 }; 43 };
44 44
45 DEBUG_ASSERT(map.size() == static_cast<size_t>(Tegra::DebugContext::Event::NumEvents)); 45 DEBUG_ASSERT(map.size() ==
46 static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents));
46 return (map.find(event) != map.end()) ? map.at(event) : QString(); 47 return (map.find(event) != map.end()) ? map.at(event) : QString();
47 } 48 }
48 49
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 7e37962d5..cbcd5dd5f 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -341,8 +341,8 @@ void GraphicsSurfaceWidget::OnUpdate() {
341 // directly... 341 // directly...
342 342
343 const auto& registers = gpu.Maxwell3D().regs; 343 const auto& registers = gpu.Maxwell3D().regs;
344 const auto& rt = registers.rt[static_cast<size_t>(surface_source) - 344 const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) -
345 static_cast<size_t>(Source::RenderTarget0)]; 345 static_cast<std::size_t>(Source::RenderTarget0)];
346 346
347 surface_address = rt.Address(); 347 surface_address = rt.Address();
348 surface_width = rt.width; 348 surface_width = rt.width;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 6c2cd967e..a3b1fd357 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
16#include "core/hle/kernel/timer.h" 16#include "core/hle/kernel/timer.h"
17#include "core/hle/kernel/wait_object.h" 17#include "core/hle/kernel/wait_object.h"
18#include "core/memory.h"
18 19
19WaitTreeItem::WaitTreeItem() = default; 20WaitTreeItem::WaitTreeItem() = default;
20WaitTreeItem::~WaitTreeItem() = default; 21WaitTreeItem::~WaitTreeItem() = default;
@@ -117,7 +118,7 @@ QString WaitTreeCallstack::GetText() const {
117std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { 118std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
118 std::vector<std::unique_ptr<WaitTreeItem>> list; 119 std::vector<std::unique_ptr<WaitTreeItem>> list;
119 120
120 constexpr size_t BaseRegister = 29; 121 constexpr std::size_t BaseRegister = 29;
121 u64 base_pointer = thread.context.cpu_registers[BaseRegister]; 122 u64 base_pointer = thread.context.cpu_registers[BaseRegister];
122 123
123 while (base_pointer != 0) { 124 while (base_pointer != 0) {
@@ -213,35 +214,35 @@ QString WaitTreeThread::GetText() const {
213 const auto& thread = static_cast<const Kernel::Thread&>(object); 214 const auto& thread = static_cast<const Kernel::Thread&>(object);
214 QString status; 215 QString status;
215 switch (thread.status) { 216 switch (thread.status) {
216 case ThreadStatus::Running: 217 case Kernel::ThreadStatus::Running:
217 status = tr("running"); 218 status = tr("running");
218 break; 219 break;
219 case ThreadStatus::Ready: 220 case Kernel::ThreadStatus::Ready:
220 status = tr("ready"); 221 status = tr("ready");
221 break; 222 break;
222 case ThreadStatus::WaitHLEEvent: 223 case Kernel::ThreadStatus::WaitHLEEvent:
223 status = tr("waiting for HLE return"); 224 status = tr("waiting for HLE return");
224 break; 225 break;
225 case ThreadStatus::WaitSleep: 226 case Kernel::ThreadStatus::WaitSleep:
226 status = tr("sleeping"); 227 status = tr("sleeping");
227 break; 228 break;
228 case ThreadStatus::WaitIPC: 229 case Kernel::ThreadStatus::WaitIPC:
229 status = tr("waiting for IPC reply"); 230 status = tr("waiting for IPC reply");
230 break; 231 break;
231 case ThreadStatus::WaitSynchAll: 232 case Kernel::ThreadStatus::WaitSynchAll:
232 case ThreadStatus::WaitSynchAny: 233 case Kernel::ThreadStatus::WaitSynchAny:
233 status = tr("waiting for objects"); 234 status = tr("waiting for objects");
234 break; 235 break;
235 case ThreadStatus::WaitMutex: 236 case Kernel::ThreadStatus::WaitMutex:
236 status = tr("waiting for mutex"); 237 status = tr("waiting for mutex");
237 break; 238 break;
238 case ThreadStatus::WaitArb: 239 case Kernel::ThreadStatus::WaitArb:
239 status = tr("waiting for address arbiter"); 240 status = tr("waiting for address arbiter");
240 break; 241 break;
241 case ThreadStatus::Dormant: 242 case Kernel::ThreadStatus::Dormant:
242 status = tr("dormant"); 243 status = tr("dormant");
243 break; 244 break;
244 case ThreadStatus::Dead: 245 case Kernel::ThreadStatus::Dead:
245 status = tr("dead"); 246 status = tr("dead");
246 break; 247 break;
247 } 248 }
@@ -254,23 +255,23 @@ QString WaitTreeThread::GetText() const {
254QColor WaitTreeThread::GetColor() const { 255QColor WaitTreeThread::GetColor() const {
255 const auto& thread = static_cast<const Kernel::Thread&>(object); 256 const auto& thread = static_cast<const Kernel::Thread&>(object);
256 switch (thread.status) { 257 switch (thread.status) {
257 case ThreadStatus::Running: 258 case Kernel::ThreadStatus::Running:
258 return QColor(Qt::GlobalColor::darkGreen); 259 return QColor(Qt::GlobalColor::darkGreen);
259 case ThreadStatus::Ready: 260 case Kernel::ThreadStatus::Ready:
260 return QColor(Qt::GlobalColor::darkBlue); 261 return QColor(Qt::GlobalColor::darkBlue);
261 case ThreadStatus::WaitHLEEvent: 262 case Kernel::ThreadStatus::WaitHLEEvent:
262 case ThreadStatus::WaitIPC: 263 case Kernel::ThreadStatus::WaitIPC:
263 return QColor(Qt::GlobalColor::darkRed); 264 return QColor(Qt::GlobalColor::darkRed);
264 case ThreadStatus::WaitSleep: 265 case Kernel::ThreadStatus::WaitSleep:
265 return QColor(Qt::GlobalColor::darkYellow); 266 return QColor(Qt::GlobalColor::darkYellow);
266 case ThreadStatus::WaitSynchAll: 267 case Kernel::ThreadStatus::WaitSynchAll:
267 case ThreadStatus::WaitSynchAny: 268 case Kernel::ThreadStatus::WaitSynchAny:
268 case ThreadStatus::WaitMutex: 269 case Kernel::ThreadStatus::WaitMutex:
269 case ThreadStatus::WaitArb: 270 case Kernel::ThreadStatus::WaitArb:
270 return QColor(Qt::GlobalColor::red); 271 return QColor(Qt::GlobalColor::red);
271 case ThreadStatus::Dormant: 272 case Kernel::ThreadStatus::Dormant:
272 return QColor(Qt::GlobalColor::darkCyan); 273 return QColor(Qt::GlobalColor::darkCyan);
273 case ThreadStatus::Dead: 274 case Kernel::ThreadStatus::Dead:
274 return QColor(Qt::GlobalColor::gray); 275 return QColor(Qt::GlobalColor::gray);
275 default: 276 default:
276 return WaitTreeItem::GetColor(); 277 return WaitTreeItem::GetColor();
@@ -284,13 +285,13 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
284 285
285 QString processor; 286 QString processor;
286 switch (thread.processor_id) { 287 switch (thread.processor_id) {
287 case ThreadProcessorId::THREADPROCESSORID_DEFAULT: 288 case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT:
288 processor = tr("default"); 289 processor = tr("default");
289 break; 290 break;
290 case ThreadProcessorId::THREADPROCESSORID_0: 291 case Kernel::ThreadProcessorId::THREADPROCESSORID_0:
291 case ThreadProcessorId::THREADPROCESSORID_1: 292 case Kernel::ThreadProcessorId::THREADPROCESSORID_1:
292 case ThreadProcessorId::THREADPROCESSORID_2: 293 case Kernel::ThreadProcessorId::THREADPROCESSORID_2:
293 case ThreadProcessorId::THREADPROCESSORID_3: 294 case Kernel::ThreadProcessorId::THREADPROCESSORID_3:
294 processor = tr("core %1").arg(thread.processor_id); 295 processor = tr("core %1").arg(thread.processor_id);
295 break; 296 break;
296 default: 297 default:
@@ -314,8 +315,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
314 else 315 else
315 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); 316 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
316 317
317 if (thread.status == ThreadStatus::WaitSynchAny || 318 if (thread.status == Kernel::ThreadStatus::WaitSynchAny ||
318 thread.status == ThreadStatus::WaitSynchAll) { 319 thread.status == Kernel::ThreadStatus::WaitSynchAll) {
319 list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, 320 list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
320 thread.IsSleepingOnWaitAll())); 321 thread.IsSleepingOnWaitAll()));
321 } 322 }
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index d15242d59..e8b2f720a 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -13,20 +13,16 @@
13#include <QKeyEvent> 13#include <QKeyEvent>
14#include <QMenu> 14#include <QMenu>
15#include <QThreadPool> 15#include <QThreadPool>
16#include <boost/container/flat_map.hpp>
17#include <fmt/format.h> 16#include <fmt/format.h>
18#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/common_types.h"
19#include "common/file_util.h"
19#include "common/logging/log.h" 20#include "common/logging/log.h"
20#include "common/string_util.h" 21#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/content_archive.h" 22#include "yuzu/compatibility_list.h"
22#include "core/file_sys/control_metadata.h"
23#include "core/file_sys/registered_cache.h"
24#include "core/file_sys/romfs.h"
25#include "core/file_sys/vfs_real.h"
26#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h"
28#include "yuzu/game_list.h" 23#include "yuzu/game_list.h"
29#include "yuzu/game_list_p.h" 24#include "yuzu/game_list_p.h"
25#include "yuzu/game_list_worker.h"
30#include "yuzu/main.h" 26#include "yuzu/main.h"
31#include "yuzu/ui_settings.h" 27#include "yuzu/ui_settings.h"
32 28
@@ -93,15 +89,7 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e
93} 89}
94 90
95void GameList::SearchField::setFilterResult(int visible, int total) { 91void GameList::SearchField::setFilterResult(int visible, int total) {
96 QString result_of_text = tr("of"); 92 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
97 QString result_text;
98 if (total == 1) {
99 result_text = tr("result");
100 } else {
101 result_text = tr("results");
102 }
103 label_filter_result->setText(
104 QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text));
105} 93}
106 94
107void GameList::SearchField::clear() { 95void GameList::SearchField::clear() {
@@ -231,6 +219,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
231 item_model->insertColumns(0, COLUMN_COUNT); 219 item_model->insertColumns(0, COLUMN_COUNT);
232 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 220 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
233 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); 221 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
222 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons");
234 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); 223 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
235 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); 224 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
236 225
@@ -369,7 +358,7 @@ void GameList::LoadCompatibilityList() {
369 QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); 358 QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8());
370 QJsonArray arr = json.array(); 359 QJsonArray arr = json.array();
371 360
372 for (const QJsonValue& value : arr) { 361 for (const QJsonValueRef& value : arr) {
373 QJsonObject game = value.toObject(); 362 QJsonObject game = value.toObject();
374 363
375 if (game.contains("compatibility") && game["compatibility"].isDouble()) { 364 if (game.contains("compatibility") && game["compatibility"].isDouble()) {
@@ -377,9 +366,9 @@ void GameList::LoadCompatibilityList() {
377 QString directory = game["directory"].toString(); 366 QString directory = game["directory"].toString();
378 QJsonArray ids = game["releases"].toArray(); 367 QJsonArray ids = game["releases"].toArray();
379 368
380 for (const QJsonValue& value : ids) { 369 for (const QJsonValueRef& id_ref : ids) {
381 QJsonObject object = value.toObject(); 370 QJsonObject id_object = id_ref.toObject();
382 QString id = object["id"].toString(); 371 QString id = id_object["id"].toString();
383 compatibility_list.emplace( 372 compatibility_list.emplace(
384 id.toUpper().toStdString(), 373 id.toUpper().toStdString(),
385 std::make_pair(QString::number(compatibility), directory)); 374 std::make_pair(QString::number(compatibility), directory));
@@ -431,27 +420,7 @@ void GameList::LoadInterfaceLayout() {
431 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 420 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
432} 421}
433 422
434const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; 423const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
435
436static bool HasSupportedFileExtension(const std::string& file_name) {
437 const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
438 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
439}
440
441static bool IsExtractedNCAMain(const std::string& file_name) {
442 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
443}
444
445static QString FormatGameName(const std::string& physical_name) {
446 const QString physical_name_as_qstring = QString::fromStdString(physical_name);
447 const QFileInfo file_info(physical_name_as_qstring);
448
449 if (IsExtractedNCAMain(physical_name)) {
450 return file_info.dir().path();
451 }
452
453 return physical_name_as_qstring;
454}
455 424
456void GameList::RefreshGameDirectory() { 425void GameList::RefreshGameDirectory() {
457 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { 426 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
@@ -460,175 +429,3 @@ void GameList::RefreshGameDirectory() {
460 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 429 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
461 } 430 }
462} 431}
463
464static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca,
465 std::vector<u8>& icon, std::string& name) {
466 const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
467 if (control_dir == nullptr)
468 return;
469
470 const auto nacp_file = control_dir->GetFile("control.nacp");
471 if (nacp_file == nullptr)
472 return;
473 FileSys::NACP nacp(nacp_file);
474 name = nacp.GetApplicationName();
475
476 FileSys::VirtualFile icon_file = nullptr;
477 for (const auto& language : FileSys::LANGUAGE_NAMES) {
478 icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat");
479 if (icon_file != nullptr) {
480 icon = icon_file->ReadAllBytes();
481 break;
482 }
483 }
484}
485
486GameListWorker::GameListWorker(
487 FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
488 const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
489 : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
490 compatibility_list(compatibility_list) {}
491
492GameListWorker::~GameListWorker() = default;
493
494void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache) {
495 const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application,
496 FileSys::ContentRecordType::Program);
497
498 for (const auto& game : installed_games) {
499 const auto& file = cache->GetEntryUnparsed(game);
500 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
501 if (!loader)
502 continue;
503
504 std::vector<u8> icon;
505 std::string name;
506 u64 program_id = 0;
507 loader->ReadProgramId(program_id);
508
509 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
510 if (control != nullptr)
511 GetMetadataFromControlNCA(control, icon, name);
512 emit EntryReady({
513 new GameListItemPath(
514 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
515 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
516 program_id),
517 new GameListItem(
518 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
519 new GameListItemSize(file->GetSize()),
520 });
521 }
522
523 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
524 FileSys::ContentRecordType::Control);
525
526 for (const auto& entry : control_data) {
527 const auto nca = cache->GetEntry(entry);
528 if (nca != nullptr)
529 nca_control_map.insert_or_assign(entry.title_id, nca);
530 }
531}
532
533void GameListWorker::FillControlMap(const std::string& dir_path) {
534 const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
535 const std::string& virtual_name) -> bool {
536 std::string physical_name = directory + DIR_SEP + virtual_name;
537
538 if (stop_processing)
539 return false; // Breaks the callback loop.
540
541 bool is_dir = FileUtil::IsDirectory(physical_name);
542 QFileInfo file_info(physical_name.c_str());
543 if (!is_dir && file_info.suffix().toStdString() == "nca") {
544 auto nca =
545 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
546 if (nca->GetType() == FileSys::NCAContentType::Control)
547 nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
548 }
549 return true;
550 };
551
552 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
553}
554
555void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
556 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
557 const std::string& virtual_name) -> bool {
558 std::string physical_name = directory + DIR_SEP + virtual_name;
559
560 if (stop_processing)
561 return false; // Breaks the callback loop.
562
563 bool is_dir = FileUtil::IsDirectory(physical_name);
564 if (!is_dir &&
565 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
566 std::unique_ptr<Loader::AppLoader> loader =
567 Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
568 if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
569 loader->GetFileType() == Loader::FileType::Error) &&
570 !UISettings::values.show_unknown))
571 return true;
572
573 std::vector<u8> icon;
574 const auto res1 = loader->ReadIcon(icon);
575
576 u64 program_id = 0;
577 const auto res2 = loader->ReadProgramId(program_id);
578
579 std::string name = " ";
580 const auto res3 = loader->ReadTitle(name);
581
582 if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
583 res2 == Loader::ResultStatus::Success) {
584 // Use from metadata pool.
585 if (nca_control_map.find(program_id) != nca_control_map.end()) {
586 const auto nca = nca_control_map[program_id];
587 GetMetadataFromControlNCA(nca, icon, name);
588 }
589 }
590
591 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
592
593 // The game list uses this as compatibility number for untested games
594 QString compatibility("99");
595 if (it != compatibility_list.end())
596 compatibility = it->second.first;
597
598 emit EntryReady({
599 new GameListItemPath(
600 FormatGameName(physical_name), icon, QString::fromStdString(name),
601 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
602 program_id),
603 new GameListItemCompat(compatibility),
604 new GameListItem(
605 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
606 new GameListItemSize(FileUtil::GetSize(physical_name)),
607 });
608 } else if (is_dir && recursion > 0) {
609 watch_list.append(QString::fromStdString(physical_name));
610 AddFstEntriesToGameList(physical_name, recursion - 1);
611 }
612
613 return true;
614 };
615
616 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
617}
618
619void GameListWorker::run() {
620 stop_processing = false;
621 watch_list.append(dir_path);
622 FillControlMap(dir_path.toStdString());
623 AddInstalledTitlesToGameList(Service::FileSystem::GetUserNANDContents());
624 AddInstalledTitlesToGameList(Service::FileSystem::GetSystemNANDContents());
625 AddInstalledTitlesToGameList(Service::FileSystem::GetSDMCContents());
626 AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
627 nca_control_map.clear();
628 emit Finished(watch_list);
629}
630
631void GameListWorker::Cancel() {
632 this->disconnect();
633 stop_processing = true;
634}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 6a5c2f5f8..2713e7b54 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <unordered_map>
8
9#include <QFileSystemWatcher> 7#include <QFileSystemWatcher>
10#include <QHBoxLayout> 8#include <QHBoxLayout>
11#include <QLabel> 9#include <QLabel>
@@ -20,6 +18,9 @@
20#include <QVBoxLayout> 18#include <QVBoxLayout>
21#include <QWidget> 19#include <QWidget>
22 20
21#include "common/common_types.h"
22#include "yuzu/compatibility_list.h"
23
23class GameListWorker; 24class GameListWorker;
24class GMainWindow; 25class GMainWindow;
25 26
@@ -36,6 +37,7 @@ public:
36 enum { 37 enum {
37 COLUMN_NAME, 38 COLUMN_NAME,
38 COLUMN_COMPATIBILITY, 39 COLUMN_COMPATIBILITY,
40 COLUMN_ADD_ONS,
39 COLUMN_FILE_TYPE, 41 COLUMN_FILE_TYPE,
40 COLUMN_SIZE, 42 COLUMN_SIZE,
41 COLUMN_COUNT, // Number of columns 43 COLUMN_COUNT, // Number of columns
@@ -87,9 +89,8 @@ signals:
87 void GameChosen(QString game_path); 89 void GameChosen(QString game_path);
88 void ShouldCancelWorker(); 90 void ShouldCancelWorker();
89 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 91 void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
90 void NavigateToGamedbEntryRequested( 92 void NavigateToGamedbEntryRequested(u64 program_id,
91 u64 program_id, 93 const CompatibilityList& compatibility_list);
92 std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
93 94
94private slots: 95private slots:
95 void onTextChanged(const QString& newText); 96 void onTextChanged(const QString& newText);
@@ -111,7 +112,7 @@ private:
111 QStandardItemModel* item_model = nullptr; 112 QStandardItemModel* item_model = nullptr;
112 GameListWorker* current_worker = nullptr; 113 GameListWorker* current_worker = nullptr;
113 QFileSystemWatcher* watcher = nullptr; 114 QFileSystemWatcher* watcher = nullptr;
114 std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; 115 CompatibilityList compatibility_list;
115}; 116};
116 117
117Q_DECLARE_METATYPE(GameListOpenTarget); 118Q_DECLARE_METATYPE(GameListOpenTarget);
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 3624cb21a..b6272d536 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -4,29 +4,25 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <array> 8#include <array>
8#include <atomic>
9#include <map> 9#include <map>
10#include <memory> 10#include <string>
11#include <unordered_map> 11#include <unordered_map>
12#include <utility> 12#include <utility>
13
13#include <QCoreApplication> 14#include <QCoreApplication>
14#include <QImage> 15#include <QImage>
15#include <QObject> 16#include <QObject>
16#include <QRunnable>
17#include <QStandardItem> 17#include <QStandardItem>
18#include <QString> 18#include <QString>
19
20#include "common/common_types.h"
19#include "common/logging/log.h" 21#include "common/logging/log.h"
20#include "common/string_util.h" 22#include "common/string_util.h"
21#include "yuzu/ui_settings.h" 23#include "yuzu/ui_settings.h"
22#include "yuzu/util/util.h" 24#include "yuzu/util/util.h"
23 25
24namespace FileSys {
25class NCA;
26class RegisteredCache;
27class VfsFilesystem;
28} // namespace FileSys
29
30/** 26/**
31 * Gets the default icon (for games without valid SMDH) 27 * Gets the default icon (for games without valid SMDH)
32 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) 28 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
@@ -38,17 +34,6 @@ static QPixmap GetDefaultIcon(u32 size) {
38 return icon; 34 return icon;
39} 35}
40 36
41static auto FindMatchingCompatibilityEntry(
42 const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
43 u64 program_id) {
44 return std::find_if(
45 compatibility_list.begin(), compatibility_list.end(),
46 [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
47 std::string pid = fmt::format("{:016X}", program_id);
48 return element.first == pid;
49 });
50}
51
52class GameListItem : public QStandardItem { 37class GameListItem : public QStandardItem {
53 38
54public: 39public:
@@ -121,7 +106,7 @@ class GameListItemCompat : public GameListItem {
121public: 106public:
122 static const int CompatNumberRole = Qt::UserRole + 1; 107 static const int CompatNumberRole = Qt::UserRole + 1;
123 GameListItemCompat() = default; 108 GameListItemCompat() = default;
124 explicit GameListItemCompat(const QString& compatiblity) { 109 explicit GameListItemCompat(const QString& compatibility) {
125 struct CompatStatus { 110 struct CompatStatus {
126 QString color; 111 QString color;
127 const char* text; 112 const char* text;
@@ -138,13 +123,13 @@ public:
138 {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; 123 {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
139 // clang-format on 124 // clang-format on
140 125
141 auto iterator = status_data.find(compatiblity); 126 auto iterator = status_data.find(compatibility);
142 if (iterator == status_data.end()) { 127 if (iterator == status_data.end()) {
143 LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); 128 LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString());
144 return; 129 return;
145 } 130 }
146 CompatStatus status = iterator->second; 131 const CompatStatus& status = iterator->second;
147 setData(compatiblity, CompatNumberRole); 132 setData(compatibility, CompatNumberRole);
148 setText(QObject::tr(status.text)); 133 setText(QObject::tr(status.text));
149 setToolTip(QObject::tr(status.tooltip)); 134 setToolTip(QObject::tr(status.tooltip));
150 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); 135 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
@@ -191,50 +176,3 @@ public:
191 return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); 176 return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
192 } 177 }
193}; 178};
194
195/**
196 * Asynchronous worker object for populating the game list.
197 * Communicates with other threads through Qt's signal/slot system.
198 */
199class GameListWorker : public QObject, public QRunnable {
200 Q_OBJECT
201
202public:
203 GameListWorker(
204 std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan,
205 const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
206 ~GameListWorker() override;
207
208public slots:
209 /// Starts the processing of directory tree information.
210 void run() override;
211 /// Tells the worker that it should no longer continue processing. Thread-safe.
212 void Cancel();
213
214signals:
215 /**
216 * The `EntryReady` signal is emitted once an entry has been prepared and is ready
217 * to be added to the game list.
218 * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
219 */
220 void EntryReady(QList<QStandardItem*> entry_items);
221
222 /**
223 * After the worker has traversed the game directory looking for entries, this signal is emmited
224 * with a list of folders that should be watched for changes as well.
225 */
226 void Finished(QStringList watch_list);
227
228private:
229 std::shared_ptr<FileSys::VfsFilesystem> vfs;
230 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
231 QStringList watch_list;
232 QString dir_path;
233 bool deep_scan;
234 const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
235 std::atomic_bool stop_processing;
236
237 void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache);
238 void FillControlMap(const std::string& dir_path);
239 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
240};
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
new file mode 100644
index 000000000..e228d61bd
--- /dev/null
+++ b/src/yuzu/game_list_worker.cpp
@@ -0,0 +1,239 @@
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#include <string>
7#include <utility>
8#include <vector>
9
10#include <QDir>
11#include <QFileInfo>
12
13#include "common/common_paths.h"
14#include "common/file_util.h"
15#include "core/file_sys/content_archive.h"
16#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/mode.h"
18#include "core/file_sys/nca_metadata.h"
19#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/registered_cache.h"
21#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/loader.h"
23#include "yuzu/compatibility_list.h"
24#include "yuzu/game_list.h"
25#include "yuzu/game_list_p.h"
26#include "yuzu/game_list_worker.h"
27#include "yuzu/ui_settings.h"
28
29namespace {
30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager,
31 const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon,
32 std::string& name) {
33 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
34 if (icon_file != nullptr)
35 icon = icon_file->ReadAllBytes();
36 if (nacp != nullptr)
37 name = nacp->GetApplicationName();
38}
39
40bool HasSupportedFileExtension(const std::string& file_name) {
41 const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
42 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
43}
44
45bool IsExtractedNCAMain(const std::string& file_name) {
46 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
47}
48
49QString FormatGameName(const std::string& physical_name) {
50 const QString physical_name_as_qstring = QString::fromStdString(physical_name);
51 const QFileInfo file_info(physical_name_as_qstring);
52
53 if (IsExtractedNCAMain(physical_name)) {
54 return file_info.dir().path();
55 }
56
57 return physical_name_as_qstring;
58}
59
60QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) {
61 QString out;
62 for (const auto& kv : patch_manager.GetPatchVersionNames()) {
63 if (!updatable && kv.first == FileSys::PatchType::Update)
64 continue;
65
66 if (kv.second.empty()) {
67 out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str());
68 } else {
69 out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second)
70 .c_str());
71 }
72 }
73
74 out.chop(1);
75 return out;
76}
77} // Anonymous namespace
78
79GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
80 const CompatibilityList& compatibility_list)
81 : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
82 compatibility_list(compatibility_list) {}
83
84GameListWorker::~GameListWorker() = default;
85
86void GameListWorker::AddInstalledTitlesToGameList() {
87 const auto cache = Service::FileSystem::GetUnionContents();
88 const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application,
89 FileSys::ContentRecordType::Program);
90
91 for (const auto& game : installed_games) {
92 const auto& file = cache->GetEntryUnparsed(game);
93 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
94 if (!loader)
95 continue;
96
97 std::vector<u8> icon;
98 std::string name;
99 u64 program_id = 0;
100 loader->ReadProgramId(program_id);
101
102 const FileSys::PatchManager patch{program_id};
103 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
104 if (control != nullptr)
105 GetMetadataFromControlNCA(patch, control, icon, name);
106
107 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
108
109 // The game list uses this as compatibility number for untested games
110 QString compatibility("99");
111 if (it != compatibility_list.end())
112 compatibility = it->second.first;
113
114 emit EntryReady({
115 new GameListItemPath(
116 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
117 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
118 program_id),
119 new GameListItemCompat(compatibility),
120 new GameListItem(FormatPatchNameVersions(patch)),
121 new GameListItem(
122 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
123 new GameListItemSize(file->GetSize()),
124 });
125 }
126
127 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
128 FileSys::ContentRecordType::Control);
129
130 for (const auto& entry : control_data) {
131 const auto nca = cache->GetEntry(entry);
132 if (nca != nullptr)
133 nca_control_map.insert_or_assign(entry.title_id, nca);
134 }
135}
136
137void GameListWorker::FillControlMap(const std::string& dir_path) {
138 const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
139 const std::string& virtual_name) -> bool {
140 std::string physical_name = directory + DIR_SEP + virtual_name;
141
142 if (stop_processing)
143 return false; // Breaks the callback loop.
144
145 bool is_dir = FileUtil::IsDirectory(physical_name);
146 QFileInfo file_info(physical_name.c_str());
147 if (!is_dir && file_info.suffix().toStdString() == "nca") {
148 auto nca =
149 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
150 if (nca->GetType() == FileSys::NCAContentType::Control)
151 nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
152 }
153 return true;
154 };
155
156 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
157}
158
159void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
160 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
161 const std::string& virtual_name) -> bool {
162 std::string physical_name = directory + DIR_SEP + virtual_name;
163
164 if (stop_processing)
165 return false; // Breaks the callback loop.
166
167 bool is_dir = FileUtil::IsDirectory(physical_name);
168 if (!is_dir &&
169 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
170 std::unique_ptr<Loader::AppLoader> loader =
171 Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
172 if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
173 loader->GetFileType() == Loader::FileType::Error) &&
174 !UISettings::values.show_unknown))
175 return true;
176
177 std::vector<u8> icon;
178 const auto res1 = loader->ReadIcon(icon);
179
180 u64 program_id = 0;
181 const auto res2 = loader->ReadProgramId(program_id);
182
183 std::string name = " ";
184 const auto res3 = loader->ReadTitle(name);
185
186 const FileSys::PatchManager patch{program_id};
187
188 if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
189 res2 == Loader::ResultStatus::Success) {
190 // Use from metadata pool.
191 if (nca_control_map.find(program_id) != nca_control_map.end()) {
192 const auto nca = nca_control_map[program_id];
193 GetMetadataFromControlNCA(patch, nca, icon, name);
194 }
195 }
196
197 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
198
199 // The game list uses this as compatibility number for untested games
200 QString compatibility("99");
201 if (it != compatibility_list.end())
202 compatibility = it->second.first;
203
204 emit EntryReady({
205 new GameListItemPath(
206 FormatGameName(physical_name), icon, QString::fromStdString(name),
207 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
208 program_id),
209 new GameListItemCompat(compatibility),
210 new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())),
211 new GameListItem(
212 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
213 new GameListItemSize(FileUtil::GetSize(physical_name)),
214 });
215 } else if (is_dir && recursion > 0) {
216 watch_list.append(QString::fromStdString(physical_name));
217 AddFstEntriesToGameList(physical_name, recursion - 1);
218 }
219
220 return true;
221 };
222
223 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
224}
225
226void GameListWorker::run() {
227 stop_processing = false;
228 watch_list.append(dir_path);
229 FillControlMap(dir_path.toStdString());
230 AddInstalledTitlesToGameList();
231 AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
232 nca_control_map.clear();
233 emit Finished(watch_list);
234}
235
236void GameListWorker::Cancel() {
237 this->disconnect();
238 stop_processing = true;
239}
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
new file mode 100644
index 000000000..09d20c42f
--- /dev/null
+++ b/src/yuzu/game_list_worker.h
@@ -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#pragma once
6
7#include <atomic>
8#include <map>
9#include <memory>
10#include <string>
11#include <unordered_map>
12
13#include <QList>
14#include <QObject>
15#include <QRunnable>
16#include <QString>
17
18#include "common/common_types.h"
19#include "yuzu/compatibility_list.h"
20
21class QStandardItem;
22
23namespace FileSys {
24class NCA;
25class VfsFilesystem;
26} // namespace FileSys
27
28/**
29 * Asynchronous worker object for populating the game list.
30 * Communicates with other threads through Qt's signal/slot system.
31 */
32class GameListWorker : public QObject, public QRunnable {
33 Q_OBJECT
34
35public:
36 GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan,
37 const CompatibilityList& compatibility_list);
38 ~GameListWorker() override;
39
40 /// Starts the processing of directory tree information.
41 void run() override;
42
43 /// Tells the worker that it should no longer continue processing. Thread-safe.
44 void Cancel();
45
46signals:
47 /**
48 * The `EntryReady` signal is emitted once an entry has been prepared and is ready
49 * to be added to the game list.
50 * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
51 */
52 void EntryReady(QList<QStandardItem*> entry_items);
53
54 /**
55 * After the worker has traversed the game directory looking for entries, this signal is emitted
56 * with a list of folders that should be watched for changes as well.
57 */
58 void Finished(QStringList watch_list);
59
60private:
61 void AddInstalledTitlesToGameList();
62 void FillControlMap(const std::string& dir_path);
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list;
68 QString dir_path;
69 bool deep_scan;
70 const CompatibilityList& compatibility_list;
71 std::atomic_bool stop_processing;
72};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e11ba7854..45bb1d1d1 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -12,12 +12,14 @@
12 12
13#define QT_NO_OPENGL 13#define QT_NO_OPENGL
14#include <QDesktopWidget> 14#include <QDesktopWidget>
15#include <QDialogButtonBox>
15#include <QFileDialog> 16#include <QFileDialog>
16#include <QMessageBox> 17#include <QMessageBox>
17#include <QtGui> 18#include <QtGui>
18#include <QtWidgets> 19#include <QtWidgets>
19#include <fmt/format.h> 20#include <fmt/format.h>
20#include "common/common_paths.h" 21#include "common/common_paths.h"
22#include "common/file_util.h"
21#include "common/logging/backend.h" 23#include "common/logging/backend.h"
22#include "common/logging/filter.h" 24#include "common/logging/filter.h"
23#include "common/logging/log.h" 25#include "common/logging/log.h"
@@ -29,9 +31,14 @@
29#include "core/core.h" 31#include "core/core.h"
30#include "core/crypto/key_manager.h" 32#include "core/crypto/key_manager.h"
31#include "core/file_sys/card_image.h" 33#include "core/file_sys/card_image.h"
34#include "core/file_sys/content_archive.h"
35#include "core/file_sys/control_metadata.h"
36#include "core/file_sys/patch_manager.h"
32#include "core/file_sys/registered_cache.h" 37#include "core/file_sys/registered_cache.h"
33#include "core/file_sys/savedata_factory.h" 38#include "core/file_sys/savedata_factory.h"
39#include "core/file_sys/submission_package.h"
34#include "core/file_sys/vfs_real.h" 40#include "core/file_sys/vfs_real.h"
41#include "core/hle/kernel/process.h"
35#include "core/hle/service/filesystem/filesystem.h" 42#include "core/hle/service/filesystem/filesystem.h"
36#include "core/loader/loader.h" 43#include "core/loader/loader.h"
37#include "core/perf_stats.h" 44#include "core/perf_stats.h"
@@ -40,6 +47,7 @@
40#include "video_core/debug_utils/debug_utils.h" 47#include "video_core/debug_utils/debug_utils.h"
41#include "yuzu/about_dialog.h" 48#include "yuzu/about_dialog.h"
42#include "yuzu/bootmanager.h" 49#include "yuzu/bootmanager.h"
50#include "yuzu/compatibility_list.h"
43#include "yuzu/configuration/config.h" 51#include "yuzu/configuration/config.h"
44#include "yuzu/configuration/configure_dialog.h" 52#include "yuzu/configuration/configure_dialog.h"
45#include "yuzu/debugger/console.h" 53#include "yuzu/debugger/console.h"
@@ -73,6 +81,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
73 */ 81 */
74enum class CalloutFlag : uint32_t { 82enum class CalloutFlag : uint32_t {
75 Telemetry = 0x1, 83 Telemetry = 0x1,
84 DRDDeprecation = 0x2,
76}; 85};
77 86
78static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { 87static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
@@ -128,11 +137,11 @@ GMainWindow::GMainWindow()
128 137
129 ConnectMenuEvents(); 138 ConnectMenuEvents();
130 ConnectWidgetEvents(); 139 ConnectWidgetEvents();
131 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_name, Common::g_scm_branch, 140 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
132 Common::g_scm_desc); 141 Common::g_scm_desc);
133 142
134 setWindowTitle(QString("yuzu %1| %2-%3") 143 setWindowTitle(QString("yuzu %1| %2-%3")
135 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 144 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
136 show(); 145 show();
137 146
138 // Necessary to load titles from nand in gamelist. 147 // Necessary to load titles from nand in gamelist.
@@ -372,6 +381,10 @@ void GMainWindow::ConnectMenuEvents() {
372 &GMainWindow::OnMenuInstallToNAND); 381 &GMainWindow::OnMenuInstallToNAND);
373 connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, 382 connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
374 &GMainWindow::OnMenuSelectGameListRoot); 383 &GMainWindow::OnMenuSelectGameListRoot);
384 connect(ui.action_Select_NAND_Directory, &QAction::triggered, this,
385 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); });
386 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
387 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
375 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 388 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
376 389
377 // Emulation 390 // Emulation
@@ -419,7 +432,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
419 } 432 }
420} 433}
421 434
422bool GMainWindow::SupportsRequiredGLExtensions() { 435QStringList GMainWindow::GetUnsupportedGLExtensions() {
423 QStringList unsupported_ext; 436 QStringList unsupported_ext;
424 437
425 if (!GLAD_GL_ARB_program_interface_query) 438 if (!GLAD_GL_ARB_program_interface_query)
@@ -432,6 +445,12 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
432 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); 445 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
433 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 446 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
434 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); 447 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
448 if (!GLAD_GL_ARB_base_instance)
449 unsupported_ext.append("ARB_base_instance");
450 if (!GLAD_GL_ARB_texture_storage)
451 unsupported_ext.append("ARB_texture_storage");
452 if (!GLAD_GL_ARB_multi_bind)
453 unsupported_ext.append("ARB_multi_bind");
435 454
436 // Extensions required to support some texture formats. 455 // Extensions required to support some texture formats.
437 if (!GLAD_GL_EXT_texture_compression_s3tc) 456 if (!GLAD_GL_EXT_texture_compression_s3tc)
@@ -446,7 +465,7 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
446 for (const QString& ext : unsupported_ext) 465 for (const QString& ext : unsupported_ext)
447 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); 466 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
448 467
449 return unsupported_ext.empty(); 468 return unsupported_ext;
450} 469}
451 470
452bool GMainWindow::LoadROM(const QString& filename) { 471bool GMainWindow::LoadROM(const QString& filename) {
@@ -464,11 +483,13 @@ bool GMainWindow::LoadROM(const QString& filename) {
464 return false; 483 return false;
465 } 484 }
466 485
467 if (!SupportsRequiredGLExtensions()) { 486 QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
468 QMessageBox::critical( 487 if (!unsupported_gl_extensions.empty()) {
469 this, tr("Error while initializing OpenGL Core!"), 488 QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"),
470 tr("Your GPU may not support one or more required OpenGL extensions. Please " 489 tr("Your GPU may not support one or more required OpenGL"
471 "ensure you have the latest graphics driver. See the log for more details.")); 490 "extensions. Please ensure you have the latest graphics "
491 "driver.<br><br>Unsupported extensions:<br>") +
492 unsupported_gl_extensions.join("<br>"));
472 return false; 493 return false;
473 } 494 }
474 495
@@ -479,6 +500,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
479 500
480 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 501 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
481 502
503 const auto drd_callout =
504 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
505
506 if (result == Core::System::ResultStatus::Success &&
507 system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
508 drd_callout) {
509 UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation);
510 QMessageBox::warning(
511 this, tr("Warning Outdated Game Format"),
512 tr("You are using the deconstructed ROM directory format for this game, which is an "
513 "outdated format that has been superseded by others such as NCA, NAX, XCI, or "
514 "NSP. Deconstructed ROM directories lack icons, metadata, and update "
515 "support.<br><br>For an explanation of the various Switch formats yuzu supports, <a "
516 "href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our "
517 "wiki</a>. This message will not be shown again."));
518 }
519
482 render_window->DoneCurrent(); 520 render_window->DoneCurrent();
483 521
484 if (result != Core::System::ResultStatus::Success) { 522 if (result != Core::System::ResultStatus::Success) {
@@ -563,11 +601,19 @@ void GMainWindow::BootGame(const QString& filename) {
563 601
564 std::string title_name; 602 std::string title_name;
565 const auto res = Core::System::GetInstance().GetGameName(title_name); 603 const auto res = Core::System::GetInstance().GetGameName(title_name);
566 if (res != Loader::ResultStatus::Success) 604 if (res != Loader::ResultStatus::Success) {
567 title_name = FileUtil::GetFilename(filename.toStdString()); 605 const u64 program_id = Core::System::GetInstance().CurrentProcess()->program_id;
606
607 const auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
608 if (nacp != nullptr)
609 title_name = nacp->GetApplicationName();
610
611 if (title_name.empty())
612 title_name = FileUtil::GetFilename(filename.toStdString());
613 }
568 614
569 setWindowTitle(QString("yuzu %1| %4 | %2-%3") 615 setWindowTitle(QString("yuzu %1| %4 | %2-%3")
570 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc, 616 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc,
571 QString::fromStdString(title_name))); 617 QString::fromStdString(title_name)));
572 618
573 render_window->show(); 619 render_window->show();
@@ -602,7 +648,7 @@ void GMainWindow::ShutdownGame() {
602 game_list->show(); 648 game_list->show();
603 game_list->setFilterFocus(); 649 game_list->setFilterFocus();
604 setWindowTitle(QString("yuzu %1| %2-%3") 650 setWindowTitle(QString("yuzu %1| %2-%3")
605 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 651 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
606 652
607 // Disable status bar updates 653 // Disable status bar updates
608 status_bar_update_timer.stop(); 654 status_bar_update_timer.stop();
@@ -684,14 +730,11 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
684 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); 730 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
685} 731}
686 732
687void GMainWindow::OnGameListNavigateToGamedbEntry( 733void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
688 u64 program_id, 734 const CompatibilityList& compatibility_list) {
689 std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) { 735 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
690
691 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
692 736
693 QString directory; 737 QString directory;
694
695 if (it != compatibility_list.end()) 738 if (it != compatibility_list.end())
696 directory = it->second.second; 739 directory = it->second.second;
697 740
@@ -737,7 +780,8 @@ void GMainWindow::OnMenuLoadFolder() {
737 780
738void GMainWindow::OnMenuInstallToNAND() { 781void GMainWindow::OnMenuInstallToNAND() {
739 const QString file_filter = 782 const QString file_filter =
740 tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge " 783 tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
784 "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge "
741 "Image (*.xci)"); 785 "Image (*.xci)");
742 QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), 786 QString filename = QFileDialog::getOpenFileName(this, tr("Install File"),
743 UISettings::values.roms_path, file_filter); 787 UISettings::values.roms_path, file_filter);
@@ -760,7 +804,7 @@ void GMainWindow::OnMenuInstallToNAND() {
760 tr("Cancel"), 0, progress_maximum, this); 804 tr("Cancel"), 0, progress_maximum, this);
761 progress.setWindowModality(Qt::WindowModal); 805 progress.setWindowModality(Qt::WindowModal);
762 806
763 for (size_t i = 0; i < src->GetSize(); i += buffer.size()) { 807 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
764 if (progress.wasCanceled()) { 808 if (progress.wasCanceled()) {
765 dest->Resize(0); 809 dest->Resize(0);
766 return false; 810 return false;
@@ -797,22 +841,34 @@ void GMainWindow::OnMenuInstallToNAND() {
797 QMessageBox::Yes; 841 QMessageBox::Yes;
798 }; 842 };
799 843
800 if (filename.endsWith("xci", Qt::CaseInsensitive)) { 844 if (filename.endsWith("xci", Qt::CaseInsensitive) ||
801 const auto xci = std::make_shared<FileSys::XCI>( 845 filename.endsWith("nsp", Qt::CaseInsensitive)) {
802 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 846
803 if (xci->GetStatus() != Loader::ResultStatus::Success) { 847 std::shared_ptr<FileSys::NSP> nsp;
848 if (filename.endsWith("nsp", Qt::CaseInsensitive)) {
849 nsp = std::make_shared<FileSys::NSP>(
850 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
851 if (nsp->IsExtractedType())
852 failed();
853 } else {
854 const auto xci = std::make_shared<FileSys::XCI>(
855 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
856 nsp = xci->GetSecurePartitionNSP();
857 }
858
859 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
804 failed(); 860 failed();
805 return; 861 return;
806 } 862 }
807 const auto res = 863 const auto res =
808 Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy); 864 Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy);
809 if (res == FileSys::InstallResult::Success) { 865 if (res == FileSys::InstallResult::Success) {
810 success(); 866 success();
811 } else { 867 } else {
812 if (res == FileSys::InstallResult::ErrorAlreadyExists) { 868 if (res == FileSys::InstallResult::ErrorAlreadyExists) {
813 if (overwrite()) { 869 if (overwrite()) {
814 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( 870 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
815 xci, true, qt_raw_copy); 871 nsp, true, qt_raw_copy);
816 if (res2 == FileSys::InstallResult::Success) { 872 if (res2 == FileSys::InstallResult::Success) {
817 success(); 873 success();
818 } else { 874 } else {
@@ -826,7 +882,11 @@ void GMainWindow::OnMenuInstallToNAND() {
826 } else { 882 } else {
827 const auto nca = std::make_shared<FileSys::NCA>( 883 const auto nca = std::make_shared<FileSys::NCA>(
828 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 884 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
829 if (nca->GetStatus() != Loader::ResultStatus::Success) { 885 const auto id = nca->GetStatus();
886
887 // Game updates necessary are missing base RomFS
888 if (id != Loader::ResultStatus::Success &&
889 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
830 failed(); 890 failed();
831 return; 891 return;
832 } 892 }
@@ -885,6 +945,28 @@ void GMainWindow::OnMenuSelectGameListRoot() {
885 } 945 }
886} 946}
887 947
948void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) {
949 const auto res = QMessageBox::information(
950 this, tr("Changing Emulated Directory"),
951 tr("You are about to change the emulated %1 directory of the system. Please note "
952 "that this does not also move the contents of the previous directory to the "
953 "new one and you will have to do that yourself.")
954 .arg(target == EmulatedDirectoryTarget::SDMC ? tr("SD card") : tr("NAND")),
955 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
956
957 if (res == QMessageBox::Cancel)
958 return;
959
960 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
961 if (!dir_path.isEmpty()) {
962 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
963 : FileUtil::UserPath::NANDDir,
964 dir_path.toStdString());
965 Service::FileSystem::CreateFactories(vfs);
966 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
967 }
968}
969
888void GMainWindow::OnMenuRecentFile() { 970void GMainWindow::OnMenuRecentFile() {
889 QAction* action = qobject_cast<QAction*>(sender()); 971 QAction* action = qobject_cast<QAction*>(sender());
890 assert(action); 972 assert(action);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0b97e8220..552e3e61c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -6,10 +6,14 @@
6 6
7#include <memory> 7#include <memory>
8#include <unordered_map> 8#include <unordered_map>
9
9#include <QMainWindow> 10#include <QMainWindow>
10#include <QTimer> 11#include <QTimer>
12
13#include "common/common_types.h"
11#include "core/core.h" 14#include "core/core.h"
12#include "ui_main.h" 15#include "ui_main.h"
16#include "yuzu/compatibility_list.h"
13#include "yuzu/hotkeys.h" 17#include "yuzu/hotkeys.h"
14 18
15class Config; 19class Config;
@@ -32,6 +36,11 @@ namespace Tegra {
32class DebugContext; 36class DebugContext;
33} 37}
34 38
39enum class EmulatedDirectoryTarget {
40 NAND,
41 SDMC,
42};
43
35class GMainWindow : public QMainWindow { 44class GMainWindow : public QMainWindow {
36 Q_OBJECT 45 Q_OBJECT
37 46
@@ -85,7 +94,7 @@ private:
85 void ConnectWidgetEvents(); 94 void ConnectWidgetEvents();
86 void ConnectMenuEvents(); 95 void ConnectMenuEvents();
87 96
88 bool SupportsRequiredGLExtensions(); 97 QStringList GetUnsupportedGLExtensions();
89 bool LoadROM(const QString& filename); 98 bool LoadROM(const QString& filename);
90 void BootGame(const QString& filename); 99 void BootGame(const QString& filename);
91 void ShutdownGame(); 100 void ShutdownGame();
@@ -129,14 +138,15 @@ private slots:
129 /// Called whenever a user selects a game in the game list widget. 138 /// Called whenever a user selects a game in the game list widget.
130 void OnGameListLoadFile(QString game_path); 139 void OnGameListLoadFile(QString game_path);
131 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 140 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
132 void OnGameListNavigateToGamedbEntry( 141 void OnGameListNavigateToGamedbEntry(u64 program_id,
133 u64 program_id, 142 const CompatibilityList& compatibility_list);
134 std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
135 void OnMenuLoadFile(); 143 void OnMenuLoadFile();
136 void OnMenuLoadFolder(); 144 void OnMenuLoadFolder();
137 void OnMenuInstallToNAND(); 145 void OnMenuInstallToNAND();
138 /// Called whenever a user selects the "File->Select Game List Root" menu item 146 /// Called whenever a user selects the "File->Select Game List Root" menu item
139 void OnMenuSelectGameListRoot(); 147 void OnMenuSelectGameListRoot();
148 /// Called whenever a user select the "File->Select -- Directory" where -- is NAND or SD Card
149 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
140 void OnMenuRecentFile(); 150 void OnMenuRecentFile();
141 void OnConfigure(); 151 void OnConfigure();
142 void OnAbout(); 152 void OnAbout();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index faa0c626a..3879d4813 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -65,6 +65,9 @@
65 <addaction name="action_Select_Game_List_Root"/> 65 <addaction name="action_Select_Game_List_Root"/>
66 <addaction name="menu_recent_files"/> 66 <addaction name="menu_recent_files"/>
67 <addaction name="separator"/> 67 <addaction name="separator"/>
68 <addaction name="action_Select_NAND_Directory"/>
69 <addaction name="action_Select_SDMC_Directory"/>
70 <addaction name="separator"/>
68 <addaction name="action_Exit"/> 71 <addaction name="action_Exit"/>
69 </widget> 72 </widget>
70 <widget class="QMenu" name="menu_Emulation"> 73 <widget class="QMenu" name="menu_Emulation">
@@ -204,6 +207,22 @@
204 <string>Selects a folder to display in the game list</string> 207 <string>Selects a folder to display in the game list</string>
205 </property> 208 </property>
206 </action> 209 </action>
210 <action name="action_Select_NAND_Directory">
211 <property name="text">
212 <string>Select NAND Directory...</string>
213 </property>
214 <property name="toolTip">
215 <string>Selects a folder to use as the root of the emulated NAND</string>
216 </property>
217 </action>
218 <action name="action_Select_SDMC_Directory">
219 <property name="text">
220 <string>Select SD Card Directory...</string>
221 </property>
222 <property name="toolTip">
223 <string>Selects a folder to use as the root of the emulated SD card</string>
224 </property>
225 </action>
207 <action name="action_Fullscreen"> 226 <action name="action_Fullscreen">
208 <property name="checkable"> 227 <property name="checkable">
209 <bool>true</bool> 228 <bool>true</bool>
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index e99042a23..62c080aff 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -30,8 +30,9 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
30 QPixmap circle_pixmap(16, 16); 30 QPixmap circle_pixmap(16, 16);
31 circle_pixmap.fill(Qt::transparent); 31 circle_pixmap.fill(Qt::transparent);
32 QPainter painter(&circle_pixmap); 32 QPainter painter(&circle_pixmap);
33 painter.setRenderHint(QPainter::Antialiasing);
33 painter.setPen(color); 34 painter.setPen(color);
34 painter.setBrush(color); 35 painter.setBrush(color);
35 painter.drawEllipse(0, 0, 15, 15); 36 painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0);
36 return circle_pixmap; 37 return circle_pixmap;
37} 38}
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index a95580152..7ec1f5110 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -108,15 +108,27 @@ void Config::ReadValues() {
108 108
109 // Audio 109 // Audio
110 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 110 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
111 Settings::values.enable_audio_stretching =
112 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
111 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 113 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
112 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); 114 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
113 115
114 // Data Storage 116 // Data Storage
115 Settings::values.use_virtual_sd = 117 Settings::values.use_virtual_sd =
116 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 118 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
119 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
120 sdl2_config->Get("Data Storage", "nand_directory",
121 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
122 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
123 sdl2_config->Get("Data Storage", "nand_directory",
124 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
117 125
118 // System 126 // System
119 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
128 Settings::values.username = sdl2_config->Get("System", "username", "yuzu");
129 if (Settings::values.username.empty()) {
130 Settings::values.username = "yuzu";
131 }
120 132
121 // Miscellaneous 133 // Miscellaneous
122 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 134 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6ed9e7962..d35c441e9 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -150,6 +150,12 @@ swap_screen =
150# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) 150# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available)
151output_engine = 151output_engine =
152 152
153# Whether or not to enable the audio-stretching post-processing effect.
154# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
155# at the cost of increasing audio latency.
156# 0: No, 1 (default): Yes
157enable_audio_stretching =
158
153# Which audio device to use. 159# Which audio device to use.
154# auto (default): Auto-select 160# auto (default): Auto-select
155output_device = 161output_device =
@@ -170,7 +176,7 @@ use_docked_mode =
170 176
171# Sets the account username, max length is 32 characters 177# Sets the account username, max length is 32 characters
172# yuzu (default) 178# yuzu (default)
173username = 179username = yuzu
174 180
175# Sets the systems language index 181# Sets the systems language index
176# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, 182# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 351dd9225..0733301b2 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -16,6 +16,7 @@
16#include "input_common/keyboard.h" 16#include "input_common/keyboard.h"
17#include "input_common/main.h" 17#include "input_common/main.h"
18#include "input_common/motion_emu.h" 18#include "input_common/motion_emu.h"
19#include "input_common/sdl/sdl.h"
19#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 20#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
20 21
21void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@@ -91,6 +92,12 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
91 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); 92 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
92 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 93 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
93 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); 94 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
95 if (!GLAD_GL_ARB_base_instance)
96 unsupported_ext.push_back("ARB_base_instance");
97 if (!GLAD_GL_ARB_texture_storage)
98 unsupported_ext.push_back("ARB_texture_storage");
99 if (!GLAD_GL_ARB_multi_bind)
100 unsupported_ext.push_back("ARB_multi_bind");
94 101
95 // Extensions required to support some texture formats. 102 // Extensions required to support some texture formats.
96 if (!GLAD_GL_EXT_texture_compression_s3tc) 103 if (!GLAD_GL_EXT_texture_compression_s3tc)
@@ -114,7 +121,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
114 SDL_SetMainReady(); 121 SDL_SetMainReady();
115 122
116 // Initialize the window 123 // Initialize the window
117 if (SDL_Init(SDL_INIT_VIDEO) < 0) { 124 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
118 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 125 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
119 exit(1); 126 exit(1);
120 } 127 }
@@ -128,7 +135,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
128 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 135 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
129 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 136 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
130 137
131 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, 138 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
132 Common::g_scm_branch, Common::g_scm_desc); 139 Common::g_scm_branch, Common::g_scm_desc);
133 render_window = 140 render_window =
134 SDL_CreateWindow(window_title.c_str(), 141 SDL_CreateWindow(window_title.c_str(),
@@ -166,13 +173,15 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
166 OnResize(); 173 OnResize();
167 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 174 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
168 SDL_PumpEvents(); 175 SDL_PumpEvents();
169 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_name, Common::g_scm_branch, 176 SDL_GL_SetSwapInterval(false);
177 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
170 Common::g_scm_desc); 178 Common::g_scm_desc);
171 179
172 DoneCurrent(); 180 DoneCurrent();
173} 181}
174 182
175EmuWindow_SDL2::~EmuWindow_SDL2() { 183EmuWindow_SDL2::~EmuWindow_SDL2() {
184 InputCommon::SDL::CloseSDLJoysticks();
176 SDL_GL_DeleteContext(gl_context); 185 SDL_GL_DeleteContext(gl_context);
177 SDL_Quit(); 186 SDL_Quit();
178 187
@@ -217,6 +226,9 @@ void EmuWindow_SDL2::PollEvents() {
217 case SDL_QUIT: 226 case SDL_QUIT:
218 is_open = false; 227 is_open = false;
219 break; 228 break;
229 default:
230 InputCommon::SDL::HandleGameControllerEvent(event);
231 break;
220 } 232 }
221 } 233 }
222} 234}
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 41e7da897..b2559b717 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -10,6 +10,7 @@
10#include <fmt/ostream.h> 10#include <fmt/ostream.h>
11 11
12#include "common/common_paths.h" 12#include "common/common_paths.h"
13#include "common/file_util.h"
13#include "common/logging/backend.h" 14#include "common/logging/backend.h"
14#include "common/logging/filter.h" 15#include "common/logging/filter.h"
15#include "common/logging/log.h" 16#include "common/logging/log.h"
@@ -19,8 +20,10 @@
19#include "common/string_util.h" 20#include "common/string_util.h"
20#include "common/telemetry.h" 21#include "common/telemetry.h"
21#include "core/core.h" 22#include "core/core.h"
23#include "core/crypto/key_manager.h"
22#include "core/file_sys/vfs_real.h" 24#include "core/file_sys/vfs_real.h"
23#include "core/gdbstub/gdbstub.h" 25#include "core/gdbstub/gdbstub.h"
26#include "core/hle/service/filesystem/filesystem.h"
24#include "core/loader/loader.h" 27#include "core/loader/loader.h"
25#include "core/settings.h" 28#include "core/settings.h"
26#include "core/telemetry_session.h" 29#include "core/telemetry_session.h"
@@ -28,7 +31,6 @@
28#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 31#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
29 32
30#include <getopt.h> 33#include <getopt.h>
31#include "core/crypto/key_manager.h"
32#ifndef _MSC_VER 34#ifndef _MSC_VER
33#include <unistd.h> 35#include <unistd.h>
34#endif 36#endif
@@ -81,6 +83,9 @@ int main(int argc, char** argv) {
81 int option_index = 0; 83 int option_index = 0;
82 bool use_gdbstub = Settings::values.use_gdbstub; 84 bool use_gdbstub = Settings::values.use_gdbstub;
83 u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); 85 u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
86
87 InitializeLogging();
88
84 char* endarg; 89 char* endarg;
85#ifdef _WIN32 90#ifdef _WIN32
86 int argc_w; 91 int argc_w;
@@ -143,8 +148,6 @@ int main(int argc, char** argv) {
143 LocalFree(argv_w); 148 LocalFree(argv_w);
144#endif 149#endif
145 150
146 InitializeLogging();
147
148 MicroProfileOnThreadCreate("EmuThread"); 151 MicroProfileOnThreadCreate("EmuThread");
149 SCOPE_EXIT({ MicroProfileShutdown(); }); 152 SCOPE_EXIT({ MicroProfileShutdown(); });
150 153
@@ -167,6 +170,7 @@ int main(int argc, char** argv) {
167 170
168 Core::System& system{Core::System::GetInstance()}; 171 Core::System& system{Core::System::GetInstance()};
169 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 172 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
173 Service::FileSystem::CreateFactories(system.GetFilesystem());
170 174
171 SCOPE_EXIT({ system.Shutdown(); }); 175 SCOPE_EXIT({ system.Shutdown(); });
172 176