all repos — mgba @ 211616e4f139d90870f5bcfb798485e493e5bd8d

mGBA Game Boy Advance Emulator

src/util/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 "util/vfs.h"
  7
  8#include "util/string.h"
  9
 10#include <fcntl.h>
 11#include <dirent.h>
 12
 13#ifdef _WIN32
 14#include <io.h>
 15#include <windows.h>
 16#define PATH_SEP '\\'
 17#elif defined(_3DS)
 18#include "util/memory.h"
 19#define PATH_SEP '/'
 20#else
 21#include <sys/mman.h>
 22#define PATH_SEP '/'
 23#endif
 24
 25struct VFileFD {
 26	struct VFile d;
 27	int fd;
 28#ifdef _WIN32
 29	HANDLE hMap;
 30#endif
 31};
 32
 33static bool _vfdClose(struct VFile* vf);
 34static off_t _vfdSeek(struct VFile* vf, off_t offset, int whence);
 35static ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size);
 36static ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size);
 37static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
 38static void* _vfdMap(struct VFile* vf, size_t size, int flags);
 39static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
 40static void _vfdTruncate(struct VFile* vf, size_t size);
 41
 42static bool _vdClose(struct VDir* vd);
 43static void _vdRewind(struct VDir* vd);
 44static struct VDirEntry* _vdListNext(struct VDir* vd);
 45static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
 46
 47static const char* _vdeName(struct VDirEntry* vde);
 48
 49struct VFile* VFileOpen(const char* path, int flags) {
 50	if (!path) {
 51		return 0;
 52	}
 53#ifdef _WIN32
 54	flags |= O_BINARY;
 55#endif
 56	int fd = open(path, flags, 0666);
 57	return VFileFromFD(fd);
 58}
 59
 60struct VFile* VFileFromFD(int fd) {
 61	if (fd < 0) {
 62		return 0;
 63	}
 64
 65	struct VFileFD* vfd = malloc(sizeof(struct VFileFD));
 66	if (!vfd) {
 67		return 0;
 68	}
 69
 70	vfd->fd = fd;
 71	vfd->d.close = _vfdClose;
 72	vfd->d.seek = _vfdSeek;
 73	vfd->d.read = _vfdRead;
 74	vfd->d.readline = _vfdReadline;
 75	vfd->d.write = _vfdWrite;
 76	vfd->d.map = _vfdMap;
 77	vfd->d.unmap = _vfdUnmap;
 78	vfd->d.truncate = _vfdTruncate;
 79
 80	return &vfd->d;
 81}
 82
 83bool _vfdClose(struct VFile* vf) {
 84	struct VFileFD* vfd = (struct VFileFD*) vf;
 85	if (close(vfd->fd) < 0) {
 86		return false;
 87	}
 88	free(vfd);
 89	return true;
 90}
 91
 92off_t _vfdSeek(struct VFile* vf, off_t offset, int whence) {
 93	struct VFileFD* vfd = (struct VFileFD*) vf;
 94	return lseek(vfd->fd, offset, whence);
 95}
 96
 97ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size) {
 98	struct VFileFD* vfd = (struct VFileFD*) vf;
 99	return read(vfd->fd, buffer, size);
100}
101
102ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size) {
103	struct VFileFD* vfd = (struct VFileFD*) vf;
104	size_t bytesRead = 0;
105	while (bytesRead < size - 1) {
106		size_t newRead = read(vfd->fd, &buffer[bytesRead], 1);
107		bytesRead += newRead;
108		if (!newRead || buffer[bytesRead] == '\n') {
109			break;
110		}
111	}
112	return buffer[bytesRead] = '\0';
113}
114
115ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size) {
116	struct VFileFD* vfd = (struct VFileFD*) vf;
117	return write(vfd->fd, buffer, size);
118}
119
120#ifdef _WIN32
121static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
122	struct VFileFD* vfd = (struct VFileFD*) vf;
123	int createFlags = PAGE_WRITECOPY;
124	int mapFiles = FILE_MAP_COPY;
125	if (flags & MAP_WRITE) {
126		createFlags = PAGE_READWRITE;
127		mapFiles = FILE_MAP_WRITE;
128	}
129	size_t location = lseek(vfd->fd, 0, SEEK_CUR);
130	size_t fileSize = lseek(vfd->fd, 0, SEEK_END);
131	lseek(vfd->fd, location, SEEK_SET);
132	if (size > fileSize) {
133		size = fileSize;
134	}
135	vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
136	return MapViewOfFile(vfd->hMap, mapFiles, 0, 0, size);
137}
138
139static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
140	UNUSED(size);
141	struct VFileFD* vfd = (struct VFileFD*) vf;
142	UnmapViewOfFile(memory);
143	CloseHandle(vfd->hMap);
144	vfd->hMap = 0;
145}
146#elif defined(_3DS)
147static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
148	UNUSED(flags);
149	void* buffer = anonymousMemoryMap(size);
150	vf->read(vf, buffer, size);
151	vf->seek(vf, -(off_t) size, SEEK_CUR);
152	return buffer;
153}
154
155static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
156	UNUSED(vf);
157	mappedMemoryFree(memory, size);
158}
159#else
160static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
161	struct VFileFD* vfd = (struct VFileFD*) vf;
162	int mmapFlags = MAP_PRIVATE;
163	if (flags & MAP_WRITE) {
164		mmapFlags = MAP_SHARED;
165	}
166	return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, vfd->fd, 0);
167}
168
169static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
170	UNUSED(vf);
171	munmap(memory, size);
172}
173#endif
174
175static void _vfdTruncate(struct VFile* vf, size_t size) {
176	struct VFileFD* vfd = (struct VFileFD*) vf;
177	ftruncate(vfd->fd, size);
178}
179
180struct VDirEntryDE {
181	struct VDirEntry d;
182	struct dirent* ent;
183};
184
185struct VDirDE {
186	struct VDir d;
187	DIR* de;
188	struct VDirEntryDE vde;
189	char* path;
190};
191
192struct VDir* VDirOpen(const char* path) {
193	DIR* de = opendir(path);
194	if (!de) {
195		return 0;
196	}
197
198	struct VDirDE* vd = malloc(sizeof(struct VDirDE));
199	if (!vd) {
200		return 0;
201	}
202
203	vd->d.close = _vdClose;
204	vd->d.rewind = _vdRewind;
205	vd->d.listNext = _vdListNext;
206	vd->d.openFile = _vdOpenFile;
207	vd->path = strdup(path);
208	vd->de = de;
209
210	vd->vde.d.name = _vdeName;
211
212	return &vd->d;
213}
214
215struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
216	char path[PATH_MAX];
217	path[PATH_MAX - 1] = '\0';
218	struct VFile* vf;
219	if (!dir) {
220		if (!realPath) {
221			return 0;
222		}
223		char* dotPoint = strrchr(realPath, '.');
224		if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
225			return 0;
226		}
227		if (dotPoint > strrchr(realPath, '/')) {
228			int len = dotPoint - realPath;
229			strncpy(path, realPath, len);
230			path[len] = 0;
231			strncat(path + len, suffix, PATH_MAX - len - 1);
232		} else {
233			snprintf(path, PATH_MAX - 1, "%s%s", realPath, suffix);
234		}
235		vf = VFileOpen(path, mode);
236	} else {
237		snprintf(path, PATH_MAX - 1, "%s%s", prefix, suffix);
238		vf = dir->openFile(dir, path, mode);
239	}
240	return vf;
241}
242
243struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode) {
244	char path[PATH_MAX];
245	path[PATH_MAX - 1] = '\0';
246	char realPrefix[PATH_MAX];
247	realPrefix[PATH_MAX - 1] = '\0';
248	if (!dir) {
249		if (!realPath) {
250			return 0;
251		}
252		const char* separatorPoint = strrchr(realPath, '/');
253		const char* dotPoint;
254		size_t len;
255		if (!separatorPoint) {
256			strcpy(path, "./");
257			separatorPoint = realPath;
258			dotPoint = strrchr(realPath, '.');
259		} else {
260			path[0] = '\0';
261			dotPoint = strrchr(separatorPoint, '.');
262
263			if (separatorPoint - realPath + 1 >= PATH_MAX - 1) {
264				return 0;
265			}
266
267			len = separatorPoint - realPath;
268			strncat(path, realPath, len);
269			path[len] = '\0';
270			++separatorPoint;
271		}
272
273		if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
274			return 0;
275		}
276
277		if (dotPoint >= separatorPoint) {
278			len = dotPoint - separatorPoint;
279		} else {
280			len = PATH_MAX - 1;
281		}
282
283		strncpy(realPrefix, separatorPoint, len);
284		realPrefix[len] = '\0';
285
286		prefix = realPrefix;
287		dir = VDirOpen(path);
288	}
289	if (!dir) {
290		// This shouldn't be possible
291		return 0;
292	}
293	dir->rewind(dir);
294	struct VDirEntry* dirent;
295	size_t prefixLen = strlen(prefix);
296	size_t infixLen = strlen(infix);
297	unsigned next = 0;
298	while ((dirent = dir->listNext(dir))) {
299		const char* filename = dirent->name(dirent);
300		char* dotPoint = strrchr(filename, '.');
301		size_t len = strlen(filename);
302		if (dotPoint) {
303			len = (dotPoint - filename);
304		}
305		const char* separator = strnrstr(filename, infix, len);
306		if (!separator) {
307			continue;
308		}
309		len = separator - filename;
310		if (len != prefixLen) {
311			continue;
312		}
313		if (strncmp(filename, prefix, prefixLen) == 0) {
314			int nlen;
315			separator += infixLen;
316			snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix);
317			unsigned increment;
318			if (sscanf(separator, path, &increment, &nlen) < 1) {
319				continue;
320			}
321			len = strlen(separator);
322			if (nlen < (ssize_t) len) {
323				continue;
324			}
325			if (next <= increment) {
326				next = increment + 1;
327			}
328		}
329	}
330	snprintf(path, PATH_MAX - 1, "%s%s%u%s", prefix, infix, next, suffix);
331	path[PATH_MAX - 1] = '\0';
332	return dir->openFile(dir, path, mode);
333}
334
335bool _vdClose(struct VDir* vd) {
336	struct VDirDE* vdde = (struct VDirDE*) vd;
337	if (closedir(vdde->de) < 0) {
338		return false;
339	}
340	free(vdde->path);
341	free(vdde);
342	return true;
343}
344
345void _vdRewind(struct VDir* vd) {
346	struct VDirDE* vdde = (struct VDirDE*) vd;
347	rewinddir(vdde->de);
348}
349
350struct VDirEntry* _vdListNext(struct VDir* vd) {
351	struct VDirDE* vdde = (struct VDirDE*) vd;
352	vdde->vde.ent = readdir(vdde->de);
353	if (vdde->vde.ent) {
354		return &vdde->vde.d;
355	}
356
357	return 0;
358}
359
360struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode) {
361	struct VDirDE* vdde = (struct VDirDE*) vd;
362	if (!path) {
363		return 0;
364	}
365	const char* dir = vdde->path;
366	char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
367	sprintf(combined, "%s%c%s", dir, PATH_SEP, path);
368
369	struct VFile* file = VFileOpen(combined, mode);
370	free(combined);
371	return file;
372}
373
374const char* _vdeName(struct VDirEntry* vde) {
375	struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
376	if (vdede->ent) {
377		return vdede->ent->d_name;
378	}
379	return 0;
380}