all repos — mgba @ e443b61c21ca6bda3dc36edc46e851c1352f3e89

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