1#ifndef SEAD_FILEDEVICE_H_
2#define SEAD_FILEDEVICE_H_
3
4#include <basis/seadTypes.h>
5#include <container/seadSafeArray.h>
6#include <container/seadTList.h>
7#include <heap/seadDisposer.h>
8#include <heap/seadHeap.h>
9#include <prim/seadRuntimeTypeInfo.h>
10#include <prim/seadSafeString.h>
11
12namespace sead
13{
14class FileDevice;
15
16using HandleBuffer = SafeArray<u8, 32>;
17
18class HandleBase
19{
20public:
21 HandleBase() = default;
22 HandleBase(const HandleBase&) = delete;
23 HandleBase& operator=(const HandleBase&) = delete;
24 virtual ~HandleBase() = default;
25
26 FileDevice* getDevice() const { return mDevice; }
27 FileDevice* getOriginalDevice() const { return mOriginalDevice; }
28 bool isOpened() const { return mOriginalDevice != nullptr; }
29
30protected:
31 friend class FileDevice;
32
33 FileDevice* mDevice = nullptr;
34 FileDevice* mOriginalDevice = nullptr;
35 HandleBuffer mHandleBuffer{};
36};
37
38class FileHandle;
39class DirectoryHandle;
40struct DirectoryEntry;
41
42class FileDevice : public TListNode<FileDevice*>, public IDisposer
43{
44 SEAD_RTTI_BASE(FileDevice)
45
46public:
47 enum FileOpenFlag
48 {
49 cFileOpenFlag_ReadOnly = 0, // r
50 cFileOpenFlag_WriteOnly = 1, // w
51 cFileOpenFlag_ReadWrite = 2, // r+
52 cFileOpenFlag_Create = 3 // w+
53 };
54
55 enum SeekOrigin
56 {
57 cSeekOrigin_Begin = 0,
58 cSeekOrigin_Current = 1,
59 cSeekOrigin_End = 2
60 };
61
62 struct LoadArg
63 {
64 SafeString path;
65 u8* buffer = nullptr;
66 u32 buffer_size = 0;
67 Heap* heap = nullptr;
68 s32 alignment = 0;
69 s32 buffer_size_alignment = 0;
70 /// Read chunk size
71 u32 div_size = 0;
72 bool assert_on_alloc_fail = true;
73 bool check_read_entire_file = true;
74 u32 read_size = 0;
75 u32 roundup_size = 0;
76 bool need_unload = false;
77 };
78
79 struct SaveArg
80 {
81 SafeString path = "";
82 const u8* buffer = nullptr;
83 u32 buffer_size = 0;
84 u32 write_size = 0;
85 };
86
87public:
88 FileDevice() : TListNode<FileDevice*>(this), IDisposer(), mDriveName(), mPermission(true) {}
89
90 explicit FileDevice(const SafeString& name)
91 : TListNode<FileDevice*>(this), IDisposer(), mDriveName(), mPermission(true)
92 {
93 setDriveName(name);
94 }
95
96 ~FileDevice() override;
97
98 const SafeString& getDriveName() const { return mDriveName; }
99 void setDriveName(const SafeString& name)
100 {
101#ifdef SEAD_DEBUG
102 if (name.include(':'))
103 SEAD_WARN("drive name should not include ':'. (in %s)", name.cstr());
104#endif
105 mDriveName = name;
106 }
107
108 bool hasPermission() const { return mPermission; }
109 void setHasPermission(bool perm) { mPermission = perm; }
110
111 bool isAvailable() const;
112
113 u8* tryLoad(LoadArg& arg);
114
115 bool save(SaveArg& arg)
116 {
117 if (!trySave(arg))
118 {
119 SEAD_ASSERT_MSG(false, "file save error");
120 return false;
121 }
122 return true;
123 }
124 bool trySave(SaveArg& arg);
125
126 FileDevice* open(FileHandle* handle, const SafeString& path, FileOpenFlag flag, u32 divSize = 0)
127 {
128 auto* device = tryOpen(handle, path, flag, divSize);
129 if (!device)
130 {
131 SEAD_ASSERT_MSG(false, "file open error");
132 return nullptr;
133 }
134 return device;
135 }
136 FileDevice* tryOpen(FileHandle* handle, const SafeString& path, FileOpenFlag flag,
137 u32 divSize = 0);
138
139 bool close(FileHandle* handle)
140 {
141 if (!tryClose(handle))
142 {
143 SEAD_ASSERT_MSG(false, "file close error");
144 return false;
145 }
146 return true;
147 }
148 bool tryClose(FileHandle* handle);
149
150 bool flush(FileHandle* handle)
151 {
152 if (!tryFlush(handle))
153 {
154 SEAD_ASSERT_MSG(false, "file flush error");
155 return false;
156 }
157 return true;
158 }
159 bool tryFlush(FileHandle* handle);
160
161 bool remove(const SafeString& str)
162 {
163 if (!tryRemove(str))
164 {
165 SEAD_ASSERT_MSG(false, "file remove error");
166 return false;
167 }
168 return true;
169 }
170 bool tryRemove(const SafeString& str);
171
172 u32 read(FileHandle* handle, u8* data, u32 size)
173 {
174 u32 bytes_read = 0;
175 if (!tryRead(bytesRead: &bytes_read, handle, outBuffer: data, bytesToRead: size))
176 SEAD_ASSERT_MSG(false, "file read error");
177 return bytes_read;
178 }
179 bool tryRead(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead);
180
181 u32 write(FileHandle* handle, const u8* data, u32 size)
182 {
183 u32 bytes_written = 0;
184 if (!tryWrite(bytesWritten: &bytes_written, handle, inBuffer: data, bytesToWrite: size))
185 SEAD_ASSERT_MSG(false, "file write error");
186 return bytes_written;
187 }
188 bool tryWrite(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, u32 bytesToWrite);
189
190 bool seek(FileHandle* handle, s32 offset, SeekOrigin origin)
191 {
192 if (!trySeek(handle, offset, origin))
193 {
194 SEAD_ASSERT_MSG(false, "file seek error");
195 return false;
196 }
197 return true;
198 }
199 bool trySeek(FileHandle* handle, s32 offset, SeekOrigin origin);
200
201 u32 getCurrentSeekPos(FileHandle* handle)
202 {
203 u32 seek_pos = 0;
204 if (!tryGetCurrentSeekPos(seekPos: &seek_pos, handle))
205 SEAD_ASSERT_MSG(false, "tryGetCurrentSeekPos error");
206 return seek_pos;
207 }
208 bool tryGetCurrentSeekPos(u32* seekPos, FileHandle* handle);
209
210 u32 getFileSize(const SafeString& path)
211 {
212 u32 file_size = 0;
213 if (!tryGetFileSize(fileSize: &file_size, path))
214 SEAD_ASSERT_MSG(false, "tryGetFileSize error");
215 return file_size;
216 }
217 bool tryGetFileSize(u32* fileSize, const SafeString& path);
218
219 u32 getFileSize(FileHandle* handle)
220 {
221 u32 file_size = 0;
222 if (!tryGetFileSize(size: &file_size, handle))
223 SEAD_ASSERT_MSG(false, "tryGetFileSize error");
224 return file_size;
225 }
226 bool tryGetFileSize(u32* size, FileHandle* handle);
227
228 bool isExistFile(const SafeString& path)
229 {
230 bool exists = false;
231 if (!tryIsExistFile(exists: &exists, path))
232 SEAD_ASSERT_MSG(false, "tryIsExistFile error");
233 return exists;
234 }
235 bool tryIsExistFile(bool* exists, const SafeString& path);
236
237 bool isExistDirectory(const SafeString& path)
238 {
239 bool exists = false;
240 if (!tryIsExistDirectory(exists: &exists, path))
241 SEAD_ASSERT_MSG(false, "isExistDirectory failed");
242 return exists;
243 }
244 bool tryIsExistDirectory(bool* exists, const SafeString& path);
245
246 FileDevice* openDirectory(DirectoryHandle* handle, const SafeString& path)
247 {
248 auto* device = tryOpenDirectory(handle, path);
249 if (!device)
250 {
251 SEAD_ASSERT_MSG(false, "directory open error");
252 return nullptr;
253 }
254 return device;
255 }
256 FileDevice* tryOpenDirectory(DirectoryHandle* handle, const SafeString& path);
257
258 bool closeDirectory(DirectoryHandle* handle)
259 {
260 if (!tryCloseDirectory(handle))
261 {
262 SEAD_ASSERT_MSG(false, "directory close error");
263 return false;
264 }
265 return true;
266 }
267 bool tryCloseDirectory(DirectoryHandle* handle);
268
269 u32 readDirectory(DirectoryHandle* handle, DirectoryEntry* entries, u32 num_entries)
270 {
271 u32 entries_read = 0;
272 if (!tryReadDirectory(entriesRead: &entries_read, handle, entries, entriesToRead: num_entries))
273 SEAD_ASSERT_MSG(false, "directory read error");
274 return entries_read;
275 }
276 bool tryReadDirectory(u32* entriesRead, DirectoryHandle* handle, DirectoryEntry* entries,
277
278 u32 entriesToRead);
279
280 bool makeDirectory(const SafeString& path, u32 x)
281 {
282 if (!tryMakeDirectory(path, x))
283 {
284 SEAD_ASSERT_MSG(false, "directory make error");
285 return false;
286 }
287 return true;
288 }
289 bool tryMakeDirectory(const SafeString& path, u32);
290
291 bool makeDirectoryWithParent(const SafeString& path, u32 x)
292 {
293 if (!tryMakeDirectoryWithParent(path, x))
294 {
295 SEAD_ASSERT_MSG(false, "directory make with parent error");
296 return false;
297 }
298 return true;
299 }
300 bool tryMakeDirectoryWithParent(const SafeString& path, u32);
301
302 s32 getLastRawError() const;
303
304 virtual void traceFilePath(const SafeString& path) const;
305 virtual void traceDirectoryPath(const SafeString& path) const;
306 virtual void resolveFilePath(BufferedSafeString* out, const SafeString& path) const;
307 virtual void resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const;
308 virtual bool isMatchDevice_(const HandleBase* handle) const;
309
310#ifdef SWITCH
311 static const s32 cBufferMinAlignment = 0x20;
312#else
313 static const s32 cBufferMinAlignment = 0x40;
314#endif
315
316protected:
317 virtual bool doIsAvailable_() const = 0;
318 virtual u8* doLoad_(LoadArg& arg);
319 virtual bool doSave_(SaveArg& arg);
320 virtual FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag) = 0;
321 virtual bool doClose_(FileHandle* handle) = 0;
322 virtual bool doFlush_(FileHandle* handle) = 0;
323 virtual bool doRemove_(const SafeString& str) = 0;
324 virtual bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead) = 0;
325 virtual bool doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
326 u32 bytesToWrite) = 0;
327 virtual bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin) = 0;
328 virtual bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle) = 0;
329 virtual bool doGetFileSize_(u32* fileSize, const SafeString& path) = 0;
330 virtual bool doGetFileSize_(u32* fileSize, FileHandle* handle) = 0;
331 virtual bool doIsExistFile_(bool* exists, const SafeString& path) = 0;
332 virtual bool doIsExistDirectory_(bool* exists, const SafeString& path) = 0;
333 virtual FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path) = 0;
334 virtual bool doCloseDirectory_(DirectoryHandle* handle) = 0;
335 virtual bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle,
336 DirectoryEntry* entries, u32 entriesToRead) = 0;
337 virtual bool doMakeDirectory_(const SafeString& path, u32) = 0;
338 virtual s32 doGetLastRawError_() const = 0;
339 virtual void doTracePath_(const SafeString& path) const;
340 virtual void doResolvePath_(BufferedSafeString* out, const SafeString& path) const;
341
342 void setFileHandleDivSize_(FileHandle* handle, u32 divSize) const;
343 void setHandleBaseFileDevice_(HandleBase* handle, FileDevice* device) const;
344 void setHandleBaseOriginalFileDevice_(HandleBase* handle, FileDevice* device) const;
345 HandleBuffer& getHandleBaseHandleBuffer_(HandleBase* handle) const;
346
347 FixedSafeString<32> mDriveName;
348 bool mPermission;
349};
350
351class FileHandle : public HandleBase
352{
353public:
354 FileHandle() : HandleBase(), mDivSize(0) {}
355
356 virtual ~FileHandle()
357 {
358 FileDevice* _device = mOriginalDevice;
359 if (_device != NULL)
360 _device->tryClose(handle: this);
361 }
362
363 bool close();
364 bool tryClose();
365
366 bool flush();
367 bool tryFlush();
368
369 u32 read(u8* outBuffer, u32 bytesToRead);
370 bool tryRead(u32* actual_size, u8* data, u32 size);
371
372 u32 write(const u8* data, u32 size);
373 bool tryWrite(u32* actual_size, const u8* data, u32 size);
374
375 bool seek(s32 offset, FileDevice::SeekOrigin origin);
376 bool trySeek(s32 offset, FileDevice::SeekOrigin origin);
377 u32 getCurrentSeekPos();
378 bool tryGetCurrentSeekPos(u32* pos);
379
380 u32 getFileSize();
381 bool tryGetFileSize(u32* size);
382
383 u32 getDivSize() const { return mDivSize; }
384
385protected:
386 friend class FileDevice;
387
388 s32 mDivSize;
389};
390
391class DirectoryHandle : public HandleBase
392{
393public:
394 DirectoryHandle() : HandleBase() {}
395
396 virtual ~DirectoryHandle()
397 {
398 FileDevice* _device = mOriginalDevice;
399 if (_device != NULL)
400 _device->tryCloseDirectory(handle: this);
401 }
402
403 bool close();
404 bool tryClose();
405 u32 read(DirectoryEntry* entries, u32 count);
406 bool tryRead(u32* actual_count, DirectoryEntry* entries, u32 count);
407};
408
409struct DirectoryEntry
410{
411 DirectoryEntry() : name(), is_directory(false) {}
412
413 FixedSafeString<256> name;
414 bool is_directory;
415};
416
417} // namespace sead
418
419#endif // SEAD_FILEDEVICE_H_
420