1 /*************************************************************************/
2 /* dir_access_windows.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #if defined(WINDOWS_ENABLED)
32
33 #include "dir_access_windows.h"
34
35 #include "core/os/memory.h"
36 #include "core/print_string.h"
37
38 #include <stdio.h>
39 #include <wchar.h>
40 #include <windows.h>
41
42 /*
43
44 [03:57] <reduz> yessopie, so i don't havemak to rely on unicows
45 [03:58] <yessopie> reduz- yeah, all of the functions fail, and then you can call GetLastError () which will return 120
46 [03:58] <drumstick> CategoryApl, hehe, what? :)
47 [03:59] <CategoryApl> didn't Verona lead to some trouble
48 [03:59] <yessopie> 120 = ERROR_CALL_NOT_IMPLEMENTED
49 [03:59] <yessopie> (you can use that constant if you include winerr.h)
50 [03:59] <CategoryApl> well answer with winning a compo
51
52 [04:02] <yessopie> if ( SetCurrentDirectoryW ( L"." ) == FALSE && GetLastError () == ERROR_CALL_NOT_IMPLEMENTED ) { use ANSI }
53 */
54
55 struct DirAccessWindowsPrivate {
56
57 HANDLE h; //handle for findfirstfile
58 WIN32_FIND_DATA f;
59 WIN32_FIND_DATAW fu; //unicode version
60 };
61
62 // CreateFolderAsync
63
list_dir_begin()64 Error DirAccessWindows::list_dir_begin() {
65
66 _cisdir = false;
67 _cishidden = false;
68
69 list_dir_end();
70 p->h = FindFirstFileExW((current_dir + "\\*").c_str(), FindExInfoStandard, &p->fu, FindExSearchNameMatch, NULL, 0);
71
72 return (p->h == INVALID_HANDLE_VALUE) ? ERR_CANT_OPEN : OK;
73 }
74
get_next()75 String DirAccessWindows::get_next() {
76
77 if (p->h == INVALID_HANDLE_VALUE)
78 return "";
79
80 _cisdir = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
81 _cishidden = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
82
83 String name = p->fu.cFileName;
84
85 if (FindNextFileW(p->h, &p->fu) == 0) {
86
87 FindClose(p->h);
88 p->h = INVALID_HANDLE_VALUE;
89 }
90
91 return name;
92 }
93
current_is_dir() const94 bool DirAccessWindows::current_is_dir() const {
95
96 return _cisdir;
97 }
98
current_is_hidden() const99 bool DirAccessWindows::current_is_hidden() const {
100
101 return _cishidden;
102 }
103
list_dir_end()104 void DirAccessWindows::list_dir_end() {
105
106 if (p->h != INVALID_HANDLE_VALUE) {
107
108 FindClose(p->h);
109 p->h = INVALID_HANDLE_VALUE;
110 }
111 }
get_drive_count()112 int DirAccessWindows::get_drive_count() {
113
114 return drive_count;
115 }
get_drive(int p_drive)116 String DirAccessWindows::get_drive(int p_drive) {
117
118 if (p_drive < 0 || p_drive >= drive_count)
119 return "";
120
121 return String::chr(drives[p_drive]) + ":";
122 }
123
change_dir(String p_dir)124 Error DirAccessWindows::change_dir(String p_dir) {
125
126 GLOBAL_LOCK_FUNCTION
127
128 p_dir = fix_path(p_dir);
129
130 wchar_t real_current_dir_name[2048];
131 GetCurrentDirectoryW(2048, real_current_dir_name);
132 String prev_dir = real_current_dir_name;
133
134 SetCurrentDirectoryW(current_dir.c_str());
135 bool worked = (SetCurrentDirectoryW(p_dir.c_str()) != 0);
136
137 String base = _get_root_path();
138 if (base != "") {
139
140 GetCurrentDirectoryW(2048, real_current_dir_name);
141 String new_dir;
142 new_dir = String(real_current_dir_name).replace("\\", "/");
143 if (!new_dir.begins_with(base)) {
144 worked = false;
145 }
146 }
147
148 if (worked) {
149
150 GetCurrentDirectoryW(2048, real_current_dir_name);
151 current_dir = real_current_dir_name; // TODO, utf8 parser
152 current_dir = current_dir.replace("\\", "/");
153
154 } //else {
155
156 SetCurrentDirectoryW(prev_dir.c_str());
157 //}
158
159 return worked ? OK : ERR_INVALID_PARAMETER;
160 }
161
make_dir(String p_dir)162 Error DirAccessWindows::make_dir(String p_dir) {
163
164 GLOBAL_LOCK_FUNCTION
165
166 p_dir = fix_path(p_dir);
167 if (p_dir.is_rel_path())
168 p_dir = current_dir.plus_file(p_dir);
169
170 p_dir = p_dir.replace("/", "\\");
171
172 bool success;
173 int err;
174
175 p_dir = "\\\\?\\" + p_dir; //done according to
176 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
177
178 success = CreateDirectoryW(p_dir.c_str(), NULL);
179 err = GetLastError();
180
181 if (success) {
182 return OK;
183 };
184
185 if (err == ERROR_ALREADY_EXISTS || err == ERROR_ACCESS_DENIED) {
186 return ERR_ALREADY_EXISTS;
187 };
188
189 return ERR_CANT_CREATE;
190 }
191
get_current_dir()192 String DirAccessWindows::get_current_dir() {
193
194 String base = _get_root_path();
195 if (base != "") {
196
197 String bd = current_dir.replace("\\", "/").replace_first(base, "");
198 if (bd.begins_with("/"))
199 return _get_root_string() + bd.substr(1, bd.length());
200 else
201 return _get_root_string() + bd;
202
203 } else {
204 }
205
206 return current_dir;
207 }
208
get_current_dir_without_drive()209 String DirAccessWindows::get_current_dir_without_drive() {
210
211 String dir = get_current_dir();
212
213 if (_get_root_string() == "") {
214 int p = current_dir.find(":");
215 if (p != -1) {
216 dir = dir.right(p + 1);
217 }
218 }
219
220 return dir;
221 }
222
file_exists(String p_file)223 bool DirAccessWindows::file_exists(String p_file) {
224
225 GLOBAL_LOCK_FUNCTION
226
227 if (!p_file.is_abs_path())
228 p_file = get_current_dir().plus_file(p_file);
229
230 p_file = fix_path(p_file);
231
232 //p_file.replace("/","\\");
233
234 //WIN32_FILE_ATTRIBUTE_DATA fileInfo;
235
236 DWORD fileAttr;
237
238 fileAttr = GetFileAttributesW(p_file.c_str());
239 if (INVALID_FILE_ATTRIBUTES == fileAttr)
240 return false;
241
242 return !(fileAttr & FILE_ATTRIBUTE_DIRECTORY);
243 }
244
dir_exists(String p_dir)245 bool DirAccessWindows::dir_exists(String p_dir) {
246
247 GLOBAL_LOCK_FUNCTION
248
249 if (p_dir.is_rel_path())
250 p_dir = get_current_dir().plus_file(p_dir);
251
252 p_dir = fix_path(p_dir);
253
254 //p_dir.replace("/","\\");
255
256 //WIN32_FILE_ATTRIBUTE_DATA fileInfo;
257
258 DWORD fileAttr;
259
260 fileAttr = GetFileAttributesW(p_dir.c_str());
261 if (INVALID_FILE_ATTRIBUTES == fileAttr)
262 return false;
263 return (fileAttr & FILE_ATTRIBUTE_DIRECTORY);
264 }
265
rename(String p_path,String p_new_path)266 Error DirAccessWindows::rename(String p_path, String p_new_path) {
267
268 if (p_path.is_rel_path())
269 p_path = get_current_dir().plus_file(p_path);
270
271 p_path = fix_path(p_path);
272
273 if (p_new_path.is_rel_path())
274 p_new_path = get_current_dir().plus_file(p_new_path);
275
276 p_new_path = fix_path(p_new_path);
277
278 // If we're only changing file name case we need to do a little juggling
279 if (p_path.to_lower() == p_new_path.to_lower()) {
280 WCHAR tmpfile[MAX_PATH];
281
282 if (!GetTempFileNameW(fix_path(get_current_dir()).c_str(), NULL, 0, tmpfile)) {
283 return FAILED;
284 }
285
286 if (!::ReplaceFileW(tmpfile, p_path.c_str(), NULL, 0, NULL, NULL)) {
287 DeleteFileW(tmpfile);
288 return FAILED;
289 }
290
291 return ::_wrename(tmpfile, p_new_path.c_str()) == 0 ? OK : FAILED;
292
293 } else {
294 if (file_exists(p_new_path)) {
295 if (remove(p_new_path) != OK) {
296 return FAILED;
297 }
298 }
299
300 return ::_wrename(p_path.c_str(), p_new_path.c_str()) == 0 ? OK : FAILED;
301 }
302 }
303
remove(String p_path)304 Error DirAccessWindows::remove(String p_path) {
305
306 if (p_path.is_rel_path())
307 p_path = get_current_dir().plus_file(p_path);
308
309 p_path = fix_path(p_path);
310
311 DWORD fileAttr;
312
313 fileAttr = GetFileAttributesW(p_path.c_str());
314 if (INVALID_FILE_ATTRIBUTES == fileAttr)
315 return FAILED;
316 if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY))
317 return ::_wrmdir(p_path.c_str()) == 0 ? OK : FAILED;
318 else
319 return ::_wunlink(p_path.c_str()) == 0 ? OK : FAILED;
320 }
321 /*
322
323 FileType DirAccessWindows::get_file_type(const String& p_file) const {
324
325
326 wchar_t real_current_dir_name[2048];
327 GetCurrentDirectoryW(2048,real_current_dir_name);
328 String prev_dir=real_current_dir_name;
329
330 bool worked SetCurrentDirectoryW(current_dir.c_str());
331
332 DWORD attr;
333 if (worked) {
334
335 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
336 attr = GetFileAttributesExW(p_file.c_str(), GetFileExInfoStandard, &fileInfo);
337
338 }
339
340 SetCurrentDirectoryW(prev_dir.c_str());
341
342 if (!worked)
343 return FILE_TYPE_NONE;
344
345
346 return (attr&FILE_ATTRIBUTE_DIRECTORY)?FILE_TYPE_
347 }
348 */
get_space_left()349 size_t DirAccessWindows::get_space_left() {
350
351 uint64_t bytes = 0;
352 if (!GetDiskFreeSpaceEx(NULL, (PULARGE_INTEGER)&bytes, NULL, NULL))
353 return 0;
354
355 //this is either 0 or a value in bytes.
356 return (size_t)bytes;
357 }
358
get_filesystem_type() const359 String DirAccessWindows::get_filesystem_type() const {
360 String path = fix_path(const_cast<DirAccessWindows *>(this)->get_current_dir());
361
362 int unit_end = path.find(":");
363 ERR_FAIL_COND_V(unit_end == -1, String());
364 String unit = path.substr(0, unit_end + 1) + "\\";
365
366 WCHAR szVolumeName[100];
367 WCHAR szFileSystemName[10];
368 DWORD dwSerialNumber = 0;
369 DWORD dwMaxFileNameLength = 0;
370 DWORD dwFileSystemFlags = 0;
371
372 if (::GetVolumeInformationW(unit.c_str(),
373 szVolumeName,
374 sizeof(szVolumeName),
375 &dwSerialNumber,
376 &dwMaxFileNameLength,
377 &dwFileSystemFlags,
378 szFileSystemName,
379 sizeof(szFileSystemName)) == TRUE) {
380
381 return String(szFileSystemName);
382 }
383
384 ERR_FAIL_V("");
385 }
386
DirAccessWindows()387 DirAccessWindows::DirAccessWindows() {
388
389 p = memnew(DirAccessWindowsPrivate);
390 p->h = INVALID_HANDLE_VALUE;
391 current_dir = ".";
392
393 drive_count = 0;
394
395 #ifdef UWP_ENABLED
396 Windows::Storage::StorageFolder ^ install_folder = Windows::ApplicationModel::Package::Current->InstalledLocation;
397 change_dir(install_folder->Path->Data());
398
399 #else
400
401 DWORD mask = GetLogicalDrives();
402
403 for (int i = 0; i < MAX_DRIVES; i++) {
404
405 if (mask & (1 << i)) { //DRIVE EXISTS
406
407 drives[drive_count] = 'A' + i;
408 drive_count++;
409 }
410 }
411
412 change_dir(".");
413 #endif
414 }
415
~DirAccessWindows()416 DirAccessWindows::~DirAccessWindows() {
417
418 memdelete(p);
419 }
420
421 #endif //windows DirAccess support
422