all repos — mgba @ f1afeae74c64f2e1d1ffd43cf964e574bd97cc46

mGBA Game Boy Advance Emulator

src/util/vfs.c (view raw)

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