summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h3
-rw-r--r--src/citra_qt/config.cpp8
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/arm/arm_interface.h9
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp10
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h57
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp49
-rw-r--r--src/core/arm/dyncom/arm_dyncom_thumb.cpp14
-rw-r--r--src/core/arm/interpreter/armcopro.cpp7
-rw-r--r--src/core/arm/interpreter/arminit.cpp14
-rw-r--r--src/core/arm/skyeye_common/arm_regformat.h4
-rw-r--r--src/core/arm/skyeye_common/armdefs.h12
-rw-r--r--src/core/arm/skyeye_common/armemu.h14
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp3
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp3
-rw-r--r--src/core/file_sys/archive_backend.h39
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp15
-rw-r--r--src/core/file_sys/archive_extsavedata.h21
-rw-r--r--src/core/file_sys/archive_romfs.cpp17
-rw-r--r--src/core/file_sys/archive_romfs.h11
-rw-r--r--src/core/file_sys/archive_savedata.cpp23
-rw-r--r--src/core/file_sys/archive_savedata.h17
-rw-r--r--src/core/file_sys/archive_savedatacheck.cpp28
-rw-r--r--src/core/file_sys/archive_savedatacheck.h8
-rw-r--r--src/core/file_sys/archive_sdmc.cpp17
-rw-r--r--src/core/file_sys/archive_sdmc.h10
-rw-r--r--src/core/file_sys/disk_archive.cpp26
-rw-r--r--src/core/file_sys/disk_archive.h25
-rw-r--r--src/core/file_sys/ivfc_archive.cpp22
-rw-r--r--src/core/file_sys/ivfc_archive.h13
-rw-r--r--src/core/hle/kernel/kernel.cpp6
-rw-r--r--src/core/hle/kernel/thread.cpp360
-rw-r--r--src/core/hle/kernel/thread.h115
-rw-r--r--src/core/hle/result.h6
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp66
-rw-r--r--src/core/hle/service/dsp_dsp.cpp66
-rw-r--r--src/core/hle/service/fs/archive.cpp191
-rw-r--r--src/core/hle/service/fs/archive.h45
-rw-r--r--src/core/hle/service/fs/fs_user.cpp10
-rw-r--r--src/core/hle/service/ldr_ro.cpp64
-rw-r--r--src/core/hle/service/ptm_u.cpp35
-rw-r--r--src/core/hle/svc.cpp14
-rw-r--r--src/core/loader/loader.cpp2
-rw-r--r--src/core/settings.h3
-rw-r--r--src/video_core/pica.h6
-rw-r--r--src/video_core/rasterizer.cpp45
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]
34use_virtual_sd = 34use_virtual_sd =
35 35
36[System Region]
37region_value = ## 0 : Japan, 1 : Usa (default), 2 : Europe, 3 : Australia, 4 : China, 5 : Korea, 6 : Taiwan.
38
36[Miscellaneous] 39[Miscellaneous]
37log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical 40log_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
96void 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
96void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { 106void 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
14class ARM_DynCom final : virtual public ARM_Interface { 14class ARM_DynCom final : virtual public ARM_Interface {
15public: 15public:
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
91private: 36private:
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.
50unsigned int ARMul_CoProInit(ARMul_State* state) 50void 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\***************************************************************************/
64ARMul_State* ARMul_NewState(ARMul_State* state) 64ARMul_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.
100extern char ARMul_BitList[]; // Number of bits in a byte table. 100extern char ARMul_BitList[]; // Number of bits in a byte table.
101 101
102// Coprocessor support functions. 102// Coprocessor support functions.
103extern unsigned ARMul_CoProInit (ARMul_State *); 103extern void ARMul_CoProInit(ARMul_State*);
104extern void ARMul_CoProExit (ARMul_State *); 104extern void ARMul_CoProExit(ARMul_State*);
105extern void ARMul_CoProAttach (ARMul_State *, unsigned, ARMul_CPInits *, 105extern 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*);
109extern void ARMul_CoProDetach (ARMul_State *, unsigned); 109extern 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
249class ArchiveFactory : NonCopyable {
250public:
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
36Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_location, bool shared) 37ArchiveFactory_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
41bool Archive_ExtSaveData::Initialize() { 42bool 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
50ResultCode Archive_ExtSaveData::Open(const Path& path) { 51ResultVal<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
61ResultCode Archive_ExtSaveData::Format(const Path& path) const { 62ResultCode 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 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the ExtSaveData archive 17/// File system interface to the ExtSaveData archive
18class Archive_ExtSaveData final : public DiskArchive { 18class ArchiveFactory_ExtSaveData final : public ArchiveFactory {
19public: 19public:
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
36protected: 33 const std::string& GetMountPoint() const { return mount_point; }
34
35private:
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
16namespace FileSys { 16namespace FileSys {
17 17
18Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { 18ArchiveFactory_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
26ResultVal<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
31ResultCode 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 @@
17namespace FileSys { 18namespace FileSys {
18 19
19/// File system interface to the RomFS archive 20/// File system interface to the RomFS archive
20class Archive_RomFS final : public IVFCArchive { 21class ArchiveFactory_RomFS final : public ArchiveFactory {
21public: 22public:
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
29private:
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
31Archive_SaveData::Archive_SaveData(const std::string& sdmc_directory) 32ArchiveFactory_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
36ResultCode Archive_SaveData::Open(const Path& path) { 37ResultVal<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
50ResultCode Archive_SaveData::Format(const Path& path) const { 52ResultCode 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 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SaveData archive 17/// File system interface to the SaveData archive
18class Archive_SaveData final : public DiskArchive { 18class ArchiveFactory_SaveData final : public ArchiveFactory {
19public: 19public:
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; 27private:
27 28 std::string mount_point;
28 const std::string& GetMountPoint() const override {
29 return concrete_mount_point;
30 }
31
32protected:
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
24Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) : 25ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory) :
25 mount_point(GetSaveDataCheckContainerPath(nand_directory)) { 26 mount_point(GetSaveDataCheckContainerPath(nand_directory)) {
26} 27}
27 28
28ResultCode Archive_SaveDataCheck::Open(const Path& path) { 29ResultVal<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
47ResultCode 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 @@
17namespace FileSys { 17namespace FileSys {
18 18
19/// File system interface to the SaveDataCheck archive 19/// File system interface to the SaveDataCheck archive
20class Archive_SaveDataCheck final : public IVFCArchive { 20class ArchiveFactory_SaveDataCheck final : public ArchiveFactory {
21public: 21public:
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
27private: 29private:
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
17namespace FileSys { 18namespace FileSys {
18 19
19Archive_SDMC::Archive_SDMC(const std::string& sdmc_directory) : DiskArchive(sdmc_directory) { 20ArchiveFactory_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
23bool Archive_SDMC::Initialize() { 24bool 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
38ResultVal<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
43ResultCode 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 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SDMC archive 17/// File system interface to the SDMC archive
18class Archive_SDMC final : public DiskArchive { 18class ArchiveFactory_SDMC final : public ArchiveFactory {
19public: 19public:
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
33private:
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
19std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { 19std::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
27bool DiskArchive::DeleteFile(const Path& path) const { 27bool 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
31bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 31bool 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
35bool DiskArchive::DeleteDirectory(const Path& path) const { 35bool 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
39ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { 39ResultCode 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
60bool DiskArchive::CreateDirectory(const Path& path) const { 60bool 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
64bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { 64bool 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
68std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { 68std::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
78DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { 78DiskFile::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
87bool DiskFile::Open() { 86bool DiskFile::Open() {
@@ -134,12 +133,11 @@ bool DiskFile::Close() const {
134 133
135//////////////////////////////////////////////////////////////////////////////////////////////////// 134////////////////////////////////////////////////////////////////////////////////////////////////////
136 135
137DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { 136DiskDirectory::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
145bool DiskDirectory::Open() { 143bool 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 {
24public: 24public:
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
50protected: 38protected:
39 friend class DiskFile;
40 friend class DiskDirectory;
41
51 std::string mount_point; 42 std::string mount_point;
52}; 43};
53 44
54class DiskFile : public FileBackend { 45class DiskFile : public FileBackend {
55public: 46public:
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
70protected: 61protected:
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:
77class DiskDirectory : public DirectoryBackend { 67class DiskDirectory : public DirectoryBackend {
78public: 68public:
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
93protected: 83protected:
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
16namespace FileSys { 16namespace FileSys {
17 17
18IVFCArchive::IVFCArchive() { 18IVFCArchive::IVFCArchive(std::shared_ptr<const std::vector<u8>> data) : data(data) {
19}
20
21std::string IVFCArchive::GetName() const {
22 return "IVFC";
19} 23}
20 24
21std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { 25std::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
25bool IVFCArchive::DeleteFile(const Path& path) const { 29bool 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
60ResultCode 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
68size_t IVFCFile::Read(const u64 offset, const u32 length, u8* buffer) const { 66size_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
74size_t IVFCFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { 72size_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
79size_t IVFCFile::GetSize() const { 77size_t IVFCFile::GetSize() const {
80 return sizeof(u8) * archive->raw_data.size(); 78 return sizeof(u8) * data->size();
81} 79}
82 80
83bool IVFCFile::SetSize(const u64 size) const { 81bool 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 */
24class IVFCArchive : public ArchiveBackend { 25class IVFCArchive : public ArchiveBackend {
25public: 26public:
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
38protected: 40protected:
39 friend class IVFCFile; 41 std::shared_ptr<const std::vector<u8>> data;
40 std::vector<u8> raw_data;
41}; 42};
42 43
43class IVFCFile : public FileBackend { 44class IVFCFile : public FileBackend {
44public: 45public:
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
55private: 56private:
56 const IVFCArchive* archive; 57 std::shared_ptr<const std::vector<u8>> data;
57}; 58};
58 59
59class IVFCDirectory : public DirectoryBackend { 60class 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 */
155bool LoadExec(u32 entry_point) { 155bool 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
22namespace Kernel { 22namespace Kernel {
23 23
24/// Event type for the thread wake up event
25static int ThreadWakeupEventType = -1;
26
24bool Thread::ShouldWait() { 27bool Thread::ShouldWait() {
25 return status != THREADSTATUS_DORMANT; 28 return status != THREADSTATUS_DEAD;
26} 29}
27 30
28void Thread::Acquire() { 31void Thread::Acquire() {
@@ -33,12 +36,20 @@ void Thread::Acquire() {
33static std::vector<SharedPtr<Thread>> thread_list; 36static std::vector<SharedPtr<Thread>> thread_list;
34 37
35// Lists only ready thread ids. 38// Lists only ready thread ids.
36static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; 39static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue;
37 40
38static Thread* current_thread; 41static Thread* current_thread;
39 42
40static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup 43// The first available thread id at startup
41static u32 next_thread_id; ///< The next available thread id 44static u32 next_thread_id = 1;
45
46/**
47 * Creates a new thread ID
48 * @return The new thread ID
49 */
50inline static u32 const NewThreadId() {
51 return next_thread_id++;
52}
42 53
43Thread::Thread() {} 54Thread::Thread() {}
44Thread::~Thread() {} 55Thread::~Thread() {}
@@ -47,86 +58,53 @@ Thread* GetCurrentThread() {
47 return current_thread; 58 return current_thread;
48} 59}
49 60
50/// Resets a thread 61/**
51static 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
73static 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
89static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { 67static 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 */
99static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { 81static 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 85void Thread::Stop() {
104void 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
121static 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
130Thread* ArbitrateHighestPriorityThread(u32 address) { 108Thread* 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
157void ArbitrateAllThreads(u32 address) { 134void 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/**
167static 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} 146static 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();
173static 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/**
196static Thread* NextThread() { 177 * Pops and returns the next thread from the thread queue
178 * @return A pointer to the next ready thread
179 */
180static 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
211void WaitCurrentThread_Sleep() { 195void 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
216void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { 200void 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
228void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { 208void 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
235static 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.
238static Kernel::HandleTable wakeup_callback_handle_table; 216static 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 */
241static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { 223static 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
258void Thread::WakeAfterDelay(s64 nanoseconds) { 241void 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
267void Thread::ReleaseWaitObject(WaitObject* wait_object) { 250void 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
309void Thread::ResumeFromWait() { 292void 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 */
330static void DebugThreadQueue() { 325static 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
344ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, 341ResultVal<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.
395void Thread::SetPriority(s32 priority) { 388static 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 401void 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
420SharedPtr<Thread> SetupIdleThread() { 418SharedPtr<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
430SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { 427SharedPtr<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)
454void Reschedule() { 442void 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
482void ThreadingInit() { 470void 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
487void ThreadingShutdown() { 477void 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
33enum ThreadStatus { 33enum 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
43namespace Kernel { 43namespace Kernel {
@@ -46,8 +46,19 @@ class Mutex;
46 46
47class Thread final : public WaitObject { 47class Thread final : public WaitObject {
48public: 48public:
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
137extern SharedPtr<Thread> g_main_thread; 164extern SharedPtr<Thread> g_main_thread;
138 165
139/// Sets up the primary application thread 166/**
140SharedPtr<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 */
173SharedPtr<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 */
143void Reschedule(); 178void 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 */
146Thread* ArbitrateHighestPriorityThread(u32 address); 184Thread* 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 */
149void ArbitrateAllThreads(u32 address); 190void ArbitrateAllThreads(u32 address);
150 191
151/// Gets the current thread 192/**
193 * Gets the current thread
194 */
152Thread* GetCurrentThread(); 195Thread* GetCurrentThread();
153 196
154/// Waits the current thread on a sleep 197/**
198 * Waits the current thread on a sleep
199 */
155void WaitCurrentThread_Sleep(); 200void 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 */
163void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); 208void 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 */
177SharedPtr<Thread> SetupIdleThread(); 222SharedPtr<Thread> SetupIdleThread();
178 223
179/// Initialize threading 224/**
225 * Initialize threading
226 */
180void ThreadingInit(); 227void ThreadingInit();
181 228
182/// Shutdown threading 229/**
230 * Shutdown threading
231 */
183void ThreadingShutdown(); 232void 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 */
141static 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 */
158static 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 */
178static 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
172const Interface::FunctionInfo FunctionTable[] = { 232const 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 */
141void 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 */
204void 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 */
223void 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
172const Interface::FunctionInfo FunctionTable[] = { 232const 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
77class Archive { 78ResultVal<bool> File::SyncRequest() {
78public: 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
89class File : public Kernel::Session {
90public:
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);
196class Directory : public Kernel::Session { 170}
197public:
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 { 172ResultVal<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
211using FileSys::ArchiveBackend;
212using 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 */
251static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map; 218static 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 */
256static std::unordered_map<ArchiveHandle, Archive*> handle_map; 223static std::unordered_map<ArchiveHandle, std::unique_ptr<ArchiveBackend>> handle_map;
257static ArchiveHandle next_handle; 224static ArchiveHandle next_handle;
258 225
259static Archive* GetArchive(ArchiveHandle handle) { 226static 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
264ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archive_path) { 231ResultVal<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
295ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { 260ResultCode 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
306ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, 271ResultVal<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
322ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 287ResultCode 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
333ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, 298ResultCode 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
354ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 319ResultCode 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
365ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { 330ResultCode 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
373ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 338ResultCode 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
384ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, 349ResultCode 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
405ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, 370ResultVal<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
421ResultCode FormatSaveData() { 386ResultCode 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
432ResultCode CreateExtSaveData(u32 high, u32 low) { 395ResultCode 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
37typedef u64 ArchiveHandle; 38typedef u64 ArchiveHandle;
38 39
40class File : public Kernel::Session {
41public:
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
55class Directory : public Kernel::Session {
56public:
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
51ResultCode CloseArchive(ArchiveHandle handle); 81ResultCode 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 */
58ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); 88ResultCode 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 */
67ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, 97ResultVal<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 */
131ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, 161ResultVal<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 */
138ResultCode FormatSaveData(); 171ResultCode 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
490static void CreateExtSaveData(Service::Interface* self) { 490static 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
12namespace LDR_RO { 12namespace 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 */
26static 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 */
56static 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
14const Interface::FunctionInfo FunctionTable[] = { 74const 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};
30static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 }; 31static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 };
31static std::unique_ptr<FileSys::Archive_ExtSaveData> ptm_shared_extsavedata;
32static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0}; 32static 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
139Interface::Interface() { 139Interface::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
338static void ExitThread() { 342static 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());