all repos — mgba @ 6a7e80c969740a73dcc0b7b41786bf71844aedea

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
 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