src/platform/windows/vfs-w32.c (view raw)
1/* Copyright (c) 2013-2016 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 <mgba-util/vfs.h>
7
8#include <mgba-util/string.h>
9#include <strsafe.h>
10
11static bool _vdwClose(struct VDir* vd);
12static void _vdwRewind(struct VDir* vd);
13static struct VDirEntry* _vdwListNext(struct VDir* vd);
14static struct VFile* _vdwOpenFile(struct VDir* vd, const char* path, int mode);
15static struct VDir* _vdwOpenDir(struct VDir* vd, const char* path);
16static bool _vdwDeleteFile(struct VDir* vd, const char* path);
17
18static const char* _vdweName(struct VDirEntry* vde);
19static enum VFSType _vdweType(struct VDirEntry* vde);
20
21struct VDirW32;
22struct VDirEntryW32 {
23 struct VDirEntry d;
24 WIN32_FIND_DATAW ffData;
25 char* utf8Name;
26};
27
28struct VDirW32 {
29 struct VDir d;
30 HANDLE handle;
31 struct VDirEntryW32 vde;
32 char* path;
33};
34
35struct VDir* VDirOpen(const char* path) {
36 if (!path || !path[0]) {
37 return 0;
38 }
39 wchar_t name[MAX_PATH + 1];
40 MultiByteToWideChar(CP_UTF8, 0, path, -1, name, MAX_PATH);
41 StringCchCatNW(name, MAX_PATH, L"\\*", 2);
42 WIN32_FIND_DATAW ffData;
43 HANDLE handle = FindFirstFileW(name, &ffData);
44 if (handle == INVALID_HANDLE_VALUE) {
45 return 0;
46 }
47
48 struct VDirW32* vd = malloc(sizeof(struct VDirW32));
49 if (!vd) {
50 FindClose(handle);
51 return 0;
52 }
53
54 vd->d.close = _vdwClose;
55 vd->d.rewind = _vdwRewind;
56 vd->d.listNext = _vdwListNext;
57 vd->d.openFile = _vdwOpenFile;
58 vd->d.openDir = _vdwOpenDir;
59 vd->d.deleteFile = _vdwDeleteFile;
60 vd->handle = handle;
61 vd->path = _strdup(path);
62
63 vd->vde.d.name = _vdweName;
64 vd->vde.d.type = _vdweType;
65 vd->vde.ffData = ffData;
66 vd->vde.utf8Name = NULL;
67
68 return &vd->d;
69}
70
71bool _vdwClose(struct VDir* vd) {
72 struct VDirW32* vdw = (struct VDirW32*) vd;
73 FindClose(vdw->handle);
74 free(vdw->path);
75 if (vdw->vde.utf8Name) {
76 free(vdw->vde.utf8Name);
77 vdw->vde.utf8Name = NULL;
78 }
79 free(vdw);
80 return true;
81}
82
83void _vdwRewind(struct VDir* vd) {
84 struct VDirW32* vdw = (struct VDirW32*) vd;
85 FindClose(vdw->handle);
86 wchar_t name[MAX_PATH + 1];
87 MultiByteToWideChar(CP_UTF8, 0, vdw->path, -1, name, MAX_PATH);
88 StringCchCatNW(name, MAX_PATH, L"\\*", 2);
89 if (vdw->vde.utf8Name) {
90 free(vdw->vde.utf8Name);
91 vdw->vde.utf8Name = NULL;
92 }
93 vdw->handle = FindFirstFileW(name, &vdw->vde.ffData);
94}
95
96struct VDirEntry* _vdwListNext(struct VDir* vd) {
97 struct VDirW32* vdw = (struct VDirW32*) vd;
98 if (FindNextFileW(vdw->handle, &vdw->vde.ffData)) {
99 if (vdw->vde.utf8Name) {
100 free(vdw->vde.utf8Name);
101 vdw->vde.utf8Name = NULL;
102 }
103 return &vdw->vde.d;
104 }
105
106 return 0;
107}
108
109struct VFile* _vdwOpenFile(struct VDir* vd, const char* path, int mode) {
110 struct VDirW32* vdw = (struct VDirW32*) vd;
111 if (!path) {
112 return 0;
113 }
114 const char* dir = vdw->path;
115 size_t size = sizeof(char) * (strlen(path) + strlen(dir) + 2);
116 char* combined = malloc(size);
117 StringCbPrintfA(combined, size, "%s\\%s", dir, path);
118
119 struct VFile* file = VFileOpen(combined, mode);
120 free(combined);
121 return file;
122}
123
124struct VDir* _vdwOpenDir(struct VDir* vd, const char* path) {
125 struct VDirW32* vdw = (struct VDirW32*) vd;
126 if (!path) {
127 return 0;
128 }
129 const char* dir = vdw->path;
130 size_t size = sizeof(char) * (strlen(path) + strlen(dir) + 2);
131 char* combined = malloc(size);
132 StringCbPrintfA(combined, size, "%s\\%s", dir, path);
133
134 struct VDir* vd2 = VDirOpen(combined);
135 if (!vd2) {
136 vd2 = VDirOpenArchive(combined);
137 }
138 free(combined);
139 return vd2;
140}
141
142bool _vdwDeleteFile(struct VDir* vd, const char* path) {
143 struct VDirW32* vdw = (struct VDirW32*) vd;
144 if (!path) {
145 return 0;
146 }
147 wchar_t dir[MAX_PATH + 1];
148 wchar_t pathw[MAX_PATH + 1];
149 wchar_t combined[MAX_PATH + 1];
150 MultiByteToWideChar(CP_UTF8, 0, vdw->path, -1, dir, MAX_PATH);
151 MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw, MAX_PATH);
152 StringCchPrintfW(combined, MAX_PATH, L"%ws\\%ws", dir, pathw);
153
154 return DeleteFileW(combined);
155}
156
157const char* _vdweName(struct VDirEntry* vde) {
158 struct VDirEntryW32* vdwe = (struct VDirEntryW32*) vde;
159 if (vdwe->utf8Name) {
160 return vdwe->utf8Name;
161 }
162 size_t len = 4 * wcslen(vdwe->ffData.cFileName);
163 vdwe->utf8Name = malloc(len);
164 WideCharToMultiByte(CP_UTF8, 0, vdwe->ffData.cFileName, -1, vdwe->utf8Name, len - 1, NULL, NULL);
165 return vdwe->utf8Name;
166}
167
168static enum VFSType _vdweType(struct VDirEntry* vde) {
169 struct VDirEntryW32* vdwe = (struct VDirEntryW32*) vde;
170 if (vdwe->ffData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
171 return VFS_DIRECTORY;
172 }
173 return VFS_FILE;
174}
175
176bool VDirCreate(const char* path) {
177 wchar_t wpath[MAX_PATH];
178 MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);
179 if (CreateDirectoryW(wpath, NULL)) {
180 return true;
181 }
182 if (GetLastError() == ERROR_ALREADY_EXISTS) {
183 return true;
184 }
185 return false;
186}