all repos — mgba @ 4748ef7d5d24c2396212be649f9cb67f3fe2e106

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