Also added in the windows implementation of FileAccess and DirAccess from Pandemonium. The windows build will be fixed later.

This commit is contained in:
Relintai 2024-01-13 12:03:32 +01:00
parent c398d7f4b3
commit c7fd01656a
4 changed files with 747 additions and 2 deletions

View File

@ -21,6 +21,12 @@
//--STRIP
#if defined(_WIN64) || defined(_WIN32)
#include <stdio.h>
#include <wchar.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dirent.h>
@ -40,6 +46,386 @@
#endif
#if defined(_WIN64) || defined(_WIN32)
bool DirAccess::is_link(String p_file) {
return false;
};
String DirAccess::read_link(String p_file) {
return p_file;
};
Error DirAccess::create_link(String p_source, String p_target) {
return FAILED;
};
/*
[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, NULL, 0);
return (p->h == INVALID_HANDLE_VALUE) ? ERR_CANT_OPEN : 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.simplify_path().replace("/", "\\");
bool success;
int err;
if (!p_dir.is_network_share_path()) {
p_dir = "\\\\?\\" + p_dir;
// Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
}
success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), NULL);
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() {
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;
}
}
return current_dir;
}
String DirAccessWindows::get_current_dir_without_drive() {
String dir = get_current_dir();
if (_get_root_string() == "") {
int p = current_dir.find(":");
if (p != -1) {
dir = dir.right(p + 1);
}
}
return 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()) {
if (dir_exists(p_path)) {
// The path is a dir; just rename
return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED;
}
// The path is a file; juggle
WCHAR tmpfile[MAX_PATH];
if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), NULL, 0, tmpfile)) {
return FAILED;
}
if (!::ReplaceFileW(tmpfile, (LPCWSTR)(p_path.utf16().get_data()), NULL, 0, NULL, NULL)) {
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_
}
*/
uint64_t DirAccessWindows::get_space_left() {
uint64_t bytes = 0;
if (!GetDiskFreeSpaceEx(NULL, (PULARGE_INTEGER)&bytes, NULL, NULL)) {
return 0;
}
//this is either 0 or a value in bytes.
return bytes;
}
String DirAccessWindows::get_filesystem_type() const {
String path = fix_path(const_cast<DirAccessWindows *>(this)->get_current_dir());
if (path.is_network_share_path()) {
return "Network Share";
}
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() {
list_dir_end();
memdelete(p);
}
#else
Error DirAccess::list_dir_begin(bool skip_specials) {

View File

@ -13,6 +13,7 @@
//--STRIP
#if defined(_WIN64) || defined(_WIN32)
struct DirAccessWindowsPrivate;
#else
struct __dirstream;
typedef struct __dirstream DIR;
@ -77,7 +78,6 @@ public:
protected:
#if defined(_WIN64) || defined(_WIN32)
#else
String current_dir;
virtual String fix_unicode_name(const char *p_name) const { return String::utf8(p_name); }
virtual bool is_hidden(const String &p_name);
#endif
@ -86,7 +86,22 @@ protected:
bool _skip_specials;
#if defined(_WIN64) || defined(_WIN32)
enum {
MAX_DRIVES = 26
};
DirAccessWindowsPrivate *p;
/* Windows stuff */
char drives[MAX_DRIVES]; // a-z:
int drive_count;
String current_dir;
bool _cisdir;
bool _cishidden;
#else
String current_dir;
DIR *dir_stream;
bool _cisdir;

View File

@ -39,7 +39,24 @@
//--STRIP
#if defined(_WIN64) || defined(_WIN32)
#include <share.h> // _SH_DENYNO
#include <shlwapi.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tchar.h>
#include <wchar.h>
#ifdef _MSC_VER
#define S_ISREG(m) ((m)&_S_IFREG)
#endif
#else
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -65,6 +82,324 @@
#endif
#if defined(_WIN64) || defined(_WIN32)
void FileAccessWindows::check_errors() const {
ERR_FAIL_COND(!f);
if (feof(f)) {
last_error = ERR_FILE_EOF;
}
}
Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
path_src = p_path;
path = fix_path(p_path);
if (f) {
close();
}
const WCHAR *mode_string;
if (p_mode_flags == READ) {
mode_string = L"rb";
} else if (p_mode_flags == WRITE) {
mode_string = L"wb";
} else if (p_mode_flags == READ_WRITE) {
mode_string = L"rb+";
} else if (p_mode_flags == WRITE_READ) {
mode_string = L"wb+";
} else {
return ERR_INVALID_PARAMETER;
}
/* pretty much every implementation that uses fopen as primary
backend supports utf8 encoding */
struct _stat st;
if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) {
if (!S_ISREG(st.st_mode)) {
return ERR_FILE_CANT_OPEN;
}
};
#ifdef TOOLS_ENABLED
// Windows is case insensitive, but all other platforms are sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows, but won't on other
// platforms).
if (p_mode_flags == READ) {
WIN32_FIND_DATAW d;
HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d);
if (f != INVALID_HANDLE_VALUE) {
String fname = String::utf16((const char16_t *)(d.cFileName));
if (fname != String()) {
String base_file = path.get_file();
if (base_file != fname && base_file.findn(fname) == 0) {
WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
}
}
FindClose(f);
}
}
#endif
if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) {
save_path = path;
path = path + ".tmp";
}
f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, _SH_DENYNO);
if (f == nullptr) {
switch (errno) {
case ENOENT: {
last_error = ERR_FILE_NOT_FOUND;
} break;
default: {
last_error = ERR_FILE_CANT_OPEN;
} break;
}
return last_error;
} else {
last_error = OK;
flags = p_mode_flags;
return OK;
}
}
void FileAccessWindows::close() {
if (!f) {
return;
}
fclose(f);
f = NULL;
if (save_path != "") {
bool rename_error = true;
int attempts = 4;
while (rename_error && attempts) {
// This workaround of trying multiple times is added to deal with paranoid Windows
// antiviruses that love reading just written files even if they are not executable, thus
// locking the file and preventing renaming from happening.
#ifdef UWP_ENABLED
// UWP has no PathFileExists, so we check attributes instead
DWORD fileAttr;
fileAttr = GetFileAttributesW((LPCWSTR)(save_path.utf16().get_data()));
if (INVALID_FILE_ATTRIBUTES == fileAttr) {
#else
if (!PathFileExistsW((LPCWSTR)(save_path.utf16().get_data()))) {
#endif
//creating new file
rename_error = _wrename((LPCWSTR)((save_path + ".tmp").utf16().get_data()), (LPCWSTR)(save_path.utf16().get_data())) != 0;
} else {
//atomic replace for existing file
rename_error = !ReplaceFileW((LPCWSTR)(save_path.utf16().get_data()), (LPCWSTR)((save_path + ".tmp").utf16().get_data()), NULL, 2 | 4, NULL, NULL);
}
if (rename_error) {
attempts--;
OS::get_singleton()->delay_usec(100000); // wait 100msec and try again
}
}
if (rename_error) {
if (close_fail_notify) {
close_fail_notify(save_path);
}
}
save_path = "";
ERR_FAIL_COND_MSG(rename_error, "Safe save failed. This may be a permissions problem, but also may happen because you are running a paranoid antivirus. If this is the case, please switch to Windows Defender or disable the 'safe save' option in editor settings. This makes it work, but increases the risk of file corruption in a crash.");
}
}
String FileAccessWindows::get_path() const {
return path_src;
}
String FileAccessWindows::get_path_absolute() const {
return path;
}
bool FileAccessWindows::is_open() const {
return (f != NULL);
}
void FileAccessWindows::seek(uint64_t p_position) {
ERR_FAIL_COND(!f);
last_error = OK;
if (_fseeki64(f, p_position, SEEK_SET)) {
check_errors();
}
prev_op = 0;
}
void FileAccessWindows::seek_end(int64_t p_position) {
ERR_FAIL_COND(!f);
if (_fseeki64(f, p_position, SEEK_END)) {
check_errors();
}
prev_op = 0;
}
uint64_t FileAccessWindows::get_position() const {
int64_t aux_position = _ftelli64(f);
if (aux_position < 0) {
check_errors();
}
return aux_position;
}
uint64_t FileAccessWindows::get_len() const {
ERR_FAIL_COND_V(!f, 0);
uint64_t pos = get_position();
_fseeki64(f, 0, SEEK_END);
uint64_t size = get_position();
_fseeki64(f, pos, SEEK_SET);
return size;
}
bool FileAccessWindows::eof_reached() const {
check_errors();
return last_error == ERR_FILE_EOF;
}
uint8_t FileAccessWindows::get_8() const {
ERR_FAIL_COND_V(!f, 0);
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == WRITE) {
fflush(f);
}
prev_op = READ;
}
uint8_t b;
if (fread(&b, 1, 1, f) == 0) {
check_errors();
b = '\0';
};
return b;
}
uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V(!f, -1);
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == WRITE) {
fflush(f);
}
prev_op = READ;
}
uint64_t read = fread(p_dst, 1, p_length, f);
check_errors();
return read;
};
Error FileAccessWindows::get_error() const {
return last_error;
}
void FileAccessWindows::flush() {
ERR_FAIL_COND(!f);
fflush(f);
if (prev_op == WRITE) {
prev_op = 0;
}
}
void FileAccessWindows::store_8(uint8_t p_dest) {
ERR_FAIL_COND(!f);
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == READ) {
if (last_error != ERR_FILE_EOF) {
fseek(f, 0, SEEK_CUR);
}
}
prev_op = WRITE;
}
fwrite(&p_dest, 1, 1, f);
}
void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND(!f);
ERR_FAIL_COND(!p_src && p_length > 0);
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == READ) {
if (last_error != ERR_FILE_EOF) {
fseek(f, 0, SEEK_CUR);
}
}
prev_op = WRITE;
}
ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != (size_t)p_length);
}
bool FileAccessWindows::file_exists(const String &p_name) {
String filename = fix_path(p_name);
FILE *g = _wfsopen((LPCWSTR)(filename.utf16().get_data()), L"rb", _SH_DENYNO);
if (g == nullptr) {
return false;
} else {
fclose(g);
return true;
}
}
uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
String file = fix_path(p_file);
if (file.ends_with("/") && file != "/")
file = file.substr(0, file.length() - 1);
struct _stat st;
int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st);
if (rv == 0) {
return st.st_mtime;
} else {
print_verbose("Failed to get modified time for: " + p_file + "");
return 0;
}
}
uint32_t FileAccessWindows::_get_unix_permissions(const String &p_file) {
return 0;
}
Error FileAccessWindows::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
return ERR_UNAVAILABLE;
}
FileAccessWindows::FileAccessWindows() :
f(NULL),
flags(0),
prev_op(0),
last_error(OK) {
}
FileAccessWindows::~FileAccessWindows() {
close();
}
#else
void FileAccess::check_errors() const {

View File

@ -84,7 +84,7 @@ public:
virtual String get_path_absolute() const; /// returns the absolute path for the current open file
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position); ///< seek from the end of file with negative offset
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file with negative offset
virtual uint64_t get_position() const; ///< get position in the file
virtual uint64_t get_len() const; ///< get size of the file
@ -162,6 +162,15 @@ public:
protected:
#if defined(_WIN64) || defined(_WIN32)
void check_errors() const;
FILE *f;
int flags;
mutable int prev_op;
mutable Error last_error;
String path;
String path_src;
String save_path;
#else
void check_errors() const;