src/platform/3ds/3ds-vfs.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/platform/3ds/3ds-vfs.h>
7
8#ifdef USE_VFS_3DS
9#include <mgba-util/memory.h>
10#include <mgba-util/string.h>
11
12struct VFile3DS {
13 struct VFile d;
14
15 Handle handle;
16 u64 offset;
17};
18
19struct VDirEntry3DS {
20 struct VDirEntry d;
21 FS_DirectoryEntry ent;
22 char utf8Name[256];
23};
24
25struct VDir3DS {
26 struct VDir d;
27
28 char* path;
29 Handle handle;
30 struct VDirEntry3DS vde;
31};
32
33static bool _vf3dClose(struct VFile* vf);
34static off_t _vf3dSeek(struct VFile* vf, off_t offset, int whence);
35static ssize_t _vf3dRead(struct VFile* vf, void* buffer, size_t size);
36static ssize_t _vf3dWrite(struct VFile* vf, const void* buffer, size_t size);
37static void* _vf3dMap(struct VFile* vf, size_t size, int flags);
38static void _vf3dUnmap(struct VFile* vf, void* memory, size_t size);
39static void _vf3dTruncate(struct VFile* vf, size_t size);
40static ssize_t _vf3dSize(struct VFile* vf);
41static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size);
42
43static bool _vd3dClose(struct VDir* vd);
44static void _vd3dRewind(struct VDir* vd);
45static struct VDirEntry* _vd3dListNext(struct VDir* vd);
46static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode);
47static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path);
48static bool _vd3dDeleteFile(struct VDir* vd, const char* path);
49
50static const char* _vd3deName(struct VDirEntry* vde);
51static enum VFSType _vd3deType(struct VDirEntry* vde);
52
53struct VFile* VFileOpen3DS(FS_Archive* archive, const char* path, int flags) {
54 struct VFile3DS* vf3d = malloc(sizeof(struct VFile3DS));
55 if (!vf3d) {
56 return 0;
57 }
58
59 uint16_t utf16Path[PATH_MAX + 1];
60 ssize_t units = utf8_to_utf16(utf16Path, (const uint8_t*) path, PATH_MAX);
61 utf16Path[units] = 0;
62 FS_Path newPath = fsMakePath(PATH_UTF16, utf16Path);
63 Result res = FSUSER_OpenFile(&vf3d->handle, *archive, newPath, flags, 0);
64 if (res & 0xFFFC03FF) {
65 free(vf3d);
66 return 0;
67 }
68
69 vf3d->offset = 0;
70
71 vf3d->d.close = _vf3dClose;
72 vf3d->d.seek = _vf3dSeek;
73 vf3d->d.read = _vf3dRead;
74 vf3d->d.readline = VFileReadline;
75 vf3d->d.write = _vf3dWrite;
76 vf3d->d.map = _vf3dMap;
77 vf3d->d.unmap = _vf3dUnmap;
78 vf3d->d.truncate = _vf3dTruncate;
79 vf3d->d.size = _vf3dSize;
80 vf3d->d.sync = _vf3dSync;
81
82 return &vf3d->d;
83}
84
85bool _vf3dClose(struct VFile* vf) {
86 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
87
88 FSFILE_Close(vf3d->handle);
89 svcCloseHandle(vf3d->handle);
90 return true;
91}
92
93off_t _vf3dSeek(struct VFile* vf, off_t offset, int whence) {
94 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
95 u64 size;
96 switch (whence) {
97 case SEEK_SET:
98 vf3d->offset = offset;
99 break;
100 case SEEK_END:
101 FSFILE_GetSize(vf3d->handle, &size);
102 vf3d->offset = size;
103 // Fall through
104 case SEEK_CUR:
105 vf3d->offset += offset;
106 break;
107 }
108 return vf3d->offset;
109}
110
111ssize_t _vf3dRead(struct VFile* vf, void* buffer, size_t size) {
112 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
113 u32 sizeRead;
114 Result res = FSFILE_Read(vf3d->handle, &sizeRead, vf3d->offset, buffer, size);
115 if (res) {
116 return -1;
117 }
118 vf3d->offset += sizeRead;
119 return sizeRead;
120}
121
122ssize_t _vf3dWrite(struct VFile* vf, const void* buffer, size_t size) {
123 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
124 u32 sizeWritten;
125 Result res = FSFILE_Write(vf3d->handle, &sizeWritten, vf3d->offset, buffer, size, FS_WRITE_FLUSH);
126 if (res) {
127 return -1;
128 }
129 vf3d->offset += sizeWritten;
130 return sizeWritten;
131}
132
133static void* _vf3dMap(struct VFile* vf, size_t size, int flags) {
134 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
135 UNUSED(flags);
136 void* buffer = anonymousMemoryMap(size);
137 if (buffer) {
138 u32 sizeRead;
139 FSFILE_Read(vf3d->handle, &sizeRead, 0, buffer, size);
140 }
141 return buffer;
142}
143
144static void _vf3dUnmap(struct VFile* vf, void* memory, size_t size) {
145 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
146 u32 sizeWritten;
147 FSFILE_Write(vf3d->handle, &sizeWritten, 0, memory, size, FS_WRITE_FLUSH);
148 mappedMemoryFree(memory, size);
149}
150
151static void _vf3dTruncate(struct VFile* vf, size_t size) {
152 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
153 FSFILE_SetSize(vf3d->handle, size);
154}
155
156ssize_t _vf3dSize(struct VFile* vf) {
157 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
158 u64 size;
159 FSFILE_GetSize(vf3d->handle, &size);
160 return size;
161}
162
163static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size) {
164 struct VFile3DS* vf3d = (struct VFile3DS*) vf;
165 if (buffer) {
166 u32 sizeWritten;
167 Result res = FSFILE_Write(vf3d->handle, &sizeWritten, 0, buffer, size, FS_WRITE_FLUSH);
168 if (res) {
169 return false;
170 }
171 }
172 FSFILE_Flush(vf3d->handle);
173 return true;
174}
175
176struct VDir* VDirOpen(const char* path) {
177 struct VDir3DS* vd3d = malloc(sizeof(struct VDir3DS));
178 if (!vd3d) {
179 return 0;
180 }
181
182 uint16_t utf16Path[PATH_MAX + 1];
183 ssize_t units = utf8_to_utf16(utf16Path, (const uint8_t*) path, PATH_MAX);
184 utf16Path[units] = 0;
185 FS_Path newPath = fsMakePath(PATH_UTF16, utf16Path);
186 Result res = FSUSER_OpenDirectory(&vd3d->handle, sdmcArchive, newPath);
187 if (res & 0xFFFC03FF) {
188 free(vd3d);
189 return 0;
190 }
191
192 vd3d->path = strdup(path);
193
194 vd3d->d.close = _vd3dClose;
195 vd3d->d.rewind = _vd3dRewind;
196 vd3d->d.listNext = _vd3dListNext;
197 vd3d->d.openFile = _vd3dOpenFile;
198 vd3d->d.openDir = _vd3dOpenDir;
199 vd3d->d.deleteFile = _vd3dDeleteFile;
200
201 vd3d->vde.d.name = _vd3deName;
202 vd3d->vde.d.type = _vd3deType;
203
204 return &vd3d->d;
205}
206
207static bool _vd3dClose(struct VDir* vd) {
208 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
209 FSDIR_Close(vd3d->handle);
210 free(vd3d->path);
211 free(vd3d);
212 return true;
213}
214
215static void _vd3dRewind(struct VDir* vd) {
216 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
217 FSDIR_Close(vd3d->handle);
218 uint16_t utf16Path[PATH_MAX + 1];
219 ssize_t units = utf8_to_utf16(utf16Path, (const uint8_t*) vd3d->path, PATH_MAX);
220 utf16Path[units] = 0;
221 FS_Path newPath = fsMakePath(PATH_UTF16, utf16Path);
222 FSUSER_OpenDirectory(&vd3d->handle, sdmcArchive, newPath);
223}
224
225static struct VDirEntry* _vd3dListNext(struct VDir* vd) {
226 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
227 u32 n = 0;
228 memset(&vd3d->vde.ent, 0, sizeof(vd3d->vde.ent));
229 memset(vd3d->vde.utf8Name, 0, sizeof(vd3d->vde.utf8Name));
230 FSDIR_Read(vd3d->handle, &n, 1, &vd3d->vde.ent);
231 if (!n) {
232 return 0;
233 }
234 return &vd3d->vde.d;
235}
236
237static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode) {
238 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
239 if (!path) {
240 return 0;
241 }
242 const char* dir = vd3d->path;
243 char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
244 if (dir[strlen(dir) - 1] == '/') {
245 sprintf(combined, "%s%s", dir, path);
246 } else {
247 sprintf(combined, "%s/%s", dir, path);
248 }
249
250 struct VFile* file = VFileOpen(combined, mode);
251 free(combined);
252 return file;
253}
254
255static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path) {
256 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
257 if (!path) {
258 return 0;
259 }
260 const char* dir = vd3d->path;
261 char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
262 if (dir[strlen(dir) - 1] == '/') {
263 sprintf(combined, "%s%s", dir, path);
264 } else {
265 sprintf(combined, "%s/%s", dir, path);
266 }
267
268 struct VDir* vd2 = VDirOpen(combined);
269 if (!vd2) {
270 vd2 = VDirOpenArchive(combined);
271 }
272 free(combined);
273 return vd2;
274}
275
276static bool _vd3dDeleteFile(struct VDir* vd, const char* path) {
277 struct VDir3DS* vd3d = (struct VDir3DS*) vd;
278 if (!path) {
279 return 0;
280 }
281 const char* dir = vd3d->path;
282 char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
283 if (dir[strlen(dir) - 1] == '/') {
284 sprintf(combined, "%s%s", dir, path);
285 } else {
286 sprintf(combined, "%s/%s", dir, path);
287 }
288
289 uint16_t utf16Path[PATH_MAX + 1];
290 ssize_t units = utf8_to_utf16(utf16Path, (const uint8_t*) combined, PATH_MAX);
291 utf16Path[units] = 0;
292 FS_Path newPath = fsMakePath(PATH_UTF16, utf16Path);
293 bool ret = !FSUSER_DeleteFile(sdmcArchive, newPath);
294 free(combined);
295 return ret;
296}
297
298static const char* _vd3deName(struct VDirEntry* vde) {
299 struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
300 if (!vd3de->utf8Name[0]) {
301 utf16_to_utf8((uint8_t*) vd3de->utf8Name, vd3de->ent.name, sizeof(vd3de->utf8Name));
302 }
303 return vd3de->utf8Name;
304}
305
306static enum VFSType _vd3deType(struct VDirEntry* vde) {
307 struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
308 if (vd3de->ent.attributes & FS_ATTRIBUTE_DIRECTORY) {
309 return VFS_DIRECTORY;
310 }
311 return VFS_FILE;
312}
313
314bool VDirCreate(const char* path) {
315 Result rc = FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, path), 0);
316 return R_SUCCEEDED(rc) || rc == 0xC82044BE;
317}
318#endif