mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			396 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*************************************************************************/
 | |
| /*  dir_access_windows.cpp                                               */
 | |
| /*************************************************************************/
 | |
| /*                       This file is part of:                           */
 | |
| /*                           GODOT ENGINE                                */
 | |
| /*                      https://godotengine.org                          */
 | |
| /*************************************************************************/
 | |
| /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
 | |
| /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
 | |
| /*                                                                       */
 | |
| /* Permission is hereby granted, free of charge, to any person obtaining */
 | |
| /* a copy of this software and associated documentation files (the       */
 | |
| /* "Software"), to deal in the Software without restriction, including   */
 | |
| /* without limitation the rights to use, copy, modify, merge, publish,   */
 | |
| /* distribute, sublicense, and/or sell copies of the Software, and to    */
 | |
| /* permit persons to whom the Software is furnished to do so, subject to */
 | |
| /* the following conditions:                                             */
 | |
| /*                                                                       */
 | |
| /* The above copyright notice and this permission notice shall be        */
 | |
| /* included in all copies or substantial portions of the Software.       */
 | |
| /*                                                                       */
 | |
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
 | |
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
 | |
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
 | |
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
 | |
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
 | |
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
 | |
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 | |
| /*************************************************************************/
 | |
| 
 | |
| #if defined(WINDOWS_ENABLED)
 | |
| 
 | |
| #include "dir_access_windows.h"
 | |
| 
 | |
| #include "core/os/memory.h"
 | |
| #include "core/print_string.h"
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <wchar.h>
 | |
| #include <windows.h>
 | |
| 
 | |
| /*
 | |
| 
 | |
| [03:57] <reduz> yessopie, so i don't havemak to rely on unicows
 | |
| [03:58] <yessopie> reduz- yeah, all of the functions fail, and then you can call GetLastError () which will return 120
 | |
| [03:58] <drumstick> CategoryApl, hehe, what? :)
 | |
| [03:59] <CategoryApl> didn't Verona lead to some trouble
 | |
| [03:59] <yessopie> 120 = ERROR_CALL_NOT_IMPLEMENTED
 | |
| [03:59] <yessopie> (you can use that constant if you include winerr.h)
 | |
| [03:59] <CategoryApl> well answer with winning a compo
 | |
| 
 | |
| [04:02] <yessopie> if ( SetCurrentDirectoryW ( L"." ) == FALSE && GetLastError () == ERROR_CALL_NOT_IMPLEMENTED ) { use ANSI }
 | |
| */
 | |
| 
 | |
| struct DirAccessWindowsPrivate {
 | |
| 	HANDLE h; //handle for findfirstfile
 | |
| 	WIN32_FIND_DATA f;
 | |
| 	WIN32_FIND_DATAW fu; //unicode version
 | |
| };
 | |
| 
 | |
| // CreateFolderAsync
 | |
| 
 | |
| Error DirAccessWindows::list_dir_begin() {
 | |
| 	_cisdir = false;
 | |
| 	_cishidden = false;
 | |
| 
 | |
| 	list_dir_end();
 | |
| 	p->h = FindFirstFileExW((LPCWSTR)(String(current_dir + "\\*").utf16().get_data()), FindExInfoStandard, &p->fu, FindExSearchNameMatch, nullptr, 0);
 | |
| 
 | |
| 	if (p->h == INVALID_HANDLE_VALUE) {
 | |
| 		return ERR_CANT_OPEN;
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| String DirAccessWindows::get_next() {
 | |
| 	if (p->h == INVALID_HANDLE_VALUE) {
 | |
| 		return "";
 | |
| 	}
 | |
| 
 | |
| 	_cisdir = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
 | |
| 	_cishidden = (p->fu.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
 | |
| 
 | |
| 	String name = String::utf16((const char16_t *)(p->fu.cFileName));
 | |
| 
 | |
| 	if (FindNextFileW(p->h, &p->fu) == 0) {
 | |
| 		FindClose(p->h);
 | |
| 		p->h = INVALID_HANDLE_VALUE;
 | |
| 	}
 | |
| 
 | |
| 	return name;
 | |
| }
 | |
| 
 | |
| bool DirAccessWindows::current_is_dir() const {
 | |
| 	return _cisdir;
 | |
| }
 | |
| 
 | |
| bool DirAccessWindows::current_is_hidden() const {
 | |
| 	return _cishidden;
 | |
| }
 | |
| 
 | |
| void DirAccessWindows::list_dir_end() {
 | |
| 	if (p->h != INVALID_HANDLE_VALUE) {
 | |
| 		FindClose(p->h);
 | |
| 		p->h = INVALID_HANDLE_VALUE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int DirAccessWindows::get_drive_count() {
 | |
| 	return drive_count;
 | |
| }
 | |
| 
 | |
| String DirAccessWindows::get_drive(int p_drive) {
 | |
| 	if (p_drive < 0 || p_drive >= drive_count) {
 | |
| 		return "";
 | |
| 	}
 | |
| 
 | |
| 	return String::chr(drives[p_drive]) + ":";
 | |
| }
 | |
| 
 | |
| Error DirAccessWindows::change_dir(String p_dir) {
 | |
| 	GLOBAL_LOCK_FUNCTION
 | |
| 
 | |
| 	p_dir = fix_path(p_dir);
 | |
| 
 | |
| 	WCHAR real_current_dir_name[2048];
 | |
| 	GetCurrentDirectoryW(2048, real_current_dir_name);
 | |
| 	String prev_dir = String::utf16((const char16_t *)real_current_dir_name);
 | |
| 
 | |
| 	SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data()));
 | |
| 	bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0);
 | |
| 
 | |
| 	String base = _get_root_path();
 | |
| 	if (base != "") {
 | |
| 		GetCurrentDirectoryW(2048, real_current_dir_name);
 | |
| 		String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/");
 | |
| 		if (!new_dir.begins_with(base)) {
 | |
| 			worked = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (worked) {
 | |
| 		GetCurrentDirectoryW(2048, real_current_dir_name);
 | |
| 		current_dir = String::utf16((const char16_t *)real_current_dir_name);
 | |
| 		current_dir = current_dir.replace("\\", "/");
 | |
| 	}
 | |
| 
 | |
| 	SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data()));
 | |
| 
 | |
| 	return worked ? OK : ERR_INVALID_PARAMETER;
 | |
| }
 | |
| 
 | |
| Error DirAccessWindows::make_dir(String p_dir) {
 | |
| 	GLOBAL_LOCK_FUNCTION
 | |
| 
 | |
| 	p_dir = fix_path(p_dir);
 | |
| 	if (p_dir.is_rel_path()) {
 | |
| 		p_dir = current_dir.plus_file(p_dir);
 | |
| 	}
 | |
| 
 | |
| 	p_dir = p_dir.replace("/", "\\");
 | |
| 
 | |
| 	bool success;
 | |
| 	int err;
 | |
| 
 | |
| 	p_dir = "\\\\?\\" + p_dir; //done according to
 | |
| 	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
 | |
| 
 | |
| 	success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr);
 | |
| 	err = GetLastError();
 | |
| 
 | |
| 	if (success) {
 | |
| 		return OK;
 | |
| 	}
 | |
| 
 | |
| 	if (err == ERROR_ALREADY_EXISTS || err == ERROR_ACCESS_DENIED) {
 | |
| 		return ERR_ALREADY_EXISTS;
 | |
| 	}
 | |
| 
 | |
| 	return ERR_CANT_CREATE;
 | |
| }
 | |
| 
 | |
| String DirAccessWindows::get_current_dir(bool p_include_drive) {
 | |
| 	String base = _get_root_path();
 | |
| 	if (base != "") {
 | |
| 		String bd = current_dir.replace("\\", "/").replace_first(base, "");
 | |
| 		if (bd.begins_with("/")) {
 | |
| 			return _get_root_string() + bd.substr(1, bd.length());
 | |
| 		} else {
 | |
| 			return _get_root_string() + bd;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (p_include_drive) {
 | |
| 		return current_dir;
 | |
| 	} else {
 | |
| 		if (_get_root_string() == "") {
 | |
| 			int p = current_dir.find(":");
 | |
| 			if (p != -1) {
 | |
| 				return current_dir.right(p + 1);
 | |
| 			}
 | |
| 		}
 | |
| 		return current_dir;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool DirAccessWindows::file_exists(String p_file) {
 | |
| 	GLOBAL_LOCK_FUNCTION
 | |
| 
 | |
| 	if (!p_file.is_abs_path()) {
 | |
| 		p_file = get_current_dir().plus_file(p_file);
 | |
| 	}
 | |
| 
 | |
| 	p_file = fix_path(p_file);
 | |
| 
 | |
| 	DWORD fileAttr;
 | |
| 
 | |
| 	fileAttr = GetFileAttributesW((LPCWSTR)(p_file.utf16().get_data()));
 | |
| 	if (INVALID_FILE_ATTRIBUTES == fileAttr) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	return !(fileAttr & FILE_ATTRIBUTE_DIRECTORY);
 | |
| }
 | |
| 
 | |
| bool DirAccessWindows::dir_exists(String p_dir) {
 | |
| 	GLOBAL_LOCK_FUNCTION
 | |
| 
 | |
| 	if (p_dir.is_rel_path()) {
 | |
| 		p_dir = get_current_dir().plus_file(p_dir);
 | |
| 	}
 | |
| 
 | |
| 	p_dir = fix_path(p_dir);
 | |
| 
 | |
| 	DWORD fileAttr;
 | |
| 	fileAttr = GetFileAttributesW((LPCWSTR)(p_dir.utf16().get_data()));
 | |
| 	if (INVALID_FILE_ATTRIBUTES == fileAttr) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	return (fileAttr & FILE_ATTRIBUTE_DIRECTORY);
 | |
| }
 | |
| 
 | |
| Error DirAccessWindows::rename(String p_path, String p_new_path) {
 | |
| 	if (p_path.is_rel_path()) {
 | |
| 		p_path = get_current_dir().plus_file(p_path);
 | |
| 	}
 | |
| 
 | |
| 	p_path = fix_path(p_path);
 | |
| 
 | |
| 	if (p_new_path.is_rel_path()) {
 | |
| 		p_new_path = get_current_dir().plus_file(p_new_path);
 | |
| 	}
 | |
| 
 | |
| 	p_new_path = fix_path(p_new_path);
 | |
| 
 | |
| 	// If we're only changing file name case we need to do a little juggling
 | |
| 	if (p_path.to_lower() == p_new_path.to_lower()) {
 | |
| 		WCHAR tmpfile[MAX_PATH];
 | |
| 
 | |
| 		if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), nullptr, 0, tmpfile)) {
 | |
| 			return FAILED;
 | |
| 		}
 | |
| 
 | |
| 		if (!::ReplaceFileW(tmpfile, (LPCWSTR)(p_path.utf16().get_data()), nullptr, 0, nullptr, nullptr)) {
 | |
| 			DeleteFileW(tmpfile);
 | |
| 			return FAILED;
 | |
| 		}
 | |
| 
 | |
| 		return ::_wrename(tmpfile, (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED;
 | |
| 
 | |
| 	} else {
 | |
| 		if (file_exists(p_new_path)) {
 | |
| 			if (remove(p_new_path) != OK) {
 | |
| 				return FAILED;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| Error DirAccessWindows::remove(String p_path) {
 | |
| 	if (p_path.is_rel_path()) {
 | |
| 		p_path = get_current_dir().plus_file(p_path);
 | |
| 	}
 | |
| 
 | |
| 	p_path = fix_path(p_path);
 | |
| 
 | |
| 	DWORD fileAttr;
 | |
| 
 | |
| 	fileAttr = GetFileAttributesW((LPCWSTR)(p_path.utf16().get_data()));
 | |
| 	if (INVALID_FILE_ATTRIBUTES == fileAttr) {
 | |
| 		return FAILED;
 | |
| 	}
 | |
| 	if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY)) {
 | |
| 		return ::_wrmdir((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED;
 | |
| 	} else {
 | |
| 		return ::_wunlink((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
| 
 | |
| FileType DirAccessWindows::get_file_type(const String& p_file) const {
 | |
| 	WCHAR real_current_dir_name[2048];
 | |
| 	GetCurrentDirectoryW(2048, real_current_dir_name);
 | |
| 	String prev_dir = Strong::utf16((const char16_t *)real_current_dir_name);
 | |
| 
 | |
| 	bool worked = SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data()));
 | |
| 
 | |
| 	DWORD attr;
 | |
| 	if (worked) {
 | |
| 		WIN32_FILE_ATTRIBUTE_DATA fileInfo;
 | |
| 		attr = GetFileAttributesExW((LPCWSTR)(p_file.utf16().get_data()), GetFileExInfoStandard, &fileInfo);
 | |
| 	}
 | |
| 
 | |
| 	SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data()));
 | |
| 
 | |
| 	if (!worked) {
 | |
| 		return FILE_TYPE_NONE;
 | |
| 	}
 | |
| 
 | |
| 	return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FILE_TYPE_
 | |
| }
 | |
| 
 | |
| */
 | |
| size_t DirAccessWindows::get_space_left() {
 | |
| 	uint64_t bytes = 0;
 | |
| 	if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	//this is either 0 or a value in bytes.
 | |
| 	return (size_t)bytes;
 | |
| }
 | |
| 
 | |
| String DirAccessWindows::get_filesystem_type() const {
 | |
| 	String path = fix_path(const_cast<DirAccessWindows *>(this)->get_current_dir());
 | |
| 
 | |
| 	int unit_end = path.find(":");
 | |
| 	ERR_FAIL_COND_V(unit_end == -1, String());
 | |
| 	String unit = path.substr(0, unit_end + 1) + "\\";
 | |
| 
 | |
| 	WCHAR szVolumeName[100];
 | |
| 	WCHAR szFileSystemName[10];
 | |
| 	DWORD dwSerialNumber = 0;
 | |
| 	DWORD dwMaxFileNameLength = 0;
 | |
| 	DWORD dwFileSystemFlags = 0;
 | |
| 
 | |
| 	if (::GetVolumeInformationW((LPCWSTR)(unit.utf16().get_data()),
 | |
| 				szVolumeName,
 | |
| 				sizeof(szVolumeName),
 | |
| 				&dwSerialNumber,
 | |
| 				&dwMaxFileNameLength,
 | |
| 				&dwFileSystemFlags,
 | |
| 				szFileSystemName,
 | |
| 				sizeof(szFileSystemName)) == TRUE) {
 | |
| 		return String::utf16((const char16_t *)szFileSystemName);
 | |
| 	}
 | |
| 
 | |
| 	ERR_FAIL_V("");
 | |
| }
 | |
| 
 | |
| DirAccessWindows::DirAccessWindows() {
 | |
| 	p = memnew(DirAccessWindowsPrivate);
 | |
| 	p->h = INVALID_HANDLE_VALUE;
 | |
| 	current_dir = ".";
 | |
| 
 | |
| 	drive_count = 0;
 | |
| 
 | |
| #ifdef UWP_ENABLED
 | |
| 	Windows::Storage::StorageFolder ^ install_folder = Windows::ApplicationModel::Package::Current->InstalledLocation;
 | |
| 	change_dir(install_folder->Path->Data());
 | |
| 
 | |
| #else
 | |
| 
 | |
| 	DWORD mask = GetLogicalDrives();
 | |
| 
 | |
| 	for (int i = 0; i < MAX_DRIVES; i++) {
 | |
| 		if (mask & (1 << i)) { //DRIVE EXISTS
 | |
| 
 | |
| 			drives[drive_count] = 'A' + i;
 | |
| 			drive_count++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	change_dir(".");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| DirAccessWindows::~DirAccessWindows() {
 | |
| 	memdelete(p);
 | |
| }
 | |
| 
 | |
| #endif //windows DirAccess support
 | 
