diff options
Diffstat (limited to 'src/core')
58 files changed, 1713 insertions, 1317 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0ab0e440c..212da25c5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,9 +35,10 @@ set(SRCS hle/service/am_app.cpp hle/service/am_net.cpp hle/service/am_sys.cpp - hle/service/apt_a.cpp - hle/service/apt_s.cpp - hle/service/apt_u.cpp + hle/service/apt/apt.cpp + hle/service/apt/apt_a.cpp + hle/service/apt/apt_s.cpp + hle/service/apt/apt_u.cpp hle/service/boss_p.cpp hle/service/boss_u.cpp hle/service/cam_u.cpp @@ -71,9 +72,10 @@ set(SRCS hle/service/ns_s.cpp hle/service/nwm_uds.cpp hle/service/pm_app.cpp - hle/service/ptm_play.cpp - hle/service/ptm_u.cpp - hle/service/ptm_sysm.cpp + hle/service/ptm/ptm.cpp + hle/service/ptm/ptm_play.cpp + hle/service/ptm/ptm_u.cpp + hle/service/ptm/ptm_sysm.cpp hle/service/service.cpp hle/service/soc_u.cpp hle/service/srv.cpp @@ -140,9 +142,10 @@ set(HEADERS hle/service/am_app.h hle/service/am_net.h hle/service/am_sys.h - hle/service/apt_a.h - hle/service/apt_s.h - hle/service/apt_u.h + hle/service/apt/apt.h + hle/service/apt/apt_a.h + hle/service/apt/apt_s.h + hle/service/apt/apt_u.h hle/service/boss_p.h hle/service/boss_u.h hle/service/cam_u.h @@ -176,9 +179,10 @@ set(HEADERS hle/service/ns_s.h hle/service/nwm_uds.h hle/service/pm_app.h - hle/service/ptm_play.h - hle/service/ptm_u.h - hle/service/ptm_sysm.h + hle/service/ptm/ptm.h + hle/service/ptm/ptm_play.h + hle/service/ptm/ptm_u.h + hle/service/ptm/ptm_sysm.h hle/service/service.h hle/service/soc_u.h hle/service/srv.h diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp index f7c7451e9..913dc1454 100644 --- a/src/core/arm/disassembler/arm_disasm.cpp +++ b/src/core/arm/disassembler/arm_disasm.cpp @@ -24,7 +24,7 @@ static const char *cond_names[] = { "RESERVED" }; -const char *opcode_names[] = { +static const char *opcode_names[] = { "invalid", "undefined", "adc", @@ -131,7 +131,7 @@ static const char *shift_names[] = { "ROR" }; -static const char* cond_to_str(int cond) { +static const char* cond_to_str(uint32_t cond) { return cond_names[cond]; } @@ -337,8 +337,9 @@ std::string ARM_Disasm::DisassembleBX(uint32_t insn) std::string ARM_Disasm::DisassembleBKPT(uint32_t insn) { + uint8_t cond = (insn >> 28) & 0xf; uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf); - return Common::StringFromFormat("bkpt\t#%d", immed); + return Common::StringFromFormat("bkpt%s\t#%d", cond_to_str(cond), immed); } std::string ARM_Disasm::DisassembleCLZ(uint32_t insn) @@ -351,7 +352,6 @@ std::string ARM_Disasm::DisassembleCLZ(uint32_t insn) std::string ARM_Disasm::DisassembleMemblock(Opcode opcode, uint32_t insn) { - std::string tmp_reg; std::string tmp_list; uint8_t cond = (insn >> 28) & 0xf; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index b691ffbc3..2f72f5077 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -9,6 +9,7 @@ #include <unordered_map> #include "common/logging/log.h" +#include "common/profiler.h" #include "core/mem_map.h" #include "core/hle/hle.h" @@ -20,6 +21,9 @@ #include "core/arm/skyeye_common/armmmu.h" #include "core/arm/skyeye_common/vfp/vfp.h" +Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); +Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); + enum { COND = (1 << 0), NON_BRANCH = (1 << 1), @@ -3569,6 +3573,8 @@ typedef struct instruction_set_encoding_item ISEITEM; extern const ISEITEM arm_instruction[]; static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, addr_t addr) { + Common::Profiling::ScopeTimer timer_decode(profile_decode); + // Decode instruction, get index // Allocate memory and init InsCream // Go on next, until terminal instruction @@ -3641,6 +3647,8 @@ static bool InAPrivilegedMode(ARMul_State* core) { } unsigned InterpreterMainLoop(ARMul_State* state) { + Common::Profiling::ScopeTimer timer_execute(profile_execute); + #undef RM #undef RS @@ -4354,6 +4362,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) { cpu->Reg[14] = Memory::Read32(addr); else cpu->Reg_usr[1] = Memory::Read32(addr); + + addr += 4; } } else if (!BIT(inst, 22)) { for(int i = 0; i < 16; i++ ){ @@ -4422,12 +4432,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) { inst_cream->get_addr(cpu, inst_cream->inst, addr, 1); unsigned int value = Memory::Read32(addr); - if (BIT(CP15_REG(CP15_CONTROL), 22) == 1) - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - else { - value = ROTATE_RIGHT_32(value,(8*(addr&0x3))); - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - } + cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { // For armv5t, should enter thumb when bits[0] is non-zero. @@ -4450,12 +4455,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) { inst_cream->get_addr(cpu, inst_cream->inst, addr, 1); unsigned int value = Memory::Read32(addr); - if (BIT(CP15_REG(CP15_CONTROL), 22) == 1) - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - else { - value = ROTATE_RIGHT_32(value,(8*(addr&0x3))); - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - } + cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { // For armv5t, should enter thumb when bits[0] is non-zero. @@ -4699,11 +4699,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) { unsigned int value = Memory::Read32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - if (BIT(CP15_REG(CP15_CONTROL), 22) == 1) - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - else - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ROTATE_RIGHT_32(value,(8*(addr&0x3))) ; - if (BITS(inst_cream->inst, 12, 15) == 15) { INC_PC(sizeof(ldst_inst)); goto DISPATCH; @@ -4724,9 +4719,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) { DEBUG_MSG; } else { if (inst_cream->cp_num == 15) { - if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { - CP15_REG(CP15_MAIN_ID) = RD; - } else if(CRn == 1 && CRm == 0 && OPCODE_2 == 0) { + if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) { CP15_REG(CP15_CONTROL) = RD; } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { CP15_REG(CP15_AUXILIARY_CONTROL) = RD; @@ -5983,54 +5976,51 @@ unsigned InterpreterMainLoop(ARMul_State* state) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; unsigned int inst = inst_cream->inst; - int i; unsigned int Rn = BITS(inst, 16, 19); unsigned int old_RN = cpu->Reg[Rn]; inst_cream->get_addr(cpu, inst_cream->inst, addr, 0); if (BIT(inst_cream->inst, 22) == 1) { - for (i = 0; i < 13; i++) { - if(BIT(inst_cream->inst, i)) { + for (int i = 0; i < 13; i++) { + if (BIT(inst_cream->inst, i)) { Memory::Write32(addr, cpu->Reg[i]); addr += 4; } } if (BIT(inst_cream->inst, 13)) { - if (cpu->Mode == USER32MODE) { - Memory::Write32(addr, cpu->Reg[i]); - addr += 4; - } else { + if (cpu->Mode == USER32MODE) + Memory::Write32(addr, cpu->Reg[13]); + else Memory::Write32(addr, cpu->Reg_usr[0]); - addr += 4; - } + + addr += 4; } if (BIT(inst_cream->inst, 14)) { - if (cpu->Mode == USER32MODE) { - Memory::Write32(addr, cpu->Reg[i]); - addr += 4; - } else { + if (cpu->Mode == USER32MODE) + Memory::Write32(addr, cpu->Reg[14]); + else Memory::Write32(addr, cpu->Reg_usr[1]); - addr += 4; - } + + addr += 4; } if (BIT(inst_cream->inst, 15)) { Memory::Write32(addr, cpu->Reg_usr[1] + 8); } } else { - for( i = 0; i < 15; i++ ) { - if(BIT(inst_cream->inst, i)) { - if(i == Rn) + for (int i = 0; i < 15; i++) { + if (BIT(inst_cream->inst, i)) { + if (i == Rn) Memory::Write32(addr, old_RN); else Memory::Write32(addr, cpu->Reg[i]); + addr += 4; } } // Check PC reg - if(BIT(inst_cream->inst, i)) { + if (BIT(inst_cream->inst, 15)) Memory::Write32(addr, cpu->Reg_usr[1] + 8); - } } } cpu->Reg[15] += GET_INST_SIZE(cpu); diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index abafe226e..4ac827e0a 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp @@ -58,11 +58,7 @@ void ARMul_SelectProcessor(ARMul_State* state, unsigned properties) state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0; state->is_v5 = (properties & ARM_v5_Prop) != 0; state->is_v5e = (properties & ARM_v5e_Prop) != 0; - state->is_XScale = (properties & ARM_XScale_Prop) != 0; - state->is_iWMMXt = (properties & ARM_iWMMXt_Prop) != 0; state->is_v6 = (properties & ARM_v6_Prop) != 0; - state->is_ep9312 = (properties & ARM_ep9312_Prop) != 0; - state->is_pxa27x = (properties & ARM_PXA27X_Prop) != 0; state->is_v7 = (properties & ARM_v7_Prop) != 0; // Only initialse the coprocessor support once we diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h index 070fcf7dc..16f3ac86c 100644 --- a/src/core/arm/skyeye_common/armdefs.h +++ b/src/core/arm/skyeye_common/armdefs.h @@ -185,10 +185,6 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) bool is_v5e; // Are we emulating a v5e architecture? bool is_v6; // Are we emulating a v6 architecture? bool is_v7; // Are we emulating a v7 architecture? - bool is_XScale; // Are we emulating an XScale architecture? - bool is_iWMMXt; // Are we emulating an iWMMXt co-processor? - bool is_ep9312; // Are we emulating a Cirrus Maverick co-processor? - bool is_pxa27x; // Are we emulating a Intel PXA27x co-processor? // ARM_ARM A2-18 // 0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model @@ -211,20 +207,11 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) \***************************************************************************/ enum { - ARM_Nexec_Prop = 0x02, - ARM_Debug_Prop = 0x10, - ARM_Isync_Prop = ARM_Debug_Prop, - ARM_Lock_Prop = 0x20, - ARM_v4_Prop = 0x40, - ARM_v5_Prop = 0x80, - ARM_v6_Prop = 0xc0, - - ARM_v5e_Prop = 0x100, - ARM_XScale_Prop = 0x200, - ARM_ep9312_Prop = 0x400, - ARM_iWMMXt_Prop = 0x800, - ARM_PXA27X_Prop = 0x1000, - ARM_v7_Prop = 0x2000, + ARM_v4_Prop = 0x01, + ARM_v5_Prop = 0x02, + ARM_v5e_Prop = 0x04, + ARM_v6_Prop = 0x08, + ARM_v7_Prop = 0x10, }; /***************************************************************************\ diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h index 75d860e95..5d1b4e53f 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -144,8 +144,8 @@ static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m) u32 nh, nl, mh, ml; u64 rh, rma, rmb, rl; - nl = n; - ml = m; + nl = static_cast<u32>(n); + ml = static_cast<u32>(m); rl = (u64)nl * ml; nh = n >> 32; diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index 1a05ef8c1..d76d37fd4 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -661,8 +661,8 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 if ((rem + incr) < rem && d < 0xffffffff) d += 1; - if (d > (0x7fffffff + (vdm.sign != 0))) { - d = (0x7fffffff + (vdm.sign != 0)); + if (d > (0x7fffffffU + (vdm.sign != 0))) { + d = (0x7fffffffU + (vdm.sign != 0)); exceptions |= FPSCR_IOC; } else if (rem) exceptions |= FPSCR_IXC; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index cabe2a074..6f716b1ca 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -147,7 +147,7 @@ void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callba void UnregisterAllEvents() { if (first) - PanicAlert("Cannot unregister events with events pending"); + LOG_ERROR(Core_Timing, "Cannot unregister events with events pending"); event_types.clear(); } @@ -535,7 +535,7 @@ std::string GetScheduledEventsSummary() { while (event) { unsigned int t = event->type; if (t >= event_types.size()) - PanicAlert("Invalid event type"); // %i", t); + LOG_ERROR(Core_Timing, "Invalid event type"); // %i", t); const char* name = event_types[event->type].name; if (!name) name = "[unknown]"; diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index c2a5d641a..25c94cd26 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -6,9 +6,9 @@ #include "common/common_types.h" #include "common/file_util.h" +#include "common/make_unique.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" @@ -17,9 +17,11 @@ namespace FileSys { -static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 save_id) { - u32 save_high = static_cast<u32>((save_id >> 32) & 0xFFFFFFFF); - u32 save_low = static_cast<u32>(save_id & 0xFFFFFFFF); +static std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) { + std::vector<u8> vec_data = path.AsBinary(); + const u32* data = reinterpret_cast<const u32*>(vec_data.data()); + u32 save_low = data[1]; + u32 save_high = data[0]; return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high); } @@ -27,18 +29,25 @@ static std::string GetSystemSaveDataContainerPath(const std::string& mount_point return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str()); } -Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) - : DiskArchive(GetSystemSaveDataPath(GetSystemSaveDataContainerPath(mount_point), save_id)) { - LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); +ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path) + : base_path(GetSystemSaveDataContainerPath(nand_path)) { } -bool Archive_SystemSaveData::Initialize() { - if (!FileUtil::CreateFullPath(mount_point)) { - LOG_ERROR(Service_FS, "Unable to create SystemSaveData path."); - return false; +ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path) { + std::string fullpath = GetSystemSaveDataPath(base_path, path); + if (!FileUtil::Exists(fullpath)) { + // TODO(Subv): Check error code, this one is probably wrong + return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, + ErrorSummary::InvalidState, ErrorLevel::Status); } + auto archive = Common::make_unique<DiskArchive>(fullpath); + return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); +} - return true; +ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) { + std::string fullpath = GetSystemSaveDataPath(base_path, path); + FileUtil::CreateFullPath(fullpath); + return RESULT_SUCCESS; } } // namespace FileSys diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index c8f5845ca..556a2a488 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -15,17 +15,17 @@ namespace FileSys { /// File system interface to the SystemSaveData archive -class Archive_SystemSaveData final : public DiskArchive { +class ArchiveFactory_SystemSaveData final : public ArchiveFactory { public: - Archive_SystemSaveData(const std::string& mount_point, u64 save_id); + ArchiveFactory_SystemSaveData(const std::string& mount_point); - /** - * Initialize the archive. - * @return true if it initialized successfully - */ - bool Initialize(); + ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; + ResultCode Format(const Path& path) override; std::string GetName() const override { return "SystemSaveData"; } + +private: + std::string base_path; }; } // namespace FileSys diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index b0066e15e..1aaeaa9c9 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -4,6 +4,8 @@ #include <vector> +#include "common/profiler.h" + #include "core/arm/arm_interface.h" #include "core/mem_map.h" #include "core/hle/hle.h" @@ -11,14 +13,13 @@ #include "core/hle/shared_page.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" -#include "core/hle/service/fs/archive.h" -#include "core/hle/service/cfg/cfg.h" -#include "core/hle/service/hid/hid.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace HLE { +Common::Profiling::TimingCategory profiler_svc("SVC Calls"); + static std::vector<ModuleDef> g_module_db; bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread @@ -33,6 +34,8 @@ static const FunctionDef* GetSVCInfo(u32 opcode) { } void CallSVC(u32 opcode) { + Common::Profiling::ScopeTimer timer_svc(profiler_svc); + const FunctionDef *info = GetSVCInfo(opcode); if (!info) { @@ -70,9 +73,6 @@ static void RegisterAllModules() { void Init() { Service::Init(); - Service::FS::ArchiveInit(); - Service::CFG::CFGInit(); - Service::HID::HIDInit(); RegisterAllModules(); @@ -83,9 +83,6 @@ void Init() { } void Shutdown() { - Service::HID::HIDShutdown(); - Service::CFG::CFGShutdown(); - Service::FS::ArchiveShutdown(); Service::Shutdown(); g_module_db.clear(); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f8c834a8d..be1aed615 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/common.h" +#include "common/math_util.h" #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" @@ -339,7 +340,7 @@ static void DebugThreadQueue() { ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, u32 arg, s32 processor_id, VAddr stack_top) { if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { - s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + s32 new_priority = MathUtil::Clamp<s32>(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", name.c_str(), priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm @@ -387,7 +388,7 @@ static void ClampPriority(const Thread* thread, s32* priority) { if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) { DEBUG_ASSERT_MSG(false, "Application passed an out of range priority. An error should be returned."); - s32 new_priority = CLAMP(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + s32 new_priority = MathUtil::Clamp<s32>(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", thread->name.c_str(), *priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0e391fe2d..3648a168b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -208,11 +208,11 @@ union ResultCode { } }; -inline bool operator==(const ResultCode a, const ResultCode b) { +inline bool operator==(const ResultCode& a, const ResultCode& b) { return a.raw == b.raw; } -inline bool operator!=(const ResultCode a, const ResultCode b) { +inline bool operator!=(const ResultCode& a, const ResultCode& b) { return a.raw != b.raw; } diff --git a/src/core/hle/service/am_sys.cpp b/src/core/hle/service/am_sys.cpp index 7ab89569f..b244190a2 100644 --- a/src/core/hle/service/am_sys.cpp +++ b/src/core/hle/service/am_sys.cpp @@ -5,19 +5,56 @@ #include "core/hle/hle.h" #include "core/hle/service/am_sys.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace AM_SYS - namespace AM_SYS { -// Empty arrays are illegal -- commented out until an entry is added. -//const Interface::FunctionInfo FunctionTable[] = { }; +/** + * Gets the number of installed titles in the requested media type + * Inputs: + * 0: Command header (0x00010040) + * 1: Media type to load the titles from + * Outputs: + * 1: Result, 0 on success, otherwise error code + * 2: The number of titles in the requested media type + */ +static void TitleIDListGetTotal(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 media_type = cmd_buff[1] & 0xFF; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + LOG_WARNING(Service_CFG, "(STUBBED) media_type %u", media_type); +} + +/** + * Loads information about the desired number of titles from the desired media type into an array + * Inputs: + * 0: Command header (0x00020082) + * 1: The maximum number of titles to load + * 2: Media type to load the titles from + * 3: Descriptor of the output buffer pointer + * 4: Address of the output buffer + * Outputs: + * 1: Result, 0 on success, otherwise error code + * 2: The number of titles loaded from the requested media type + */ +static void GetTitleIDList(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 num_titles = cmd_buff[1]; + u32 media_type = cmd_buff[2] & 0xFF; + u32 addr = cmd_buff[4]; + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + LOG_WARNING(Service_CFG, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type); +} -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, + {0x00020082, GetTitleIDList, "GetTitleIDList"}, +}; Interface::Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp new file mode 100644 index 000000000..5971f860b --- /dev/null +++ b/src/core/hle/service/apt/apt.cpp @@ -0,0 +1,285 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/file_util.h" + +#include "core/hle/service/service.h" +#include "core/hle/service/apt/apt.h" +#include "core/hle/service/apt/apt_a.h" +#include "core/hle/service/apt/apt_s.h" +#include "core/hle/service/apt/apt_u.h" + +#include "core/hle/hle.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/thread.h" + +namespace Service { +namespace APT { + +// Address used for shared font (as observed on HW) +// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via +// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any +// address other than 0x18000000 due to internal pointers in the shared font dump that would need to +// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then +// correctly mapping it in Citra, however we still do not understand how the mapping is determined. +static const VAddr SHARED_FONT_VADDR = 0x18000000; + +/// Handle to shared memory region designated to for shared system font +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> pause_event = 0; ///< APT pause event +static std::vector<u8> shared_font; + +void Initialize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom(); + + // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called. + notification_event->Clear(); + pause_event->Signal(); // Fire start event + + ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); + lock->Release(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error +} + +void GetSharedFont(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + if (!shared_font.empty()) { + // TODO(bunnei): This function shouldn't copy the shared font every time it's called. + // Instead, it should probably map the shared font as RO memory. We don't currently have + // an easy way to do this, but the copy should be sufficient for now. + memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); + + cmd_buff[0] = 0x00440082; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = SHARED_FONT_VADDR; + cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); + } else { + cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) + LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); + } +} + +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. + pause_event->Signal(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); +} + +void GetLockHandle(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + // 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[3] = 0; + cmd_buff[4] = 0; + + cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom(); + LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]); +} + +void Enable(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); +} + +void GetAppletManInfo(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 unk = cmd_buff[1]; + 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 + + LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); +} + +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 + LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); +} + +void InquireNotification(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] = static_cast<u32>(SignalType::None); // Signal type + LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); +} + +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]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + 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); +} + +void ReceiveParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 app_id = cmd_buff[1]; + u32 buffer_size = cmd_buff[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[6] = 0; + cmd_buff[7] = 0; + LOG_WARNING(Service_APT, "(STUBBED) 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]; + + 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[6] = 0; + cmd_buff[7] = 0; + + LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); +} + +void CancelParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 flag1 = cmd_buff[1]; + u32 unk = cmd_buff[2]; + u32 flag2 = cmd_buff[3]; + u32 app_id = cmd_buff[4]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 1; // Set to Success + + LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", + flag1, unk, flag2, app_id); +} + +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 buffer1_size = cmd_buff[2]; + u32 buffer2_size = cmd_buff[3]; + u32 buffer1_addr = cmd_buff[5]; + u32 buffer2_addr = cmd_buff[65]; + + 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, + buffer1_addr, buffer2_addr); +} + +void SetAppCpuTimeLimit(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 value = cmd_buff[1]; + u32 percent = cmd_buff[2]; + + if (value != 1) { + LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value); +} + +void GetAppCpuTimeLimit(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 value = cmd_buff[1]; + + if (value != 1) { + LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); + } + + // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should + // be set by the application. + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0x80; // Set to 80% + + LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value); +} + +void APTInit() { + AddService(new APT_A_Interface); + AddService(new APT_S_Interface); + AddService(new APT_U_Interface); + + // 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 + // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file + // "shared_font.bin" in the Citra "sysdata" directory. + + shared_font.clear(); + std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; + + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + + if (file.IsOpen()) { + // Read shared font data + shared_font.resize((size_t)file.GetSize()); + file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem"); + } else { + LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); + shared_font_mem = nullptr; + } + + lock = Kernel::Mutex::Create(false, "APT_U:Lock"); + + // TODO(bunnei): Check if these are created in Initialize or on APT process startup. + notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); + pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause"); +} + +void APTShutdown() { + +} + +} // namespace APT +} // namespace Service diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h new file mode 100644 index 000000000..a39adbff9 --- /dev/null +++ b/src/core/hle/service/apt/apt.h @@ -0,0 +1,222 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "core/hle/result.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace APT { + +/// Signals used by APT functions +enum class SignalType : u32 { + None = 0x0, + AppJustStarted = 0x1, + ReturningToApp = 0xB, + ExitingApp = 0xC, +}; + +/// App Id's used by APT functions +enum class AppID : u32 { + HomeMenu = 0x101, + AlternateMenu = 0x103, + Camera = 0x110, + FriendsList = 0x112, + GameNotes = 0x113, + InternetBrowser = 0x114, + InstructionManual = 0x115, + Notifications = 0x116, + Miiverse = 0x117, + SoftwareKeyboard1 = 0x201, + Ed = 0x202, + PnoteApp = 0x204, + SnoteApp = 0x205, + Error = 0x206, + Mint = 0x207, + Extrapad = 0x208, + Memolib = 0x209, + Application = 0x300, + SoftwareKeyboard2 = 0x401, +}; + +/** + * APT::Initialize service function + * Service function that initializes the APT process for the running application + * Outputs: + * 1 : Result of the function, 0 on success, otherwise error code + * 3 : Handle to the notification event + * 4 : Handle to the pause event + */ +void Initialize(Service::Interface* self); + +/** + * APT::GetSharedFont service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Virtual address of where shared font will be loaded in memory + * 4 : Handle to shared font memory + */ +void GetSharedFont(Service::Interface* self); + +/** + * APT::NotifyToWait service function + * Inputs: + * 1 : AppID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void NotifyToWait(Service::Interface* self); + +void GetLockHandle(Service::Interface* self); + +void Enable(Service::Interface* self); + +/** + * APT::GetAppletManInfo service function. + * Inputs: + * 1 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Unknown u32 value + * 3 : Unknown u8 value + * 4 : Home Menu AppId + * 5 : AppID of currently active app + */ +void GetAppletManInfo(Service::Interface* self); + +/** + * APT::IsRegistered service function. This returns whether the specified AppID is registered with NS yet. + * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this + * command to determine when the launched process is running and to determine when to stop using GSP etc, + * while displaying the "Nintendo 3DS" loading screen. + * Inputs: + * 1 : AppID + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output, 0 = not registered, 1 = registered. + */ +void IsRegistered(Service::Interface* self); + +void InquireNotification(Service::Interface* self); + +/** + * APT::SendParameter service function. This sets the parameter data state. + * Inputs: + * 1 : Source AppID + * 2 : Destination AppID + * 3 : Signal type + * 4 : Parameter buffer size, max size is 0x1000 (this can be zero) + * 5 : Value + * 6 : Handle to the destination process, likely used for shared memory (this can be zero) + * 7 : (Size<<14) | 2 + * 8 : Input parameter buffer ptr + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code +*/ +void SendParameter(Service::Interface* self); + +/** + * APT::ReceiveParameter service function. This returns the current parameter data from NS state, + * from the source process which set the parameters. Once finished, NS will clear a flag in the NS + * state so that this command will return an error if this command is used again if parameters were + * not set again. This is called when the second Initialize event is triggered. It returns a signal + * type indicating why it was triggered. + * Inputs: + * 1 : AppID + * 2 : Parameter buffer size, max size is 0x1000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : AppID of the process which sent these parameters + * 3 : Signal type + * 4 : Actual parameter buffer size, this is <= to the the input size + * 5 : Value + * 6 : Handle from the source process which set the parameters, likely used for shared memory + * 7 : Size + * 8 : Output parameter buffer ptr + */ +void ReceiveParameter(Service::Interface* self); + +/** + * APT::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter + * (except for the word value prior to the output handle), except this will not clear the flag + * (except when responseword[3]==8 || responseword[3]==9) in NS state. + * Inputs: + * 1 : AppID + * 2 : Parameter buffer size, max size is 0x1000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Unknown, for now assume AppID of the process which sent these parameters + * 3 : Unknown, for now assume Signal type + * 4 : Actual parameter buffer size, this is <= to the the input size + * 5 : Value + * 6 : Handle from the source process which set the parameters, likely used for shared memory + * 7 : Size + * 8 : Output parameter buffer ptr + */ +void GlanceParameter(Service::Interface* self); + +/** + * APT::CancelParameter service function. When the parameter data is available, and when the above + * specified fields match the ones in NS state(for the ones where the checks are enabled), this + * clears the flag which indicates that parameter data is available + * (same flag cleared by APT:ReceiveParameter). + * Inputs: + * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. + * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter. + * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. + * 4 : AppID + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled + * fields don't match the fields in NS state. 1 = success. + */ +void CancelParameter(Service::Interface* self); + +/** + * APT::AppletUtility service function + * Inputs: + * 1 : Unknown, but clearly used for something + * 2 : Buffer 1 size (purpose is unknown) + * 3 : Buffer 2 size (purpose is unknown) + * 5 : Buffer 1 address (purpose is unknown) + * 65 : Buffer 2 address (purpose is unknown) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void AppletUtility(Service::Interface* self); + +/** + * APT::SetAppCpuTimeLimit service function + * Inputs: + * 1 : Value, must be one + * 2 : Percentage of CPU time from 5 to 80 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void SetAppCpuTimeLimit(Service::Interface* self); + +/** + * APT::GetAppCpuTimeLimit service function + * Inputs: + * 1 : Value, must be one + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : System core CPU time percentage + */ +void GetAppCpuTimeLimit(Service::Interface* self); + +/// Initialize the APT service +void APTInit(); + +/// Shutdown the APT service +void APTShutdown(); + +} // namespace APT +} // namespace Service diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp new file mode 100644 index 000000000..dbe5c1d87 --- /dev/null +++ b/src/core/hle/service/apt/apt_a.cpp @@ -0,0 +1,33 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/hle.h" +#include "core/hle/service/apt/apt.h" +#include "core/hle/service/apt/apt_a.h" + +namespace Service { +namespace APT { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, GetLockHandle, "GetLockHandle?"}, + {0x00020080, Initialize, "Initialize?"}, + {0x00030040, nullptr, "Enable?"}, + {0x00040040, nullptr, "Finalize?"}, + {0x00050040, nullptr, "GetAppletManInfo?"}, + {0x00060040, nullptr, "GetAppletInfo?"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, + {0x000E0080, GlanceParameter, "GlanceParameter?"}, + {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x00430040, nullptr, "NotifyToWait?"}, + {0x00440000, GetSharedFont, "GetSharedFont?"}, + {0x004B00C2, nullptr, "AppletUtility?"}, + {0x00550040, nullptr, "WriteInputToNsState?"}, +}; + +APT_A_Interface::APT_A_Interface() { + Register(FunctionTable); +} + +} // namespace APT +} // namespace Service diff --git a/src/core/hle/service/apt_a.h b/src/core/hle/service/apt/apt_a.h index 6cbf1288f..331fb5586 100644 --- a/src/core/hle/service/apt_a.h +++ b/src/core/hle/service/apt/apt_a.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_A +namespace Service { +namespace APT { -namespace APT_A { - -class Interface : public Service::Interface { +class APT_A_Interface : public Service::Interface { public: - Interface(); + APT_A_Interface(); std::string GetPortName() const override { return "APT:A"; } }; -} // namespace +} // namespace APT +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp new file mode 100644 index 000000000..3fd348651 --- /dev/null +++ b/src/core/hle/service/apt/apt_s.cpp @@ -0,0 +1,104 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/common.h" +#include "common/file_util.h" + +#include "core/hle/hle.h" +#include "core/hle/service/apt/apt.h" +#include "core/hle/service/apt/apt_s.h" + +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"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, + {0x00090040, nullptr, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, + {0x000B0040, InquireNotification, "InquireNotification"}, + {0x000C0104, nullptr, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, nullptr, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, + {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00160040, nullptr, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, + {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, + {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, NotifyToWait, "NotifyToWait"}, + {0x00440000, GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, + {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, + {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, +}; + +APT_S_Interface::APT_S_Interface() { + Register(FunctionTable); +} + +} // namespace APT +} // namespace Service diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt/apt_s.h index f097c9747..8e87b69af 100644 --- a/src/core/hle/service/apt_s.h +++ b/src/core/hle/service/apt/apt_s.h @@ -6,10 +6,8 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_S - -namespace APT_S { +namespace Service { +namespace APT { // Application and title launching service. These services handle signaling for home/power button as // well. Only one session for either APT service can be open at a time, normally processes close the @@ -18,13 +16,14 @@ namespace APT_S { // svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. /// Interface to "APT:S" service -class Interface : public Service::Interface { +class APT_S_Interface : public Service::Interface { public: - Interface(); + APT_S_Interface(); std::string GetPortName() const override { return "APT:S"; } }; -} // namespace +} // namespace APT +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp new file mode 100644 index 000000000..5ab23801e --- /dev/null +++ b/src/core/hle/service/apt/apt_u.cpp @@ -0,0 +1,103 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/common.h" +#include "common/file_util.h" + +#include "core/hle/service/apt/apt.h" +#include "core/hle/service/apt/apt_u.h" + +namespace Service { +namespace APT { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, GetLockHandle, "GetLockHandle"}, + {0x00020080, Initialize, "Initialize"}, + {0x00030040, Enable, "Enable"}, + {0x00040040, nullptr, "Finalize"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, nullptr, "GetAppletInfo"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, + {0x00090040, IsRegistered, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, + {0x000B0040, InquireNotification, "InquireNotification"}, + {0x000C0104, SendParameter, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, CancelParameter, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, + {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00160040, nullptr, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, + {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, + {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, NotifyToWait, "NotifyToWait"}, + {0x00440000, GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, +}; + +APT_U_Interface::APT_U_Interface() { + Register(FunctionTable); +} + +} // namespace APT +} // namespace Service diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt/apt_u.h index aad918cfc..8c7fe0ccb 100644 --- a/src/core/hle/service/apt_u.h +++ b/src/core/hle/service/apt/apt_u.h @@ -6,10 +6,8 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_U - -namespace APT_U { +namespace Service { +namespace APT { // Application and title launching service. These services handle signaling for home/power button as // well. Only one session for either APT service can be open at a time, normally processes close the @@ -18,13 +16,14 @@ namespace APT_U { // svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. /// Interface to "APT:U" service -class Interface : public Service::Interface { +class APT_U_Interface : public Service::Interface { public: - Interface(); + APT_U_Interface(); std::string GetPortName() const override { return "APT:U"; } }; -} // namespace +} // namespace APT +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp deleted file mode 100644 index 1c1d92572..000000000 --- a/src/core/hle/service/apt_a.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/hle.h" -#include "core/hle/service/apt_a.h" - -namespace APT_U { - extern void Initialize(Service::Interface* self); - extern void GetLockHandle(Service::Interface* self); - extern void ReceiveParameter(Service::Interface* self); - extern void GlanceParameter(Service::Interface* self); - extern void GetSharedFont(Service::Interface* self); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_A - -namespace APT_A { - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, APT_U::GetLockHandle, "GetLockHandle?"}, - {0x00020080, APT_U::Initialize, "Initialize?"}, - {0x00030040, nullptr, "Enable?"}, - {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, - {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter?"}, - {0x000E0080, APT_U::GlanceParameter, "GlanceParameter?"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, - {0x00430040, nullptr, "NotifyToWait?"}, - {0x00440000, APT_U::GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, nullptr, "AppletUtility?"}, - {0x00550040, nullptr, "WriteInputToNsState?"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp deleted file mode 100644 index 686335428..000000000 --- a/src/core/hle/service/apt_s.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - - -#include "common/common.h" -#include "common/file_util.h" - -#include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/service/apt_s.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_S - -namespace APT_U { - extern void GetLockHandle(Service::Interface* self); - extern void Initialize(Service::Interface* self); - extern void Enable(Service::Interface* self); - extern void InquireNotification(Service::Interface* self); - extern void NotifyToWait(Service::Interface* self); - extern void GetSharedFont(Service::Interface* self); - extern void AppletUtility(Service::Interface* self); - extern void GlanceParameter(Service::Interface* self); - extern void ReceiveParameter(Service::Interface* self); -} - -namespace APT_S { - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, APT_U::GetLockHandle, "GetLockHandle"}, - {0x00020080, APT_U::Initialize, "Initialize"}, - {0x00030040, APT_U::Enable, "Enable"}, - {0x00040040, nullptr, "Finalize"}, - {0x00050040, nullptr, "GetAppletManInfo"}, - {0x00060040, nullptr, "GetAppletInfo"}, - {0x00070000, nullptr, "GetLastSignaledAppletId"}, - {0x00080000, nullptr, "CountRegisteredApplet"}, - {0x00090040, nullptr, "IsRegistered"}, - {0x000A0040, nullptr, "GetAttribute"}, - {0x000B0040, APT_U::InquireNotification, "InquireNotification"}, - {0x000C0104, nullptr, "SendParameter"}, - {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"}, - {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"}, - {0x000F0100, nullptr, "CancelParameter"}, - {0x001000C2, nullptr, "DebugFunc"}, - {0x001100C0, nullptr, "MapProgramIdForDebug"}, - {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, - {0x00130000, nullptr, "GetPreparationState"}, - {0x00140040, nullptr, "SetPreparationState"}, - {0x00150140, nullptr, "PrepareToStartApplication"}, - {0x00160040, nullptr, "PreloadLibraryApplet"}, - {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, - {0x00190040, nullptr, "PrepareToStartSystemApplet"}, - {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, - {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, - {0x001F0084, nullptr, "StartSystemApplet"}, - {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, - {0x00230040, nullptr, "PrepareToJumpToApplication"}, - {0x00240044, nullptr, "JumpToApplication"}, - {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, - {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, - {0x00270044, nullptr, "CloseApplication"}, - {0x00280044, nullptr, "CloseLibraryApplet"}, - {0x00290044, nullptr, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, - {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, - {0x00300044, nullptr, "LeaveResidentApplet"}, - {0x00310100, nullptr, "PrepareToDoApplicationJump"}, - {0x00320084, nullptr, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, - {0x00340084, nullptr, "SendDeliverArg"}, - {0x00350080, nullptr, "ReceiveDeliverArg"}, - {0x00360040, nullptr, "LoadSysMenuArg"}, - {0x00370042, nullptr, "StoreSysMenuArg"}, - {0x00380040, nullptr, "PreloadResidentApplet"}, - {0x00390040, nullptr, "PrepareToStartResidentApplet"}, - {0x003A0044, nullptr, "StartResidentApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet"}, - {0x003C0042, nullptr, "SendDspSleep"}, - {0x003D0042, nullptr, "SendDspWakeUp"}, - {0x003E0080, nullptr, "ReplySleepQuery"}, - {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, - {0x00400042, nullptr, "SendCaptureBufferInfo"}, - {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, - {0x00420080, nullptr, "SleepSystem"}, - {0x00430040, APT_U::NotifyToWait, "NotifyToWait"}, - {0x00440000, APT_U::GetSharedFont, "GetSharedFont"}, - {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, - {0x00480100, nullptr, "GetProgramInfo"}, - {0x00490180, nullptr, "Reboot"}, - {0x004A0040, nullptr, "GetCaptureInfo"}, - {0x004B00C2, APT_U::AppletUtility, "AppletUtility"}, - {0x004C0000, nullptr, "SetFatalErrDispMode"}, - {0x004D0080, nullptr, "GetAppletProgramInfo"}, - {0x004E0000, nullptr, "HardwareResetAsync"}, - {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, - {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp deleted file mode 100644 index 2d605a767..000000000 --- a/src/core/hle/service/apt_u.cpp +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - - -#include "common/common.h" -#include "common/file_util.h" - -#include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/service/apt_u.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace APT_U - -namespace APT_U { - -// Address used for shared font (as observed on HW) -// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via -// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any -// address other than 0x18000000 due to internal pointers in the shared font dump that would need to -// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then -// correctly mapping it in Citra, however we still do not understand how the mapping is determined. -static const VAddr SHARED_FONT_VADDR = 0x18000000; - -/// Handle to shared memory region designated to for shared system font -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> pause_event = 0; ///< APT pause event -static std::vector<u8> shared_font; - -/// Signals used by APT functions -enum class SignalType : u32 { - None = 0x0, - AppJustStarted = 0x1, - ReturningToApp = 0xB, - ExitingApp = 0xC, -}; - -/// App Id's used by APT functions -enum class AppID : u32 { - HomeMenu = 0x101, - AlternateMenu = 0x103, - Camera = 0x110, - FriendsList = 0x112, - GameNotes = 0x113, - InternetBrowser = 0x114, - InstructionManual = 0x115, - Notifications = 0x116, - Miiverse = 0x117, - SoftwareKeyboard1 = 0x201, - Ed = 0x202, - PnoteApp = 0x204, - SnoteApp = 0x205, - Error = 0x206, - Mint = 0x207, - Extrapad = 0x208, - Memolib = 0x209, - Application = 0x300, - SoftwareKeyboard2 = 0x401, -}; - -void Initialize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(bunnei): Check if these are created in Initialize or on APT process startup. - notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); - pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause"); - - cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom(); - - // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called. - notification_event->Clear(); - pause_event->Signal(); // Fire start event - - ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); - lock->Release(); - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error -} - -/** - * APT_U::NotifyToWait service function - * Inputs: - * 1 : AppID - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -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. - pause_event->Signal(); - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); -} - -void GetLockHandle(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - // 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[3] = 0; - cmd_buff[4] = 0; - - cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom(); - LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]); -} - -void Enable(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); -} - -/** - * APT_U::GetAppletManInfo service function. - * Inputs: - * 1 : Unknown - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Unknown u32 value - * 3 : Unknown u8 value - * 4 : Home Menu AppId - * 5 : AppID of currently active app - */ -void GetAppletManInfo(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk = cmd_buff[1]; - 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 - - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); -} - -/** - * APT_U::IsRegistered service function. This returns whether the specified AppID is registered with NS yet. - * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this - * command to determine when the launched process is running and to determine when to stop using GSP etc, - * while displaying the "Nintendo 3DS" loading screen. - * Inputs: - * 1 : AppID - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output, 0 = not registered, 1 = registered. - */ -static 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 - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); -} - -void InquireNotification(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] = static_cast<u32>(SignalType::None); // Signal type - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); -} - -/** - * APT_U::SendParameter service function. This sets the parameter data state. - * Inputs: - * 1 : Source AppID - * 2 : Destination AppID - * 3 : Signal type - * 4 : Parameter buffer size, max size is 0x1000 (this can be zero) - * 5 : Value - * 6 : Handle to the destination process, likely used for shared memory (this can be zero) - * 7 : (Size<<14) | 2 - * 8 : Input parameter buffer ptr - * Outputs: - * 0 : Return Header - * 1 : Result of function, 0 on success, otherwise error code -*/ -static 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]; - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - 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); -} - -/** - * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state, - * from the source process which set the parameters. Once finished, NS will clear a flag in the NS - * state so that this command will return an error if this command is used again if parameters were - * not set again. This is called when the second Initialize event is triggered. It returns a signal - * type indicating why it was triggered. - * Inputs: - * 1 : AppID - * 2 : Parameter buffer size, max size is 0x1000 - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : AppID of the process which sent these parameters - * 3 : Signal type - * 4 : Actual parameter buffer size, this is <= to the the input size - * 5 : Value - * 6 : Handle from the source process which set the parameters, likely used for shared memory - * 7 : Size - * 8 : Output parameter buffer ptr - */ -void ReceiveParameter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - u32 buffer_size = cmd_buff[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[6] = 0; - cmd_buff[7] = 0; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); -} - -/** - * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter - * (except for the word value prior to the output handle), except this will not clear the flag - * (except when responseword[3]==8 || responseword[3]==9) in NS state. - * Inputs: - * 1 : AppID - * 2 : Parameter buffer size, max size is 0x1000 - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Unknown, for now assume AppID of the process which sent these parameters - * 3 : Unknown, for now assume Signal type - * 4 : Actual parameter buffer size, this is <= to the the input size - * 5 : Value - * 6 : Handle from the source process which set the parameters, likely used for shared memory - * 7 : Size - * 8 : Output parameter buffer ptr - */ -void GlanceParameter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - u32 buffer_size = cmd_buff[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[6] = 0; - cmd_buff[7] = 0; - - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); -} - -/** - * APT_U::CancelParameter service function. When the parameter data is available, and when the above - * specified fields match the ones in NS state(for the ones where the checks are enabled), this - * clears the flag which indicates that parameter data is available - * (same flag cleared by APT:ReceiveParameter). - * Inputs: - * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. - * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter. - * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. - * 4 : AppID - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled - * fields don't match the fields in NS state. 1 = success. - */ -static void CancelParameter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 flag1 = cmd_buff[1]; - u32 unk = cmd_buff[2]; - u32 flag2 = cmd_buff[3]; - u32 app_id = cmd_buff[4]; - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 1; // Set to Success - - LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", - flag1, unk, flag2, app_id); -} - -/** - * APT_U::AppletUtility service function - * Inputs: - * 1 : Unknown, but clearly used for something - * 2 : Buffer 1 size (purpose is unknown) - * 3 : Buffer 2 size (purpose is unknown) - * 5 : Buffer 1 address (purpose is unknown) - * 65 : Buffer 2 address (purpose is unknown) - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -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 buffer1_size = cmd_buff[2]; - u32 buffer2_size = cmd_buff[3]; - u32 buffer1_addr = cmd_buff[5]; - u32 buffer2_addr = cmd_buff[65]; - - 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, - buffer1_addr, buffer2_addr); -} - -/** - * APT_U::GetSharedFont service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Virtual address of where shared font will be loaded in memory - * 4 : Handle to shared font memory - */ -void GetSharedFont(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - if (!shared_font.empty()) { - // TODO(bunnei): This function shouldn't copy the shared font every time it's called. - // Instead, it should probably map the shared font as RO memory. We don't currently have - // an easy way to do this, but the copy should be sufficient for now. - memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); - - cmd_buff[0] = 0x00440082; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = SHARED_FONT_VADDR; - cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); - } else { - cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) - LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); - } -} - -/** - * APT_U::SetAppCpuTimeLimit service function - * Inputs: - * 1 : Value, must be one - * 2 : Percentage of CPU time from 5 to 80 - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -static void SetAppCpuTimeLimit(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 value = cmd_buff[1]; - u32 percent = cmd_buff[2]; - - if (value != 1) { - LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); - } - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value); -} - -/** - * APT_U::GetAppCpuTimeLimit service function - * Inputs: - * 1 : Value, must be one - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : System core CPU time percentage - */ -static void GetAppCpuTimeLimit(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 value = cmd_buff[1]; - - if (value != 1) { - LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); - } - - // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should - // be set by the application. - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0x80; // Set to 80% - - LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value); -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle"}, - {0x00020080, Initialize, "Initialize"}, - {0x00030040, Enable, "Enable"}, - {0x00040040, nullptr, "Finalize"}, - {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, - {0x00060040, nullptr, "GetAppletInfo"}, - {0x00070000, nullptr, "GetLastSignaledAppletId"}, - {0x00080000, nullptr, "CountRegisteredApplet"}, - {0x00090040, IsRegistered, "IsRegistered"}, - {0x000A0040, nullptr, "GetAttribute"}, - {0x000B0040, InquireNotification, "InquireNotification"}, - {0x000C0104, SendParameter, "SendParameter"}, - {0x000D0080, ReceiveParameter, "ReceiveParameter"}, - {0x000E0080, GlanceParameter, "GlanceParameter"}, - {0x000F0100, CancelParameter, "CancelParameter"}, - {0x001000C2, nullptr, "DebugFunc"}, - {0x001100C0, nullptr, "MapProgramIdForDebug"}, - {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, - {0x00130000, nullptr, "GetPreparationState"}, - {0x00140040, nullptr, "SetPreparationState"}, - {0x00150140, nullptr, "PrepareToStartApplication"}, - {0x00160040, nullptr, "PreloadLibraryApplet"}, - {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, - {0x00190040, nullptr, "PrepareToStartSystemApplet"}, - {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, - {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, - {0x001F0084, nullptr, "StartSystemApplet"}, - {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, - {0x00230040, nullptr, "PrepareToJumpToApplication"}, - {0x00240044, nullptr, "JumpToApplication"}, - {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, - {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, - {0x00270044, nullptr, "CloseApplication"}, - {0x00280044, nullptr, "CloseLibraryApplet"}, - {0x00290044, nullptr, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, - {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, - {0x00300044, nullptr, "LeaveResidentApplet"}, - {0x00310100, nullptr, "PrepareToDoApplicationJump"}, - {0x00320084, nullptr, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, - {0x00340084, nullptr, "SendDeliverArg"}, - {0x00350080, nullptr, "ReceiveDeliverArg"}, - {0x00360040, nullptr, "LoadSysMenuArg"}, - {0x00370042, nullptr, "StoreSysMenuArg"}, - {0x00380040, nullptr, "PreloadResidentApplet"}, - {0x00390040, nullptr, "PrepareToStartResidentApplet"}, - {0x003A0044, nullptr, "StartResidentApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet"}, - {0x003C0042, nullptr, "SendDspSleep"}, - {0x003D0042, nullptr, "SendDspWakeUp"}, - {0x003E0080, nullptr, "ReplySleepQuery"}, - {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, - {0x00400042, nullptr, "SendCaptureBufferInfo"}, - {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, - {0x00420080, nullptr, "SleepSystem"}, - {0x00430040, NotifyToWait, "NotifyToWait"}, - {0x00440000, GetSharedFont, "GetSharedFont"}, - {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, - {0x00480100, nullptr, "GetProgramInfo"}, - {0x00490180, nullptr, "Reboot"}, - {0x004A0040, nullptr, "GetCaptureInfo"}, - {0x004B00C2, AppletUtility, "AppletUtility"}, - {0x004C0000, nullptr, "SetFatalErrDispMode"}, - {0x004D0080, nullptr, "GetAppletProgramInfo"}, - {0x004E0000, nullptr, "HardwareResetAsync"}, - {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, - {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - // 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 - // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file - // "shared_font.bin" in the Citra "sysdata" directory. - - shared_font.clear(); - std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; - - FileUtil::CreateFullPath(filepath); // Create path if not already created - FileUtil::IOFile file(filepath, "rb"); - - if (file.IsOpen()) { - // Read shared font data - shared_font.resize((size_t)file.GetSize()); - file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); - - // Create shared font memory object - shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem"); - } else { - LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); - shared_font_mem = nullptr; - } - - lock = Kernel::Mutex::Create(false, "APT_U:Lock"); - - Register(FunctionTable); -} - -} // namespace diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 71986f7d2..6adadb224 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -3,9 +3,13 @@ // Refer to the license.txt file included. #include <algorithm> -#include "common/make_unique.h" -#include "core/file_sys/archive_systemsavedata.h" + +#include "core/hle/service/fs/archive.h" +#include "core/hle/service/service.h" #include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/cfg/cfg_i.h" +#include "core/hle/service/cfg/cfg_s.h" +#include "core/hle/service/cfg/cfg_u.h" namespace Service { namespace CFG { @@ -36,7 +40,8 @@ const std::array<float, 8> STEREO_CAMERA_SETTINGS = { static const u32 CONFIG_SAVEFILE_SIZE = 0x8000; static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; -static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data; +static Service::FS::ArchiveHandle cfg_system_save_data_archive; +static const std::vector<u8> cfg_system_savedata_id = { 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00 }; ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { // Read the header @@ -102,19 +107,22 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data ResultCode DeleteConfigNANDSaveFile() { FileSys::Path path("config"); - if (cfg_system_save_data->DeleteFile(path)) - return RESULT_SUCCESS; - return ResultCode(-1); // TODO(Subv): Find the right error code + return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path); } ResultCode UpdateConfigNANDSavegame() { FileSys::Mode mode = {}; mode.write_flag = 1; mode.create_flag = 1; + FileSys::Path path("config"); - auto file = cfg_system_save_data->OpenFile(path, mode); - ASSERT_MSG(file != nullptr, "could not open file"); - file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); + + auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); + ASSERT_MSG(config_result.Succeeded(), "could not open file"); + + auto config = config_result.MoveFrom(); + config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); + return RESULT_SUCCESS; } @@ -163,27 +171,37 @@ ResultCode FormatConfig() { } void CFGInit() { - // TODO(Subv): In the future we should use the FS service to query this archive, - // currently it is not possible because you can only have one open archive of the same type at any time - std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); - cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>( - nand_directory, CFG_SAVE_ID); - if (!cfg_system_save_data->Initialize()) { - LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service"); - return; + AddService(new CFG_I_Interface); + AddService(new CFG_S_Interface); + AddService(new CFG_U_Interface); + + // Open the SystemSaveData archive 0x00010017 + FileSys::Path archive_path(cfg_system_savedata_id); + auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); + + // If the archive didn't exist, create the files inside + if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { + // Format the archive to create the directories + Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); + + // Open it again to get a valid archive now that the folder exists + archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); } - // TODO(Subv): All this code should be moved to cfg:i, - // it's only here because we do not currently emulate the lower level code that uses that service - // Try to open the file in read-only mode to check its existence - FileSys::Mode mode = {}; - mode.read_flag = 1; - FileSys::Path path("config"); - auto file = cfg_system_save_data->OpenFile(path, mode); + ASSERT_MSG(archive_result.Succeeded(), "Could not open the CFG SystemSaveData archive!"); + + cfg_system_save_data_archive = *archive_result; + + FileSys::Path config_path("config"); + FileSys::Mode open_mode = {}; + open_mode.read_flag = 1; - // Load the config if it already exists - if (file != nullptr) { - file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); + auto config_result = Service::FS::OpenFileFromArchive(*archive_result, config_path, open_mode); + + // Read the file if it already exists + if (config_result.Succeeded()) { + auto config = config_result.MoveFrom(); + config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); return; } @@ -191,10 +209,12 @@ void CFGInit() { // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals CONSOLE_USERNAME_BLOCK.ng_word = 0; CONSOLE_USERNAME_BLOCK.zero = 0; + // Copy string to buffer and pad with zeros at the end auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14); std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size, std::end(CONSOLE_USERNAME_BLOCK.username), 0); + FormatConfig(); } diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 20b09a8cb..6d1eee4e0 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -6,10 +6,8 @@ #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_I - -namespace CFG_I { +namespace Service { +namespace CFG { /** * CFG_I::GetConfigInfoBlk8 service function @@ -99,11 +97,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08180042, nullptr, "SecureInfoGetSerialNo"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +CFG_I_Interface::CFG_I_Interface() { Register(FunctionTable); } -} // namespace +} // namespace CFG +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/cfg/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h index a498dd589..d0a2cce39 100644 --- a/src/core/hle/service/cfg/cfg_i.h +++ b/src/core/hle/service/cfg/cfg_i.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_I +namespace Service { +namespace CFG { -namespace CFG_I { - -class Interface : public Service::Interface { +class CFG_I_Interface : public Service::Interface { public: - Interface(); + CFG_I_Interface(); std::string GetPortName() const override { return "cfg:i"; } }; -} // namespace +} // namespace CFG +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index d80aeae8d..d9a3e5d51 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -6,10 +6,8 @@ #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_s.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_S - -namespace CFG_S { +namespace Service { +namespace CFG { /** * CFG_S::GetConfigInfoBlk2 service function @@ -87,11 +85,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x04090000, nullptr, "UpdateConfigBlk00040003"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +CFG_S_Interface::CFG_S_Interface() { Register(FunctionTable); } -} // namespace +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h index d8b67137f..5568d6485 100644 --- a/src/core/hle/service/cfg/cfg_s.h +++ b/src/core/hle/service/cfg/cfg_s.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_S +namespace Service { +namespace CFG { -namespace CFG_S { - -class Interface : public Service::Interface { +class CFG_S_Interface : public Service::Interface { public: - Interface(); + CFG_S_Interface(); std::string GetPortName() const override { return "cfg:s"; } }; -} // namespace +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index a65da90c5..c8c1c5b17 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -10,10 +10,8 @@ #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_u.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_U - -namespace CFG_U { +namespace Service { +namespace CFG { // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. #define C(code) ((code)[0] | ((code)[1] << 8)) @@ -241,11 +239,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +CFG_U_Interface::CFG_U_Interface() { Register(FunctionTable); } -} // namespace +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/cfg/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h index 9ad73f355..5303d8ac6 100644 --- a/src/core/hle/service/cfg/cfg_u.h +++ b/src/core/hle/service/cfg/cfg_u.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace CFG_U +namespace Service { +namespace CFG { -namespace CFG_U { - -class Interface : public Service::Interface { +class CFG_U_Interface : public Service::Interface { public: - Interface(); + CFG_U_Interface(); std::string GetPortName() const override { return "cfg:u"; } }; -} // namespace +} // namespace CFG +} // namespace Service diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 8d765acb5..58c5acd1e 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -10,8 +10,171 @@ namespace ERR_F { +enum { + ErrSpecifier0 = 0, + ErrSpecifier1 = 1, + ErrSpecifier3 = 3, + ErrSpecifier4 = 4, +}; + +// This is used instead of ResultCode from result.h +// because we can't have non-trivial data members in unions. +union RSL { + u32 raw; + + BitField<0, 10, u32> description; + BitField<10, 8, u32> module; + BitField<21, 6, u32> summary; + BitField<27, 5, u32> level; +}; + +union ErrInfo { + u8 specifier; + + struct { + u8 specifier; // 0x0 + u8 rev_high; // 0x1 + u16 rev_low; // 0x2 + RSL result_code; // 0x4 + u32 address; // 0x8 + INSERT_PADDING_BYTES(4); // 0xC + u32 pid_low; // 0x10 + u32 pid_high; // 0x14 + u32 aid_low; // 0x18 + u32 aid_high; // 0x1C + } errtype1; + + struct { + u8 specifier; // 0x0 + u8 rev_high; // 0x1 + u16 rev_low; // 0x2 + INSERT_PADDING_BYTES(0xC); // 0x4 + u32 pid_low; // 0x10 + u32 pid_high; // 0x14 + u32 aid_low; // 0x18 + u32 aid_high; // 0x1C + u8 error_type; // 0x20 + INSERT_PADDING_BYTES(3); // 0x21 + u32 fault_status_reg; // 0x24 + u32 fault_addr; // 0x28 + u32 fpexc; // 0x2C + u32 finst; // 0x30 + u32 finst2; // 0x34 + INSERT_PADDING_BYTES(0x34); // 0x38 + u32 sp; // 0x6C + u32 pc; // 0x70 + u32 lr; // 0x74 + u32 cpsr; // 0x78 + } errtype3; + + struct { + u8 specifier; // 0x0 + u8 rev_high; // 0x1 + u16 rev_low; // 0x2 + RSL result_code; // 0x4 + INSERT_PADDING_BYTES(8); // 0x8 + u32 pid_low; // 0x10 + u32 pid_high; // 0x14 + u32 aid_low; // 0x18 + u32 aid_high; // 0x1C + char debug_string1[0x2E]; // 0x20 + char debug_string2[0x2E]; // 0x4E + } errtype4; +}; + +enum { + PrefetchAbort = 0, + DataAbort = 1, + UndefInstr = 2, + VectorFP = 3 +}; + +static std::string GetErrInfo3Type(u8 type_code) { + switch (type_code) { + case PrefetchAbort: return "Prefetch Abort"; + case DataAbort: return "Data Abort"; + case UndefInstr: return "Undefined Instruction"; + case VectorFP: return "Vector Floating Point"; + default: return "unknown"; + } +} + +static void ThrowFatalError(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + LOG_CRITICAL(Service_ERR, "Fatal error!"); + const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); + + switch (errinfo->specifier) { + case ErrSpecifier0: + case ErrSpecifier1: + { + const auto& errtype = errinfo->errtype1; + LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); + LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); + LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); + LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address); + + LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); + LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); + LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); + LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); + LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); + break; + } + + case ErrSpecifier3: + { + const auto& errtype = errinfo->errtype3; + LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); + LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); + LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); + LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str()); + + LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc); + LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr); + LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp); + LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr); + + switch (errtype.error_type) { + case PrefetchAbort: + case DataAbort: + LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr); + LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg); + break; + case VectorFP: + LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc); + LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst); + LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2); + break; + } + break; + } + + case ErrSpecifier4: + { + const auto& errtype = errinfo->errtype4; + LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); + LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); + LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); + + LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); + LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); + LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); + LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); + LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); + + LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1); + LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2); + break; + } + } + + cmd_buff[1] = 0; // No error +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00010800, nullptr, "ThrowFatalError"} + {0x00010800, ThrowFatalError, "ThrowFatalError"} }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index a69c4f25b..9da2e7aa2 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -18,8 +18,11 @@ #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/hle/service/service.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/fs/fs_user.h" #include "core/hle/result.h" // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. @@ -418,6 +421,8 @@ ResultCode CreateExtSaveData(u32 high, u32 low) { void ArchiveInit() { next_handle = 1; + AddService(new FS::Interface); + // TODO(Subv): Add the other archive types (see here for the known types: // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). @@ -449,6 +454,9 @@ void ArchiveInit() { // Create the SaveDataCheck archive, basically a small variation of the RomFS archive auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); + + auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); + RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); } /// Shutdown archives diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 71ee4ff55..eb312496e 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -487,6 +487,15 @@ static void FormatThisUserSaveData(Service::Interface* self) { cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; } +/** + * FS_User::CreateExtSaveData service function + * Inputs: + * 0: 0x08510242 + * 1: High word of the saveid to create + * 2: Low word of the saveid to create + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ static void CreateExtSaveData(Service::Interface* self) { // TODO(Subv): Figure out the other parameters. u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -496,7 +505,22 @@ static void CreateExtSaveData(Service::Interface* self) { cmd_buff[1] = CreateExtSaveData(save_high, save_low).raw; } -const FSUserInterface::FunctionInfo FunctionTable[] = { +/** + * FS_User::CardSlotIsInserted service function. + * Inputs: + * 0: 0x08210000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Whether there is a game card inserted into the slot or not. + */ +static void CardSlotIsInserted(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + LOG_WARNING(Service_FS, "(STUBBED) called"); +} + +const Interface::FunctionInfo FunctionTable[] = { {0x000100C6, nullptr, "Dummy1"}, {0x040100C4, nullptr, "Control"}, {0x08010002, Initialize, "Initialize"}, @@ -531,7 +555,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { {0x081E0042, nullptr, "GetNandLog"}, {0x081F0000, nullptr, "ClearSdmcLog"}, {0x08200000, nullptr, "ClearNandLog"}, - {0x08210000, nullptr, "CardSlotIsInserted"}, + {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"}, {0x08220000, nullptr, "CardSlotPowerOn"}, {0x08230000, nullptr, "CardSlotPowerOff"}, {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, @@ -590,7 +614,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class -FSUserInterface::FSUserInterface() { +Interface::Interface() { Register(FunctionTable); } diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index 2d896dd5f..bb6ab195e 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -6,16 +6,13 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace FS_User - namespace Service { namespace FS { /// Interface to "fs:USER" service -class FSUserInterface : public Service::Interface { +class Interface : public Service::Interface { public: - FSUserInterface(); + Interface(); std::string GetPortName() const override { return "fs:USER"; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 7cb01729e..e0689be2e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2,7 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/service.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_spvr.h" +#include "core/hle/service/hid/hid_user.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/event.h" @@ -35,6 +38,19 @@ static inline PadData* GetPadData() { return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr)); } +// TODO(peachum): +// Add a method for setting analog input from joystick device for the circle Pad. +// +// This method should: +// * Be called after both PadButton<Press, Release>(). +// * Be called before PadUpdateComplete() +// * Set current PadEntry.circle_pad_<axis> using analog data +// * Set PadData.raw_circle_pad_data +// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 +// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 +// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 +// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 + /** * Circle Pad from keys. * @@ -121,9 +137,25 @@ void PadUpdateComplete() { g_event_pad_or_touch_2->Signal(); } +void GetIPCHandles(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = 0; // No error + // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) + cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom(); + cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom(); + cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom(); + cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); + cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); +} + void HIDInit() { using namespace Kernel; + AddService(new HID_U_Interface); + AddService(new HID_SPVR_Interface); + g_shared_mem = SharedMemory::Create("HID:SharedMem"); // Create event handles diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 2116d2ca3..9c6e86f77 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -7,6 +7,7 @@ #include <array> #include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" #include "common/bit_field.h" namespace Kernel { @@ -47,6 +48,15 @@ struct PadState { BitField<10, 1, u32> x; BitField<11, 1, u32> y; + BitField<14, 1, u32> zl; + BitField<15, 1, u32> zr; + + BitField<20, 1, u32> touch; + + BitField<24, 1, u32> c_right; + BitField<25, 1, u32> c_left; + BitField<26, 1, u32> c_up; + BitField<27, 1, u32> c_down; BitField<28, 1, u32> circle_right; BitField<29, 1, u32> circle_left; BitField<30, 1, u32> circle_up; @@ -99,11 +109,37 @@ const PadState PAD_R = {{1u << 8}}; const PadState PAD_L = {{1u << 9}}; const PadState PAD_X = {{1u << 10}}; const PadState PAD_Y = {{1u << 11}}; + +const PadState PAD_ZL = {{1u << 14}}; +const PadState PAD_ZR = {{1u << 15}}; + +const PadState PAD_TOUCH = {{1u << 20}}; + +const PadState PAD_C_RIGHT = {{1u << 24}}; +const PadState PAD_C_LEFT = {{1u << 25}}; +const PadState PAD_C_UP = {{1u << 26}}; +const PadState PAD_C_DOWN = {{1u << 27}}; const PadState PAD_CIRCLE_RIGHT = {{1u << 28}}; const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; const PadState PAD_CIRCLE_UP = {{1u << 30}}; const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; +/** + * HID::GetIPCHandles service function + * Inputs: + * None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Unused + * 3 : Handle to HID_User shared memory + * 4 : Event signaled by HID_User + * 5 : Event signaled by HID_User + * 6 : Event signaled by HID_User + * 7 : Gyroscope event + * 8 : Event signaled by HID_User + */ +void GetIPCHandles(Interface* self); + // Methods for updating the HID module's state void PadButtonPress(const PadState& pad_state); void PadButtonRelease(const PadState& pad_state); diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp index 8f06b224d..790dcabbf 100644 --- a/src/core/hle/service/hid/hid_spvr.cpp +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -3,19 +3,14 @@ // Refer to the license.txt file included. #include "core/hle/hle.h" +#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_spvr.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_SPVR - -namespace HID_User { - extern void GetIPCHandles(Service::Interface* self); -} - -namespace HID_SPVR { +namespace Service { +namespace HID { const Interface::FunctionInfo FunctionTable[] = { - {0x000A0000, HID_User::GetIPCHandles, "GetIPCHandles"}, + {0x000A0000, GetIPCHandles, "GetIPCHandles"}, {0x000B0000, nullptr, "StartAnalogStickCalibration"}, {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, {0x00110000, nullptr, "EnableAccelerometer"}, @@ -27,11 +22,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00170000, nullptr, "GetSoundVolume"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +HID_SPVR_Interface::HID_SPVR_Interface() { Register(FunctionTable); } -} // namespace +} // namespace HID +} // namespace Service diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h index 53ddc8569..ba61583d2 100644 --- a/src/core/hle/service/hid/hid_spvr.h +++ b/src/core/hle/service/hid/hid_spvr.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_SPVR +namespace Service { +namespace HID { -namespace HID_SPVR { - -class Interface : public Service::Interface { +class HID_SPVR_Interface : public Service::Interface { public: - Interface(); + HID_SPVR_Interface(); std::string GetPortName() const override { return "hid:SPVR"; } }; -} // namespace +} // namespace HID +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index 7f464705f..1d0accefe 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -6,54 +6,10 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" -#include "hid_user.h" +#include "core/hle/service/hid/hid_user.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_User - -namespace HID_User { - - -// TODO(peachum): -// Add a method for setting analog input from joystick device for the circle Pad. -// -// This method should: -// * Be called after both PadButton<Press, Release>(). -// * Be called before PadUpdateComplete() -// * Set current PadEntry.circle_pad_<axis> using analog data -// * Set PadData.raw_circle_pad_data -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 -// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 -// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 - - -/** - * HID_User::GetIPCHandles service function - * Inputs: - * None - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Unused - * 3 : Handle to HID_User shared memory - * 4 : Event signaled by HID_User - * 5 : Event signaled by HID_User - * 6 : Event signaled by HID_User - * 7 : Gyroscope event - * 8 : Event signaled by HID_User - */ -void GetIPCHandles(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = 0; // No error - // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) - cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom(); - cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom(); - cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom(); - cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); - cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); -} +namespace Service { +namespace HID { const Interface::FunctionInfo FunctionTable[] = { {0x000A0000, GetIPCHandles, "GetIPCHandles"}, @@ -66,11 +22,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00170000, nullptr, "GetSoundVolume"}, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +HID_U_Interface::HID_U_Interface() { Register(FunctionTable); } -} // namespace +} // namespace HID +} // namespace Service diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h index 1d9929e67..0eeec2c25 100644 --- a/src/core/hle/service/hid/hid_user.h +++ b/src/core/hle/service/hid/hid_user.h @@ -6,24 +6,23 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_User - // This service is used for interfacing to physical user controls. // Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad. -namespace HID_User { - +namespace Service { +namespace HID { + /** * HID service interface. */ -class Interface : public Service::Interface { +class HID_U_Interface : public Service::Interface { public: - Interface(); + HID_U_Interface(); std::string GetPortName() const override { return "hid:USER"; } }; -} // namespace +} // namespace HID +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp new file mode 100644 index 000000000..56c918d4f --- /dev/null +++ b/src/core/hle/service/ptm/ptm.cpp @@ -0,0 +1,76 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/service.h" +#include "core/hle/service/fs/archive.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ptm_play.h" +#include "core/hle/service/ptm/ptm_sysm.h" +#include "core/hle/service/ptm/ptm_u.h" + +namespace Service { +namespace PTM { + +/// Values for the default gamecoin.dat file +static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; + +/// Id of the SharedExtData archive used by the PTM process +static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; + +static bool shell_open = true; + +static bool battery_is_charging = true; + +u32 GetAdapterState() { + // TODO(purpasmart96): This function is only a stub, + // it returns a valid result without implementing full functionality. + return battery_is_charging ? 1 : 0; +} + +u32 GetShellState() { + return shell_open ? 1 : 0; +} + +ChargeLevels GetBatteryLevel() { + // TODO(purpasmart96): This function is only a stub, + // it returns a valid result without implementing full functionality. + return ChargeLevels::CompletelyFull; // Set to a completely full battery +} + +void PTMInit() { + AddService(new PTM_Play_Interface); + AddService(new PTM_Sysm_Interface); + AddService(new PTM_U_Interface); + + // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't exist + FileSys::Path archive_path(ptm_shared_extdata_id); + auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); + // If the archive didn't exist, create the files inside + if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { + // Format the archive to create the directories + Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); + // Open it again to get a valid archive now that the folder exists + archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); + ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); + + FileSys::Path gamecoin_path("gamecoin.dat"); + FileSys::Mode open_mode = {}; + open_mode.write_flag = 1; + open_mode.create_flag = 1; + // Open the file and write the default gamecoin information + auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); + if (gamecoin_result.Succeeded()) { + auto gamecoin = gamecoin_result.MoveFrom(); + gamecoin->backend->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin)); + gamecoin->backend->Close(); + } + } +} + +void PTMShutdown() { + +} + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h new file mode 100644 index 000000000..f697aae4d --- /dev/null +++ b/src/core/hle/service/ptm/ptm.h @@ -0,0 +1,65 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "core/hle/result.h" + +namespace Service { +namespace PTM { + +/// Charge levels used by PTM functions +enum class ChargeLevels : u32 { + CriticalBattery = 1, + LowBattery = 2, + HalfFull = 3, + MostlyFull = 4, + CompletelyFull = 5, +}; + +/** + * Represents the gamecoin file structure in the SharedExtData archive + * More information in 3dbrew (http://www.3dbrew.org/wiki/Extdata#Shared_Extdata_0xf000000b_gamecoin.dat) + */ +struct GameCoin { + u32 magic; ///< Magic number: 0x4F00 + u16 total_coins; ///< Total Play Coins + u16 total_coins_on_date; ///< Total Play Coins obtained on the date stored below. + u32 step_count; ///< Total step count at the time a new Play Coin was obtained. + u32 last_step_count; ///< Step count for the day the last Play Coin was obtained + u16 year; + u8 month; + u8 day; +}; + +/** + * Returns whether the battery is charging or not. + * It is unknown if GetAdapterState is the same as GetBatteryChargeState, + * it is likely to just be a duplicate function of GetBatteryChargeState + * that controls another part of the HW. + * @returns 1 if the battery is charging, and 0 otherwise. + */ +u32 GetAdapterState(); + +/** + * Returns whether the 3DS's physical shell casing is open or closed + * @returns 1 if the shell is open, and 0 if otherwise + */ +u32 GetShellState(); + +/** + * Get the current battery's charge level. + * @returns The battery's charge level. + */ +ChargeLevels GetBatteryLevel(); + +/// Initialize the PTM service +void PTMInit(); + +/// Shutdown the PTM service +void PTMShutdown(); + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp index f21d9088e..8e8ae8558 100644 --- a/src/core/hle/service/ptm_play.cpp +++ b/src/core/hle/service/ptm/ptm_play.cpp @@ -3,12 +3,10 @@ // Refer to the license.txt file included. #include "core/hle/hle.h" -#include "core/hle/service/ptm_play.h" +#include "core/hle/service/ptm/ptm_play.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_PLAY - -namespace PTM_PLAY { +namespace Service { +namespace PTM { const Interface::FunctionInfo FunctionTable[] = { { 0x08070082, nullptr, "GetPlayHistory" }, @@ -17,11 +15,9 @@ const Interface::FunctionInfo FunctionTable[] = { { 0x080B0080, nullptr, "CalcPlayHistoryStart" }, }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +PTM_Play_Interface::PTM_Play_Interface() { Register(FunctionTable); } -} // namespace +} // namespace PTM +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h index 2f4f0d6fd..e5c3e04df 100644 --- a/src/core/hle/service/ptm_play.h +++ b/src/core/hle/service/ptm/ptm_play.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_PLAY +namespace Service { +namespace PTM { -namespace PTM_PLAY { - -class Interface : public Service::Interface { +class PTM_Play_Interface : public Service::Interface { public: - Interface(); + PTM_Play_Interface(); std::string GetPortName() const override { return "ptm:play"; } }; -} // namespace +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index 96ef2dce0..2d841f69c 100644 --- a/src/core/hle/service/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -5,12 +5,22 @@ #include "common/make_unique.h" #include "core/file_sys/archive_extsavedata.h" #include "core/hle/hle.h" -#include "core/hle/service/ptm_sysm.h" +#include "core/hle/service/ptm/ptm_sysm.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_SYSM +namespace Service { +namespace PTM { -namespace PTM_SYSM { +/** + * Returns whether the system is powering off (?) + * Outputs: + * 1: Result code, 0 on success, otherwise error code + * 2: Whether the system is going through a power off + */ +void IsLegacyPowerOff(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; +} const Interface::FunctionInfo FunctionTable[] = { {0x040100C0, nullptr, "SetRtcAlarmEx"}, @@ -37,7 +47,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080C0080, nullptr, "SetUserTime"}, {0x080D0000, nullptr, "InvalidateSystemTime"}, {0x080E0140, nullptr, "NotifyPlayEvent"}, - {0x080F0000, nullptr, "IsLegacyPowerOff"}, + {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"}, {0x08100000, nullptr, "ClearLegacyPowerOff"}, {0x08110000, nullptr, "GetShellStatus"}, {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, @@ -45,11 +55,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} }; -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { +PTM_Sysm_Interface::PTM_Sysm_Interface() { Register(FunctionTable); } -} // namespace +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h index 0f267b214..e37f20546 100644 --- a/src/core/hle/service/ptm_sysm.h +++ b/src/core/hle/service/ptm/ptm_sysm.h @@ -6,18 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_SYSM +namespace Service { +namespace PTM { -namespace PTM_SYSM { - -class Interface : public Service::Interface { +class PTM_Sysm_Interface : public Interface { public: - Interface(); + PTM_Sysm_Interface(); std::string GetPortName() const override { return "ptm:sysm"; } }; -} // namespace +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp new file mode 100644 index 000000000..0af7c8bf6 --- /dev/null +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -0,0 +1,99 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/make_unique.h" + +#include "core/hle/hle.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ptm_u.h" + +namespace Service { +namespace PTM { + +/** + * PTM_U::GetAdapterState service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output of function, 0 = not charging, 1 = charging. + */ +static void GetAdapterState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = GetAdapterState(); + + LOG_WARNING(Service_PTM, "(STUBBED) called"); +} + +/* + * PTM_User::GetShellState service function. + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) + */ +static void GetShellState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = GetShellState(); +} + +/** + * PTM_U::GetBatteryLevel service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, + * 3 = half full battery, 2 = low battery, 1 = critical battery. + */ +static void GetBatteryLevel(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(GetBatteryLevel()); + + LOG_WARNING(Service_PTM, "(STUBBED) called"); +} + +/** + * PTM_U::GetBatteryChargeState service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output of function, 0 = not charging, 1 = charging. + */ +static void GetBatteryChargeState(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // TODO(purpasmart96): This function is only a stub, + // it returns a valid result without implementing full functionality. + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = GetAdapterState(); + + LOG_WARNING(Service_PTM, "(STUBBED) called"); +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010002, nullptr, "RegisterAlarmClient"}, + {0x00020080, nullptr, "SetRtcAlarm"}, + {0x00030000, nullptr, "GetRtcAlarm"}, + {0x00040000, nullptr, "CancelRtcAlarm"}, + {0x00050000, GetAdapterState, "GetAdapterState"}, + {0x00060000, GetShellState, "GetShellState"}, + {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, + {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, + {0x00090000, nullptr, "GetPedometerState"}, + {0x000A0042, nullptr, "GetStepHistoryEntry"}, + {0x000B00C2, nullptr, "GetStepHistory"}, + {0x000C0000, nullptr, "GetTotalStepCount"}, + {0x000D0040, nullptr, "SetPedometerRecordingMode"}, + {0x000E0000, nullptr, "GetPedometerRecordingMode"}, + {0x000F0084, nullptr, "GetStepHistoryAll"}, +}; + +PTM_U_Interface::PTM_U_Interface() { + Register(FunctionTable); +} + +} // namespace PTM +} // namespace Service diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h index a44624fd5..bf132f610 100644 --- a/src/core/hle/service/ptm_u.h +++ b/src/core/hle/service/ptm/ptm_u.h @@ -6,20 +6,17 @@ #include "core/hle/service/service.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_U +namespace Service { +namespace PTM { -// ptm service - -namespace PTM_U { - -class Interface : public Service::Interface { +class PTM_U_Interface : public Interface { public: - Interface(); + PTM_U_Interface(); std::string GetPortName() const override { return "ptm:u"; } }; -} // namespace +} // namespace PTM +} // namespace Service
\ No newline at end of file diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp deleted file mode 100644 index 7121d837c..000000000 --- a/src/core/hle/service/ptm_u.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/make_unique.h" - -#include "core/hle/hle.h" -#include "core/hle/service/fs/archive.h" -#include "core/hle/service/ptm_u.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace PTM_U - -namespace PTM_U { - -/** - * Represents the gamecoin file structure in the SharedExtData archive - * More information in 3dbrew (http://www.3dbrew.org/wiki/Extdata#Shared_Extdata_0xf000000b_gamecoin.dat) - */ -struct GameCoin { - u32 magic; ///< Magic number: 0x4F00 - u16 total_coins; ///< Total Play Coins - u16 total_coins_on_date; ///< Total Play Coins obtained on the date stored below. - u32 step_count; ///< Total step count at the time a new Play Coin was obtained. - u32 last_step_count; ///< Step count for the day the last Play Coin was obtained - u16 year; - u8 month; - u8 day; -}; -static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; -static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; - -/// Charge levels used by PTM functions -enum class ChargeLevels : u32 { - CriticalBattery = 1, - LowBattery = 2, - HalfFull = 3, - MostlyFull = 4, - CompletelyFull = 5, -}; - -static bool shell_open = true; - -static bool battery_is_charging = true; - -/** - * It is unknown if GetAdapterState is the same as GetBatteryChargeState, - * it is likely to just be a duplicate function of GetBatteryChargeState - * that controls another part of the HW. - * PTM_U::GetAdapterState service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output of function, 0 = not charging, 1 = charging. - */ -static void GetAdapterState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. - - cmd_buff[1] = 0; // No error - cmd_buff[2] = battery_is_charging ? 1 : 0; - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - -/* - * PTM_User::GetShellState service function. - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) - */ -static void GetShellState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = 0; - cmd_buff[2] = shell_open ? 1 : 0; -} - -/** - * PTM_U::GetBatteryLevel service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery, - * 3 = half full battery, 2 = low battery, 1 = critical battery. - */ -static void GetBatteryLevel(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. - - cmd_buff[1] = 0; // No error - cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - -/** - * PTM_U::GetBatteryChargeState service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output of function, 0 = not charging, 1 = charging. - */ -static void GetBatteryChargeState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. - - cmd_buff[1] = 0; // No error - cmd_buff[2] = battery_is_charging ? 1 : 0; - - LOG_WARNING(Service_PTM, "(STUBBED) called"); -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x00010002, nullptr, "RegisterAlarmClient"}, - {0x00020080, nullptr, "SetRtcAlarm"}, - {0x00030000, nullptr, "GetRtcAlarm"}, - {0x00040000, nullptr, "CancelRtcAlarm"}, - {0x00050000, GetAdapterState, "GetAdapterState"}, - {0x00060000, GetShellState, "GetShellState"}, - {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, - {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, - {0x00090000, nullptr, "GetPedometerState"}, - {0x000A0042, nullptr, "GetStepHistoryEntry"}, - {0x000B00C2, nullptr, "GetStepHistory"}, - {0x000C0000, nullptr, "GetTotalStepCount"}, - {0x000D0040, nullptr, "SetPedometerRecordingMode"}, - {0x000E0000, nullptr, "GetPedometerRecordingMode"}, - {0x000F0084, nullptr, "GetStepHistoryAll"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - Register(FunctionTable); - - // Open the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file - FileSys::Path archive_path(ptm_shared_extdata_id); - auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); - // If the archive didn't exist, create the files inside - if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { - // Format the archive to create the directories - Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); - // Open it again to get a valid archive now that the folder exists - archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); - ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); - - FileSys::Path gamecoin_path("gamecoin.dat"); - FileSys::Mode open_mode = {}; - open_mode.write_flag = 1; - open_mode.create_flag = 1; - // Open the file and write the default gamecoin information - auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); - if (gamecoin_result.Succeeded()) { - auto gamecoin = gamecoin_result.MoveFrom(); - gamecoin->backend->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin)); - gamecoin->backend->Close(); - } - } -} - -} // namespace diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5dce8068e..91f13cd7e 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -11,26 +11,17 @@ #include "core/hle/service/am_app.h" #include "core/hle/service/am_net.h" #include "core/hle/service/am_sys.h" -#include "core/hle/service/apt_a.h" -#include "core/hle/service/apt_s.h" -#include "core/hle/service/apt_u.h" #include "core/hle/service/boss_p.h" #include "core/hle/service/boss_u.h" #include "core/hle/service/cam_u.h" #include "core/hle/service/cecd_u.h" #include "core/hle/service/cecd_s.h" -#include "core/hle/service/cfg/cfg_i.h" -#include "core/hle/service/cfg/cfg_s.h" -#include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/csnd_snd.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/err_f.h" -#include "core/hle/service/fs/fs_user.h" #include "core/hle/service/frd_a.h" #include "core/hle/service/frd_u.h" #include "core/hle/service/gsp_gpu.h" -#include "core/hle/service/hid/hid_spvr.h" -#include "core/hle/service/hid/hid_user.h" #include "core/hle/service/gsp_lcd.h" #include "core/hle/service/http_c.h" #include "core/hle/service/ir_rst.h" @@ -44,14 +35,17 @@ #include "core/hle/service/ns_s.h" #include "core/hle/service/nwm_uds.h" #include "core/hle/service/pm_app.h" -#include "core/hle/service/ptm_play.h" -#include "core/hle/service/ptm_u.h" -#include "core/hle/service/ptm_sysm.h" #include "core/hle/service/soc_u.h" #include "core/hle/service/srv.h" #include "core/hle/service/ssl_c.h" #include "core/hle/service/y2r_u.h" +#include "core/hle/service/apt/apt.h" +#include "core/hle/service/fs/archive.h" +#include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/ptm/ptm.h" + namespace Service { std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; @@ -60,12 +54,12 @@ std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; //////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface -static void AddNamedPort(Interface* interface) { - g_kernel_named_ports.emplace(interface->GetPortName(), interface); +static void AddNamedPort(Interface* interface_) { + g_kernel_named_ports.emplace(interface_->GetPortName(), interface_); } -static void AddService(Interface* interface) { - g_srv_services.emplace(interface->GetPortName(), interface); +void AddService(Interface* interface_) { + g_srv_services.emplace(interface_->GetPortName(), interface_); } /// Initialize ServiceManager @@ -73,31 +67,28 @@ void Init() { AddNamedPort(new SRV::Interface); AddNamedPort(new ERR_F::Interface); + Service::FS::ArchiveInit(); + Service::CFG::CFGInit(); + Service::APT::APTInit(); + Service::PTM::PTMInit(); + Service::HID::HIDInit(); + AddService(new AC_U::Interface); AddService(new ACT_U::Interface); AddService(new AM_APP::Interface); AddService(new AM_NET::Interface); AddService(new AM_SYS::Interface); - AddService(new APT_A::Interface); - AddService(new APT_S::Interface); - AddService(new APT_U::Interface); AddService(new BOSS_P::Interface); AddService(new BOSS_U::Interface); AddService(new CAM_U::Interface); AddService(new CECD_S::Interface); AddService(new CECD_U::Interface); - AddService(new CFG_I::Interface); - AddService(new CFG_S::Interface); - AddService(new CFG_U::Interface); AddService(new CSND_SND::Interface); AddService(new DSP_DSP::Interface); AddService(new FRD_A::Interface); AddService(new FRD_U::Interface); - AddService(new FS::FSUserInterface); AddService(new GSP_GPU::Interface); AddService(new GSP_LCD::Interface); - AddService(new HID_User::Interface); - AddService(new HID_SPVR::Interface); AddService(new HTTP_C::Interface); AddService(new IR_RST::Interface); AddService(new IR_U::Interface); @@ -110,9 +101,6 @@ void Init() { AddService(new NS_S::Interface); AddService(new NWM_UDS::Interface); AddService(new PM_APP::Interface); - AddService(new PTM_PLAY::Interface); - AddService(new PTM_U::Interface); - AddService(new PTM_SYSM::Interface); AddService(new SOC_U::Interface); AddService(new SSL_C::Interface); AddService(new Y2R_U::Interface); @@ -122,6 +110,12 @@ void Init() { /// Shutdown ServiceManager void Shutdown() { + Service::HID::HIDShutdown(); + Service::PTM::PTMShutdown(); + Service::APT::APTShutdown(); + Service::CFG::CFGShutdown(); + Service::FS::ArchiveShutdown(); + g_srv_services.clear(); g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 3370f9f9b..bfe16ebad 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -120,4 +120,7 @@ extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_na /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; +/// Adds a service to the services table +void AddService(Interface* interface_); + } // namespace diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index a58e04d6d..6607965e1 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -11,6 +11,8 @@ namespace Y2R_U { +static Kernel::SharedPtr<Kernel::Event> completion_event = 0; + /** * Y2R_U::IsBusyConversion service function * Outputs: @@ -26,13 +28,26 @@ static void IsBusyConversion(Service::Interface* self) { LOG_WARNING(Service, "(STUBBED) called"); } +/** + * Y2R_U::GetTransferEndEvent service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : The handle of the completion event + */ +static void GetTransferEndEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010040, nullptr, "SetInputFormat"}, {0x00030040, nullptr, "SetOutputFormat"}, {0x00050040, nullptr, "SetRotation"}, {0x00070040, nullptr, "SetBlockAlignment"}, {0x000D0040, nullptr, "SetTransferEndInterrupt"}, - {0x000F0000, nullptr, "GetTransferEndEvent"}, + {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, {0x00100102, nullptr, "SetSendingY"}, {0x00110102, nullptr, "SetSendingU"}, {0x00120102, nullptr, "SetSendingV"}, @@ -53,6 +68,8 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { + completion_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "Y2R:Completed"); + Register(FunctionTable); } diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index bd7d92cd1..424ce2ca7 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -18,8 +18,9 @@ #include "core/hw/gpu.h" #include "video_core/command_processor.h" +#include "video_core/utils.h" #include "video_core/video_core.h" - +#include <video_core/color.h> namespace GPU { @@ -112,61 +113,109 @@ inline void Write(u32 addr, const T data) { { const auto& config = g_regs.display_transfer_config; if (config.trigger & 1) { - u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress())); - u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress())); - - // Cheap emulation of horizontal scaling: Just skip each second pixel of the - // input framebuffer. We keep track of this in the pixel_skip variable. - unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1; - - u32 output_width = config.output_width / pixel_skip; - - for (u32 y = 0; y < config.output_height; ++y) { - // TODO: Why does the register seem to hold twice the framebuffer width? + u8* src_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress())); + u8* dst_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress())); + + unsigned horizontal_scale = (config.scale_horizontally != 0) ? 2 : 1; + unsigned vertical_scale = (config.scale_vertically != 0) ? 2 : 1; + + u32 output_width = config.output_width / horizontal_scale; + u32 output_height = config.output_height / vertical_scale; + + if (config.raw_copy) { + // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions + // TODO(Subv): Verify if raw copies perform scaling + memcpy(dst_pointer, src_pointer, config.output_width * config.output_height * + GPU::Regs::BytesPerPixel(config.output_format)); + + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), flags 0x%08X, Raw copy", + config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), + config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), + config.GetPhysicalOutputAddress(), config.output_width.Value(), config.output_height.Value(), + config.output_format.Value(), config.flags); + + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); + break; + } + // TODO(Subv): Blend the pixels when horizontal / vertical 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) { - struct { - int r, g, b, a; - } source_color = { 0, 0, 0, 0 }; + Math::Vec4<u8> src_color = { 0, 0, 0, 0 }; + + u32 scaled_x = x * horizontal_scale; + u32 scaled_y = y * vertical_scale; + + u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); + u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); + u32 src_offset; + 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 = (scaled_x + scaled_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Interpret the input as tiled and the output as linear + u32 coarse_y = scaled_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(scaled_x, scaled_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } + const u8* src_pixel = src_pointer + src_offset; switch (config.input_format) { case Regs::PixelFormat::RGBA8: - { - // TODO: Most likely got the component order messed up. - u8* srcptr = source_pointer + (x * pixel_skip + y * config.input_width) * 4; - source_color.r = srcptr[0]; // blue - source_color.g = srcptr[1]; // green - source_color.b = srcptr[2]; // red - source_color.a = srcptr[3]; // alpha + 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; } + u8* dst_pixel = dst_pointer + dst_offset; switch (config.output_format) { - /*case Regs::PixelFormat::RGBA8: - { - // TODO: Untested - u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.output_width * 4); - dstptr[0] = source_color.r; - dstptr[1] = source_color.g; - dstptr[2] = source_color.b; - dstptr[3] = source_color.a; + case Regs::PixelFormat::RGBA8: + Color::EncodeRGBA8(src_color, dst_pixel); break; - }*/ case Regs::PixelFormat::RGB8: - { - // TODO: Most likely got the component order messed up. - u8* dstptr = dest_pointer + (x + y * output_width) * 3; - dstptr[0] = source_color.r; // blue - dstptr[1] = source_color.g; // green - dstptr[2] = source_color.b; // red + Color::EncodeRGB8(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGB565: + Color::EncodeRGB565(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGB5A1: + Color::EncodeRGB5A1(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGBA4: + Color::EncodeRGBA4(src_color, dst_pixel); break; - } default: LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); @@ -175,11 +224,11 @@ inline void Write(u32 addr, const T data) { } } - LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", - config.output_height * output_width * 4, - config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height, - config.GetPhysicalOutputAddress(), (u32)output_width, (u32)config.output_height, - config.output_format.Value()); + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", + config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), + config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), + config.GetPhysicalOutputAddress(), output_width, output_height, + config.output_format.Value(), config.flags); GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index df9aa0d71..737b1e968 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -34,13 +34,6 @@ namespace GPU { // MMIO region 0x1EFxxxxx struct Regs { -// helper macro to properly align structure members. -// Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121", -// depending on the current source line to make sure variable names are unique. -#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y -#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) -#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)] - // helper macro to make sure the defined structures are of the expected size. #if defined(_MSC_VER) // TODO: MSVC does not support using sizeof() on non-static data members even though this @@ -53,7 +46,7 @@ struct Regs { "Structure size and register block length don't match") #endif - // All of those formats are described in reverse byte order, since the 3DS is little-endian. + // Components are laid out in reverse byte order, most significant bits first. enum class PixelFormat : u32 { RGBA8 = 0, RGB8 = 1, @@ -199,12 +192,13 @@ struct Regs { u32 flags; BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true + 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< 8, 3, PixelFormat> input_format; BitField<12, 3, PixelFormat> output_format; - BitField<16, 1, u32> output_tiled; // stores output in a tiled format - // TODO: Not really sure if this actually scales, or even resizes at all. BitField<24, 1, u32> scale_horizontally; + BitField<25, 1, u32> scale_vertically; }; INSERT_PADDING_WORDS(0x1); @@ -238,10 +232,6 @@ struct Regs { INSERT_PADDING_WORDS(0x9c3); -#undef INSERT_PADDING_WORDS_HELPER1 -#undef INSERT_PADDING_WORDS_HELPER2 -#undef INSERT_PADDING_WORDS - static inline size_t NumIds() { return sizeof(Regs) / sizeof(u32); } diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index 4f93c0e64..48f61db4e 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp @@ -236,30 +236,12 @@ u8 Read8(const VAddr addr) { u16 Read16(const VAddr addr) { u16_le data = 0; Read<u16_le>(data, addr); - - // Check for 16-bit unaligned memory reads... - if (addr & 1) { - // TODO(bunnei): Implement 16-bit unaligned memory reads - LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!"); - } - return (u16)data; } u32 Read32(const VAddr addr) { u32_le data = 0; Read<u32_le>(data, addr); - - // Check for 32-bit unaligned memory reads... - if (addr & 3) { - // ARM allows for unaligned memory reads, however older ARM architectures read out memory - // from unaligned addresses in a shifted way. Our ARM CPU core (SkyEye) corrects for this, - // so therefore expects the memory to be read out in this manner. - // TODO(bunnei): Determine if this is necessary - perhaps it is OK to remove this from both - // SkyEye and here? - int shift = (addr & 3) * 8; - data = (data << shift) | (data >> (32 - shift)); - } return (u32)data; } diff --git a/src/core/settings.h b/src/core/settings.h index e62dd4358..870eea958 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -16,6 +16,8 @@ struct Values { int pad_y_key; int pad_l_key; int pad_r_key; + int pad_zl_key; + int pad_zr_key; int pad_start_key; int pad_select_key; int pad_home_key; @@ -27,6 +29,10 @@ struct Values { int pad_sdown_key; int pad_sleft_key; int pad_sright_key; + int pad_cup_key; + int pad_cdown_key; + int pad_cleft_key; + int pad_cright_key; // Core int gpu_refresh_rate; |