summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2014-07-04 21:58:08 -0400
committerGravatar bunnei2014-07-04 21:58:08 -0400
commitad1adb2f9270cc48bfbfd8b12ad1dac162c48e39 (patch)
tree6bb6ab148504beaacdfa02ed1dd069e3a5f61427 /src
parentMerge pull request #22 from bunnei/loader-improvements (diff)
parentNCCH: Updated ExeFS memory allocation to be safer. (diff)
downloadyuzu-ad1adb2f9270cc48bfbfd8b12ad1dac162c48e39.tar.gz
yuzu-ad1adb2f9270cc48bfbfd8b12ad1dac162c48e39.tar.xz
yuzu-ad1adb2f9270cc48bfbfd8b12ad1dac162c48e39.zip
Merge pull request #26 from bunnei/romfs-archive
Adds preliminary RomFS archive support
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/main.cpp2
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/core.vcxproj12
-rw-r--r--src/core/core.vcxproj.filters36
-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/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.h1
-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/service.cpp2
-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/system.cpp2
-rw-r--r--src/core/system.h2
27 files changed, 807 insertions, 1839 deletions
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/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 634f4d572..9ee803fda 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,17 @@ 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/archive.cpp
38 hle/kernel/event.cpp 38 hle/kernel/event.cpp
39 hle/kernel/kernel.cpp 39 hle/kernel/kernel.cpp
40 hle/kernel/mutex.cpp 40 hle/kernel/mutex.cpp
41 hle/kernel/thread.cpp 41 hle/kernel/thread.cpp
42 hle/service/apt.cpp 42 hle/service/apt.cpp
43 hle/service/fs.cpp
43 hle/service/gsp.cpp 44 hle/service/gsp.cpp
44 hle/service/hid.cpp 45 hle/service/hid.cpp
45 hle/service/ndm.cpp 46 hle/service/ndm.cpp
@@ -75,18 +76,19 @@ set(HEADERS core.h
75 arm/interpreter/vfp/asm_vfp.h 76 arm/interpreter/vfp/asm_vfp.h
76 arm/interpreter/vfp/vfp.h 77 arm/interpreter/vfp/vfp.h
77 arm/interpreter/vfp/vfp_helper.h 78 arm/interpreter/vfp/vfp_helper.h
78 file_sys/directory_file_system.h 79 file_sys/archive.h
79 file_sys/file_sys.h 80 file_sys/archive_romfs.h
80 file_sys/meta_file_system.h
81 hle/config_mem.h 81 hle/config_mem.h
82 hle/coprocessor.h 82 hle/coprocessor.h
83 hle/hle.h 83 hle/hle.h
84 hle/svc.h 84 hle/svc.h
85 hle/kernel/archive.h
85 hle/kernel/kernel.h 86 hle/kernel/kernel.h
86 hle/kernel/mutex.h 87 hle/kernel/mutex.h
87 hle/kernel/thread.h 88 hle/kernel/thread.h
88 hle/function_wrappers.h 89 hle/function_wrappers.h
89 hle/service/apt.h 90 hle/service/apt.h
91 hle/service/fs.h
90 hle/service/gsp.h 92 hle/service/gsp.h
91 hle/service/hid.h 93 hle/service/hid.h
92 hle/service/service.h 94 hle/service/service.h
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index e2216760a..4e521903c 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -162,16 +162,17 @@
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\archive.cpp" />
170 <ClCompile Include="hle\kernel\event.cpp" /> 170 <ClCompile Include="hle\kernel\event.cpp" />
171 <ClCompile Include="hle\kernel\kernel.cpp" /> 171 <ClCompile Include="hle\kernel\kernel.cpp" />
172 <ClCompile Include="hle\kernel\mutex.cpp" /> 172 <ClCompile Include="hle\kernel\mutex.cpp" />
173 <ClCompile Include="hle\kernel\thread.cpp" /> 173 <ClCompile Include="hle\kernel\thread.cpp" />
174 <ClCompile Include="hle\service\apt.cpp" /> 174 <ClCompile Include="hle\service\apt.cpp" />
175 <ClCompile Include="hle\service\fs.cpp" />
175 <ClCompile Include="hle\service\gsp.cpp" /> 176 <ClCompile Include="hle\service\gsp.cpp" />
176 <ClCompile Include="hle\service\hid.cpp" /> 177 <ClCompile Include="hle\service\hid.cpp" />
177 <ClCompile Include="hle\service\ndm.cpp" /> 178 <ClCompile Include="hle\service\ndm.cpp" />
@@ -211,18 +212,19 @@
211 <ClInclude Include="arm\interpreter\vfp\vfp_helper.h" /> 212 <ClInclude Include="arm\interpreter\vfp\vfp_helper.h" />
212 <ClInclude Include="core.h" /> 213 <ClInclude Include="core.h" />
213 <ClInclude Include="core_timing.h" /> 214 <ClInclude Include="core_timing.h" />
214 <ClInclude Include="file_sys\directory_file_system.h" /> 215 <ClInclude Include="file_sys\archive.h" />
215 <ClInclude Include="file_sys\file_sys.h" /> 216 <ClInclude Include="file_sys\archive_romfs.h" />
216 <ClInclude Include="file_sys\meta_file_system.h" />
217 <ClInclude Include="hle\config_mem.h" /> 217 <ClInclude Include="hle\config_mem.h" />
218 <ClInclude Include="hle\coprocessor.h" /> 218 <ClInclude Include="hle\coprocessor.h" />
219 <ClInclude Include="hle\function_wrappers.h" /> 219 <ClInclude Include="hle\function_wrappers.h" />
220 <ClInclude Include="hle\hle.h" /> 220 <ClInclude Include="hle\hle.h" />
221 <ClInclude Include="hle\kernel\archive.h" />
221 <ClInclude Include="hle\kernel\event.h" /> 222 <ClInclude Include="hle\kernel\event.h" />
222 <ClInclude Include="hle\kernel\kernel.h" /> 223 <ClInclude Include="hle\kernel\kernel.h" />
223 <ClInclude Include="hle\kernel\mutex.h" /> 224 <ClInclude Include="hle\kernel\mutex.h" />
224 <ClInclude Include="hle\kernel\thread.h" /> 225 <ClInclude Include="hle\kernel\thread.h" />
225 <ClInclude Include="hle\service\apt.h" /> 226 <ClInclude Include="hle\service\apt.h" />
227 <ClInclude Include="hle\service\fs.h" />
226 <ClInclude Include="hle\service\gsp.h" /> 228 <ClInclude Include="hle\service\gsp.h" />
227 <ClInclude Include="hle\service\hid.h" /> 229 <ClInclude Include="hle\service\hid.h" />
228 <ClInclude Include="hle\service\ndm.h" /> 230 <ClInclude Include="hle\service\ndm.h" />
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 91d3292da..17829b8b1 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,15 @@
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>
179 </ItemGroup> 182 </ItemGroup>
180 <ItemGroup> 183 <ItemGroup>
181 <ClInclude Include="arm\disassembler\arm_disasm.h"> 184 <ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -205,15 +208,6 @@
205 <ClInclude Include="arm\interpreter\skyeye_defs.h"> 208 <ClInclude Include="arm\interpreter\skyeye_defs.h">
206 <Filter>arm\interpreter</Filter> 209 <Filter>arm\interpreter</Filter>
207 </ClInclude> 210 </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"> 211 <ClInclude Include="hw\hw.h">
218 <Filter>hw</Filter> 212 <Filter>hw</Filter>
219 </ClInclude> 213 </ClInclude>
@@ -314,6 +308,18 @@
314 <ClInclude Include="loader\elf.h"> 308 <ClInclude Include="loader\elf.h">
315 <Filter>loader</Filter> 309 <Filter>loader</Filter>
316 </ClInclude> 310 </ClInclude>
311 <ClInclude Include="hle\kernel\archive.h">
312 <Filter>hle\kernel</Filter>
313 </ClInclude>
314 <ClInclude Include="hle\service\fs.h">
315 <Filter>hle\service</Filter>
316 </ClInclude>
317 <ClInclude Include="file_sys\archive.h">
318 <Filter>file_sys</Filter>
319 </ClInclude>
320 <ClInclude Include="file_sys\archive_romfs.h">
321 <Filter>file_sys</Filter>
322 </ClInclude>
317 </ItemGroup> 323 </ItemGroup>
318 <ItemGroup> 324 <ItemGroup>
319 <Text Include="CMakeLists.txt" /> 325 <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/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..69f4ddd37 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -29,6 +29,7 @@ enum class HandleType : u32 {
29 Arbiter = 9, 29 Arbiter = 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/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/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/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/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);