summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/loader')
-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
5 files changed, 148 insertions, 147 deletions
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;