all repos — mgba @ b24b02d46cfd6fb4cc89d34fa38c442d31367017

mGBA Game Boy Advance Emulator

src/util/vfs.c (view raw)

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