diff options
47 files changed, 913 insertions, 624 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 1ebe74941..f5b4069c7 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -63,6 +63,9 @@ void Config::ReadValues() { | |||
| 63 | // Data Storage | 63 | // Data Storage |
| 64 | Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); | 64 | Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); |
| 65 | 65 | ||
| 66 | // System Region | ||
| 67 | Settings::values.region_value = glfw_config->GetInteger("System Region", "region_value", 1); | ||
| 68 | |||
| 66 | // Miscellaneous | 69 | // Miscellaneous |
| 67 | Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); | 70 | Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); |
| 68 | } | 71 | } |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 3f523857f..be4b289bd 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -33,6 +33,9 @@ frame_skip = ## 0: No frameskip (default), 1 : 2x frameskip, 2 : 4x frameskip, e | |||
| 33 | [Data Storage] | 33 | [Data Storage] |
| 34 | use_virtual_sd = | 34 | use_virtual_sd = |
| 35 | 35 | ||
| 36 | [System Region] | ||
| 37 | region_value = ## 0 : Japan, 1 : Usa (default), 2 : Europe, 3 : Australia, 4 : China, 5 : Korea, 6 : Taiwan. | ||
| 38 | |||
| 36 | [Miscellaneous] | 39 | [Miscellaneous] |
| 37 | log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | 40 | log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical |
| 38 | )"; | 41 | )"; |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 955c8a4e0..76aeaedd0 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -51,6 +51,10 @@ void Config::ReadValues() { | |||
| 51 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); | 51 | Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); |
| 52 | qt_config->endGroup(); | 52 | qt_config->endGroup(); |
| 53 | 53 | ||
| 54 | qt_config->beginGroup("System Region"); | ||
| 55 | Settings::values.region_value = qt_config->value("region_value", 1).toInt(); | ||
| 56 | qt_config->endGroup(); | ||
| 57 | |||
| 54 | qt_config->beginGroup("Miscellaneous"); | 58 | qt_config->beginGroup("Miscellaneous"); |
| 55 | Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); | 59 | Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); |
| 56 | qt_config->endGroup(); | 60 | qt_config->endGroup(); |
| @@ -86,6 +90,10 @@ void Config::SaveValues() { | |||
| 86 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); | 90 | qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); |
| 87 | qt_config->endGroup(); | 91 | qt_config->endGroup(); |
| 88 | 92 | ||
| 93 | qt_config->beginGroup("System Region"); | ||
| 94 | qt_config->setValue("region_value", Settings::values.region_value); | ||
| 95 | qt_config->endGroup(); | ||
| 96 | |||
| 89 | qt_config->beginGroup("Miscellaneous"); | 97 | qt_config->beginGroup("Miscellaneous"); |
| 90 | qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); | 98 | qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); |
| 91 | qt_config->endGroup(); | 99 | qt_config->endGroup(); |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 897ef36b8..6c5ca3968 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -57,6 +57,7 @@ enum class Class : ClassType { | |||
| 57 | Service_GSP, ///< The GSP (GPU control) service | 57 | Service_GSP, ///< The GSP (GPU control) service |
| 58 | Service_AC, ///< The AC (WiFi status) service | 58 | Service_AC, ///< The AC (WiFi status) service |
| 59 | Service_PTM, ///< The PTM (Power status & misc.) service | 59 | Service_PTM, ///< The PTM (Power status & misc.) service |
| 60 | Service_LDR, ///< The LDR (3ds dll loader) service | ||
| 60 | Service_CFG, ///< The CFG (Configuration) service | 61 | Service_CFG, ///< The CFG (Configuration) service |
| 61 | Service_DSP, ///< The DSP (DSP control) service | 62 | Service_DSP, ///< The DSP (DSP control) service |
| 62 | Service_HID, ///< The HID (User input) service | 63 | Service_HID, ///< The HID (User input) service |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e612f7439..ef37ee055 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -86,6 +86,15 @@ public: | |||
| 86 | virtual void AddTicks(u64 ticks) = 0; | 86 | virtual void AddTicks(u64 ticks) = 0; |
| 87 | 87 | ||
| 88 | /** | 88 | /** |
| 89 | * Initializes a CPU context for use on this CPU | ||
| 90 | * @param context Thread context to reset | ||
| 91 | * @param stack_top Pointer to the top of the stack | ||
| 92 | * @param entry_point Entry point for execution | ||
| 93 | * @param arg User argument for thread | ||
| 94 | */ | ||
| 95 | virtual void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) = 0; | ||
| 96 | |||
| 97 | /** | ||
| 89 | * Saves the current CPU context | 98 | * Saves the current CPU context |
| 90 | * @param ctx Thread context to save | 99 | * @param ctx Thread context to save |
| 91 | */ | 100 | */ |
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index f6628ca33..68fddc94f 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -93,6 +93,16 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { | |||
| 93 | AddTicks(ticks_executed); | 93 | AddTicks(ticks_executed); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) { | ||
| 97 | memset(&context, 0, sizeof(Core::ThreadContext)); | ||
| 98 | |||
| 99 | context.cpu_registers[0] = arg; | ||
| 100 | context.pc = entry_point; | ||
| 101 | context.sp = stack_top; | ||
| 102 | context.cpsr = 0x1F; // Usermode | ||
| 103 | context.mode = 8; // Instructs dyncom CPU core to start execution as if it's "resuming" a thread. | ||
| 104 | } | ||
| 105 | |||
| 96 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { | 106 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { |
| 97 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | 107 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); |
| 98 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | 108 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index f16fb070c..9e2dda843 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -13,79 +13,24 @@ | |||
| 13 | 13 | ||
| 14 | class ARM_DynCom final : virtual public ARM_Interface { | 14 | class ARM_DynCom final : virtual public ARM_Interface { |
| 15 | public: | 15 | public: |
| 16 | |||
| 17 | ARM_DynCom(); | 16 | ARM_DynCom(); |
| 18 | ~ARM_DynCom(); | 17 | ~ARM_DynCom(); |
| 19 | 18 | ||
| 20 | /** | ||
| 21 | * Set the Program Counter to an address | ||
| 22 | * @param pc Address to set PC to | ||
| 23 | */ | ||
| 24 | void SetPC(u32 pc) override; | 19 | void SetPC(u32 pc) override; |
| 25 | |||
| 26 | /* | ||
| 27 | * Get the current Program Counter | ||
| 28 | * @return Returns current PC | ||
| 29 | */ | ||
| 30 | u32 GetPC() const override; | 20 | u32 GetPC() const override; |
| 31 | |||
| 32 | /** | ||
| 33 | * Get an ARM register | ||
| 34 | * @param index Register index (0-15) | ||
| 35 | * @return Returns the value in the register | ||
| 36 | */ | ||
| 37 | u32 GetReg(int index) const override; | 21 | u32 GetReg(int index) const override; |
| 38 | |||
| 39 | /** | ||
| 40 | * Set an ARM register | ||
| 41 | * @param index Register index (0-15) | ||
| 42 | * @param value Value to set register to | ||
| 43 | */ | ||
| 44 | void SetReg(int index, u32 value) override; | 22 | void SetReg(int index, u32 value) override; |
| 45 | |||
| 46 | /** | ||
| 47 | * Get the current CPSR register | ||
| 48 | * @return Returns the value of the CPSR register | ||
| 49 | */ | ||
| 50 | u32 GetCPSR() const override; | 23 | u32 GetCPSR() const override; |
| 51 | |||
| 52 | /** | ||
| 53 | * Set the current CPSR register | ||
| 54 | * @param cpsr Value to set CPSR to | ||
| 55 | */ | ||
| 56 | void SetCPSR(u32 cpsr) override; | 24 | void SetCPSR(u32 cpsr) override; |
| 57 | 25 | ||
| 58 | /** | ||
| 59 | * Returns the number of clock ticks since the last reset | ||
| 60 | * @return Returns number of clock ticks | ||
| 61 | */ | ||
| 62 | u64 GetTicks() const override; | 26 | u64 GetTicks() const override; |
| 63 | |||
| 64 | /** | ||
| 65 | * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) | ||
| 66 | * @param ticks Number of ticks to advance the CPU core | ||
| 67 | */ | ||
| 68 | void AddTicks(u64 ticks) override; | 27 | void AddTicks(u64 ticks) override; |
| 69 | 28 | ||
| 70 | /** | 29 | void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg); |
| 71 | * Saves the current CPU context | ||
| 72 | * @param ctx Thread context to save | ||
| 73 | */ | ||
| 74 | void SaveContext(Core::ThreadContext& ctx) override; | 30 | void SaveContext(Core::ThreadContext& ctx) override; |
| 75 | |||
| 76 | /** | ||
| 77 | * Loads a CPU context | ||
| 78 | * @param ctx Thread context to load | ||
| 79 | */ | ||
| 80 | void LoadContext(const Core::ThreadContext& ctx) override; | 31 | void LoadContext(const Core::ThreadContext& ctx) override; |
| 81 | 32 | ||
| 82 | /// Prepare core for thread reschedule (if needed to correctly handle state) | ||
| 83 | void PrepareReschedule() override; | 33 | void PrepareReschedule() override; |
| 84 | |||
| 85 | /** | ||
| 86 | * Executes the given number of instructions | ||
| 87 | * @param num_instructions Number of instructions to executes | ||
| 88 | */ | ||
| 89 | void ExecuteInstructions(int num_instructions) override; | 34 | void ExecuteInstructions(int num_instructions) override; |
| 90 | 35 | ||
| 91 | private: | 36 | private: |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 786ea91cb..c91943f24 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -4725,20 +4725,20 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 4725 | if (inst_cream->cp_num == 15) { | 4725 | if (inst_cream->cp_num == 15) { |
| 4726 | if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { | 4726 | if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { |
| 4727 | CP15_REG(CP15_MAIN_ID) = RD; | 4727 | CP15_REG(CP15_MAIN_ID) = RD; |
| 4728 | } else if(CRn == 1 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4729 | CP15_REG(CP15_CONTROL) = RD; | ||
| 4728 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { | 4730 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { |
| 4729 | CP15_REG(CP15_AUXILIARY_CONTROL) = RD; | 4731 | CP15_REG(CP15_AUXILIARY_CONTROL) = RD; |
| 4730 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { | 4732 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { |
| 4731 | CP15_REG(CP15_COPROCESSOR_ACCESS_CONTROL) = RD; | 4733 | CP15_REG(CP15_COPROCESSOR_ACCESS_CONTROL) = RD; |
| 4732 | } else if(CRn == 1 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4733 | CP15_REG(CP15_CONTROL) = RD; | ||
| 4734 | } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4735 | CP15_REG(CP15_DOMAIN_ACCESS_CONTROL) = RD; | ||
| 4736 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { | 4734 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { |
| 4737 | CP15_REG(CP15_TRANSLATION_BASE_TABLE_0) = RD; | 4735 | CP15_REG(CP15_TRANSLATION_BASE_TABLE_0) = RD; |
| 4738 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 1) { | 4736 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 1) { |
| 4739 | CP15_REG(CP15_TRANSLATION_BASE_TABLE_1) = RD; | 4737 | CP15_REG(CP15_TRANSLATION_BASE_TABLE_1) = RD; |
| 4740 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 2) { | 4738 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 2) { |
| 4741 | CP15_REG(CP15_TRANSLATION_BASE_CONTROL) = RD; | 4739 | CP15_REG(CP15_TRANSLATION_BASE_CONTROL) = RD; |
| 4740 | } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4741 | CP15_REG(CP15_DOMAIN_ACCESS_CONTROL) = RD; | ||
| 4742 | } else if(CRn == MMU_CACHE_OPS){ | 4742 | } else if(CRn == MMU_CACHE_OPS){ |
| 4743 | //LOG_WARNING(Core_ARM11, "cache operations have not implemented."); | 4743 | //LOG_WARNING(Core_ARM11, "cache operations have not implemented."); |
| 4744 | } else if(CRn == MMU_TLB_OPS){ | 4744 | } else if(CRn == MMU_TLB_OPS){ |
| @@ -4793,12 +4793,18 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 4793 | break; | 4793 | break; |
| 4794 | } | 4794 | } |
| 4795 | } else if(CRn == MMU_PID) { | 4795 | } else if(CRn == MMU_PID) { |
| 4796 | if(OPCODE_2 == 0) | 4796 | if(OPCODE_2 == 0) { |
| 4797 | CP15_REG(CP15_PID) = RD; | 4797 | CP15_REG(CP15_PID) = RD; |
| 4798 | else if(OPCODE_2 == 1) | 4798 | } else if(OPCODE_2 == 1) { |
| 4799 | CP15_REG(CP15_CONTEXT_ID) = RD; | 4799 | CP15_REG(CP15_CONTEXT_ID) = RD; |
| 4800 | else if(OPCODE_2 == 3) { | 4800 | } else if (OPCODE_2 == 2) { |
| 4801 | CP15_REG(CP15_THREAD_URO) = RD; | 4801 | CP15_REG(CP15_THREAD_UPRW) = RD; |
| 4802 | } else if(OPCODE_2 == 3) { | ||
| 4803 | if (InAPrivilegedMode(cpu)) | ||
| 4804 | CP15_REG(CP15_THREAD_URO) = RD; | ||
| 4805 | } else if (OPCODE_2 == 4) { | ||
| 4806 | if (InAPrivilegedMode(cpu)) | ||
| 4807 | CP15_REG(CP15_THREAD_PRW) = RD; | ||
| 4802 | } else { | 4808 | } else { |
| 4803 | LOG_ERROR(Core_ARM11, "mmu_mcr wrote UNKNOWN - reg %d", CRn); | 4809 | LOG_ERROR(Core_ARM11, "mmu_mcr wrote UNKNOWN - reg %d", CRn); |
| 4804 | } | 4810 | } |
| @@ -4886,31 +4892,40 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 4886 | if (inst_cream->cp_num == 15) { | 4892 | if (inst_cream->cp_num == 15) { |
| 4887 | if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { | 4893 | if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) { |
| 4888 | RD = cpu->CP15[CP15(CP15_MAIN_ID)]; | 4894 | RD = cpu->CP15[CP15(CP15_MAIN_ID)]; |
| 4895 | } else if (CRn == 0 && CRm == 0 && OPCODE_2 == 1) { | ||
| 4896 | RD = cpu->CP15[CP15(CP15_CACHE_TYPE)]; | ||
| 4889 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) { | 4897 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) { |
| 4890 | RD = cpu->CP15[CP15(CP15_CONTROL)]; | 4898 | RD = cpu->CP15[CP15(CP15_CONTROL)]; |
| 4891 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { | 4899 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) { |
| 4892 | RD = cpu->CP15[CP15(CP15_AUXILIARY_CONTROL)]; | 4900 | RD = cpu->CP15[CP15(CP15_AUXILIARY_CONTROL)]; |
| 4893 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { | 4901 | } else if (CRn == 1 && CRm == 0 && OPCODE_2 == 2) { |
| 4894 | RD = cpu->CP15[CP15(CP15_COPROCESSOR_ACCESS_CONTROL)]; | 4902 | RD = cpu->CP15[CP15(CP15_COPROCESSOR_ACCESS_CONTROL)]; |
| 4895 | } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4896 | RD = cpu->CP15[CP15(CP15_DOMAIN_ACCESS_CONTROL)]; | ||
| 4897 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { | 4903 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 0) { |
| 4898 | RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_TABLE_0)]; | 4904 | RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_TABLE_0)]; |
| 4905 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 1) { | ||
| 4906 | RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_TABLE_1)]; | ||
| 4907 | } else if (CRn == 2 && CRm == 0 && OPCODE_2 == 2) { | ||
| 4908 | RD = cpu->CP15[CP15(CP15_TRANSLATION_BASE_CONTROL)]; | ||
| 4909 | } else if (CRn == 3 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4910 | RD = cpu->CP15[CP15(CP15_DOMAIN_ACCESS_CONTROL)]; | ||
| 4899 | } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 0) { | 4911 | } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 0) { |
| 4900 | RD = cpu->CP15[CP15(CP15_FAULT_STATUS)]; | 4912 | RD = cpu->CP15[CP15(CP15_FAULT_STATUS)]; |
| 4901 | } else if (CRn == 6 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4902 | RD = cpu->CP15[CP15(CP15_FAULT_ADDRESS)]; | ||
| 4903 | } else if (CRn == 0 && CRm == 0 && OPCODE_2 == 1) { | ||
| 4904 | RD = cpu->CP15[CP15(CP15_CACHE_TYPE)]; | ||
| 4905 | } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 1) { | 4913 | } else if (CRn == 5 && CRm == 0 && OPCODE_2 == 1) { |
| 4906 | RD = cpu->CP15[CP15(CP15_INSTR_FAULT_STATUS)]; | 4914 | RD = cpu->CP15[CP15(CP15_INSTR_FAULT_STATUS)]; |
| 4915 | } else if (CRn == 6 && CRm == 0 && OPCODE_2 == 0) { | ||
| 4916 | RD = cpu->CP15[CP15(CP15_FAULT_ADDRESS)]; | ||
| 4907 | } else if (CRn == 13) { | 4917 | } else if (CRn == 13) { |
| 4908 | if(OPCODE_2 == 0) | 4918 | if(OPCODE_2 == 0) { |
| 4909 | RD = CP15_REG(CP15_PID); | 4919 | RD = CP15_REG(CP15_PID); |
| 4910 | else if(OPCODE_2 == 1) | 4920 | } else if(OPCODE_2 == 1) { |
| 4911 | RD = CP15_REG(CP15_CONTEXT_ID); | 4921 | RD = CP15_REG(CP15_CONTEXT_ID); |
| 4912 | else if(OPCODE_2 == 3) { | 4922 | } else if (OPCODE_2 == 2) { |
| 4923 | RD = CP15_REG(CP15_THREAD_UPRW); | ||
| 4924 | } else if(OPCODE_2 == 3) { | ||
| 4913 | RD = Memory::KERNEL_MEMORY_VADDR; | 4925 | RD = Memory::KERNEL_MEMORY_VADDR; |
| 4926 | } else if (OPCODE_2 == 4) { | ||
| 4927 | if (InAPrivilegedMode(cpu)) | ||
| 4928 | RD = CP15_REG(CP15_THREAD_PRW); | ||
| 4914 | } else { | 4929 | } else { |
| 4915 | LOG_ERROR(Core_ARM11, "mmu_mrr wrote UNKNOWN - reg %d", CRn); | 4930 | LOG_ERROR(Core_ARM11, "mmu_mrr wrote UNKNOWN - reg %d", CRn); |
| 4916 | } | 4931 | } |
diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.cpp b/src/core/arm/dyncom/arm_dyncom_thumb.cpp index de70ca8ae..d5a698365 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.cpp +++ b/src/core/arm/dyncom/arm_dyncom_thumb.cpp | |||
| @@ -48,7 +48,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 48 | 48 | ||
| 49 | case 3: // ADD/SUB | 49 | case 3: // ADD/SUB |
| 50 | { | 50 | { |
| 51 | ARMword subset[4] = { | 51 | static const ARMword subset[4] = { |
| 52 | 0xE0900000, // ADDS Rd,Rs,Rn | 52 | 0xE0900000, // ADDS Rd,Rs,Rn |
| 53 | 0xE0500000, // SUBS Rd,Rs,Rn | 53 | 0xE0500000, // SUBS Rd,Rs,Rn |
| 54 | 0xE2900000, // ADDS Rd,Rs,#imm3 | 54 | 0xE2900000, // ADDS Rd,Rs,#imm3 |
| @@ -67,7 +67,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 67 | case 6: // ADD | 67 | case 6: // ADD |
| 68 | case 7: // SUB | 68 | case 7: // SUB |
| 69 | { | 69 | { |
| 70 | ARMword subset[4] = { | 70 | static const ARMword subset[4] = { |
| 71 | 0xE3B00000, // MOVS Rd,#imm8 | 71 | 0xE3B00000, // MOVS Rd,#imm8 |
| 72 | 0xE3500000, // CMP Rd,#imm8 | 72 | 0xE3500000, // CMP Rd,#imm8 |
| 73 | 0xE2900000, // ADDS Rd,Rd,#imm8 | 73 | 0xE2900000, // ADDS Rd,Rd,#imm8 |
| @@ -95,7 +95,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 95 | t_mul | 95 | t_mul |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 98 | struct { | 98 | static const struct { |
| 99 | ARMword opcode; | 99 | ARMword opcode; |
| 100 | otype type; | 100 | otype type; |
| 101 | } subset[16] = { | 101 | } subset[16] = { |
| @@ -205,7 +205,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 205 | // merged into a single subset, saving on the following boolean: | 205 | // merged into a single subset, saving on the following boolean: |
| 206 | 206 | ||
| 207 | if ((tinstr & (1 << 9)) == 0) { | 207 | if ((tinstr & (1 << 9)) == 0) { |
| 208 | ARMword subset[4] = { | 208 | static const ARMword subset[4] = { |
| 209 | 0xE7800000, // STR Rd,[Rb,Ro] | 209 | 0xE7800000, // STR Rd,[Rb,Ro] |
| 210 | 0xE7C00000, // STRB Rd,[Rb,Ro] | 210 | 0xE7C00000, // STRB Rd,[Rb,Ro] |
| 211 | 0xE7900000, // LDR Rd,[Rb,Ro] | 211 | 0xE7900000, // LDR Rd,[Rb,Ro] |
| @@ -218,7 +218,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 218 | |((tinstr & 0x01C0) >> 6); // Ro | 218 | |((tinstr & 0x01C0) >> 6); // Ro |
| 219 | 219 | ||
| 220 | } else { | 220 | } else { |
| 221 | ARMword subset[4] = { | 221 | static const ARMword subset[4] = { |
| 222 | 0xE18000B0, // STRH Rd,[Rb,Ro] | 222 | 0xE18000B0, // STRH Rd,[Rb,Ro] |
| 223 | 0xE19000D0, // LDRSB Rd,[Rb,Ro] | 223 | 0xE19000D0, // LDRSB Rd,[Rb,Ro] |
| 224 | 0xE19000B0, // LDRH Rd,[Rb,Ro] | 224 | 0xE19000B0, // LDRH Rd,[Rb,Ro] |
| @@ -236,7 +236,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 236 | case 14: // STRB Rd,[Rb,#imm5] | 236 | case 14: // STRB Rd,[Rb,#imm5] |
| 237 | case 15: // LDRB Rd,[Rb,#imm5] | 237 | case 15: // LDRB Rd,[Rb,#imm5] |
| 238 | { | 238 | { |
| 239 | ARMword subset[4] = { | 239 | static const ARMword subset[4] = { |
| 240 | 0xE5800000, // STR Rd,[Rb,#imm5] | 240 | 0xE5800000, // STR Rd,[Rb,#imm5] |
| 241 | 0xE5900000, // LDR Rd,[Rb,#imm5] | 241 | 0xE5900000, // LDR Rd,[Rb,#imm5] |
| 242 | 0xE5C00000, // STRB Rd,[Rb,#imm5] | 242 | 0xE5C00000, // STRB Rd,[Rb,#imm5] |
| @@ -300,7 +300,7 @@ tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t | |||
| 300 | } else if ((tinstr & 0x0F00) == 0x0e00) | 300 | } else if ((tinstr & 0x0F00) == 0x0e00) |
| 301 | *ainstr = 0xEF000000 | SWI_Breakpoint; | 301 | *ainstr = 0xEF000000 | SWI_Breakpoint; |
| 302 | else { | 302 | else { |
| 303 | ARMword subset[4] = { | 303 | static const ARMword subset[4] = { |
| 304 | 0xE92D0000, // STMDB sp!,{rlist} | 304 | 0xE92D0000, // STMDB sp!,{rlist} |
| 305 | 0xE92D4000, // STMDB sp!,{rlist,lr} | 305 | 0xE92D4000, // STMDB sp!,{rlist,lr} |
| 306 | 0xE8BD0000, // LDMIA sp!,{rlist} | 306 | 0xE8BD0000, // LDMIA sp!,{rlist} |
diff --git a/src/core/arm/interpreter/armcopro.cpp b/src/core/arm/interpreter/armcopro.cpp index bb9ca98fe..4ae0c52e4 100644 --- a/src/core/arm/interpreter/armcopro.cpp +++ b/src/core/arm/interpreter/armcopro.cpp | |||
| @@ -47,7 +47,7 @@ static unsigned int NoCoPro5W(ARMul_State* state, unsigned int a, ARMword b, ARM | |||
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | // Install co-processor instruction handlers in this routine. | 49 | // Install co-processor instruction handlers in this routine. |
| 50 | unsigned int ARMul_CoProInit(ARMul_State* state) | 50 | void ARMul_CoProInit(ARMul_State* state) |
| 51 | { | 51 | { |
| 52 | // Initialise tham all first. | 52 | // Initialise tham all first. |
| 53 | for (unsigned int i = 0; i < 16; i++) | 53 | for (unsigned int i = 0; i < 16; i++) |
| @@ -71,11 +71,10 @@ unsigned int ARMul_CoProInit(ARMul_State* state) | |||
| 71 | // No handlers below here. | 71 | // No handlers below here. |
| 72 | 72 | ||
| 73 | // Call all the initialisation routines. | 73 | // Call all the initialisation routines. |
| 74 | for (unsigned int i = 0; i < 16; i++) | 74 | for (unsigned int i = 0; i < 16; i++) { |
| 75 | if (state->CPInit[i]) | 75 | if (state->CPInit[i]) |
| 76 | (state->CPInit[i]) (state); | 76 | (state->CPInit[i]) (state); |
| 77 | 77 | } | |
| 78 | return TRUE; | ||
| 79 | } | 78 | } |
| 80 | 79 | ||
| 81 | // Install co-processor finalisation routines in this routine. | 80 | // Install co-processor finalisation routines in this routine. |
diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index a52d14960..7b502e240 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp | |||
| @@ -63,24 +63,22 @@ void ARMul_EmulateInit() | |||
| 63 | \***************************************************************************/ | 63 | \***************************************************************************/ |
| 64 | ARMul_State* ARMul_NewState(ARMul_State* state) | 64 | ARMul_State* ARMul_NewState(ARMul_State* state) |
| 65 | { | 65 | { |
| 66 | unsigned i, j; | ||
| 67 | |||
| 68 | memset (state, 0, sizeof (ARMul_State)); | 66 | memset (state, 0, sizeof (ARMul_State)); |
| 69 | 67 | ||
| 70 | state->Emulate = RUN; | 68 | state->Emulate = RUN; |
| 71 | for (i = 0; i < 16; i++) { | 69 | for (unsigned int i = 0; i < 16; i++) { |
| 72 | state->Reg[i] = 0; | 70 | state->Reg[i] = 0; |
| 73 | for (j = 0; j < 7; j++) | 71 | for (unsigned int j = 0; j < 7; j++) |
| 74 | state->RegBank[j][i] = 0; | 72 | state->RegBank[j][i] = 0; |
| 75 | } | 73 | } |
| 76 | for (i = 0; i < 7; i++) | 74 | for (unsigned int i = 0; i < 7; i++) |
| 77 | state->Spsr[i] = 0; | 75 | state->Spsr[i] = 0; |
| 76 | |||
| 78 | state->Mode = 0; | 77 | state->Mode = 0; |
| 79 | 78 | ||
| 80 | state->Debug = FALSE; | ||
| 81 | state->VectorCatch = 0; | 79 | state->VectorCatch = 0; |
| 82 | state->Aborted = FALSE; | 80 | state->Aborted = false; |
| 83 | state->Reseted = FALSE; | 81 | state->Reseted = false; |
| 84 | state->Inted = 3; | 82 | state->Inted = 3; |
| 85 | state->LastInted = 3; | 83 | state->LastInted = 3; |
| 86 | 84 | ||
diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index 997874764..5be3a561f 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h | |||
| @@ -86,7 +86,9 @@ enum { | |||
| 86 | CP15_IFAR, | 86 | CP15_IFAR, |
| 87 | CP15_PID, | 87 | CP15_PID, |
| 88 | CP15_CONTEXT_ID, | 88 | CP15_CONTEXT_ID, |
| 89 | CP15_THREAD_URO, | 89 | CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write |
| 90 | CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W) | ||
| 91 | CP15_THREAD_PRW, // Thread ID register - Privileged R/W only. | ||
| 90 | CP15_TLB_FAULT_ADDR, /* defined by SkyEye */ | 92 | CP15_TLB_FAULT_ADDR, /* defined by SkyEye */ |
| 91 | CP15_TLB_FAULT_STATUS, /* defined by SkyEye */ | 93 | CP15_TLB_FAULT_STATUS, /* defined by SkyEye */ |
| 92 | /* VFP registers */ | 94 | /* VFP registers */ |
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h index 778868783..ff9296e0f 100644 --- a/src/core/arm/skyeye_common/armdefs.h +++ b/src/core/arm/skyeye_common/armdefs.h | |||
| @@ -35,11 +35,6 @@ | |||
| 35 | #define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)) | 35 | #define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)) |
| 36 | #define BIT(s, n) ((s >> (n)) & 1) | 36 | #define BIT(s, n) ((s >> (n)) & 1) |
| 37 | 37 | ||
| 38 | #ifndef FALSE | ||
| 39 | #define FALSE 0 | ||
| 40 | #define TRUE 1 | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #define LOW 0 | 38 | #define LOW 0 |
| 44 | #define HIGH 1 | 39 | #define HIGH 1 |
| 45 | #define LOWHIGH 1 | 40 | #define LOWHIGH 1 |
| @@ -135,7 +130,6 @@ struct ARMul_State | |||
| 135 | unsigned char* CPData[16]; // Coprocessor data | 130 | unsigned char* CPData[16]; // Coprocessor data |
| 136 | unsigned char const* CPRegWords[16]; // Map of coprocessor register sizes | 131 | unsigned char const* CPRegWords[16]; // Map of coprocessor register sizes |
| 137 | 132 | ||
| 138 | unsigned Debug; // Show instructions as they are executed | ||
| 139 | unsigned NresetSig; // Reset the processor | 133 | unsigned NresetSig; // Reset the processor |
| 140 | unsigned NfiqSig; | 134 | unsigned NfiqSig; |
| 141 | unsigned NirqSig; | 135 | unsigned NirqSig; |
| @@ -180,12 +174,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model) | |||
| 180 | */ | 174 | */ |
| 181 | unsigned lateabtSig; | 175 | unsigned lateabtSig; |
| 182 | 176 | ||
| 183 | ARMword Vector; // Synthesize aborts in cycle modes | 177 | bool Aborted; // Sticky flag for aborts |
| 184 | ARMword Aborted; // Sticky flag for aborts | 178 | bool Reseted; // Sticky flag for Reset |
| 185 | ARMword Reseted; // Sticky flag for Reset | ||
| 186 | ARMword Inted, LastInted; // Sticky flags for interrupts | 179 | ARMword Inted, LastInted; // Sticky flags for interrupts |
| 187 | ARMword Base; // Extra hand for base writeback | 180 | ARMword Base; // Extra hand for base writeback |
| 188 | ARMword AbortAddr; // To keep track of Prefetch aborts | 181 | ARMword AbortAddr; // To keep track of Prefetch aborts |
| 182 | ARMword Vector; // Synthesize aborts in cycle modes | ||
| 189 | 183 | ||
| 190 | // For differentiating ARM core emulaiton. | 184 | // For differentiating ARM core emulaiton. |
| 191 | bool is_v4; // Are we emulating a v4 architecture (or higher)? | 185 | bool is_v4; // Are we emulating a v4 architecture (or higher)? |
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h index 6071d447b..beee54c9a 100644 --- a/src/core/arm/skyeye_common/armemu.h +++ b/src/core/arm/skyeye_common/armemu.h | |||
| @@ -100,10 +100,10 @@ extern ARMword ARMul_ImmedTable[]; // Immediate DP LHS values. | |||
| 100 | extern char ARMul_BitList[]; // Number of bits in a byte table. | 100 | extern char ARMul_BitList[]; // Number of bits in a byte table. |
| 101 | 101 | ||
| 102 | // Coprocessor support functions. | 102 | // Coprocessor support functions. |
| 103 | extern unsigned ARMul_CoProInit (ARMul_State *); | 103 | extern void ARMul_CoProInit(ARMul_State*); |
| 104 | extern void ARMul_CoProExit (ARMul_State *); | 104 | extern void ARMul_CoProExit(ARMul_State*); |
| 105 | extern void ARMul_CoProAttach (ARMul_State *, unsigned, ARMul_CPInits *, | 105 | extern void ARMul_CoProAttach(ARMul_State*, unsigned, ARMul_CPInits*, |
| 106 | ARMul_CPExits *, ARMul_LDCs *, ARMul_STCs *, | 106 | ARMul_CPExits*, ARMul_LDCs*, ARMul_STCs*, |
| 107 | ARMul_MRCs *, ARMul_MCRs *, ARMul_MRRCs *, ARMul_MCRRs *, | 107 | ARMul_MRCs*, ARMul_MCRs*, ARMul_MRRCs*, ARMul_MCRRs*, |
| 108 | ARMul_CDPs *, ARMul_CPReads *, ARMul_CPWrites *); | 108 | ARMul_CDPs*, ARMul_CPReads*, ARMul_CPWrites*); |
| 109 | extern void ARMul_CoProDetach (ARMul_State *, unsigned); | 109 | extern void ARMul_CoProDetach(ARMul_State*, unsigned); |
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index 2c15db12b..9a7088088 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp | |||
| @@ -908,6 +908,9 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f | |||
| 908 | vdp.sign = vfp_sign_negate(vdp.sign); | 908 | vdp.sign = vfp_sign_negate(vdp.sign); |
| 909 | 909 | ||
| 910 | vfp_double_unpack(&vdn, vfp_get_double(state, dd)); | 910 | vfp_double_unpack(&vdn, vfp_get_double(state, dd)); |
| 911 | if (vdn.exponent == 0 && vdn.significand != 0) | ||
| 912 | vfp_double_normalise_denormal(&vdn); | ||
| 913 | |||
| 911 | if (negate & NEG_SUBTRACT) | 914 | if (negate & NEG_SUBTRACT) |
| 912 | vdn.sign = vfp_sign_negate(vdn.sign); | 915 | vdn.sign = vfp_sign_negate(vdn.sign); |
| 913 | 916 | ||
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 678b63f51..8b2dfa388 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp | |||
| @@ -941,6 +941,9 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp | |||
| 941 | v = vfp_get_float(state, sd); | 941 | v = vfp_get_float(state, sd); |
| 942 | pr_debug("VFP: s%u = %08x\n", sd, v); | 942 | pr_debug("VFP: s%u = %08x\n", sd, v); |
| 943 | vfp_single_unpack(&vsn, v); | 943 | vfp_single_unpack(&vsn, v); |
| 944 | if (vsn.exponent == 0 && vsn.significand != 0) | ||
| 945 | vfp_single_normalise_denormal(&vsn); | ||
| 946 | |||
| 944 | if (negate & NEG_SUBTRACT) | 947 | if (negate & NEG_SUBTRACT) |
| 945 | vsn.sign = vfp_sign_negate(vsn.sign); | 948 | vsn.sign = vfp_sign_negate(vsn.sign); |
| 946 | 949 | ||
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 390178f67..43a106549 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h | |||
| @@ -181,20 +181,6 @@ public: | |||
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | /** | 183 | /** |
| 184 | * Tries to open the archive of this type with the specified path | ||
| 185 | * @param path Path to the archive | ||
| 186 | * @return ResultCode of the operation | ||
| 187 | */ | ||
| 188 | virtual ResultCode Open(const Path& path) = 0; | ||
| 189 | |||
| 190 | /** | ||
| 191 | * Deletes the archive contents and then re-creates the base folder | ||
| 192 | * @param path Path to the archive | ||
| 193 | * @return ResultCode of the operation, 0 on success | ||
| 194 | */ | ||
| 195 | virtual ResultCode Format(const Path& path) const = 0; | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | 184 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |
| 199 | */ | 185 | */ |
| 200 | virtual std::string GetName() const = 0; | 186 | virtual std::string GetName() const = 0; |
| @@ -260,4 +246,29 @@ public: | |||
| 260 | virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; | 246 | virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; |
| 261 | }; | 247 | }; |
| 262 | 248 | ||
| 249 | class ArchiveFactory : NonCopyable { | ||
| 250 | public: | ||
| 251 | virtual ~ArchiveFactory() { | ||
| 252 | } | ||
| 253 | |||
| 254 | /** | ||
| 255 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 256 | */ | ||
| 257 | virtual std::string GetName() const = 0; | ||
| 258 | |||
| 259 | /** | ||
| 260 | * Tries to open the archive of this type with the specified path | ||
| 261 | * @param path Path to the archive | ||
| 262 | * @return An ArchiveBackend corresponding operating specified archive path. | ||
| 263 | */ | ||
| 264 | virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0; | ||
| 265 | |||
| 266 | /** | ||
| 267 | * Deletes the archive contents and then re-creates the base folder | ||
| 268 | * @param path Path to the archive | ||
| 269 | * @return ResultCode of the operation, 0 on success | ||
| 270 | */ | ||
| 271 | virtual ResultCode Format(const Path& path) = 0; | ||
| 272 | }; | ||
| 273 | |||
| 263 | } // namespace FileSys | 274 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 33e4e76f8..0363c9771 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/make_unique.h" | ||
| 9 | 10 | ||
| 10 | #include "core/file_sys/archive_extsavedata.h" | 11 | #include "core/file_sys/archive_extsavedata.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 12 | #include "core/file_sys/disk_archive.h" |
| @@ -33,12 +34,12 @@ std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) | |||
| 33 | SYSTEM_ID.c_str(), SDCARD_ID.c_str()); | 34 | SYSTEM_ID.c_str(), SDCARD_ID.c_str()); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_location, bool shared) | 37 | ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared) |
| 37 | : DiskArchive(GetExtDataContainerPath(mount_location, shared)) { | 38 | : mount_point(GetExtDataContainerPath(mount_location, shared)) { |
| 38 | LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); | 39 | LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | bool Archive_ExtSaveData::Initialize() { | 42 | bool ArchiveFactory_ExtSaveData::Initialize() { |
| 42 | if (!FileUtil::CreateFullPath(mount_point)) { | 43 | if (!FileUtil::CreateFullPath(mount_point)) { |
| 43 | LOG_ERROR(Service_FS, "Unable to create ExtSaveData base path."); | 44 | LOG_ERROR(Service_FS, "Unable to create ExtSaveData base path."); |
| 44 | return false; | 45 | return false; |
| @@ -47,18 +48,18 @@ bool Archive_ExtSaveData::Initialize() { | |||
| 47 | return true; | 48 | return true; |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | ResultCode Archive_ExtSaveData::Open(const Path& path) { | 51 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) { |
| 51 | std::string fullpath = GetExtSaveDataPath(mount_point, path); | 52 | std::string fullpath = GetExtSaveDataPath(mount_point, path); |
| 52 | if (!FileUtil::Exists(fullpath)) { | 53 | if (!FileUtil::Exists(fullpath)) { |
| 53 | // TODO(Subv): Check error code, this one is probably wrong | 54 | // TODO(Subv): Check error code, this one is probably wrong |
| 54 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | 55 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, |
| 55 | ErrorSummary::InvalidState, ErrorLevel::Status); | 56 | ErrorSummary::InvalidState, ErrorLevel::Status); |
| 56 | } | 57 | } |
| 57 | concrete_mount_point = fullpath; | 58 | auto archive = Common::make_unique<DiskArchive>(fullpath); |
| 58 | return RESULT_SUCCESS; | 59 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | ResultCode Archive_ExtSaveData::Format(const Path& path) const { | 62 | ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path) { |
| 62 | std::string fullpath = GetExtSaveDataPath(mount_point, path); | 63 | std::string fullpath = GetExtSaveDataPath(mount_point, path); |
| 63 | FileUtil::CreateFullPath(fullpath); | 64 | FileUtil::CreateFullPath(fullpath); |
| 64 | return RESULT_SUCCESS; | 65 | return RESULT_SUCCESS; |
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index 802a11b5f..83c6b0291 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the ExtSaveData archive | 17 | /// File system interface to the ExtSaveData archive |
| 18 | class Archive_ExtSaveData final : public DiskArchive { | 18 | class ArchiveFactory_ExtSaveData final : public ArchiveFactory { |
| 19 | public: | 19 | public: |
| 20 | Archive_ExtSaveData(const std::string& mount_point, bool shared); | 20 | ArchiveFactory_ExtSaveData(const std::string& mount_point, bool shared); |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Initialize the archive. | 23 | * Initialize the archive. |
| @@ -25,21 +25,20 @@ public: | |||
| 25 | */ | 25 | */ |
| 26 | bool Initialize(); | 26 | bool Initialize(); |
| 27 | 27 | ||
| 28 | ResultCode Open(const Path& path) override; | ||
| 29 | ResultCode Format(const Path& path) const override; | ||
| 30 | std::string GetName() const override { return "ExtSaveData"; } | 28 | std::string GetName() const override { return "ExtSaveData"; } |
| 31 | 29 | ||
| 32 | const std::string& GetMountPoint() const override { | 30 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |
| 33 | return concrete_mount_point; | 31 | ResultCode Format(const Path& path) override; |
| 34 | } | ||
| 35 | 32 | ||
| 36 | protected: | 33 | const std::string& GetMountPoint() const { return mount_point; } |
| 34 | |||
| 35 | private: | ||
| 37 | /** | 36 | /** |
| 38 | * This holds the full directory path for this archive, it is only set after a successful call to Open, | 37 | * This holds the full directory path for this archive, it is only set after a successful call |
| 39 | * this is formed as <base extsavedatapath>/<type>/<high>/<low>. | 38 | * to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>. |
| 40 | * See GetExtSaveDataPath for the code that extracts this data from an archive path. | 39 | * See GetExtSaveDataPath for the code that extracts this data from an archive path. |
| 41 | */ | 40 | */ |
| 42 | std::string concrete_mount_point; | 41 | std::string mount_point; |
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| 45 | /** | 44 | /** |
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index a30f73d0e..bf54a3866 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp | |||
| @@ -15,11 +15,24 @@ | |||
| 15 | 15 | ||
| 16 | namespace FileSys { | 16 | namespace FileSys { |
| 17 | 17 | ||
| 18 | Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { | 18 | ArchiveFactory_RomFS::ArchiveFactory_RomFS(const Loader::AppLoader& app_loader) |
| 19 | : romfs_data(std::make_shared<std::vector<u8>>()) { | ||
| 19 | // Load the RomFS from the app | 20 | // Load the RomFS from the app |
| 20 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) { | 21 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(*romfs_data)) { |
| 21 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 22 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 22 | } | 23 | } |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 26 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) { | ||
| 27 | auto archive = Common::make_unique<IVFCArchive>(romfs_data); | ||
| 28 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||
| 29 | } | ||
| 30 | |||
| 31 | ResultCode ArchiveFactory_RomFS::Format(const Path& path) { | ||
| 32 | LOG_ERROR(Service_FS, "Attempted to format a RomFS archive."); | ||
| 33 | // TODO: Verify error code | ||
| 34 | return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||
| 35 | ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 36 | } | ||
| 37 | |||
| 25 | } // namespace FileSys | 38 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 5cb75e04d..409bc670a 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -17,12 +18,16 @@ | |||
| 17 | namespace FileSys { | 18 | namespace FileSys { |
| 18 | 19 | ||
| 19 | /// File system interface to the RomFS archive | 20 | /// File system interface to the RomFS archive |
| 20 | class Archive_RomFS final : public IVFCArchive { | 21 | class ArchiveFactory_RomFS final : public ArchiveFactory { |
| 21 | public: | 22 | public: |
| 22 | Archive_RomFS(const Loader::AppLoader& app_loader); | 23 | ArchiveFactory_RomFS(const Loader::AppLoader& app_loader); |
| 23 | 24 | ||
| 24 | std::string GetName() const override { return "RomFS"; } | 25 | std::string GetName() const override { return "RomFS"; } |
| 25 | ResultCode Open(const Path& path) override { return RESULT_SUCCESS; } | 26 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |
| 27 | ResultCode Format(const Path& path) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | std::shared_ptr<std::vector<u8>> romfs_data; | ||
| 26 | }; | 31 | }; |
| 27 | 32 | ||
| 28 | } // namespace FileSys | 33 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index 3baee5294..8496e06f3 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/make_unique.h" | ||
| 9 | 10 | ||
| 10 | #include "core/file_sys/archive_savedata.h" | 11 | #include "core/file_sys/archive_savedata.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 12 | #include "core/file_sys/disk_archive.h" |
| @@ -28,26 +29,28 @@ static std::string GetSaveDataPath(const std::string& mount_location, u64 progra | |||
| 28 | return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); | 29 | return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | Archive_SaveData::Archive_SaveData(const std::string& sdmc_directory) | 32 | ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory) |
| 32 | : DiskArchive(GetSaveDataContainerPath(sdmc_directory)) { | 33 | : mount_point(GetSaveDataContainerPath(sdmc_directory)) { |
| 33 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | 34 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | ResultCode Archive_SaveData::Open(const Path& path) { | 37 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { |
| 37 | if (concrete_mount_point.empty()) | 38 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); |
| 38 | concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); | ||
| 39 | if (!FileUtil::Exists(concrete_mount_point)) { | 39 | if (!FileUtil::Exists(concrete_mount_point)) { |
| 40 | // When a SaveData archive is created for the first time, it is not yet formatted | 40 | // When a SaveData archive is created for the first time, it is not yet formatted |
| 41 | // and the save file/directory structure expected by the game has not yet been initialized. | 41 | // and the save file/directory structure expected by the game has not yet been initialized. |
| 42 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive | 42 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive |
| 43 | // with the files and folders that it expects. | 43 | // with the files and folders that it expects. |
| 44 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | 44 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, |
| 45 | ErrorSummary::InvalidState, ErrorLevel::Status); | 45 | ErrorSummary::InvalidState, ErrorLevel::Status); |
| 46 | } | 46 | } |
| 47 | return RESULT_SUCCESS; | 47 | |
| 48 | auto archive = Common::make_unique<DiskArchive>(std::move(concrete_mount_point)); | ||
| 49 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | ResultCode Archive_SaveData::Format(const Path& path) const { | 52 | ResultCode ArchiveFactory_SaveData::Format(const Path& path) { |
| 53 | std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); | ||
| 51 | FileUtil::DeleteDirRecursively(concrete_mount_point); | 54 | FileUtil::DeleteDirRecursively(concrete_mount_point); |
| 52 | FileUtil::CreateFullPath(concrete_mount_point); | 55 | FileUtil::CreateFullPath(concrete_mount_point); |
| 53 | return RESULT_SUCCESS; | 56 | return RESULT_SUCCESS; |
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index 07c7f7eff..db17afc92 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -15,22 +15,17 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SaveData archive | 17 | /// File system interface to the SaveData archive |
| 18 | class Archive_SaveData final : public DiskArchive { | 18 | class ArchiveFactory_SaveData final : public ArchiveFactory { |
| 19 | public: | 19 | public: |
| 20 | Archive_SaveData(const std::string& mount_point); | 20 | ArchiveFactory_SaveData(const std::string& mount_point); |
| 21 | 21 | ||
| 22 | std::string GetName() const override { return "SaveData"; } | 22 | std::string GetName() const override { return "SaveData"; } |
| 23 | 23 | ||
| 24 | ResultCode Open(const Path& path) override; | 24 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |
| 25 | ResultCode Format(const Path& path) override; | ||
| 25 | 26 | ||
| 26 | ResultCode Format(const Path& path) const override; | 27 | private: |
| 27 | 28 | std::string mount_point; | |
| 28 | const std::string& GetMountPoint() const override { | ||
| 29 | return concrete_mount_point; | ||
| 30 | } | ||
| 31 | |||
| 32 | protected: | ||
| 33 | std::string concrete_mount_point; | ||
| 34 | }; | 29 | }; |
| 35 | 30 | ||
| 36 | } // namespace FileSys | 31 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index a7a507536..47d8a9d25 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/file_util.h" | 5 | #include "common/file_util.h" |
| 6 | #include "common/make_unique.h" | ||
| 6 | 7 | ||
| 7 | #include "core/file_sys/archive_savedatacheck.h" | 8 | #include "core/file_sys/archive_savedatacheck.h" |
| 8 | #include "core/hle/service/fs/archive.h" | 9 | #include "core/hle/service/fs/archive.h" |
| @@ -21,32 +22,33 @@ static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high | |||
| 21 | mount_point.c_str(), high, low); | 22 | mount_point.c_str(), high, low); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) : | 25 | ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory) : |
| 25 | mount_point(GetSaveDataCheckContainerPath(nand_directory)) { | 26 | mount_point(GetSaveDataCheckContainerPath(nand_directory)) { |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | ResultCode Archive_SaveDataCheck::Open(const Path& path) { | 29 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) { |
| 29 | // TODO(Subv): We should not be overwriting raw_data everytime this function is called, | ||
| 30 | // but until we use factory classes to create the archives at runtime instead of creating them beforehand | ||
| 31 | // and allow multiple archives of the same type to be open at the same time without clobbering each other, | ||
| 32 | // we won't be able to maintain the state of each archive, hence we overwrite it every time it's needed. | ||
| 33 | // There are a number of problems with this, for example opening a file in this archive, then opening | ||
| 34 | // this archive again with a different path, will corrupt the previously open file. | ||
| 35 | auto vec = path.AsBinary(); | 30 | auto vec = path.AsBinary(); |
| 36 | const u32* data = reinterpret_cast<u32*>(vec.data()); | 31 | const u32* data = reinterpret_cast<u32*>(vec.data()); |
| 37 | std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); | 32 | std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); |
| 38 | FileUtil::IOFile file(file_path, "rb"); | 33 | FileUtil::IOFile file(file_path, "rb"); |
| 39 | 34 | ||
| 40 | std::fill(raw_data.begin(), raw_data.end(), 0); | ||
| 41 | |||
| 42 | if (!file.IsOpen()) { | 35 | if (!file.IsOpen()) { |
| 43 | return ResultCode(-1); // TODO(Subv): Find the right error code | 36 | return ResultCode(-1); // TODO(Subv): Find the right error code |
| 44 | } | 37 | } |
| 45 | auto size = file.GetSize(); | 38 | auto size = file.GetSize(); |
| 46 | raw_data.resize(size); | 39 | auto raw_data = std::make_shared<std::vector<u8>>(size); |
| 47 | file.ReadBytes(raw_data.data(), size); | 40 | file.ReadBytes(raw_data->data(), size); |
| 48 | file.Close(); | 41 | file.Close(); |
| 49 | return RESULT_SUCCESS; | 42 | |
| 43 | auto archive = Common::make_unique<IVFCArchive>(std::move(raw_data)); | ||
| 44 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||
| 45 | } | ||
| 46 | |||
| 47 | ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path) { | ||
| 48 | LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); | ||
| 49 | // TODO: Verify error code | ||
| 50 | return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||
| 51 | ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | } // namespace FileSys | 54 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h index f6e73e803..f78a6f02e 100644 --- a/src/core/file_sys/archive_savedatacheck.h +++ b/src/core/file_sys/archive_savedatacheck.h | |||
| @@ -17,12 +17,14 @@ | |||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | 18 | ||
| 19 | /// File system interface to the SaveDataCheck archive | 19 | /// File system interface to the SaveDataCheck archive |
| 20 | class Archive_SaveDataCheck final : public IVFCArchive { | 20 | class ArchiveFactory_SaveDataCheck final : public ArchiveFactory { |
| 21 | public: | 21 | public: |
| 22 | Archive_SaveDataCheck(const std::string& mount_point); | 22 | ArchiveFactory_SaveDataCheck(const std::string& mount_point); |
| 23 | 23 | ||
| 24 | std::string GetName() const override { return "SaveDataCheck"; } | 24 | std::string GetName() const override { return "SaveDataCheck"; } |
| 25 | ResultCode Open(const Path& path) override; | 25 | |
| 26 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||
| 27 | ResultCode Format(const Path& path) override; | ||
| 26 | 28 | ||
| 27 | private: | 29 | private: |
| 28 | std::string mount_point; | 30 | std::string mount_point; |
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 26b03e82f..92b20c7f6 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/make_unique.h" | ||
| 9 | 10 | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | 11 | #include "core/file_sys/archive_sdmc.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 12 | #include "core/file_sys/disk_archive.h" |
| @@ -16,17 +17,17 @@ | |||
| 16 | 17 | ||
| 17 | namespace FileSys { | 18 | namespace FileSys { |
| 18 | 19 | ||
| 19 | Archive_SDMC::Archive_SDMC(const std::string& sdmc_directory) : DiskArchive(sdmc_directory) { | 20 | ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory) : sdmc_directory(sdmc_directory) { |
| 20 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); | 21 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | bool Archive_SDMC::Initialize() { | 24 | bool ArchiveFactory_SDMC::Initialize() { |
| 24 | if (!Settings::values.use_virtual_sd) { | 25 | if (!Settings::values.use_virtual_sd) { |
| 25 | LOG_WARNING(Service_FS, "SDMC disabled by config."); | 26 | LOG_WARNING(Service_FS, "SDMC disabled by config."); |
| 26 | return false; | 27 | return false; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | if (!FileUtil::CreateFullPath(mount_point)) { | 30 | if (!FileUtil::CreateFullPath(sdmc_directory)) { |
| 30 | LOG_ERROR(Service_FS, "Unable to create SDMC path."); | 31 | LOG_ERROR(Service_FS, "Unable to create SDMC path."); |
| 31 | return false; | 32 | return false; |
| 32 | } | 33 | } |
| @@ -34,4 +35,14 @@ bool Archive_SDMC::Initialize() { | |||
| 34 | return true; | 35 | return true; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 38 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { | ||
| 39 | auto archive = Common::make_unique<DiskArchive>(sdmc_directory); | ||
| 40 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultCode ArchiveFactory_SDMC::Format(const Path& path) { | ||
| 44 | // This is kind of an undesirable operation, so let's just ignore it. :) | ||
| 45 | return RESULT_SUCCESS; | ||
| 46 | } | ||
| 47 | |||
| 37 | } // namespace FileSys | 48 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 1b801f217..1becf6c0f 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SDMC archive | 17 | /// File system interface to the SDMC archive |
| 18 | class Archive_SDMC final : public DiskArchive { | 18 | class ArchiveFactory_SDMC final : public ArchiveFactory { |
| 19 | public: | 19 | public: |
| 20 | Archive_SDMC(const std::string& mount_point); | 20 | ArchiveFactory_SDMC(const std::string& mount_point); |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Initialize the archive. | 23 | * Initialize the archive. |
| @@ -26,6 +26,12 @@ public: | |||
| 26 | bool Initialize(); | 26 | bool Initialize(); |
| 27 | 27 | ||
| 28 | std::string GetName() const override { return "SDMC"; } | 28 | std::string GetName() const override { return "SDMC"; } |
| 29 | |||
| 30 | ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||
| 31 | ResultCode Format(const Path& path) override; | ||
| 32 | |||
| 33 | private: | ||
| 34 | std::string sdmc_directory; | ||
| 29 | }; | 35 | }; |
| 30 | 36 | ||
| 31 | } // namespace FileSys | 37 | } // namespace FileSys |
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index c6e033fcd..f53fd57db 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp | |||
| @@ -18,26 +18,26 @@ namespace FileSys { | |||
| 18 | 18 | ||
| 19 | std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | 19 | std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { |
| 20 | LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | 20 | LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); |
| 21 | auto file = Common::make_unique<DiskFile>(this, path, mode); | 21 | auto file = Common::make_unique<DiskFile>(*this, path, mode); |
| 22 | if (!file->Open()) | 22 | if (!file->Open()) |
| 23 | return nullptr; | 23 | return nullptr; |
| 24 | return std::move(file); | 24 | return std::move(file); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | bool DiskArchive::DeleteFile(const Path& path) const { | 27 | bool DiskArchive::DeleteFile(const Path& path) const { |
| 28 | return FileUtil::Delete(GetMountPoint() + path.AsString()); | 28 | return FileUtil::Delete(mount_point + path.AsString()); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | 31 | bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { |
| 32 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | 32 | return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | bool DiskArchive::DeleteDirectory(const Path& path) const { | 35 | bool DiskArchive::DeleteDirectory(const Path& path) const { |
| 36 | return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | 36 | return FileUtil::DeleteDir(mount_point + path.AsString()); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { | 39 | ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { |
| 40 | std::string full_path = GetMountPoint() + path.AsString(); | 40 | std::string full_path = mount_point + path.AsString(); |
| 41 | 41 | ||
| 42 | if (FileUtil::Exists(full_path)) | 42 | if (FileUtil::Exists(full_path)) |
| 43 | return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); | 43 | return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); |
| @@ -58,16 +58,16 @@ ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { | |||
| 58 | 58 | ||
| 59 | 59 | ||
| 60 | bool DiskArchive::CreateDirectory(const Path& path) const { | 60 | bool DiskArchive::CreateDirectory(const Path& path) const { |
| 61 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | 61 | return FileUtil::CreateDir(mount_point + path.AsString()); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | 64 | bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { |
| 65 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | 65 | return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | 68 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { |
| 69 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | 69 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); |
| 70 | auto directory = Common::make_unique<DiskDirectory>(this, path); | 70 | auto directory = Common::make_unique<DiskDirectory>(*this, path); |
| 71 | if (!directory->Open()) | 71 | if (!directory->Open()) |
| 72 | return nullptr; | 72 | return nullptr; |
| 73 | return std::move(directory); | 73 | return std::move(directory); |
| @@ -75,13 +75,12 @@ std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) c | |||
| 75 | 75 | ||
| 76 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 76 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 77 | 77 | ||
| 78 | DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { | 78 | DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) { |
| 79 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | 79 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass |
| 80 | // the root directory we set while opening the archive. | 80 | // the root directory we set while opening the archive. |
| 81 | // For example, opening /../../etc/passwd can give the emulated program your users list. | 81 | // For example, opening /../../etc/passwd can give the emulated program your users list. |
| 82 | this->path = archive->GetMountPoint() + path.AsString(); | 82 | this->path = archive.mount_point + path.AsString(); |
| 83 | this->mode.hex = mode.hex; | 83 | this->mode.hex = mode.hex; |
| 84 | this->archive = archive; | ||
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | bool DiskFile::Open() { | 86 | bool DiskFile::Open() { |
| @@ -134,12 +133,11 @@ bool DiskFile::Close() const { | |||
| 134 | 133 | ||
| 135 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 134 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 136 | 135 | ||
| 137 | DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { | 136 | DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) { |
| 138 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | 137 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass |
| 139 | // the root directory we set while opening the archive. | 138 | // the root directory we set while opening the archive. |
| 140 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | 139 | // For example, opening /../../usr/bin can give the emulated program your installed programs. |
| 141 | this->path = archive->GetMountPoint() + path.AsString(); | 140 | this->path = archive.mount_point + path.AsString(); |
| 142 | this->archive = archive; | ||
| 143 | } | 141 | } |
| 144 | 142 | ||
| 145 | bool DiskDirectory::Open() { | 143 | bool DiskDirectory::Open() { |
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index 3472f6874..dbbdced74 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h | |||
| @@ -24,8 +24,8 @@ class DiskArchive : public ArchiveBackend { | |||
| 24 | public: | 24 | public: |
| 25 | DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | 25 | DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} |
| 26 | 26 | ||
| 27 | virtual std::string GetName() const = 0; | 27 | virtual std::string GetName() const { return "DiskArchive: " + mount_point; } |
| 28 | virtual ResultCode Format(const Path& path) const { return RESULT_SUCCESS; } | 28 | |
| 29 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | 29 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; |
| 30 | bool DeleteFile(const Path& path) const override; | 30 | bool DeleteFile(const Path& path) const override; |
| 31 | bool RenameFile(const Path& src_path, const Path& dest_path) const override; | 31 | bool RenameFile(const Path& src_path, const Path& dest_path) const override; |
| @@ -35,26 +35,17 @@ public: | |||
| 35 | bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; | 35 | bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; |
| 36 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | 36 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |
| 37 | 37 | ||
| 38 | virtual ResultCode Open(const Path& path) override { | ||
| 39 | return RESULT_SUCCESS; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Getter for the path used for this Archive | ||
| 44 | * @return Mount point of that passthrough archive | ||
| 45 | */ | ||
| 46 | virtual const std::string& GetMountPoint() const { | ||
| 47 | return mount_point; | ||
| 48 | } | ||
| 49 | |||
| 50 | protected: | 38 | protected: |
| 39 | friend class DiskFile; | ||
| 40 | friend class DiskDirectory; | ||
| 41 | |||
| 51 | std::string mount_point; | 42 | std::string mount_point; |
| 52 | }; | 43 | }; |
| 53 | 44 | ||
| 54 | class DiskFile : public FileBackend { | 45 | class DiskFile : public FileBackend { |
| 55 | public: | 46 | public: |
| 56 | DiskFile(); | 47 | DiskFile(); |
| 57 | DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); | 48 | DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); |
| 58 | 49 | ||
| 59 | bool Open() override; | 50 | bool Open() override; |
| 60 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | 51 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; |
| @@ -68,7 +59,6 @@ public: | |||
| 68 | } | 59 | } |
| 69 | 60 | ||
| 70 | protected: | 61 | protected: |
| 71 | const DiskArchive* archive; | ||
| 72 | std::string path; | 62 | std::string path; |
| 73 | Mode mode; | 63 | Mode mode; |
| 74 | std::unique_ptr<FileUtil::IOFile> file; | 64 | std::unique_ptr<FileUtil::IOFile> file; |
| @@ -77,7 +67,7 @@ protected: | |||
| 77 | class DiskDirectory : public DirectoryBackend { | 67 | class DiskDirectory : public DirectoryBackend { |
| 78 | public: | 68 | public: |
| 79 | DiskDirectory(); | 69 | DiskDirectory(); |
| 80 | DiskDirectory(const DiskArchive* archive, const Path& path); | 70 | DiskDirectory(const DiskArchive& archive, const Path& path); |
| 81 | 71 | ||
| 82 | ~DiskDirectory() override { | 72 | ~DiskDirectory() override { |
| 83 | Close(); | 73 | Close(); |
| @@ -91,7 +81,6 @@ public: | |||
| 91 | } | 81 | } |
| 92 | 82 | ||
| 93 | protected: | 83 | protected: |
| 94 | const DiskArchive* archive; | ||
| 95 | std::string path; | 84 | std::string path; |
| 96 | u32 total_entries_in_directory; | 85 | u32 total_entries_in_directory; |
| 97 | FileUtil::FSTEntry directory; | 86 | FileUtil::FSTEntry directory; |
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 68c3c8b81..35aca54fa 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp | |||
| @@ -15,11 +15,15 @@ | |||
| 15 | 15 | ||
| 16 | namespace FileSys { | 16 | namespace FileSys { |
| 17 | 17 | ||
| 18 | IVFCArchive::IVFCArchive() { | 18 | IVFCArchive::IVFCArchive(std::shared_ptr<const std::vector<u8>> data) : data(data) { |
| 19 | } | ||
| 20 | |||
| 21 | std::string IVFCArchive::GetName() const { | ||
| 22 | return "IVFC"; | ||
| 19 | } | 23 | } |
| 20 | 24 | ||
| 21 | std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | 25 | std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { |
| 22 | return Common::make_unique<IVFCFile>(this); | 26 | return Common::make_unique<IVFCFile>(data); |
| 23 | } | 27 | } |
| 24 | 28 | ||
| 25 | bool IVFCArchive::DeleteFile(const Path& path) const { | 29 | bool IVFCArchive::DeleteFile(const Path& path) const { |
| @@ -57,31 +61,25 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c | |||
| 57 | return Common::make_unique<IVFCDirectory>(); | 61 | return Common::make_unique<IVFCDirectory>(); |
| 58 | } | 62 | } |
| 59 | 63 | ||
| 60 | ResultCode IVFCArchive::Format(const Path& path) const { | ||
| 61 | LOG_CRITICAL(Service_FS, "Attempted to format an IVFC archive (%s).", GetName().c_str()); | ||
| 62 | // TODO: Verify error code | ||
| 63 | return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 64 | } | ||
| 65 | |||
| 66 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 64 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 67 | 65 | ||
| 68 | size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { | 66 | size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { |
| 69 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); | 67 | LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); |
| 70 | memcpy(buffer, &archive->raw_data[(u32)offset], length); | 68 | memcpy(buffer, data->data() + offset, length); |
| 71 | return length; | 69 | return length; |
| 72 | } | 70 | } |
| 73 | 71 | ||
| 74 | size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | 72 | size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { |
| 75 | LOG_CRITICAL(Service_FS, "Attempted to write to IVFC file in archive %s.", archive->GetName().c_str()); | 73 | LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); |
| 76 | return 0; | 74 | return 0; |
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | size_t IVFCFile::GetSize() const { | 77 | size_t IVFCFile::GetSize() const { |
| 80 | return sizeof(u8) * archive->raw_data.size(); | 78 | return sizeof(u8) * data->size(); |
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | bool IVFCFile::SetSize(const u64 size) const { | 81 | bool IVFCFile::SetSize(const u64 size) const { |
| 84 | LOG_CRITICAL(Service_FS, "Attempted to set the size of an IVFC file in archive %s", archive->GetName().c_str()); | 82 | LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file"); |
| 85 | return false; | 83 | return false; |
| 86 | } | 84 | } |
| 87 | 85 | ||
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 6f4cc86df..1aff9e0a4 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -23,7 +24,9 @@ namespace FileSys { | |||
| 23 | */ | 24 | */ |
| 24 | class IVFCArchive : public ArchiveBackend { | 25 | class IVFCArchive : public ArchiveBackend { |
| 25 | public: | 26 | public: |
| 26 | IVFCArchive(); | 27 | IVFCArchive(std::shared_ptr<const std::vector<u8>> data); |
| 28 | |||
| 29 | std::string GetName() const override; | ||
| 27 | 30 | ||
| 28 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | 31 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; |
| 29 | bool DeleteFile(const Path& path) const override; | 32 | bool DeleteFile(const Path& path) const override; |
| @@ -33,16 +36,14 @@ public: | |||
| 33 | bool CreateDirectory(const Path& path) const override; | 36 | bool CreateDirectory(const Path& path) const override; |
| 34 | bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; | 37 | bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; |
| 35 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | 38 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |
| 36 | ResultCode Format(const Path& path) const override; | ||
| 37 | 39 | ||
| 38 | protected: | 40 | protected: |
| 39 | friend class IVFCFile; | 41 | std::shared_ptr<const std::vector<u8>> data; |
| 40 | std::vector<u8> raw_data; | ||
| 41 | }; | 42 | }; |
| 42 | 43 | ||
| 43 | class IVFCFile : public FileBackend { | 44 | class IVFCFile : public FileBackend { |
| 44 | public: | 45 | public: |
| 45 | IVFCFile(const IVFCArchive* archive) : archive(archive) {} | 46 | IVFCFile(std::shared_ptr<const std::vector<u8>> data) : data(data) {} |
| 46 | 47 | ||
| 47 | bool Open() override { return true; } | 48 | bool Open() override { return true; } |
| 48 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | 49 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; |
| @@ -53,7 +54,7 @@ public: | |||
| 53 | void Flush() const override { } | 54 | void Flush() const override { } |
| 54 | 55 | ||
| 55 | private: | 56 | private: |
| 56 | const IVFCArchive* archive; | 57 | std::shared_ptr<const std::vector<u8>> data; |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| 59 | class IVFCDirectory : public DirectoryBackend { | 60 | class IVFCDirectory : public DirectoryBackend { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 52dca4dd8..a2ffbcdb7 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -153,12 +153,8 @@ void Shutdown() { | |||
| 153 | * @return True on success, otherwise false | 153 | * @return True on success, otherwise false |
| 154 | */ | 154 | */ |
| 155 | bool LoadExec(u32 entry_point) { | 155 | bool LoadExec(u32 entry_point) { |
| 156 | Core::g_app_core->SetPC(entry_point); | ||
| 157 | |||
| 158 | // 0x30 is the typical main thread priority I've seen used so far | 156 | // 0x30 is the typical main thread priority I've seen used so far |
| 159 | g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); | 157 | g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30); |
| 160 | // Setup the idle thread | ||
| 161 | Kernel::SetupIdleThread(); | ||
| 162 | 158 | ||
| 163 | return true; | 159 | return true; |
| 164 | } | 160 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3987f9608..7f629c20e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -21,8 +21,11 @@ | |||
| 21 | 21 | ||
| 22 | namespace Kernel { | 22 | namespace Kernel { |
| 23 | 23 | ||
| 24 | /// Event type for the thread wake up event | ||
| 25 | static int ThreadWakeupEventType = -1; | ||
| 26 | |||
| 24 | bool Thread::ShouldWait() { | 27 | bool Thread::ShouldWait() { |
| 25 | return status != THREADSTATUS_DORMANT; | 28 | return status != THREADSTATUS_DEAD; |
| 26 | } | 29 | } |
| 27 | 30 | ||
| 28 | void Thread::Acquire() { | 31 | void Thread::Acquire() { |
| @@ -33,12 +36,20 @@ void Thread::Acquire() { | |||
| 33 | static std::vector<SharedPtr<Thread>> thread_list; | 36 | static std::vector<SharedPtr<Thread>> thread_list; |
| 34 | 37 | ||
| 35 | // Lists only ready thread ids. | 38 | // Lists only ready thread ids. |
| 36 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; | 39 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue; |
| 37 | 40 | ||
| 38 | static Thread* current_thread; | 41 | static Thread* current_thread; |
| 39 | 42 | ||
| 40 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 43 | // The first available thread id at startup |
| 41 | static u32 next_thread_id; ///< The next available thread id | 44 | static u32 next_thread_id = 1; |
| 45 | |||
| 46 | /** | ||
| 47 | * Creates a new thread ID | ||
| 48 | * @return The new thread ID | ||
| 49 | */ | ||
| 50 | inline static u32 const NewThreadId() { | ||
| 51 | return next_thread_id++; | ||
| 52 | } | ||
| 42 | 53 | ||
| 43 | Thread::Thread() {} | 54 | Thread::Thread() {} |
| 44 | Thread::~Thread() {} | 55 | Thread::~Thread() {} |
| @@ -47,86 +58,53 @@ Thread* GetCurrentThread() { | |||
| 47 | return current_thread; | 58 | return current_thread; |
| 48 | } | 59 | } |
| 49 | 60 | ||
| 50 | /// Resets a thread | 61 | /** |
| 51 | static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | 62 | * Check if a thread is waiting on the specified wait object |
| 52 | memset(&t->context, 0, sizeof(Core::ThreadContext)); | 63 | * @param thread The thread to test |
| 53 | 64 | * @param wait_object The object to test against | |
| 54 | t->context.cpu_registers[0] = arg; | 65 | * @return True if the thread is waiting, false otherwise |
| 55 | t->context.pc = t->entry_point; | 66 | */ |
| 56 | t->context.sp = t->stack_top; | ||
| 57 | t->context.cpsr = 0x1F; // Usermode | ||
| 58 | |||
| 59 | // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a | ||
| 60 | // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be | ||
| 61 | // agnostic of the CPU core. | ||
| 62 | t->context.mode = 8; | ||
| 63 | |||
| 64 | if (t->current_priority < lowest_priority) { | ||
| 65 | t->current_priority = t->initial_priority; | ||
| 66 | } | ||
| 67 | |||
| 68 | t->wait_objects.clear(); | ||
| 69 | t->wait_address = 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Change a thread to "ready" state | ||
| 73 | static void ChangeReadyState(Thread* t, bool ready) { | ||
| 74 | if (t->IsReady()) { | ||
| 75 | if (!ready) { | ||
| 76 | thread_ready_queue.remove(t->current_priority, t); | ||
| 77 | } | ||
| 78 | } else if (ready) { | ||
| 79 | if (t->IsRunning()) { | ||
| 80 | thread_ready_queue.push_front(t->current_priority, t); | ||
| 81 | } else { | ||
| 82 | thread_ready_queue.push_back(t->current_priority, t); | ||
| 83 | } | ||
| 84 | t->status = THREADSTATUS_READY; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Check if a thread is waiting on a the specified wait object | ||
| 89 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | 67 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { |
| 90 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); | 68 | if (thread->status != THREADSTATUS_WAIT_SYNCH) |
| 69 | return false; | ||
| 91 | 70 | ||
| 92 | if (itr != thread->wait_objects.end()) | 71 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); |
| 93 | return thread->IsWaiting(); | 72 | return itr != thread->wait_objects.end(); |
| 94 | |||
| 95 | return false; | ||
| 96 | } | 73 | } |
| 97 | 74 | ||
| 98 | /// Check if the specified thread is waiting on the specified address to be arbitrated | 75 | /** |
| 76 | * Check if the specified thread is waiting on the specified address to be arbitrated | ||
| 77 | * @param thread The thread to test | ||
| 78 | * @param wait_address The address to test against | ||
| 79 | * @return True if the thread is waiting, false otherwise | ||
| 80 | */ | ||
| 99 | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | 81 | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { |
| 100 | return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; | 82 | return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address; |
| 101 | } | 83 | } |
| 102 | 84 | ||
| 103 | /// Stops the current thread | 85 | void Thread::Stop() { |
| 104 | void Thread::Stop(const char* reason) { | ||
| 105 | // Release all the mutexes that this thread holds | 86 | // Release all the mutexes that this thread holds |
| 106 | ReleaseThreadMutexes(this); | 87 | ReleaseThreadMutexes(this); |
| 107 | 88 | ||
| 108 | ChangeReadyState(this, false); | 89 | // Cancel any outstanding wakeup events for this thread |
| 109 | status = THREADSTATUS_DORMANT; | 90 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 91 | |||
| 92 | // Clean up thread from ready queue | ||
| 93 | // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) | ||
| 94 | if (status == THREADSTATUS_READY){ | ||
| 95 | ready_queue.remove(current_priority, this); | ||
| 96 | } | ||
| 97 | |||
| 98 | status = THREADSTATUS_DEAD; | ||
| 99 | |||
| 110 | WakeupAllWaitingThreads(); | 100 | WakeupAllWaitingThreads(); |
| 111 | 101 | ||
| 112 | // Stopped threads are never waiting. | 102 | // Clean up any dangling references in objects that this thread was waiting for |
| 113 | for (auto& wait_object : wait_objects) { | 103 | for (auto& wait_object : wait_objects) { |
| 114 | wait_object->RemoveWaitingThread(this); | 104 | wait_object->RemoveWaitingThread(this); |
| 115 | } | 105 | } |
| 116 | wait_objects.clear(); | ||
| 117 | wait_address = 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Changes a threads state | ||
| 121 | static void ChangeThreadState(Thread* t, ThreadStatus new_status) { | ||
| 122 | if (!t || t->status == new_status) { | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); | ||
| 126 | t->status = new_status; | ||
| 127 | } | 106 | } |
| 128 | 107 | ||
| 129 | /// Arbitrate the highest priority thread that is waiting | ||
| 130 | Thread* ArbitrateHighestPriorityThread(u32 address) { | 108 | Thread* ArbitrateHighestPriorityThread(u32 address) { |
| 131 | Thread* highest_priority_thread = nullptr; | 109 | Thread* highest_priority_thread = nullptr; |
| 132 | s32 priority = THREADPRIO_LOWEST; | 110 | s32 priority = THREADPRIO_LOWEST; |
| @@ -153,108 +131,113 @@ Thread* ArbitrateHighestPriorityThread(u32 address) { | |||
| 153 | return highest_priority_thread; | 131 | return highest_priority_thread; |
| 154 | } | 132 | } |
| 155 | 133 | ||
| 156 | /// Arbitrate all threads currently waiting | ||
| 157 | void ArbitrateAllThreads(u32 address) { | 134 | void ArbitrateAllThreads(u32 address) { |
| 158 | 135 | // Resume all threads found to be waiting on the address | |
| 159 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | ||
| 160 | for (auto& thread : thread_list) { | 136 | for (auto& thread : thread_list) { |
| 161 | if (CheckWait_AddressArbiter(thread.get(), address)) | 137 | if (CheckWait_AddressArbiter(thread.get(), address)) |
| 162 | thread->ResumeFromWait(); | 138 | thread->ResumeFromWait(); |
| 163 | } | 139 | } |
| 164 | } | 140 | } |
| 165 | 141 | ||
| 166 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) | 142 | /** |
| 167 | static void CallThread(Thread* t) { | 143 | * Switches the CPU's active thread context to that of the specified thread |
| 168 | // Stop waiting | 144 | * @param new_thread The thread to switch to |
| 169 | ChangeThreadState(t, THREADSTATUS_READY); | 145 | */ |
| 170 | } | 146 | static void SwitchContext(Thread* new_thread) { |
| 147 | _dbg_assert_msg_(Kernel, new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); | ||
| 171 | 148 | ||
| 172 | /// Switches CPU context to that of the specified thread | 149 | Thread* previous_thread = GetCurrentThread(); |
| 173 | static void SwitchContext(Thread* t) { | ||
| 174 | Thread* cur = GetCurrentThread(); | ||
| 175 | 150 | ||
| 176 | // Save context for current thread | 151 | // Save context for previous thread |
| 177 | if (cur) { | 152 | if (previous_thread) { |
| 178 | Core::g_app_core->SaveContext(cur->context); | 153 | Core::g_app_core->SaveContext(previous_thread->context); |
| 179 | 154 | ||
| 180 | if (cur->IsRunning()) { | 155 | if (previous_thread->status == THREADSTATUS_RUNNING) { |
| 181 | ChangeReadyState(cur, true); | 156 | // This is only the case when a reschedule is triggered without the current thread |
| 157 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | ||
| 158 | ready_queue.push_front(previous_thread->current_priority, previous_thread); | ||
| 159 | previous_thread->status = THREADSTATUS_READY; | ||
| 182 | } | 160 | } |
| 183 | } | 161 | } |
| 162 | |||
| 184 | // Load context of new thread | 163 | // Load context of new thread |
| 185 | if (t) { | 164 | if (new_thread) { |
| 186 | current_thread = t; | 165 | current_thread = new_thread; |
| 187 | ChangeReadyState(t, false); | 166 | |
| 188 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | 167 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 189 | Core::g_app_core->LoadContext(t->context); | 168 | new_thread->status = THREADSTATUS_RUNNING; |
| 169 | |||
| 170 | Core::g_app_core->LoadContext(new_thread->context); | ||
| 190 | } else { | 171 | } else { |
| 191 | current_thread = nullptr; | 172 | current_thread = nullptr; |
| 192 | } | 173 | } |
| 193 | } | 174 | } |
| 194 | 175 | ||
| 195 | /// Gets the next thread that is ready to be run by priority | 176 | /** |
| 196 | static Thread* NextThread() { | 177 | * Pops and returns the next thread from the thread queue |
| 178 | * @return A pointer to the next ready thread | ||
| 179 | */ | ||
| 180 | static Thread* PopNextReadyThread() { | ||
| 197 | Thread* next; | 181 | Thread* next; |
| 198 | Thread* cur = GetCurrentThread(); | 182 | Thread* thread = GetCurrentThread(); |
| 199 | 183 | ||
| 200 | if (cur && cur->IsRunning()) { | 184 | if (thread && thread->status == THREADSTATUS_RUNNING) { |
| 201 | next = thread_ready_queue.pop_first_better(cur->current_priority); | 185 | // We have to do better than the current thread. |
| 186 | // This call returns null when that's not possible. | ||
| 187 | next = ready_queue.pop_first_better(thread->current_priority); | ||
| 202 | } else { | 188 | } else { |
| 203 | next = thread_ready_queue.pop_first(); | 189 | next = ready_queue.pop_first(); |
| 204 | } | ||
| 205 | if (next == 0) { | ||
| 206 | return nullptr; | ||
| 207 | } | 190 | } |
| 191 | |||
| 208 | return next; | 192 | return next; |
| 209 | } | 193 | } |
| 210 | 194 | ||
| 211 | void WaitCurrentThread_Sleep() { | 195 | void WaitCurrentThread_Sleep() { |
| 212 | Thread* thread = GetCurrentThread(); | 196 | Thread* thread = GetCurrentThread(); |
| 213 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 197 | thread->status = THREADSTATUS_WAIT_SLEEP; |
| 214 | } | 198 | } |
| 215 | 199 | ||
| 216 | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { | 200 | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all) { |
| 217 | Thread* thread = GetCurrentThread(); | 201 | Thread* thread = GetCurrentThread(); |
| 218 | thread->wait_set_output = wait_set_output; | 202 | thread->wait_set_output = wait_set_output; |
| 219 | thread->wait_all = wait_all; | 203 | thread->wait_all = wait_all; |
| 220 | 204 | thread->wait_objects = std::move(wait_objects); | |
| 221 | // It's possible to call WaitSynchronizationN without any objects passed in... | 205 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 222 | if (wait_object != nullptr) | ||
| 223 | thread->wait_objects.push_back(wait_object); | ||
| 224 | |||
| 225 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||
| 226 | } | 206 | } |
| 227 | 207 | ||
| 228 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | 208 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { |
| 229 | Thread* thread = GetCurrentThread(); | 209 | Thread* thread = GetCurrentThread(); |
| 230 | thread->wait_address = wait_address; | 210 | thread->wait_address = wait_address; |
| 231 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 211 | thread->status = THREADSTATUS_WAIT_ARB; |
| 232 | } | 212 | } |
| 233 | 213 | ||
| 234 | /// Event type for the thread wake up event | ||
| 235 | static int ThreadWakeupEventType = -1; | ||
| 236 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing | 214 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing |
| 237 | // us to simply use a pool index or similar. | 215 | // us to simply use a pool index or similar. |
| 238 | static Kernel::HandleTable wakeup_callback_handle_table; | 216 | static Kernel::HandleTable wakeup_callback_handle_table; |
| 239 | 217 | ||
| 240 | /// Callback that will wake up the thread it was scheduled for | 218 | /** |
| 219 | * Callback that will wake up the thread it was scheduled for | ||
| 220 | * @param thread_handle The handle of the thread that's been awoken | ||
| 221 | * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time | ||
| 222 | */ | ||
| 241 | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | 223 | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { |
| 242 | SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); | 224 | SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); |
| 243 | if (thread == nullptr) { | 225 | if (thread == nullptr) { |
| 244 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle); | 226 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle); |
| 245 | return; | 227 | return; |
| 246 | } | 228 | } |
| 247 | 229 | ||
| 248 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 230 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { |
| 249 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | 231 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 232 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | ||
| 250 | 233 | ||
| 251 | if (thread->wait_set_output) | 234 | if (thread->wait_set_output) |
| 252 | thread->SetWaitSynchronizationOutput(-1); | 235 | thread->SetWaitSynchronizationOutput(-1); |
| 236 | } | ||
| 253 | 237 | ||
| 254 | thread->ResumeFromWait(); | 238 | thread->ResumeFromWait(); |
| 255 | } | 239 | } |
| 256 | 240 | ||
| 257 | |||
| 258 | void Thread::WakeAfterDelay(s64 nanoseconds) { | 241 | void Thread::WakeAfterDelay(s64 nanoseconds) { |
| 259 | // Don't schedule a wakeup if the thread wants to wait forever | 242 | // Don't schedule a wakeup if the thread wants to wait forever |
| 260 | if (nanoseconds == -1) | 243 | if (nanoseconds == -1) |
| @@ -265,7 +248,7 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 265 | } | 248 | } |
| 266 | 249 | ||
| 267 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { | 250 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { |
| 268 | if (wait_objects.empty()) { | 251 | if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) { |
| 269 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | 252 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); |
| 270 | return; | 253 | return; |
| 271 | } | 254 | } |
| @@ -307,34 +290,48 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) { | |||
| 307 | } | 290 | } |
| 308 | 291 | ||
| 309 | void Thread::ResumeFromWait() { | 292 | void Thread::ResumeFromWait() { |
| 310 | // Cancel any outstanding wakeup events | 293 | // Cancel any outstanding wakeup events for this thread |
| 311 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 294 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 312 | 295 | ||
| 313 | status &= ~THREADSTATUS_WAIT; | 296 | switch (status) { |
| 314 | 297 | case THREADSTATUS_WAIT_SYNCH: | |
| 315 | // Remove this thread from all other WaitObjects | 298 | // Remove this thread from all other WaitObjects |
| 316 | for (auto wait_object : wait_objects) | 299 | for (auto wait_object : wait_objects) |
| 317 | wait_object->RemoveWaitingThread(this); | 300 | wait_object->RemoveWaitingThread(this); |
| 318 | 301 | break; | |
| 319 | wait_objects.clear(); | 302 | case THREADSTATUS_WAIT_ARB: |
| 320 | wait_set_output = false; | 303 | case THREADSTATUS_WAIT_SLEEP: |
| 321 | wait_all = false; | 304 | break; |
| 322 | wait_address = 0; | 305 | case THREADSTATUS_RUNNING: |
| 323 | 306 | case THREADSTATUS_READY: | |
| 324 | if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 307 | LOG_ERROR(Kernel, "Thread with object id %u has already resumed.", GetObjectId()); |
| 325 | ChangeReadyState(this, true); | 308 | _dbg_assert_(Kernel, false); |
| 309 | return; | ||
| 310 | case THREADSTATUS_DEAD: | ||
| 311 | // This should never happen, as threads must complete before being stopped. | ||
| 312 | LOG_CRITICAL(Kernel, "Thread with object id %u cannot be resumed because it's DEAD.", | ||
| 313 | GetObjectId()); | ||
| 314 | _dbg_assert_(Kernel, false); | ||
| 315 | return; | ||
| 326 | } | 316 | } |
| 317 | |||
| 318 | ready_queue.push_back(current_priority, this); | ||
| 319 | status = THREADSTATUS_READY; | ||
| 327 | } | 320 | } |
| 328 | 321 | ||
| 329 | /// Prints the thread queue for debugging purposes | 322 | /** |
| 323 | * Prints the thread queue for debugging purposes | ||
| 324 | */ | ||
| 330 | static void DebugThreadQueue() { | 325 | static void DebugThreadQueue() { |
| 331 | Thread* thread = GetCurrentThread(); | 326 | Thread* thread = GetCurrentThread(); |
| 332 | if (!thread) { | 327 | if (!thread) { |
| 333 | return; | 328 | LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); |
| 329 | } else { | ||
| 330 | LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); | ||
| 334 | } | 331 | } |
| 335 | LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); | 332 | |
| 336 | for (auto& t : thread_list) { | 333 | for (auto& t : thread_list) { |
| 337 | s32 priority = thread_ready_queue.contains(t.get()); | 334 | s32 priority = ready_queue.contains(t.get()); |
| 338 | if (priority != -1) { | 335 | if (priority != -1) { |
| 339 | LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); | 336 | LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); |
| 340 | } | 337 | } |
| @@ -342,14 +339,7 @@ static void DebugThreadQueue() { | |||
| 342 | } | 339 | } |
| 343 | 340 | ||
| 344 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, | 341 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, |
| 345 | u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) { | 342 | u32 arg, s32 processor_id, VAddr stack_top) { |
| 346 | if (stack_size < 0x200) { | ||
| 347 | LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size); | ||
| 348 | // TODO: Verify error | ||
| 349 | return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, | ||
| 350 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 351 | } | ||
| 352 | |||
| 353 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 343 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 354 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 344 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 355 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | 345 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", |
| @@ -369,13 +359,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 369 | SharedPtr<Thread> thread(new Thread); | 359 | SharedPtr<Thread> thread(new Thread); |
| 370 | 360 | ||
| 371 | thread_list.push_back(thread); | 361 | thread_list.push_back(thread); |
| 372 | thread_ready_queue.prepare(priority); | 362 | ready_queue.prepare(priority); |
| 373 | 363 | ||
| 374 | thread->thread_id = next_thread_id++; | 364 | thread->thread_id = NewThreadId(); |
| 375 | thread->status = THREADSTATUS_DORMANT; | 365 | thread->status = THREADSTATUS_DORMANT; |
| 376 | thread->entry_point = entry_point; | 366 | thread->entry_point = entry_point; |
| 377 | thread->stack_top = stack_top; | 367 | thread->stack_top = stack_top; |
| 378 | thread->stack_size = stack_size; | ||
| 379 | thread->initial_priority = thread->current_priority = priority; | 368 | thread->initial_priority = thread->current_priority = priority; |
| 380 | thread->processor_id = processor_id; | 369 | thread->processor_id = processor_id; |
| 381 | thread->wait_set_output = false; | 370 | thread->wait_set_output = false; |
| @@ -385,75 +374,74 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 385 | thread->name = std::move(name); | 374 | thread->name = std::move(name); |
| 386 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 375 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); |
| 387 | 376 | ||
| 388 | ResetThread(thread.get(), arg, 0); | 377 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used |
| 389 | CallThread(thread.get()); | 378 | // to initialize the context |
| 379 | Core::g_app_core->ResetContext(thread->context, stack_top, entry_point, arg); | ||
| 380 | |||
| 381 | ready_queue.push_back(thread->current_priority, thread.get()); | ||
| 382 | thread->status = THREADSTATUS_READY; | ||
| 390 | 383 | ||
| 391 | return MakeResult<SharedPtr<Thread>>(std::move(thread)); | 384 | return MakeResult<SharedPtr<Thread>>(std::move(thread)); |
| 392 | } | 385 | } |
| 393 | 386 | ||
| 394 | /// Set the priority of the thread specified by handle | 387 | // TODO(peachum): Remove this. Range checking should be done, and an appropriate error should be returned. |
| 395 | void Thread::SetPriority(s32 priority) { | 388 | static void ClampPriority(const Thread* thread, s32* priority) { |
| 396 | // If priority is invalid, clamp to valid range | 389 | if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) { |
| 397 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 390 | _dbg_assert_msg_(Kernel, false, "Application passed an out of range priority. An error should be returned."); |
| 398 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 391 | |
| 399 | LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); | 392 | s32 new_priority = CLAMP(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 393 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | ||
| 394 | thread->name.c_str(), *priority, new_priority); | ||
| 400 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 395 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 401 | // validity of this | 396 | // validity of this |
| 402 | priority = new_priority; | 397 | *priority = new_priority; |
| 403 | } | 398 | } |
| 399 | } | ||
| 404 | 400 | ||
| 405 | // Change thread priority | 401 | void Thread::SetPriority(s32 priority) { |
| 406 | s32 old = current_priority; | 402 | ClampPriority(this, &priority); |
| 407 | thread_ready_queue.remove(old, this); | ||
| 408 | current_priority = priority; | ||
| 409 | thread_ready_queue.prepare(current_priority); | ||
| 410 | 403 | ||
| 411 | // Change thread status to "ready" and push to ready queue | 404 | if (current_priority == priority) { |
| 412 | if (IsRunning()) { | 405 | return; |
| 413 | status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; | ||
| 414 | } | 406 | } |
| 415 | if (IsReady()) { | 407 | |
| 416 | thread_ready_queue.push_back(current_priority, this); | 408 | if (status == THREADSTATUS_READY) { |
| 409 | // If thread was ready, adjust queues | ||
| 410 | ready_queue.remove(current_priority, this); | ||
| 411 | ready_queue.prepare(priority); | ||
| 412 | ready_queue.push_back(priority, this); | ||
| 417 | } | 413 | } |
| 414 | |||
| 415 | current_priority = priority; | ||
| 418 | } | 416 | } |
| 419 | 417 | ||
| 420 | SharedPtr<Thread> SetupIdleThread() { | 418 | SharedPtr<Thread> SetupIdleThread() { |
| 421 | // We need to pass a few valid values to get around parameter checking in Thread::Create. | 419 | // We need to pass a few valid values to get around parameter checking in Thread::Create. |
| 422 | auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, | 420 | auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, |
| 423 | THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom(); | 421 | THREADPROCESSORID_0, 0).MoveFrom(); |
| 424 | 422 | ||
| 425 | thread->idle = true; | 423 | thread->idle = true; |
| 426 | CallThread(thread.get()); | ||
| 427 | return thread; | 424 | return thread; |
| 428 | } | 425 | } |
| 429 | 426 | ||
| 430 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { | 427 | SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority) { |
| 428 | _dbg_assert_(Kernel, !GetCurrentThread()); | ||
| 429 | |||
| 431 | // Initialize new "main" thread | 430 | // Initialize new "main" thread |
| 432 | auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, | 431 | auto thread_res = Thread::Create("main", entry_point, priority, 0, |
| 433 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | 432 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END); |
| 434 | // TODO(yuriks): Propagate error | 433 | |
| 435 | _dbg_assert_(Kernel, thread_res.Succeeded()); | 434 | SharedPtr<Thread> thread = thread_res.MoveFrom(); |
| 436 | SharedPtr<Thread> thread = std::move(*thread_res); | ||
| 437 | |||
| 438 | // If running another thread already, set it to "ready" state | ||
| 439 | Thread* cur = GetCurrentThread(); | ||
| 440 | if (cur && cur->IsRunning()) { | ||
| 441 | ChangeReadyState(cur, true); | ||
| 442 | } | ||
| 443 | 435 | ||
| 444 | // Run new "main" thread | 436 | // Run new "main" thread |
| 445 | current_thread = thread.get(); | 437 | SwitchContext(thread.get()); |
| 446 | thread->status = THREADSTATUS_RUNNING; | ||
| 447 | Core::g_app_core->LoadContext(thread->context); | ||
| 448 | 438 | ||
| 449 | return thread; | 439 | return thread; |
| 450 | } | 440 | } |
| 451 | 441 | ||
| 452 | |||
| 453 | /// Reschedules to the next available thread (call after current thread is suspended) | ||
| 454 | void Reschedule() { | 442 | void Reschedule() { |
| 455 | Thread* prev = GetCurrentThread(); | 443 | Thread* prev = GetCurrentThread(); |
| 456 | Thread* next = NextThread(); | 444 | Thread* next = PopNextReadyThread(); |
| 457 | HLE::g_reschedule = false; | 445 | HLE::g_reschedule = false; |
| 458 | 446 | ||
| 459 | if (next != nullptr) { | 447 | if (next != nullptr) { |
| @@ -480,8 +468,10 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||
| 480 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 468 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 481 | 469 | ||
| 482 | void ThreadingInit() { | 470 | void ThreadingInit() { |
| 483 | next_thread_id = INITIAL_THREAD_ID; | ||
| 484 | ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | 471 | ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); |
| 472 | |||
| 473 | // Setup the idle thread | ||
| 474 | SetupIdleThread(); | ||
| 485 | } | 475 | } |
| 486 | 476 | ||
| 487 | void ThreadingShutdown() { | 477 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 633bb7c98..cfd073a70 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -31,13 +31,13 @@ enum ThreadProcessorId { | |||
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum ThreadStatus { | 33 | enum ThreadStatus { |
| 34 | THREADSTATUS_RUNNING = 1, | 34 | THREADSTATUS_RUNNING, ///< Currently running |
| 35 | THREADSTATUS_READY = 2, | 35 | THREADSTATUS_READY, ///< Ready to run |
| 36 | THREADSTATUS_WAIT = 4, | 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter |
| 37 | THREADSTATUS_SUSPEND = 8, | 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC |
| 38 | THREADSTATUS_DORMANT = 16, | 38 | THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC |
| 39 | THREADSTATUS_DEAD = 32, | 39 | THREADSTATUS_DORMANT, ///< Created but not yet made ready |
| 40 | THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND | 40 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | namespace Kernel { | 43 | namespace Kernel { |
| @@ -46,8 +46,19 @@ class Mutex; | |||
| 46 | 46 | ||
| 47 | class Thread final : public WaitObject { | 47 | class Thread final : public WaitObject { |
| 48 | public: | 48 | public: |
| 49 | /** | ||
| 50 | * Creates and returns a new thread. The new thread is immediately scheduled | ||
| 51 | * @param name The friendly name desired for the thread | ||
| 52 | * @param entry_point The address at which the thread should start execution | ||
| 53 | * @param priority The thread's priority | ||
| 54 | * @param arg User data to pass to the thread | ||
| 55 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | ||
| 56 | * @param stack_top The address of the thread's stack top | ||
| 57 | * @param stack_size The size of the thread's stack | ||
| 58 | * @return A shared pointer to the newly created thread | ||
| 59 | */ | ||
| 49 | static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, | 60 | static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, |
| 50 | u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); | 61 | u32 arg, s32 processor_id, VAddr stack_top); |
| 51 | 62 | ||
| 52 | std::string GetName() const override { return name; } | 63 | std::string GetName() const override { return name; } |
| 53 | std::string GetTypeName() const override { return "Thread"; } | 64 | std::string GetTypeName() const override { return "Thread"; } |
| @@ -55,22 +66,32 @@ public: | |||
| 55 | static const HandleType HANDLE_TYPE = HandleType::Thread; | 66 | static const HandleType HANDLE_TYPE = HandleType::Thread; |
| 56 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | 67 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 57 | 68 | ||
| 58 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | ||
| 59 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | ||
| 60 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 61 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 62 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 63 | inline bool IsIdle() const { return idle; } | ||
| 64 | |||
| 65 | bool ShouldWait() override; | 69 | bool ShouldWait() override; |
| 66 | void Acquire() override; | 70 | void Acquire() override; |
| 67 | 71 | ||
| 72 | /** | ||
| 73 | * Checks if the thread is an idle (stub) thread | ||
| 74 | * @return True if the thread is an idle (stub) thread, false otherwise | ||
| 75 | */ | ||
| 76 | inline bool IsIdle() const { return idle; } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Gets the thread's current priority | ||
| 80 | * @return The current thread's priority | ||
| 81 | */ | ||
| 68 | s32 GetPriority() const { return current_priority; } | 82 | s32 GetPriority() const { return current_priority; } |
| 83 | |||
| 84 | /** | ||
| 85 | * Sets the thread's current priority | ||
| 86 | * @param priority The new priority | ||
| 87 | */ | ||
| 69 | void SetPriority(s32 priority); | 88 | void SetPriority(s32 priority); |
| 70 | 89 | ||
| 90 | /** | ||
| 91 | * Gets the thread's thread ID | ||
| 92 | * @return The thread's ID | ||
| 93 | */ | ||
| 71 | u32 GetThreadId() const { return thread_id; } | 94 | u32 GetThreadId() const { return thread_id; } |
| 72 | |||
| 73 | void Stop(const char* reason); | ||
| 74 | 95 | ||
| 75 | /** | 96 | /** |
| 76 | * Release an acquired wait object | 97 | * Release an acquired wait object |
| @@ -78,12 +99,14 @@ public: | |||
| 78 | */ | 99 | */ |
| 79 | void ReleaseWaitObject(WaitObject* wait_object); | 100 | void ReleaseWaitObject(WaitObject* wait_object); |
| 80 | 101 | ||
| 81 | /// Resumes a thread from waiting by marking it as "ready" | 102 | /** |
| 103 | * Resumes a thread from waiting | ||
| 104 | */ | ||
| 82 | void ResumeFromWait(); | 105 | void ResumeFromWait(); |
| 83 | 106 | ||
| 84 | /** | 107 | /** |
| 85 | * Schedules an event to wake up the specified thread after the specified delay. | 108 | * Schedules an event to wake up the specified thread after the specified delay |
| 86 | * @param nanoseconds The time this thread will be allowed to sleep for. | 109 | * @param nanoseconds The time this thread will be allowed to sleep for |
| 87 | */ | 110 | */ |
| 88 | void WakeAfterDelay(s64 nanoseconds); | 111 | void WakeAfterDelay(s64 nanoseconds); |
| 89 | 112 | ||
| @@ -99,6 +122,11 @@ public: | |||
| 99 | */ | 122 | */ |
| 100 | void SetWaitSynchronizationOutput(s32 output); | 123 | void SetWaitSynchronizationOutput(s32 output); |
| 101 | 124 | ||
| 125 | /** | ||
| 126 | * Stops a thread, invalidating it from further use | ||
| 127 | */ | ||
| 128 | void Stop(); | ||
| 129 | |||
| 102 | Core::ThreadContext context; | 130 | Core::ThreadContext context; |
| 103 | 131 | ||
| 104 | u32 thread_id; | 132 | u32 thread_id; |
| @@ -106,7 +134,6 @@ public: | |||
| 106 | u32 status; | 134 | u32 status; |
| 107 | u32 entry_point; | 135 | u32 entry_point; |
| 108 | u32 stack_top; | 136 | u32 stack_top; |
| 109 | u32 stack_size; | ||
| 110 | 137 | ||
| 111 | s32 initial_priority; | 138 | s32 initial_priority; |
| 112 | s32 current_priority; | 139 | s32 current_priority; |
| @@ -136,31 +163,49 @@ private: | |||
| 136 | 163 | ||
| 137 | extern SharedPtr<Thread> g_main_thread; | 164 | extern SharedPtr<Thread> g_main_thread; |
| 138 | 165 | ||
| 139 | /// Sets up the primary application thread | 166 | /** |
| 140 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | 167 | * Sets up the primary application thread |
| 168 | * @param stack_size The size of the thread's stack | ||
| 169 | * @param entry_point The address at which the thread should start execution | ||
| 170 | * @param priority The priority to give the main thread | ||
| 171 | * @return A shared pointer to the main thread | ||
| 172 | */ | ||
| 173 | SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority); | ||
| 141 | 174 | ||
| 142 | /// Reschedules to the next available thread (call after current thread is suspended) | 175 | /** |
| 176 | * Reschedules to the next available thread (call after current thread is suspended) | ||
| 177 | */ | ||
| 143 | void Reschedule(); | 178 | void Reschedule(); |
| 144 | 179 | ||
| 145 | /// Arbitrate the highest priority thread that is waiting | 180 | /** |
| 181 | * Arbitrate the highest priority thread that is waiting | ||
| 182 | * @param address The address for which waiting threads should be arbitrated | ||
| 183 | */ | ||
| 146 | Thread* ArbitrateHighestPriorityThread(u32 address); | 184 | Thread* ArbitrateHighestPriorityThread(u32 address); |
| 147 | 185 | ||
| 148 | /// Arbitrate all threads currently waiting... | 186 | /** |
| 187 | * Arbitrate all threads currently waiting. | ||
| 188 | * @param address The address for which waiting threads should be arbitrated | ||
| 189 | */ | ||
| 149 | void ArbitrateAllThreads(u32 address); | 190 | void ArbitrateAllThreads(u32 address); |
| 150 | 191 | ||
| 151 | /// Gets the current thread | 192 | /** |
| 193 | * Gets the current thread | ||
| 194 | */ | ||
| 152 | Thread* GetCurrentThread(); | 195 | Thread* GetCurrentThread(); |
| 153 | 196 | ||
| 154 | /// Waits the current thread on a sleep | 197 | /** |
| 198 | * Waits the current thread on a sleep | ||
| 199 | */ | ||
| 155 | void WaitCurrentThread_Sleep(); | 200 | void WaitCurrentThread_Sleep(); |
| 156 | 201 | ||
| 157 | /** | 202 | /** |
| 158 | * Waits the current thread from a WaitSynchronization call | 203 | * Waits the current thread from a WaitSynchronization call |
| 159 | * @param wait_object Kernel object that we are waiting on | 204 | * @param wait_objects Kernel objects that we are waiting on |
| 160 | * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) | 205 | * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) |
| 161 | * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) | 206 | * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) |
| 162 | */ | 207 | */ |
| 163 | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); | 208 | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all); |
| 164 | 209 | ||
| 165 | /** | 210 | /** |
| 166 | * Waits the current thread from an ArbitrateAddress call | 211 | * Waits the current thread from an ArbitrateAddress call |
| @@ -172,14 +217,18 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); | |||
| 172 | * Sets up the idle thread, this is a thread that is intended to never execute instructions, | 217 | * Sets up the idle thread, this is a thread that is intended to never execute instructions, |
| 173 | * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue | 218 | * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue |
| 174 | * and will try to yield on every call. | 219 | * and will try to yield on every call. |
| 175 | * @returns The handle of the idle thread | 220 | * @return The handle of the idle thread |
| 176 | */ | 221 | */ |
| 177 | SharedPtr<Thread> SetupIdleThread(); | 222 | SharedPtr<Thread> SetupIdleThread(); |
| 178 | 223 | ||
| 179 | /// Initialize threading | 224 | /** |
| 225 | * Initialize threading | ||
| 226 | */ | ||
| 180 | void ThreadingInit(); | 227 | void ThreadingInit(); |
| 181 | 228 | ||
| 182 | /// Shutdown threading | 229 | /** |
| 230 | * Shutdown threading | ||
| 231 | */ | ||
| 183 | void ThreadingShutdown(); | 232 | void ThreadingShutdown(); |
| 184 | 233 | ||
| 185 | } // namespace | 234 | } // namespace |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 948b9e38e..9c6ca29e5 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -307,14 +307,14 @@ public: | |||
| 307 | } | 307 | } |
| 308 | 308 | ||
| 309 | ResultVal& operator=(const ResultVal& o) { | 309 | ResultVal& operator=(const ResultVal& o) { |
| 310 | if (*this) { | 310 | if (!empty()) { |
| 311 | if (o) { | 311 | if (!o.empty()) { |
| 312 | *GetPointer() = *o.GetPointer(); | 312 | *GetPointer() = *o.GetPointer(); |
| 313 | } else { | 313 | } else { |
| 314 | GetPointer()->~T(); | 314 | GetPointer()->~T(); |
| 315 | } | 315 | } |
| 316 | } else { | 316 | } else { |
| 317 | if (o) { | 317 | if (!o.empty()) { |
| 318 | new (&storage) T(*o.GetPointer()); | 318 | new (&storage) T(*o.GetPointer()); |
| 319 | } | 319 | } |
| 320 | } | 320 | } |
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 1da9f59f6..5aa53cf75 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/file_util.h" | 5 | #include "common/file_util.h" |
| 6 | #include "common/log.h" | 6 | #include "common/log.h" |
| 7 | #include "common/string_util.h" | 7 | #include "common/string_util.h" |
| 8 | #include "core/settings.h" | ||
| 8 | #include "core/file_sys/archive_systemsavedata.h" | 9 | #include "core/file_sys/archive_systemsavedata.h" |
| 9 | #include "core/hle/hle.h" | 10 | #include "core/hle/hle.h" |
| 10 | #include "core/hle/service/cfg/cfg.h" | 11 | #include "core/hle/service/cfg/cfg.h" |
| @@ -129,6 +130,65 @@ static void GetConfigInfoBlk2(Service::Interface* self) { | |||
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | /** | 132 | /** |
| 133 | * CFG_User::SecureInfoGetRegion service function | ||
| 134 | * Inputs: | ||
| 135 | * 1 : None | ||
| 136 | * Outputs: | ||
| 137 | * 0 : Result Header code | ||
| 138 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 139 | * 2 : Region value loaded from SecureInfo offset 0x100 | ||
| 140 | */ | ||
| 141 | static void SecureInfoGetRegion(Service::Interface* self) { | ||
| 142 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 143 | |||
| 144 | cmd_buffer[1] = RESULT_SUCCESS.raw; // No Error | ||
| 145 | cmd_buffer[2] = Settings::values.region_value; | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * CFG_User::GenHashConsoleUnique service function | ||
| 150 | * Inputs: | ||
| 151 | * 1 : 20 bit application ID salt | ||
| 152 | * Outputs: | ||
| 153 | * 0 : Result Header code | ||
| 154 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 155 | * 2 : Hash/"ID" lower word | ||
| 156 | * 3 : Hash/"ID" upper word | ||
| 157 | */ | ||
| 158 | static void GenHashConsoleUnique(Service::Interface* self) { | ||
| 159 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 160 | u32 app_id_salt = cmd_buffer[1]; | ||
| 161 | |||
| 162 | cmd_buffer[1] = RESULT_SUCCESS.raw; // No Error | ||
| 163 | cmd_buffer[2] = 0x33646D6F ^ (app_id_salt & 0xFFFFF); // 3dmoo hash | ||
| 164 | cmd_buffer[3] = 0x6F534841 ^ (app_id_salt & 0xFFFFF); | ||
| 165 | |||
| 166 | LOG_WARNING(Service_CFG, "(STUBBED) called app_id_salt=0x%08X", app_id_salt); | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * CFG_User::GetRegionCanadaUSA service function | ||
| 171 | * Inputs: | ||
| 172 | * 1 : None | ||
| 173 | * Outputs: | ||
| 174 | * 0 : Result Header code | ||
| 175 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 176 | * 2 : Output value | ||
| 177 | */ | ||
| 178 | static void GetRegionCanadaUSA(Service::Interface* self) { | ||
| 179 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 180 | |||
| 181 | cmd_buffer[1] = RESULT_SUCCESS.raw; // No Error | ||
| 182 | |||
| 183 | u8 canada_or_usa = 1; | ||
| 184 | if (canada_or_usa == Settings::values.region_value) { | ||
| 185 | cmd_buffer[2] = 1; | ||
| 186 | } else { | ||
| 187 | cmd_buffer[2] = 0; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 132 | * CFG_User::GetSystemModel service function | 192 | * CFG_User::GetSystemModel service function |
| 133 | * Inputs: | 193 | * Inputs: |
| 134 | * 0 : 0x00050000 | 194 | * 0 : 0x00050000 |
| @@ -171,9 +231,9 @@ static void GetModelNintendo2DS(Service::Interface* self) { | |||
| 171 | 231 | ||
| 172 | const Interface::FunctionInfo FunctionTable[] = { | 232 | const Interface::FunctionInfo FunctionTable[] = { |
| 173 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, | 233 | {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, |
| 174 | {0x00020000, nullptr, "SecureInfoGetRegion"}, | 234 | {0x00020000, SecureInfoGetRegion, "SecureInfoGetRegion"}, |
| 175 | {0x00030040, nullptr, "GenHashConsoleUnique"}, | 235 | {0x00030040, GenHashConsoleUnique, "GenHashConsoleUnique"}, |
| 176 | {0x00040000, nullptr, "GetRegionCanadaUSA"}, | 236 | {0x00040000, GetRegionCanadaUSA, "GetRegionCanadaUSA"}, |
| 177 | {0x00050000, GetSystemModel, "GetSystemModel"}, | 237 | {0x00050000, GetSystemModel, "GetSystemModel"}, |
| 178 | {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, | 238 | {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, |
| 179 | {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, | 239 | {0x00070040, nullptr, "WriteToFirstByteCfgSavegame"}, |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 0f86894a6..a720b63f3 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -128,6 +128,31 @@ void WriteReg0x10(Service::Interface* self) { | |||
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | /** | 130 | /** |
| 131 | * DSP_DSP::WriteProcessPipe service function | ||
| 132 | * Inputs: | ||
| 133 | * 1 : Number | ||
| 134 | * 2 : Size | ||
| 135 | * 3 : (size <<14) | 0x402 | ||
| 136 | * 4 : Buffer | ||
| 137 | * Outputs: | ||
| 138 | * 0 : Return header | ||
| 139 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 140 | */ | ||
| 141 | void WriteProcessPipe(Service::Interface* self) { | ||
| 142 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 143 | |||
| 144 | u32 number = cmd_buff[1]; | ||
| 145 | u32 size = cmd_buff[2]; | ||
| 146 | u32 new_size = cmd_buff[3]; | ||
| 147 | u32 buffer = cmd_buff[4]; | ||
| 148 | |||
| 149 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 150 | |||
| 151 | LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%08X, new_size=0x%08X, buffer=0x%08X", | ||
| 152 | number, size, new_size, buffer); | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 131 | * DSP_DSP::ReadPipeIfPossible service function | 156 | * DSP_DSP::ReadPipeIfPossible service function |
| 132 | * Inputs: | 157 | * Inputs: |
| 133 | * 1 : Unknown | 158 | * 1 : Unknown |
| @@ -169,6 +194,41 @@ void ReadPipeIfPossible(Service::Interface* self) { | |||
| 169 | LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); | 194 | LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); |
| 170 | } | 195 | } |
| 171 | 196 | ||
| 197 | /** | ||
| 198 | * DSP_DSP::SetSemaphoreMask service function | ||
| 199 | * Inputs: | ||
| 200 | * 1 : Mask | ||
| 201 | * Outputs: | ||
| 202 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 203 | */ | ||
| 204 | void SetSemaphoreMask(Service::Interface* self) { | ||
| 205 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 206 | |||
| 207 | u32 mask = cmd_buff[1]; | ||
| 208 | |||
| 209 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 210 | |||
| 211 | LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); | ||
| 212 | } | ||
| 213 | |||
| 214 | /** | ||
| 215 | * DSP_DSP::GetHeadphoneStatus service function | ||
| 216 | * Inputs: | ||
| 217 | * 1 : None | ||
| 218 | * Outputs: | ||
| 219 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 220 | * 2 : The headphone status response, 0 = Not using headphones?, | ||
| 221 | * 1 = using headphones? | ||
| 222 | */ | ||
| 223 | void GetHeadphoneStatus(Service::Interface* self) { | ||
| 224 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 225 | |||
| 226 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 227 | cmd_buff[2] = 0; // Not using headphones? | ||
| 228 | |||
| 229 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 230 | } | ||
| 231 | |||
| 172 | const Interface::FunctionInfo FunctionTable[] = { | 232 | const Interface::FunctionInfo FunctionTable[] = { |
| 173 | {0x00010040, nullptr, "RecvData"}, | 233 | {0x00010040, nullptr, "RecvData"}, |
| 174 | {0x00020040, nullptr, "RecvDataIsReady"}, | 234 | {0x00020040, nullptr, "RecvDataIsReady"}, |
| @@ -179,7 +239,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 179 | {0x00090040, nullptr, "ClearSemaphore"}, | 239 | {0x00090040, nullptr, "ClearSemaphore"}, |
| 180 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, | 240 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, |
| 181 | {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, | 241 | {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, |
| 182 | {0x000D0082, nullptr, "WriteProcessPipe"}, | 242 | {0x000D0082, WriteProcessPipe, "WriteProcessPipe"}, |
| 183 | {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, | 243 | {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, |
| 184 | {0x001100C2, LoadComponent, "LoadComponent"}, | 244 | {0x001100C2, LoadComponent, "LoadComponent"}, |
| 185 | {0x00120000, nullptr, "UnloadComponent"}, | 245 | {0x00120000, nullptr, "UnloadComponent"}, |
| @@ -187,13 +247,13 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 187 | {0x00140082, nullptr, "InvalidateDCache"}, | 247 | {0x00140082, nullptr, "InvalidateDCache"}, |
| 188 | {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, | 248 | {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, |
| 189 | {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, | 249 | {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, |
| 190 | {0x00170040, nullptr, "SetSemaphoreMask"}, | 250 | {0x00170040, SetSemaphoreMask, "SetSemaphoreMask"}, |
| 191 | {0x00180040, nullptr, "GetPhysicalAddress"}, | 251 | {0x00180040, nullptr, "GetPhysicalAddress"}, |
| 192 | {0x00190040, nullptr, "GetVirtualAddress"}, | 252 | {0x00190040, nullptr, "GetVirtualAddress"}, |
| 193 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, | 253 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, |
| 194 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, | 254 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, |
| 195 | {0x001C0082, nullptr, "SetIirFilterEQ"}, | 255 | {0x001C0082, nullptr, "SetIirFilterEQ"}, |
| 196 | {0x001F0000, nullptr, "GetHeadphoneStatus"}, | 256 | {0x001F0000, GetHeadphoneStatus, "GetHeadphoneStatus"}, |
| 197 | {0x00210000, nullptr, "GetIsDspOccupied"}, | 257 | {0x00210000, nullptr, "GetIsDspOccupied"}, |
| 198 | }; | 258 | }; |
| 199 | 259 | ||
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index ccf132f31..e197d3599 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <unordered_map> | 6 | #include <unordered_map> |
| 7 | 7 | ||
| 8 | #include <boost/container/flat_map.hpp> | ||
| 9 | |||
| 8 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 9 | #include "common/file_util.h" | 11 | #include "common/file_util.h" |
| 10 | #include "common/make_unique.h" | 12 | #include "common/make_unique.h" |
| @@ -18,7 +20,6 @@ | |||
| 18 | #include "core/file_sys/archive_sdmc.h" | 20 | #include "core/file_sys/archive_sdmc.h" |
| 19 | #include "core/file_sys/directory_backend.h" | 21 | #include "core/file_sys/directory_backend.h" |
| 20 | #include "core/hle/service/fs/archive.h" | 22 | #include "core/hle/service/fs/archive.h" |
| 21 | #include "core/hle/kernel/session.h" | ||
| 22 | #include "core/hle/result.h" | 23 | #include "core/hle/result.h" |
| 23 | 24 | ||
| 24 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. | 25 | // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map. |
| @@ -74,43 +75,19 @@ enum class DirectoryCommand : u32 { | |||
| 74 | Close = 0x08020000, | 75 | Close = 0x08020000, |
| 75 | }; | 76 | }; |
| 76 | 77 | ||
| 77 | class Archive { | 78 | ResultVal<bool> File::SyncRequest() { |
| 78 | public: | 79 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 79 | Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) | 80 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); |
| 80 | : id_code(id_code), backend(std::move(backend)) { | 81 | switch (cmd) { |
| 81 | } | ||
| 82 | |||
| 83 | std::string GetName() const { return "Archive: " + backend->GetName(); } | ||
| 84 | |||
| 85 | ArchiveIdCode id_code; ///< Id code of the archive | ||
| 86 | std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface | ||
| 87 | }; | ||
| 88 | |||
| 89 | class File : public Kernel::Session { | ||
| 90 | public: | ||
| 91 | File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) | ||
| 92 | : path(path), priority(0), backend(std::move(backend)) { | ||
| 93 | } | ||
| 94 | |||
| 95 | std::string GetName() const override { return "Path: " + path.DebugStr(); } | ||
| 96 | |||
| 97 | FileSys::Path path; ///< Path of the file | ||
| 98 | u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means | ||
| 99 | std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface | ||
| 100 | |||
| 101 | ResultVal<bool> SyncRequest() override { | ||
| 102 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 103 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 104 | switch (cmd) { | ||
| 105 | 82 | ||
| 106 | // Read from file... | 83 | // Read from file... |
| 107 | case FileCommand::Read: | 84 | case FileCommand::Read: |
| 108 | { | 85 | { |
| 109 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | 86 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2]) << 32; |
| 110 | u32 length = cmd_buff[3]; | 87 | u32 length = cmd_buff[3]; |
| 111 | u32 address = cmd_buff[5]; | 88 | u32 address = cmd_buff[5]; |
| 112 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | 89 | LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", |
| 113 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | 90 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); |
| 114 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | 91 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); |
| 115 | break; | 92 | break; |
| 116 | } | 93 | } |
| @@ -118,12 +95,12 @@ public: | |||
| 118 | // Write to file... | 95 | // Write to file... |
| 119 | case FileCommand::Write: | 96 | case FileCommand::Write: |
| 120 | { | 97 | { |
| 121 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | 98 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2]) << 32; |
| 122 | u32 length = cmd_buff[3]; | 99 | u32 length = cmd_buff[3]; |
| 123 | u32 flush = cmd_buff[4]; | 100 | u32 flush = cmd_buff[4]; |
| 124 | u32 address = cmd_buff[6]; | 101 | u32 address = cmd_buff[6]; |
| 125 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | 102 | LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", |
| 126 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | 103 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); |
| 127 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | 104 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); |
| 128 | break; | 105 | break; |
| 129 | } | 106 | } |
| @@ -141,7 +118,7 @@ public: | |||
| 141 | { | 118 | { |
| 142 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | 119 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); |
| 143 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", | 120 | LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", |
| 144 | GetTypeName().c_str(), GetName().c_str(), size); | 121 | GetTypeName().c_str(), GetName().c_str(), size); |
| 145 | backend->SetSize(size); | 122 | backend->SetSize(size); |
| 146 | break; | 123 | break; |
| 147 | } | 124 | } |
| @@ -187,27 +164,15 @@ public: | |||
| 187 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | 164 | ResultCode error = UnimplementedFunction(ErrorModule::FS); |
| 188 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | 165 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. |
| 189 | return error; | 166 | return error; |
| 190 | } | ||
| 191 | cmd_buff[1] = 0; // No error | ||
| 192 | return MakeResult<bool>(false); | ||
| 193 | } | 167 | } |
| 194 | }; | 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 195 | 169 | return MakeResult<bool>(false); | |
| 196 | class Directory : public Kernel::Session { | 170 | } |
| 197 | public: | ||
| 198 | Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) | ||
| 199 | : path(path), backend(std::move(backend)) { | ||
| 200 | } | ||
| 201 | |||
| 202 | std::string GetName() const override { return "Directory: " + path.DebugStr(); } | ||
| 203 | |||
| 204 | FileSys::Path path; ///< Path of the directory | ||
| 205 | std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface | ||
| 206 | 171 | ||
| 207 | ResultVal<bool> SyncRequest() override { | 172 | ResultVal<bool> Directory::SyncRequest() { |
| 208 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 173 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 209 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | 174 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); |
| 210 | switch (cmd) { | 175 | switch (cmd) { |
| 211 | 176 | ||
| 212 | // Read from directory... | 177 | // Read from directory... |
| 213 | case DirectoryCommand::Read: | 178 | case DirectoryCommand::Read: |
| @@ -216,7 +181,7 @@ public: | |||
| 216 | u32 address = cmd_buff[3]; | 181 | u32 address = cmd_buff[3]; |
| 217 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | 182 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); |
| 218 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", | 183 | LOG_TRACE(Service_FS, "Read %s %s: count=%d", |
| 219 | GetTypeName().c_str(), GetName().c_str(), count); | 184 | GetTypeName().c_str(), GetName().c_str(), count); |
| 220 | 185 | ||
| 221 | // Number of entries actually read | 186 | // Number of entries actually read |
| 222 | cmd_buff[2] = backend->Read(count, entries); | 187 | cmd_buff[2] = backend->Read(count, entries); |
| @@ -236,29 +201,31 @@ public: | |||
| 236 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | 201 | ResultCode error = UnimplementedFunction(ErrorModule::FS); |
| 237 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | 202 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. |
| 238 | return MakeResult<bool>(false); | 203 | return MakeResult<bool>(false); |
| 239 | } | ||
| 240 | cmd_buff[1] = 0; // No error | ||
| 241 | return MakeResult<bool>(false); | ||
| 242 | } | 204 | } |
| 243 | }; | 205 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 206 | return MakeResult<bool>(false); | ||
| 207 | } | ||
| 244 | 208 | ||
| 245 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 209 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 246 | 210 | ||
| 211 | using FileSys::ArchiveBackend; | ||
| 212 | using FileSys::ArchiveFactory; | ||
| 213 | |||
| 247 | /** | 214 | /** |
| 248 | * Map of registered archives, identified by id code. Once an archive is registered here, it is | 215 | * Map of registered archives, identified by id code. Once an archive is registered here, it is |
| 249 | * never removed until the FS service is shut down. | 216 | * never removed until the FS service is shut down. |
| 250 | */ | 217 | */ |
| 251 | static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; | 218 | static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; |
| 252 | 219 | ||
| 253 | /** | 220 | /** |
| 254 | * Map of active archive handles. Values are pointers to the archives in `idcode_map`. | 221 | * Map of active archive handles. Values are pointers to the archives in `idcode_map`. |
| 255 | */ | 222 | */ |
| 256 | static std::unordered_map<ArchiveHandle, Archive*> handle_map; | 223 | static std::unordered_map<ArchiveHandle, std::unique_ptr<ArchiveBackend>> handle_map; |
| 257 | static ArchiveHandle next_handle; | 224 | static ArchiveHandle next_handle; |
| 258 | 225 | ||
| 259 | static Archive* GetArchive(ArchiveHandle handle) { | 226 | static ArchiveBackend* GetArchive(ArchiveHandle handle) { |
| 260 | auto itr = handle_map.find(handle); | 227 | auto itr = handle_map.find(handle); |
| 261 | return (itr == handle_map.end()) ? nullptr : itr->second; | 228 | return (itr == handle_map.end()) ? nullptr : itr->second.get(); |
| 262 | } | 229 | } |
| 263 | 230 | ||
| 264 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archive_path) { | 231 | ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archive_path) { |
| @@ -271,15 +238,13 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi | |||
| 271 | ErrorSummary::NotFound, ErrorLevel::Permanent); | 238 | ErrorSummary::NotFound, ErrorLevel::Permanent); |
| 272 | } | 239 | } |
| 273 | 240 | ||
| 274 | ResultCode res = itr->second->backend->Open(archive_path); | 241 | CASCADE_RESULT(std::unique_ptr<ArchiveBackend> res, itr->second->Open(archive_path)); |
| 275 | if (!res.IsSuccess()) | ||
| 276 | return res; | ||
| 277 | 242 | ||
| 278 | // This should never even happen in the first place with 64-bit handles, | 243 | // This should never even happen in the first place with 64-bit handles, |
| 279 | while (handle_map.count(next_handle) != 0) { | 244 | while (handle_map.count(next_handle) != 0) { |
| 280 | ++next_handle; | 245 | ++next_handle; |
| 281 | } | 246 | } |
| 282 | handle_map.emplace(next_handle, itr->second.get()); | 247 | handle_map.emplace(next_handle, std::move(res)); |
| 283 | return MakeResult<ArchiveHandle>(next_handle++); | 248 | return MakeResult<ArchiveHandle>(next_handle++); |
| 284 | } | 249 | } |
| 285 | 250 | ||
| @@ -292,39 +257,39 @@ ResultCode CloseArchive(ArchiveHandle handle) { | |||
| 292 | 257 | ||
| 293 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in | 258 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in |
| 294 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 | 259 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 |
| 295 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { | 260 | ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factory, ArchiveIdCode id_code) { |
| 296 | auto result = id_code_map.emplace(id_code, Common::make_unique<Archive>(std::move(backend), id_code)); | 261 | auto result = id_code_map.emplace(id_code, std::move(factory)); |
| 297 | 262 | ||
| 298 | bool inserted = result.second; | 263 | bool inserted = result.second; |
| 299 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); | 264 | _assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); |
| 300 | 265 | ||
| 301 | auto& archive = result.first->second; | 266 | auto& archive = result.first->second; |
| 302 | LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); | 267 | LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code); |
| 303 | return RESULT_SUCCESS; | 268 | return RESULT_SUCCESS; |
| 304 | } | 269 | } |
| 305 | 270 | ||
| 306 | ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, | 271 | ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, |
| 307 | const FileSys::Path& path, const FileSys::Mode mode) { | 272 | const FileSys::Path& path, const FileSys::Mode mode) { |
| 308 | Archive* archive = GetArchive(archive_handle); | 273 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 309 | if (archive == nullptr) | 274 | if (archive == nullptr) |
| 310 | return ERR_INVALID_HANDLE; | 275 | return ERR_INVALID_HANDLE; |
| 311 | 276 | ||
| 312 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | 277 | std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode); |
| 313 | if (backend == nullptr) { | 278 | if (backend == nullptr) { |
| 314 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | 279 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, |
| 315 | ErrorSummary::NotFound, ErrorLevel::Status); | 280 | ErrorSummary::NotFound, ErrorLevel::Status); |
| 316 | } | 281 | } |
| 317 | 282 | ||
| 318 | auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path)); | 283 | auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path)); |
| 319 | return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(file)); | 284 | return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); |
| 320 | } | 285 | } |
| 321 | 286 | ||
| 322 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | 287 | ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { |
| 323 | Archive* archive = GetArchive(archive_handle); | 288 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 324 | if (archive == nullptr) | 289 | if (archive == nullptr) |
| 325 | return ERR_INVALID_HANDLE; | 290 | return ERR_INVALID_HANDLE; |
| 326 | 291 | ||
| 327 | if (archive->backend->DeleteFile(path)) | 292 | if (archive->DeleteFile(path)) |
| 328 | return RESULT_SUCCESS; | 293 | return RESULT_SUCCESS; |
| 329 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | 294 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description |
| 330 | ErrorSummary::Canceled, ErrorLevel::Status); | 295 | ErrorSummary::Canceled, ErrorLevel::Status); |
| @@ -332,13 +297,13 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa | |||
| 332 | 297 | ||
| 333 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | 298 | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, |
| 334 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | 299 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { |
| 335 | Archive* src_archive = GetArchive(src_archive_handle); | 300 | ArchiveBackend* src_archive = GetArchive(src_archive_handle); |
| 336 | Archive* dest_archive = GetArchive(dest_archive_handle); | 301 | ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); |
| 337 | if (src_archive == nullptr || dest_archive == nullptr) | 302 | if (src_archive == nullptr || dest_archive == nullptr) |
| 338 | return ERR_INVALID_HANDLE; | 303 | return ERR_INVALID_HANDLE; |
| 339 | 304 | ||
| 340 | if (src_archive == dest_archive) { | 305 | if (src_archive == dest_archive) { |
| 341 | if (src_archive->backend->RenameFile(src_path, dest_path)) | 306 | if (src_archive->RenameFile(src_path, dest_path)) |
| 342 | return RESULT_SUCCESS; | 307 | return RESULT_SUCCESS; |
| 343 | } else { | 308 | } else { |
| 344 | // TODO: Implement renaming across archives | 309 | // TODO: Implement renaming across archives |
| @@ -352,30 +317,30 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil | |||
| 352 | } | 317 | } |
| 353 | 318 | ||
| 354 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | 319 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { |
| 355 | Archive* archive = GetArchive(archive_handle); | 320 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 356 | if (archive == nullptr) | 321 | if (archive == nullptr) |
| 357 | return ERR_INVALID_HANDLE; | 322 | return ERR_INVALID_HANDLE; |
| 358 | 323 | ||
| 359 | if (archive->backend->DeleteDirectory(path)) | 324 | if (archive->DeleteDirectory(path)) |
| 360 | return RESULT_SUCCESS; | 325 | return RESULT_SUCCESS; |
| 361 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | 326 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description |
| 362 | ErrorSummary::Canceled, ErrorLevel::Status); | 327 | ErrorSummary::Canceled, ErrorLevel::Status); |
| 363 | } | 328 | } |
| 364 | 329 | ||
| 365 | ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { | 330 | ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { |
| 366 | Archive* archive = GetArchive(archive_handle); | 331 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 367 | if (archive == nullptr) | 332 | if (archive == nullptr) |
| 368 | return ERR_INVALID_HANDLE; | 333 | return ERR_INVALID_HANDLE; |
| 369 | 334 | ||
| 370 | return archive->backend->CreateFile(path, file_size); | 335 | return archive->CreateFile(path, file_size); |
| 371 | } | 336 | } |
| 372 | 337 | ||
| 373 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | 338 | ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { |
| 374 | Archive* archive = GetArchive(archive_handle); | 339 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 375 | if (archive == nullptr) | 340 | if (archive == nullptr) |
| 376 | return ERR_INVALID_HANDLE; | 341 | return ERR_INVALID_HANDLE; |
| 377 | 342 | ||
| 378 | if (archive->backend->CreateDirectory(path)) | 343 | if (archive->CreateDirectory(path)) |
| 379 | return RESULT_SUCCESS; | 344 | return RESULT_SUCCESS; |
| 380 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description | 345 | return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description |
| 381 | ErrorSummary::Canceled, ErrorLevel::Status); | 346 | ErrorSummary::Canceled, ErrorLevel::Status); |
| @@ -383,13 +348,13 @@ ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy | |||
| 383 | 348 | ||
| 384 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | 349 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, |
| 385 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { | 350 | ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) { |
| 386 | Archive* src_archive = GetArchive(src_archive_handle); | 351 | ArchiveBackend* src_archive = GetArchive(src_archive_handle); |
| 387 | Archive* dest_archive = GetArchive(dest_archive_handle); | 352 | ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); |
| 388 | if (src_archive == nullptr || dest_archive == nullptr) | 353 | if (src_archive == nullptr || dest_archive == nullptr) |
| 389 | return ERR_INVALID_HANDLE; | 354 | return ERR_INVALID_HANDLE; |
| 390 | 355 | ||
| 391 | if (src_archive == dest_archive) { | 356 | if (src_archive == dest_archive) { |
| 392 | if (src_archive->backend->RenameDirectory(src_path, dest_path)) | 357 | if (src_archive->RenameDirectory(src_path, dest_path)) |
| 393 | return RESULT_SUCCESS; | 358 | return RESULT_SUCCESS; |
| 394 | } else { | 359 | } else { |
| 395 | // TODO: Implement renaming across archives | 360 | // TODO: Implement renaming across archives |
| @@ -402,31 +367,29 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons | |||
| 402 | ErrorSummary::NothingHappened, ErrorLevel::Status); | 367 | ErrorSummary::NothingHappened, ErrorLevel::Status); |
| 403 | } | 368 | } |
| 404 | 369 | ||
| 405 | ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | 370 | ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, |
| 406 | const FileSys::Path& path) { | 371 | const FileSys::Path& path) { |
| 407 | Archive* archive = GetArchive(archive_handle); | 372 | ArchiveBackend* archive = GetArchive(archive_handle); |
| 408 | if (archive == nullptr) | 373 | if (archive == nullptr) |
| 409 | return ERR_INVALID_HANDLE; | 374 | return ERR_INVALID_HANDLE; |
| 410 | 375 | ||
| 411 | std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); | 376 | std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); |
| 412 | if (backend == nullptr) { | 377 | if (backend == nullptr) { |
| 413 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | 378 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, |
| 414 | ErrorSummary::NotFound, ErrorLevel::Permanent); | 379 | ErrorSummary::NotFound, ErrorLevel::Permanent); |
| 415 | } | 380 | } |
| 416 | 381 | ||
| 417 | auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path)); | 382 | auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path)); |
| 418 | return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(directory)); | 383 | return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); |
| 419 | } | 384 | } |
| 420 | 385 | ||
| 421 | ResultCode FormatSaveData() { | 386 | ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) { |
| 422 | // Do not create the archive again if it already exists | 387 | auto archive_itr = id_code_map.find(id_code); |
| 423 | auto archive_itr = id_code_map.find(ArchiveIdCode::SaveData); | ||
| 424 | if (archive_itr == id_code_map.end()) { | 388 | if (archive_itr == id_code_map.end()) { |
| 425 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error | 389 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error |
| 426 | } | 390 | } |
| 427 | 391 | ||
| 428 | // Use an empty path, we do not use it when formatting the savedata | 392 | return archive_itr->second->Format(path); |
| 429 | return archive_itr->second->backend->Format(FileSys::Path()); | ||
| 430 | } | 393 | } |
| 431 | 394 | ||
| 432 | ResultCode CreateExtSaveData(u32 high, u32 low) { | 395 | ResultCode CreateExtSaveData(u32 high, u32 low) { |
| @@ -460,32 +423,32 @@ void ArchiveInit() { | |||
| 460 | 423 | ||
| 461 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 424 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); |
| 462 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | 425 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); |
| 463 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | 426 | auto sdmc_factory = Common::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory); |
| 464 | if (sdmc_archive->Initialize()) | 427 | if (sdmc_factory->Initialize()) |
| 465 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | 428 | RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC); |
| 466 | else | 429 | else |
| 467 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | 430 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); |
| 468 | 431 | ||
| 469 | // Create the SaveData archive | 432 | // Create the SaveData archive |
| 470 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(sdmc_directory); | 433 | auto savedata_factory = Common::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); |
| 471 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | 434 | RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); |
| 472 | 435 | ||
| 473 | auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sdmc_directory, false); | 436 | auto extsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false); |
| 474 | if (extsavedata_archive->Initialize()) | 437 | if (extsavedata_factory->Initialize()) |
| 475 | CreateArchive(std::move(extsavedata_archive), ArchiveIdCode::ExtSaveData); | 438 | RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData); |
| 476 | else | 439 | else |
| 477 | LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_archive->GetMountPoint().c_str()); | 440 | LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str()); |
| 478 | 441 | ||
| 479 | auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true); | 442 | auto sharedextsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true); |
| 480 | if (sharedextsavedata_archive->Initialize()) | 443 | if (sharedextsavedata_factory->Initialize()) |
| 481 | CreateArchive(std::move(sharedextsavedata_archive), ArchiveIdCode::SharedExtSaveData); | 444 | RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData); |
| 482 | else | 445 | else |
| 483 | LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", | 446 | LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", |
| 484 | sharedextsavedata_archive->GetMountPoint().c_str()); | 447 | sharedextsavedata_factory->GetMountPoint().c_str()); |
| 485 | 448 | ||
| 486 | // Create the SaveDataCheck archive, basically a small variation of the RomFS archive | 449 | // Create the SaveDataCheck archive, basically a small variation of the RomFS archive |
| 487 | auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(nand_directory); | 450 | auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); |
| 488 | CreateArchive(std::move(savedatacheck_archive), ArchiveIdCode::SaveDataCheck); | 451 | RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); |
| 489 | } | 452 | } |
| 490 | 453 | ||
| 491 | /// Shutdown archives | 454 | /// Shutdown archives |
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index ab5ea4da8..c490327d0 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "core/file_sys/archive_backend.h" | 9 | #include "core/file_sys/archive_backend.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/session.h" | ||
| 11 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 12 | 13 | ||
| 13 | /// The unique system identifier hash, also known as ID0 | 14 | /// The unique system identifier hash, also known as ID0 |
| @@ -36,6 +37,35 @@ enum class ArchiveIdCode : u32 { | |||
| 36 | 37 | ||
| 37 | typedef u64 ArchiveHandle; | 38 | typedef u64 ArchiveHandle; |
| 38 | 39 | ||
| 40 | class File : public Kernel::Session { | ||
| 41 | public: | ||
| 42 | File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) | ||
| 43 | : path(path), priority(0), backend(std::move(backend)) { | ||
| 44 | } | ||
| 45 | |||
| 46 | std::string GetName() const override { return "Path: " + path.DebugStr(); } | ||
| 47 | |||
| 48 | FileSys::Path path; ///< Path of the file | ||
| 49 | u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means | ||
| 50 | std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface | ||
| 51 | |||
| 52 | ResultVal<bool> SyncRequest() override; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class Directory : public Kernel::Session { | ||
| 56 | public: | ||
| 57 | Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path) | ||
| 58 | : path(path), backend(std::move(backend)) { | ||
| 59 | } | ||
| 60 | |||
| 61 | std::string GetName() const override { return "Directory: " + path.DebugStr(); } | ||
| 62 | |||
| 63 | FileSys::Path path; ///< Path of the directory | ||
| 64 | std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface | ||
| 65 | |||
| 66 | ResultVal<bool> SyncRequest() override; | ||
| 67 | }; | ||
| 68 | |||
| 39 | /** | 69 | /** |
| 40 | * Opens an archive | 70 | * Opens an archive |
| 41 | * @param id_code IdCode of the archive to open | 71 | * @param id_code IdCode of the archive to open |
| @@ -51,11 +81,11 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi | |||
| 51 | ResultCode CloseArchive(ArchiveHandle handle); | 81 | ResultCode CloseArchive(ArchiveHandle handle); |
| 52 | 82 | ||
| 53 | /** | 83 | /** |
| 54 | * Creates an Archive | 84 | * Registers an Archive type, instances of which can later be opened using its IdCode. |
| 55 | * @param backend File system backend interface to the archive | 85 | * @param backend File system backend interface to the archive |
| 56 | * @param id_code Id code used to access this type of archive | 86 | * @param id_code Id code used to access this type of archive |
| 57 | */ | 87 | */ |
| 58 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); | 88 | ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factory, ArchiveIdCode id_code); |
| 59 | 89 | ||
| 60 | /** | 90 | /** |
| 61 | * Open a File from an Archive | 91 | * Open a File from an Archive |
| @@ -64,7 +94,7 @@ ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, Arc | |||
| 64 | * @param mode Mode under which to open the File | 94 | * @param mode Mode under which to open the File |
| 65 | * @return The opened File object as a Session | 95 | * @return The opened File object as a Session |
| 66 | */ | 96 | */ |
| 67 | ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, | 97 | ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, |
| 68 | const FileSys::Path& path, const FileSys::Mode mode); | 98 | const FileSys::Path& path, const FileSys::Mode mode); |
| 69 | 99 | ||
| 70 | /** | 100 | /** |
| @@ -128,14 +158,17 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons | |||
| 128 | * @param path Path to the Directory inside of the Archive | 158 | * @param path Path to the Directory inside of the Archive |
| 129 | * @return The opened Directory object as a Session | 159 | * @return The opened Directory object as a Session |
| 130 | */ | 160 | */ |
| 131 | ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | 161 | ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, |
| 132 | const FileSys::Path& path); | 162 | const FileSys::Path& path); |
| 133 | 163 | ||
| 134 | /** | 164 | /** |
| 135 | * Creates a blank SaveData archive. | 165 | * Erases the contents of the physical folder that contains the archive |
| 166 | * identified by the specified id code and path | ||
| 167 | * @param id_code The id of the archive to format | ||
| 168 | * @param path The path to the archive, if relevant. | ||
| 136 | * @return ResultCode 0 on success or the corresponding code on error | 169 | * @return ResultCode 0 on success or the corresponding code on error |
| 137 | */ | 170 | */ |
| 138 | ResultCode FormatSaveData(); | 171 | ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = FileSys::Path()); |
| 139 | 172 | ||
| 140 | /** | 173 | /** |
| 141 | * Creates a blank SharedExtSaveData archive for the specified extdata ID | 174 | * Creates a blank SharedExtSaveData archive for the specified extdata ID |
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 94a3a31c8..71ee4ff55 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -61,7 +61,7 @@ static void OpenFile(Service::Interface* self) { | |||
| 61 | 61 | ||
| 62 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); | 62 | LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); |
| 63 | 63 | ||
| 64 | ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); | 64 | ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); |
| 65 | cmd_buff[1] = file_res.Code().raw; | 65 | cmd_buff[1] = file_res.Code().raw; |
| 66 | if (file_res.Succeeded()) { | 66 | if (file_res.Succeeded()) { |
| 67 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); | 67 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); |
| @@ -117,7 +117,7 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 117 | } | 117 | } |
| 118 | SCOPE_EXIT({ CloseArchive(*archive_handle); }); | 118 | SCOPE_EXIT({ CloseArchive(*archive_handle); }); |
| 119 | 119 | ||
| 120 | ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode); | 120 | ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode); |
| 121 | cmd_buff[1] = file_res.Code().raw; | 121 | cmd_buff[1] = file_res.Code().raw; |
| 122 | if (file_res.Succeeded()) { | 122 | if (file_res.Succeeded()) { |
| 123 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); | 123 | cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); |
| @@ -337,7 +337,7 @@ static void OpenDirectory(Service::Interface* self) { | |||
| 337 | 337 | ||
| 338 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); | 338 | LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); |
| 339 | 339 | ||
| 340 | ResultVal<SharedPtr<Session>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path); | 340 | ResultVal<SharedPtr<Directory>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path); |
| 341 | cmd_buff[1] = dir_res.Code().raw; | 341 | cmd_buff[1] = dir_res.Code().raw; |
| 342 | if (dir_res.Succeeded()) { | 342 | if (dir_res.Succeeded()) { |
| 343 | cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); | 343 | cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); |
| @@ -468,7 +468,7 @@ static void FormatSaveData(Service::Interface* self) { | |||
| 468 | return; | 468 | return; |
| 469 | } | 469 | } |
| 470 | 470 | ||
| 471 | cmd_buff[1] = FormatSaveData().raw; | 471 | cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; |
| 472 | } | 472 | } |
| 473 | 473 | ||
| 474 | /** | 474 | /** |
| @@ -484,7 +484,7 @@ static void FormatThisUserSaveData(Service::Interface* self) { | |||
| 484 | 484 | ||
| 485 | // TODO(Subv): Find out what the inputs and outputs of this function are | 485 | // TODO(Subv): Find out what the inputs and outputs of this function are |
| 486 | 486 | ||
| 487 | cmd_buff[1] = FormatSaveData().raw; | 487 | cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; |
| 488 | } | 488 | } |
| 489 | 489 | ||
| 490 | static void CreateExtSaveData(Service::Interface* self) { | 490 | static void CreateExtSaveData(Service::Interface* self) { |
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp index 459717fff..ea96f64af 100644 --- a/src/core/hle/service/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro.cpp | |||
| @@ -11,9 +11,69 @@ | |||
| 11 | 11 | ||
| 12 | namespace LDR_RO { | 12 | namespace LDR_RO { |
| 13 | 13 | ||
| 14 | /** | ||
| 15 | * LDR_RO::Initialize service function | ||
| 16 | * Inputs: | ||
| 17 | * 1 : CRS buffer pointer | ||
| 18 | * 2 : CRS Size | ||
| 19 | * 3 : Process memory address where the CRS will be mapped | ||
| 20 | * 4 : Value, must be zero | ||
| 21 | * 5 : KProcess handle | ||
| 22 | * Outputs: | ||
| 23 | * 0 : Return header | ||
| 24 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 25 | */ | ||
| 26 | static void Initialize(Service::Interface* self) { | ||
| 27 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 28 | u32 crs_buffer_ptr = cmd_buff[1]; | ||
| 29 | u32 crs_size = cmd_buff[2]; | ||
| 30 | u32 address = cmd_buff[3]; | ||
| 31 | u32 value = cmd_buff[4]; | ||
| 32 | u32 process = cmd_buff[5]; | ||
| 33 | |||
| 34 | if (value != 0) { | ||
| 35 | LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); | ||
| 36 | } | ||
| 37 | |||
| 38 | // TODO(purpasmart96): Verify return header on HW | ||
| 39 | |||
| 40 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 41 | |||
| 42 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * LDR_RO::LoadCRR service function | ||
| 47 | * Inputs: | ||
| 48 | * 1 : CRS buffer pointer | ||
| 49 | * 2 : CRS Size | ||
| 50 | * 3 : Value, must be zero | ||
| 51 | * 4 : KProcess handle | ||
| 52 | * Outputs: | ||
| 53 | * 0 : Return header | ||
| 54 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 55 | */ | ||
| 56 | static void LoadCRR(Service::Interface* self) { | ||
| 57 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 58 | u32 crs_buffer_ptr = cmd_buff[1]; | ||
| 59 | u32 crs_size = cmd_buff[2]; | ||
| 60 | u32 value = cmd_buff[3]; | ||
| 61 | u32 process = cmd_buff[4]; | ||
| 62 | |||
| 63 | if (value != 0) { | ||
| 64 | LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); | ||
| 65 | } | ||
| 66 | |||
| 67 | // TODO(purpasmart96): Verify return header on HW | ||
| 68 | |||
| 69 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 70 | |||
| 71 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 72 | } | ||
| 73 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 74 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x000100C2, nullptr, "Initialize"}, | 75 | {0x000100C2, Initialize, "Initialize"}, |
| 16 | {0x00020082, nullptr, "LoadCRR"}, | 76 | {0x00020082, LoadCRR, "LoadCRR"}, |
| 17 | {0x00030042, nullptr, "UnloadCCR"}, | 77 | {0x00030042, nullptr, "UnloadCCR"}, |
| 18 | {0x000402C2, nullptr, "LoadExeCRO"}, | 78 | {0x000402C2, nullptr, "LoadExeCRO"}, |
| 19 | {0x000500C2, nullptr, "LoadCROSymbols"}, | 79 | {0x000500C2, nullptr, "LoadCROSymbols"}, |
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index 7b465a348..7c8d9ce8c 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp | |||
| @@ -4,8 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| 6 | #include "common/make_unique.h" | 6 | #include "common/make_unique.h" |
| 7 | #include "core/file_sys/archive_extsavedata.h" | 7 | |
| 8 | #include "core/hle/hle.h" | 8 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/service/fs/archive.h" | ||
| 9 | #include "core/hle/service/ptm_u.h" | 10 | #include "core/hle/service/ptm_u.h" |
| 10 | 11 | ||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -28,7 +29,6 @@ struct GameCoin { | |||
| 28 | u8 day; | 29 | u8 day; |
| 29 | }; | 30 | }; |
| 30 | static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; | 31 | static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; |
| 31 | static std::unique_ptr<FileSys::Archive_ExtSaveData> ptm_shared_extsavedata; | ||
| 32 | static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; | 32 | static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; |
| 33 | 33 | ||
| 34 | /// Charge levels used by PTM functions | 34 | /// Charge levels used by PTM functions |
| @@ -138,31 +138,28 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 138 | 138 | ||
| 139 | Interface::Interface() { | 139 | Interface::Interface() { |
| 140 | Register(FunctionTable); | 140 | Register(FunctionTable); |
| 141 | // Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file | 141 | |
| 142 | // TODO(Subv): In the future we should use the FS service to query this archive | 142 | // Open the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file |
| 143 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||
| 144 | ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true); | ||
| 145 | if (!ptm_shared_extsavedata->Initialize()) { | ||
| 146 | LOG_CRITICAL(Service_PTM, "Could not initialize SharedExtSaveData archive for the PTM:U service"); | ||
| 147 | return; | ||
| 148 | } | ||
| 149 | FileSys::Path archive_path(ptm_shared_extdata_id); | 143 | FileSys::Path archive_path(ptm_shared_extdata_id); |
| 150 | ResultCode result = ptm_shared_extsavedata->Open(archive_path); | 144 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); |
| 151 | // If the archive didn't exist, create the files inside | 145 | // If the archive didn't exist, create the files inside |
| 152 | if (result.description == ErrorDescription::FS_NotFormatted) { | 146 | if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { |
| 153 | // Format the archive to clear the directories | 147 | // Format the archive to create the directories |
| 154 | ptm_shared_extsavedata->Format(archive_path); | 148 | Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); |
| 155 | // Open it again to get a valid archive now that the folder exists | 149 | // Open it again to get a valid archive now that the folder exists |
| 156 | ptm_shared_extsavedata->Open(archive_path); | 150 | archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); |
| 151 | _assert_msg_(Service_PTM, archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); | ||
| 152 | |||
| 157 | FileSys::Path gamecoin_path("gamecoin.dat"); | 153 | FileSys::Path gamecoin_path("gamecoin.dat"); |
| 158 | FileSys::Mode open_mode = {}; | 154 | FileSys::Mode open_mode = {}; |
| 159 | open_mode.write_flag = 1; | 155 | open_mode.write_flag = 1; |
| 160 | open_mode.create_flag = 1; | 156 | open_mode.create_flag = 1; |
| 161 | // Open the file and write the default gamecoin information | 157 | // Open the file and write the default gamecoin information |
| 162 | auto gamecoin = ptm_shared_extsavedata->OpenFile(gamecoin_path, open_mode); | 158 | auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); |
| 163 | if (gamecoin != nullptr) { | 159 | if (gamecoin_result.Succeeded()) { |
| 164 | gamecoin->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin)); | 160 | auto gamecoin = gamecoin_result.MoveFrom(); |
| 165 | gamecoin->Close(); | 161 | gamecoin->backend->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin)); |
| 162 | gamecoin->backend->Close(); | ||
| 166 | } | 163 | } |
| 167 | } | 164 | } |
| 168 | } | 165 | } |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c4866fcce..96da29923 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -150,7 +150,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 150 | if (object->ShouldWait()) { | 150 | if (object->ShouldWait()) { |
| 151 | 151 | ||
| 152 | object->AddWaitingThread(Kernel::GetCurrentThread()); | 152 | object->AddWaitingThread(Kernel::GetCurrentThread()); |
| 153 | Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); | 153 | Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false); |
| 154 | 154 | ||
| 155 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 155 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 156 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 156 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); |
| @@ -212,7 +212,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 212 | // NOTE: This should deadlock the current thread if no timeout was specified | 212 | // NOTE: This should deadlock the current thread if no timeout was specified |
| 213 | if (!wait_all) { | 213 | if (!wait_all) { |
| 214 | wait_thread = true; | 214 | wait_thread = true; |
| 215 | Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); | ||
| 216 | } | 215 | } |
| 217 | } | 216 | } |
| 218 | 217 | ||
| @@ -222,12 +221,17 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 222 | if (wait_thread) { | 221 | if (wait_thread) { |
| 223 | 222 | ||
| 224 | // Actually wait the current thread on each object if we decided to wait... | 223 | // Actually wait the current thread on each object if we decided to wait... |
| 224 | std::vector<SharedPtr<Kernel::WaitObject>> wait_objects; | ||
| 225 | wait_objects.reserve(handle_count); | ||
| 226 | |||
| 225 | for (int i = 0; i < handle_count; ++i) { | 227 | for (int i = 0; i < handle_count; ++i) { |
| 226 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | 228 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); |
| 227 | object->AddWaitingThread(Kernel::GetCurrentThread()); | 229 | object->AddWaitingThread(Kernel::GetCurrentThread()); |
| 228 | Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); | 230 | wait_objects.push_back(object); |
| 229 | } | 231 | } |
| 230 | 232 | ||
| 233 | Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all); | ||
| 234 | |||
| 231 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 235 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 232 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 236 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); |
| 233 | 237 | ||
| @@ -319,7 +323,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u | |||
| 319 | } | 323 | } |
| 320 | 324 | ||
| 321 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( | 325 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( |
| 322 | name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE)); | 326 | name, entry_point, priority, arg, processor_id, stack_top)); |
| 323 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 327 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 324 | 328 | ||
| 325 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 329 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| @@ -338,7 +342,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u | |||
| 338 | static void ExitThread() { | 342 | static void ExitThread() { |
| 339 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); | 343 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); |
| 340 | 344 | ||
| 341 | Kernel::GetCurrentThread()->Stop(__func__); | 345 | Kernel::GetCurrentThread()->Stop(); |
| 342 | HLE::Reschedule(__func__); | 346 | HLE::Reschedule(__func__); |
| 343 | } | 347 | } |
| 344 | 348 | ||
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 52730a7b4..94dcc50f9 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -127,7 +127,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 127 | // Load application and RomFS | 127 | // Load application and RomFS |
| 128 | if (ResultStatus::Success == app_loader.Load()) { | 128 | if (ResultStatus::Success == app_loader.Load()) { |
| 129 | Kernel::g_program_id = app_loader.GetProgramId(); | 129 | Kernel::g_program_id = app_loader.GetProgramId(); |
| 130 | Service::FS::CreateArchive(Common::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | 130 | Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); |
| 131 | return ResultStatus::Success; | 131 | return ResultStatus::Success; |
| 132 | } | 132 | } |
| 133 | break; | 133 | break; |
diff --git a/src/core/settings.h b/src/core/settings.h index cedba3a98..e62dd4358 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -35,6 +35,9 @@ struct Values { | |||
| 35 | // Data Storage | 35 | // Data Storage |
| 36 | bool use_virtual_sd; | 36 | bool use_virtual_sd; |
| 37 | 37 | ||
| 38 | // System Region | ||
| 39 | int region_value; | ||
| 40 | |||
| 38 | std::string log_filter; | 41 | std::string log_filter; |
| 39 | } extern values; | 42 | } extern values; |
| 40 | 43 | ||
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 1566b890d..9c1a12dc8 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -338,7 +338,11 @@ struct Regs { | |||
| 338 | 338 | ||
| 339 | union { | 339 | union { |
| 340 | enum BlendEquation : u32 { | 340 | enum BlendEquation : u32 { |
| 341 | Add = 0, | 341 | Add = 0, |
| 342 | Subtract = 1, | ||
| 343 | ReverseSubtract = 2, | ||
| 344 | Min = 3, | ||
| 345 | Max = 4 | ||
| 342 | }; | 346 | }; |
| 343 | 347 | ||
| 344 | enum BlendFactor : u32 { | 348 | enum BlendFactor : u32 { |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 5920477eb..06fd8d140 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -616,17 +616,60 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 616 | LookupFactorA(params.factor_source_a)); | 616 | LookupFactorA(params.factor_source_a)); |
| 617 | auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb), | 617 | auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb), |
| 618 | LookupFactorA(params.factor_dest_a)); | 618 | LookupFactorA(params.factor_dest_a)); |
| 619 | |||
| 620 | auto src_result = (combiner_output * srcfactor).Cast<int>(); | ||
| 621 | auto dst_result = (dest * dstfactor).Cast<int>(); | ||
| 619 | 622 | ||
| 620 | switch (params.blend_equation_rgb) { | 623 | switch (params.blend_equation_rgb) { |
| 621 | case params.Add: | 624 | case params.Add: |
| 622 | { | 625 | { |
| 623 | auto result = (combiner_output * srcfactor + dest * dstfactor) / 255; | 626 | auto result = (src_result + dst_result) / 255; |
| 624 | result.r() = std::min(255, result.r()); | 627 | result.r() = std::min(255, result.r()); |
| 625 | result.g() = std::min(255, result.g()); | 628 | result.g() = std::min(255, result.g()); |
| 626 | result.b() = std::min(255, result.b()); | 629 | result.b() = std::min(255, result.b()); |
| 627 | combiner_output = result.Cast<u8>(); | 630 | combiner_output = result.Cast<u8>(); |
| 628 | break; | 631 | break; |
| 629 | } | 632 | } |
| 633 | |||
| 634 | case params.Subtract: | ||
| 635 | { | ||
| 636 | auto result = (src_result - dst_result) / 255; | ||
| 637 | result.r() = std::max(0, result.r()); | ||
| 638 | result.g() = std::max(0, result.g()); | ||
| 639 | result.b() = std::max(0, result.b()); | ||
| 640 | combiner_output = result.Cast<u8>(); | ||
| 641 | break; | ||
| 642 | } | ||
| 643 | |||
| 644 | case params.ReverseSubtract: | ||
| 645 | { | ||
| 646 | auto result = (dst_result - src_result) / 255; | ||
| 647 | result.r() = std::max(0, result.r()); | ||
| 648 | result.g() = std::max(0, result.g()); | ||
| 649 | result.b() = std::max(0, result.b()); | ||
| 650 | combiner_output = result.Cast<u8>(); | ||
| 651 | break; | ||
| 652 | } | ||
| 653 | |||
| 654 | case params.Min: | ||
| 655 | { | ||
| 656 | Math::Vec4<int> result; | ||
| 657 | result.r() = std::min(src_result.r(),dst_result.r()); | ||
| 658 | result.g() = std::min(src_result.g(),dst_result.g()); | ||
| 659 | result.b() = std::min(src_result.b(),dst_result.b()); | ||
| 660 | combiner_output = result.Cast<u8>(); | ||
| 661 | break; | ||
| 662 | } | ||
| 663 | |||
| 664 | case params.Max: | ||
| 665 | { | ||
| 666 | Math::Vec4<int> result; | ||
| 667 | result.r() = std::max(src_result.r(),dst_result.r()); | ||
| 668 | result.g() = std::max(src_result.g(),dst_result.g()); | ||
| 669 | result.b() = std::max(src_result.b(),dst_result.b()); | ||
| 670 | combiner_output = result.Cast<u8>(); | ||
| 671 | break; | ||
| 672 | } | ||
| 630 | 673 | ||
| 631 | default: | 674 | default: |
| 632 | LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", params.blend_equation_rgb.Value()); | 675 | LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", params.blend_equation_rgb.Value()); |