diff options
Diffstat (limited to '')
102 files changed, 1975 insertions, 536 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fcda4874..8267ee586 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -25,6 +25,8 @@ set(SRCS file_sys/ivfc_archive.cpp hle/config_mem.cpp hle/hle.cpp + hle/applets/applet.cpp + hle/applets/swkbd.cpp hle/kernel/address_arbiter.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp @@ -113,6 +115,7 @@ set(SRCS loader/elf.cpp loader/loader.cpp loader/ncch.cpp + tracer/recorder.cpp mem_map.cpp memory.cpp settings.cpp @@ -150,6 +153,8 @@ set(HEADERS hle/config_mem.h hle/function_wrappers.h hle/hle.h + hle/applets/applet.h + hle/applets/swkbd.h hle/kernel/address_arbiter.h hle/kernel/event.h hle/kernel/kernel.h @@ -239,6 +244,8 @@ set(HEADERS loader/elf.h loader/loader.h loader/ncch.h + tracer/recorder.h + tracer/citrace.h mem_map.h memory.h memory_setup.h diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp index 13d26d170..eb20bf6f7 100644 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ b/src/core/arm/disassembler/load_symbol_map.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <sstream> #include <string> #include <vector> diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 2488c879c..cc9355722 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -10,6 +10,11 @@ #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armdefs.h" +#include "core/arm/skyeye_common/arm_regformat.h" + +namespace Core { +struct ThreadContext; +} class ARM_DynCom final : virtual public ARM_Interface { public: diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index b00eb49a9..785f39566 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -4144,11 +4144,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { bx_inst* const inst_cream = (bx_inst*)inst_base->component; + u32 address = RM; + if (inst_cream->Rm == 15) - LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]); + address += 2 * GET_INST_SIZE(cpu); - cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; - cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; + cpu->TFlag = address & 1; + cpu->Reg[15] = address & 0xfffffffe; INC_PC(sizeof(bx_inst)); goto DISPATCH; } @@ -5695,7 +5697,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF); const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16); - RD = (result & (0xFFFFFFFFFFFFFFFFLL >> 15)) >> 16; + RD = BITS(result, 16, 47); if ((result >> 16) != (s32)RD) cpu->Cpsr |= (1 << 27); @@ -6246,7 +6248,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { SWI_INST: { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { - SVC::CallSVC(Memory::Read32(cpu->Reg[15])); + swi_inst* const inst_cream = (swi_inst*)inst_base->component; + SVC::CallSVC(inst_cream->num & 0xFFFF); } cpu->Reg[15] += GET_INST_SIZE(cpu); diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index 3e79c44c0..f10a5b70f 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp @@ -130,14 +130,13 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { } } else { ARMword Rd = ((tinstr & 0x0007) >> 0); - ARMword Rs = ((tinstr & 0x0038) >> 3); + ARMword Rs = ((tinstr & 0x0078) >> 3); if (tinstr & (1 << 7)) Rd += 8; - if (tinstr & (1 << 6)) - Rs += 8; switch ((tinstr & 0x03C0) >> 6) { + case 0x0: // ADD Rd,Rd,Rs case 0x1: // ADD Rd,Rd,Hs case 0x2: // ADD Hd,Hd,Rs case 0x3: // ADD Hd,Hd,Hs @@ -146,19 +145,19 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { |(Rd << 12) // Rd |(Rs << 0); // Rm break; + case 0x4: // CMP Rd,Rs case 0x5: // CMP Rd,Hs case 0x6: // CMP Hd,Rs case 0x7: // CMP Hd,Hs *ainstr = 0xE1500000 // base | (Rd << 16) // Rn - |(Rd << 12) // Rd |(Rs << 0); // Rm break; + case 0x8: // MOV Rd,Rs case 0x9: // MOV Rd,Hs case 0xA: // MOV Hd,Rs case 0xB: // MOV Hd,Hs *ainstr = 0xE1A00000 // base - | (Rd << 16) // Rn |(Rd << 12) // Rd |(Rs << 0); // Rm break; @@ -167,11 +166,6 @@ tdstate thumb_translate(u32 addr, u32 instr, u32* ainstr, u32* inst_size) { *ainstr = 0xE12FFF10 // base | ((tinstr & 0x0078) >> 3); // Rd break; - case 0x0: // UNDEFINED - case 0x4: // UNDEFINED - case 0x8: // UNDEFINED - valid = t_undefined; - break; case 0xE: // BLX case 0xF: // BLX *ainstr = 0xE1200030 // base diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index a92effbb4..d1c721809 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h @@ -59,6 +59,8 @@ enum { VFP_FPSID, VFP_FPSCR, VFP_FPEXC, + VFP_FPINST, + VFP_FPINST2, VFP_MVFR0, VFP_MVFR1, diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp index 571d6c2f2..1ffc1f9af 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.cpp +++ b/src/core/arm/skyeye_common/vfp/vfp.cpp @@ -20,36 +20,27 @@ /* Note: this file handles interface with arm core and vfp registers */ +#include "common/common_funcs.h" #include "common/logging/log.h" #include "core/arm/skyeye_common/armdefs.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/arm/skyeye_common/vfp/vfp.h" -unsigned VFPInit(ARMul_State* state) +void VFPInit(ARMul_State* state) { state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 | VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION; state->VFP[VFP_FPEXC] = 0; state->VFP[VFP_FPSCR] = 0; + // ARM11 MPCore instruction register reset values. + state->VFP[VFP_FPINST] = 0xEE000A00; + state->VFP[VFP_FPINST2] = 0; + // ARM11 MPCore feature register values. state->VFP[VFP_MVFR0] = 0x11111111; state->VFP[VFP_MVFR1] = 0; - - return 0; -} - -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt) -{ - if (reg == 1) - { - state->VFP[VFP_FPSCR] = state->Reg[Rt]; - } - else if (reg == 8) - { - state->VFP[VFP_FPEXC] = state->Reg[Rt]; - } } void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value) @@ -153,9 +144,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions); if (exceptions == VFP_EXCEPTION_ERROR) { - LOG_TRACE(Core_ARM11, "unhandled bounce %x\n", inst); - exit(-1); - return; + LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst); + Crash(); } /* diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h index acefae9bb..80ca93ccd 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.h +++ b/src/core/arm/skyeye_common/vfp/vfp.h @@ -26,7 +26,7 @@ #define CHECK_VFP_ENABLED #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); -unsigned VFPInit(ARMul_State* state); +void VFPInit(ARMul_State* state); s32 vfp_get_float(ARMul_State* state, u32 reg); void vfp_put_float(ARMul_State* state, s32 val, u32 reg); @@ -36,10 +36,8 @@ void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpsc u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr); u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr); -void VMSR(ARMul_State* state, ARMword reg, ARMword Rt); void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value); void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); void VMOVBRRSS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2); void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm); void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword imm); - diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 67fe63aa4..8efcbab1c 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp @@ -995,7 +995,7 @@ VMOVBRS_INST: #ifdef VFP_INTERPRETER_STRUCT struct vmsr_inst { unsigned int reg; - unsigned int Rd; + unsigned int Rt; }; #endif #ifdef VFP_INTERPRETER_TRANS @@ -1009,7 +1009,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) inst_base->br = NON_BRANCH; inst_cream->reg = BITS(inst, 16, 19); - inst_cream->Rd = BITS(inst, 12, 15); + inst_cream->Rt = BITS(inst, 12, 15); return inst_base; } @@ -1017,15 +1017,30 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmsr)(unsigned int inst, int index) #ifdef VFP_INTERPRETER_IMPL VMSR_INST: { - if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled , and in privileged mode */ /* Exceptions must be checked, according to v7 ref manual */ CHECK_VFP_ENABLED; - vmsr_inst *inst_cream = (vmsr_inst *)inst_base->component; + vmsr_inst* const inst_cream = (vmsr_inst*)inst_base->component; + + unsigned int reg = inst_cream->reg; + unsigned int rt = inst_cream->Rt; - VMSR(cpu, inst_cream->reg, inst_cream->Rd); + if (reg == 1) + { + cpu->VFP[VFP_FPSCR] = cpu->Reg[rt]; + } + else if (InAPrivilegedMode(cpu)) + { + if (reg == 8) + cpu->VFP[VFP_FPEXC] = cpu->Reg[rt]; + else if (reg == 9) + cpu->VFP[VFP_FPINST] = cpu->Reg[rt]; + else if (reg == 10) + cpu->VFP[VFP_FPINST2] = cpu->Reg[rt]; + } } cpu->Reg[15] += GET_INST_SIZE(cpu); INC_PC(sizeof(vmsr_inst)); @@ -1111,19 +1126,22 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmrs)(unsigned int inst, int index) #ifdef VFP_INTERPRETER_IMPL VMRS_INST: { - if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { + if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { /* FIXME: special case for access to FPSID and FPEXC, VFP must be disabled, and in privileged mode */ /* Exceptions must be checked, according to v7 ref manual */ CHECK_VFP_ENABLED; - vmrs_inst *inst_cream = (vmrs_inst *)inst_base->component; + vmrs_inst* const inst_cream = (vmrs_inst*)inst_base->component; - if (inst_cream->reg == 1) /* FPSCR */ + unsigned int reg = inst_cream->reg; + unsigned int rt = inst_cream->Rt; + + if (reg == 1) // FPSCR { - if (inst_cream->Rt != 15) + if (rt != 15) { - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSCR]; + cpu->Reg[rt] = cpu->VFP[VFP_FPSCR]; } else { @@ -1133,25 +1151,26 @@ VMRS_INST: cpu->VFlag = (cpu->VFP[VFP_FPSCR] >> 28) & 1; } } - else + else if (reg == 0) { - switch (inst_cream->reg) - { - case 0: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; - break; - case 6: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR1]; - break; - case 7: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR0]; - break; - case 8: - cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; - break; - default: - break; - } + cpu->Reg[rt] = cpu->VFP[VFP_FPSID]; + } + else if (reg == 6) + { + cpu->Reg[rt] = cpu->VFP[VFP_MVFR1]; + } + else if (reg == 7) + { + cpu->Reg[rt] = cpu->VFP[VFP_MVFR0]; + } + else if (InAPrivilegedMode(cpu)) + { + if (reg == 8) + cpu->Reg[rt] = cpu->VFP[VFP_FPEXC]; + else if (reg == 9) + cpu->Reg[rt] = cpu->VFP[VFP_FPINST]; + else if (reg == 10) + cpu->Reg[rt] = cpu->VFP[VFP_FPINST2]; } } cpu->Reg[15] += GET_INST_SIZE(cpu); diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 5a655a6f2..e5d339252 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -53,6 +53,8 @@ #include <cinttypes> +#include "common/common_funcs.h" +#include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/skyeye_common/vfp/vfp_helper.h" @@ -1246,7 +1248,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) if (!fop->fn) { LOG_CRITICAL(Core_ARM11, "could not find single op %d, inst=0x%x@0x%x", FEXT_TO_IDX(inst), inst, state->Reg[15]); - exit(-1); + Crash(); goto invalid; } diff --git a/src/core/core.cpp b/src/core/core.cpp index 79038cd52..dddc16708 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,15 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/settings.h" #include "core/arm/arm_interface.h" -#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/dyncom/arm_dyncom.h" #include "core/hle/hle.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e53c2e606..20f2da0fe 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <atomic> -#include <cstdio> #include <mutex> #include <vector> -#include "common/assert.h" #include "common/chunk_file.h" +#include "common/logging/log.h" +#include "common/string_util.h" #include "core/arm/arm_interface.h" #include "core/core.h" @@ -502,7 +502,7 @@ void Advance() { Core::g_app_core->down_count += diff; } if (advance_callback) - advance_callback(cycles_executed); + advance_callback(static_cast<int>(cycles_executed)); } void LogPendingEvents() { diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp index 45a559ce8..3f81447df 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/archive_backend.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstddef> +#include <iomanip> #include <sstream> #include "common/logging/log.h" diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e50c58a52..92dad8e6f 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -2,17 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/disk_archive.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index ef0b27bde..ec8d770fc 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -4,10 +4,13 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index d4a12ed10..696b51a94 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -2,30 +2,30 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <memory> #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" +#include "core/file_sys/ivfc_archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace namespace FileSys { -ArchiveFactory_RomFS::ArchiveFactory_RomFS(const Loader::AppLoader& app_loader) - : romfs_data(std::make_shared<std::vector<u8>>()) { +ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) { // Load the RomFS from the app - if (Loader::ResultStatus::Success != app_loader.ReadRomFS(*romfs_data)) { + if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { LOG_ERROR(Service_FS, "Unable to read RomFS!"); } } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { - auto archive = Common::make_unique<IVFCArchive>(romfs_data); + auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 409bc670a..2bedfa9c6 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h @@ -5,11 +5,13 @@ #pragma once #include <memory> +#include <string> #include <vector> #include "common/common_types.h" -#include "core/file_sys/ivfc_archive.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -20,14 +22,16 @@ namespace FileSys { /// File system interface to the RomFS archive class ArchiveFactory_RomFS final : public ArchiveFactory { public: - ArchiveFactory_RomFS(const Loader::AppLoader& app_loader); + ArchiveFactory_RomFS(Loader::AppLoader& app_loader); std::string GetName() const override { return "RomFS"; } ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; ResultCode Format(const Path& path) override; private: - std::shared_ptr<std::vector<u8>> romfs_data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; } // namespace FileSys diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index a92309377..12876899f 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -2,18 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_savedata.h" #include "core/file_sys/disk_archive.h" #include "core/hle/kernel/process.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -37,7 +37,7 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directo } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); if (!FileUtil::Exists(concrete_mount_point)) { // When a SaveData archive is created for the first time, it is not yet formatted // and the save file/directory structure expected by the game has not yet been initialized. @@ -52,7 +52,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P } ResultCode ArchiveFactory_SaveData::Format(const Path& path) { - std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->program_id); + std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); FileUtil::DeleteDirRecursively(concrete_mount_point); FileUtil::CreateFullPath(concrete_mount_point); return RESULT_SUCCESS; diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index db17afc92..1f65297dd 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h @@ -4,10 +4,11 @@ #pragma once -#include "common/common_types.h" +#include <memory> +#include <string> -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index e7e4fbf1d..ea1dfe2c7 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp @@ -2,11 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <vector> + +#include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_savedatacheck.h" +#include "core/file_sys/ivfc_archive.h" #include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,17 +37,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co auto vec = path.AsBinary(); const u32* data = reinterpret_cast<u32*>(vec.data()); std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); - FileUtil::IOFile file(file_path, "rb"); + auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); - if (!file.IsOpen()) { + if (!file->IsOpen()) { return ResultCode(-1); // TODO(Subv): Find the right error code } - auto size = file.GetSize(); - auto raw_data = std::make_shared<std::vector<u8>>(size); - file.ReadBytes(raw_data->data(), size); - file.Close(); + auto size = file->GetSize(); - auto archive = Common::make_unique<IVFCArchive>(std::move(raw_data)); + auto archive = Common::make_unique<IVFCArchive>(file, 0, size); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h index f78a6f02e..b14aefe8b 100644 --- a/src/core/file_sys/archive_savedatacheck.h +++ b/src/core/file_sys/archive_savedatacheck.h @@ -4,12 +4,11 @@ #pragma once -#include <vector> +#include <memory> +#include <string> -#include "common/common_types.h" - -#include "core/file_sys/ivfc_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index c1234a186..5c825f429 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -2,9 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> -#include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 1becf6c0f..10b273bdb 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -4,10 +4,11 @@ #pragma once -#include "common/common_types.h" +#include <memory> +#include <string> -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 4fe785c97..896f89529 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -2,15 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <vector> #include "common/common_types.h" #include "common/file_util.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/disk_archive.h" #include "core/hle/service/fs/archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index 3431fed88..afc689848 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -4,10 +4,13 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" -#include "core/file_sys/disk_archive.h" -#include "core/loader/loader.h" +#include "core/file_sys/archive_backend.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 9980cced1..1096fd34d 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <sys/stat.h> +#include <algorithm> +#include <cstdio> #include "common/common_types.h" #include "common/file_util.h" @@ -10,7 +11,6 @@ #include "common/make_unique.h" #include "core/file_sys/disk_archive.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -105,12 +105,12 @@ bool DiskFile::Open() { return true; } -size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { +size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { file->Seek(offset, SEEK_SET); return file->ReadBytes(buffer, length); } -size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { +size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { file->Seek(offset, SEEK_SET); size_t written = file->WriteBytes(buffer, length); if (flush) @@ -118,8 +118,8 @@ size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, cons return written; } -size_t DiskFile::GetSize() const { - return static_cast<size_t>(file->GetSize()); +u64 DiskFile::GetSize() const { + return file->GetSize(); } bool DiskFile::SetSize(const u64 size) const { diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index a22d3837a..c5da07508 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h @@ -4,13 +4,18 @@ #pragma once +#include <cstddef> +#include <memory> +#include <string> +#include <vector> + #include "common/common_types.h" #include "common/file_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/file_backend.h" -#include "core/loader/loader.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -50,10 +55,10 @@ public: DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); bool Open() override; - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; - size_t GetSize() const override; - bool SetSize(const u64 size) const override; + size_t Read(u64 offset, size_t length, u8* buffer) const override; + size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; bool Close() const override; void Flush() const override { diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 0fcff1845..df7165df3 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -4,6 +4,8 @@ #pragma once +#include <cstddef> + #include "common/common_types.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -29,7 +31,7 @@ public: * @param buffer Buffer to read data into * @return Number of bytes read */ - virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; + virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0; /** * Write data to the file @@ -39,20 +41,20 @@ public: * @param buffer Buffer to read data from * @return Number of bytes written */ - virtual size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const = 0; + virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; /** * Get the size of the file in bytes * @return Size of the file in bytes */ - virtual size_t GetSize() const = 0; + virtual u64 GetSize() const = 0; /** * Set the size of the file in bytes * @param size New size of the file * @return true if successful */ - virtual bool SetSize(const u64 size) const = 0; + virtual bool SetSize(u64 size) const = 0; /** * Close the file diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 2d2509d16..e16aa1491 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -2,10 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <memory> #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" @@ -16,15 +16,12 @@ namespace FileSys { -IVFCArchive::IVFCArchive(std::shared_ptr<const std::vector<u8>> data) : data(data) { -} - std::string IVFCArchive::GetName() const { return "IVFC"; } std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { - return Common::make_unique<IVFCFile>(data); + return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); } bool IVFCArchive::DeleteFile(const Path& path) const { @@ -64,19 +61,21 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c //////////////////////////////////////////////////////////////////////////////////////////////////// -size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { +size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); - memcpy(buffer, data->data() + offset, length); - return length; + romfs_file->Seek(data_offset + offset, SEEK_SET); + size_t read_length = (size_t)std::min((u64)length, data_size - offset); + + return romfs_file->ReadBytes(buffer, read_length); } -size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { +size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); return 0; } -size_t IVFCFile::GetSize() const { - return sizeof(u8) * data->size(); +u64 IVFCFile::GetSize() const { + return data_size; } bool IVFCFile::SetSize(const u64 size) const { diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 10415798d..c15a6c4ae 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -4,15 +4,18 @@ #pragma once +#include <cstddef> #include <memory> +#include <string> #include <vector> #include "common/common_types.h" +#include "common/file_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/file_backend.h" -#include "core/loader/loader.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -26,7 +29,8 @@ namespace FileSys { */ class IVFCArchive : public ArchiveBackend { public: - IVFCArchive(std::shared_ptr<const std::vector<u8>> data); + IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + : romfs_file(file), data_offset(offset), data_size(size) {} std::string GetName() const override; @@ -40,23 +44,28 @@ public: std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; protected: - std::shared_ptr<const std::vector<u8>> data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; class IVFCFile : public FileBackend { public: - IVFCFile(std::shared_ptr<const std::vector<u8>> data) : data(data) {} + IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + : romfs_file(file), data_offset(offset), data_size(size) {} bool Open() override { return true; } - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; - size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; - size_t GetSize() const override; - bool SetSize(const u64 size) const override; + size_t Read(u64 offset, size_t length, u8* buffer) const override; + size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; bool Close() const override { return false; } void Flush() const override { } private: - std::shared_ptr<const std::vector<u8>> data; + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; }; class IVFCDirectory : public DirectoryBackend { diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp new file mode 100644 index 000000000..826f6cbb6 --- /dev/null +++ b/src/core/hle/applets/applet.cpp @@ -0,0 +1,101 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <unordered_map> + +#include "common/assert.h" +#include "common/common_types.h" + +#include "core/core_timing.h" +#include "core/hle/applets/applet.h" +#include "core/hle/applets/swkbd.h" +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// Specializes std::hash for AppletId, so that we can use it in std::unordered_map. +// Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 +namespace std { + template <> + struct hash<Service::APT::AppletId> { + typedef Service::APT::AppletId argument_type; + typedef std::size_t result_type; + + result_type operator()(const argument_type& id_code) const { + typedef std::underlying_type<argument_type>::type Type; + return std::hash<Type>()(static_cast<Type>(id_code)); + } + }; +} + +namespace HLE { +namespace Applets { + +static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets; +static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. +/// The interval at which the Applet update callback will be called, 16.6ms +static const u64 applet_update_interval_us = 16666; + +ResultCode Applet::Create(Service::APT::AppletId id) { + switch (id) { + case Service::APT::AppletId::SoftwareKeyboard1: + case Service::APT::AppletId::SoftwareKeyboard2: + applets[id] = std::make_shared<SoftwareKeyboard>(id); + break; + default: + // TODO(Subv): Find the right error code + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); + } + + return RESULT_SUCCESS; +} + +std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) { + auto itr = applets.find(id); + if (itr != applets.end()) + return itr->second; + return nullptr; +} + +/// Handles updating the current Applet every time it's called. +static void AppletUpdateEvent(u64 applet_id, int cycles_late) { + Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id); + std::shared_ptr<Applet> applet = Applet::Get(id); + ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); + + applet->Update(); + + // If the applet is still running after the last update, reschedule the event + if (applet->IsRunning()) { + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, + applet_update_event, applet_id); + } else { + // Otherwise the applet has terminated, in which case we should clean it up + applets[id] = nullptr; + } +} + +ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) { + ResultCode result = StartImpl(parameter); + if (result.IsError()) + return result; + // Schedule the update event + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id)); + return result; +} + +void Init() { + // Register the applet update callback + applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); +} + +void Shutdown() { +} + +} +} // namespace diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h new file mode 100644 index 000000000..b235d0b8a --- /dev/null +++ b/src/core/hle/applets/applet.h @@ -0,0 +1,77 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +class Applet { +public: + virtual ~Applet() { } + Applet(Service::APT::AppletId id) : id(id) { } + + /** + * Creates an instance of the Applet subclass identified by the parameter. + * and stores it in a global map. + * @param id Id of the applet to create. + * @returns ResultCode Whether the operation was successful or not. + */ + static ResultCode Create(Service::APT::AppletId id); + + /** + * Retrieves the Applet instance identified by the specified id. + * @param id Id of the Applet to retrieve. + * @returns Requested Applet or nullptr if not found. + */ + static std::shared_ptr<Applet> Get(Service::APT::AppletId id); + + /** + * Handles a parameter from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0; + + /** + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + ResultCode Start(const Service::APT::AppletStartupParameter& parameter); + + /** + * Whether the applet is currently executing instead of the host application or not. + */ + virtual bool IsRunning() const = 0; + + /** + * Handles an update tick for the Applet, lets it update the screen, send commands, etc. + */ + virtual void Update() = 0; + +protected: + /** + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; + + Service::APT::AppletId id; ///< Id of this Applet +}; + +/// Initializes the HLE applets +void Init(); + +/// Shuts down the HLE applets +void Shutdown(); + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp new file mode 100644 index 000000000..1db6b5a17 --- /dev/null +++ b/src/core/hle/applets/swkbd.cpp @@ -0,0 +1,113 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <string> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "core/hle/applets/swkbd.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/gsp_gpu.h" +#include "core/hle/result.h" +#include "core/memory.h" + +#include "video_core/video_core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace HLE { +namespace Applets { + +SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { + // Create the SharedMemory that will hold the framebuffer data + // TODO(Subv): What size should we use here? + using Kernel::MemoryPermission; + framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); +} + +ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + Service::APT::MessageParameter result; + // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo + result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.data = nullptr; + result.buffer_size = 0; + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { + ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong"); + + memcpy(&config, parameter.data, parameter.buffer_size); + text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object); + + // TODO(Subv): Verify if this is the correct behavior + memset(text_memory->GetPointer(), 0, text_memory->size); + + DrawScreenKeyboard(); + + started = true; + return RESULT_SUCCESS; +} + +void SoftwareKeyboard::Update() { + // TODO(Subv): Handle input using the touch events from the HID module + + // TODO(Subv): Remove this hardcoded text + std::u16string text = Common::UTF8ToUTF16("Citra"); + memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); + + // TODO(Subv): Ask for input and write it to the shared memory + // TODO(Subv): Find out what are the possible values for the return code, + // some games seem to check for a hardcoded 2 + config.return_code = 2; + config.text_length = 6; + config.text_offset = 0; + + // TODO(Subv): We're finalizing the applet immediately after it's started, + // but we should defer this call until after all the input has been collected. + Finalize(); +} + +void SoftwareKeyboard::DrawScreenKeyboard() { + auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); + auto info = bottom_screen->framebuffer_info[bottom_screen->index]; + + // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer + memset(Memory::GetPointer(info.address_left), 0, info.stride * 320); + + GSP_GPU::SetBufferSwap(1, info); +} + +void SoftwareKeyboard::Finalize() { + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer_size = sizeof(SoftwareKeyboardConfig); + message.data = reinterpret_cast<u8*>(&config); + message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + started = false; +} + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h new file mode 100644 index 000000000..cb95b8d90 --- /dev/null +++ b/src/core/hle/applets/swkbd.h @@ -0,0 +1,90 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "common/common_funcs.h" + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/result.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +struct SoftwareKeyboardConfig { + INSERT_PADDING_WORDS(0x8); + + u16 max_text_length; ///< Maximum length of the input text + + INSERT_PADDING_BYTES(0x6E); + + char16_t display_text[65]; ///< Text to display when asking the user for input + + INSERT_PADDING_BYTES(0xE); + + u32 default_text_offset; ///< Offset of the default text in the output SharedMemory + + INSERT_PADDING_WORDS(0x3); + + u32 shared_memory_size; ///< Size of the SharedMemory + + INSERT_PADDING_WORDS(0x1); + + u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown + + INSERT_PADDING_WORDS(0x2); + + u32 text_offset; ///< Offset in the SharedMemory where the output text starts + u16 text_length; ///< Length in characters of the output text + + INSERT_PADDING_BYTES(0x2B6); +}; + +/** + * The size of this structure (0x400) has been verified via reverse engineering of multiple games + * that use the software keyboard. + */ +static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); + +class SoftwareKeyboard final : public Applet { +public: + SoftwareKeyboard(Service::APT::AppletId id); + ~SoftwareKeyboard() {} + + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; + void Update() override; + bool IsRunning() const override { return started; } + + /** + * Draws a keyboard to the current bottom screen framebuffer. + */ + void DrawScreenKeyboard(); + + /** + * Sends the LibAppletClosing signal to the application, + * along with the relevant data buffers. + */ + void Finalize(); + + /// TODO(Subv): Find out what this is actually used for. + /// It is believed that the application stores the current screen image here. + Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; + + /// SharedMemory where the output text will be stored + Kernel::SharedPtr<Kernel::SharedMemory> text_memory; + + /// Configuration of this instance of the SoftwareKeyboard, as received from the application + SoftwareKeyboardConfig config; + + /// Whether this applet is currently running instead of the host application or not. + bool started; +}; + +} +} // namespace diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index ea0777438..1a0518926 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -87,8 +87,28 @@ template<ResultCode func(u32, s64)> void Wrap() { } } -template<ResultCode func(void*, void*, u32)> void Wrap(){ - FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)).raw); +template<ResultCode func(MemoryInfo*, PageInfo*, u32)> void Wrap() { + MemoryInfo memory_info = {}; + PageInfo page_info = {}; + u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; + Core::g_app_core->SetReg(1, memory_info.base_address); + Core::g_app_core->SetReg(2, memory_info.size); + Core::g_app_core->SetReg(3, memory_info.permission); + Core::g_app_core->SetReg(4, memory_info.state); + Core::g_app_core->SetReg(5, page_info.flags); + FuncReturn(retval); +} + +template<ResultCode func(MemoryInfo*, PageInfo*, Handle, u32)> void Wrap() { + MemoryInfo memory_info = {}; + PageInfo page_info = {}; + u32 retval = func(&memory_info, &page_info, PARAM(2), PARAM(3)).raw; + Core::g_app_core->SetReg(1, memory_info.base_address); + Core::g_app_core->SetReg(2, memory_info.size); + Core::g_app_core->SetReg(3, memory_info.permission); + Core::g_app_core->SetReg(4, memory_info.state); + Core::g_app_core->SetReg(5, page_info.flags); + FuncReturn(retval); } template<ResultCode func(s32*, u32)> void Wrap(){ diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index fdeb9a028..cd0a400dc 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -10,7 +10,6 @@ #include "core/hle/hle.h" #include "core/hle/config_mem.h" #include "core/hle/shared_page.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index f338f3266..53feebbc0 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -21,7 +21,7 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { SharedPtr<Event> evt(new Event); evt->signaled = false; - evt->reset_type = evt->intitial_reset_type = reset_type; + evt->reset_type = reset_type; evt->name = std::move(name); return evt; diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index fba960d2a..89d405236 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -26,7 +26,6 @@ public: static const HandleType HANDLE_TYPE = HandleType::Event; HandleType GetHandleType() const override { return HANDLE_TYPE; } - ResetType intitial_reset_type; ///< ResetType specified at Event initialization ResetType reset_type; ///< Current ResetType bool signaled; ///< Whether the event has already been signaled diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 20e11da16..5711c0405 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -7,8 +7,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/arm/arm_interface.h" -#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 64595f758..4d4276f7a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,10 +4,11 @@ #pragma once -#include <boost/intrusive_ptr.hpp> +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include <algorithm> #include <array> -#include <memory> +#include <cstddef> #include <string> #include <vector> @@ -16,8 +17,6 @@ #include "core/hle/hle.h" #include "core/hle/result.h" -struct ApplicationInfo; - namespace Kernel { class Thread; @@ -48,6 +47,7 @@ enum class HandleType : u32 { Semaphore = 10, Timer = 11, ResourceLimit = 12, + CodeSet = 13, }; enum { @@ -86,6 +86,7 @@ public: case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: + case HandleType::CodeSet: return false; } } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b0e75ba59..a7892c652 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -5,24 +5,39 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/make_unique.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/vm_manager.h" +#include "core/mem_map.h" #include "core/memory.h" namespace Kernel { +SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) { + SharedPtr<CodeSet> codeset(new CodeSet); + + codeset->name = std::move(name); + codeset->program_id = program_id; + + return codeset; +} + +CodeSet::CodeSet() {} +CodeSet::~CodeSet() {} + u32 Process::next_process_id; -SharedPtr<Process> Process::Create(std::string name, u64 program_id) { +SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { SharedPtr<Process> process(new Process); - process->name = std::move(name); - process->program_id = program_id; - + process->codeset = std::move(code_set); process->flags.raw = 0; process->flags.memory_region = MemoryRegion::APPLICATION; + process->address_space = Common::make_unique<VMManager>(); + Memory::InitLegacyAddressSpace(*process->address_space); return process; } @@ -87,8 +102,19 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } } -void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { - Kernel::SetupMainThread(entry_point, main_thread_priority); +void Process::Run(s32 main_thread_priority, u32 stack_size) { + auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { + auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, + segment.offset, segment.size, memory_state).Unwrap(); + address_space->Reprotect(vma, permissions); + }; + + MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); + MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); + MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); + + address_space->LogLayout(); + Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); } Kernel::Process::Process() {} diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 7b8a68610..83d3aceae 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -5,6 +5,9 @@ #pragma once #include <bitset> +#include <cstddef> +#include <memory> +#include <string> #include <boost/container/static_vector.hpp> @@ -12,7 +15,6 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" namespace Kernel { @@ -46,23 +48,51 @@ union ProcessFlags { }; class ResourceLimit; +class VMManager; + +struct CodeSet final : public Object { + static SharedPtr<CodeSet> Create(std::string name, u64 program_id); + + std::string GetTypeName() const override { return "CodeSet"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::CodeSet; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /// Name of the process + std::string name; + /// Title ID corresponding to the process + u64 program_id; + + std::shared_ptr<std::vector<u8>> memory; + + struct Segment { + size_t offset = 0; + VAddr addr = 0; + u32 size = 0; + }; + + Segment code, rodata, data; + VAddr entrypoint; + +private: + CodeSet(); + ~CodeSet() override; +}; class Process final : public Object { public: - static SharedPtr<Process> Create(std::string name, u64 program_id); + static SharedPtr<Process> Create(SharedPtr<CodeSet> code_set); std::string GetTypeName() const override { return "Process"; } - std::string GetName() const override { return name; } + std::string GetName() const override { return codeset->name; } static const HandleType HANDLE_TYPE = HandleType::Process; HandleType GetHandleType() const override { return HANDLE_TYPE; } static u32 next_process_id; - /// Name of the process - std::string name; - /// Title ID corresponding to the process - u64 program_id; + SharedPtr<CodeSet> codeset; /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -80,6 +110,7 @@ public: /// Bitmask of the used TLS slots std::bitset<300> used_tls_slots; + std::unique_ptr<VMManager> address_space; /** * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them @@ -90,7 +121,7 @@ public: /** * Applies address space changes and launches the process main thread. */ - void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + void Run(s32 main_thread_priority, u32 stack_size); private: Process(); diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 257da9105..adaffcafe 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -4,8 +4,14 @@ #pragma once +#include <string> + +#include "common/assert.h" +#include "common/common_types.h" + #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/result.h" #include "core/memory.h" namespace IPC { diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 204266896..7a2922776 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -4,9 +4,12 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4729a7fe0..8b49fc7df 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -37,6 +37,10 @@ void Thread::Acquire() { ASSERT_MSG(!ShouldWait(), "object unavailable!"); } +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; + // Lists all thread ids that aren't deleted/etc. static std::vector<SharedPtr<Thread>> thread_list; @@ -93,6 +97,8 @@ void Thread::Stop() { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + wakeup_callback_handle_table.Close(callback_handle); + callback_handle = 0; // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) @@ -108,6 +114,7 @@ void Thread::Stop() { for (auto& wait_object : wait_objects) { wait_object->RemoveWaitingThread(this); } + wait_objects.clear(); Kernel::g_current_process->used_tls_slots[tls_index] = false; @@ -210,6 +217,14 @@ static void SwitchContext(Thread* new_thread) { new_thread->context.pc -= thumb_mode ? 2 : 4; } + // Clean up the thread's wait_objects, they'll be restored if needed during + // the svcWaitSynchronization call + for (int i = 0; i < new_thread->wait_objects.size(); ++i) { + SharedPtr<WaitObject> object = new_thread->wait_objects[i]; + object->RemoveWaitingThread(new_thread); + } + new_thread->wait_objects.clear(); + ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = THREADSTATUS_RUNNING; @@ -268,10 +283,6 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { thread->status = THREADSTATUS_WAIT_ARB; } -// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing -// us to simply use a pool index or similar. -static Kernel::HandleTable wakeup_callback_handle_table; - /** * Callback that will wake up the thread it was scheduled for * @param thread_handle The handle of the thread that's been awoken @@ -503,12 +514,16 @@ void ThreadingInit() { current_thread = nullptr; next_thread_id = 1; - - thread_list.clear(); - ready_queue.clear(); } void ThreadingShutdown() { + current_thread = nullptr; + + for (auto& t : thread_list) { + t->Stop(); + } + thread_list.clear(); + ready_queue.clear(); } } // namespace diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index b8160bb2c..1ff1d9b97 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -13,6 +13,7 @@ #include "core/core.h" +#include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index b2dd21542..205cc7b53 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <iterator> + #include "common/assert.h" #include "core/hle/kernel/vm_manager.h" @@ -33,6 +35,10 @@ VMManager::VMManager() { Reset(); } +VMManager::~VMManager() { + Reset(); +} + void VMManager::Reset() { vma_map.clear(); @@ -128,6 +134,16 @@ void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { MergeAdjacent(iter); } +void VMManager::LogLayout() const { + for (const auto& p : vma_map) { + const VirtualMemoryArea& vma = p.second; + LOG_DEBUG(Kernel, "%08X - %08X size: %8X %c%c%c", vma.base, vma.base + vma.size, vma.size, + (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', + (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', + (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-'); + } +} + VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given // non-const access to its container. diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 22b724603..b3795a94a 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -6,7 +6,6 @@ #include <map> #include <memory> -#include <string> #include <vector> #include "common/common_types.h" @@ -102,7 +101,7 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ -class VMManager { +class VMManager final { // TODO(yuriks): Make page tables switchable to support multiple VMManagers public: /** @@ -122,6 +121,7 @@ public: using VMAHandle = decltype(vma_map)::const_iterator; VMManager(); + ~VMManager(); /// Clears the address space map, re-initializing with a single free area. void Reset(); @@ -169,6 +169,9 @@ public: /// Changes the permissions of the given VMA. void Reprotect(VMAHandle vma, VMAPermission new_perms); + /// Dumps the address space layout to the log, for debugging + void LogLayout() const; + private: using VMAIter = decltype(vma_map)::iterator; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index ce633d841..cb2d681e0 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -4,7 +4,7 @@ #pragma once -#include <cstddef> +#include <new> #include <type_traits> #include <utility> diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 57dc1ece7..7332478fb 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -38,6 +38,15 @@ void GetTitleIDList(Service::Interface* self) { LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type); } +void GetNumContentInfos(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; // Number of content infos plus one + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + void Init() { using namespace Kernel; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 063b8bd09..0b78f5393 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -37,6 +37,19 @@ void TitleIDListGetTotal(Service::Interface* self); */ void GetTitleIDList(Service::Interface* self); +/** + * AM::GetNumContentInfos service function + * Inputs: + * 0 : Command header (0x100100C0) + * 1 : Unknown + * 2 : Unknown + * 3 : Unknown + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Number of content infos plus one + */ +void GetNumContentInfos(Service::Interface* self); + /// Initialize AM service void Init(); diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index c6fc81bc3..f40a87cb4 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -9,11 +9,19 @@ namespace Service { namespace AM { -// Empty arrays are illegal -- commented out until an entry is added. -//const Interface::FunctionInfo FunctionTable[] = { }; +const Interface::FunctionInfo FunctionTable[] = { + {0x100100C0, GetNumContentInfos, "GetNumContentInfos"}, + {0x10020104, nullptr, "FindContentInfos"}, + {0x10030142, nullptr, "ListContentInfos"}, + {0x10040102, nullptr, "DeleteContents"}, + {0x10050084, nullptr, "GetDataTitleInfos"}, + {0x10070102, nullptr, "ListDataTitleTicketInfos"}, + {0x100900C0, nullptr, "IsDataTitleInUse"}, + {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"}, +}; AM_APP_Interface::AM_APP_Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace AM diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b454a2709..7b6ab4ce0 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -6,6 +6,7 @@ #include "common/file_util.h" #include "common/logging/log.h" +#include "core/hle/applets/applet.h" #include "core/hle/service/service.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_a.h" @@ -34,12 +35,21 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; static Kernel::SharedPtr<Kernel::Mutex> lock; static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event -static Kernel::SharedPtr<Kernel::Event> start_event; ///< APT start event +static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event static std::vector<u8> shared_font; static u32 cpu_percent; ///< CPU time available to the running application +/// Parameter data to be returned in the next call to Glance/ReceiveParameter +static MessageParameter next_parameter; + +void SendParameter(const MessageParameter& parameter) { + next_parameter = parameter; + // Signal the event to let the application know that a new parameter is ready to be read + parameter_event->Signal(); +} + void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; @@ -47,18 +57,18 @@ void Initialize(Service::Interface* self) { cmd_buff[2] = IPC::MoveHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); - start_event->Clear(); + parameter_event->Clear(); ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); lock->Release(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_TRACE(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { @@ -85,9 +95,6 @@ void GetSharedFont(Service::Interface* self) { void NotifyToWait(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; - // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. - start_event->Signal(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } @@ -100,7 +107,7 @@ void GetLockHandle(Service::Interface* self) { // Not sure what these parameters are used for, but retail apps check that they are 0 after // GetLockHandle has been called. - cmd_buff[2] = 0; + cmd_buff[2] = 0; // Applet Attributes, this value is passed to Enable. cmd_buff[3] = 0; cmd_buff[4] = 0; @@ -110,9 +117,10 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? + u32 attributes = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); + parameter_event->Signal(); // Let the application know that it has been started + LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); } void GetAppletManInfo(Service::Interface* self) { @@ -121,8 +129,8 @@ void GetAppletManInfo(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; cmd_buff[3] = 0; - cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID - cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly + cmd_buff[4] = static_cast<u32>(AppletId::HomeMenu); // Home menu AppID + cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } @@ -131,7 +139,13 @@ void IsRegistered(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 1; // Set to registered + /// TODO(Subv): It is currently unknown what this value (0x400) means, + /// but i believe it is used as a global "LibraryApplet" id, to verify if there's + /// any LibApplet currently running. This is not verified. + if (app_id != 0x400) + cmd_buff[2] = 1; // Set to registered + else + cmd_buff[2] = 0; // Set to not registered LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); } @@ -145,50 +159,82 @@ void InquireNotification(Service::Interface* self) { void SendParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 src_app_id = cmd_buff[1]; - u32 dst_app_id = cmd_buff[2]; - u32 signal_type = cmd_buff[3]; - u32 buffer_size = cmd_buff[4]; - u32 value = cmd_buff[5]; - u32 handle = cmd_buff[6]; - u32 size = cmd_buff[7]; - u32 in_param_buffer_ptr = cmd_buff[8]; + u32 src_app_id = cmd_buff[1]; + u32 dst_app_id = cmd_buff[2]; + u32 signal_type = cmd_buff[3]; + u32 buffer_size = cmd_buff[4]; + u32 value = cmd_buff[5]; + u32 handle = cmd_buff[6]; + u32 size = cmd_buff[7]; + u32 buffer = cmd_buff[8]; + + std::shared_ptr<HLE::Applets::Applet> dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); + + if (dest_applet == nullptr) { + LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + MessageParameter param; + param.buffer_size = buffer_size; + param.destination_id = dst_app_id; + param.sender_id = src_app_id; + param.object = Kernel::g_handle_table.GetGeneric(handle); + param.signal = signal_type; + param.data = Memory::GetPointer(buffer); + + cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", - src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr); + src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer); } void ReceiveParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; + + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void CancelParameter(Service::Interface* self) { @@ -240,7 +286,7 @@ void AppletUtility(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // These are from 3dbrew - I'm not really sure what they're used for. - u32 unk = cmd_buff[1]; + u32 command = cmd_buff[1]; u32 buffer1_size = cmd_buff[2]; u32 buffer2_size = cmd_buff[3]; u32 buffer1_addr = cmd_buff[5]; @@ -248,8 +294,8 @@ void AppletUtility(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " - "buffer1_addr=0x%08X, buffer2_addr=0x%08X", unk, buffer1_size, buffer2_size, + LOG_WARNING(Service_APT, "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " + "buffer1_addr=0x%08X, buffer2_addr=0x%08X", command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); } @@ -281,11 +327,41 @@ void GetAppCpuTimeLimit(Service::Interface* self) { LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); } +void PrepareToStartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); +} + +void StartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id); + + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + + if (applet == nullptr) { + LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } + + AppletStartupParameter parameter; + parameter.buffer_size = cmd_buff[2]; + parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); + parameter.data = Memory::GetPointer(cmd_buff[6]); + + cmd_buff[1] = applet->Start(parameter).raw; +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); AddService(new APT_U_Interface); + HLE::Applets::Init(); + // Load the shared system font (if available). // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided @@ -318,7 +394,10 @@ void Init() { // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); - start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + parameter_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + + next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); + next_parameter.destination_id = 0x300; } void Shutdown() { @@ -326,7 +405,11 @@ void Shutdown() { shared_font_mem = nullptr; lock = nullptr; notification_event = nullptr; - start_event = nullptr; + parameter_event = nullptr; + + next_parameter.object = nullptr; + + HLE::Applets::Shutdown(); } } // namespace APT diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a03e1712a..72972d05b 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -4,13 +4,33 @@ #pragma once -#include <array> -#include "core/hle/result.h" -#include "core/hle/service/service.h" +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" namespace Service { + +class Interface; + namespace APT { +/// Holds information about the parameters used in Send/Glance/ReceiveParameter +struct MessageParameter { + u32 sender_id = 0; + u32 destination_id = 0; + u32 signal = 0; + u32 buffer_size = 0; + Kernel::SharedPtr<Kernel::Object> object = nullptr; + u8* data = nullptr; +}; + +/// Holds information about the parameters used in StartLibraryApplet +struct AppletStartupParameter { + u32 buffer_size = 0; + Kernel::SharedPtr<Kernel::Object> object = nullptr; + u8* data = nullptr; +}; + /// Signals used by APT functions enum class SignalType : u32 { None = 0x0, @@ -23,7 +43,7 @@ enum class SignalType : u32 { }; /// App Id's used by APT functions -enum class AppID : u32 { +enum class AppletId : u32 { HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -45,6 +65,9 @@ enum class AppID : u32 { SoftwareKeyboard2 = 0x401, }; +/// Send a parameter to the currently-running application, which will read it via ReceiveParameter +void SendParameter(const MessageParameter& parameter); + /** * APT::Initialize service function * Service function that initializes the APT process for the running application @@ -249,6 +272,33 @@ void SetAppCpuTimeLimit(Service::Interface* self); */ void GetAppCpuTimeLimit(Service::Interface* self); +/** + * APT::PrepareToStartLibraryApplet service function + * Inputs: + * 0 : Command header [0x00180040] + * 1 : Id of the applet to start + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void PrepareToStartLibraryApplet(Service::Interface* self); + +/** + * APT::StartLibraryApplet service function + * Inputs: + * 0 : Command header [0x001E0084] + * 1 : Id of the applet to start + * 2 : Buffer size + * 3 : Always 0? + * 4 : Handle passed to the applet + * 5 : (Size << 14) | 2 + * 6 : Input buffer virtual address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void StartLibraryApplet(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 864934245..88de339f9 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -10,19 +10,24 @@ namespace Service { namespace APT { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle?"}, - {0x00020080, Initialize, "Initialize?"}, - {0x00030040, Enable, "Enable?"}, - {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, - {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, - {0x000E0080, GlanceParameter, "GlanceParameter?"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, - {0x00430040, NotifyToWait, "NotifyToWait?"}, - {0x00440000, GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, AppletUtility, "AppletUtility?"}, - {0x00550040, nullptr, "WriteInputToNsState?"}, + {0x00010040, GetLockHandle, "GetLockHandle?"}, + {0x00020080, Initialize, "Initialize?"}, + {0x00030040, Enable, "Enable?"}, + {0x00040040, nullptr, "Finalize?"}, + {0x00050040, nullptr, "GetAppletManInfo?"}, + {0x00060040, nullptr, "GetAppletInfo?"}, + {0x00090040, IsRegistered, "IsRegistered"}, + {0x000C0104, SendParameter, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, CancelParameter, "CancelParameter"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x00430040, NotifyToWait, "NotifyToWait?"}, + {0x00440000, GetSharedFont, "GetSharedFont?"}, + {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x00550040, nullptr, "WriteInputToNsState?"}, }; APT_A_Interface::APT_A_Interface() { diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d006b5930..b724cd72b 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -35,13 +35,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00150140, nullptr, "PrepareToStartApplication"}, {0x00160040, nullptr, "PreloadLibraryApplet"}, {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, {0x00210000, nullptr, "OrderToCloseApplication"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index af4adba84..a329514a6 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -11,7 +11,9 @@ namespace CFG { const Interface::FunctionInfo FunctionTable[] = { {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, - {0x00020000, nullptr, "SecureInfoGetRegion"}, + {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, + {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, + {0x00050000, GetSystemModel, "GetSystemModel"}, {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, {0x04020082, nullptr, "SetConfigInfoBlk4"}, {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index fafb43a2f..a8cb15d60 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -310,4 +310,9 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + semaphore_event = nullptr; + interrupt_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index fa13bfb7c..b6f611db5 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,6 +16,7 @@ namespace DSP_DSP { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "dsp::DSP"; diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 439c7282e..3a5897d06 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -10,15 +10,26 @@ namespace Service { namespace FRD { const Interface::FunctionInfo FunctionTable[] = { + {0x00010000, nullptr, "HasLoggedIn"}, + {0x00030000, nullptr, "Login"}, + {0x00040000, nullptr, "Logout"}, {0x00050000, nullptr, "GetFriendKey"}, {0x00080000, nullptr, "GetMyPresence"}, + {0x00090000, nullptr, "GetMyScreenName"}, {0x00100040, nullptr, "GetPassword"}, + {0x00110080, nullptr, "GetFriendKeyList"}, {0x00190042, nullptr, "GetFriendFavoriteGame"}, {0x001A00C4, nullptr, "GetFriendInfo"}, {0x001B0080, nullptr, "IsOnFriendList"}, {0x001C0042, nullptr, "DecodeLocalFriendCode"}, {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, - {0x00320042, nullptr, "SetClientSdkVersion"} + {0x00230000, nullptr, "GetLastResponseResult"}, + {0x00270040, nullptr, "ResultToErrorCode"}, + {0x00280244, nullptr, "RequestGameAuthentication"}, + {0x00290000, nullptr, "GetGameAuthenticationData"}, + {0x002A0204, nullptr, "RequestServiceLocator"}, + {0x002B0000, nullptr, "GetServiceLocatorData"}, + {0x00320042, nullptr, "SetClientSdkVersion"}, }; FRD_U_Interface::FRD_U_Interface() { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 4e275cb13..6c0df67c3 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -2,29 +2,35 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstddef> +#include <system_error> +#include <type_traits> #include <memory> #include <unordered_map> +#include <utility> #include <boost/container/flat_map.hpp> +#include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/make_unique.h" -#include "common/math_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" -#include "core/file_sys/archive_romfs.h" #include "core/file_sys/archive_savedata.h" #include "core/file_sys/archive_savedatacheck.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" +#include "core/file_sys/file_backend.h" +#include "core/hle/hle.h" #include "core/hle/service/service.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" #include "core/hle/result.h" +#include "core/memory.h" // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 @@ -110,7 +116,7 @@ ResultVal<bool> File::SyncRequest() { u32 address = cmd_buff[6]; LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); - cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush, Memory::GetPointer(address))); + cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address))); break; } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 357b6b096..f61125953 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -4,22 +4,25 @@ #pragma once +#include <memory> +#include <string> + #include "common/common_types.h" #include "core/file_sys/archive_backend.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" #include "core/hle/result.h" +namespace FileSys { +class DirectoryBackend; +class FileBackend; +} + /// The unique system identifier hash, also known as ID0 extern const std::string SYSTEM_ID; /// The scrambled SD card CID, also known as ID1 extern const std::string SDCARD_ID; -namespace Kernel { - class Session; -} - namespace Service { namespace FS { diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 0ad44e55e..ae52083f9 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -115,7 +115,8 @@ static void OpenFileDirectly(Service::Interface* self) { ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); if (archive_handle.Failed()) { - LOG_ERROR(Service_FS, "failed to get a handle for archive"); + LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + archive_id, archive_path.DebugStr().c_str()); cmd_buff[1] = archive_handle.Code().raw; cmd_buff[3] = 0; return; @@ -128,7 +129,8 @@ static void OpenFileDirectly(Service::Interface* self) { cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); } else { cmd_buff[3] = 0; - LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); + LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%d", + file_path.DebugStr().c_str(), mode.hex, attributes); } } @@ -347,7 +349,8 @@ static void OpenDirectory(Service::Interface* self) { if (dir_res.Succeeded()) { cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); } else { - LOG_ERROR(Service_FS, "failed to get a handle for directory"); + LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", + dirname_type, dirname_size, dir_path.DebugStr().c_str()); } } @@ -382,7 +385,8 @@ static void OpenArchive(Service::Interface* self) { cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; } else { cmd_buff[2] = cmd_buff[3] = 0; - LOG_ERROR(Service_FS, "failed to get a handle for archive"); + LOG_ERROR(Service_FS, "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + archive_id, archive_path.DebugStr().c_str()); } } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 4b0b4229d..e93c1b436 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -9,14 +9,17 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" -#include "gsp_gpu.h" #include "core/hw/hw.h" #include "core/hw/gpu.h" #include "core/hw/lcd.h" #include "video_core/gpu_debugger.h" +#include "video_core/debug_utils/debug_utils.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" +#include "gsp_gpu.h" + // Main graphics debugger object - TODO: Here is probably not the best place for this GraphicsDebugger g_debugger; @@ -40,7 +43,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) { return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); } -static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); // For each thread there are two FrameBufferUpdate fields @@ -203,7 +206,7 @@ static void ReadHWRegs(Service::Interface* self) { } } -static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); @@ -224,6 +227,9 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { &info.format); WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), 4, &info.shown_fb); + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); } /** @@ -347,7 +353,7 @@ void SignalInterrupt(InterruptId interrupt_id) { /// Executes the next GSP command static void ExecuteCommand(const Command& command, u32 thread_id) { // Utility function to convert register ID to address - auto WriteGPURegister = [](u32 id, u32 data) { + static auto WriteGPURegister = [](u32 id, u32 data) { GPU::Write<u32>(0x1EF00000 + 4 * id, data); }; @@ -389,19 +395,24 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::SET_MEMORY_FILL: { auto& params = command.memory_fill; - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), - Memory::VirtualToPhysicalAddress(params.start1) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), - Memory::VirtualToPhysicalAddress(params.end1) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); - - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), - Memory::VirtualToPhysicalAddress(params.start2) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), - Memory::VirtualToPhysicalAddress(params.end2) >> 3); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); - WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); + + if (params.start1 != 0) { + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), + Memory::VirtualToPhysicalAddress(params.start1) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), + Memory::VirtualToPhysicalAddress(params.end1) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); + } + + if (params.start2 != 0) { + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), + Memory::VirtualToPhysicalAddress(params.start2) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), + Memory::VirtualToPhysicalAddress(params.end2) >> 3); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); + WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); + } break; } @@ -446,6 +457,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { default: LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); } + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::GSPCommandProcessed, (void*)&command); } /** @@ -582,7 +596,7 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); - g_interrupt_event = 0; + g_interrupt_event = nullptr; using Kernel::MemoryPermission; g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, @@ -591,4 +605,9 @@ Interface::Interface() { g_thread_id = 0; } +Interface::~Interface() { + g_interrupt_event = nullptr; + g_shared_memory = nullptr; +} + } // namespace diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index a435d418a..c89d0a467 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -5,8 +5,11 @@ #pragma once #include <cstddef> +#include <string> #include "common/bit_field.h" +#include "common/common_types.h" + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -158,6 +161,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "gsp::Gpu"; @@ -170,4 +174,14 @@ public: */ void SignalInterrupt(InterruptId interrupt_id); +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); + +/** + * Retrieves the framebuffer info stored in the GSP shared memory for the + * specified screen index and thread id. + * @param thread_id GSP thread id of the process that accesses the structure that we are requesting. + * @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1). + * @returns FramebufferUpdate Information about the specified framebuffer. + */ +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); } // namespace diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index c7c1bb5ab..70caa7d80 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "common/emu_window.h" #include "core/hle/service/service.h" #include "core/hle/service/hid/hid.h" diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 68e2bcee0..d50d479f8 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -6,16 +6,18 @@ #include <array> -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" -#include "common/bit_field.h" +#ifndef _MSC_VER +#include <cstddef> +#endif -namespace Kernel { - class SharedMemory; - class Event; -} +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" namespace Service { + +class Interface; + namespace HID { /** diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 25b01860e..18b22956f 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp @@ -125,4 +125,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + handle_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h index 82abdff28..0ced2359c 100644 --- a/src/core/hle/service/nwm_uds.h +++ b/src/core/hle/service/nwm_uds.h @@ -16,6 +16,7 @@ namespace NWM_UDS { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "nwm::UDS"; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index d681cc3dc..0de0b13a3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -54,7 +54,7 @@ static std::string MakeFunctionString(const char* name, const char* port_name, c std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); for (int i = 1; i <= num_params; ++i) { - function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); + function_string += Common::StringFromFormat(", cmd_buff[%i]=0x%X", i, cmd_buff[i]); } return function_string; } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 77bfb9ff1..f31135212 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -4,6 +4,7 @@ #pragma once +#include <cstddef> #include <string> #include <unordered_map> @@ -11,8 +12,8 @@ #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" +#include "core/hle/result.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 1e0f5df9b..d0e166fdf 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -2,40 +2,47 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstring> +#include <unordered_map> + +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/common_types.h" #include "common/logging/log.h" -#include "common/platform.h" - -#if EMU_PLATFORM == PLATFORM_WINDOWS -#include <winsock2.h> -#include <ws2tcpip.h> - -// MinGW does not define several errno constants -#ifndef _MSC_VER -#define EBADMSG 104 -#define ENODATA 120 -#define ENOMSG 122 -#define ENOSR 124 -#define ENOSTR 125 -#define ETIME 137 -#define EIDRM 2001 -#define ENOLINK 2002 -#endif // _MSC_VER +#include "common/scope_exit.h" +#include "core/hle/kernel/session.h" +#include "core/hle/result.h" +#include "core/hle/service/soc_u.h" +#include "core/memory.h" + +#ifdef _WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + + // MinGW does not define several errno constants + #ifndef _MSC_VER + #define EBADMSG 104 + #define ENODATA 120 + #define ENOMSG 122 + #define ENOSR 124 + #define ENOSTR 125 + #define ETIME 137 + #define EIDRM 2001 + #define ENOLINK 2002 + #endif // _MSC_VER #else -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <poll.h> + #include <cerrno> + #include <fcntl.h> + #include <netinet/in.h> + #include <netdb.h> + #include <poll.h> + #include <sys/socket.h> + #include <unistd.h> #endif -#include "common/scope_exit.h" -#include "core/hle/hle.h" -#include "core/hle/service/soc_u.h" -#include <unordered_map> - -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 # define WSAEAGAIN WSAEWOULDBLOCK # define WSAEMULTIHOP -1 // Invalid dummy value # define ERRNO(x) WSA##x @@ -328,6 +335,7 @@ static void Socket(Service::Interface* self) { if ((s32)socket_handle == SOCKET_ERROR_VALUE) result = TranslateError(GET_ERRNO); + cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); cmd_buffer[1] = result; cmd_buffer[2] = socket_handle; } @@ -351,8 +359,9 @@ static void Bind(Service::Interface* self) { if (res != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = res; + cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = res; } static void Fcntl(Service::Interface* self) { @@ -369,7 +378,7 @@ static void Fcntl(Service::Interface* self) { }); if (ctr_cmd == 3) { // F_GETFL -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 posix_ret = 0; auto iter = open_sockets.find(socket_handle); if (iter != open_sockets.end() && iter->second.blocking == false) @@ -386,7 +395,7 @@ static void Fcntl(Service::Interface* self) { posix_ret |= 4; // O_NONBLOCK #endif } else if (ctr_cmd == 4) { // F_SETFL -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); if (ret == SOCKET_ERROR_VALUE) { @@ -434,8 +443,9 @@ static void Listen(Service::Interface* self) { if (ret != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = ret; + cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = ret; } static void Accept(Service::Interface* self) { @@ -460,8 +470,10 @@ static void Accept(Service::Interface* self) { Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); } - cmd_buffer[2] = ret; + cmd_buffer[0] = IPC::MakeHeader(4, 2, 2); cmd_buffer[1] = result; + cmd_buffer[2] = ret; + cmd_buffer[3] = IPC::StaticBufferDesc(static_cast<u32>(max_addr_len), 0); } static void GetHostId(Service::Interface* self) { @@ -669,26 +681,29 @@ static void Connect(Service::Interface* self) { int result = 0; if (ret != 0) result = TranslateError(GET_ERRNO); - cmd_buffer[2] = ret; + + cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); cmd_buffer[1] = result; + cmd_buffer[2] = ret; } static void InitializeSockets(Service::Interface* self) { // TODO(Subv): Implement -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); #endif u32* cmd_buffer = Kernel::GetCommandBuffer(); - cmd_buffer[1] = 0; + cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); + cmd_buffer[1] = RESULT_SUCCESS.raw; } static void ShutdownSockets(Service::Interface* self) { // TODO(Subv): Implement CleanupSockets(); -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSACleanup(); #endif @@ -739,7 +754,7 @@ Interface::Interface() { Interface::~Interface() { CleanupSockets(); -#if EMU_PLATFORM == PLATFORM_WINDOWS +#ifdef _WIN32 WSACleanup(); #endif } diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 483b3111b..a091f597c 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 6c49fa6cf..3b8c7c0e4 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -68,4 +68,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + event_handle = nullptr; +} + } // namespace diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 653aba5cb..96c89b025 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h @@ -13,6 +13,7 @@ namespace SRV { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "srv:"; diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index ac1967da8..6e7dafaad 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -12,6 +12,7 @@ #include "core/hw/y2r.h" #include "core/mem_map.h" +#include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -409,4 +410,8 @@ Interface::Interface() { Register(FunctionTable); } +Interface::~Interface() { + completion_event = nullptr; +} + } // namespace diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 7df47fcb9..3965a5545 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -5,9 +5,11 @@ #pragma once #include <array> +#include <string> #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -110,6 +112,7 @@ struct ConversionConfiguration { class Interface : public Service::Interface { public: Interface(); + ~Interface() override; std::string GetPortName() const override { return "y2r:u"; diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index 4014eee98..26d87c7e2 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -4,12 +4,6 @@ #include <cstring> -#include "common/common_types.h" -#include "common/common_funcs.h" - -#include "core/core.h" -#include "core/memory.h" -#include "core/hle/config_mem.h" #include "core/hle/shared_page.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index fd2ab66a2..db6a5340b 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -10,9 +10,12 @@ * write access, according to 3dbrew; this is not emulated) */ +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/memory.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace SharedPage { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index cc414743a..bb64fdfb7 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -22,6 +22,7 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" +#include "core/hle/kernel/vm_manager.h" #include "core/hle/function_wrappers.h" #include "core/hle/result.h" @@ -529,12 +530,33 @@ static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) return RESULT_SUCCESS; } -/// Query memory -static ResultCode QueryMemory(void* info, void* out, u32 addr) { - LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); +/// Query process memory +static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, Handle process_handle, u32 addr) { + using Kernel::Process; + Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); + if (process == nullptr) + return ERR_INVALID_HANDLE; + + auto vma = process->address_space->FindVMA(addr); + + if (vma == process->address_space->vma_map.end()) + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + + memory_info->base_address = vma->second.base; + memory_info->permission = static_cast<u32>(vma->second.permissions); + memory_info->size = vma->second.size; + memory_info->state = static_cast<u32>(vma->second.meminfo_state); + + page_info->flags = 0; + LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=0x%08X", process_handle, addr); return RESULT_SUCCESS; } +/// Query memory +static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) { + return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr); +} + /// Create an event static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { using Kernel::Event; @@ -806,13 +828,12 @@ static const FunctionDef SVC_Table[] = { {0x7A, nullptr, "AddCodeSegment"}, {0x7B, nullptr, "Backdoor"}, {0x7C, nullptr, "KernelSetState"}, - {0x7D, nullptr, "QueryProcessMemory"}, + {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, }; Common::Profiling::TimingCategory profiler_svc("SVC Calls"); -static const FunctionDef* GetSVCInfo(u32 opcode) { - u32 func_num = opcode & 0xFFFFFF; // 8 bits +static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= ARRAY_SIZE(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); return nullptr; @@ -820,10 +841,10 @@ static const FunctionDef* GetSVCInfo(u32 opcode) { return &SVC_Table[func_num]; } -void CallSVC(u32 opcode) { +void CallSVC(u32 immediate) { Common::Profiling::ScopeTimer timer_svc(profiler_svc); - const FunctionDef *info = GetSVCInfo(opcode); + const FunctionDef* info = GetSVCInfo(immediate); if (info) { if (info->func) { info->func(); diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index 4389aa73d..12de9ffbe 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h @@ -41,6 +41,6 @@ enum ArbitrationType { namespace SVC { -void CallSVC(u32 opcode); +void CallSVC(u32 immediate); } // namespace diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7471def57..3ccbc03b2 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -2,17 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <type_traits> + #include "common/color.h" #include "common/common_types.h" - -#include "core/arm/arm_interface.h" +#include "common/logging/log.h" +#include "common/vector_math.h" #include "core/settings.h" -#include "core/core.h" #include "core/memory.h" #include "core/core_timing.h" -#include "core/hle/hle.h" #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/hid/hid.h" @@ -20,10 +21,17 @@ #include "core/hw/hw.h" #include "core/hw/gpu.h" +#include "core/tracer/recorder.h" + #include "video_core/command_processor.h" +#include "video_core/hwrasterizer_base.h" +#include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" +#include "video_core/debug_utils/debug_utils.h" + + namespace GPU { Regs g_regs; @@ -53,6 +61,29 @@ inline void Read(T &var, const u32 raw_addr) { var = g_regs[addr / 4]; } +static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_pixel) { + switch (input_format) { + case Regs::PixelFormat::RGBA8: + return Color::DecodeRGBA8(src_pixel); + + case Regs::PixelFormat::RGB8: + return Color::DecodeRGB8(src_pixel); + + case Regs::PixelFormat::RGB565: + return Color::DecodeRGB565(src_pixel); + + case Regs::PixelFormat::RGB5A1: + return Color::DecodeRGB5A1(src_pixel); + + case Regs::PixelFormat::RGBA4: + return Color::DecodeRGBA4(src_pixel); + + default: + LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); + return {0, 0, 0, 0}; + } +} + template <typename T> inline void Write(u32 addr, const T data) { addr -= HW::VADDR_GPU; @@ -75,39 +106,43 @@ inline void Write(u32 addr, const T data) { const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger)); auto& config = g_regs.memory_fill_config[is_second_filler]; - if (config.address_start && config.trigger) { - u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); - u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); - - if (config.fill_24bit) { - // fill with 24-bit values - for (u8* ptr = start; ptr < end; ptr += 3) { - ptr[0] = config.value_24bit_r; - ptr[1] = config.value_24bit_g; - ptr[2] = config.value_24bit_b; + if (config.trigger) { + if (config.address_start) { // Some games pass invalid values here + u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); + u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); + + if (config.fill_24bit) { + // fill with 24-bit values + for (u8* ptr = start; ptr < end; ptr += 3) { + ptr[0] = config.value_24bit_r; + ptr[1] = config.value_24bit_g; + ptr[2] = config.value_24bit_b; + } + } else if (config.fill_32bit) { + // fill with 32-bit values + for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) + *ptr = config.value_32bit; + } else { + // fill with 16-bit values + for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) + *ptr = config.value_16bit; } - } else if (config.fill_32bit) { - // fill with 32-bit values - for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) - *ptr = config.value_32bit; - } else { - // fill with 16-bit values - for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) - *ptr = config.value_16bit; - } - LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); + LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); - config.trigger = 0; - config.finished = 1; + if (!is_second_filler) { + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); + } else { + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); + } - if (!is_second_filler) { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); - } else { - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); + VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + // Reset "trigger" flag and set the "finish" flag + // NOTE: This was confirmed to happen on hardware even if "address_start" is zero. + config.trigger = 0; + config.finished = 1; } break; } @@ -116,6 +151,10 @@ inline void Write(u32 addr, const T data) { { const auto& config = g_regs.display_transfer_config; if (config.trigger & 1) { + + if (Pica::g_debug_context) + Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); @@ -125,11 +164,18 @@ inline void Write(u32 addr, const T data) { break; } - unsigned horizontal_scale = (config.scaling != config.NoScale) ? 2 : 1; - unsigned vertical_scale = (config.scaling == config.ScaleXY) ? 2 : 1; + if (config.output_tiled && + (config.scaling == config.ScaleXY || config.scaling == config.ScaleX)) { + LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); + UNIMPLEMENTED(); + break; + } - u32 output_width = config.output_width / horizontal_scale; - u32 output_height = config.output_height / vertical_scale; + bool horizontal_scale = config.scaling != config.NoScale; + bool vertical_scale = config.scaling == config.ScaleXY; + + u32 output_width = config.output_width >> horizontal_scale; + u32 output_height = config.output_height >> vertical_scale; u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); @@ -153,16 +199,14 @@ inline void Write(u32 addr, const T data) { break; } - // TODO(Subv): Implement the box filter when scaling is enabled - // right now we're just skipping the extra pixels. for (u32 y = 0; y < output_height; ++y) { for (u32 x = 0; x < output_width; ++x) { - Math::Vec4<u8> src_color = { 0, 0, 0, 0 }; + Math::Vec4<u8> src_color; // Calculate the [x,y] position of the input image // based on the current output position and the scale - u32 input_x = x * horizontal_scale; - u32 input_y = y * vertical_scale; + u32 input_x = x << horizontal_scale; + u32 input_y = y << vertical_scale; if (config.flip_vertically) { // Flip the y value of the output data, @@ -177,46 +221,49 @@ inline void Write(u32 addr, const T data) { u32 dst_offset; if (config.output_tiled) { - // Interpret the input as linear and the output as tiled - u32 coarse_y = y & ~7; - u32 stride = output_width * dst_bytes_per_pixel; - - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + if (!config.dont_swizzle) { + // Interpret the input as linear and the output as tiled + u32 coarse_y = y & ~7; + u32 stride = output_width * dst_bytes_per_pixel; + + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Both input and output are linear + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } } else { - // Interpret the input as tiled and the output as linear - u32 coarse_y = input_y & ~7; - u32 stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + if (!config.dont_swizzle) { + // Interpret the input as tiled and the output as linear + u32 coarse_y = input_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } else { + // Both input and output are tiled + u32 out_coarse_y = y & ~7; + u32 out_stride = output_width * dst_bytes_per_pixel; + + u32 in_coarse_y = input_y & ~7; + u32 in_stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + } } const u8* src_pixel = src_pointer + src_offset; - switch (config.input_format) { - case Regs::PixelFormat::RGBA8: - src_color = Color::DecodeRGBA8(src_pixel); - break; - - case Regs::PixelFormat::RGB8: - src_color = Color::DecodeRGB8(src_pixel); - break; - - case Regs::PixelFormat::RGB565: - src_color = Color::DecodeRGB565(src_pixel); - break; - - case Regs::PixelFormat::RGB5A1: - src_color = Color::DecodeRGB5A1(src_pixel); - break; - - case Regs::PixelFormat::RGBA4: - src_color = Color::DecodeRGBA4(src_pixel); - break; - - default: - LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value()); - break; + src_color = DecodePixel(config.input_format, src_pixel); + if (config.scaling == config.ScaleX) { + Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); + src_color = ((src_color + pixel) / 2).Cast<u8>(); + } else if (config.scaling == config.ScaleXY) { + Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); + Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); + Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); + src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); } u8* dst_pixel = dst_pointer + dst_offset; @@ -254,6 +301,7 @@ inline void Write(u32 addr, const T data) { config.GetPhysicalOutputAddress(), output_width, output_height, config.output_format.Value(), config.flags); + g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); @@ -268,7 +316,14 @@ inline void Write(u32 addr, const T data) { if (config.trigger & 1) { u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); + + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size * sizeof(u32), config.GetPhysicalAddress()); + } + Pica::CommandProcessor::ProcessCommandList(buffer, config.size); + + g_regs.command_processor_config.trigger = 0; } break; } @@ -276,6 +331,13 @@ inline void Write(u32 addr, const T data) { default: break; } + + // Notify tracer about the register write + // This is happening *after* handling the write to make sure we properly catch all memory reads. + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + // addr + GPU VBase - IO VBase + IO PBase + Pica::g_debug_context->recorder->RegisterWritten<T>(addr + 0x1EF00000 - 0x1EC00000 + 0x10100000, data); + } } // Explicitly instantiate template functions because we aren't defining this in the header: diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 699bcd2a5..daad506fe 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <type_traits> #include "common/assert.h" #include "common/bit_field.h" @@ -202,6 +203,7 @@ struct Regs { BitField< 0, 1, u32> flip_vertically; // flips input data vertically BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing + BitField< 5, 1, u32> dont_swizzle; BitField< 8, 3, PixelFormat> input_format; BitField<12, 3, PixelFormat> output_format; diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index c7006a498..b5fdbf9c1 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -15,6 +15,21 @@ template <typename T> inline void Read(T &var, const u32 addr) { switch (addr & 0xFFFFF000) { case VADDR_GPU: + case VADDR_GPU + 0x1000: + case VADDR_GPU + 0x2000: + case VADDR_GPU + 0x3000: + case VADDR_GPU + 0x4000: + case VADDR_GPU + 0x5000: + case VADDR_GPU + 0x6000: + case VADDR_GPU + 0x7000: + case VADDR_GPU + 0x8000: + case VADDR_GPU + 0x9000: + case VADDR_GPU + 0xA000: + case VADDR_GPU + 0xB000: + case VADDR_GPU + 0xC000: + case VADDR_GPU + 0xD000: + case VADDR_GPU + 0xE000: + case VADDR_GPU + 0xF000: GPU::Read(var, addr); break; case VADDR_LCD: @@ -29,6 +44,21 @@ template <typename T> inline void Write(u32 addr, const T data) { switch (addr & 0xFFFFF000) { case VADDR_GPU: + case VADDR_GPU + 0x1000: + case VADDR_GPU + 0x2000: + case VADDR_GPU + 0x3000: + case VADDR_GPU + 0x4000: + case VADDR_GPU + 0x5000: + case VADDR_GPU + 0x6000: + case VADDR_GPU + 0x7000: + case VADDR_GPU + 0x8000: + case VADDR_GPU + 0x9000: + case VADDR_GPU + 0xA000: + case VADDR_GPU + 0xB000: + case VADDR_GPU + 0xC000: + case VADDR_GPU + 0xD000: + case VADDR_GPU + 0xE000: + case VADDR_GPU + 0xF000: GPU::Write(addr, data); break; case VADDR_LCD: diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 963c8d981..6f93709e3 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -7,11 +7,12 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "core/arm/arm_interface.h" -#include "core/hle/hle.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" +#include "core/tracer/recorder.h" +#include "video_core/debug_utils/debug_utils.h" + namespace LCD { Regs g_regs; @@ -42,6 +43,13 @@ inline void Write(u32 addr, const T data) { } g_regs[index] = static_cast<u32>(data); + + // Notify tracer about the register write + // This is happening *after* handling the write to make sure we properly catch all memory reads. + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + // addr + GPU VBase - IO VBase + IO PBase + Pica::g_debug_context->recorder->RegisterWritten<T>(addr + HW::VADDR_LCD - 0x1EC00000 + 0x10100000, data); + } } // Explicitly instantiate template functions because we aren't defining this in the header: diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 8631eb201..bcce6d8cf 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -5,6 +5,7 @@ #pragma once #include <cstddef> +#include <type_traits> #include "common/bit_field.h" #include "common/common_funcs.h" diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp index 5b7fb39e1..f80e26ecd 100644 --- a/src/core/hw/y2r.cpp +++ b/src/core/hw/y2r.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <array> -#include <numeric> +#include <cstddef> +#include <memory> #include "common/assert.h" #include "common/color.h" @@ -109,7 +111,7 @@ static void SendData(const u32* input, ConversionBuffer& buf, int amount_of_data while (output < unit_end) { u32 color = *input++; Math::Vec4<u8> col_vec{ - (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF, alpha, + (u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha }; switch (output_format) { diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 14aeebebb..530837d08 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -19,7 +19,7 @@ namespace Loader { -/** +/* * File layout: * - File header * - Code, rodata and data relocation table headers @@ -39,13 +39,16 @@ namespace Loader { * The entrypoint is always the start of the code segment. * The BSS section must be cleared manually by the application. */ + enum THREEDSX_Error { ERROR_NONE = 0, ERROR_READ = 1, ERROR_FILE = 2, ERROR_ALLOC = 3 }; + static const u32 RELOCBUFSIZE = 512; +static const unsigned int NUM_SEGMENTS = 3; // File header #pragma pack(1) @@ -98,7 +101,10 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets) return loadinfo->seg_addrs[2] + addr - offsets[1]; } -static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) +using Kernel::SharedPtr; +using Kernel::CodeSet; + +static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, SharedPtr<CodeSet>* out_codeset) { if (!file.IsOpen()) return ERROR_FILE; @@ -116,15 +122,13 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; - u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF; - u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size; - u32 n_reloc_tables = hdr.reloc_hdr_size / 4; - std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables); + u32 n_reloc_tables = hdr.reloc_hdr_size / sizeof(u32); + std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); loadinfo.seg_addrs[0] = base_addr; loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; - loadinfo.seg_ptrs[0] = &all_mem[0]; + loadinfo.seg_ptrs[0] = program_image.data(); loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; @@ -132,10 +136,9 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) file.Seek(hdr.header_size, SEEK_SET); // Read the relocation headers - u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); - - for (unsigned current_segment : {0, 1, 2}) { - size_t size = n_reloc_tables * 4; + std::vector<u32> relocs(n_reloc_tables * NUM_SEGMENTS); + for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { + size_t size = n_reloc_tables * sizeof(u32); if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) return ERROR_READ; } @@ -152,7 +155,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); // Relocate the segments - for (unsigned current_segment : {0, 1, 2}) { + for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; if (current_segment_reloc_table >= 2) { @@ -160,7 +163,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); continue; } - static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; + THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); @@ -179,7 +182,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) pos += table.skip; s32 num_patches = table.patch; while (0 < num_patches && pos < end_pos) { - u32 in_addr = (char*)pos - (char*)&all_mem[0]; + u32 in_addr = (u8*)pos - program_image.data(); u32 addr = TranslateAddr(*pos, &loadinfo, offsets); LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", base_addr + in_addr, addr, current_segment_reloc_table, *pos); @@ -188,7 +191,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) *pos = (addr); break; case 1: - *pos = (addr - in_addr); + *pos = static_cast<u32>(addr - in_addr); break; default: break; //this should never happen @@ -201,14 +204,29 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr) } } - // Write the data - memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); + // Create the CodeSet + SharedPtr<CodeSet> code_set = CodeSet::Create("", 0); + + code_set->code.offset = loadinfo.seg_ptrs[0] - program_image.data(); + code_set->code.addr = loadinfo.seg_addrs[0]; + code_set->code.size = loadinfo.seg_sizes[0]; + + code_set->rodata.offset = loadinfo.seg_ptrs[1] - program_image.data(); + code_set->rodata.addr = loadinfo.seg_addrs[1]; + code_set->rodata.size = loadinfo.seg_sizes[1]; - LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000); - LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000); - LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000); - LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000); + code_set->data.offset = loadinfo.seg_ptrs[2] - program_image.data(); + code_set->data.addr = loadinfo.seg_addrs[2]; + code_set->data.size = loadinfo.seg_sizes[2]; + code_set->entrypoint = code_set->code.addr; + code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + + LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]); + LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]); + LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size); + + *out_codeset = code_set; return ERROR_NONE; } @@ -228,19 +246,22 @@ ResultStatus AppLoader_THREEDSX::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) + return ResultStatus::Error; + + SharedPtr<CodeSet> codeset; + if (Load3DSXFile(file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE) return ResultStatus::Error; + codeset->name = filename; - Kernel::g_current_process = Kernel::Process::Create(filename, 0); + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - Load3DSXFile(*file, Memory::PROCESS_IMAGE_VADDR); - - Kernel::g_current_process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 096b3ec20..a0aa0c533 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -17,7 +17,7 @@ namespace Loader { /// Loads an 3DSX file class AppLoader_THREEDSX final : public AppLoader { public: - AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename) : AppLoader(std::move(file)), filename(std::move(filename)) {} /** diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index f00753a79..5d7264f12 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <string> #include <memory> @@ -10,11 +11,14 @@ #include "common/logging/log.h" #include "common/symbols.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/elf.h" #include "core/memory.h" +using Kernel::SharedPtr; +using Kernel::CodeSet; + //////////////////////////////////////////////////////////////////////////////////////////////////// // ELF Header Constants @@ -96,6 +100,12 @@ enum ElfSectionFlags #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7FFFFFFF +// Segment flags +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 +#define PF_MASKPROC 0xF0000000 + typedef unsigned int Elf32_Addr; typedef unsigned short Elf32_Half; typedef unsigned int Elf32_Off; @@ -192,7 +202,7 @@ public: ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } u32 GetEntryPoint() const { return entryPoint; } u32 GetFlags() const { return (u32)(header->e_flags); } - void LoadInto(u32 vaddr); + SharedPtr<CodeSet> LoadInto(u32 vaddr); bool LoadSymbols(); int GetNumSegments() const { return (int)(header->e_phnum); } @@ -248,7 +258,7 @@ const char *ElfReader::GetSectionName(int section) const { return nullptr; } -void ElfReader::LoadInto(u32 vaddr) { +SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx); // Should we relocate? @@ -263,22 +273,63 @@ void ElfReader::LoadInto(u32 vaddr) { LOG_DEBUG(Loader, "%i segments:", header->e_phnum); // First pass : Get the bits into RAM - u32 segment_addr[32]; u32 base_addr = relocate ? vaddr : 0; - for (unsigned i = 0; i < header->e_phnum; i++) { - Elf32_Phdr* p = segments + i; - LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, + u32 total_image_size = 0; + for (unsigned int i = 0; i < header->e_phnum; ++i) { + Elf32_Phdr* p = &segments[i]; + if (p->p_type == PT_LOAD) { + total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; + } + } + + std::vector<u8> program_image(total_image_size); + size_t current_image_position = 0; + + SharedPtr<CodeSet> codeset = CodeSet::Create("", 0); + + for (unsigned int i = 0; i < header->e_phnum; ++i) { + Elf32_Phdr* p = &segments[i]; + LOG_DEBUG(Loader, "Type: %i Vaddr: %08X Filesz: %8X Memsz: %8X ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); if (p->p_type == PT_LOAD) { - segment_addr[i] = base_addr + p->p_vaddr; - memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); - LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], - p->p_memsz); + CodeSet::Segment* codeset_segment; + u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); + if (permission_flags == (PF_R | PF_X)) { + codeset_segment = &codeset->code; + } else if (permission_flags == (PF_R)) { + codeset_segment = &codeset->rodata; + } else if (permission_flags == (PF_R | PF_W)) { + codeset_segment = &codeset->data; + } else { + LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id %u with flags %X", i, p->p_flags); + continue; + } + + if (codeset_segment->size != 0) { + LOG_ERROR(Loader, "ELF has more than one segment of the same type. Skipping extra segment (id %i)", i); + continue; + } + + u32 segment_addr = base_addr + p->p_vaddr; + u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; + + codeset_segment->offset = current_image_position; + codeset_segment->addr = segment_addr; + codeset_segment->size = aligned_size; + + memcpy(&program_image[current_image_position], GetSegmentPtr(i), p->p_filesz); + current_image_position += aligned_size; } } + + codeset->entrypoint = base_addr + header->e_entry; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + LOG_DEBUG(Loader, "Done loading."); + + return codeset; } SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { @@ -340,29 +391,29 @@ ResultStatus AppLoader_ELF::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) return ResultStatus::Error; // Reset read pointer in case this file has been read before. - file->Seek(0, SEEK_SET); + file.Seek(0, SEEK_SET); - u32 size = static_cast<u32>(file->GetSize()); + size_t size = file.GetSize(); std::unique_ptr<u8[]> buffer(new u8[size]); - if (file->ReadBytes(&buffer[0], size) != size) + if (file.ReadBytes(&buffer[0], size) != size) return ResultStatus::Error; - Kernel::g_current_process = Kernel::Process::Create(filename, 0); + ElfReader elf_reader(&buffer[0]); + SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); + codeset->name = filename; + + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - ElfReader elf_reader(&buffer[0]); - elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); - // TODO: Fill application title - - Kernel::g_current_process->Run(elf_reader.GetEntryPoint(), 48, Kernel::DEFAULT_STACK_SIZE); + Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 32841606a..c6a5ebe99 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -17,7 +17,7 @@ namespace Loader { /// Loads an ELF/AXF file class AppLoader_ELF final : public AppLoader { public: - AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file, std::string filename) + AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) : AppLoader(std::move(file)), filename(std::move(filename)) { } /** diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 8b14edf00..9ef2f8900 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> #include <string> #include "common/logging/log.h" #include "common/make_unique.h" +#include "common/string_util.h" #include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" @@ -88,8 +90,8 @@ static const char* GetFileTypeString(FileType type) { } ResultStatus LoadFile(const std::string& filename) { - std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb")); - if (!file->IsOpen()) { + FileUtil::IOFile file(filename, "rb"); + if (!file.IsOpen()) { LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); return ResultStatus::Error; } @@ -97,7 +99,7 @@ ResultStatus LoadFile(const std::string& filename) { std::string filename_filename, filename_extension; Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); - FileType type = IdentifyFile(*file); + FileType type = IdentifyFile(file); FileType filename_type = GuessFromExtension(filename_extension); if (type != filename_type) { @@ -122,7 +124,7 @@ ResultStatus LoadFile(const std::string& filename) { case FileType::CXI: case FileType::CCI: { - AppLoader_NCCH app_loader(std::move(file)); + AppLoader_NCCH app_loader(std::move(file), filename); // Load application and RomFS if (ResultStatus::Success == app_loader.Load()) { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 87e16fb98..a37d3348c 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -4,12 +4,18 @@ #pragma once +#include <algorithm> +#include <initializer_list> +#include <memory> +#include <string> #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "core/hle/kernel/process.h" +namespace Kernel { +struct AddressMapping; +} //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -46,7 +52,7 @@ static inline u32 MakeMagic(char a, char b, char c, char d) { /// Interface for loading an application class AppLoader : NonCopyable { public: - AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { } + AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { } virtual ~AppLoader() { } /** @@ -60,7 +66,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadCode(std::vector<u8>& buffer) const { + virtual ResultStatus ReadCode(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -69,7 +75,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadIcon(std::vector<u8>& buffer) const { + virtual ResultStatus ReadIcon(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -78,7 +84,7 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadBanner(std::vector<u8>& buffer) const { + virtual ResultStatus ReadBanner(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } @@ -87,22 +93,25 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - virtual ResultStatus ReadLogo(std::vector<u8>& buffer) const { + virtual ResultStatus ReadLogo(std::vector<u8>& buffer) { return ResultStatus::ErrorNotImplemented; } /** * Get the RomFS of the application - * @param buffer Reference to buffer to store data + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * @param romfs_file The file containing the RomFS + * @param offset The offset the romfs begins on + * @param size The size of the romfs * @return ResultStatus result of function */ - virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const { + virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { return ResultStatus::ErrorNotImplemented; } protected: - std::unique_ptr<FileUtil::IOFile> file; - bool is_loaded = false; + FileUtil::IOFile file; + bool is_loaded = false; }; /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 08993c4fa..094d74100 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cstring> #include <memory> #include "common/logging/log.h" @@ -10,7 +11,7 @@ #include "common/string_util.h" #include "common/swap.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/ncch.h" #include "core/memory.h" @@ -116,7 +117,10 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -ResultStatus AppLoader_NCCH::LoadExec() const { +ResultStatus AppLoader_NCCH::LoadExec() { + using Kernel::SharedPtr; + using Kernel::CodeSet; + if (!is_loaded) return ResultStatus::ErrorNotLoaded; @@ -125,7 +129,30 @@ ResultStatus AppLoader_NCCH::LoadExec() const { std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( (const char*)exheader_header.codeset_info.name, 8); u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]); - Kernel::g_current_process = Kernel::Process::Create(process_name, program_id); + + SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id); + + codeset->code.offset = 0; + codeset->code.addr = exheader_header.codeset_info.text.address; + codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; + + codeset->rodata.offset = codeset->code.offset + codeset->code.size; + codeset->rodata.addr = exheader_header.codeset_info.ro.address; + codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; + + // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just + // to the regular size. Playing it safe for now. + u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; + code.resize(code.size() + bss_page_size, 0); + + codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; + codeset->data.addr = exheader_header.codeset_info.data.address; + codeset->data.size = exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; + + codeset->entrypoint = codeset->code.addr; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); + + Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); // Attach a resource limit to the process based on the resource limit category Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory( @@ -136,18 +163,16 @@ ResultStatus AppLoader_NCCH::LoadExec() const { std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); - Memory::WriteBlock(entry_point, &code[0], code.size()); - s32 priority = exheader_header.arm11_system_local_caps.priority; u32 stack_size = exheader_header.codeset_info.stack_size; - Kernel::g_current_process->Run(entry_point, priority, stack_size); + Kernel::g_current_process->Run(priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; } -ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const { - if (!file->IsOpen()) +ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) { + if (!file.IsOpen()) return ResultStatus::Error; LOG_DEBUG(Loader, "%d sections:", kMaxSections); @@ -161,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& section.offset, section.size, section.name); s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); - file->Seek(section_offset, SEEK_SET); + file.Seek(section_offset, SEEK_SET); if (is_compressed) { // Section is compressed, read compressed .code section... @@ -172,7 +197,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& return ResultStatus::ErrorMemoryAllocationFailed; } - if (file->ReadBytes(&temp_buffer[0], section.size) != section.size) + if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) return ResultStatus::Error; // Decompress .code section... @@ -183,7 +208,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& } else { // Section is uncompressed... buffer.resize(section.size); - if (file->ReadBytes(&buffer[0], section.size) != section.size) + if (file.ReadBytes(&buffer[0], section.size) != section.size) return ResultStatus::Error; } return ResultStatus::Success; @@ -196,21 +221,21 @@ ResultStatus AppLoader_NCCH::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - if (!file->IsOpen()) + if (!file.IsOpen()) return ResultStatus::Error; // Reset read pointer in case this file has been read before. - file->Seek(0, SEEK_SET); + file.Seek(0, SEEK_SET); - if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) return ResultStatus::Error; // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); ncch_offset = 0x4000; - file->Seek(ncch_offset, SEEK_SET); - file->ReadBytes(&ncch_header, sizeof(NCCH_Header)); + file.Seek(ncch_offset, SEEK_SET); + file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); } // Verify we are loading the correct file type... @@ -219,7 +244,7 @@ ResultStatus AppLoader_NCCH::Load() { // Read ExHeader... - if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) + if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) return ResultStatus::Error; is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; @@ -239,7 +264,6 @@ ResultStatus AppLoader_NCCH::Load() { LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); LOG_DEBUG(Loader, "Core version: %d" , core_version); LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); - LOG_DEBUG(Loader, "Resource limit descriptor: 0x%08X", exheader_header.arm11_system_local_caps.resource_limit_descriptor); LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); // Read ExeFS... @@ -250,8 +274,8 @@ ResultStatus AppLoader_NCCH::Load() { LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); - file->Seek(exefs_offset + ncch_offset, SEEK_SET); - if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) + file.Seek(exefs_offset + ncch_offset, SEEK_SET); + if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) return ResultStatus::Error; is_loaded = true; // Set state to loaded @@ -259,24 +283,24 @@ ResultStatus AppLoader_NCCH::Load() { return LoadExec(); // Load the executable into memory for booting } -ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { return LoadSectionExeFS(".code", buffer); } -ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { return LoadSectionExeFS("icon", buffer); } -ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { return LoadSectionExeFS("banner", buffer); } -ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const { +ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { return LoadSectionExeFS("logo", buffer); } -ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { - if (!file->IsOpen()) +ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { + if (!file.IsOpen()) return ResultStatus::Error; // Check if the NCCH has a RomFS... @@ -287,12 +311,17 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); - buffer.resize(romfs_size); + if (file.GetSize () < romfs_offset + romfs_size) + return ResultStatus::Error; - file->Seek(romfs_offset, SEEK_SET); - if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size) + // We reopen the file, to allow its position to be independent from file's + romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); + if (!romfs_file->IsOpen()) return ResultStatus::Error; + offset = romfs_offset; + size = romfs_size; + return ResultStatus::Success; } LOG_DEBUG(Loader, "NCCH has no RomFS"); diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 29e39d2c0..b4374a476 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -163,7 +163,8 @@ namespace Loader { /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) class AppLoader_NCCH final : public AppLoader { public: - AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { } + AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) + : AppLoader(std::move(file)), filepath(filepath) { } /** * Returns the type of the file @@ -183,35 +184,35 @@ public: * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadCode(std::vector<u8>& buffer) const override; + ResultStatus ReadCode(std::vector<u8>& buffer) override; /** * Get the icon (typically icon section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadIcon(std::vector<u8>& buffer) const override; + ResultStatus ReadIcon(std::vector<u8>& buffer) override; /** * Get the banner (typically banner section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadBanner(std::vector<u8>& buffer) const override; + ResultStatus ReadBanner(std::vector<u8>& buffer) override; /** * Get the logo (typically logo section) of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadLogo(std::vector<u8>& buffer) const override; + ResultStatus ReadLogo(std::vector<u8>& buffer) override; /** * Get the RomFS of the application * @param buffer Reference to buffer to store data * @return ResultStatus result of function */ - ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; + ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) override; private: @@ -221,13 +222,13 @@ private: * @param buffer Vector to read data into * @return ResultStatus result of function */ - ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const; + ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer); /** * Loads .code section into memory for booting * @return ResultStatus result of function */ - ResultStatus LoadExec() const; + ResultStatus LoadExec(); bool is_compressed = false; @@ -244,6 +245,8 @@ private: NCCH_Header ncch_header; ExeFs_Header exefs_header; ExHeader_Header exheader_header; + + std::string filepath; }; } // namespace Loader diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index bf814b945..cbe993fbe 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp @@ -3,13 +3,14 @@ // Refer to the license.txt file included. #include <map> +#include <memory> +#include <utility> +#include <vector> #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/config_mem.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/hle/shared_page.h" @@ -31,7 +32,6 @@ struct MemoryArea { // We don't declare the IO regions in here since its handled by other means. static MemoryArea memory_areas[] = { - {PROCESS_IMAGE_VADDR, PROCESS_IMAGE_MAX_SIZE, "Process Image"}, // ExeFS:/.code is loaded here {HEAP_VADDR, HEAP_SIZE, "Heap"}, // Application heap (main memory) {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory {LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE, "Linear Heap"}, // Linear heap (main memory) @@ -131,13 +131,13 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { return addr | 0x80000000; } -// TODO(yuriks): Move this into Process -static Kernel::VMManager address_space; - void Init() { - using namespace Kernel; - InitMemoryMap(); + LOG_DEBUG(HW_Memory, "initialized OK"); +} + +void InitLegacyAddressSpace(Kernel::VMManager& address_space) { + using namespace Kernel; for (MemoryArea& area : memory_areas) { auto block = std::make_shared<std::vector<u8>>(area.size); @@ -151,14 +151,11 @@ void Init() { auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); address_space.Reprotect(shared_page_vma, VMAPermission::Read); - - LOG_DEBUG(HW_Memory, "initialized OK"); } void Shutdown() { heap_map.clear(); heap_linear_map.clear(); - address_space.Reset(); LOG_DEBUG(HW_Memory, "shutdown OK"); } diff --git a/src/core/mem_map.h b/src/core/mem_map.h index ba50914a8..229ef82c5 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -6,9 +6,14 @@ #include "common/common_types.h" +namespace Kernel { +class VMManager; +} + namespace Memory { void Init(); +void InitLegacyAddressSpace(Kernel::VMManager& address_space); void Shutdown(); /** diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 28844a915..1f66bb27d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -9,9 +9,6 @@ #include "common/logging/log.h" #include "common/swap.h" -#include "core/hle/config_mem.h" -#include "core/hle/shared_page.h" -#include "core/hw/hw.h" #include "core/mem_map.h" #include "core/memory.h" #include "core/memory_setup.h" @@ -62,14 +59,12 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); - if (current_page_table->attributes[base] != PageType::Unmapped && type != PageType::Unmapped) { - LOG_ERROR(HW_Memory, "overlapping memory ranges at %08X", base * PAGE_SIZE); - } current_page_table->attributes[base] = type; current_page_table->pointers[base] = memory; base += 1; - memory += PAGE_SIZE; + if (memory != nullptr) + memory += PAGE_SIZE; } } diff --git a/src/core/memory.h b/src/core/memory.h index 0b8ff9ec4..418609de0 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -4,6 +4,8 @@ #pragma once +#include <cstddef> + #include "common/common_types.h" namespace Memory { diff --git a/src/core/tracer/citrace.h b/src/core/tracer/citrace.h new file mode 100644 index 000000000..5deb6ce9e --- /dev/null +++ b/src/core/tracer/citrace.h @@ -0,0 +1,101 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstdint> + +namespace CiTrace { + +// NOTE: Things are stored in little-endian + +#pragma pack(1) + +struct CTHeader { + static const char* ExpectedMagicWord() { + return "CiTr"; + } + + static uint32_t ExpectedVersion() { + return 1; + } + + char magic[4]; + uint32_t version; + uint32_t header_size; + + struct { + // NOTE: Register range sizes are technically hardware-constants, but the actual limits + // aren't known. Hence we store the presumed limits along the offsets. + // Sizes are given in uint32_t units. + uint32_t gpu_registers; + uint32_t gpu_registers_size; + uint32_t lcd_registers; + uint32_t lcd_registers_size; + uint32_t pica_registers; + uint32_t pica_registers_size; + uint32_t default_attributes; + uint32_t default_attributes_size; + uint32_t vs_program_binary; + uint32_t vs_program_binary_size; + uint32_t vs_swizzle_data; + uint32_t vs_swizzle_data_size; + uint32_t vs_float_uniforms; + uint32_t vs_float_uniforms_size; + uint32_t gs_program_binary; + uint32_t gs_program_binary_size; + uint32_t gs_swizzle_data; + uint32_t gs_swizzle_data_size; + uint32_t gs_float_uniforms; + uint32_t gs_float_uniforms_size; + + // Other things we might want to store here: + // - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM + // - Lookup tables for fragment lighting + // - Lookup tables for procedural textures + } initial_state_offsets; + + uint32_t stream_offset; + uint32_t stream_size; +}; + +enum CTStreamElementType : uint32_t { + FrameMarker = 0xE1, + MemoryLoad = 0xE2, + RegisterWrite = 0xE3, +}; + +struct CTMemoryLoad { + uint32_t file_offset; + uint32_t size; + uint32_t physical_address; + uint32_t pad; +}; + +struct CTRegisterWrite { + uint32_t physical_address; + + enum : uint32_t { + SIZE_8 = 0xD1, + SIZE_16 = 0xD2, + SIZE_32 = 0xD3, + SIZE_64 = 0xD4 + } size; + + // TODO: Make it clearer which bits of this member are used for sizes other than 32 bits + uint64_t value; +}; + +struct CTStreamElement { + CTStreamElementType type; + + union { + CTMemoryLoad memory_load; + CTRegisterWrite register_write; + }; +}; + +#pragma pack() + +} diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp new file mode 100644 index 000000000..656706c0c --- /dev/null +++ b/src/core/tracer/recorder.cpp @@ -0,0 +1,187 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> + +#include "common/assert.h" +#include "common/file_util.h" +#include "common/logging/log.h" + +#include "recorder.h" + +namespace CiTrace { + +Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) { + +} + +void Recorder::Finish(const std::string& filename) { + // Setup CiTrace header + CTHeader header; + std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4); + header.version = CTHeader::ExpectedVersion(); + header.header_size = sizeof(CTHeader); + + // Calculate file offsets + auto& initial = header.initial_state_offsets; + + initial.gpu_registers_size = initial_state.gpu_registers.size(); + initial.lcd_registers_size = initial_state.lcd_registers.size(); + initial.pica_registers_size = initial_state.pica_registers.size(); + initial.default_attributes_size = initial_state.default_attributes.size(); + initial.vs_program_binary_size = initial_state.vs_program_binary.size(); + initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size(); + initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size(); + initial.gs_program_binary_size = initial_state.gs_program_binary.size(); + initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size(); + initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size(); + header.stream_size = stream.size(); + + initial.gpu_registers = sizeof(header); + initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32); + initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);; + initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32); + initial.vs_program_binary = initial.default_attributes + initial.default_attributes_size * sizeof(u32); + initial.vs_swizzle_data = initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32); + initial.vs_float_uniforms = initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32); + initial.gs_program_binary = initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32); + initial.gs_swizzle_data = initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32); + initial.gs_float_uniforms = initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32); + header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32); + + // Iterate through stream elements, update relevant stream element data + for (auto& stream_element : stream) { + switch (stream_element.data.type) { + case MemoryLoad: + { + auto& file_offset = memory_regions[stream_element.hash]; + if (!stream_element.uses_existing_data) { + file_offset = header.stream_offset; + } + stream_element.data.memory_load.file_offset = file_offset; + break; + } + + default: + // Other commands don't use any extra data + DEBUG_ASSERT(stream_element.extra_data.size() == 0); + break; + } + header.stream_offset += stream_element.extra_data.size(); + } + + try { + // Open file and write header + FileUtil::IOFile file(filename, "wb"); + size_t written = file.WriteObject(header); + if (written != 1 || file.Tell() != initial.gpu_registers) + throw "Failed to write header"; + + // Write initial state + written = file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size()); + if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers) + throw "Failed to write GPU registers"; + + written = file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size()); + if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers) + throw "Failed to write LCD registers"; + + written = file.WriteArray(initial_state.pica_registers.data(), initial_state.pica_registers.size()); + if (written != initial_state.pica_registers.size() || file.Tell() != initial.default_attributes) + throw "Failed to write Pica registers"; + + written = file.WriteArray(initial_state.default_attributes.data(), initial_state.default_attributes.size()); + if (written != initial_state.default_attributes.size() || file.Tell() != initial.vs_program_binary) + throw "Failed to write default vertex attributes"; + + written = file.WriteArray(initial_state.vs_program_binary.data(), initial_state.vs_program_binary.size()); + if (written != initial_state.vs_program_binary.size() || file.Tell() != initial.vs_swizzle_data) + throw "Failed to write vertex shader program binary"; + + written = file.WriteArray(initial_state.vs_swizzle_data.data(), initial_state.vs_swizzle_data.size()); + if (written != initial_state.vs_swizzle_data.size() || file.Tell() != initial.vs_float_uniforms) + throw "Failed to write vertex shader swizzle data"; + + written = file.WriteArray(initial_state.vs_float_uniforms.data(), initial_state.vs_float_uniforms.size()); + if (written != initial_state.vs_float_uniforms.size() || file.Tell() != initial.gs_program_binary) + throw "Failed to write vertex shader float uniforms"; + + written = file.WriteArray(initial_state.gs_program_binary.data(), initial_state.gs_program_binary.size()); + if (written != initial_state.gs_program_binary.size() || file.Tell() != initial.gs_swizzle_data) + throw "Failed to write geomtry shader program binary"; + + written = file.WriteArray(initial_state.gs_swizzle_data.data(), initial_state.gs_swizzle_data.size()); + if (written != initial_state.gs_swizzle_data.size() || file.Tell() != initial.gs_float_uniforms) + throw "Failed to write geometry shader swizzle data"; + + written = file.WriteArray(initial_state.gs_float_uniforms.data(), initial_state.gs_float_uniforms.size()); + if (written != initial_state.gs_float_uniforms.size() || file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size) + throw "Failed to write geometry shader float uniforms"; + + // Iterate through stream elements, write "extra data" + for (const auto& stream_element : stream) { + if (stream_element.extra_data.size() == 0) + continue; + + written = file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size()); + if (written != stream_element.extra_data.size()) + throw "Failed to write extra data"; + } + + if (file.Tell() != header.stream_offset) + throw "Unexpected end of extra data"; + + // Write actual stream elements + for (const auto& stream_element : stream) { + if (1 != file.WriteObject(stream_element.data)) + throw "Failed to write stream element"; + } + } catch(const char* str) { + LOG_ERROR(HW_GPU, "Writing CiTrace file failed: %s", str); + } +} + +void Recorder::FrameFinished() { + stream.push_back( { FrameMarker } ); +} + +void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) { + StreamElement element = { MemoryLoad }; + element.data.memory_load.size = size; + element.data.memory_load.physical_address = physical_address; + + // Compute hash over given memory region to check if the contents are already stored internally + boost::crc_32_type result; + result.process_bytes(data, size); + element.hash = result.checksum(); + + element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end()); + if (!element.uses_existing_data) { + element.extra_data.resize(size); + memcpy(element.extra_data.data(), data, size); + memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish() + } + + stream.push_back(element); +} + +template<typename T> +void Recorder::RegisterWritten(u32 physical_address, T value) { + StreamElement element = { RegisterWrite }; + element.data.register_write.size = (sizeof(T) == 1) ? CTRegisterWrite::SIZE_8 + : (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16 + : (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32 + : CTRegisterWrite::SIZE_64; + element.data.register_write.physical_address = physical_address; + element.data.register_write.value = value; + + stream.push_back(element); +} + +template void Recorder::RegisterWritten(u32,u8); +template void Recorder::RegisterWritten(u32,u16); +template void Recorder::RegisterWritten(u32,u32); +template void Recorder::RegisterWritten(u32,u64); + +} diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h new file mode 100644 index 000000000..6e4b70015 --- /dev/null +++ b/src/core/tracer/recorder.h @@ -0,0 +1,90 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <unordered_map> +#include <vector> + +#include <boost/crc.hpp> + +#include "common/common_types.h" + +#include "citrace.h" + +namespace CiTrace { + +class Recorder { +public: + struct InitialState { + std::vector<u32> gpu_registers; + std::vector<u32> lcd_registers; + std::vector<u32> pica_registers; + std::vector<u32> default_attributes; + std::vector<u32> vs_program_binary; + std::vector<u32> vs_swizzle_data; + std::vector<u32> vs_float_uniforms; + std::vector<u32> gs_program_binary; + std::vector<u32> gs_swizzle_data; + std::vector<u32> gs_float_uniforms; + }; + + /** + * Recorder constructor + * @param default_attributes Pointer to an array of 32-bit-aligned 24-bit floating point values. + * @param vs_float_uniforms Pointer to an array of 32-bit-aligned 24-bit floating point values. + */ + Recorder(const InitialState& initial_state); + + /// Finish recording of this Citrace and save it using the given filename. + void Finish(const std::string& filename); + + /// Mark end of a frame + void FrameFinished(); + + /** + * Store a copy of the given memory range in the recording. + * @note Use this whenever the GPU is about to access a particular memory region. + * @note The implementation will make sure to minimize redundant memory updates. + */ + void MemoryAccessed(const u8* data, u32 size, u32 physical_address); + + /** + * Record a register write. + * @note Use this whenever a GPU-related MMIO register has been written to. + */ + template<typename T> + void RegisterWritten(u32 physical_address, T value); + +private: + // Initial state of recording start + InitialState initial_state; + + // Command stream + struct StreamElement { + CTStreamElement data; + + /** + * Extra data to store along "core" data. + * This is e.g. used for data used in MemoryUpdates. + */ + std::vector<u8> extra_data; + + /// Optional CRC hash (e.g. for hashing memory regions) + boost::crc_32_type::value_type hash; + + /// If true, refer to data already written to the output file instead of extra_data + bool uses_existing_data; + }; + + std::vector<StreamElement> stream; + + /** + * Internal cache which maps hashes of memory contents to file offsets at which those memory + * contents are stored. + */ + std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions; +}; + +} // namespace |