all repos — mgba @ 8903d3145849278cd087aabe72ebe4ad46b78464

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