all repos — mgba @ 54ef61f715d59e50429a3b3982c7f32e56b7b8eb

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