1#include "filedevice/nin/seadNinFileDeviceBaseNin.h"
2#include "filedevice/seadPath.h"
3
4#include <nn/fs/fs_directories.h>
5#include <nn/fs/fs_files.h>
6
7namespace sead
8{
9struct NinFileDeviceBase::FileHandleInner
10{
11 nn::fs::FileHandle mHandle;
12 s64 mOffset;
13 bool mIsWriteMode;
14 bool mDoNotFlushOnClose;
15};
16
17struct NinFileDeviceBase::DirectoryHandleInner
18{
19 nn::fs::DirectoryHandle mHandle;
20};
21
22NinFileDeviceBase::NinFileDeviceBase(const SafeString& name, const SafeString& mount_point)
23 : FileDevice(name), mMountPoint(mount_point)
24{
25}
26
27bool NinFileDeviceBase::doIsAvailable_() const
28{
29 return true;
30}
31
32// NON_MATCHING: inverted branching for should_set_size
33FileDevice* NinFileDeviceBase::doOpen_(FileHandle* handle, const SafeString& path,
34 FileDevice::FileOpenFlag flag)
35{
36 static constexpr u32 sModes[4] = {
37 nn::fs::OpenMode_Read,
38 nn::fs::OpenMode_Write | nn::fs::OpenMode_Append,
39 nn::fs::OpenMode_ReadWrite | nn::fs::OpenMode_Append,
40 nn::fs::OpenMode_Write | nn::fs::OpenMode_Append,
41 };
42 const u32 mode = flag <= 3u ? sModes[s32(flag)] : u32(nn::fs::OpenMode_Read);
43
44 FixedSafeString<256> fs_path;
45 if (!formatPathForFS_(out: &fs_path, path))
46 {
47 mLastError = nn::fs::ResultUnexpected();
48 SEAD_WARN("invalid path. path = %s", fs_path.cstr());
49 return nullptr;
50 }
51
52 bool should_set_size = true;
53 if ((flag | cFileOpenFlag_ReadWrite) == cFileOpenFlag_Create)
54 {
55 bool is_file = false;
56 nn::fs::DirectoryEntryType type;
57 const auto result = nn::fs::GetEntryType(&type, fs_path.cstr());
58 if (result.IsSuccess())
59 {
60 is_file = type == nn::fs::DirectoryEntryType_File;
61 }
62 else if (!nn::fs::ResultPathNotFound().Includes(result))
63 {
64 SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x "
65 "path = %s",
66 result.GetModule(), result.GetDescription(), result.GetInnerValueForDebug(),
67 fs_path.cstr());
68 mLastError = result;
69 return nullptr;
70 }
71
72 should_set_size = flag == cFileOpenFlag_Create || !is_file;
73 if (flag == cFileOpenFlag_Create || !is_file)
74 {
75 if (is_file)
76 {
77 mLastError = nn::fs::ResultPathAlreadyExists();
78 return nullptr;
79 }
80 const auto create_result = nn::fs::CreateFile(path: fs_path.cstr(), size: 0);
81 if (create_result.IsFailure())
82 {
83 SEAD_WARN("nn::fs::CreateFile failed. module = %d desc = %d inner_value = 0x%08x "
84 "path = %s",
85 create_result.GetModule(), create_result.GetDescription(),
86 create_result.GetInnerValueForDebug(), fs_path.cstr());
87 mLastError = create_result;
88 return nullptr;
89 }
90 }
91 }
92
93 auto* handle_inner = getFileHandleInner_(handle, construct: true);
94 handle_inner->mOffset = 0;
95 handle_inner->mIsWriteMode = (mode >> 1) & 1;
96 handle_inner->mDoNotFlushOnClose = false;
97
98 const auto open_result = nn::fs::OpenFile(handleOut: &handle_inner->mHandle, path: fs_path.cstr(), mode);
99 mLastError = open_result;
100 if (open_result.IsFailure())
101 {
102 if (!nn::fs::ResultPathNotFound().Includes(result: open_result))
103 SEAD_WARN(
104 "nn::fs::OpenFile failed. module = %d desc = %d inner_value = 0x%08x path = %s",
105 open_result.GetModule(), open_result.GetDescription(),
106 open_result.GetInnerValueForDebug(), fs_path.cstr());
107 return nullptr;
108 }
109
110 if (flag == cFileOpenFlag_WriteOnly && !should_set_size)
111 {
112 const auto set_result = nn::fs::SetFileSize(fileHandle: handle_inner->mHandle, filesize: 0);
113 if (set_result.IsFailure())
114 {
115 SEAD_WARN("nn::fs::SetFileSize failed. module = %d desc = %d inner_value = 0x%08x path "
116 "= %s",
117 set_result.GetModule(), set_result.GetDescription(),
118 set_result.GetInnerValueForDebug(), fs_path.cstr());
119 nn::fs::CloseFile(handle: handle_inner->mHandle);
120 mLastError = set_result;
121 return nullptr;
122 }
123 }
124
125 return this;
126}
127
128bool NinFileDeviceBase::doClose_(FileHandle* handle)
129{
130 const auto* inner = getFileHandleInner_(handle);
131
132 if (inner->mIsWriteMode && !inner->mDoNotFlushOnClose)
133 {
134 const auto result = nn::fs::FlushFile(handle: inner->mHandle);
135 if (result.IsFailure())
136 {
137 mLastError = result;
138 nn::fs::CloseFile(handle: inner->mHandle);
139 return false;
140 }
141 }
142
143 nn::fs::CloseFile(handle: inner->mHandle);
144 mLastError = nn::ResultSuccess();
145 return true;
146}
147
148bool NinFileDeviceBase::doFlush_(FileHandle* handle)
149{
150 auto* inner = getFileHandleInner_(handle);
151
152 mLastError = nn::fs::FlushFile(handle: inner->mHandle);
153 if (mLastError.IsFailure())
154 {
155 inner->mDoNotFlushOnClose = true;
156 return false;
157 }
158 return true;
159}
160
161bool NinFileDeviceBase::doRemove_(const SafeString& path)
162{
163 FixedSafeString<256> fs_path;
164 if (!formatPathForFS_(out: &fs_path, path))
165 {
166 mLastError = nn::fs::ResultUnexpected();
167 SEAD_WARN("invalid path. path = %s.", path.cstr());
168 return false;
169 }
170
171 mLastError = nn::fs::DeleteFile(path: fs_path.cstr());
172 if (mLastError.IsFailure())
173 {
174 SEAD_WARN("nn::fs::DeleteFile failed. module = %d desc = %d inner_value = 0x%08x path = %s",
175 mLastError.GetModule(), mLastError.GetDescription(),
176 mLastError.GetInnerValueForDebug(), fs_path.cstr());
177 return false;
178 }
179 return true;
180}
181
182bool NinFileDeviceBase::doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
183{
184 auto* inner = getFileHandleInner_(handle);
185
186 u64 out_size = 0;
187 mLastError = nn::fs::ReadFile(outSize: &out_size, handle: inner->mHandle, offset: inner->mOffset, buffer: outBuffer, bufferSize: bytesToRead,
188 option: nn::fs::ReadOption{});
189 if (mLastError.IsFailure())
190 {
191 SEAD_WARN("nn::fs::ReadFile failed. module = %d desc = %d inner_value = 0x%08x",
192 mLastError.GetModule(), mLastError.GetDescription(),
193 mLastError.GetInnerValueForDebug());
194 return false;
195 }
196
197 inner->mOffset += out_size;
198 if (bytesRead)
199 *bytesRead = out_size;
200
201 return true;
202}
203
204bool NinFileDeviceBase::doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
205 u32 bytesToWrite)
206{
207 auto* inner = getFileHandleInner_(handle);
208
209 mLastError = nn::fs::WriteFile(handle: inner->mHandle, position: inner->mOffset, buffer: inBuffer, size: bytesToWrite,
210 option: nn::fs::WriteOption{});
211 if (mLastError.IsSuccess())
212 {
213 inner->mOffset += bytesToWrite;
214 if (bytesWritten)
215 *bytesWritten = bytesToWrite;
216 return true;
217 }
218
219 SEAD_WARN("nn::fs::WriteFile failed. module = %d desc = %d inner_value = 0x%08x",
220 mLastError.GetModule(), mLastError.GetDescription(),
221 mLastError.GetInnerValueForDebug());
222 inner->mDoNotFlushOnClose = true;
223 return false;
224}
225
226bool NinFileDeviceBase::doSeek_(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
227{
228 auto* inner = getFileHandleInner_(handle);
229 switch (origin)
230 {
231 case FileDevice::cSeekOrigin_Begin:
232 inner->mOffset = offset;
233 return true;
234 case FileDevice::cSeekOrigin_Current:
235 inner->mOffset += offset;
236 return true;
237 case FileDevice::cSeekOrigin_End:
238 {
239 SEAD_ASSERT(offset <= 0);
240 u32 file_size = 0;
241 if (!doGetFileSize_(fileSize: &file_size, handle))
242 break;
243 inner->mOffset = file_size + offset;
244 return true;
245 }
246 }
247 return false;
248}
249
250bool NinFileDeviceBase::doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle)
251{
252 *seekPos = getFileHandleInner_(handle)->mOffset;
253 return true;
254}
255
256bool NinFileDeviceBase::doGetFileSize_(u32* fileSize, const SafeString& path)
257{
258 FileHandle handle;
259 if (!doOpen_(handle: &handle, path, flag: cFileOpenFlag_ReadOnly))
260 return false;
261
262 const bool ret = doGetFileSize_(fileSize, handle: &handle);
263 doClose_(handle: &handle);
264 return ret;
265}
266
267bool NinFileDeviceBase::doGetFileSize_(u32* fileSize, FileHandle* handle)
268{
269 const auto* inner = getFileHandleInner_(handle);
270 s64 size = 0;
271 mLastError = nn::fs::GetFileSize(size: &size, handle: inner->mHandle);
272 if (mLastError.IsSuccess())
273 {
274 *fileSize = size;
275 return true;
276 }
277
278 SEAD_WARN("nn::fs::GetFileSize failed. module = %d desc = %d inner_value = 0x%08x",
279 mLastError.GetModule(), mLastError.GetDescription(),
280 mLastError.GetInnerValueForDebug());
281 return false;
282}
283
284bool NinFileDeviceBase::doIsExistFile_(bool* exists, const SafeString& path)
285{
286 FixedSafeString<256> fs_path;
287 if (!formatPathForFS_(out: &fs_path, path))
288 {
289 mLastError = nn::fs::ResultUnexpected();
290 SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
291 return false;
292 }
293
294 nn::fs::DirectoryEntryType type;
295 mLastError = nn::fs::GetEntryType(&type, fs_path.cstr());
296
297 if (mLastError.IsSuccess())
298 {
299 *exists = type == nn::fs::DirectoryEntryType_File;
300 return true;
301 }
302
303 if (nn::fs::ResultPathNotFound().Includes(result: mLastError))
304 {
305 *exists = false;
306 return true;
307 }
308
309 SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x path = %s",
310 mLastError.GetModule(), mLastError.GetDescription(),
311 mLastError.GetInnerValueForDebug(), fs_path.cstr());
312 return false;
313}
314
315bool NinFileDeviceBase::doIsExistDirectory_(bool* exists, const SafeString& path)
316{
317 FixedSafeString<256> fs_path;
318 if (!formatPathForFS_(out: &fs_path, path))
319 {
320 mLastError = nn::fs::ResultUnexpected();
321 SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
322 return false;
323 }
324
325 nn::fs::DirectoryEntryType type;
326 mLastError = nn::fs::GetEntryType(&type, fs_path.cstr());
327
328 if (mLastError.IsSuccess())
329 {
330 *exists = type == nn::fs::DirectoryEntryType_Directory;
331 return true;
332 }
333
334 if (nn::fs::ResultPathNotFound().Includes(result: mLastError))
335 {
336 *exists = false;
337 return true;
338 }
339
340 SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x path = %s",
341 mLastError.GetModule(), mLastError.GetDescription(),
342 mLastError.GetInnerValueForDebug(), fs_path.cstr());
343 return false;
344}
345
346FileDevice* NinFileDeviceBase::doOpenDirectory_(DirectoryHandle* handle, const SafeString& path)
347{
348 auto* inner = getDirectoryHandleInner_(handle, construct: true);
349
350 FixedSafeString<256> fs_path;
351 if (!formatPathForFS_(out: &fs_path, path))
352 {
353 mLastError = nn::fs::ResultUnexpected();
354 SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
355 return nullptr;
356 }
357
358 mLastError =
359 nn::fs::OpenDirectory(handleOut: &inner->mHandle, path: fs_path.cstr(), openMode: nn::fs::OpenDirectoryMode_All);
360 if (mLastError.IsSuccess())
361 return this;
362
363 if (nn::fs::ResultPathNotFound().Includes(result: mLastError))
364 return nullptr;
365
366 SEAD_WARN("nn::fs::OpenDirectory failed. module = %d desc = %d inner_value = 0x%08x path = %s",
367 mLastError.GetModule(), mLastError.GetDescription(),
368 mLastError.GetInnerValueForDebug(), fs_path.cstr());
369 return nullptr;
370}
371
372bool NinFileDeviceBase::doCloseDirectory_(DirectoryHandle* handle)
373{
374 nn::fs::CloseDirectory(handle: getDirectoryHandleInner_(handle)->mHandle);
375 return true;
376}
377
378bool NinFileDeviceBase::doReadDirectory_(u32* entries_read, DirectoryHandle* handle,
379 DirectoryEntry* entries, u32 num_entries)
380{
381 const auto* inner = getDirectoryHandleInner_(handle);
382
383 for (u32 i = 0; i < num_entries; ++i)
384 {
385 nn::fs::DirectoryEntry entry;
386 s64 count = 0;
387 mLastError = nn::fs::ReadDirectory(entryCountOut: &count, entriesOut: &entry, handle: inner->mHandle, entryBufferLength: 1);
388 if (mLastError.IsFailure())
389 {
390 SEAD_WARN("nn::fs::ReadDirectory failed. module = %d desc = %d inner_value = 0x%08x",
391 mLastError.GetModule(), mLastError.GetDescription(),
392 mLastError.GetInnerValueForDebug());
393 return false;
394 }
395
396 // No more entries to read.
397 if (count != 1)
398 {
399 if (entries_read)
400 *entries_read = i;
401 return true;
402 }
403
404 entries[i].name = entry.mName;
405 entries[i].is_directory = entry.mTypeByte == nn::fs::DirectoryEntryType_Directory;
406 }
407
408 if (entries_read)
409 *entries_read = num_entries;
410 return true;
411}
412
413bool NinFileDeviceBase::doMakeDirectory_(const SafeString& path, u32)
414{
415 FixedSafeString<256> fs_path;
416 if (!formatPathForFS_(out: &fs_path, path))
417 {
418 mLastError = nn::fs::ResultUnexpected();
419 SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
420 return false;
421 }
422
423 const auto result = nn::fs::CreateDirectory(path: fs_path.cstr());
424 mLastError = result;
425 if (result.IsSuccess())
426 return true;
427
428 SEAD_WARN("nn::fs::CreateDirectory[%s] failed. module = %d desc = %d inner_value = 0x%08x",
429 fs_path.cstr(), result.GetModule(), result.GetDescription(),
430 result.GetInnerValueForDebug());
431 return false;
432}
433
434s32 NinFileDeviceBase::doGetLastRawError_() const
435{
436 return mLastError.GetInnerValueForDebug();
437}
438
439void NinFileDeviceBase::doResolvePath_(BufferedSafeString* out, const SafeString& path) const
440{
441 formatPathForFS_(out, path);
442}
443
444bool NinFileDeviceBase::formatPathForFS_(BufferedSafeString* out, const SafeString& path) const
445{
446 out->format(formatStr: "%s:/%s", mMountPoint.cstr(), path.cstr());
447 Path::changeDelimiter(out, delimiter: '/');
448 return true;
449}
450
451NinFileDeviceBase::FileHandleInner* NinFileDeviceBase::getFileHandleInner_(HandleBase* handle,
452 bool construct) const
453{
454 auto* buffer = getHandleBaseHandleBuffer_(handle).getBufferPtr();
455 static_assert(sizeof(FileHandleInner) <= sizeof(HandleBuffer));
456 static_assert(alignof(FileHandleInner) <= alignof(HandleBase));
457 if (construct)
458 return new (buffer) FileHandleInner;
459 return reinterpret_cast<FileHandleInner*>(buffer);
460}
461
462NinFileDeviceBase::DirectoryHandleInner*
463NinFileDeviceBase::getDirectoryHandleInner_(HandleBase* handle, bool construct) const
464{
465 auto* buffer = getHandleBaseHandleBuffer_(handle).getBufferPtr();
466 static_assert(sizeof(DirectoryHandleInner) <= sizeof(HandleBuffer));
467 static_assert(alignof(DirectoryHandleInner) <= alignof(HandleBase));
468 if (construct)
469 return new (buffer) DirectoryHandleInner;
470 return reinterpret_cast<DirectoryHandleInner*>(buffer);
471}
472} // namespace sead
473