all repos — mgba @ 22245617f434049f4646916d1b2930d376503b0d

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