summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/debugger/graphics.cpp34
-rw-r--r--src/citra_qt/main.cpp2
-rw-r--r--src/common/bit_field.h24
-rw-r--r--src/common/register_set.h14
-rw-r--r--src/common/scm_rev_gen.js9
-rw-r--r--src/core/CMakeLists.txt16
-rw-r--r--src/core/arm/interpreter/armdefs.h7
-rw-r--r--src/core/core.vcxproj16
-rw-r--r--src/core/core.vcxproj.filters48
-rw-r--r--src/core/file_sys/archive.h54
-rw-r--r--src/core/file_sys/archive_romfs.cpp46
-rw-r--r--src/core/file_sys/archive_romfs.h50
-rw-r--r--src/core/file_sys/directory_file_system.cpp669
-rw-r--r--src/core/file_sys/directory_file_system.h155
-rw-r--r--src/core/file_sys/file_sys.h138
-rw-r--r--src/core/file_sys/meta_file_system.cpp519
-rw-r--r--src/core/file_sys/meta_file_system.h110
-rw-r--r--src/core/hle/config_mem.h6
-rw-r--r--src/core/hle/function_wrappers.h11
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp87
-rw-r--r--src/core/hle/kernel/address_arbiter.h36
-rw-r--r--src/core/hle/kernel/archive.cpp157
-rw-r--r--src/core/hle/kernel/archive.h38
-rw-r--r--src/core/hle/kernel/kernel.cpp3
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp105
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/thread.cpp37
-rw-r--r--src/core/hle/kernel/thread.h7
-rw-r--r--src/core/hle/service/apt.cpp164
-rw-r--r--src/core/hle/service/fs.cpp148
-rw-r--r--src/core/hle/service/fs.h31
-rw-r--r--src/core/hle/service/gsp.cpp184
-rw-r--r--src/core/hle/service/gsp.h60
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/svc.cpp44
-rw-r--r--src/core/hw/gpu.cpp253
-rw-r--r--src/core/hw/gpu.h219
-rw-r--r--src/core/hw/hw.h4
-rw-r--r--src/core/loader/elf.h6
-rw-r--r--src/core/loader/loader.cpp15
-rw-r--r--src/core/loader/loader.h55
-rw-r--r--src/core/loader/ncch.cpp172
-rw-r--r--src/core/loader/ncch.h47
-rw-r--r--src/core/mem_map.h14
-rw-r--r--src/core/mem_map_funcs.cpp42
-rw-r--r--src/core/system.cpp2
-rw-r--r--src/core/system.h2
-rw-r--r--src/video_core/gpu_debugger.h11
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp62
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h7
51 files changed, 1837 insertions, 2156 deletions
diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp
index 9aaade8f9..0f911a015 100644
--- a/src/citra_qt/debugger/graphics.cpp
+++ b/src/citra_qt/debugger/graphics.cpp
@@ -28,22 +28,24 @@ QVariant GPUCommandStreamItemModel::data(const QModelIndex& index, int role) con
28 const GSP_GPU::GXCommand& command = GetDebugger()->ReadGXCommandHistory(command_index); 28 const GSP_GPU::GXCommand& command = GetDebugger()->ReadGXCommandHistory(command_index);
29 if (role == Qt::DisplayRole) 29 if (role == Qt::DisplayRole)
30 { 30 {
31 std::map<GSP_GPU::GXCommandId, const char*> command_names; 31 std::map<GSP_GPU::GXCommandId, const char*> command_names = {
32 command_names[GSP_GPU::GXCommandId::REQUEST_DMA] = "REQUEST_DMA"; 32 { GSP_GPU::GXCommandId::REQUEST_DMA, "REQUEST_DMA" },
33 command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_FIRST] = "SET_COMMAND_LIST_FIRST"; 33 { GSP_GPU::GXCommandId::SET_COMMAND_LIST_FIRST, "SET_COMMAND_LIST_FIRST" },
34 command_names[GSP_GPU::GXCommandId::SET_MEMORY_FILL] = "SET_MEMORY_FILL"; 34 { GSP_GPU::GXCommandId::SET_MEMORY_FILL, "SET_MEMORY_FILL" },
35 command_names[GSP_GPU::GXCommandId::SET_DISPLAY_TRANSFER] = "SET_DISPLAY_TRANSFER"; 35 { GSP_GPU::GXCommandId::SET_DISPLAY_TRANSFER, "SET_DISPLAY_TRANSFER" },
36 command_names[GSP_GPU::GXCommandId::SET_TEXTURE_COPY] = "SET_TEXTURE_COPY"; 36 { GSP_GPU::GXCommandId::SET_TEXTURE_COPY, "SET_TEXTURE_COPY" },
37 command_names[GSP_GPU::GXCommandId::SET_COMMAND_LIST_LAST] = "SET_COMMAND_LIST_LAST"; 37 { GSP_GPU::GXCommandId::SET_COMMAND_LIST_LAST, "SET_COMMAND_LIST_LAST" }
38 QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[static_cast<GSP_GPU::GXCommandId>(command.id)]) 38 };
39 .arg(command.data[0], 8, 16, QLatin1Char('0')) 39 const u32* command_data = reinterpret_cast<const u32*>(&command);
40 .arg(command.data[1], 8, 16, QLatin1Char('0')) 40 QString str = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(command_names[command.id])
41 .arg(command.data[2], 8, 16, QLatin1Char('0')) 41 .arg(command_data[0], 8, 16, QLatin1Char('0'))
42 .arg(command.data[3], 8, 16, QLatin1Char('0')) 42 .arg(command_data[1], 8, 16, QLatin1Char('0'))
43 .arg(command.data[4], 8, 16, QLatin1Char('0')) 43 .arg(command_data[2], 8, 16, QLatin1Char('0'))
44 .arg(command.data[5], 8, 16, QLatin1Char('0')) 44 .arg(command_data[3], 8, 16, QLatin1Char('0'))
45 .arg(command.data[6], 8, 16, QLatin1Char('0')) 45 .arg(command_data[4], 8, 16, QLatin1Char('0'))
46 .arg(command.data[7], 8, 16, QLatin1Char('0')); 46 .arg(command_data[5], 8, 16, QLatin1Char('0'))
47 .arg(command_data[6], 8, 16, QLatin1Char('0'))
48 .arg(command_data[7], 8, 16, QLatin1Char('0'));
47 return QVariant(str); 49 return QVariant(str);
48 } 50 }
49 else 51 else
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 0bcce7d16..997e82cc9 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -152,7 +152,7 @@ void GMainWindow::BootGame(const char* filename)
152 152
153void GMainWindow::OnMenuLoadFile() 153void GMainWindow::OnMenuLoadFile()
154{ 154{
155 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.axf *.dat *.bin)")); 155 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.cci *.cxi)"));
156 if (filename.size()) 156 if (filename.size())
157 BootGame(filename.toLatin1().data()); 157 BootGame(filename.toLatin1().data());
158} 158}
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index dfd00d198..b6f0179c6 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -124,14 +124,35 @@ public:
124 // so that we can use this within unions 124 // so that we can use this within unions
125 BitField() = default; 125 BitField() = default;
126 126
127#ifndef _WIN32
128 // We explicitly delete the copy assigment operator here, because the
129 // default copy assignment would copy the full storage value, rather than
130 // just the bits relevant to this particular bit field.
131 // Ideally, we would just implement the copy assignment to copy only the
132 // relevant bits, but this requires compiler support for unrestricted
133 // unions.
134 // MSVC 2013 has no support for this, hence we disable this code on
135 // Windows (so that the default copy assignment operator will be used).
136 // For any C++11 conformant compiler we delete the operator to make sure
137 // we never use this inappropriate operator to begin with.
138 // TODO: Implement this operator properly once all target compilers
139 // support unrestricted unions.
140 BitField& operator=(const BitField&) = delete;
141#endif
142
127 __forceinline BitField& operator=(T val) 143 __forceinline BitField& operator=(T val)
128 { 144 {
129 storage = (storage & ~GetMask()) | ((val << position) & GetMask()); 145 storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask());
130 return *this; 146 return *this;
131 } 147 }
132 148
133 __forceinline operator T() const 149 __forceinline operator T() const
134 { 150 {
151 return Value();
152 }
153
154 __forceinline T Value() const
155 {
135 if (std::numeric_limits<T>::is_signed) 156 if (std::numeric_limits<T>::is_signed)
136 { 157 {
137 std::size_t shift = 8 * sizeof(T)-bits; 158 std::size_t shift = 8 * sizeof(T)-bits;
@@ -168,5 +189,6 @@ private:
168 static_assert(position < 8 * sizeof(T), "Invalid position"); 189 static_assert(position < 8 * sizeof(T), "Invalid position");
169 static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); 190 static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
170 static_assert(bits > 0, "Invalid number of bits"); 191 static_assert(bits > 0, "Invalid number of bits");
192 static_assert(std::is_standard_layout<T>::value, "Invalid base type");
171}; 193};
172#pragma pack() 194#pragma pack()
diff --git a/src/common/register_set.h b/src/common/register_set.h
index 0418551b3..ba19a2614 100644
--- a/src/common/register_set.h
+++ b/src/common/register_set.h
@@ -34,7 +34,7 @@
34/* 34/*
35 * Standardized way to define a group of registers and corresponding data structures. To define 35 * Standardized way to define a group of registers and corresponding data structures. To define
36 * a new register set, first define struct containing an enumeration called "Id" containing 36 * a new register set, first define struct containing an enumeration called "Id" containing
37 * all register IDs and a template union called "Struct". Specialize the Struct union for any 37 * all register IDs and a template struct called "Struct". Specialize the Struct struct for any
38 * register ID which needs to be accessed in a specialized way. You can then declare the object 38 * register ID which needs to be accessed in a specialized way. You can then declare the object
39 * containing all register values using the RegisterSet<BaseType, DefiningStruct> type, where 39 * containing all register values using the RegisterSet<BaseType, DefiningStruct> type, where
40 * BaseType is the underlying type of each register (e.g. u32). 40 * BaseType is the underlying type of each register (e.g. u32).
@@ -54,7 +54,7 @@
54 * 54 *
55 * // declare register definition structures 55 * // declare register definition structures
56 * template<Id id> 56 * template<Id id>
57 * union Struct; 57 * struct Struct;
58 * }; 58 * };
59 * 59 *
60 * // Define register set object 60 * // Define register set object
@@ -62,9 +62,11 @@
62 * 62 *
63 * // define register definition structures 63 * // define register definition structures
64 * template<> 64 * template<>
65 * union Regs::Struct<Regs::Value1> { 65 * struct Regs::Struct<Regs::Value1> {
66 * BitField<0, 4, u32> some_field; 66 * union {
67 * BitField<4, 3, u32> some_other_field; 67 * BitField<0, 4, u32> some_field;
68 * BitField<4, 3, u32> some_other_field;
69 * };
68 * }; 70 * };
69 * 71 *
70 * Usage in external code (within SomeNamespace scope): 72 * Usage in external code (within SomeNamespace scope):
@@ -77,7 +79,7 @@
77 * 79 *
78 * 80 *
79 * @tparam BaseType Base type used for storing individual registers, e.g. u32 81 * @tparam BaseType Base type used for storing individual registers, e.g. u32
80 * @tparam RegDefinition Class defining an enumeration called "Id" and a template<Id id> union, as described above. 82 * @tparam RegDefinition Class defining an enumeration called "Id" and a template<Id id> struct, as described above.
81 * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated. 83 * @note RegDefinition::Id needs to have an enum value called NumIds defining the number of registers to be allocated.
82 */ 84 */
83template<typename BaseType, typename RegDefinition> 85template<typename BaseType, typename RegDefinition>
diff --git a/src/common/scm_rev_gen.js b/src/common/scm_rev_gen.js
index 29c913b85..98313e376 100644
--- a/src/common/scm_rev_gen.js
+++ b/src/common/scm_rev_gen.js
@@ -6,8 +6,15 @@ var cmd_revision = " rev-parse HEAD";
6var cmd_describe = " describe --always --long --dirty"; 6var cmd_describe = " describe --always --long --dirty";
7var cmd_branch = " rev-parse --abbrev-ref HEAD"; 7var cmd_branch = " rev-parse --abbrev-ref HEAD";
8 8
9var git_search_paths = {
10 "git.cmd": 1,
11 "git": 1,
12 "C:\\Program Files (x86)\\Git\\bin\\git.exe": 1,
13 "C:\\Program Files\\Git\\bin\\git.exe": 1
14};
15
9function GetGitExe() { 16function GetGitExe() {
10 for (var gitexe in { "git.cmd": 1, "git": 1 }) { 17 for (var gitexe in git_search_paths) {
11 try { 18 try {
12 wshShell.Exec(gitexe); 19 wshShell.Exec(gitexe);
13 return gitexe; 20 return gitexe;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 634f4d572..207f39707 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -8,6 +8,7 @@ set(SRCS core.cpp
8 system.cpp 8 system.cpp
9 arm/disassembler/arm_disasm.cpp 9 arm/disassembler/arm_disasm.cpp
10 arm/disassembler/load_symbol_map.cpp 10 arm/disassembler/load_symbol_map.cpp
11 file_sys/archive_romfs.cpp
11 arm/interpreter/arm_interpreter.cpp 12 arm/interpreter/arm_interpreter.cpp
12 arm/interpreter/armcopro.cpp 13 arm/interpreter/armcopro.cpp
13 arm/interpreter/armemu.cpp 14 arm/interpreter/armemu.cpp
@@ -29,17 +30,19 @@ set(SRCS core.cpp
29 arm/interpreter/mmu/tlb.cpp 30 arm/interpreter/mmu/tlb.cpp
30 arm/interpreter/mmu/wb.cpp 31 arm/interpreter/mmu/wb.cpp
31 arm/interpreter/mmu/xscale_copro.cpp 32 arm/interpreter/mmu/xscale_copro.cpp
32 file_sys/directory_file_system.cpp
33 file_sys/meta_file_system.cpp
34 hle/hle.cpp 33 hle/hle.cpp
35 hle/config_mem.cpp 34 hle/config_mem.cpp
36 hle/coprocessor.cpp 35 hle/coprocessor.cpp
37 hle/svc.cpp 36 hle/svc.cpp
37 hle/kernel/address_arbiter.cpp
38 hle/kernel/archive.cpp
38 hle/kernel/event.cpp 39 hle/kernel/event.cpp
39 hle/kernel/kernel.cpp 40 hle/kernel/kernel.cpp
40 hle/kernel/mutex.cpp 41 hle/kernel/mutex.cpp
42 hle/kernel/shared_memory.cpp
41 hle/kernel/thread.cpp 43 hle/kernel/thread.cpp
42 hle/service/apt.cpp 44 hle/service/apt.cpp
45 hle/service/fs.cpp
43 hle/service/gsp.cpp 46 hle/service/gsp.cpp
44 hle/service/hid.cpp 47 hle/service/hid.cpp
45 hle/service/ndm.cpp 48 hle/service/ndm.cpp
@@ -75,18 +78,21 @@ set(HEADERS core.h
75 arm/interpreter/vfp/asm_vfp.h 78 arm/interpreter/vfp/asm_vfp.h
76 arm/interpreter/vfp/vfp.h 79 arm/interpreter/vfp/vfp.h
77 arm/interpreter/vfp/vfp_helper.h 80 arm/interpreter/vfp/vfp_helper.h
78 file_sys/directory_file_system.h 81 file_sys/archive.h
79 file_sys/file_sys.h 82 file_sys/archive_romfs.h
80 file_sys/meta_file_system.h
81 hle/config_mem.h 83 hle/config_mem.h
82 hle/coprocessor.h 84 hle/coprocessor.h
83 hle/hle.h 85 hle/hle.h
84 hle/svc.h 86 hle/svc.h
87 hle/kernel/address_arbiter.h
88 hle/kernel/archive.h
85 hle/kernel/kernel.h 89 hle/kernel/kernel.h
86 hle/kernel/mutex.h 90 hle/kernel/mutex.h
91 hle/kernel/shared_memory.h
87 hle/kernel/thread.h 92 hle/kernel/thread.h
88 hle/function_wrappers.h 93 hle/function_wrappers.h
89 hle/service/apt.h 94 hle/service/apt.h
95 hle/service/fs.h
90 hle/service/gsp.h 96 hle/service/gsp.h
91 hle/service/hid.h 97 hle/service/hid.h
92 hle/service/service.h 98 hle/service/service.h
diff --git a/src/core/arm/interpreter/armdefs.h b/src/core/arm/interpreter/armdefs.h
index d8eae4d3f..1ff560fe7 100644
--- a/src/core/arm/interpreter/armdefs.h
+++ b/src/core/arm/interpreter/armdefs.h
@@ -70,13 +70,6 @@
70#define LOWHIGH 1 70#define LOWHIGH 1
71#define HIGHLOW 2 71#define HIGHLOW 2
72 72
73#ifndef u8
74#define u8 unsigned char
75#define u16 unsigned short
76#define u32 unsigned int
77#define u64 unsigned long long
78#endif /*u8 */
79
80//teawater add DBCT_TEST_SPEED 2005.10.04--------------------------------------- 73//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
81#include <signal.h> 74#include <signal.h>
82 75
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index e2216760a..ddc174c2c 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -162,16 +162,19 @@
162 <ClCompile Include="arm\interpreter\vfp\vfpsingle.cpp" /> 162 <ClCompile Include="arm\interpreter\vfp\vfpsingle.cpp" />
163 <ClCompile Include="core.cpp" /> 163 <ClCompile Include="core.cpp" />
164 <ClCompile Include="core_timing.cpp" /> 164 <ClCompile Include="core_timing.cpp" />
165 <ClCompile Include="file_sys\directory_file_system.cpp" /> 165 <ClCompile Include="file_sys\archive_romfs.cpp" />
166 <ClCompile Include="file_sys\meta_file_system.cpp" />
167 <ClCompile Include="hle\config_mem.cpp" /> 166 <ClCompile Include="hle\config_mem.cpp" />
168 <ClCompile Include="hle\coprocessor.cpp" /> 167 <ClCompile Include="hle\coprocessor.cpp" />
169 <ClCompile Include="hle\hle.cpp" /> 168 <ClCompile Include="hle\hle.cpp" />
169 <ClCompile Include="hle\kernel\address_arbiter.cpp" />
170 <ClCompile Include="hle\kernel\archive.cpp" />
170 <ClCompile Include="hle\kernel\event.cpp" /> 171 <ClCompile Include="hle\kernel\event.cpp" />
171 <ClCompile Include="hle\kernel\kernel.cpp" /> 172 <ClCompile Include="hle\kernel\kernel.cpp" />
172 <ClCompile Include="hle\kernel\mutex.cpp" /> 173 <ClCompile Include="hle\kernel\mutex.cpp" />
174 <ClCompile Include="hle\kernel\shared_memory.cpp" />
173 <ClCompile Include="hle\kernel\thread.cpp" /> 175 <ClCompile Include="hle\kernel\thread.cpp" />
174 <ClCompile Include="hle\service\apt.cpp" /> 176 <ClCompile Include="hle\service\apt.cpp" />
177 <ClCompile Include="hle\service\fs.cpp" />
175 <ClCompile Include="hle\service\gsp.cpp" /> 178 <ClCompile Include="hle\service\gsp.cpp" />
176 <ClCompile Include="hle\service\hid.cpp" /> 179 <ClCompile Include="hle\service\hid.cpp" />
177 <ClCompile Include="hle\service\ndm.cpp" /> 180 <ClCompile Include="hle\service\ndm.cpp" />
@@ -211,18 +214,21 @@
211 <ClInclude Include="arm\interpreter\vfp\vfp_helper.h" /> 214 <ClInclude Include="arm\interpreter\vfp\vfp_helper.h" />
212 <ClInclude Include="core.h" /> 215 <ClInclude Include="core.h" />
213 <ClInclude Include="core_timing.h" /> 216 <ClInclude Include="core_timing.h" />
214 <ClInclude Include="file_sys\directory_file_system.h" /> 217 <ClInclude Include="file_sys\archive.h" />
215 <ClInclude Include="file_sys\file_sys.h" /> 218 <ClInclude Include="file_sys\archive_romfs.h" />
216 <ClInclude Include="file_sys\meta_file_system.h" />
217 <ClInclude Include="hle\config_mem.h" /> 219 <ClInclude Include="hle\config_mem.h" />
218 <ClInclude Include="hle\coprocessor.h" /> 220 <ClInclude Include="hle\coprocessor.h" />
219 <ClInclude Include="hle\function_wrappers.h" /> 221 <ClInclude Include="hle\function_wrappers.h" />
220 <ClInclude Include="hle\hle.h" /> 222 <ClInclude Include="hle\hle.h" />
223 <ClInclude Include="hle\kernel\address_arbiter.h" />
224 <ClInclude Include="hle\kernel\archive.h" />
221 <ClInclude Include="hle\kernel\event.h" /> 225 <ClInclude Include="hle\kernel\event.h" />
222 <ClInclude Include="hle\kernel\kernel.h" /> 226 <ClInclude Include="hle\kernel\kernel.h" />
223 <ClInclude Include="hle\kernel\mutex.h" /> 227 <ClInclude Include="hle\kernel\mutex.h" />
228 <ClInclude Include="hle\kernel\shared_memory.h" />
224 <ClInclude Include="hle\kernel\thread.h" /> 229 <ClInclude Include="hle\kernel\thread.h" />
225 <ClInclude Include="hle\service\apt.h" /> 230 <ClInclude Include="hle\service\apt.h" />
231 <ClInclude Include="hle\service\fs.h" />
226 <ClInclude Include="hle\service\gsp.h" /> 232 <ClInclude Include="hle\service\gsp.h" />
227 <ClInclude Include="hle\service\hid.h" /> 233 <ClInclude Include="hle\service\hid.h" />
228 <ClInclude Include="hle\service\ndm.h" /> 234 <ClInclude Include="hle\service\ndm.h" />
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 91d3292da..68ba9e50b 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -63,12 +63,6 @@
63 <ClCompile Include="arm\interpreter\thumbemu.cpp"> 63 <ClCompile Include="arm\interpreter\thumbemu.cpp">
64 <Filter>arm\interpreter</Filter> 64 <Filter>arm\interpreter</Filter>
65 </ClCompile> 65 </ClCompile>
66 <ClCompile Include="file_sys\directory_file_system.cpp">
67 <Filter>file_sys</Filter>
68 </ClCompile>
69 <ClCompile Include="file_sys\meta_file_system.cpp">
70 <Filter>file_sys</Filter>
71 </ClCompile>
72 <ClCompile Include="hw\hw.cpp"> 66 <ClCompile Include="hw\hw.cpp">
73 <Filter>hw</Filter> 67 <Filter>hw</Filter>
74 </ClCompile> 68 </ClCompile>
@@ -176,6 +170,21 @@
176 <ClCompile Include="loader\elf.cpp"> 170 <ClCompile Include="loader\elf.cpp">
177 <Filter>loader</Filter> 171 <Filter>loader</Filter>
178 </ClCompile> 172 </ClCompile>
173 <ClCompile Include="hle\kernel\archive.cpp">
174 <Filter>hle\kernel</Filter>
175 </ClCompile>
176 <ClCompile Include="hle\service\fs.cpp">
177 <Filter>hle\service</Filter>
178 </ClCompile>
179 <ClCompile Include="file_sys\archive_romfs.cpp">
180 <Filter>file_sys</Filter>
181 </ClCompile>
182 <ClCompile Include="hle\kernel\shared_memory.cpp">
183 <Filter>hle\kernel</Filter>
184 </ClCompile>
185 <ClCompile Include="hle\kernel\address_arbiter.cpp">
186 <Filter>hle\kernel</Filter>
187 </ClCompile>
179 </ItemGroup> 188 </ItemGroup>
180 <ItemGroup> 189 <ItemGroup>
181 <ClInclude Include="arm\disassembler\arm_disasm.h"> 190 <ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -205,15 +214,6 @@
205 <ClInclude Include="arm\interpreter\skyeye_defs.h"> 214 <ClInclude Include="arm\interpreter\skyeye_defs.h">
206 <Filter>arm\interpreter</Filter> 215 <Filter>arm\interpreter</Filter>
207 </ClInclude> 216 </ClInclude>
208 <ClInclude Include="file_sys\directory_file_system.h">
209 <Filter>file_sys</Filter>
210 </ClInclude>
211 <ClInclude Include="file_sys\file_sys.h">
212 <Filter>file_sys</Filter>
213 </ClInclude>
214 <ClInclude Include="file_sys\meta_file_system.h">
215 <Filter>file_sys</Filter>
216 </ClInclude>
217 <ClInclude Include="hw\hw.h"> 217 <ClInclude Include="hw\hw.h">
218 <Filter>hw</Filter> 218 <Filter>hw</Filter>
219 </ClInclude> 219 </ClInclude>
@@ -314,6 +314,24 @@
314 <ClInclude Include="loader\elf.h"> 314 <ClInclude Include="loader\elf.h">
315 <Filter>loader</Filter> 315 <Filter>loader</Filter>
316 </ClInclude> 316 </ClInclude>
317 <ClInclude Include="hle\kernel\archive.h">
318 <Filter>hle\kernel</Filter>
319 </ClInclude>
320 <ClInclude Include="hle\service\fs.h">
321 <Filter>hle\service</Filter>
322 </ClInclude>
323 <ClInclude Include="file_sys\archive.h">
324 <Filter>file_sys</Filter>
325 </ClInclude>
326 <ClInclude Include="file_sys\archive_romfs.h">
327 <Filter>file_sys</Filter>
328 </ClInclude>
329 <ClInclude Include="hle\kernel\shared_memory.h">
330 <Filter>hle\kernel</Filter>
331 </ClInclude>
332 <ClInclude Include="hle\kernel\address_arbiter.h">
333 <Filter>hle\kernel</Filter>
334 </ClInclude>
317 </ItemGroup> 335 </ItemGroup>
318 <ItemGroup> 336 <ItemGroup>
319 <Text Include="CMakeLists.txt" /> 337 <Text Include="CMakeLists.txt" />
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h
new file mode 100644
index 000000000..ed2d83640
--- /dev/null
+++ b/src/core/file_sys/archive.h
@@ -0,0 +1,54 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// FileSys namespace
13
14namespace FileSys {
15
16class Archive : NonCopyable {
17public:
18 /// Supported archive types
19 enum class IdCode : u32 {
20 RomFS = 0x00000003,
21 SaveData = 0x00000004,
22 ExtSaveData = 0x00000006,
23 SharedExtSaveData = 0x00000007,
24 SystemSaveData = 0x00000008,
25 SDMC = 0x00000009,
26 SDMCWriteOnly = 0x0000000A,
27 };
28
29 Archive() { }
30 virtual ~Archive() { }
31
32 /**
33 * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
34 * @return IdCode of the archive
35 */
36 virtual IdCode GetIdCode() const = 0;
37
38 /**
39 * Read data from the archive
40 * @param offset Offset in bytes to start reading archive from
41 * @param length Length in bytes to read data from archive
42 * @param buffer Buffer to read data into
43 * @return Number of bytes read
44 */
45 virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
46
47 /**
48 * Get the size of the archive in bytes
49 * @return Size of the archive in bytes
50 */
51 virtual size_t GetSize() const = 0;
52};
53
54} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
new file mode 100644
index 000000000..fd84b9c8c
--- /dev/null
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -0,0 +1,46 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6
7#include "core/file_sys/archive_romfs.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace
11
12namespace FileSys {
13
14Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) {
15 // Load the RomFS from the app
16 if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
17 WARN_LOG(FILESYS, "Unable to read RomFS!");
18 }
19}
20
21Archive_RomFS::~Archive_RomFS() {
22}
23
24/**
25 * Read data from the archive
26 * @param offset Offset in bytes to start reading archive from
27 * @param length Length in bytes to read data from archive
28 * @param buffer Buffer to read data into
29 * @return Number of bytes read
30 */
31size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
32 DEBUG_LOG(FILESYS, "called offset=%d, length=%d", offset, length);
33 memcpy(buffer, &raw_data[(u32)offset], length);
34 return length;
35}
36
37/**
38 * Get the size of the archive in bytes
39 * @return Size of the archive in bytes
40 */
41size_t Archive_RomFS::GetSize() const {
42 ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
43 return 0;
44}
45
46} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
new file mode 100644
index 000000000..8a31190a9
--- /dev/null
+++ b/src/core/file_sys/archive_romfs.h
@@ -0,0 +1,50 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common_types.h"
10
11#include "core/file_sys/archive.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19/// File system interface to the RomFS archive
20class Archive_RomFS final : public Archive {
21public:
22 Archive_RomFS(const Loader::AppLoader& app_loader);
23 ~Archive_RomFS() override;
24
25 /**
26 * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
27 * @return IdCode of the archive
28 */
29 IdCode GetIdCode() const override { return IdCode::RomFS; };
30
31 /**
32 * Read data from the archive
33 * @param offset Offset in bytes to start reading archive from
34 * @param length Length in bytes to read data from archive
35 * @param buffer Buffer to read data into
36 * @return Number of bytes read
37 */
38 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
39
40 /**
41 * Get the size of the archive in bytes
42 * @return Size of the archive in bytes
43 */
44 size_t GetSize() const override;
45
46private:
47 std::vector<u8> raw_data;
48};
49
50} // namespace FileSys
diff --git a/src/core/file_sys/directory_file_system.cpp b/src/core/file_sys/directory_file_system.cpp
deleted file mode 100644
index 6c6f33c2b..000000000
--- a/src/core/file_sys/directory_file_system.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
1// Copyright (c) 2012- PPSSPP Project.
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14
15// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18#include "common/chunk_file.h"
19#include "common/file_util.h"
20#include "common/utf8.h"
21
22#include "core/file_sys/directory_file_system.h"
23
24#if EMU_PLATFORM == PLATFORM_WINDOWS
25#include <windows.h>
26#include <sys/stat.h>
27#else
28#include <dirent.h>
29#include <unistd.h>
30#include <sys/stat.h>
31#include <ctype.h>
32#endif
33
34#if HOST_IS_CASE_SENSITIVE
35static bool FixFilenameCase(const std::string &path, std::string &filename)
36{
37 // Are we lucky?
38 if (File::Exists(path + filename))
39 return true;
40
41 size_t filenameSize = filename.size(); // size in bytes, not characters
42 for (size_t i = 0; i < filenameSize; i++)
43 {
44 filename[i] = tolower(filename[i]);
45 }
46
47 //TODO: lookup filename in cache for "path"
48
49 struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; } diren;
50 struct dirent_large;
51 struct dirent *result = NULL;
52
53 DIR *dirp = opendir(path.c_str());
54 if (!dirp)
55 return false;
56
57 bool retValue = false;
58
59 while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
60 {
61 if (strlen(result->d_name) != filenameSize)
62 continue;
63
64 size_t i;
65 for (i = 0; i < filenameSize; i++)
66 {
67 if (filename[i] != tolower(result->d_name[i]))
68 break;
69 }
70
71 if (i < filenameSize)
72 continue;
73
74 filename = result->d_name;
75 retValue = true;
76 }
77
78 closedir(dirp);
79
80 return retValue;
81}
82
83bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior)
84{
85 size_t len = path.size();
86
87 if (len == 0)
88 return true;
89
90 if (path[len - 1] == '/')
91 {
92 len--;
93
94 if (len == 0)
95 return true;
96 }
97
98 std::string fullPath;
99 fullPath.reserve(basePath.size() + len + 1);
100 fullPath.append(basePath);
101
102 size_t start = 0;
103 while (start < len)
104 {
105 size_t i = path.find('/', start);
106 if (i == std::string::npos)
107 i = len;
108
109 if (i > start)
110 {
111 std::string component = path.substr(start, i - start);
112
113 // Fix case and stop on nonexistant path component
114 if (FixFilenameCase(fullPath, component) == false) {
115 // Still counts as success if partial matches allowed or if this
116 // is the last component and only the ones before it are required
117 return (behavior == FPC_PARTIAL_ALLOWED || (behavior == FPC_PATH_MUST_EXIST && i >= len));
118 }
119
120 path.replace(start, i - start, component);
121
122 fullPath.append(component);
123 fullPath.append(1, '/');
124 }
125
126 start = i + 1;
127 }
128
129 return true;
130}
131
132#endif
133
134std::string DirectoryFileHandle::GetLocalPath(std::string& basePath, std::string localpath)
135{
136 if (localpath.empty())
137 return basePath;
138
139 if (localpath[0] == '/')
140 localpath.erase(0,1);
141 //Convert slashes
142#ifdef _WIN32
143 for (size_t i = 0; i < localpath.size(); i++) {
144 if (localpath[i] == '/')
145 localpath[i] = '\\';
146 }
147#endif
148 return basePath + localpath;
149}
150
151bool DirectoryFileHandle::Open(std::string& basePath, std::string& fileName, FileAccess access)
152{
153#if HOST_IS_CASE_SENSITIVE
154 if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE))
155 {
156 DEBUG_LOG(FILESYS, "Checking case for path %s", fileName.c_str());
157 if ( ! FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST) )
158 return false; // or go on and attempt (for a better error code than just 0?)
159 }
160 // else we try fopen first (in case we're lucky) before simulating case insensitivity
161#endif
162
163 std::string fullName = GetLocalPath(basePath,fileName);
164 INFO_LOG(FILESYS,"Actually opening %s", fullName.c_str());
165
166 //TODO: tests, should append seek to end of file? seeking in a file opened for append?
167#ifdef _WIN32
168 // Convert parameters to Windows permissions and access
169 DWORD desired = 0;
170 DWORD sharemode = 0;
171 DWORD openmode = 0;
172 if (access & FILEACCESS_READ) {
173 desired |= GENERIC_READ;
174 sharemode |= FILE_SHARE_READ;
175 }
176 if (access & FILEACCESS_WRITE) {
177 desired |= GENERIC_WRITE;
178 sharemode |= FILE_SHARE_WRITE;
179 }
180 if (access & FILEACCESS_CREATE) {
181 openmode = OPEN_ALWAYS;
182 } else {
183 openmode = OPEN_EXISTING;
184 }
185 //Let's do it!
186 hFile = CreateFile(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, 0, openmode, 0, 0);
187 bool success = hFile != INVALID_HANDLE_VALUE;
188#else
189 // Convert flags in access parameter to fopen access mode
190 const char *mode = NULL;
191 if (access & FILEACCESS_APPEND) {
192 if (access & FILEACCESS_READ)
193 mode = "ab+"; // append+read, create if needed
194 else
195 mode = "ab"; // append only, create if needed
196 } else if (access & FILEACCESS_WRITE) {
197 if (access & FILEACCESS_READ) {
198 // FILEACCESS_CREATE is ignored for read only, write only, and append
199 // because C++ standard fopen's nonexistant file creation can only be
200 // customized for files opened read+write
201 if (access & FILEACCESS_CREATE)
202 mode = "wb+"; // read+write, create if needed
203 else
204 mode = "rb+"; // read+write, but don't create
205 } else {
206 mode = "wb"; // write only, create if needed
207 }
208 } else { // neither write nor append, so default to read only
209 mode = "rb"; // read only, don't create
210 }
211
212 hFile = fopen(fullName.c_str(), mode);
213 bool success = hFile != 0;
214#endif
215
216#if HOST_IS_CASE_SENSITIVE
217 if (!success &&
218 !(access & FILEACCESS_APPEND) &&
219 !(access & FILEACCESS_CREATE) &&
220 !(access & FILEACCESS_WRITE))
221 {
222 if ( ! FixPathCase(basePath,fileName, FPC_PATH_MUST_EXIST) )
223 return 0; // or go on and attempt (for a better error code than just 0?)
224 fullName = GetLocalPath(basePath,fileName);
225 const char* fullNameC = fullName.c_str();
226
227 DEBUG_LOG(FILESYS, "Case may have been incorrect, second try opening %s (%s)", fullNameC, fileName.c_str());
228
229 // And try again with the correct case this time
230#ifdef _WIN32
231 hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
232 success = hFile != INVALID_HANDLE_VALUE;
233#else
234 hFile = fopen(fullNameC, mode);
235 success = hFile != 0;
236#endif
237 }
238#endif
239
240 return success;
241}
242
243size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
244{
245 size_t bytesRead = 0;
246#ifdef _WIN32
247 ::ReadFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
248#else
249 bytesRead = fread(pointer, 1, size, hFile);
250#endif
251 return bytesRead;
252}
253
254size_t DirectoryFileHandle::Write(const u8* pointer, s64 size)
255{
256 size_t bytesWritten = 0;
257#ifdef _WIN32
258 ::WriteFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0);
259#else
260 bytesWritten = fwrite(pointer, 1, size, hFile);
261#endif
262 return bytesWritten;
263}
264
265size_t DirectoryFileHandle::Seek(s32 position, FileMove type)
266{
267#ifdef _WIN32
268 DWORD moveMethod = 0;
269 switch (type) {
270 case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
271 case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
272 case FILEMOVE_END: moveMethod = FILE_END; break;
273 }
274 DWORD newPos = SetFilePointer(hFile, (LONG)position, 0, moveMethod);
275 return newPos;
276#else
277 int moveMethod = 0;
278 switch (type) {
279 case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
280 case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
281 case FILEMOVE_END: moveMethod = SEEK_END; break;
282 }
283 fseek(hFile, position, moveMethod);
284 return ftell(hFile);
285#endif
286}
287
288void DirectoryFileHandle::Close()
289{
290#ifdef _WIN32
291 if (hFile != (HANDLE)-1)
292 CloseHandle(hFile);
293#else
294 if (hFile != 0)
295 fclose(hFile);
296#endif
297}
298
299DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) {
300 File::CreateFullPath(basePath);
301 hAlloc = _hAlloc;
302}
303
304DirectoryFileSystem::~DirectoryFileSystem() {
305 for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
306 iter->second.hFile.Close();
307 }
308}
309
310std::string DirectoryFileSystem::GetLocalPath(std::string localpath) {
311 if (localpath.empty())
312 return basePath;
313
314 if (localpath[0] == '/')
315 localpath.erase(0,1);
316 //Convert slashes
317#ifdef _WIN32
318 for (size_t i = 0; i < localpath.size(); i++) {
319 if (localpath[i] == '/')
320 localpath[i] = '\\';
321 }
322#endif
323 return basePath + localpath;
324}
325
326bool DirectoryFileSystem::MkDir(const std::string &dirname) {
327#if HOST_IS_CASE_SENSITIVE
328 // Must fix case BEFORE attempting, because MkDir would create
329 // duplicate (different case) directories
330
331 std::string fixedCase = dirname;
332 if ( ! FixPathCase(basePath,fixedCase, FPC_PARTIAL_ALLOWED) )
333 return false;
334
335 return File::CreateFullPath(GetLocalPath(fixedCase));
336#else
337 return File::CreateFullPath(GetLocalPath(dirname));
338#endif
339}
340
341bool DirectoryFileSystem::RmDir(const std::string &dirname) {
342 std::string fullName = GetLocalPath(dirname);
343
344#if HOST_IS_CASE_SENSITIVE
345 // Maybe we're lucky?
346 if (File::DeleteDirRecursively(fullName))
347 return true;
348
349 // Nope, fix case and try again
350 fullName = dirname;
351 if ( ! FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST) )
352 return false; // or go on and attempt (for a better error code than just false?)
353
354 fullName = GetLocalPath(fullName);
355#endif
356
357/*#ifdef _WIN32
358 return RemoveDirectory(fullName.c_str()) == TRUE;
359#else
360 return 0 == rmdir(fullName.c_str());
361#endif*/
362 return File::DeleteDirRecursively(fullName);
363}
364
365int DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) {
366 std::string fullTo = to;
367
368 // Rename ignores the path (even if specified) on to.
369 size_t chop_at = to.find_last_of('/');
370 if (chop_at != to.npos)
371 fullTo = to.substr(chop_at + 1);
372
373 // Now put it in the same directory as from.
374 size_t dirname_end = from.find_last_of('/');
375 if (dirname_end != from.npos)
376 fullTo = from.substr(0, dirname_end + 1) + fullTo;
377
378 // At this point, we should check if the paths match and give an already exists error.
379 if (from == fullTo)
380 return -1;//SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS;
381
382 std::string fullFrom = GetLocalPath(from);
383
384#if HOST_IS_CASE_SENSITIVE
385 // In case TO should overwrite a file with different case
386 if ( ! FixPathCase(basePath,fullTo, FPC_PATH_MUST_EXIST) )
387 return -1; // or go on and attempt (for a better error code than just false?)
388#endif
389
390 fullTo = GetLocalPath(fullTo);
391 const char * fullToC = fullTo.c_str();
392
393#ifdef _WIN32
394 bool retValue = (MoveFile(ConvertUTF8ToWString(fullFrom).c_str(), ConvertUTF8ToWString(fullToC).c_str()) == TRUE);
395#else
396 bool retValue = (0 == rename(fullFrom.c_str(), fullToC));
397#endif
398
399#if HOST_IS_CASE_SENSITIVE
400 if (! retValue)
401 {
402 // May have failed due to case sensitivity on FROM, so try again
403 fullFrom = from;
404 if ( ! FixPathCase(basePath,fullFrom, FPC_FILE_MUST_EXIST) )
405 return -1; // or go on and attempt (for a better error code than just false?)
406 fullFrom = GetLocalPath(fullFrom);
407
408#ifdef _WIN32
409 retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
410#else
411 retValue = (0 == rename(fullFrom.c_str(), fullToC));
412#endif
413 }
414#endif
415
416 // TODO: Better error codes.
417 return retValue ? 0 : -1;//SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS;
418}
419
420bool DirectoryFileSystem::RemoveFile(const std::string &filename) {
421 std::string fullName = GetLocalPath(filename);
422#ifdef _WIN32
423 bool retValue = (::DeleteFileA(fullName.c_str()) == TRUE);
424#else
425 bool retValue = (0 == unlink(fullName.c_str()));
426#endif
427
428#if HOST_IS_CASE_SENSITIVE
429 if (! retValue)
430 {
431 // May have failed due to case sensitivity, so try again
432 fullName = filename;
433 if ( ! FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST) )
434 return false; // or go on and attempt (for a better error code than just false?)
435 fullName = GetLocalPath(fullName);
436
437#ifdef _WIN32
438 retValue = (::DeleteFileA(fullName.c_str()) == TRUE);
439#else
440 retValue = (0 == unlink(fullName.c_str()));
441#endif
442 }
443#endif
444
445 return retValue;
446}
447
448u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) {
449 OpenFileEntry entry;
450 bool success = entry.hFile.Open(basePath,filename,access);
451
452 if (!success) {
453#ifdef _WIN32
454 ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
455#else
456 ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, access = %i", (int)access);
457#endif
458 //wwwwaaaaahh!!
459 return 0;
460 } else {
461#ifdef _WIN32
462 if (access & FILEACCESS_APPEND)
463 entry.hFile.Seek(0,FILEMOVE_END);
464#endif
465
466 u32 newHandle = hAlloc->GetNewHandle();
467 entries[newHandle] = entry;
468
469 return newHandle;
470 }
471}
472
473void DirectoryFileSystem::CloseFile(u32 handle) {
474 EntryMap::iterator iter = entries.find(handle);
475 if (iter != entries.end()) {
476 hAlloc->FreeHandle(handle);
477 iter->second.hFile.Close();
478 entries.erase(iter);
479 } else {
480 //This shouldn't happen...
481 ERROR_LOG(FILESYS,"Cannot close file that hasn't been opened: %08x", handle);
482 }
483}
484
485bool DirectoryFileSystem::OwnsHandle(u32 handle) {
486 EntryMap::iterator iter = entries.find(handle);
487 return (iter != entries.end());
488}
489
490size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
491 EntryMap::iterator iter = entries.find(handle);
492 if (iter != entries.end())
493 {
494 size_t bytesRead = iter->second.hFile.Read(pointer,size);
495 return bytesRead;
496 } else {
497 //This shouldn't happen...
498 ERROR_LOG(FILESYS,"Cannot read file that hasn't been opened: %08x", handle);
499 return 0;
500 }
501}
502
503size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
504 EntryMap::iterator iter = entries.find(handle);
505 if (iter != entries.end())
506 {
507 size_t bytesWritten = iter->second.hFile.Write(pointer,size);
508 return bytesWritten;
509 } else {
510 //This shouldn't happen...
511 ERROR_LOG(FILESYS,"Cannot write to file that hasn't been opened: %08x", handle);
512 return 0;
513 }
514}
515
516size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
517 EntryMap::iterator iter = entries.find(handle);
518 if (iter != entries.end()) {
519 return iter->second.hFile.Seek(position,type);
520 } else {
521 //This shouldn't happen...
522 ERROR_LOG(FILESYS,"Cannot seek in file that hasn't been opened: %08x", handle);
523 return 0;
524 }
525}
526
527FileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
528 FileInfo x;
529 x.name = filename;
530
531 std::string fullName = GetLocalPath(filename);
532 if (! File::Exists(fullName)) {
533#if HOST_IS_CASE_SENSITIVE
534 if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
535 return x;
536 fullName = GetLocalPath(filename);
537
538 if (! File::Exists(fullName))
539 return x;
540#else
541 return x;
542#endif
543 }
544 x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
545 x.exists = true;
546
547 if (x.type != FILETYPE_DIRECTORY)
548 {
549#ifdef _WIN32
550 struct _stat64i32 s;
551 _wstat64i32(ConvertUTF8ToWString(fullName).c_str(), &s);
552#else
553 struct stat s;
554 stat(fullName.c_str(), &s);
555#endif
556
557 x.size = File::GetSize(fullName);
558 x.access = s.st_mode & 0x1FF;
559 localtime_r((time_t*)&s.st_atime,&x.atime);
560 localtime_r((time_t*)&s.st_ctime,&x.ctime);
561 localtime_r((time_t*)&s.st_mtime,&x.mtime);
562 }
563
564 return x;
565}
566
567bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) {
568 outpath = GetLocalPath(inpath);
569 return true;
570}
571
572#ifdef _WIN32
573#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL
574
575static void tmFromFiletime(tm &dest, FILETIME &src)
576{
577 u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;
578 u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;
579
580 time_t t = (time_t) (from_1970_us / 1000000UL);
581 localtime_r(&t, &dest);
582}
583#endif
584
585std::vector<FileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
586 std::vector<FileInfo> myVector;
587#ifdef _WIN32
588 WIN32_FIND_DATA findData;
589 HANDLE hFind;
590
591 std::string w32path = GetLocalPath(path) + "\\*.*";
592
593 hFind = FindFirstFile(ConvertUTF8ToWString(w32path).c_str(), &findData);
594
595 if (hFind == INVALID_HANDLE_VALUE) {
596 return myVector; //the empty list
597 }
598
599 while (true) {
600 FileInfo entry;
601 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
602 entry.type = FILETYPE_DIRECTORY;
603 else
604 entry.type = FILETYPE_NORMAL;
605
606 // TODO: Make this more correct?
607 entry.access = entry.type == FILETYPE_NORMAL ? 0666 : 0777;
608 // TODO: is this just for .. or all subdirectories? Need to add a directory to the test
609 // to find out. Also why so different than the old test results?
610 if (!wcscmp(findData.cFileName, L"..") )
611 entry.size = 4096;
612 else
613 entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
614 entry.name = ConvertWStringToUTF8(findData.cFileName);
615 tmFromFiletime(entry.atime, findData.ftLastAccessTime);
616 tmFromFiletime(entry.ctime, findData.ftCreationTime);
617 tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
618 myVector.push_back(entry);
619
620 int retval = FindNextFile(hFind, &findData);
621 if (!retval)
622 break;
623 }
624#else
625 dirent *dirp;
626 std::string localPath = GetLocalPath(path);
627 DIR *dp = opendir(localPath.c_str());
628
629#if HOST_IS_CASE_SENSITIVE
630 if(dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) {
631 // May have failed due to case sensitivity, try again
632 localPath = GetLocalPath(path);
633 dp = opendir(localPath.c_str());
634 }
635#endif
636
637 if (dp == NULL) {
638 ERROR_LOG(FILESYS,"Error opening directory %s\n",path.c_str());
639 return myVector;
640 }
641
642 while ((dirp = readdir(dp)) != NULL) {
643 FileInfo entry;
644 struct stat s;
645 std::string fullName = GetLocalPath(path) + "/"+dirp->d_name;
646 stat(fullName.c_str(), &s);
647 if (S_ISDIR(s.st_mode))
648 entry.type = FILETYPE_DIRECTORY;
649 else
650 entry.type = FILETYPE_NORMAL;
651 entry.access = s.st_mode & 0x1FF;
652 entry.name = dirp->d_name;
653 entry.size = s.st_size;
654 localtime_r((time_t*)&s.st_atime,&entry.atime);
655 localtime_r((time_t*)&s.st_ctime,&entry.ctime);
656 localtime_r((time_t*)&s.st_mtime,&entry.mtime);
657 myVector.push_back(entry);
658 }
659 closedir(dp);
660#endif
661 return myVector;
662}
663
664void DirectoryFileSystem::DoState(PointerWrap &p) {
665 if (!entries.empty()) {
666 p.SetError(p.ERROR_WARNING);
667 ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly.");
668 }
669}
diff --git a/src/core/file_sys/directory_file_system.h b/src/core/file_sys/directory_file_system.h
deleted file mode 100644
index 9af2854a2..000000000
--- a/src/core/file_sys/directory_file_system.h
+++ /dev/null
@@ -1,155 +0,0 @@
1// Copyright (c) 2012- PPSSPP Project.
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14
15// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18#pragma once
19
20// TODO: Remove the Windows-specific code, FILE is fine there too.
21
22#include <map>
23
24#include "core/file_sys/file_sys.h"
25
26#ifdef _WIN32
27typedef void * HANDLE;
28#endif
29
30#if defined(__APPLE__)
31
32#if TARGET_OS_IPHONE
33#define HOST_IS_CASE_SENSITIVE 1
34#elif TARGET_IPHONE_SIMULATOR
35#define HOST_IS_CASE_SENSITIVE 0
36#else
37// Mac OSX case sensitivity defaults off, but is user configurable (when
38// creating a filesytem), so assume the worst:
39#define HOST_IS_CASE_SENSITIVE 1
40#endif
41
42#elif defined(_WIN32) || defined(__SYMBIAN32__)
43#define HOST_IS_CASE_SENSITIVE 0
44
45#else // Android, Linux, BSD (and the rest?)
46#define HOST_IS_CASE_SENSITIVE 1
47
48#endif
49
50#if HOST_IS_CASE_SENSITIVE
51enum FixPathCaseBehavior {
52 FPC_FILE_MUST_EXIST, // all path components must exist (rmdir, move from)
53 FPC_PATH_MUST_EXIST, // all except the last one must exist - still tries to fix last one (fopen, move to)
54 FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive)
55};
56
57bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior);
58#endif
59
60struct DirectoryFileHandle
61{
62#ifdef _WIN32
63 HANDLE hFile;
64#else
65 FILE* hFile;
66#endif
67 DirectoryFileHandle()
68 {
69#ifdef _WIN32
70 hFile = (HANDLE)-1;
71#else
72 hFile = 0;
73#endif
74 }
75
76 std::string GetLocalPath(std::string& basePath, std::string localpath);
77 bool Open(std::string& basePath, std::string& fileName, FileAccess access);
78 size_t Read(u8* pointer, s64 size);
79 size_t Write(const u8* pointer, s64 size);
80 size_t Seek(s32 position, FileMove type);
81 void Close();
82};
83
84class DirectoryFileSystem : public IFileSystem {
85public:
86 DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
87 ~DirectoryFileSystem();
88
89 void DoState(PointerWrap &p);
90 std::vector<FileInfo> GetDirListing(std::string path);
91 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
92 void CloseFile(u32 handle);
93 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
94 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
95 size_t SeekFile(u32 handle, s32 position, FileMove type);
96 FileInfo GetFileInfo(std::string filename);
97 bool OwnsHandle(u32 handle);
98
99 bool MkDir(const std::string &dirname);
100 bool RmDir(const std::string &dirname);
101 int RenameFile(const std::string &from, const std::string &to);
102 bool RemoveFile(const std::string &filename);
103 bool GetHostPath(const std::string &inpath, std::string &outpath);
104
105private:
106 struct OpenFileEntry {
107 DirectoryFileHandle hFile;
108 };
109
110 typedef std::map<u32, OpenFileEntry> EntryMap;
111 EntryMap entries;
112 std::string basePath;
113 IHandleAllocator *hAlloc;
114
115 // In case of Windows: Translate slashes, etc.
116 std::string GetLocalPath(std::string localpath);
117};
118
119// VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts.
120// Very inefficient - always load the whole file on open.
121class VFSFileSystem : public IFileSystem {
122public:
123 VFSFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
124 ~VFSFileSystem();
125
126 void DoState(PointerWrap &p);
127 std::vector<FileInfo> GetDirListing(std::string path);
128 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
129 void CloseFile(u32 handle);
130 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
131 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
132 size_t SeekFile(u32 handle, s32 position, FileMove type);
133 FileInfo GetFileInfo(std::string filename);
134 bool OwnsHandle(u32 handle);
135
136 bool MkDir(const std::string &dirname);
137 bool RmDir(const std::string &dirname);
138 int RenameFile(const std::string &from, const std::string &to);
139 bool RemoveFile(const std::string &filename);
140 bool GetHostPath(const std::string &inpath, std::string &outpath);
141
142private:
143 struct OpenFileEntry {
144 u8 *fileData;
145 size_t size;
146 size_t seekPos;
147 };
148
149 typedef std::map<u32, OpenFileEntry> EntryMap;
150 EntryMap entries;
151 std::string basePath;
152 IHandleAllocator *hAlloc;
153
154 std::string GetLocalPath(std::string localpath);
155};
diff --git a/src/core/file_sys/file_sys.h b/src/core/file_sys/file_sys.h
deleted file mode 100644
index bb8503e62..000000000
--- a/src/core/file_sys/file_sys.h
+++ /dev/null
@@ -1,138 +0,0 @@
1// Copyright (c) 2012- PPSSPP Project.
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14
15// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18#pragma once
19
20#include "common/common.h"
21#include "common/chunk_file.h"
22
23enum FileAccess {
24 FILEACCESS_NONE=0,
25 FILEACCESS_READ=1,
26 FILEACCESS_WRITE=2,
27 FILEACCESS_APPEND=4,
28 FILEACCESS_CREATE=8
29};
30
31enum FileMove {
32 FILEMOVE_BEGIN=0,
33 FILEMOVE_CURRENT=1,
34 FILEMOVE_END=2
35};
36
37enum FileType {
38 FILETYPE_NORMAL=1,
39 FILETYPE_DIRECTORY=2
40};
41
42
43class IHandleAllocator {
44public:
45 virtual ~IHandleAllocator() {}
46 virtual u32 GetNewHandle() = 0;
47 virtual void FreeHandle(u32 handle) = 0;
48};
49
50class SequentialHandleAllocator : public IHandleAllocator {
51public:
52 SequentialHandleAllocator() : handle_(1) {}
53 virtual u32 GetNewHandle() { return handle_++; }
54 virtual void FreeHandle(u32 handle) {}
55private:
56 int handle_;
57};
58
59struct FileInfo {
60 FileInfo()
61 : size(0), access(0), exists(false), type(FILETYPE_NORMAL), isOnSectorSystem(false), startSector(0), numSectors(0) {}
62
63 void DoState(PointerWrap &p) {
64 auto s = p.Section("FileInfo", 1);
65 if (!s)
66 return;
67
68 p.Do(name);
69 p.Do(size);
70 p.Do(access);
71 p.Do(exists);
72 p.Do(type);
73 p.Do(atime);
74 p.Do(ctime);
75 p.Do(mtime);
76 p.Do(isOnSectorSystem);
77 p.Do(startSector);
78 p.Do(numSectors);
79 p.Do(sectorSize);
80 }
81
82 std::string name;
83 s64 size;
84 u32 access; //unix 777
85 bool exists;
86 FileType type;
87
88 tm atime;
89 tm ctime;
90 tm mtime;
91
92 bool isOnSectorSystem;
93 u32 startSector;
94 u32 numSectors;
95 u32 sectorSize;
96};
97
98
99class IFileSystem {
100public:
101 virtual ~IFileSystem() {}
102
103 virtual void DoState(PointerWrap &p) = 0;
104 virtual std::vector<FileInfo> GetDirListing(std::string path) = 0;
105 virtual u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) = 0;
106 virtual void CloseFile(u32 handle) = 0;
107 virtual size_t ReadFile(u32 handle, u8 *pointer, s64 size) = 0;
108 virtual size_t WriteFile(u32 handle, const u8 *pointer, s64 size) = 0;
109 virtual size_t SeekFile(u32 handle, s32 position, FileMove type) = 0;
110 virtual FileInfo GetFileInfo(std::string filename) = 0;
111 virtual bool OwnsHandle(u32 handle) = 0;
112 virtual bool MkDir(const std::string &dirname) = 0;
113 virtual bool RmDir(const std::string &dirname) = 0;
114 virtual int RenameFile(const std::string &from, const std::string &to) = 0;
115 virtual bool RemoveFile(const std::string &filename) = 0;
116 virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0;
117};
118
119
120class EmptyFileSystem : public IFileSystem {
121public:
122 virtual void DoState(PointerWrap &p) {}
123 std::vector<FileInfo> GetDirListing(std::string path) {std::vector<FileInfo> vec; return vec;}
124 u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) {return 0;}
125 void CloseFile(u32 handle) {}
126 size_t ReadFile(u32 handle, u8 *pointer, s64 size) {return 0;}
127 size_t WriteFile(u32 handle, const u8 *pointer, s64 size) {return 0;}
128 size_t SeekFile(u32 handle, s32 position, FileMove type) {return 0;}
129 FileInfo GetFileInfo(std::string filename) {FileInfo f; return f;}
130 bool OwnsHandle(u32 handle) {return false;}
131 virtual bool MkDir(const std::string &dirname) {return false;}
132 virtual bool RmDir(const std::string &dirname) {return false;}
133 virtual int RenameFile(const std::string &from, const std::string &to) {return -1;}
134 virtual bool RemoveFile(const std::string &filename) {return false;}
135 virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
136};
137
138
diff --git a/src/core/file_sys/meta_file_system.cpp b/src/core/file_sys/meta_file_system.cpp
deleted file mode 100644
index 4347ff451..000000000
--- a/src/core/file_sys/meta_file_system.cpp
+++ /dev/null
@@ -1,519 +0,0 @@
1// Copyright (c) 2012- PPSSPP Project.
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14
15// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18#include <set>
19#include <algorithm>
20
21#include "common/string_util.h"
22#include "core/file_sys/meta_file_system.h"
23
24static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
25{
26 size_t len = pathString.length();
27 size_t start = 0;
28
29 while (start < len)
30 {
31 size_t i = pathString.find('/', start);
32 if (i == std::string::npos)
33 i = len;
34
35 if (i > start)
36 {
37 std::string component = pathString.substr(start, i - start);
38 if (component != ".")
39 {
40 if (component == "..")
41 {
42 if (vector.size() != 0)
43 {
44 vector.pop_back();
45 }
46 else
47 {
48 // The PSP silently ignores attempts to .. to parent of root directory
49 WARN_LOG(FILESYS, "RealPath: ignoring .. beyond root - root directory is its own parent: \"%s\"", pathString.c_str());
50 }
51 }
52 else
53 {
54 vector.push_back(component);
55 }
56 }
57 }
58
59 start = i + 1;
60 }
61
62 return true;
63}
64
65/*
66 * Changes relative paths to absolute, removes ".", "..", and trailing "/"
67 * "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:")
68 * babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename"
69 */
70static bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
71{
72 size_t inLen = inPath.length();
73 if (inLen == 0)
74 {
75 WARN_LOG(FILESYS, "RealPath: inPath is empty");
76 outPath = currentDirectory;
77 return true;
78 }
79
80 size_t inColon = inPath.find(':');
81 if (inColon + 1 == inLen)
82 {
83 // There's nothing after the colon, e.g. umd0: - this is perfectly valid.
84 outPath = inPath;
85 return true;
86 }
87
88 bool relative = (inColon == std::string::npos);
89
90 std::string prefix, inAfterColon;
91 std::vector<std::string> cmpnts; // path components
92 size_t outPathCapacityGuess = inPath.length();
93
94 if (relative)
95 {
96 size_t curDirLen = currentDirectory.length();
97 if (curDirLen == 0)
98 {
99 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str());
100 return false;
101 }
102
103 size_t curDirColon = currentDirectory.find(':');
104 if (curDirColon == std::string::npos)
105 {
106 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str());
107 return false;
108 }
109 if (curDirColon + 1 == curDirLen)
110 {
111 ERROR_LOG(FILESYS, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str());
112 }
113 else
114 {
115 const std::string curDirAfter = currentDirectory.substr(curDirColon + 1);
116 if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) )
117 {
118 ERROR_LOG(FILESYS,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str());
119 return false;
120 }
121
122 outPathCapacityGuess += curDirLen;
123 }
124
125 prefix = currentDirectory.substr(0, curDirColon + 1);
126 inAfterColon = inPath;
127 }
128 else
129 {
130 prefix = inPath.substr(0, inColon + 1);
131 inAfterColon = inPath.substr(inColon + 1);
132 }
133
134 // Special case: "disc0:" is different from "disc0:/", so keep track of the single slash.
135 if (inAfterColon == "/")
136 {
137 outPath = prefix + inAfterColon;
138 return true;
139 }
140
141 if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) )
142 {
143 WARN_LOG(FILESYS, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
144 return false;
145 }
146
147 outPath.clear();
148 outPath.reserve(outPathCapacityGuess);
149
150 outPath.append(prefix);
151
152 size_t numCmpnts = cmpnts.size();
153 for (size_t i = 0; i < numCmpnts; i++)
154 {
155 outPath.append(1, '/');
156 outPath.append(cmpnts[i]);
157 }
158
159 return true;
160}
161
162IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
163{
164 std::lock_guard<std::recursive_mutex> guard(lock);
165 for (size_t i = 0; i < fileSystems.size(); i++)
166 {
167 if (fileSystems[i].system->OwnsHandle(handle))
168 return fileSystems[i].system; //got it!
169 }
170 //none found?
171 return 0;
172}
173
174bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system)
175{
176 std::lock_guard<std::recursive_mutex> guard(lock);
177 std::string realpath;
178
179 // Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
180 // appears to mean the current directory on the UMD. Let's just assume the current directory.
181 std::string inpath = _inpath;
182 if (strncasecmp(inpath.c_str(), "host0:", strlen("host0:")) == 0) {
183 INFO_LOG(FILESYS, "Host0 path detected, stripping: %s", inpath.c_str());
184 inpath = inpath.substr(strlen("host0:"));
185 }
186
187 const std::string *currentDirectory = &startingDirectory;
188
189 _assert_msg_(FILESYS, false, "must implement equiv of __KernelGetCurThread");
190
191 int currentThread = 0;//__KernelGetCurThread();
192 currentDir_t::iterator it = currentDir.find(currentThread);
193 if (it == currentDir.end())
194 {
195 //Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
196 if (inpath.find(':') == std::string::npos /* means path is relative */)
197 {
198 lastOpenError = -1;//SCE_KERNEL_ERROR_NOCWD;
199 WARN_LOG(FILESYS, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
200 }
201 }
202 else
203 {
204 currentDirectory = &(it->second);
205 }
206
207 if ( RealPath(*currentDirectory, inpath, realpath) )
208 {
209 for (size_t i = 0; i < fileSystems.size(); i++)
210 {
211 size_t prefLen = fileSystems[i].prefix.size();
212 if (strncasecmp(fileSystems[i].prefix.c_str(), realpath.c_str(), prefLen) == 0)
213 {
214 outpath = realpath.substr(prefLen);
215 *system = &(fileSystems[i]);
216
217 INFO_LOG(FILESYS, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str());
218
219 return true;
220 }
221 }
222 }
223
224 DEBUG_LOG(FILESYS, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str());
225 return false;
226}
227
228void MetaFileSystem::Mount(std::string prefix, IFileSystem *system)
229{
230 std::lock_guard<std::recursive_mutex> guard(lock);
231 MountPoint x;
232 x.prefix = prefix;
233 x.system = system;
234 fileSystems.push_back(x);
235}
236
237void MetaFileSystem::Unmount(std::string prefix, IFileSystem *system)
238{
239 std::lock_guard<std::recursive_mutex> guard(lock);
240 MountPoint x;
241 x.prefix = prefix;
242 x.system = system;
243 fileSystems.erase(std::remove(fileSystems.begin(), fileSystems.end(), x), fileSystems.end());
244}
245
246void MetaFileSystem::Shutdown()
247{
248 std::lock_guard<std::recursive_mutex> guard(lock);
249 current = 6;
250
251 // Ownership is a bit convoluted. Let's just delete everything once.
252
253 std::set<IFileSystem *> toDelete;
254 for (size_t i = 0; i < fileSystems.size(); i++) {
255 toDelete.insert(fileSystems[i].system);
256 }
257
258 for (auto iter = toDelete.begin(); iter != toDelete.end(); ++iter)
259 {
260 delete *iter;
261 }
262
263 fileSystems.clear();
264 currentDir.clear();
265 startingDirectory = "";
266}
267
268u32 MetaFileSystem::OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename)
269{
270 std::lock_guard<std::recursive_mutex> guard(lock);
271 u32 h = OpenFile(filename, access, devicename);
272 error = lastOpenError;
273 return h;
274}
275
276u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
277{
278 std::lock_guard<std::recursive_mutex> guard(lock);
279 lastOpenError = 0;
280 std::string of;
281 MountPoint *mount;
282 if (MapFilePath(filename, of, &mount))
283 {
284 return mount->system->OpenFile(of, access, mount->prefix.c_str());
285 }
286 else
287 {
288 return 0;
289 }
290}
291
292FileInfo MetaFileSystem::GetFileInfo(std::string filename)
293{
294 std::lock_guard<std::recursive_mutex> guard(lock);
295 std::string of;
296 IFileSystem *system;
297 if (MapFilePath(filename, of, &system))
298 {
299 return system->GetFileInfo(of);
300 }
301 else
302 {
303 FileInfo bogus; // TODO
304 return bogus;
305 }
306}
307
308bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
309{
310 std::lock_guard<std::recursive_mutex> guard(lock);
311 std::string of;
312 IFileSystem *system;
313 if (MapFilePath(inpath, of, &system)) {
314 return system->GetHostPath(of, outpath);
315 } else {
316 return false;
317 }
318}
319
320std::vector<FileInfo> MetaFileSystem::GetDirListing(std::string path)
321{
322 std::lock_guard<std::recursive_mutex> guard(lock);
323 std::string of;
324 IFileSystem *system;
325 if (MapFilePath(path, of, &system))
326 {
327 return system->GetDirListing(of);
328 }
329 else
330 {
331 std::vector<FileInfo> empty;
332 return empty;
333 }
334}
335
336void MetaFileSystem::ThreadEnded(int threadID)
337{
338 std::lock_guard<std::recursive_mutex> guard(lock);
339 currentDir.erase(threadID);
340}
341
342int MetaFileSystem::ChDir(const std::string &dir)
343{
344 std::lock_guard<std::recursive_mutex> guard(lock);
345 // Retain the old path and fail if the arg is 1023 bytes or longer.
346 if (dir.size() >= 1023)
347 return -1;//SCE_KERNEL_ERROR_NAMETOOLONG;
348
349 _assert_msg_(FILESYS, false, "must implement equiv of __KernelGetCurThread");
350
351 int curThread = 0; //__KernelGetCurThread();
352
353 std::string of;
354 MountPoint *mountPoint;
355 if (MapFilePath(dir, of, &mountPoint))
356 {
357 currentDir[curThread] = mountPoint->prefix + of;
358 return 0;
359 }
360 else
361 {
362 for (size_t i = 0; i < fileSystems.size(); i++)
363 {
364 const std::string &prefix = fileSystems[i].prefix;
365 if (strncasecmp(prefix.c_str(), dir.c_str(), prefix.size()) == 0)
366 {
367 // The PSP is completely happy with invalid current dirs as long as they have a valid device.
368 WARN_LOG(FILESYS, "ChDir failed to map path \"%s\", saving as current directory anyway", dir.c_str());
369 currentDir[curThread] = dir;
370 return 0;
371 }
372 }
373
374 WARN_LOG(FILESYS, "ChDir failed to map device for \"%s\", failing", dir.c_str());
375 return -1;//SCE_KERNEL_ERROR_NODEV;
376 }
377}
378
379bool MetaFileSystem::MkDir(const std::string &dirname)
380{
381 std::lock_guard<std::recursive_mutex> guard(lock);
382 std::string of;
383 IFileSystem *system;
384 if (MapFilePath(dirname, of, &system))
385 {
386 return system->MkDir(of);
387 }
388 else
389 {
390 return false;
391 }
392}
393
394bool MetaFileSystem::RmDir(const std::string &dirname)
395{
396 std::lock_guard<std::recursive_mutex> guard(lock);
397 std::string of;
398 IFileSystem *system;
399 if (MapFilePath(dirname, of, &system))
400 {
401 return system->RmDir(of);
402 }
403 else
404 {
405 return false;
406 }
407}
408
409int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
410{
411 std::lock_guard<std::recursive_mutex> guard(lock);
412 std::string of;
413 std::string rf;
414 IFileSystem *osystem;
415 IFileSystem *rsystem = NULL;
416 if (MapFilePath(from, of, &osystem))
417 {
418 // If it's a relative path, it seems to always use from's filesystem.
419 if (to.find(":/") != to.npos)
420 {
421 if (!MapFilePath(to, rf, &rsystem))
422 return -1;
423 }
424 else
425 {
426 rf = to;
427 rsystem = osystem;
428 }
429
430 if (osystem != rsystem)
431 return -1;//SCE_KERNEL_ERROR_XDEV;
432
433 return osystem->RenameFile(of, rf);
434 }
435 else
436 {
437 return -1;
438 }
439}
440
441bool MetaFileSystem::RemoveFile(const std::string &filename)
442{
443 std::lock_guard<std::recursive_mutex> guard(lock);
444 std::string of;
445 IFileSystem *system;
446 if (MapFilePath(filename, of, &system))
447 {
448 return system->RemoveFile(of);
449 }
450 else
451 {
452 return false;
453 }
454}
455
456void MetaFileSystem::CloseFile(u32 handle)
457{
458 std::lock_guard<std::recursive_mutex> guard(lock);
459 IFileSystem *sys = GetHandleOwner(handle);
460 if (sys)
461 sys->CloseFile(handle);
462}
463
464size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
465{
466 std::lock_guard<std::recursive_mutex> guard(lock);
467 IFileSystem *sys = GetHandleOwner(handle);
468 if (sys)
469 return sys->ReadFile(handle,pointer,size);
470 else
471 return 0;
472}
473
474size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
475{
476 std::lock_guard<std::recursive_mutex> guard(lock);
477 IFileSystem *sys = GetHandleOwner(handle);
478 if (sys)
479 return sys->WriteFile(handle,pointer,size);
480 else
481 return 0;
482}
483
484size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
485{
486 std::lock_guard<std::recursive_mutex> guard(lock);
487 IFileSystem *sys = GetHandleOwner(handle);
488 if (sys)
489 return sys->SeekFile(handle,position,type);
490 else
491 return 0;
492}
493
494void MetaFileSystem::DoState(PointerWrap &p)
495{
496 std::lock_guard<std::recursive_mutex> guard(lock);
497
498 auto s = p.Section("MetaFileSystem", 1);
499 if (!s)
500 return;
501
502 p.Do(current);
503
504 // Save/load per-thread current directory map
505 p.Do(currentDir);
506
507 u32 n = (u32) fileSystems.size();
508 p.Do(n);
509 if (n != (u32) fileSystems.size())
510 {
511 p.SetError(p.ERROR_FAILURE);
512 ERROR_LOG(FILESYS, "Savestate failure: number of filesystems doesn't match.");
513 return;
514 }
515
516 for (u32 i = 0; i < n; ++i)
517 fileSystems[i].system->DoState(p);
518}
519
diff --git a/src/core/file_sys/meta_file_system.h b/src/core/file_sys/meta_file_system.h
deleted file mode 100644
index f358d8d5c..000000000
--- a/src/core/file_sys/meta_file_system.h
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright (c) 2012- PPSSPP Project.
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14
15// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18#pragma once
19
20#include "common/std_mutex.h"
21
22#include "core/file_sys/file_sys.h"
23
24class MetaFileSystem : public IHandleAllocator, public IFileSystem
25{
26private:
27 u32 current;
28 struct MountPoint
29 {
30 std::string prefix;
31 IFileSystem *system;
32
33 bool operator == (const MountPoint &other) const
34 {
35 return prefix == other.prefix && system == other.system;
36 }
37 };
38 std::vector<MountPoint> fileSystems;
39
40 typedef std::map<int, std::string> currentDir_t;
41 currentDir_t currentDir;
42
43 std::string startingDirectory;
44 int lastOpenError;
45 std::recursive_mutex lock;
46
47public:
48 MetaFileSystem()
49 {
50 current = 6; // what?
51 }
52
53 void Mount(std::string prefix, IFileSystem *system);
54 void Unmount(std::string prefix, IFileSystem *system);
55
56 void ThreadEnded(int threadID);
57
58 void Shutdown();
59
60 u32 GetNewHandle() {return current++;}
61 void FreeHandle(u32 handle) {}
62
63 virtual void DoState(PointerWrap &p);
64
65 IFileSystem *GetHandleOwner(u32 handle);
66 bool MapFilePath(const std::string &inpath, std::string &outpath, MountPoint **system);
67
68 inline bool MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system)
69 {
70 MountPoint *mountPoint;
71 if (MapFilePath(_inpath, outpath, &mountPoint))
72 {
73 *system = mountPoint->system;
74 return true;
75 }
76
77 return false;
78 }
79
80 // Only possible if a file system is a DirectoryFileSystem or similar.
81 bool GetHostPath(const std::string &inpath, std::string &outpath);
82
83 std::vector<FileInfo> GetDirListing(std::string path);
84 u32 OpenFile(std::string filename, FileAccess access, const char *devicename = NULL);
85 u32 OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename = NULL);
86 void CloseFile(u32 handle);
87 size_t ReadFile(u32 handle, u8 *pointer, s64 size);
88 size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
89 size_t SeekFile(u32 handle, s32 position, FileMove type);
90 FileInfo GetFileInfo(std::string filename);
91 bool OwnsHandle(u32 handle) {return false;}
92 inline size_t GetSeekPos(u32 handle)
93 {
94 return SeekFile(handle, 0, FILEMOVE_CURRENT);
95 }
96
97 virtual int ChDir(const std::string &dir);
98
99 virtual bool MkDir(const std::string &dirname);
100 virtual bool RmDir(const std::string &dirname);
101 virtual int RenameFile(const std::string &from, const std::string &to);
102 virtual bool RemoveFile(const std::string &filename);
103
104 // TODO: void IoCtl(...)
105
106 void SetStartingDirectory(const std::string &dir) {
107 std::lock_guard<std::recursive_mutex> guard(lock);
108 startingDirectory = dir;
109 }
110};
diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h
index da396a3e6..fa01b5cdb 100644
--- a/src/core/hle/config_mem.h
+++ b/src/core/hle/config_mem.h
@@ -1,10 +1,10 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7// Configuration memory stores various hardware/kernel configuration settings. This memory page is 7// Configuration memory stores various hardware/kernel configuration settings. This memory page is
8// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/ 8// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
9// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm 9// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
10// putting this as a subset of HLE for now. 10// putting this as a subset of HLE for now.
@@ -16,6 +16,6 @@
16namespace ConfigMem { 16namespace ConfigMem {
17 17
18template <typename T> 18template <typename T>
19inline void Read(T &var, const u32 addr); 19void Read(T &var, const u32 addr);
20 20
21} // namespace 21} // namespace
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0bed78653..ea603a1bb 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -39,9 +39,16 @@ template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
39 RETURN(retval); 39 RETURN(retval);
40} 40}
41 41
42// TODO(bunnei): Is this correct? Probably not 42// TODO(bunnei): Is this correct? Probably not - Last parameter looks wrong for ArbitrateAddress
43template<s32 func(u32, u32, u32, u32, s64)> void Wrap() { 43template<s32 func(u32, u32, u32, u32, s64)> void Wrap() {
44 RETURN(func(PARAM(5), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(4) << 32) | PARAM(0)))); 44 RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))));
45}
46
47template<s32 func(u32*)> void Wrap(){
48 u32 param_1 = 0;
49 u32 retval = func(&param_1);
50 Core::g_app_core->SetReg(1, param_1);
51 RETURN(retval);
45} 52}
46 53
47template<s32 func(u32, s64)> void Wrap() { 54template<s32 func(u32, s64)> void Wrap() {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
new file mode 100644
index 000000000..61717bbe4
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -0,0 +1,87 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6
7#include "core/mem_map.h"
8
9#include "core/hle/hle.h"
10#include "core/hle/kernel/address_arbiter.h"
11#include "core/hle/kernel/thread.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Kernel namespace
15
16namespace Kernel {
17
18class AddressArbiter : public Object {
19public:
20 const char* GetTypeName() const { return "Arbiter"; }
21 const char* GetName() const { return name.c_str(); }
22
23 static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; }
24 Kernel::HandleType GetHandleType() const { return HandleType::AddressArbiter; }
25
26 std::string name; ///< Name of address arbiter object (optional)
27
28 /**
29 * Wait for kernel object to synchronize
30 * @param wait Boolean wait set if current thread should wait as a result of sync operation
31 * @return Result of operation, 0 on success, otherwise error code
32 */
33 Result WaitSynchronization(bool* wait) {
34 // TODO(bunnei): ImplementMe
35 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
36 return 0;
37 }
38};
39
40////////////////////////////////////////////////////////////////////////////////////////////////////
41
42/// Arbitrate an address
43Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
44 switch (type) {
45
46 // Signal thread(s) waiting for arbitrate address...
47 case ArbitrationType::Signal:
48 // Negative value means resume all threads
49 if (value < 0) {
50 ArbitrateAllThreads(handle, address);
51 } else {
52 // Resume first N threads
53 for(int i = 0; i < value; i++)
54 ArbitrateHighestPriorityThread(handle, address);
55 }
56 HLE::Reschedule(__func__);
57
58 // Wait current thread (acquire the arbiter)...
59 case ArbitrationType::WaitIfLessThan:
60 if ((s32)Memory::Read32(address) <= value) {
61 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
62 HLE::Reschedule(__func__);
63 }
64
65 default:
66 ERROR_LOG(KERNEL, "unknown type=%d", type);
67 return -1;
68 }
69 return 0;
70}
71
72/// Create an address arbiter
73AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
74 AddressArbiter* address_arbiter = new AddressArbiter;
75 handle = Kernel::g_object_pool.Create(address_arbiter);
76 address_arbiter->name = name;
77 return address_arbiter;
78}
79
80/// Create an address arbiter
81Handle CreateAddressArbiter(const std::string& name) {
82 Handle handle;
83 CreateAddressArbiter(handle, name);
84 return handle;
85}
86
87} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
new file mode 100644
index 000000000..a483fe466
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -0,0 +1,36 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11// Address arbiters are an underlying kernel synchronization object that can be created/used via
12// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
13// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
14// semphores.
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// Kernel namespace
18
19namespace Kernel {
20
21/// Address arbitration types
22enum class ArbitrationType : u32 {
23 Signal,
24 WaitIfLessThan,
25 DecrementAndWaitIfLessThan,
26 WaitIfLessThanWithTimeout,
27 DecrementAndWaitIfLessThanWithTimeout,
28};
29
30/// Arbitrate an address
31Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
32
33/// Create an address arbiter
34Handle CreateAddressArbiter(const std::string& name = "Unknown");
35
36} // namespace FileSys
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
new file mode 100644
index 000000000..76b2520da
--- /dev/null
+++ b/src/core/hle/kernel/archive.cpp
@@ -0,0 +1,157 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6
7#include "core/file_sys/archive.h"
8#include "core/hle/service/service.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/archive.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// Kernel namespace
14
15namespace Kernel {
16
17// Command to access archive file
18enum class FileCommand : u32 {
19 Dummy1 = 0x000100C6,
20 Control = 0x040100C4,
21 OpenSubFile = 0x08010100,
22 Read = 0x080200C2,
23 Write = 0x08030102,
24 GetSize = 0x08040000,
25 SetSize = 0x08050080,
26 GetAttributes = 0x08060000,
27 SetAttributes = 0x08070040,
28 Close = 0x08080000,
29 Flush = 0x08090000,
30};
31
32class Archive : public Object {
33public:
34 const char* GetTypeName() const { return "Archive"; }
35 const char* GetName() const { return name.c_str(); }
36
37 static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
38 Kernel::HandleType GetHandleType() const { return HandleType::Archive; }
39
40 std::string name; ///< Name of archive (optional)
41 FileSys::Archive* backend; ///< Archive backend interface
42
43 /**
44 * Synchronize kernel object
45 * @param wait Boolean wait set if current thread should wait as a result of sync operation
46 * @return Result of operation, 0 on success, otherwise error code
47 */
48 Result SyncRequest(bool* wait) {
49 u32* cmd_buff = Service::GetCommandBuffer();
50 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
51 switch (cmd) {
52
53 // Read from archive...
54 case FileCommand::Read:
55 {
56 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
57 u32 length = cmd_buff[3];
58 u32 address = cmd_buff[5];
59 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
60 break;
61 }
62
63 // Unknown command...
64 default:
65 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
66 return -1;
67 }
68 cmd_buff[1] = 0; // No error
69 return 0;
70 }
71
72 /**
73 * Wait for kernel object to synchronize
74 * @param wait Boolean wait set if current thread should wait as a result of sync operation
75 * @return Result of operation, 0 on success, otherwise error code
76 */
77 Result WaitSynchronization(bool* wait) {
78 // TODO(bunnei): ImplementMe
79 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
80 return 0;
81 }
82};
83
84////////////////////////////////////////////////////////////////////////////////////////////////////
85
86std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
87
88/**
89 * Opens an archive
90 * @param id_code IdCode of the archive to open
91 * @return Handle to archive if it exists, otherwise a null handle (0)
92 */
93Handle OpenArchive(FileSys::Archive::IdCode id_code) {
94 auto itr = g_archive_map.find(id_code);
95 if (itr == g_archive_map.end()) {
96 return 0;
97 }
98 return itr->second;
99}
100
101/**
102 * Mounts an archive
103 * @param archive Pointer to the archive to mount
104 * @return Result of operation, 0 on success, otherwise error code
105 */
106Result MountArchive(Archive* archive) {
107 FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
108 if (0 != OpenArchive(id_code)) {
109 ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
110 return -1;
111 }
112 g_archive_map[id_code] = archive->GetHandle();
113 INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName());
114 return 0;
115}
116
117/**
118 * Creates an Archive
119 * @param handle Handle to newly created archive object
120 * @param backend File system backend interface to the archive
121 * @param name Optional name of Archive
122 * @return Newly created Archive object
123 */
124Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
125 Archive* archive = new Archive;
126 handle = Kernel::g_object_pool.Create(archive);
127 archive->name = name;
128 archive->backend = backend;
129
130 MountArchive(archive);
131
132 return archive;
133}
134
135/**
136 * Creates an Archive
137 * @param backend File system backend interface to the archive
138 * @param name Optional name of Archive
139 * @return Handle to newly created Archive object
140 */
141Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
142 Handle handle;
143 Archive* archive = CreateArchive(handle, backend, name);
144 return handle;
145}
146
147/// Initialize archives
148void ArchiveInit() {
149 g_archive_map.clear();
150}
151
152/// Shutdown archives
153void ArchiveShutdown() {
154 g_archive_map.clear();
155}
156
157} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
new file mode 100644
index 000000000..3758e7061
--- /dev/null
+++ b/src/core/hle/kernel/archive.h
@@ -0,0 +1,38 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10#include "core/file_sys/archive.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// Kernel namespace
14
15namespace Kernel {
16
17/**
18 * Opens an archive
19 * @param id_code IdCode of the archive to open
20 * @return Handle to archive if it exists, otherwise a null handle (0)
21 */
22Handle OpenArchive(FileSys::Archive::IdCode id_code);
23
24/**
25 * Creates an Archive
26 * @param backend File system backend interface to the archive
27 * @param name Optional name of Archive
28 * @return Handle to newly created Archive object
29 */
30Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
31
32/// Initialize archives
33void ArchiveInit();
34
35/// Shutdown archives
36void ArchiveShutdown();
37
38} // namespace FileSys
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index cda183add..7d9bd261e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -9,6 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
12#include "core/hle/kernel/archive.h"
12 13
13namespace Kernel { 14namespace Kernel {
14 15
@@ -133,11 +134,13 @@ Object* ObjectPool::CreateByIDType(int type) {
133/// Initialize the kernel 134/// Initialize the kernel
134void Init() { 135void Init() {
135 Kernel::ThreadingInit(); 136 Kernel::ThreadingInit();
137 Kernel::ArchiveInit();
136} 138}
137 139
138/// Shutdown the kernel 140/// Shutdown the kernel
139void Shutdown() { 141void Shutdown() {
140 Kernel::ThreadingShutdown(); 142 Kernel::ThreadingShutdown();
143 Kernel::ArchiveShutdown();
141 144
142 g_object_pool.Clear(); // Free all kernel objects 145 g_object_pool.Clear(); // Free all kernel objects
143} 146}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3f15da0ac..d9afcdd25 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -26,9 +26,10 @@ enum class HandleType : u32 {
26 Redirection = 6, 26 Redirection = 6,
27 Thread = 7, 27 Thread = 7,
28 Process = 8, 28 Process = 8,
29 Arbiter = 9, 29 AddressArbiter = 9,
30 File = 10, 30 File = 10,
31 Semaphore = 11, 31 Semaphore = 11,
32 Archive = 12,
32}; 33};
33 34
34enum { 35enum {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
new file mode 100644
index 000000000..52823048f
--- /dev/null
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -0,0 +1,105 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6
7#include "core/mem_map.h"
8#include "core/hle/kernel/shared_memory.h"
9
10namespace Kernel {
11
12class SharedMemory : public Object {
13public:
14 const char* GetTypeName() const { return "SharedMemory"; }
15
16 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
17 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::SharedMemory; }
18
19 /**
20 * Wait for kernel object to synchronize
21 * @param wait Boolean wait set if current thread should wait as a result of sync operation
22 * @return Result of operation, 0 on success, otherwise error code
23 */
24 Result WaitSynchronization(bool* wait) {
25 // TODO(bunnei): ImplementMe
26 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
27 return 0;
28 }
29
30 u32 base_address; ///< Address of shared memory block in RAM
31 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
32 MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
33 std::string name; ///< Name of shared memory object (optional)
34};
35
36////////////////////////////////////////////////////////////////////////////////////////////////////
37
38/**
39 * Creates a shared memory object
40 * @param handle Handle of newly created shared memory object
41 * @param name Name of shared memory object
42 * @return Pointer to newly created shared memory object
43 */
44SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
45 SharedMemory* shared_memory = new SharedMemory;
46 handle = Kernel::g_object_pool.Create(shared_memory);
47 shared_memory->name = name;
48 return shared_memory;
49}
50
51/**
52 * Creates a shared memory object
53 * @param name Optional name of shared memory object
54 * @return Handle of newly created shared memory object
55 */
56Handle CreateSharedMemory(const std::string& name) {
57 Handle handle;
58 CreateSharedMemory(handle, name);
59 return handle;
60}
61
62/**
63 * Maps a shared memory block to an address in system memory
64 * @param handle Shared memory block handle
65 * @param address Address in system memory to map shared memory block to
66 * @param permissions Memory block map permissions (specified by SVC field)
67 * @param other_permissions Memory block map other permissions (specified by SVC field)
68 * @return Result of operation, 0 on success, otherwise error code
69 */
70Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
71 MemoryPermission other_permissions) {
72
73 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
74 ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
75 handle);
76 return -1;
77 }
78 SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
79 _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
80
81 shared_memory->base_address = address;
82 shared_memory->permissions = permissions;
83 shared_memory->other_permissions = other_permissions;
84
85 return 0;
86}
87
88/**
89 * Gets a pointer to the shared memory block
90 * @param handle Shared memory block handle
91 * @param offset Offset from the start of the shared memory block to get pointer
92 * @return Pointer to the shared memory block from the specified offset
93 */
94u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
95 SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
96 _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
97
98 if (0 != shared_memory->base_address)
99 return Memory::GetPointer(shared_memory->base_address + offset);
100
101 ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
102 return nullptr;
103}
104
105} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
new file mode 100644
index 000000000..5312b8854
--- /dev/null
+++ b/src/core/hle/kernel/shared_memory.h
@@ -0,0 +1,48 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/// Permissions for mapped shared memory blocks
14enum class MemoryPermission : u32 {
15 None = 0,
16 Read = (1u << 0),
17 Write = (1u << 1),
18 ReadWrite = (Read | Write),
19 DontCare = (1u << 28)
20};
21
22/**
23 * Creates a shared memory object
24 * @param name Optional name of shared memory object
25 * @return Handle of newly created shared memory object
26 */
27Handle CreateSharedMemory(const std::string& name="Unknown");
28
29/**
30 * Maps a shared memory block to an address in system memory
31 * @param handle Shared memory block handle
32 * @param address Address in system memory to map shared memory block to
33 * @param permissions Memory block map permissions (specified by SVC field)
34 * @param other_permissions Memory block map other permissions (specified by SVC field)
35 * @return Result of operation, 0 on success, otherwise error code
36 */
37Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
38 MemoryPermission other_permissions);
39
40/**
41 * Gets a pointer to the shared memory block
42 * @param handle Shared memory block handle
43 * @param offset Offset from the start of the shared memory block to get pointer
44 * @return Pointer to the shared memory block from the specified offset
45 */
46u8* GetSharedMemoryPointer(Handle handle, u32 offset);
47
48} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index ab5a5559e..86bbf29d0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -188,6 +188,43 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
188 } 188 }
189} 189}
190 190
191/// Arbitrate the highest priority thread that is waiting
192Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
193 Handle highest_priority_thread = 0;
194 s32 priority = THREADPRIO_LOWEST;
195
196 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
197 for (const auto& handle : g_thread_queue) {
198
199 // TODO(bunnei): Verify arbiter address...
200 if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
201 continue;
202
203 Thread* thread = g_object_pool.GetFast<Thread>(handle);
204 if(thread->current_priority <= priority) {
205 highest_priority_thread = handle;
206 priority = thread->current_priority;
207 }
208 }
209 // If a thread was arbitrated, resume it
210 if (0 != highest_priority_thread)
211 ResumeThreadFromWait(highest_priority_thread);
212
213 return highest_priority_thread;
214}
215
216/// Arbitrate all threads currently waiting
217void ArbitrateAllThreads(u32 arbiter, u32 address) {
218
219 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
220 for (const auto& handle : g_thread_queue) {
221
222 // TODO(bunnei): Verify arbiter address...
223 if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
224 ResumeThreadFromWait(handle);
225 }
226}
227
191/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) 228/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
192void CallThread(Thread* t) { 229void CallThread(Thread* t) {
193 // Stop waiting 230 // Stop waiting
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 04914ba90..f2bfdfa1a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -39,6 +39,7 @@ enum WaitType {
39 WAITTYPE_VBLANK, 39 WAITTYPE_VBLANK,
40 WAITTYPE_MUTEX, 40 WAITTYPE_MUTEX,
41 WAITTYPE_SYNCH, 41 WAITTYPE_SYNCH,
42 WAITTYPE_ARB,
42}; 43};
43 44
44namespace Kernel { 45namespace Kernel {
@@ -59,6 +60,12 @@ void StopThread(Handle thread, const char* reason);
59/// Resumes a thread from waiting by marking it as "ready" 60/// Resumes a thread from waiting by marking it as "ready"
60void ResumeThreadFromWait(Handle handle); 61void ResumeThreadFromWait(Handle handle);
61 62
63/// Arbitrate the highest priority thread that is waiting
64Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address);
65
66/// Arbitrate all threads currently waiting...
67void ArbitrateAllThreads(u32 arbiter, u32 address);
68
62/// Gets the current thread handle 69/// Gets the current thread handle
63Handle GetCurrentThreadHandle(); 70Handle GetCurrentThreadHandle();
64 71
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp
index a0012b5dd..e97e7dbf7 100644
--- a/src/core/hle/service/apt.cpp
+++ b/src/core/hle/service/apt.cpp
@@ -15,9 +15,16 @@
15 15
16namespace APT_U { 16namespace APT_U {
17 17
18/// Signals used by APT functions
19enum class SignalType : u32 {
20 None = 0x0,
21 AppJustStarted = 0x1,
22 ReturningToApp = 0xB,
23 ExitingApp = 0xC,
24};
25
18void Initialize(Service::Interface* self) { 26void Initialize(Service::Interface* self) {
19 u32* cmd_buff = Service::GetCommandBuffer(); 27 u32* cmd_buff = Service::GetCommandBuffer();
20 DEBUG_LOG(KERNEL, "called");
21 28
22 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle 29 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
23 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle 30 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
@@ -26,6 +33,7 @@ void Initialize(Service::Interface* self) {
26 Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event 33 Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
27 34
28 cmd_buff[1] = 0; // No error 35 cmd_buff[1] = 0; // No error
36 DEBUG_LOG(KERNEL, "called");
29} 37}
30 38
31void GetLockHandle(Service::Interface* self) { 39void GetLockHandle(Service::Interface* self) {
@@ -40,15 +48,29 @@ void Enable(Service::Interface* self) {
40 u32* cmd_buff = Service::GetCommandBuffer(); 48 u32* cmd_buff = Service::GetCommandBuffer();
41 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? 49 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
42 cmd_buff[1] = 0; // No error 50 cmd_buff[1] = 0; // No error
43 ERROR_LOG(KERNEL, "(UNIMPEMENTED) called unk=0x%08X", unk); 51 WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
44} 52}
45 53
46void InquireNotification(Service::Interface* self) { 54void InquireNotification(Service::Interface* self) {
47 u32* cmd_buff = Service::GetCommandBuffer(); 55 u32* cmd_buff = Service::GetCommandBuffer();
48 u32 app_id = cmd_buff[2]; 56 u32 app_id = cmd_buff[2];
49 cmd_buff[1] = 0; // No error 57 cmd_buff[1] = 0; // No error
50 cmd_buff[3] = 0; // Signal type 58 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
51 ERROR_LOG(KERNEL, "(UNIMPEMENTED) called app_id=0x%08X", app_id); 59 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
60}
61
62void ReceiveParameter(Service::Interface* self) {
63 u32* cmd_buff = Service::GetCommandBuffer();
64 u32 app_id = cmd_buff[1];
65 u32 buffer_size = cmd_buff[2];
66 cmd_buff[1] = 0; // No error
67 cmd_buff[2] = 0;
68 cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
69 cmd_buff[4] = 0x10;
70 cmd_buff[5] = 0;
71 cmd_buff[6] = 0;
72 cmd_buff[7] = 0;
73 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
52} 74}
53 75
54const Interface::FunctionInfo FunctionTable[] = { 76const Interface::FunctionInfo FunctionTable[] = {
@@ -63,73 +85,73 @@ const Interface::FunctionInfo FunctionTable[] = {
63 {0x00090040, nullptr, "IsRegistered"}, 85 {0x00090040, nullptr, "IsRegistered"},
64 {0x000A0040, nullptr, "GetAttribute"}, 86 {0x000A0040, nullptr, "GetAttribute"},
65 {0x000B0040, InquireNotification, "InquireNotification"}, 87 {0x000B0040, InquireNotification, "InquireNotification"},
66 {0x000C0104, nullptr, "SendParameter"}, 88 {0x000C0104, nullptr, "SendParameter"},
67 {0x000D0080, nullptr, "ReceiveParameter"}, 89 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
68 {0x000E0080, nullptr, "GlanceParameter"}, 90 {0x000E0080, nullptr, "GlanceParameter"},
69 {0x000F0100, nullptr, "CancelParameter"}, 91 {0x000F0100, nullptr, "CancelParameter"},
70 {0x001000C2, nullptr, "DebugFunc"}, 92 {0x001000C2, nullptr, "DebugFunc"},
71 {0x001100C0, nullptr, "MapProgramIdForDebug"}, 93 {0x001100C0, nullptr, "MapProgramIdForDebug"},
72 {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, 94 {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
73 {0x00130000, nullptr, "GetPreparationState"}, 95 {0x00130000, nullptr, "GetPreparationState"},
74 {0x00140040, nullptr, "SetPreparationState"}, 96 {0x00140040, nullptr, "SetPreparationState"},
75 {0x00150140, nullptr, "PrepareToStartApplication"}, 97 {0x00150140, nullptr, "PrepareToStartApplication"},
76 {0x00160040, nullptr, "PreloadLibraryApplet"}, 98 {0x00160040, nullptr, "PreloadLibraryApplet"},
77 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, 99 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
78 {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, 100 {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
79 {0x00190040, nullptr, "PrepareToStartSystemApplet"}, 101 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
80 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, 102 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
81 {0x001B00C4, nullptr, "StartApplication"}, 103 {0x001B00C4, nullptr, "StartApplication"},
82 {0x001C0000, nullptr, "WakeupApplication"}, 104 {0x001C0000, nullptr, "WakeupApplication"},
83 {0x001D0000, nullptr, "CancelApplication"}, 105 {0x001D0000, nullptr, "CancelApplication"},
84 {0x001E0084, nullptr, "StartLibraryApplet"}, 106 {0x001E0084, nullptr, "StartLibraryApplet"},
85 {0x001F0084, nullptr, "StartSystemApplet"}, 107 {0x001F0084, nullptr, "StartSystemApplet"},
86 {0x00200044, nullptr, "StartNewestHomeMenu"}, 108 {0x00200044, nullptr, "StartNewestHomeMenu"},
87 {0x00210000, nullptr, "OrderToCloseApplication"}, 109 {0x00210000, nullptr, "OrderToCloseApplication"},
88 {0x00220040, nullptr, "PrepareToCloseApplication"}, 110 {0x00220040, nullptr, "PrepareToCloseApplication"},
89 {0x00230040, nullptr, "PrepareToJumpToApplication"}, 111 {0x00230040, nullptr, "PrepareToJumpToApplication"},
90 {0x00240044, nullptr, "JumpToApplication"}, 112 {0x00240044, nullptr, "JumpToApplication"},
91 {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, 113 {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
92 {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, 114 {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
93 {0x00270044, nullptr, "CloseApplication"}, 115 {0x00270044, nullptr, "CloseApplication"},
94 {0x00280044, nullptr, "CloseLibraryApplet"}, 116 {0x00280044, nullptr, "CloseLibraryApplet"},
95 {0x00290044, nullptr, "CloseSystemApplet"}, 117 {0x00290044, nullptr, "CloseSystemApplet"},
96 {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, 118 {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
97 {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, 119 {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
98 {0x002C0044, nullptr, "JumpToHomeMenu"}, 120 {0x002C0044, nullptr, "JumpToHomeMenu"},
99 {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, 121 {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
100 {0x002E0044, nullptr, "LeaveHomeMenu"}, 122 {0x002E0044, nullptr, "LeaveHomeMenu"},
101 {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, 123 {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
102 {0x00300044, nullptr, "LeaveResidentApplet"}, 124 {0x00300044, nullptr, "LeaveResidentApplet"},
103 {0x00310100, nullptr, "PrepareToDoApplicationJump"}, 125 {0x00310100, nullptr, "PrepareToDoApplicationJump"},
104 {0x00320084, nullptr, "DoApplicationJump"}, 126 {0x00320084, nullptr, "DoApplicationJump"},
105 {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, 127 {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
106 {0x00340084, nullptr, "SendDeliverArg"}, 128 {0x00340084, nullptr, "SendDeliverArg"},
107 {0x00350080, nullptr, "ReceiveDeliverArg"}, 129 {0x00350080, nullptr, "ReceiveDeliverArg"},
108 {0x00360040, nullptr, "LoadSysMenuArg"}, 130 {0x00360040, nullptr, "LoadSysMenuArg"},
109 {0x00370042, nullptr, "StoreSysMenuArg"}, 131 {0x00370042, nullptr, "StoreSysMenuArg"},
110 {0x00380040, nullptr, "PreloadResidentApplet"}, 132 {0x00380040, nullptr, "PreloadResidentApplet"},
111 {0x00390040, nullptr, "PrepareToStartResidentApplet"}, 133 {0x00390040, nullptr, "PrepareToStartResidentApplet"},
112 {0x003A0044, nullptr, "StartResidentApplet"}, 134 {0x003A0044, nullptr, "StartResidentApplet"},
113 {0x003B0040, nullptr, "CancelLibraryApplet"}, 135 {0x003B0040, nullptr, "CancelLibraryApplet"},
114 {0x003C0042, nullptr, "SendDspSleep"}, 136 {0x003C0042, nullptr, "SendDspSleep"},
115 {0x003D0042, nullptr, "SendDspWakeUp"}, 137 {0x003D0042, nullptr, "SendDspWakeUp"},
116 {0x003E0080, nullptr, "ReplySleepQuery"}, 138 {0x003E0080, nullptr, "ReplySleepQuery"},
117 {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, 139 {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
118 {0x00400042, nullptr, "SendCaptureBufferInfo"}, 140 {0x00400042, nullptr, "SendCaptureBufferInfo"},
119 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, 141 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
120 {0x00420080, nullptr, "SleepSystem"}, 142 {0x00420080, nullptr, "SleepSystem"},
121 {0x00430040, nullptr, "NotifyToWait"}, 143 {0x00430040, nullptr, "NotifyToWait"},
122 {0x00440000, nullptr, "GetSharedFont"}, 144 {0x00440000, nullptr, "GetSharedFont"},
123 {0x00450040, nullptr, "GetWirelessRebootInfo"}, 145 {0x00450040, nullptr, "GetWirelessRebootInfo"},
124 {0x00460104, nullptr, "Wrap"}, 146 {0x00460104, nullptr, "Wrap"},
125 {0x00470104, nullptr, "Unwrap"}, 147 {0x00470104, nullptr, "Unwrap"},
126 {0x00480100, nullptr, "GetProgramInfo"}, 148 {0x00480100, nullptr, "GetProgramInfo"},
127 {0x00490180, nullptr, "Reboot"}, 149 {0x00490180, nullptr, "Reboot"},
128 {0x004A0040, nullptr, "GetCaptureInfo"}, 150 {0x004A0040, nullptr, "GetCaptureInfo"},
129 {0x004B00C2, nullptr, "AppletUtility"}, 151 {0x004B00C2, nullptr, "AppletUtility"},
130 {0x004C0000, nullptr, "SetFatalErrDispMode"}, 152 {0x004C0000, nullptr, "SetFatalErrDispMode"},
131 {0x004D0080, nullptr, "GetAppletProgramInfo"}, 153 {0x004D0080, nullptr, "GetAppletProgramInfo"},
132 {0x004E0000, nullptr, "HardwareResetAsync"}, 154 {0x004E0000, nullptr, "HardwareResetAsync"},
133}; 155};
134 156
135//////////////////////////////////////////////////////////////////////////////////////////////////// 157////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/fs.cpp b/src/core/hle/service/fs.cpp
new file mode 100644
index 000000000..5eabf36ad
--- /dev/null
+++ b/src/core/hle/service/fs.cpp
@@ -0,0 +1,148 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6
7#include "core/loader/loader.h"
8#include "core/hle/hle.h"
9#include "core/hle/service/fs.h"
10#include "core/hle/kernel/archive.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// Namespace FS_User
14
15namespace FS_User {
16
17void Initialize(Service::Interface* self) {
18 u32* cmd_buff = Service::GetCommandBuffer();
19 cmd_buff[1] = 0; // No error
20 DEBUG_LOG(KERNEL, "called");
21}
22
23void OpenFileDirectly(Service::Interface* self) {
24 u32* cmd_buff = Service::GetCommandBuffer();
25
26 FileSys::Archive::IdCode arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
27
28 // TODO(bunnei): Properly implement use of these...
29 //u32 transaction = cmd_buff[1];
30 //u32 arch_lowpath_type = cmd_buff[3];
31 //u32 arch_lowpath_sz = cmd_buff[4];
32 //u32 file_lowpath_type = cmd_buff[5];
33 //u32 file_lowpath_sz = cmd_buff[6];
34 //u32 flags = cmd_buff[7];
35 //u32 attr = cmd_buff[8];
36 //u32 arch_lowpath_desc = cmd_buff[9];
37 //u32 arch_lowpath_ptr = cmd_buff[10];
38 //u32 file_lowpath_desc = cmd_buff[11];
39 //u32 file_lowpath_ptr = cmd_buff[12];
40
41 Handle handle = Kernel::OpenArchive(arch_id);
42 if (0 != handle) {
43 cmd_buff[1] = 0; // No error
44 cmd_buff[3] = handle;
45 }
46 DEBUG_LOG(KERNEL, "called");
47}
48
49const Interface::FunctionInfo FunctionTable[] = {
50 {0x000100C6, nullptr, "Dummy1"},
51 {0x040100C4, nullptr, "Control"},
52 {0x08010002, Initialize, "Initialize"},
53 {0x080201C2, nullptr, "OpenFile"},
54 {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
55 {0x08040142, nullptr, "DeleteFile"},
56 {0x08050244, nullptr, "RenameFile"},
57 {0x08060142, nullptr, "DeleteDirectory"},
58 {0x08070142, nullptr, "DeleteDirectoryRecursively"},
59 {0x08080202, nullptr, "CreateFile"},
60 {0x08090182, nullptr, "CreateDirectory"},
61 {0x080A0244, nullptr, "RenameDirectory"},
62 {0x080B0102, nullptr, "OpenDirectory"},
63 {0x080C00C2, nullptr, "OpenArchive"},
64 {0x080D0144, nullptr, "ControlArchive"},
65 {0x080E0080, nullptr, "CloseArchive"},
66 {0x080F0180, nullptr, "FormatThisUserSaveData"},
67 {0x08100200, nullptr, "CreateSystemSaveData"},
68 {0x08110040, nullptr, "DeleteSystemSaveData"},
69 {0x08120080, nullptr, "GetFreeBytes"},
70 {0x08130000, nullptr, "GetCardType"},
71 {0x08140000, nullptr, "GetSdmcArchiveResource"},
72 {0x08150000, nullptr, "GetNandArchiveResource"},
73 {0x08160000, nullptr, "GetSdmcFatfsErro"},
74 {0x08170000, nullptr, "IsSdmcDetected"},
75 {0x08180000, nullptr, "IsSdmcWritable"},
76 {0x08190042, nullptr, "GetSdmcCid"},
77 {0x081A0042, nullptr, "GetNandCid"},
78 {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
79 {0x081C0000, nullptr, "GetNandSpeedInfo"},
80 {0x081D0042, nullptr, "GetSdmcLog"},
81 {0x081E0042, nullptr, "GetNandLog"},
82 {0x081F0000, nullptr, "ClearSdmcLog"},
83 {0x08200000, nullptr, "ClearNandLog"},
84 {0x08210000, nullptr, "CardSlotIsInserted"},
85 {0x08220000, nullptr, "CardSlotPowerOn"},
86 {0x08230000, nullptr, "CardSlotPowerOff"},
87 {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
88 {0x08250040, nullptr, "CardNorDirectCommand"},
89 {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
90 {0x08270082, nullptr, "CardNorDirectRead"},
91 {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
92 {0x08290082, nullptr, "CardNorDirectWrite"},
93 {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
94 {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
95 {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
96 {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
97 {0x082E0040, nullptr, "GetProductInfo"},
98 {0x082F0040, nullptr, "GetProgramLaunchInfo"},
99 {0x08300182, nullptr, "CreateExtSaveData"},
100 {0x08310180, nullptr, "CreateSharedExtSaveData"},
101 {0x08320102, nullptr, "ReadExtSaveDataIcon"},
102 {0x08330082, nullptr, "EnumerateExtSaveData"},
103 {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
104 {0x08350080, nullptr, "DeleteExtSaveData"},
105 {0x08360080, nullptr, "DeleteSharedExtSaveData"},
106 {0x08370040, nullptr, "SetCardSpiBaudRate"},
107 {0x08380040, nullptr, "SetCardSpiBusMode"},
108 {0x08390000, nullptr, "SendInitializeInfoTo9"},
109 {0x083A0100, nullptr, "GetSpecialContentIndex"},
110 {0x083B00C2, nullptr, "GetLegacyRomHeader"},
111 {0x083C00C2, nullptr, "GetLegacyBannerData"},
112 {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
113 {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
114 {0x083F00C0, nullptr, "GetExtDataBlockSize"},
115 {0x08400040, nullptr, "AbnegateAccessRight"},
116 {0x08410000, nullptr, "DeleteSdmcRoot"},
117 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
118 {0x08430000, nullptr, "InitializeCtrFileSystem"},
119 {0x08440000, nullptr, "CreateSeed"},
120 {0x084500C2, nullptr, "GetFormatInfo"},
121 {0x08460102, nullptr, "GetLegacyRomHeader2"},
122 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
123 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
124 {0x08490040, nullptr, "GetArchiveResource"},
125 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
126 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
127 {0x084C0242, nullptr, "FormatSaveData"},
128 {0x084D0102, nullptr, "GetLegacySubBannerData"},
129 {0x084E0342, nullptr, "UpdateSha256Context"},
130 {0x084F0102, nullptr, "ReadSpecialFile"},
131 {0x08500040, nullptr, "GetSpecialFileSize"},
132 {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
133 {0x08610042, nullptr, "InitializeWithSdkVersion"},
134 {0x08620040, nullptr, "SetPriority"},
135 {0x08630000, nullptr, "GetPriority"},
136};
137
138////////////////////////////////////////////////////////////////////////////////////////////////////
139// Interface class
140
141Interface::Interface() {
142 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
143}
144
145Interface::~Interface() {
146}
147
148} // namespace
diff --git a/src/core/hle/service/fs.h b/src/core/hle/service/fs.h
new file mode 100644
index 000000000..34b0610ad
--- /dev/null
+++ b/src/core/hle/service/fs.h
@@ -0,0 +1,31 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FS_User
11
12namespace FS_User {
13
14/// Interface to "fs:USER" service
15class Interface : public Service::Interface {
16public:
17
18 Interface();
19
20 ~Interface();
21
22 /**
23 * Gets the string port name used by CTROS for the service
24 * @return Port name of service
25 */
26 const char *GetPortName() const {
27 return "Ufs:";
28 }
29};
30
31} // namespace
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index f75ba75c2..b20203e27 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -9,6 +9,7 @@
9#include "core/mem_map.h" 9#include "core/mem_map.h"
10#include "core/hle/hle.h" 10#include "core/hle/hle.h"
11#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/service/gsp.h" 13#include "core/hle/service/gsp.h"
13 14
14#include "core/hw/gpu.h" 15#include "core/hw/gpu.h"
@@ -36,14 +37,19 @@ union GX_CmdBufferHeader {
36 BitField<8,8,u32> number_commands; 37 BitField<8,8,u32> number_commands;
37}; 38};
38 39
39/// Gets the address of the start (header) of a command buffer in GSP shared memory 40////////////////////////////////////////////////////////////////////////////////////////////////////
40static inline u32 GX_GetCmdBufferAddress(u32 thread_id) { 41// Namespace GSP_GPU
41 return (0x10002000 + 0x800 + (thread_id * 0x200)); 42
42} 43namespace GSP_GPU {
44
45Handle g_event = 0;
46Handle g_shared_memory = 0;
47
48u32 g_thread_id = 0;
43 49
44/// Gets a pointer to the start (header) of a command buffer in GSP shared memory 50/// Gets a pointer to the start (header) of a command buffer in GSP shared memory
45static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) { 51static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) {
46 return Memory::GetPointer(GX_GetCmdBufferAddress(thread_id) + offset); 52 return Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * 0x200) + offset);
47} 53}
48 54
49/// Finishes execution of a GSP command 55/// Finishes execution of a GSP command
@@ -56,122 +62,183 @@ void GX_FinishCommand(u32 thread_id) {
56 // TODO: Increment header->index? 62 // TODO: Increment header->index?
57} 63}
58 64
59//////////////////////////////////////////////////////////////////////////////////////////////////// 65/// Write a GSP GPU hardware register
60// Namespace GSP_GPU 66void WriteHWRegs(Service::Interface* self) {
67 u32* cmd_buff = Service::GetCommandBuffer();
68 u32 reg_addr = cmd_buff[1];
69 u32 size = cmd_buff[2];
61 70
62namespace GSP_GPU { 71 // TODO: Return proper error codes
72 if (reg_addr + size >= 0x420000) {
73 ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
74 return;
75 }
63 76
64Handle g_event_handle = 0; 77 // size should be word-aligned
65u32 g_thread_id = 0; 78 if ((size % 4) != 0) {
79 ERROR_LOG(GPU, "Invalid size 0x%08x", size);
80 return;
81 }
66 82
67enum { 83 u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]);
68 REG_FRAMEBUFFER_1 = 0x00400468, 84
69 REG_FRAMEBUFFER_2 = 0x00400494, 85 while (size > 0) {
70}; 86 GPU::Write<u32>(reg_addr + 0x1EB00000, *src);
87
88 size -= 4;
89 ++src;
90 reg_addr += 4;
91 }
92}
71 93
72/// Read a GSP GPU hardware register 94/// Read a GSP GPU hardware register
73void ReadHWRegs(Service::Interface* self) { 95void ReadHWRegs(Service::Interface* self) {
74 static const u32 framebuffer_1[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME1, GPU::PADDR_VRAM_TOP_RIGHT_FRAME1};
75 static const u32 framebuffer_2[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME2, GPU::PADDR_VRAM_TOP_RIGHT_FRAME2};
76
77 u32* cmd_buff = Service::GetCommandBuffer(); 96 u32* cmd_buff = Service::GetCommandBuffer();
78 u32 reg_addr = cmd_buff[1]; 97 u32 reg_addr = cmd_buff[1];
79 u32 size = cmd_buff[2]; 98 u32 size = cmd_buff[2];
80 u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
81 99
82 switch (reg_addr) { 100 // TODO: Return proper error codes
101 if (reg_addr + size >= 0x420000) {
102 ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
103 return;
104 }
83 105
84 // NOTE: Calling SetFramebufferLocation here is a hack... Not sure the correct way yet to set 106 // size should be word-aligned
85 // whether the framebuffers should be in VRAM or GSP heap, but from what I understand, if the 107 if ((size % 4) != 0) {
86 // user application is reading from either of these registers, then its going to be in VRAM. 108 ERROR_LOG(GPU, "Invalid size 0x%08x", size);
109 return;
110 }
87 111
88 // Top framebuffer 1 addresses 112 u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
89 case REG_FRAMEBUFFER_1:
90 GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
91 memcpy(dst, framebuffer_1, size);
92 break;
93 113
94 // Top framebuffer 2 addresses 114 while (size > 0) {
95 case REG_FRAMEBUFFER_2: 115 GPU::Read<u32>(*dst, reg_addr + 0x1EB00000);
96 GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
97 memcpy(dst, framebuffer_2, size);
98 break;
99 116
100 default: 117 size -= 4;
101 ERROR_LOG(GSP, "unknown register read at address %08X", reg_addr); 118 ++dst;
119 reg_addr += 4;
102 } 120 }
103
104} 121}
105 122
123/**
124 * GSP_GPU::RegisterInterruptRelayQueue service function
125 * Inputs:
126 * 1 : "Flags" field, purpose is unknown
127 * 3 : Handle to GSP synchronization event
128 * Outputs:
129 * 0 : Result of function, 0 on success, otherwise error code
130 * 2 : Thread index into GSP command buffer
131 * 4 : Handle to GSP shared memory
132 */
106void RegisterInterruptRelayQueue(Service::Interface* self) { 133void RegisterInterruptRelayQueue(Service::Interface* self) {
107 u32* cmd_buff = Service::GetCommandBuffer(); 134 u32* cmd_buff = Service::GetCommandBuffer();
108 u32 flags = cmd_buff[1]; 135 u32 flags = cmd_buff[1];
109 u32 event_handle = cmd_buff[3]; 136 g_event = cmd_buff[3];
110
111 _assert_msg_(GSP, (event_handle != 0), "called, but event is nullptr!");
112 137
113 g_event_handle = event_handle; 138 _assert_msg_(GSP, (g_event != 0), "handle is not valid!");
114 139
115 Kernel::SetEventLocked(event_handle, false); 140 Kernel::SetEventLocked(g_event, false);
116 141
117 // Hack - This function will permanently set the state of the GSP event such that GPU command 142 // Hack - This function will permanently set the state of the GSP event such that GPU command
118 // synchronization barriers always passthrough. Correct solution would be to set this after the 143 // synchronization barriers always passthrough. Correct solution would be to set this after the
119 // GPU as processed all queued up commands, but due to the emulator being single-threaded they 144 // GPU as processed all queued up commands, but due to the emulator being single-threaded they
120 // will always be ready. 145 // will always be ready.
121 Kernel::SetPermanentLock(event_handle, true); 146 Kernel::SetPermanentLock(g_event, true);
122 147
123 cmd_buff[2] = g_thread_id; // ThreadID 148 cmd_buff[0] = 0; // Result - no error
149 cmd_buff[2] = g_thread_id; // ThreadID
150 cmd_buff[4] = g_shared_memory; // GSP shared memory
124} 151}
125 152
126 153
127/// This triggers handling of the GX command written to the command buffer in shared memory. 154/// This triggers handling of the GX command written to the command buffer in shared memory.
128void TriggerCmdReqQueue(Service::Interface* self) { 155void TriggerCmdReqQueue(Service::Interface* self) {
156
157 // Utility function to convert register ID to address
158 auto WriteGPURegister = [](u32 id, u32 data) {
159 GPU::Write<u32>(0x1EF00000 + 4 * id, data);
160 };
161
129 GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id); 162 GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id);
130 u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20)); 163 auto& command = *(const GXCommand*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20));
131 164
132 switch (static_cast<GXCommandId>(cmd_buff[0])) { 165 switch (command.id) {
133 166
134 // GX request DMA - typically used for copying memory from GSP heap to VRAM 167 // GX request DMA - typically used for copying memory from GSP heap to VRAM
135 case GXCommandId::REQUEST_DMA: 168 case GXCommandId::REQUEST_DMA:
136 memcpy(Memory::GetPointer(cmd_buff[2]), Memory::GetPointer(cmd_buff[1]), cmd_buff[3]); 169 memcpy(Memory::GetPointer(command.dma_request.dest_address),
170 Memory::GetPointer(command.dma_request.source_address),
171 command.dma_request.size);
137 break; 172 break;
138 173
174 // ctrulib homebrew sends all relevant command list data with this command,
175 // hence we do all "interesting" stuff here and do nothing in SET_COMMAND_LIST_FIRST.
176 // TODO: This will need some rework in the future.
139 case GXCommandId::SET_COMMAND_LIST_LAST: 177 case GXCommandId::SET_COMMAND_LIST_LAST:
140 GPU::Write<u32>(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3); 178 {
141 GPU::Write<u32>(GPU::Registers::CommandListSize, cmd_buff[2] >> 3); 179 auto& params = command.set_command_list_last;
142 GPU::Write<u32>(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this 180 WriteGPURegister(GPU::Regs::CommandProcessor + 2, params.address >> 3);
181 WriteGPURegister(GPU::Regs::CommandProcessor, params.size >> 3);
182 WriteGPURegister(GPU::Regs::CommandProcessor + 4, 1); // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
143 183
144 // TODO: Move this to GPU 184 // TODO: Move this to GPU
145 // TODO: Not sure what units the size is measured in 185 // TODO: Not sure what units the size is measured in
146 g_debugger.CommandListCalled(cmd_buff[1], (u32*)Memory::GetPointer(cmd_buff[1]), cmd_buff[2]); 186 g_debugger.CommandListCalled(params.address,
187 (u32*)Memory::GetPointer(params.address),
188 params.size);
147 break; 189 break;
190 }
148 191
192 // It's assumed that the two "blocks" behave equivalently.
193 // Presumably this is done simply to allow two memory fills to run in parallel.
149 case GXCommandId::SET_MEMORY_FILL: 194 case GXCommandId::SET_MEMORY_FILL:
195 {
196 auto& params = command.memory_fill;
197 WriteGPURegister(GPU::Regs::MemoryFill, params.start1 >> 3);
198 WriteGPURegister(GPU::Regs::MemoryFill + 1, params.end1 >> 3);
199 WriteGPURegister(GPU::Regs::MemoryFill + 2, params.end1 - params.start1);
200 WriteGPURegister(GPU::Regs::MemoryFill + 3, params.value1);
201
202 WriteGPURegister(GPU::Regs::MemoryFill + 4, params.start2 >> 3);
203 WriteGPURegister(GPU::Regs::MemoryFill + 5, params.end2 >> 3);
204 WriteGPURegister(GPU::Regs::MemoryFill + 6, params.end2 - params.start2);
205 WriteGPURegister(GPU::Regs::MemoryFill + 7, params.value2);
150 break; 206 break;
207 }
151 208
209 // TODO: Check if texture copies are implemented correctly..
152 case GXCommandId::SET_DISPLAY_TRANSFER: 210 case GXCommandId::SET_DISPLAY_TRANSFER:
153 break;
154
155 case GXCommandId::SET_TEXTURE_COPY: 211 case GXCommandId::SET_TEXTURE_COPY:
212 {
213 auto& params = command.image_copy;
214 WriteGPURegister(GPU::Regs::DisplayTransfer, params.in_buffer_address >> 3);
215 WriteGPURegister(GPU::Regs::DisplayTransfer + 1, params.out_buffer_address >> 3);
216 WriteGPURegister(GPU::Regs::DisplayTransfer + 3, params.in_buffer_size);
217 WriteGPURegister(GPU::Regs::DisplayTransfer + 2, params.out_buffer_size);
218 WriteGPURegister(GPU::Regs::DisplayTransfer + 4, params.flags);
219
220 // TODO: Should this only be ORed with 1 for texture copies?
221 // trigger transfer
222 WriteGPURegister(GPU::Regs::DisplayTransfer + 6, 1);
156 break; 223 break;
224 }
157 225
226 // TODO: Figure out what exactly SET_COMMAND_LIST_FIRST and SET_COMMAND_LIST_LAST
227 // are supposed to do.
158 case GXCommandId::SET_COMMAND_LIST_FIRST: 228 case GXCommandId::SET_COMMAND_LIST_FIRST:
159 { 229 {
160 //u32* buf0_data = (u32*)Memory::GetPointer(cmd_buff[1]);
161 //u32* buf1_data = (u32*)Memory::GetPointer(cmd_buff[3]);
162 //u32* buf2_data = (u32*)Memory::GetPointer(cmd_buff[5]);
163 break; 230 break;
164 } 231 }
165 232
166 default: 233 default:
167 ERROR_LOG(GSP, "unknown command 0x%08X", cmd_buff[0]); 234 ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value());
168 } 235 }
169 236
170 GX_FinishCommand(g_thread_id); 237 GX_FinishCommand(g_thread_id);
171} 238}
172 239
173const Interface::FunctionInfo FunctionTable[] = { 240const Interface::FunctionInfo FunctionTable[] = {
174 {0x00010082, nullptr, "WriteHWRegs"}, 241 {0x00010082, WriteHWRegs, "WriteHWRegs"},
175 {0x00020084, nullptr, "WriteHWRegsWithMask"}, 242 {0x00020084, nullptr, "WriteHWRegsWithMask"},
176 {0x00030082, nullptr, "WriteHWRegRepeat"}, 243 {0x00030082, nullptr, "WriteHWRegRepeat"},
177 {0x00040080, ReadHWRegs, "ReadHWRegs"}, 244 {0x00040080, ReadHWRegs, "ReadHWRegs"},
@@ -208,6 +275,7 @@ const Interface::FunctionInfo FunctionTable[] = {
208 275
209Interface::Interface() { 276Interface::Interface() {
210 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 277 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
278 g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
211} 279}
212 280
213Interface::~Interface() { 281Interface::~Interface() {
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h
index 214de140f..a83cb4846 100644
--- a/src/core/hle/service/gsp.h
+++ b/src/core/hle/service/gsp.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,21 +13,58 @@
12namespace GSP_GPU { 13namespace GSP_GPU {
13 14
14enum class GXCommandId : u32 { 15enum class GXCommandId : u32 {
15 REQUEST_DMA = 0x00000000, 16 REQUEST_DMA = 0x00,
16 SET_COMMAND_LIST_LAST = 0x00000001, 17 SET_COMMAND_LIST_LAST = 0x01,
17 SET_MEMORY_FILL = 0x00000002, // TODO: Confirm? (lictru uses 0x01000102) 18
18 SET_DISPLAY_TRANSFER = 0x00000003, 19 // Fills a given memory range with a particular value
19 SET_TEXTURE_COPY = 0x00000004, 20 SET_MEMORY_FILL = 0x02,
20 SET_COMMAND_LIST_FIRST = 0x00000005, 21
22 // Copies an image and optionally performs color-conversion or scaling.
23 // This is highly similar to the GameCube's EFB copy feature
24 SET_DISPLAY_TRANSFER = 0x03,
25
26 // Conceptionally similar to SET_DISPLAY_TRANSFER and presumable uses the same hardware path
27 SET_TEXTURE_COPY = 0x04,
28
29 SET_COMMAND_LIST_FIRST = 0x05,
21}; 30};
22 31
23union GXCommand { 32struct GXCommand {
24 struct { 33 BitField<0, 8, GXCommandId> id;
25 GXCommandId id;
26 };
27 34
28 u32 data[0x20]; 35 union {
36 struct {
37 u32 source_address;
38 u32 dest_address;
39 u32 size;
40 } dma_request;
41
42 struct {
43 u32 address;
44 u32 size;
45 } set_command_list_last;
46
47 struct {
48 u32 start1;
49 u32 value1;
50 u32 end1;
51 u32 start2;
52 u32 value2;
53 u32 end2;
54 } memory_fill;
55
56 struct {
57 u32 in_buffer_address;
58 u32 out_buffer_address;
59 u32 in_buffer_size;
60 u32 out_buffer_size;
61 u32 flags;
62 } image_copy;
63
64 u8 raw_data[0x1C];
65 };
29}; 66};
67static_assert(sizeof(GXCommand) == 0x20, "GXCommand struct has incorrect size");
30 68
31/// Interface to "srv:" service 69/// Interface to "srv:" service
32class Interface : public Service::Interface { 70class Interface : public Service::Interface {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 4a1ac857e..d3af2768a 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -10,6 +10,7 @@
10 10
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/apt.h" 12#include "core/hle/service/apt.h"
13#include "core/hle/service/fs.h"
13#include "core/hle/service/gsp.h" 14#include "core/hle/service/gsp.h"
14#include "core/hle/service/hid.h" 15#include "core/hle/service/hid.h"
15#include "core/hle/service/ndm.h" 16#include "core/hle/service/ndm.h"
@@ -71,6 +72,7 @@ void Init() {
71 72
72 g_manager->AddService(new SRV::Interface); 73 g_manager->AddService(new SRV::Interface);
73 g_manager->AddService(new APT_U::Interface); 74 g_manager->AddService(new APT_U::Interface);
75 g_manager->AddService(new FS_User::Interface);
74 g_manager->AddService(new GSP_GPU::Interface); 76 g_manager->AddService(new GSP_GPU::Interface);
75 g_manager->AddService(new HID_User::Interface); 77 g_manager->AddService(new HID_User::Interface);
76 g_manager->AddService(new NDM_U::Interface); 78 g_manager->AddService(new NDM_U::Interface);
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 441d8ce8d..17967f260 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -9,9 +9,11 @@
9 9
10#include "core/mem_map.h" 10#include "core/mem_map.h"
11 11
12#include "core/hle/kernel/address_arbiter.h"
12#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/mutex.h" 15#include "core/hle/kernel/mutex.h"
16#include "core/hle/kernel/shared_memory.h"
15#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
16 18
17#include "core/hle/function_wrappers.h" 19#include "core/hle/function_wrappers.h"
@@ -28,11 +30,6 @@ enum ControlMemoryOperation {
28 MEMORY_OPERATION_GSP_HEAP = 0x00010003, 30 MEMORY_OPERATION_GSP_HEAP = 0x00010003,
29}; 31};
30 32
31enum MapMemoryPermission {
32 MEMORY_PERMISSION_UNMAP = 0x00000000,
33 MEMORY_PERMISSION_NORMAL = 0x00000001,
34};
35
36/// Map application or GSP heap memory 33/// Map application or GSP heap memory
37Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { 34Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
38 DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", 35 DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
@@ -58,17 +55,21 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
58} 55}
59 56
60/// Maps a memory block to specified address 57/// Maps a memory block to specified address
61Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) { 58Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
62 DEBUG_LOG(SVC, "called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", 59 DEBUG_LOG(SVC, "called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
63 memblock, addr, mypermissions, otherpermission); 60 handle, addr, permissions, other_permissions);
64 switch (mypermissions) { 61
65 case MEMORY_PERMISSION_NORMAL: 62 Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
66 case MEMORY_PERMISSION_NORMAL + 1: 63 switch (permissions_type) {
67 case MEMORY_PERMISSION_NORMAL + 2: 64 case Kernel::MemoryPermission::Read:
68 Memory::MapBlock_Shared(memblock, addr, mypermissions); 65 case Kernel::MemoryPermission::Write:
66 case Kernel::MemoryPermission::ReadWrite:
67 case Kernel::MemoryPermission::DontCare:
68 Kernel::MapSharedMemory(handle, addr, permissions_type,
69 static_cast<Kernel::MemoryPermission>(other_permissions));
69 break; 70 break;
70 default: 71 default:
71 ERROR_LOG(OSHLE, "unknown permissions=0x%08X", mypermissions); 72 ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
72 } 73 }
73 return 0; 74 return 0;
74} 75}
@@ -175,18 +176,19 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
175} 176}
176 177
177/// Create an address arbiter (to allocate access to shared resources) 178/// Create an address arbiter (to allocate access to shared resources)
178Result CreateAddressArbiter(void* arbiter) { 179Result CreateAddressArbiter(u32* arbiter) {
179 ERROR_LOG(SVC, "(UNIMPLEMENTED) called"); 180 DEBUG_LOG(SVC, "called");
180 Core::g_app_core->SetReg(1, 0xFABBDADD); 181 Handle handle = Kernel::CreateAddressArbiter();
182 *arbiter = handle;
181 return 0; 183 return 0;
182} 184}
183 185
184/// Arbitrate address 186/// Arbitrate address
185Result ArbitrateAddress(Handle arbiter, u32 addr, u32 _type, u32 value, s64 nanoseconds) { 187Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
186 ERROR_LOG(SVC, "(UNIMPLEMENTED) called"); 188 DEBUG_LOG(SVC, "called arbiter=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X, "
187 ArbitrationType type = (ArbitrationType)_type; 189 "nanoseconds=%d", arbiter, address, type, value, nanoseconds);
188 Memory::Write32(addr, type); 190 return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
189 return 0; 191 value);
190} 192}
191 193
192/// Used to output a message on a debug hardware unit - does nothing on a retail unit 194/// Used to output a message on a debug hardware unit - does nothing on a retail unit
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index f0ca4eada..c00be2a83 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -15,48 +15,58 @@
15 15
16namespace GPU { 16namespace GPU {
17 17
18Registers g_regs; 18RegisterSet<u32, Regs> g_regs;
19 19
20u64 g_last_ticks = 0; ///< Last CPU ticks 20u64 g_last_ticks = 0; ///< Last CPU ticks
21 21
22/** 22/**
23 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM 23 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM
24 * @param 24 * @param
25 */ 25 */
26void SetFramebufferLocation(const FramebufferLocation mode) { 26void SetFramebufferLocation(const FramebufferLocation mode) {
27 switch (mode) { 27 switch (mode) {
28 case FRAMEBUFFER_LOCATION_FCRAM: 28 case FRAMEBUFFER_LOCATION_FCRAM:
29 g_regs.framebuffer_top_left_1 = PADDR_TOP_LEFT_FRAME1; 29 {
30 g_regs.framebuffer_top_left_2 = PADDR_TOP_LEFT_FRAME2; 30 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
31 g_regs.framebuffer_top_right_1 = PADDR_TOP_RIGHT_FRAME1; 31 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
32 g_regs.framebuffer_top_right_2 = PADDR_TOP_RIGHT_FRAME2; 32
33 g_regs.framebuffer_sub_left_1 = PADDR_SUB_FRAME1; 33 framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1;
34 //g_regs.framebuffer_sub_left_2 = unknown; 34 framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2;
35 g_regs.framebuffer_sub_right_1 = PADDR_SUB_FRAME2; 35 framebuffer_top.address_right1 = PADDR_TOP_RIGHT_FRAME1;
36 //g_regs.framebufferr_sub_right_2 = unknown; 36 framebuffer_top.address_right2 = PADDR_TOP_RIGHT_FRAME2;
37 framebuffer_sub.address_left1 = PADDR_SUB_FRAME1;
38 //framebuffer_sub.address_left2 = unknown;
39 framebuffer_sub.address_right1 = PADDR_SUB_FRAME2;
40 //framebuffer_sub.address_right2 = unknown;
37 break; 41 break;
42 }
38 43
39 case FRAMEBUFFER_LOCATION_VRAM: 44 case FRAMEBUFFER_LOCATION_VRAM:
40 g_regs.framebuffer_top_left_1 = PADDR_VRAM_TOP_LEFT_FRAME1; 45 {
41 g_regs.framebuffer_top_left_2 = PADDR_VRAM_TOP_LEFT_FRAME2; 46 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
42 g_regs.framebuffer_top_right_1 = PADDR_VRAM_TOP_RIGHT_FRAME1; 47 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
43 g_regs.framebuffer_top_right_2 = PADDR_VRAM_TOP_RIGHT_FRAME2; 48
44 g_regs.framebuffer_sub_left_1 = PADDR_VRAM_SUB_FRAME1; 49 framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1;
45 //g_regs.framebuffer_sub_left_2 = unknown; 50 framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2;
46 g_regs.framebuffer_sub_right_1 = PADDR_VRAM_SUB_FRAME2; 51 framebuffer_top.address_right1 = PADDR_VRAM_TOP_RIGHT_FRAME1;
47 //g_regs.framebufferr_sub_right_2 = unknown; 52 framebuffer_top.address_right2 = PADDR_VRAM_TOP_RIGHT_FRAME2;
53 framebuffer_sub.address_left1 = PADDR_VRAM_SUB_FRAME1;
54 //framebuffer_sub.address_left2 = unknown;
55 framebuffer_sub.address_right1 = PADDR_VRAM_SUB_FRAME2;
56 //framebuffer_sub.address_right2 = unknown;
48 break; 57 break;
49 } 58 }
59 }
50} 60}
51 61
52/** 62/**
53 * Gets the location of the framebuffers 63 * Gets the location of the framebuffers
54 * @return Location of framebuffers as FramebufferLocation enum 64 * @return Location of framebuffers as FramebufferLocation enum
55 */ 65 */
56const FramebufferLocation GetFramebufferLocation() { 66FramebufferLocation GetFramebufferLocation(u32 address) {
57 if ((g_regs.framebuffer_top_right_1 & ~Memory::VRAM_MASK) == Memory::VRAM_PADDR) { 67 if ((address & ~Memory::VRAM_MASK) == Memory::VRAM_PADDR) {
58 return FRAMEBUFFER_LOCATION_VRAM; 68 return FRAMEBUFFER_LOCATION_VRAM;
59 } else if ((g_regs.framebuffer_top_right_1 & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) { 69 } else if ((address & ~Memory::FCRAM_MASK) == Memory::FCRAM_PADDR) {
60 return FRAMEBUFFER_LOCATION_FCRAM; 70 return FRAMEBUFFER_LOCATION_FCRAM;
61 } else { 71 } else {
62 ERROR_LOG(GPU, "unknown framebuffer location!"); 72 ERROR_LOG(GPU, "unknown framebuffer location!");
@@ -64,91 +74,161 @@ const FramebufferLocation GetFramebufferLocation() {
64 return FRAMEBUFFER_LOCATION_UNKNOWN; 74 return FRAMEBUFFER_LOCATION_UNKNOWN;
65} 75}
66 76
77u32 GetFramebufferAddr(const u32 address) {
78 switch (GetFramebufferLocation(address)) {
79 case FRAMEBUFFER_LOCATION_FCRAM:
80 return Memory::VirtualAddressFromPhysical_FCRAM(address);
81 case FRAMEBUFFER_LOCATION_VRAM:
82 return Memory::VirtualAddressFromPhysical_VRAM(address);
83 default:
84 ERROR_LOG(GPU, "unknown framebuffer location");
85 }
86 return 0;
87}
88
67/** 89/**
68 * Gets a read-only pointer to a framebuffer in memory 90 * Gets a read-only pointer to a framebuffer in memory
69 * @param address Physical address of framebuffer 91 * @param address Physical address of framebuffer
70 * @return Returns const pointer to raw framebuffer 92 * @return Returns const pointer to raw framebuffer
71 */ 93 */
72const u8* GetFramebufferPointer(const u32 address) { 94const u8* GetFramebufferPointer(const u32 address) {
73 switch (GetFramebufferLocation()) { 95 u32 addr = GetFramebufferAddr(address);
74 case FRAMEBUFFER_LOCATION_FCRAM: 96 return (addr != 0) ? Memory::GetPointer(addr) : nullptr;
75 return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_FCRAM(address));
76 case FRAMEBUFFER_LOCATION_VRAM:
77 return (const u8*)Memory::GetPointer(Memory::VirtualAddressFromPhysical_VRAM(address));
78 default:
79 ERROR_LOG(GPU, "unknown framebuffer location");
80 }
81 return NULL;
82} 97}
83 98
84template <typename T> 99template <typename T>
85inline void Read(T &var, const u32 addr) { 100inline void Read(T &var, const u32 raw_addr) {
86 switch (addr) { 101 u32 addr = raw_addr - 0x1EF00000;
87 case Registers::FramebufferTopLeft1: 102 int index = addr / 4;
88 var = g_regs.framebuffer_top_left_1;
89 break;
90 103
91 case Registers::FramebufferTopLeft2: 104 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
92 var = g_regs.framebuffer_top_left_2; 105 if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
93 break; 106 {
107 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
108 return;
109 }
94 110
95 case Registers::FramebufferTopRight1: 111 var = g_regs[static_cast<Regs::Id>(addr / 4)];
96 var = g_regs.framebuffer_top_right_1; 112}
97 break;
98 113
99 case Registers::FramebufferTopRight2: 114template <typename T>
100 var = g_regs.framebuffer_top_right_2; 115inline void Write(u32 addr, const T data) {
101 break; 116 addr -= 0x1EF00000;
117 int index = addr / 4;
102 118
103 case Registers::FramebufferSubLeft1: 119 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
104 var = g_regs.framebuffer_sub_left_1; 120 if (index >= Regs::NumIds || !std::is_same<T,u32>::value)
105 break; 121 {
122 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
123 return;
124 }
106 125
107 case Registers::FramebufferSubRight1: 126 g_regs[static_cast<Regs::Id>(index)] = data;
108 var = g_regs.framebuffer_sub_right_1;
109 break;
110 127
111 case Registers::CommandListSize: 128 switch (static_cast<Regs::Id>(index)) {
112 var = g_regs.command_list_size;
113 break;
114 129
115 case Registers::CommandListAddress: 130 // Memory fills are triggered once the fill value is written.
116 var = g_regs.command_list_address; 131 // NOTE: This is not verified.
117 break; 132 case Regs::MemoryFill + 3:
133 case Regs::MemoryFill + 7:
134 {
135 const auto& config = g_regs.Get<Regs::MemoryFill>(static_cast<Regs::Id>(index - 3));
118 136
119 case Registers::ProcessCommandList: 137 // TODO: Not sure if this check should be done at GSP level instead
120 var = g_regs.command_processing_enabled; 138 if (config.address_start) {
121 break; 139 // TODO: Not sure if this algorithm is correct, particularly because it doesn't use the size member at all
140 u32* start = (u32*)Memory::GetPointer(config.GetStartAddress());
141 u32* end = (u32*)Memory::GetPointer(config.GetEndAddress());
142 for (u32* ptr = start; ptr < end; ++ptr)
143 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
122 144
123 default: 145 DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
124 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); 146 }
125 break; 147 break;
126 } 148 }
127}
128
129template <typename T>
130inline void Write(u32 addr, const T data) {
131 switch (static_cast<Registers::Id>(addr)) {
132 case Registers::CommandListSize:
133 g_regs.command_list_size = data;
134 break;
135 149
136 case Registers::CommandListAddress: 150 case Regs::DisplayTransfer + 6:
137 g_regs.command_list_address = data; 151 {
152 const auto& config = g_regs.Get<Regs::DisplayTransfer>();
153 if (config.trigger & 1) {
154 u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress());
155 u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress());
156
157 for (int y = 0; y < config.output_height; ++y) {
158 // TODO: Why does the register seem to hold twice the framebuffer width?
159 for (int x = 0; x < config.output_width / 2; ++x) {
160 struct {
161 int r, g, b, a;
162 } source_color = { 0, 0, 0, 0 };
163
164 switch (config.input_format) {
165 case Regs::FramebufferFormat::RGBA8:
166 {
167 // TODO: Most likely got the component order messed up.
168 u8* srcptr = source_pointer + x * 4 + y * config.input_width * 4 / 2;
169 source_color.r = srcptr[0]; // blue
170 source_color.g = srcptr[1]; // green
171 source_color.b = srcptr[2]; // red
172 source_color.a = srcptr[3]; // alpha
173 break;
174 }
175
176 default:
177 ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value());
178 break;
179 }
180
181 switch (config.output_format) {
182 /*case Regs::FramebufferFormat::RGBA8:
183 {
184 // TODO: Untested
185 u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.output_width * 4);
186 dstptr[0] = source_color.r;
187 dstptr[1] = source_color.g;
188 dstptr[2] = source_color.b;
189 dstptr[3] = source_color.a;
190 break;
191 }*/
192
193 case Regs::FramebufferFormat::RGB8:
194 {
195 // TODO: Most likely got the component order messed up.
196 u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3 / 2;
197 dstptr[0] = source_color.r; // blue
198 dstptr[1] = source_color.g; // green
199 dstptr[2] = source_color.b; // red
200 break;
201 }
202
203 default:
204 ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
205 break;
206 }
207 }
208 }
209
210 DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%dx%d)-> 0x%08x(%dx%d), dst format %x",
211 config.output_height * config.output_width * 4,
212 config.GetPhysicalInputAddress(), (int)config.input_width, (int)config.input_height,
213 config.GetPhysicalOutputAddress(), (int)config.output_width, (int)config.output_height,
214 config.output_format.Value());
215 }
138 break; 216 break;
217 }
139 218
140 case Registers::ProcessCommandList: 219 case Regs::CommandProcessor + 4:
141 g_regs.command_processing_enabled = data; 220 {
142 if (g_regs.command_processing_enabled & 1) 221 const auto& config = g_regs.Get<Regs::CommandProcessor>();
222 if (config.trigger & 1)
143 { 223 {
144 // u32* buffer = (u32*)Memory::GetPointer(g_regs.command_list_address << 3); 224 // u32* buffer = (u32*)Memory::GetPointer(config.address << 3);
145 ERROR_LOG(GPU, "Beginning %x bytes of commands from address %x", g_regs.command_list_size, g_regs.command_list_address << 3); 225 ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.address << 3);
146 // TODO: Process command list! 226 // TODO: Process command list!
147 } 227 }
148 break; 228 break;
229 }
149 230
150 default: 231 default:
151 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
152 break; 232 break;
153 } 233 }
154} 234}
@@ -180,7 +260,24 @@ void Update() {
180/// Initialize hardware 260/// Initialize hardware
181void Init() { 261void Init() {
182 g_last_ticks = Core::g_app_core->GetTicks(); 262 g_last_ticks = Core::g_app_core->GetTicks();
183 SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); 263// SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM);
264 SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM);
265
266 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>();
267 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>();
268 // TODO: Width should be 240 instead?
269 framebuffer_top.width = 480;
270 framebuffer_top.height = 400;
271 framebuffer_top.stride = 480*3;
272 framebuffer_top.color_format = Regs::FramebufferFormat::RGB8;
273 framebuffer_top.active_fb = 0;
274
275 framebuffer_sub.width = 480;
276 framebuffer_sub.height = 400;
277 framebuffer_sub.stride = 480*3;
278 framebuffer_sub.color_format = Regs::FramebufferFormat::RGB8;
279 framebuffer_sub.active_fb = 0;
280
184 NOTICE_LOG(GPU, "initialized OK"); 281 NOTICE_LOG(GPU, "initialized OK");
185} 282}
186 283
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 3314ba989..42f18a0e7 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -5,43 +5,168 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/bit_field.h"
9#include "common/register_set.h"
8 10
9namespace GPU { 11namespace GPU {
10 12
11static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second 13static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second
12static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame 14static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame
13 15
14struct Registers { 16// MMIO region 0x1EFxxxxx
17struct Regs {
15 enum Id : u32 { 18 enum Id : u32 {
16 FramebufferTopLeft1 = 0x1EF00468, // Main LCD, first framebuffer for 3D left 19 MemoryFill = 0x00004, // + 5,6,7; second block at 8-11
17 FramebufferTopLeft2 = 0x1EF0046C, // Main LCD, second framebuffer for 3D left 20
18 FramebufferTopRight1 = 0x1EF00494, // Main LCD, first framebuffer for 3D right 21 FramebufferTop = 0x00117, // + 11a,11b,11c,11d(?),11e...126
19 FramebufferTopRight2 = 0x1EF00498, // Main LCD, second framebuffer for 3D right 22 FramebufferBottom = 0x00157, // + 15a,15b,15c,15d(?),15e...166
20 FramebufferSubLeft1 = 0x1EF00568, // Sub LCD, first framebuffer 23
21 FramebufferSubLeft2 = 0x1EF0056C, // Sub LCD, second framebuffer 24 DisplayTransfer = 0x00300, // + 301,302,303,304,305,306
22 FramebufferSubRight1 = 0x1EF00594, // Sub LCD, unused first framebuffer 25
23 FramebufferSubRight2 = 0x1EF00598, // Sub LCD, unused second framebuffer 26 CommandProcessor = 0x00638, // + 63a,63c
24 27
25 CommandListSize = 0x1EF018E0, 28 NumIds = 0x01000
26 CommandListAddress = 0x1EF018E8, 29 };
27 ProcessCommandList = 0x1EF018F0, 30
31 template<Id id>
32 struct Struct;
33
34 enum class FramebufferFormat : u32 {
35 RGBA8 = 0,
36 RGB8 = 1,
37 RGB565 = 2,
38 RGB5A1 = 3,
39 RGBA4 = 4,
40 };
41};
42
43template<>
44struct Regs::Struct<Regs::MemoryFill> {
45 u32 address_start;
46 u32 address_end; // ?
47 u32 size;
48 u32 value; // ?
49
50 inline u32 GetStartAddress() const {
51 return address_start * 8;
52 }
53
54 inline u32 GetEndAddress() const {
55 return address_end * 8;
56 }
57};
58static_assert(sizeof(Regs::Struct<Regs::MemoryFill>) == 0x10, "Structure size and register block length don't match");
59
60template<>
61struct Regs::Struct<Regs::FramebufferTop> {
62 using Format = Regs::FramebufferFormat;
63
64 union {
65 u32 size;
66
67 BitField< 0, 16, u32> width;
68 BitField<16, 16, u32> height;
69 };
70
71 u32 pad0[2];
72
73 u32 address_left1;
74 u32 address_left2;
75
76 union {
77 u32 format;
78
79 BitField< 0, 3, Format> color_format;
80 };
81
82 u32 pad1;
83
84 union {
85 u32 active_fb;
86
87 // 0: Use parameters ending with "1"
88 // 1: Use parameters ending with "2"
89 BitField<0, 1, u32> second_fb_active;
90 };
91
92 u32 pad2[5];
93
94 // Distance between two pixel rows, in bytes
95 u32 stride;
96
97 u32 address_right1;
98 u32 address_right2;
99};
100
101template<>
102struct Regs::Struct<Regs::FramebufferBottom> : public Regs::Struct<Regs::FramebufferTop> {
103};
104static_assert(sizeof(Regs::Struct<Regs::FramebufferTop>) == 0x40, "Structure size and register block length don't match");
105
106template<>
107struct Regs::Struct<Regs::DisplayTransfer> {
108 using Format = Regs::FramebufferFormat;
109
110 u32 input_address;
111 u32 output_address;
112
113 inline u32 GetPhysicalInputAddress() const {
114 return input_address * 8;
115 }
116
117 inline u32 GetPhysicalOutputAddress() const {
118 return output_address * 8;
119 }
120
121 union {
122 u32 output_size;
123
124 BitField< 0, 16, u32> output_width;
125 BitField<16, 16, u32> output_height;
126 };
127
128 union {
129 u32 input_size;
130
131 BitField< 0, 16, u32> input_width;
132 BitField<16, 16, u32> input_height;
28 }; 133 };
29 134
30 u32 framebuffer_top_left_1; 135 union {
31 u32 framebuffer_top_left_2; 136 u32 flags;
32 u32 framebuffer_top_right_1; 137
33 u32 framebuffer_top_right_2; 138 BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true
34 u32 framebuffer_sub_left_1; 139 BitField< 8, 3, Format> input_format;
35 u32 framebuffer_sub_left_2; 140 BitField<12, 3, Format> output_format;
36 u32 framebuffer_sub_right_1; 141 BitField<16, 1, u32> output_tiled; // stores output in a tiled format
37 u32 framebuffer_sub_right_2; 142 };
38 143
39 u32 command_list_size; 144 u32 unknown;
40 u32 command_list_address; 145
41 u32 command_processing_enabled; 146 // it seems that writing to this field triggers the display transfer
147 u32 trigger;
42}; 148};
149static_assert(sizeof(Regs::Struct<Regs::DisplayTransfer>) == 0x1C, "Structure size and register block length don't match");
150
151template<>
152struct Regs::Struct<Regs::CommandProcessor> {
153 // command list size
154 u32 size;
155
156 u32 pad0;
157
158 // command list address
159 u32 address;
43 160
44extern Registers g_regs; 161 u32 pad1;
162
163 // it seems that writing to this field triggers command list processing
164 u32 trigger;
165};
166static_assert(sizeof(Regs::Struct<Regs::CommandProcessor>) == 0x14, "Structure size and register block length don't match");
167
168
169extern RegisterSet<u32, Regs> g_regs;
45 170
46enum { 171enum {
47 TOP_ASPECT_X = 0x5, 172 TOP_ASPECT_X = 0x5,
@@ -51,23 +176,35 @@ enum {
51 TOP_WIDTH = 400, 176 TOP_WIDTH = 400,
52 BOTTOM_WIDTH = 320, 177 BOTTOM_WIDTH = 320,
53 178
54 // Physical addresses in FCRAM used by ARM9 applications - these are correct for real hardware 179 // Physical addresses in FCRAM (chosen arbitrarily)
55 PADDR_FRAMEBUFFER_SEL = 0x20184E59, 180 PADDR_TOP_LEFT_FRAME1 = 0x201D4C00,
56 PADDR_TOP_LEFT_FRAME1 = 0x20184E60, 181 PADDR_TOP_LEFT_FRAME2 = 0x202D4C00,
182 PADDR_TOP_RIGHT_FRAME1 = 0x203D4C00,
183 PADDR_TOP_RIGHT_FRAME2 = 0x204D4C00,
184 PADDR_SUB_FRAME1 = 0x205D4C00,
185 PADDR_SUB_FRAME2 = 0x206D4C00,
186 // Physical addresses in FCRAM used by ARM9 applications
187/* PADDR_TOP_LEFT_FRAME1 = 0x20184E60,
57 PADDR_TOP_LEFT_FRAME2 = 0x201CB370, 188 PADDR_TOP_LEFT_FRAME2 = 0x201CB370,
58 PADDR_TOP_RIGHT_FRAME1 = 0x20282160, 189 PADDR_TOP_RIGHT_FRAME1 = 0x20282160,
59 PADDR_TOP_RIGHT_FRAME2 = 0x202C8670, 190 PADDR_TOP_RIGHT_FRAME2 = 0x202C8670,
60 PADDR_SUB_FRAME1 = 0x202118E0, 191 PADDR_SUB_FRAME1 = 0x202118E0,
61 PADDR_SUB_FRAME2 = 0x20249CF0, 192 PADDR_SUB_FRAME2 = 0x20249CF0,*/
62 193
63 // Physical addresses in VRAM - I'm not sure how these are actually allocated (so not real) 194 // Physical addresses in VRAM
64 PADDR_VRAM_FRAMEBUFFER_SEL = 0x18184E59, 195 // TODO: These should just be deduced from the ones above
65 PADDR_VRAM_TOP_LEFT_FRAME1 = 0x18184E60, 196 PADDR_VRAM_TOP_LEFT_FRAME1 = 0x181D4C00,
66 PADDR_VRAM_TOP_LEFT_FRAME2 = 0x181CB370, 197 PADDR_VRAM_TOP_LEFT_FRAME2 = 0x182D4C00,
198 PADDR_VRAM_TOP_RIGHT_FRAME1 = 0x183D4C00,
199 PADDR_VRAM_TOP_RIGHT_FRAME2 = 0x184D4C00,
200 PADDR_VRAM_SUB_FRAME1 = 0x185D4C00,
201 PADDR_VRAM_SUB_FRAME2 = 0x186D4C00,
202 // Physical addresses in VRAM used by ARM9 applications
203/* PADDR_VRAM_TOP_LEFT_FRAME2 = 0x181CB370,
67 PADDR_VRAM_TOP_RIGHT_FRAME1 = 0x18282160, 204 PADDR_VRAM_TOP_RIGHT_FRAME1 = 0x18282160,
68 PADDR_VRAM_TOP_RIGHT_FRAME2 = 0x182C8670, 205 PADDR_VRAM_TOP_RIGHT_FRAME2 = 0x182C8670,
69 PADDR_VRAM_SUB_FRAME1 = 0x182118E0, 206 PADDR_VRAM_SUB_FRAME1 = 0x182118E0,
70 PADDR_VRAM_SUB_FRAME2 = 0x18249CF0, 207 PADDR_VRAM_SUB_FRAME2 = 0x18249CF0,*/
71}; 208};
72 209
73/// Framebuffer location 210/// Framebuffer location
@@ -79,7 +216,7 @@ enum FramebufferLocation {
79 216
80/** 217/**
81 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM 218 * Sets whether the framebuffers are in the GSP heap (FCRAM) or VRAM
82 * @param 219 * @param
83 */ 220 */
84void SetFramebufferLocation(const FramebufferLocation mode); 221void SetFramebufferLocation(const FramebufferLocation mode);
85 222
@@ -90,16 +227,18 @@ void SetFramebufferLocation(const FramebufferLocation mode);
90 */ 227 */
91const u8* GetFramebufferPointer(const u32 address); 228const u8* GetFramebufferPointer(const u32 address);
92 229
230u32 GetFramebufferAddr(const u32 address);
231
93/** 232/**
94 * Gets the location of the framebuffers 233 * Gets the location of the framebuffers
95 */ 234 */
96const FramebufferLocation GetFramebufferLocation(); 235FramebufferLocation GetFramebufferLocation(u32 address);
97 236
98template <typename T> 237template <typename T>
99inline void Read(T &var, const u32 addr); 238void Read(T &var, const u32 addr);
100 239
101template <typename T> 240template <typename T>
102inline void Write(u32 addr, const T data); 241void Write(u32 addr, const T data);
103 242
104/// Update hardware 243/// Update hardware
105void Update(); 244void Update();
diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h
index 92e9304ca..1055ed94f 100644
--- a/src/core/hw/hw.h
+++ b/src/core/hw/hw.h
@@ -9,10 +9,10 @@
9namespace HW { 9namespace HW {
10 10
11template <typename T> 11template <typename T>
12inline void Read(T &var, const u32 addr); 12void Read(T &var, const u32 addr);
13 13
14template <typename T> 14template <typename T>
15inline void Write(u32 addr, const T data); 15void Write(u32 addr, const T data);
16 16
17/// Update hardware 17/// Update hardware
18void Update(); 18void Update();
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index d3cbf414d..5ae88439a 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -13,16 +13,16 @@
13namespace Loader { 13namespace Loader {
14 14
15/// Loads an ELF/AXF file 15/// Loads an ELF/AXF file
16class AppLoader_ELF : public AppLoader { 16class AppLoader_ELF final : public AppLoader {
17public: 17public:
18 AppLoader_ELF(const std::string& filename); 18 AppLoader_ELF(const std::string& filename);
19 ~AppLoader_ELF(); 19 ~AppLoader_ELF() override;
20 20
21 /** 21 /**
22 * Load the bootable file 22 * Load the bootable file
23 * @return ResultStatus result of function 23 * @return ResultStatus result of function
24 */ 24 */
25 ResultStatus Load(); 25 ResultStatus Load() override;
26 26
27private: 27private:
28 std::string filename; 28 std::string filename;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 96cb81de0..2b42e3c64 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -4,9 +4,11 @@
4 4
5#include <memory> 5#include <memory>
6 6
7#include "core/file_sys/archive_romfs.h"
7#include "core/loader/loader.h" 8#include "core/loader/loader.h"
8#include "core/loader/elf.h" 9#include "core/loader/elf.h"
9#include "core/loader/ncch.h" 10#include "core/loader/ncch.h"
11#include "core/hle/kernel/archive.h"
10 12
11//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
12 14
@@ -51,14 +53,20 @@ ResultStatus LoadFile(const std::string& filename) {
51 switch (IdentifyFile(filename)) { 53 switch (IdentifyFile(filename)) {
52 54
53 // Standard ELF file format... 55 // Standard ELF file format...
54 case FileType::ELF: { 56 case FileType::ELF:
55 return AppLoader_ELF(filename).Load(); 57 return AppLoader_ELF(filename).Load();
56 }
57 58
58 // NCCH/NCSD container formats... 59 // NCCH/NCSD container formats...
59 case FileType::CXI: 60 case FileType::CXI:
60 case FileType::CCI: { 61 case FileType::CCI: {
61 return AppLoader_NCCH(filename).Load(); 62 AppLoader_NCCH app_loader(filename);
63
64 // Load application and RomFS
65 if (ResultStatus::Success == app_loader.Load()) {
66 Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS");
67 return ResultStatus::Success;
68 }
69 break;
62 } 70 }
63 71
64 // Error occurred durring IdentifyFile... 72 // Error occurred durring IdentifyFile...
@@ -70,7 +78,6 @@ ResultStatus LoadFile(const std::string& filename) {
70 default: 78 default:
71 return ResultStatus::ErrorInvalidFormat; 79 return ResultStatus::ErrorInvalidFormat;
72 } 80 }
73
74 return ResultStatus::Error; 81 return ResultStatus::Error;
75} 82}
76 83
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 95f16fcb1..4ba10de52 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -32,6 +32,7 @@ enum class ResultStatus {
32 ErrorNotLoaded, 32 ErrorNotLoaded,
33 ErrorNotUsed, 33 ErrorNotUsed,
34 ErrorAlreadyLoaded, 34 ErrorAlreadyLoaded,
35 ErrorMemoryAllocationFailed,
35}; 36};
36 37
37/// Interface for loading an application 38/// Interface for loading an application
@@ -48,60 +49,48 @@ public:
48 49
49 /** 50 /**
50 * Get the code (typically .code section) of the application 51 * Get the code (typically .code section) of the application
51 * @param error ResultStatus result of function 52 * @param buffer Reference to buffer to store data
52 * @return Reference to code buffer 53 * @return ResultStatus result of function
53 */ 54 */
54 virtual const std::vector<u8>& ReadCode(ResultStatus& error) const { 55 virtual ResultStatus ReadCode(std::vector<u8>& buffer) const {
55 error = ResultStatus::ErrorNotImplemented; 56 return ResultStatus::ErrorNotImplemented;
56 return code;
57 } 57 }
58 58
59 /** 59 /**
60 * Get the icon (typically icon section) of the application 60 * Get the icon (typically icon section) of the application
61 * @param error ResultStatus result of function 61 * @param buffer Reference to buffer to store data
62 * @return Reference to icon buffer 62 * @return ResultStatus result of function
63 */ 63 */
64 virtual const std::vector<u8>& ReadIcon(ResultStatus& error) const { 64 virtual ResultStatus ReadIcon(std::vector<u8>& buffer) const {
65 error = ResultStatus::ErrorNotImplemented; 65 return ResultStatus::ErrorNotImplemented;
66 return icon;
67 } 66 }
68 67
69 /** 68 /**
70 * Get the banner (typically banner section) of the application 69 * Get the banner (typically banner section) of the application
71 * @param error ResultStatus result of function 70 * @param buffer Reference to buffer to store data
72 * @return Reference to banner buffer 71 * @return ResultStatus result of function
73 */ 72 */
74 virtual const std::vector<u8>& ReadBanner(ResultStatus& error) const { 73 virtual ResultStatus ReadBanner(std::vector<u8>& buffer) const {
75 error = ResultStatus::ErrorNotImplemented; 74 return ResultStatus::ErrorNotImplemented;
76 return banner;
77 } 75 }
78 76
79 /** 77 /**
80 * Get the logo (typically logo section) of the application 78 * Get the logo (typically logo section) of the application
81 * @param error ResultStatus result of function 79 * @param buffer Reference to buffer to store data
82 * @return Reference to logo buffer 80 * @return ResultStatus result of function
83 */ 81 */
84 virtual const std::vector<u8>& ReadLogo(ResultStatus& error) const { 82 virtual ResultStatus ReadLogo(std::vector<u8>& buffer) const {
85 error = ResultStatus::ErrorNotImplemented; 83 return ResultStatus::ErrorNotImplemented;
86 return logo;
87 } 84 }
88 85
89 /** 86 /**
90 * Get the RomFs archive of the application 87 * Get the RomFS of the application
91 * @param error ResultStatus result of function 88 * @param buffer Reference to buffer to store data
92 * @return Reference to RomFs archive buffer 89 * @return ResultStatus result of function
93 */ 90 */
94 virtual const std::vector<u8>& ReadRomFS(ResultStatus& error) const { 91 virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const {
95 error = ResultStatus::ErrorNotImplemented; 92 return ResultStatus::ErrorNotImplemented;
96 return romfs;
97 } 93 }
98
99protected:
100 std::vector<u8> code; ///< ExeFS .code section
101 std::vector<u8> icon; ///< ExeFS .icon section
102 std::vector<u8> banner; ///< ExeFS .banner section
103 std::vector<u8> logo; ///< ExeFS .logo section
104 std::vector<u8> romfs; ///< RomFs archive
105}; 94};
106 95
107/** 96/**
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 60505bdfa..ba27eb75a 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -113,76 +113,80 @@ AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) {
113 113
114/// AppLoader_NCCH destructor 114/// AppLoader_NCCH destructor
115AppLoader_NCCH::~AppLoader_NCCH() { 115AppLoader_NCCH::~AppLoader_NCCH() {
116 if (file.IsOpen())
117 file.Close();
118} 116}
119 117
120/** 118/**
121 * Loads .code section into memory for booting 119 * Loads .code section into memory for booting
122 * @return ResultStatus result of function 120 * @return ResultStatus result of function
123 */ 121 */
124ResultStatus AppLoader_NCCH::LoadExec() { 122ResultStatus AppLoader_NCCH::LoadExec() const {
125 if (!is_loaded) 123 if (!is_loaded)
126 return ResultStatus::ErrorNotLoaded; 124 return ResultStatus::ErrorNotLoaded;
127 125
128 ResultStatus res; 126 std::vector<u8> code;
129 code = ReadCode(res); 127 if (ResultStatus::Success == ReadCode(code)) {
130
131 if (ResultStatus::Success == res) {
132 Memory::WriteBlock(entry_point, &code[0], code.size()); 128 Memory::WriteBlock(entry_point, &code[0], code.size());
133 Kernel::LoadExec(entry_point); 129 Kernel::LoadExec(entry_point);
130 return ResultStatus::Success;
134 } 131 }
135 return res; 132 return ResultStatus::Error;
136} 133}
137 134
138/** 135/**
139 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) 136 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
140 * @param name Name of section to read out of NCCH file 137 * @param name Name of section to read out of NCCH file
141 * @param buffer Vector to read data into 138 * @param buffer Vector to read data into
142 * @param error ResultStatus result of function 139 * @return ResultStatus result of function
143 * @return Reference to buffer of data that was read
144 */ 140 */
145const std::vector<u8>& AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer, 141ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const {
146 ResultStatus& error) {
147 // Iterate through the ExeFs archive until we find the .code file... 142 // Iterate through the ExeFs archive until we find the .code file...
148 for (int i = 0; i < kMaxSections; i++) { 143 File::IOFile file(filename, "rb");
149 // Load the specified section... 144 if (file.IsOpen()) {
150 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { 145 for (int i = 0; i < kMaxSections; i++) {
151 INFO_LOG(LOADER, "ExeFS section %d:", i); 146 // Load the specified section...
152 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); 147 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
153 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); 148 INFO_LOG(LOADER, "ExeFS section %d:", i);
154 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); 149 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
155 150 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
156 s64 section_offset = (exefs_header.section[i].offset + exefs_offset + 151 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
157 sizeof(ExeFs_Header) + ncch_offset); 152
158 file.Seek(section_offset, 0); 153 s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
159 154 sizeof(ExeFs_Header)+ncch_offset);
160 // Section is compressed... 155 file.Seek(section_offset, 0);
161 if (i == 0 && is_compressed) { 156
162 // Read compressed .code section... 157 // Section is compressed...
163 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]); 158 if (i == 0 && is_compressed) {
164 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); 159 // Read compressed .code section...
165 160 std::unique_ptr<u8[]> temp_buffer;
166 // Decompress .code section... 161 try {
167 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], 162 temp_buffer.reset(new u8[exefs_header.section[i].size]);
168 exefs_header.section[i].size); 163 } catch (std::bad_alloc&) {
169 buffer.resize(decompressed_size); 164 return ResultStatus::ErrorMemoryAllocationFailed;
170 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], 165 }
171 decompressed_size)) { 166 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
172 error = ResultStatus::ErrorInvalidFormat; 167
173 return buffer; 168 // Decompress .code section...
169 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0],
170 exefs_header.section[i].size);
171 buffer.resize(decompressed_size);
172 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],
173 decompressed_size)) {
174 return ResultStatus::ErrorInvalidFormat;
175 }
176 // Section is uncompressed...
174 } 177 }
175 // Section is uncompressed... 178 else {
176 } else { 179 buffer.resize(exefs_header.section[i].size);
177 buffer.resize(exefs_header.section[i].size); 180 file.ReadBytes(&buffer[0], exefs_header.section[i].size);
178 file.ReadBytes(&buffer[0], exefs_header.section[i].size); 181 }
182 return ResultStatus::Success;
179 } 183 }
180 error = ResultStatus::Success;
181 return buffer;
182 } 184 }
185 } else {
186 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
187 return ResultStatus::Error;
183 } 188 }
184 error = ResultStatus::ErrorNotUsed; 189 return ResultStatus::ErrorNotUsed;
185 return buffer;
186} 190}
187 191
188/** 192/**
@@ -197,8 +201,7 @@ ResultStatus AppLoader_NCCH::Load() {
197 if (is_loaded) 201 if (is_loaded)
198 return ResultStatus::ErrorAlreadyLoaded; 202 return ResultStatus::ErrorAlreadyLoaded;
199 203
200 file = File::IOFile(filename, "rb"); 204 File::IOFile file(filename, "rb");
201
202 if (file.IsOpen()) { 205 if (file.IsOpen()) {
203 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 206 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
204 207
@@ -241,72 +244,77 @@ ResultStatus AppLoader_NCCH::Load() {
241 LoadExec(); // Load the executable into memory for booting 244 LoadExec(); // Load the executable into memory for booting
242 245
243 return ResultStatus::Success; 246 return ResultStatus::Success;
247 } else {
248 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
244 } 249 }
245 return ResultStatus::Error; 250 return ResultStatus::Error;
246} 251}
247 252
248/** 253/**
249 * Get the code (typically .code section) of the application 254 * Get the code (typically .code section) of the application
250 * @param error ResultStatus result of function 255 * @param buffer Reference to buffer to store data
251 * @return Reference to code buffer 256 * @return ResultStatus result of function
252 */ 257 */
253const std::vector<u8>& AppLoader_NCCH::ReadCode(ResultStatus& error) { 258ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const {
254 return LoadSectionExeFS(".code", code, error); 259 return LoadSectionExeFS(".code", buffer);
255} 260}
256 261
257/** 262/**
258 * Get the icon (typically icon section) of the application 263 * Get the icon (typically icon section) of the application
259 * @param error ResultStatus result of function 264 * @param buffer Reference to buffer to store data
260 * @return Reference to icon buffer 265 * @return ResultStatus result of function
261 */ 266 */
262const std::vector<u8>& AppLoader_NCCH::ReadIcon(ResultStatus& error) { 267ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const {
263 return LoadSectionExeFS("icon", icon, error); 268 return LoadSectionExeFS("icon", buffer);
264} 269}
265 270
266/** 271/**
267 * Get the banner (typically banner section) of the application 272 * Get the banner (typically banner section) of the application
268 * @param error ResultStatus result of function 273 * @param buffer Reference to buffer to store data
269 * @return Reference to banner buffer 274 * @return ResultStatus result of function
270 */ 275 */
271const std::vector<u8>& AppLoader_NCCH::ReadBanner(ResultStatus& error) { 276ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const {
272 return LoadSectionExeFS("banner", banner, error); 277 return LoadSectionExeFS("banner", buffer);
273} 278}
274 279
275/** 280/**
276 * Get the logo (typically logo section) of the application 281 * Get the logo (typically logo section) of the application
277 * @param error ResultStatus result of function 282 * @param buffer Reference to buffer to store data
278 * @return Reference to logo buffer 283 * @return ResultStatus result of function
279 */ 284 */
280const std::vector<u8>& AppLoader_NCCH::ReadLogo(ResultStatus& error) { 285ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const {
281 return LoadSectionExeFS("logo", logo, error); 286 return LoadSectionExeFS("logo", buffer);
282} 287}
283 288
284/** 289/**
285 * Get the RomFs archive of the application 290 * Get the RomFS of the application
286 * @param error ResultStatus result of function 291 * @param buffer Reference to buffer to store data
287 * @return Reference to RomFs archive buffer 292 * @return ResultStatus result of function
288 */ 293 */
289const std::vector<u8>& AppLoader_NCCH::ReadRomFS(ResultStatus& error) { 294ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
290 // Check if the NCCH has a RomFS... 295 File::IOFile file(filename, "rb");
291 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { 296 if (file.IsOpen()) {
292 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; 297 // Check if the NCCH has a RomFS...
293 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; 298 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
299 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
300 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
294 301
295 INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); 302 INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset);
296 INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); 303 INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size);
297 304
298 romfs.resize(romfs_size); 305 buffer.resize(romfs_size);
299 306
300 file.Seek(romfs_offset, 0); 307 file.Seek(romfs_offset, 0);
301 file.ReadBytes(&romfs[0], romfs_size); 308 file.ReadBytes(&buffer[0], romfs_size);
302 309
303 error = ResultStatus::Success; 310 return ResultStatus::Success;
304 return romfs; 311 }
305 } else {
306 NOTICE_LOG(LOADER, "RomFS unused"); 312 NOTICE_LOG(LOADER, "RomFS unused");
313 return ResultStatus::ErrorNotUsed;
314 } else {
315 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
307 } 316 }
308 error = ResultStatus::ErrorNotUsed; 317 return ResultStatus::Error;
309 return romfs;
310} 318}
311 319
312} // namespace Loader 320} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index bf65425a4..29b59aa11 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -145,51 +145,51 @@ struct ExHeader_Header{
145namespace Loader { 145namespace Loader {
146 146
147/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) 147/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
148class AppLoader_NCCH : public AppLoader { 148class AppLoader_NCCH final : public AppLoader {
149public: 149public:
150 AppLoader_NCCH(const std::string& filename); 150 AppLoader_NCCH(const std::string& filename);
151 ~AppLoader_NCCH(); 151 ~AppLoader_NCCH() override;
152 152
153 /** 153 /**
154 * Load the application 154 * Load the application
155 * @return ResultStatus result of function 155 * @return ResultStatus result of function
156 */ 156 */
157 ResultStatus Load(); 157 ResultStatus Load() override;
158 158
159 /** 159 /**
160 * Get the code (typically .code section) of the application 160 * Get the code (typically .code section) of the application
161 * @param error ResultStatus result of function 161 * @param buffer Reference to buffer to store data
162 * @return Reference to code buffer 162 * @return ResultStatus result of function
163 */ 163 */
164 const std::vector<u8>& ReadCode(ResultStatus& error); 164 ResultStatus ReadCode(std::vector<u8>& buffer) const override;
165 165
166 /** 166 /**
167 * Get the icon (typically icon section) of the application 167 * Get the icon (typically icon section) of the application
168 * @param error ResultStatus result of function 168 * @param buffer Reference to buffer to store data
169 * @return Reference to icon buffer 169 * @return ResultStatus result of function
170 */ 170 */
171 const std::vector<u8>& ReadIcon(ResultStatus& error); 171 ResultStatus ReadIcon(std::vector<u8>& buffer) const override;
172 172
173 /** 173 /**
174 * Get the banner (typically banner section) of the application 174 * Get the banner (typically banner section) of the application
175 * @param error ResultStatus result of function 175 * @param buffer Reference to buffer to store data
176 * @return Reference to banner buffer 176 * @return ResultStatus result of function
177 */ 177 */
178 const std::vector<u8>& ReadBanner(ResultStatus& error); 178 ResultStatus ReadBanner(std::vector<u8>& buffer) const override;
179 179
180 /** 180 /**
181 * Get the logo (typically logo section) of the application 181 * Get the logo (typically logo section) of the application
182 * @param error ResultStatus result of function 182 * @param buffer Reference to buffer to store data
183 * @return Reference to logo buffer 183 * @return ResultStatus result of function
184 */ 184 */
185 const std::vector<u8>& ReadLogo(ResultStatus& error); 185 ResultStatus ReadLogo(std::vector<u8>& buffer) const override;
186 186
187 /** 187 /**
188 * Get the RomFs archive of the application 188 * Get the RomFS of the application
189 * @param error ResultStatus result of function 189 * @param buffer Reference to buffer to store data
190 * @return Reference to RomFs archive buffer 190 * @return ResultStatus result of function
191 */ 191 */
192 const std::vector<u8>& ReadRomFS(ResultStatus& error); 192 ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
193 193
194private: 194private:
195 195
@@ -197,19 +197,16 @@ private:
197 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) 197 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
198 * @param name Name of section to read out of NCCH file 198 * @param name Name of section to read out of NCCH file
199 * @param buffer Vector to read data into 199 * @param buffer Vector to read data into
200 * @param error ResultStatus result of function 200 * @return ResultStatus result of function
201 * @return Reference to buffer of data that was read
202 */ 201 */
203 const std::vector<u8>& LoadSectionExeFS(const char* name, std::vector<u8>& buffer, 202 ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const;
204 ResultStatus& error);
205 203
206 /** 204 /**
207 * Loads .code section into memory for booting 205 * Loads .code section into memory for booting
208 * @return ResultStatus result of function 206 * @return ResultStatus result of function
209 */ 207 */
210 ResultStatus LoadExec(); 208 ResultStatus LoadExec() const;
211 209
212 File::IOFile file;
213 std::string filename; 210 std::string filename;
214 211
215 bool is_loaded; 212 bool is_loaded;
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index d5899e4bb..12941f558 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -128,6 +128,12 @@ extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here
128void Init(); 128void Init();
129void Shutdown(); 129void Shutdown();
130 130
131template <typename T>
132inline void Read(T &var, const u32 addr);
133
134template <typename T>
135inline void Write(u32 addr, const T data);
136
131u8 Read8(const u32 addr); 137u8 Read8(const u32 addr);
132u16 Read16(const u32 addr); 138u16 Read16(const u32 addr);
133u32 Read32(const u32 addr); 139u32 Read32(const u32 addr);
@@ -144,14 +150,6 @@ void WriteBlock(const u32 addr, const u8* data, const int size);
144u8* GetPointer(const u32 Address); 150u8* GetPointer(const u32 Address);
145 151
146/** 152/**
147 * Maps a block of memory in shared memory
148 * @param handle Handle to map memory block for
149 * @param addr Address to map memory block to
150 * @param permissions Memory map permissions
151 */
152u32 MapBlock_Shared(u32 handle, u32 addr,u32 permissions) ;
153
154/**
155 * Maps a block of memory on the heap 153 * Maps a block of memory on the heap
156 * @param size Size of block in bytes 154 * @param size Size of block in bytes
157 * @param operation Memory map operation type 155 * @param operation Memory map operation type
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 37913119e..0342122df 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -41,7 +41,7 @@ u32 _VirtualAddress(const u32 addr) {
41} 41}
42 42
43template <typename T> 43template <typename T>
44inline void _Read(T &var, const u32 addr) { 44inline void Read(T &var, const u32 addr) {
45 // TODO: Figure out the fastest order of tests for both read and write (they are probably different). 45 // TODO: Figure out the fastest order of tests for both read and write (they are probably different).
46 // TODO: Make sure this represents the mirrors in a correct way. 46 // TODO: Make sure this represents the mirrors in a correct way.
47 // Could just do a base-relative read, too.... TODO 47 // Could just do a base-relative read, too.... TODO
@@ -91,7 +91,7 @@ inline void _Read(T &var, const u32 addr) {
91} 91}
92 92
93template <typename T> 93template <typename T>
94inline void _Write(u32 addr, const T data) { 94inline void Write(u32 addr, const T data) {
95 u32 vaddr = _VirtualAddress(addr); 95 u32 vaddr = _VirtualAddress(addr);
96 96
97 // Kernel memory command buffer 97 // Kernel memory command buffer
@@ -178,28 +178,6 @@ u8 *GetPointer(const u32 addr) {
178} 178}
179 179
180/** 180/**
181 * Maps a block of memory in shared memory
182 * @param handle Handle to map memory block for
183 * @param addr Address to map memory block to
184 * @param permissions Memory map permissions
185 */
186u32 MapBlock_Shared(u32 handle, u32 addr,u32 permissions) {
187 MemoryBlock block;
188
189 block.handle = handle;
190 block.base_address = addr;
191 block.permissions = permissions;
192
193 if (g_shared_map.size() > 0) {
194 const MemoryBlock last_block = g_shared_map.rbegin()->second;
195 block.address = last_block.address + last_block.size;
196 }
197 g_shared_map[block.GetVirtualAddress()] = block;
198
199 return block.GetVirtualAddress();
200}
201
202/**
203 * Maps a block of memory on the heap 181 * Maps a block of memory on the heap
204 * @param size Size of block in bytes 182 * @param size Size of block in bytes
205 * @param operation Memory map operation type 183 * @param operation Memory map operation type
@@ -247,25 +225,25 @@ u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) {
247 225
248u8 Read8(const u32 addr) { 226u8 Read8(const u32 addr) {
249 u8 _var = 0; 227 u8 _var = 0;
250 _Read<u8>(_var, addr); 228 Read<u8>(_var, addr);
251 return (u8)_var; 229 return (u8)_var;
252} 230}
253 231
254u16 Read16(const u32 addr) { 232u16 Read16(const u32 addr) {
255 u16_le _var = 0; 233 u16_le _var = 0;
256 _Read<u16_le>(_var, addr); 234 Read<u16_le>(_var, addr);
257 return (u16)_var; 235 return (u16)_var;
258} 236}
259 237
260u32 Read32(const u32 addr) { 238u32 Read32(const u32 addr) {
261 u32_le _var = 0; 239 u32_le _var = 0;
262 _Read<u32_le>(_var, addr); 240 Read<u32_le>(_var, addr);
263 return _var; 241 return _var;
264} 242}
265 243
266u64 Read64(const u32 addr) { 244u64 Read64(const u32 addr) {
267 u64_le _var = 0; 245 u64_le _var = 0;
268 _Read<u64_le>(_var, addr); 246 Read<u64_le>(_var, addr);
269 return _var; 247 return _var;
270} 248}
271 249
@@ -278,19 +256,19 @@ u32 Read16_ZX(const u32 addr) {
278} 256}
279 257
280void Write8(const u32 addr, const u8 data) { 258void Write8(const u32 addr, const u8 data) {
281 _Write<u8>(addr, data); 259 Write<u8>(addr, data);
282} 260}
283 261
284void Write16(const u32 addr, const u16 data) { 262void Write16(const u32 addr, const u16 data) {
285 _Write<u16_le>(addr, data); 263 Write<u16_le>(addr, data);
286} 264}
287 265
288void Write32(const u32 addr, const u32 data) { 266void Write32(const u32 addr, const u32 data) {
289 _Write<u32_le>(addr, data); 267 Write<u32_le>(addr, data);
290} 268}
291 269
292void Write64(const u32 addr, const u64 data) { 270void Write64(const u32 addr, const u64 data) {
293 _Write<u64_le>(addr, data); 271 Write<u64_le>(addr, data);
294} 272}
295 273
296void WriteBlock(const u32 addr, const u8* data, const int size) { 274void WriteBlock(const u32 addr, const u8* data, const int size) {
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 9b1e96888..43d0eef2c 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -15,7 +15,6 @@
15namespace System { 15namespace System {
16 16
17volatile State g_state; 17volatile State g_state;
18MetaFileSystem g_ctr_file_system;
19 18
20void UpdateState(State state) { 19void UpdateState(State state) {
21} 20}
@@ -45,7 +44,6 @@ void Shutdown() {
45 CoreTiming::Shutdown(); 44 CoreTiming::Shutdown();
46 VideoCore::Shutdown(); 45 VideoCore::Shutdown();
47 Kernel::Shutdown(); 46 Kernel::Shutdown();
48 g_ctr_file_system.Shutdown();
49} 47}
50 48
51} // namespace 49} // namespace
diff --git a/src/core/system.h b/src/core/system.h
index 09f1f6ebe..8f8ddf87b 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include "common/emu_window.h" 7#include "common/emu_window.h"
8#include "core/file_sys/meta_file_system.h"
9 8
10//////////////////////////////////////////////////////////////////////////////////////////////////// 9////////////////////////////////////////////////////////////////////////////////////////////////////
11 10
@@ -24,7 +23,6 @@ typedef enum {
24} State; 23} State;
25 24
26extern volatile State g_state; 25extern volatile State g_state;
27extern MetaFileSystem g_ctr_file_system;
28 26
29void UpdateState(State state); 27void UpdateState(State state);
30void Init(EmuWindow* emu_window); 28void Init(EmuWindow* emu_window);
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h
index 5d909beba..d92ceaa72 100644
--- a/src/video_core/gpu_debugger.h
+++ b/src/video_core/gpu_debugger.h
@@ -50,7 +50,7 @@ public:
50 virtual void GXCommandProcessed(int total_command_count) 50 virtual void GXCommandProcessed(int total_command_count)
51 { 51 {
52 const GSP_GPU::GXCommand& cmd = observed->ReadGXCommandHistory(total_command_count-1); 52 const GSP_GPU::GXCommand& cmd = observed->ReadGXCommandHistory(total_command_count-1);
53 ERROR_LOG(GSP, "Received command: id=%x", cmd.id); 53 ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value());
54 } 54 }
55 55
56 /** 56 /**
@@ -78,11 +78,13 @@ public:
78 78
79 void GXCommandProcessed(u8* command_data) 79 void GXCommandProcessed(u8* command_data)
80 { 80 {
81 if (observers.empty())
82 return;
83
81 gx_command_history.push_back(GSP_GPU::GXCommand()); 84 gx_command_history.push_back(GSP_GPU::GXCommand());
82 GSP_GPU::GXCommand& cmd = gx_command_history[gx_command_history.size()-1]; 85 GSP_GPU::GXCommand& cmd = gx_command_history[gx_command_history.size()-1];
83 86
84 const int cmd_length = sizeof(GSP_GPU::GXCommand); 87 memcpy(&cmd, command_data, sizeof(GSP_GPU::GXCommand));
85 memcpy(cmd.data, command_data, cmd_length);
86 88
87 ForEachObserver([this](DebuggerObserver* observer) { 89 ForEachObserver([this](DebuggerObserver* observer) {
88 observer->GXCommandProcessed(this->gx_command_history.size()); 90 observer->GXCommandProcessed(this->gx_command_history.size());
@@ -91,6 +93,9 @@ public:
91 93
92 void CommandListCalled(u32 address, u32* command_list, u32 size_in_words) 94 void CommandListCalled(u32 address, u32* command_list, u32 size_in_words)
93 { 95 {
96 if (observers.empty())
97 return;
98
94 PicaCommandList cmdlist; 99 PicaCommandList cmdlist;
95 for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;) 100 for (u32* parse_pointer = command_list; parse_pointer < command_list + size_in_words;)
96 { 101 {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 70af47c59..d0a8ec1da 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -12,8 +12,8 @@
12 12
13/// RendererOpenGL constructor 13/// RendererOpenGL constructor
14RendererOpenGL::RendererOpenGL() { 14RendererOpenGL::RendererOpenGL() {
15 memset(m_fbo, 0, sizeof(m_fbo)); 15 memset(m_fbo, 0, sizeof(m_fbo));
16 memset(m_fbo_rbo, 0, sizeof(m_fbo_rbo)); 16 memset(m_fbo_rbo, 0, sizeof(m_fbo_rbo));
17 memset(m_fbo_depth_buffers, 0, sizeof(m_fbo_depth_buffers)); 17 memset(m_fbo_depth_buffers, 0, sizeof(m_fbo_depth_buffers));
18 18
19 m_resolution_width = max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); 19 m_resolution_width = max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
@@ -35,7 +35,7 @@ void RendererOpenGL::SwapBuffers() {
35 m_render_window->MakeCurrent(); 35 m_render_window->MakeCurrent();
36 36
37 // EFB->XFB copy 37 // EFB->XFB copy
38 // TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some 38 // TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some
39 // register write We're also treating both framebuffers as a single one in OpenGL. 39 // register write We're also treating both framebuffers as a single one in OpenGL.
40 common::Rect framebuffer_size(0, 0, m_resolution_width, m_resolution_height); 40 common::Rect framebuffer_size(0, 0, m_resolution_width, m_resolution_height);
41 RenderXFB(framebuffer_size, framebuffer_size); 41 RenderXFB(framebuffer_size, framebuffer_size);
@@ -61,24 +61,40 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) {
61 int in_coord = 0; 61 int in_coord = 0;
62 for (int x = 0; x < VideoCore::kScreenTopWidth; x++) { 62 for (int x = 0; x < VideoCore::kScreenTopWidth; x++) {
63 for (int y = VideoCore::kScreenTopHeight-1; y >= 0; y--) { 63 for (int y = VideoCore::kScreenTopHeight-1; y >= 0; y--) {
64 // TODO: Properly support other framebuffer formats
64 int out_coord = (x + y * VideoCore::kScreenTopWidth) * 3; 65 int out_coord = (x + y * VideoCore::kScreenTopWidth) * 3;
65 out[out_coord] = in[in_coord]; 66 out[out_coord] = in[in_coord]; // blue?
66 out[out_coord + 1] = in[in_coord + 1]; 67 out[out_coord + 1] = in[in_coord + 1]; // green?
67 out[out_coord + 2] = in[in_coord + 2]; 68 out[out_coord + 2] = in[in_coord + 2]; // red?
68 in_coord+=3; 69 in_coord+=3;
69 } 70 }
70 } 71 }
71} 72}
72 73
73/** 74/**
74 * Renders external framebuffer (XFB) 75 * Renders external framebuffer (XFB)
75 * @param src_rect Source rectangle in XFB to copy 76 * @param src_rect Source rectangle in XFB to copy
76 * @param dst_rect Destination rectangle in output framebuffer to copy to 77 * @param dst_rect Destination rectangle in output framebuffer to copy to
77 */ 78 */
78void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { 79void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) {
79 80
80 FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_top_left_1), m_xfb_top_flipped); 81 const auto& framebuffer_top = GPU::g_regs.Get<GPU::Regs::FramebufferTop>();
81 FlipFramebuffer(GPU::GetFramebufferPointer(GPU::g_regs.framebuffer_sub_left_1), m_xfb_bottom_flipped); 82 const auto& framebuffer_sub = GPU::g_regs.Get<GPU::Regs::FramebufferBottom>();
83 const u32 active_fb_top = (framebuffer_top.active_fb == 1)
84 ? framebuffer_top.address_left2
85 : framebuffer_top.address_left1;
86 const u32 active_fb_sub = (framebuffer_sub.active_fb == 1)
87 ? framebuffer_sub.address_left2
88 : framebuffer_sub.address_left1;
89
90 DEBUG_LOG(GPU, "RenderXFB: 0x%08x bytes from 0x%08x(%dx%d), fmt %x",
91 framebuffer_top.stride * framebuffer_top.height,
92 GPU::GetFramebufferAddr(active_fb_top), (int)framebuffer_top.width,
93 (int)framebuffer_top.height, (int)framebuffer_top.format);
94
95 // TODO: This should consider the GPU registers for framebuffer width, height and stride.
96 FlipFramebuffer(GPU::GetFramebufferPointer(active_fb_top), m_xfb_top_flipped);
97 FlipFramebuffer(GPU::GetFramebufferPointer(active_fb_sub), m_xfb_bottom_flipped);
82 98
83 // Blit the top framebuffer 99 // Blit the top framebuffer
84 // ------------------------ 100 // ------------------------
@@ -98,7 +114,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
98 glReadBuffer(GL_COLOR_ATTACHMENT0); 114 glReadBuffer(GL_COLOR_ATTACHMENT0);
99 115
100 // Blit 116 // Blit
101 glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_, 117 glBlitFramebuffer(src_rect.x0_, src_rect.y0_, src_rect.x1_, src_rect.y1_,
102 dst_rect.x0_, dst_rect.y1_, dst_rect.x1_, dst_rect.y0_, 118 dst_rect.x0_, dst_rect.y1_, dst_rect.x1_, dst_rect.y0_,
103 GL_COLOR_BUFFER_BIT, GL_LINEAR); 119 GL_COLOR_BUFFER_BIT, GL_LINEAR);
104 120
@@ -110,7 +126,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
110 // Update textures with contents of XFB in RAM - bottom 126 // Update textures with contents of XFB in RAM - bottom
111 glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom); 127 glBindTexture(GL_TEXTURE_2D, m_xfb_texture_bottom);
112 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight, 128 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight,
113 GL_RGB, GL_UNSIGNED_BYTE, m_xfb_bottom_flipped); 129 GL_BGR, GL_UNSIGNED_BYTE, m_xfb_bottom_flipped);
114 glBindTexture(GL_TEXTURE_2D, 0); 130 glBindTexture(GL_TEXTURE_2D, 0);
115 131
116 // Render target is destination framebuffer 132 // Render target is destination framebuffer
@@ -124,7 +140,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
124 140
125 // Blit 141 // Blit
126 int offset = (VideoCore::kScreenTopWidth - VideoCore::kScreenBottomWidth) / 2; 142 int offset = (VideoCore::kScreenTopWidth - VideoCore::kScreenBottomWidth) / 2;
127 glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight, 143 glBlitFramebuffer(0,0, VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight,
128 offset, VideoCore::kScreenBottomHeight, VideoCore::kScreenBottomWidth + offset, 0, 144 offset, VideoCore::kScreenBottomHeight, VideoCore::kScreenBottomWidth + offset, 0,
129 GL_COLOR_BUFFER_BIT, GL_LINEAR); 145 GL_COLOR_BUFFER_BIT, GL_LINEAR);
130 146
@@ -133,7 +149,7 @@ void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect&
133 149
134/// Initialize the FBO 150/// Initialize the FBO
135void RendererOpenGL::InitFramebuffer() { 151void RendererOpenGL::InitFramebuffer() {
136 // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as 152 // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
137 // separate framebuffers 153 // separate framebuffers
138 154
139 // Init the FBOs 155 // Init the FBOs
@@ -146,12 +162,12 @@ void RendererOpenGL::InitFramebuffer() {
146 for (int i = 0; i < kMaxFramebuffers; i++) { 162 for (int i = 0; i < kMaxFramebuffers; i++) {
147 // Generate color buffer storage 163 // Generate color buffer storage
148 glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]); 164 glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]);
149 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth, 165 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth,
150 VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); 166 VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
151 167
152 // Generate depth buffer storage 168 // Generate depth buffer storage
153 glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]); 169 glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
154 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth, 170 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth,
155 VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); 171 VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
156 172
157 // Attach the buffers 173 // Attach the buffers
@@ -167,7 +183,7 @@ void RendererOpenGL::InitFramebuffer() {
167 } else { 183 } else {
168 ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer"); 184 ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer");
169 exit(1); 185 exit(1);
170 } 186 }
171 } 187 }
172 glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s) 188 glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s)
173 189
@@ -175,8 +191,8 @@ void RendererOpenGL::InitFramebuffer() {
175 // ------------------------------- 191 // -------------------------------
176 192
177 // Create XFB textures 193 // Create XFB textures
178 glGenTextures(1, &m_xfb_texture_top); 194 glGenTextures(1, &m_xfb_texture_top);
179 glGenTextures(1, &m_xfb_texture_bottom); 195 glGenTextures(1, &m_xfb_texture_bottom);
180 196
181 // Alocate video memorry for XFB textures 197 // Alocate video memorry for XFB textures
182 glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top); 198 glBindTexture(GL_TEXTURE_2D, m_xfb_texture_top);
@@ -192,13 +208,13 @@ void RendererOpenGL::InitFramebuffer() {
192 // Create the FBO and attach color/depth textures 208 // Create the FBO and attach color/depth textures
193 glGenFramebuffers(1, &m_xfb_top); // Generate framebuffer 209 glGenFramebuffers(1, &m_xfb_top); // Generate framebuffer
194 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_top); 210 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_top);
195 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 211 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
196 m_xfb_texture_top, 0); 212 m_xfb_texture_top, 0);
197 glBindFramebuffer(GL_FRAMEBUFFER, 0); 213 glBindFramebuffer(GL_FRAMEBUFFER, 0);
198 214
199 glGenFramebuffers(1, &m_xfb_bottom); // Generate framebuffer 215 glGenFramebuffers(1, &m_xfb_bottom); // Generate framebuffer
200 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_bottom); 216 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_xfb_bottom);
201 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 217 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
202 m_xfb_texture_bottom, 0); 218 m_xfb_texture_bottom, 0);
203 glBindFramebuffer(GL_FRAMEBUFFER, 0); 219 glBindFramebuffer(GL_FRAMEBUFFER, 0);
204} 220}
@@ -214,7 +230,7 @@ void RendererOpenGL::RenderFramebuffer() {
214 glReadBuffer(GL_COLOR_ATTACHMENT0); 230 glReadBuffer(GL_COLOR_ATTACHMENT0);
215 231
216 // Blit 232 // Blit
217 glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width, 233 glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width,
218 m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR); 234 m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
219 235
220 // Update the FPS count 236 // Update the FPS count
@@ -230,7 +246,7 @@ void RendererOpenGL::RenderFramebuffer() {
230void RendererOpenGL::UpdateFramerate() { 246void RendererOpenGL::UpdateFramerate() {
231} 247}
232 248
233/** 249/**
234 * Set the emulator window to use for renderer 250 * Set the emulator window to use for renderer
235 * @param window EmuWindow handle to emulator window to use for rendering 251 * @param window EmuWindow handle to emulator window to use for rendering
236 */ 252 */
@@ -264,7 +280,7 @@ void RendererOpenGL::Init() {
264 280
265 GLenum err = glewInit(); 281 GLenum err = glewInit();
266 if (GLEW_OK != err) { 282 if (GLEW_OK != err) {
267 ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...", 283 ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...",
268 glewGetErrorString(err)); 284 glewGetErrorString(err));
269 exit(-1); 285 exit(-1);
270 } 286 }
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index dd811cad6..30f4febe0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -84,7 +84,6 @@ private:
84 // "Flipped" framebuffers translate scanlines from native 3DS left-to-right to top-to-bottom 84 // "Flipped" framebuffers translate scanlines from native 3DS left-to-right to top-to-bottom
85 // as OpenGL expects them in a texture. There probably is a more efficient way of doing this: 85 // as OpenGL expects them in a texture. There probably is a more efficient way of doing this:
86 86
87 u8 m_xfb_top_flipped[VideoCore::kScreenTopWidth * VideoCore::kScreenTopWidth * 4]; 87 u8 m_xfb_top_flipped[VideoCore::kScreenTopWidth * VideoCore::kScreenTopHeight * 4];
88 u8 m_xfb_bottom_flipped[VideoCore::kScreenTopWidth * VideoCore::kScreenTopWidth * 4]; 88 u8 m_xfb_bottom_flipped[VideoCore::kScreenBottomWidth * VideoCore::kScreenBottomHeight * 4];
89 89};
90}; \ No newline at end of file