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