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