all repos — mgba @ a5b9faa88c88d81a961d516fe3bef21cee99b3a7

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