all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

mGBA Game Boy Advance Emulator

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