feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
@ -69,9 +70,19 @@ struct DirAccessWindowsPrivate {
|
|||
};
|
||||
|
||||
String DirAccessWindows::fix_path(const String &p_path) const {
|
||||
String r_path = DirAccess::fix_path(p_path);
|
||||
if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) {
|
||||
r_path = "\\\\?\\" + r_path.replace("/", "\\");
|
||||
String r_path = DirAccess::fix_path(p_path.trim_prefix(R"(\\?\)").replace("\\", "/"));
|
||||
if (r_path.ends_with(":")) {
|
||||
r_path += "/";
|
||||
}
|
||||
if (r_path.is_relative_path()) {
|
||||
r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path);
|
||||
} else if (r_path == ".") {
|
||||
r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/");
|
||||
}
|
||||
r_path = r_path.simplify_path();
|
||||
r_path = r_path.replace("/", "\\");
|
||||
if (!r_path.is_network_share_path() && !r_path.begins_with(R"(\\?\)")) {
|
||||
r_path = R"(\\?\)" + r_path;
|
||||
}
|
||||
return r_path;
|
||||
}
|
||||
|
|
@ -140,28 +151,33 @@ String DirAccessWindows::get_drive(int p_drive) {
|
|||
Error DirAccessWindows::change_dir(String p_dir) {
|
||||
GLOBAL_LOCK_FUNCTION
|
||||
|
||||
p_dir = fix_path(p_dir);
|
||||
String 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);
|
||||
Char16String real_current_dir_name;
|
||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||
real_current_dir_name.resize(str_len + 1);
|
||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||
String prev_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||
|
||||
SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data()));
|
||||
bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0);
|
||||
bool worked = (SetCurrentDirectoryW((LPCWSTR)(dir.utf16().get_data())) != 0);
|
||||
|
||||
String base = _get_root_path();
|
||||
if (!base.is_empty()) {
|
||||
GetCurrentDirectoryW(2048, real_current_dir_name);
|
||||
String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/");
|
||||
str_len = GetCurrentDirectoryW(0, nullptr);
|
||||
real_current_dir_name.resize(str_len + 1);
|
||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||
String new_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()).trim_prefix(R"(\\?\)").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("\\", "/");
|
||||
str_len = GetCurrentDirectoryW(0, nullptr);
|
||||
real_current_dir_name.resize(str_len + 1);
|
||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||
}
|
||||
|
||||
SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data()));
|
||||
|
|
@ -172,12 +188,6 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
|||
Error DirAccessWindows::make_dir(String p_dir) {
|
||||
GLOBAL_LOCK_FUNCTION
|
||||
|
||||
p_dir = fix_path(p_dir);
|
||||
if (p_dir.is_relative_path()) {
|
||||
p_dir = current_dir.path_join(p_dir);
|
||||
p_dir = fix_path(p_dir);
|
||||
}
|
||||
|
||||
if (FileAccessWindows::is_path_invalid(p_dir)) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
WARN_PRINT("The path :" + p_dir + " is a reserved Windows system pipe, so it can't be used for creating directories.");
|
||||
|
|
@ -185,12 +195,12 @@ Error DirAccessWindows::make_dir(String p_dir) {
|
|||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
p_dir = p_dir.simplify_path().replace("/", "\\");
|
||||
String dir = fix_path(p_dir);
|
||||
|
||||
bool success;
|
||||
int err;
|
||||
|
||||
success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr);
|
||||
success = CreateDirectoryW((LPCWSTR)(dir.utf16().get_data()), nullptr);
|
||||
err = GetLastError();
|
||||
|
||||
if (success) {
|
||||
|
|
@ -205,9 +215,10 @@ Error DirAccessWindows::make_dir(String p_dir) {
|
|||
}
|
||||
|
||||
String DirAccessWindows::get_current_dir(bool p_include_drive) const {
|
||||
String cdir = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/");
|
||||
String base = _get_root_path();
|
||||
if (!base.is_empty()) {
|
||||
String bd = current_dir.replace("\\", "/").replace_first(base, "");
|
||||
String bd = cdir.replace_first(base, "");
|
||||
if (bd.begins_with("/")) {
|
||||
return _get_root_string() + bd.substr(1, bd.length());
|
||||
} else {
|
||||
|
|
@ -216,30 +227,25 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const {
|
|||
}
|
||||
|
||||
if (p_include_drive) {
|
||||
return current_dir;
|
||||
return cdir;
|
||||
} else {
|
||||
if (_get_root_string().is_empty()) {
|
||||
int pos = current_dir.find(":");
|
||||
int pos = cdir.find_char(':');
|
||||
if (pos != -1) {
|
||||
return current_dir.substr(pos + 1);
|
||||
return cdir.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
return current_dir;
|
||||
return cdir;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessWindows::file_exists(String p_file) {
|
||||
GLOBAL_LOCK_FUNCTION
|
||||
|
||||
if (!p_file.is_absolute_path()) {
|
||||
p_file = get_current_dir().path_join(p_file);
|
||||
}
|
||||
|
||||
p_file = fix_path(p_file);
|
||||
String file = fix_path(p_file);
|
||||
|
||||
DWORD fileAttr;
|
||||
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(p_file.utf16().get_data()));
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(file.utf16().get_data()));
|
||||
if (INVALID_FILE_ATTRIBUTES == fileAttr) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -250,14 +256,10 @@ bool DirAccessWindows::file_exists(String p_file) {
|
|||
bool DirAccessWindows::dir_exists(String p_dir) {
|
||||
GLOBAL_LOCK_FUNCTION
|
||||
|
||||
if (p_dir.is_relative_path()) {
|
||||
p_dir = get_current_dir().path_join(p_dir);
|
||||
}
|
||||
|
||||
p_dir = fix_path(p_dir);
|
||||
String dir = fix_path(p_dir);
|
||||
|
||||
DWORD fileAttr;
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(p_dir.utf16().get_data()));
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(dir.utf16().get_data()));
|
||||
if (INVALID_FILE_ATTRIBUTES == fileAttr) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -265,66 +267,63 @@ bool DirAccessWindows::dir_exists(String p_dir) {
|
|||
}
|
||||
|
||||
Error DirAccessWindows::rename(String p_path, String p_new_path) {
|
||||
if (p_path.is_relative_path()) {
|
||||
p_path = get_current_dir().path_join(p_path);
|
||||
}
|
||||
|
||||
p_path = fix_path(p_path);
|
||||
|
||||
if (p_new_path.is_relative_path()) {
|
||||
p_new_path = get_current_dir().path_join(p_new_path);
|
||||
}
|
||||
|
||||
p_new_path = fix_path(p_new_path);
|
||||
String path = fix_path(p_path);
|
||||
String 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)) {
|
||||
if (path.to_lower() == new_path.to_lower()) {
|
||||
if (dir_exists(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;
|
||||
return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(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()), 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) {
|
||||
// Note: do not use GetTempFileNameW, it's not long path aware!
|
||||
Char16String tmpfile_utf16;
|
||||
uint64_t id = OS::get_singleton()->get_ticks_usec();
|
||||
while (true) {
|
||||
tmpfile_utf16 = (path + itos(id++) + ".tmp").utf16();
|
||||
HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED;
|
||||
if (!::ReplaceFileW((LPCWSTR)tmpfile_utf16.get_data(), (LPCWSTR)(path.utf16().get_data()), nullptr, 0, nullptr, nullptr)) {
|
||||
DeleteFileW((LPCWSTR)tmpfile_utf16.get_data());
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return MoveFileW((LPCWSTR)tmpfile_utf16.get_data(), (LPCWSTR)(new_path.utf16().get_data())) != 0 ? OK : FAILED;
|
||||
|
||||
} else {
|
||||
if (file_exists(new_path)) {
|
||||
if (remove(new_path) != OK) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return MoveFileW((LPCWSTR)(path.utf16().get_data()), (LPCWSTR)(new_path.utf16().get_data())) != 0 ? OK : FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessWindows::remove(String p_path) {
|
||||
if (p_path.is_relative_path()) {
|
||||
p_path = get_current_dir().path_join(p_path);
|
||||
}
|
||||
|
||||
p_path = fix_path(p_path);
|
||||
String path = fix_path(p_path);
|
||||
const Char16String &path_utf16 = path.utf16();
|
||||
|
||||
DWORD fileAttr;
|
||||
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(p_path.utf16().get_data()));
|
||||
fileAttr = GetFileAttributesW((LPCWSTR)(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;
|
||||
return RemoveDirectoryW((LPCWSTR)(path_utf16.get_data())) != 0 ? OK : FAILED;
|
||||
} else {
|
||||
return ::_wunlink((LPCWSTR)(p_path.utf16().get_data())) == 0 ? OK : FAILED;
|
||||
return DeleteFileW((LPCWSTR)(path_utf16.get_data())) != 0 ? OK : FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,16 +338,16 @@ uint64_t DirAccessWindows::get_space_left() {
|
|||
}
|
||||
|
||||
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) + "\\";
|
||||
String path = current_dir.trim_prefix(R"(\\?\)");
|
||||
|
||||
if (path.is_network_share_path()) {
|
||||
return "Network Share";
|
||||
}
|
||||
|
||||
int unit_end = path.find_char(':');
|
||||
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;
|
||||
|
|
@ -370,11 +369,7 @@ String DirAccessWindows::get_filesystem_type() const {
|
|||
}
|
||||
|
||||
bool DirAccessWindows::is_case_sensitive(const String &p_path) const {
|
||||
String f = p_path;
|
||||
if (!f.is_absolute_path()) {
|
||||
f = get_current_dir().path_join(f);
|
||||
}
|
||||
f = fix_path(f);
|
||||
String f = fix_path(p_path);
|
||||
|
||||
HANDLE h_file = ::CreateFileW((LPCWSTR)(f.utf16().get_data()), 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
|
|
@ -397,12 +392,7 @@ bool DirAccessWindows::is_case_sensitive(const String &p_path) const {
|
|||
}
|
||||
|
||||
bool DirAccessWindows::is_link(String p_file) {
|
||||
String f = p_file;
|
||||
|
||||
if (!f.is_absolute_path()) {
|
||||
f = get_current_dir().path_join(f);
|
||||
}
|
||||
f = fix_path(f);
|
||||
String f = fix_path(p_file);
|
||||
|
||||
DWORD attr = GetFileAttributesW((LPCWSTR)(f.utf16().get_data()));
|
||||
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||
|
|
@ -413,12 +403,7 @@ bool DirAccessWindows::is_link(String p_file) {
|
|||
}
|
||||
|
||||
String DirAccessWindows::read_link(String p_file) {
|
||||
String f = p_file;
|
||||
|
||||
if (!f.is_absolute_path()) {
|
||||
f = get_current_dir().path_join(f);
|
||||
}
|
||||
f = fix_path(f);
|
||||
String f = fix_path(p_file);
|
||||
|
||||
HANDLE hfile = CreateFileW((LPCWSTR)(f.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
if (hfile == INVALID_HANDLE_VALUE) {
|
||||
|
|
@ -434,22 +419,18 @@ String DirAccessWindows::read_link(String p_file) {
|
|||
GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
|
||||
CloseHandle(hfile);
|
||||
|
||||
return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)");
|
||||
return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)").replace("\\", "/");
|
||||
}
|
||||
|
||||
Error DirAccessWindows::create_link(String p_source, String p_target) {
|
||||
if (p_target.is_relative_path()) {
|
||||
p_target = get_current_dir().path_join(p_target);
|
||||
}
|
||||
String source = fix_path(p_source);
|
||||
String target = fix_path(p_target);
|
||||
|
||||
p_source = fix_path(p_source);
|
||||
p_target = fix_path(p_target);
|
||||
|
||||
DWORD file_attr = GetFileAttributesW((LPCWSTR)(p_source.utf16().get_data()));
|
||||
DWORD file_attr = GetFileAttributesW((LPCWSTR)(source.utf16().get_data()));
|
||||
bool is_dir = (file_attr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
DWORD flags = ((is_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
|
||||
if (CreateSymbolicLinkW((LPCWSTR)p_target.utf16().get_data(), (LPCWSTR)p_source.utf16().get_data(), flags) != 0) {
|
||||
if (CreateSymbolicLinkW((LPCWSTR)target.utf16().get_data(), (LPCWSTR)source.utf16().get_data(), flags) != 0) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
|
|
@ -459,7 +440,12 @@ Error DirAccessWindows::create_link(String p_source, String p_target) {
|
|||
DirAccessWindows::DirAccessWindows() {
|
||||
p = memnew(DirAccessWindowsPrivate);
|
||||
p->h = INVALID_HANDLE_VALUE;
|
||||
current_dir = ".";
|
||||
|
||||
Char16String real_current_dir_name;
|
||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||
real_current_dir_name.resize(str_len + 1);
|
||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||
|
||||
DWORD mask = GetLogicalDrives();
|
||||
|
||||
|
|
|
|||
|
|
@ -52,10 +52,18 @@
|
|||
#define S_ISREG(m) ((m) & _S_IFREG)
|
||||
#endif
|
||||
|
||||
void FileAccessWindows::check_errors() const {
|
||||
void FileAccessWindows::check_errors(bool p_write) const {
|
||||
ERR_FAIL_NULL(f);
|
||||
|
||||
if (feof(f)) {
|
||||
last_error = OK;
|
||||
if (ferror(f)) {
|
||||
if (p_write) {
|
||||
last_error = ERR_FILE_CANT_WRITE;
|
||||
} else {
|
||||
last_error = ERR_FILE_CANT_READ;
|
||||
}
|
||||
}
|
||||
if (!p_write && feof(f)) {
|
||||
last_error = ERR_FILE_EOF;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +72,7 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
|
|||
// Check for invalid operating system file.
|
||||
String fname = p_path.get_file().to_lower();
|
||||
|
||||
int dot = fname.find(".");
|
||||
int dot = fname.find_char('.');
|
||||
if (dot != -1) {
|
||||
fname = fname.substr(0, dot);
|
||||
}
|
||||
|
|
@ -73,8 +81,18 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
|
|||
|
||||
String FileAccessWindows::fix_path(const String &p_path) const {
|
||||
String r_path = FileAccess::fix_path(p_path);
|
||||
if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) {
|
||||
r_path = "\\\\?\\" + r_path.replace("/", "\\");
|
||||
|
||||
if (r_path.is_relative_path()) {
|
||||
Char16String current_dir_name;
|
||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||
current_dir_name.resize(str_len + 1);
|
||||
GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());
|
||||
r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path);
|
||||
}
|
||||
r_path = r_path.simplify_path();
|
||||
r_path = r_path.replace("/", "\\");
|
||||
if (!r_path.is_network_share_path() && !r_path.begins_with(R"(\\?\)")) {
|
||||
r_path = R"(\\?\)" + r_path;
|
||||
}
|
||||
return r_path;
|
||||
}
|
||||
|
|
@ -108,24 +126,22 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
|
|||
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;
|
||||
}
|
||||
if (path.ends_with(":\\") || path.ends_with(":")) {
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
DWORD file_attr = GetFileAttributesW((LPCWSTR)(path.utf16().get_data()));
|
||||
if (file_attr != INVALID_FILE_ATTRIBUTES && (file_attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Windows is case insensitive, but all other platforms are sensitive to it
|
||||
// Windows is case insensitive in the default configuration, but other platforms can be 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), we only check for relative paths, or paths in res:// or user://,
|
||||
// other paths aren't likely to be portable anyway.
|
||||
if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) {
|
||||
String base_path = path;
|
||||
String base_path = p_path;
|
||||
String working_path;
|
||||
String proper_path;
|
||||
|
||||
|
|
@ -144,23 +160,17 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
|
|||
}
|
||||
proper_path = "user://";
|
||||
}
|
||||
working_path = fix_path(working_path);
|
||||
|
||||
WIN32_FIND_DATAW d;
|
||||
Vector<String> parts = base_path.split("/");
|
||||
Vector<String> parts = base_path.simplify_path().split("/");
|
||||
|
||||
bool mismatch = false;
|
||||
|
||||
for (const String &part : parts) {
|
||||
working_path = working_path.path_join(part);
|
||||
|
||||
// Skip if relative.
|
||||
if (part == "." || part == "..") {
|
||||
proper_path = proper_path.path_join(part);
|
||||
continue;
|
||||
}
|
||||
working_path = working_path + "\\" + part;
|
||||
|
||||
HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d);
|
||||
|
||||
if (fnd == INVALID_HANDLE_VALUE) {
|
||||
mismatch = false;
|
||||
break;
|
||||
|
|
@ -186,12 +196,22 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
|
|||
if (is_backup_save_enabled() && p_mode_flags == WRITE) {
|
||||
save_path = path;
|
||||
// Create a temporary file in the same directory as the target file.
|
||||
WCHAR tmpFileName[MAX_PATH];
|
||||
if (GetTempFileNameW((LPCWSTR)(path.get_base_dir().utf16().get_data()), (LPCWSTR)(path.get_file().utf16().get_data()), 0, tmpFileName) == 0) {
|
||||
last_error = ERR_FILE_CANT_OPEN;
|
||||
return last_error;
|
||||
// Note: do not use GetTempFileNameW, it's not long path aware!
|
||||
String tmpfile;
|
||||
uint64_t id = OS::get_singleton()->get_ticks_usec();
|
||||
while (true) {
|
||||
tmpfile = path + itos(id++) + ".tmp";
|
||||
HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) {
|
||||
last_error = ERR_FILE_CANT_WRITE;
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
path = tmpFileName;
|
||||
path = tmpfile;
|
||||
}
|
||||
|
||||
f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, is_backup_save_enabled() ? _SH_SECURE : _SH_DENYNO);
|
||||
|
|
@ -235,7 +255,7 @@ void FileAccessWindows::_close() {
|
|||
} else {
|
||||
// Either the target exists and is locked (temporarily, hopefully)
|
||||
// or it doesn't exist; let's assume the latter before re-trying.
|
||||
rename_error = _wrename((LPCWSTR)(path_utf16.get_data()), (LPCWSTR)(save_path_utf16.get_data())) != 0;
|
||||
rename_error = MoveFileW((LPCWSTR)(path_utf16.get_data()), (LPCWSTR)(save_path_utf16.get_data())) == 0;
|
||||
}
|
||||
|
||||
if (!rename_error) {
|
||||
|
|
@ -262,7 +282,7 @@ String FileAccessWindows::get_path() const {
|
|||
}
|
||||
|
||||
String FileAccessWindows::get_path_absolute() const {
|
||||
return path;
|
||||
return path.trim_prefix(R"(\\?\)").replace("\\", "/");
|
||||
}
|
||||
|
||||
bool FileAccessWindows::is_open() const {
|
||||
|
|
@ -272,7 +292,6 @@ bool FileAccessWindows::is_open() const {
|
|||
void FileAccessWindows::seek(uint64_t p_position) {
|
||||
ERR_FAIL_NULL(f);
|
||||
|
||||
last_error = OK;
|
||||
if (_fseeki64(f, p_position, SEEK_SET)) {
|
||||
check_errors();
|
||||
}
|
||||
|
|
@ -289,6 +308,8 @@ void FileAccessWindows::seek_end(int64_t p_position) {
|
|||
}
|
||||
|
||||
uint64_t FileAccessWindows::get_position() const {
|
||||
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
|
||||
|
||||
int64_t aux_position = _ftelli64(f);
|
||||
if (aux_position < 0) {
|
||||
check_errors();
|
||||
|
|
@ -308,97 +329,12 @@ uint64_t FileAccessWindows::get_length() const {
|
|||
}
|
||||
|
||||
bool FileAccessWindows::eof_reached() const {
|
||||
check_errors();
|
||||
return last_error == ERR_FILE_EOF;
|
||||
}
|
||||
|
||||
uint8_t FileAccessWindows::get_8() const {
|
||||
ERR_FAIL_NULL_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;
|
||||
}
|
||||
|
||||
uint16_t FileAccessWindows::get_16() const {
|
||||
ERR_FAIL_NULL_V(f, 0);
|
||||
|
||||
if (flags == READ_WRITE || flags == WRITE_READ) {
|
||||
if (prev_op == WRITE) {
|
||||
fflush(f);
|
||||
}
|
||||
prev_op = READ;
|
||||
}
|
||||
|
||||
uint16_t b = 0;
|
||||
if (fread(&b, 1, 2, f) != 2) {
|
||||
check_errors();
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
b = BSWAP16(b);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
uint32_t FileAccessWindows::get_32() const {
|
||||
ERR_FAIL_NULL_V(f, 0);
|
||||
|
||||
if (flags == READ_WRITE || flags == WRITE_READ) {
|
||||
if (prev_op == WRITE) {
|
||||
fflush(f);
|
||||
}
|
||||
prev_op = READ;
|
||||
}
|
||||
|
||||
uint32_t b = 0;
|
||||
if (fread(&b, 1, 4, f) != 4) {
|
||||
check_errors();
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
b = BSWAP32(b);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
uint64_t FileAccessWindows::get_64() const {
|
||||
ERR_FAIL_NULL_V(f, 0);
|
||||
|
||||
if (flags == READ_WRITE || flags == WRITE_READ) {
|
||||
if (prev_op == WRITE) {
|
||||
fflush(f);
|
||||
}
|
||||
prev_op = READ;
|
||||
}
|
||||
|
||||
uint64_t b = 0;
|
||||
if (fread(&b, 1, 8, f) != 8) {
|
||||
check_errors();
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
b = BSWAP64(b);
|
||||
}
|
||||
|
||||
return b;
|
||||
return feof(f);
|
||||
}
|
||||
|
||||
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_NULL_V(f, -1);
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
|
||||
if (flags == READ_WRITE || flags == WRITE_READ) {
|
||||
if (prev_op == WRITE) {
|
||||
|
|
@ -406,8 +342,10 @@ uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const
|
|||
}
|
||||
prev_op = READ;
|
||||
}
|
||||
|
||||
uint64_t read = fread(p_dst, 1, p_length, f);
|
||||
check_errors();
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
|
|
@ -442,22 +380,9 @@ void FileAccessWindows::flush() {
|
|||
}
|
||||
}
|
||||
|
||||
void FileAccessWindows::store_8(uint8_t p_dest) {
|
||||
ERR_FAIL_NULL(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_16(uint16_t p_dest) {
|
||||
ERR_FAIL_NULL(f);
|
||||
bool FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_NULL_V(f, false);
|
||||
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
|
||||
|
||||
if (flags == READ_WRITE || flags == WRITE_READ) {
|
||||
if (prev_op == READ) {
|
||||
|
|
@ -468,64 +393,9 @@ void FileAccessWindows::store_16(uint16_t p_dest) {
|
|||
prev_op = WRITE;
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
p_dest = BSWAP16(p_dest);
|
||||
}
|
||||
|
||||
fwrite(&p_dest, 1, 2, f);
|
||||
}
|
||||
|
||||
void FileAccessWindows::store_32(uint32_t p_dest) {
|
||||
ERR_FAIL_NULL(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;
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
p_dest = BSWAP32(p_dest);
|
||||
}
|
||||
|
||||
fwrite(&p_dest, 1, 4, f);
|
||||
}
|
||||
|
||||
void FileAccessWindows::store_64(uint64_t p_dest) {
|
||||
ERR_FAIL_NULL(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;
|
||||
}
|
||||
|
||||
if (big_endian) {
|
||||
p_dest = BSWAP64(p_dest);
|
||||
}
|
||||
|
||||
fwrite(&p_dest, 1, 8, f);
|
||||
}
|
||||
|
||||
void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_NULL(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 res = fwrite(p_src, 1, p_length, f) == (size_t)p_length;
|
||||
check_errors(true);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool FileAccessWindows::file_exists(const String &p_name) {
|
||||
|
|
@ -534,13 +404,8 @@ 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;
|
||||
}
|
||||
DWORD file_attr = GetFileAttributesW((LPCWSTR)(filename.utf16().get_data()));
|
||||
return (file_attr != INVALID_FILE_ATTRIBUTES) && !(file_attr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
|
||||
|
|
@ -549,19 +414,43 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
|
|||
}
|
||||
|
||||
String file = fix_path(p_file);
|
||||
if (file.ends_with("/") && 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);
|
||||
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
|
||||
if (rv == 0) {
|
||||
return st.st_mtime;
|
||||
} else {
|
||||
print_verbose("Failed to get modified time for: " + p_file + "");
|
||||
return 0;
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
FILETIME ft_create, ft_write;
|
||||
|
||||
bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write);
|
||||
|
||||
CloseHandle(handle);
|
||||
|
||||
if (status) {
|
||||
uint64_t ret = 0;
|
||||
|
||||
// If write time is invalid, fallback to creation time.
|
||||
if (ft_write.dwHighDateTime == 0 && ft_write.dwLowDateTime == 0) {
|
||||
ret = ft_create.dwHighDateTime;
|
||||
ret <<= 32;
|
||||
ret |= ft_create.dwLowDateTime;
|
||||
} else {
|
||||
ret = ft_write.dwHighDateTime;
|
||||
ret <<= 32;
|
||||
ret |= ft_write.dwLowDateTime;
|
||||
}
|
||||
|
||||
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
|
||||
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
|
||||
|
||||
if (ret >= TICKS_TO_UNIX_EPOCH) {
|
||||
return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) {
|
||||
|
|
@ -582,14 +471,15 @@ bool FileAccessWindows::_get_hidden_attribute(const String &p_file) {
|
|||
|
||||
Error FileAccessWindows::_set_hidden_attribute(const String &p_file, bool p_hidden) {
|
||||
String file = fix_path(p_file);
|
||||
const Char16String &file_utf16 = file.utf16();
|
||||
|
||||
DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
|
||||
DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data());
|
||||
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
|
||||
BOOL ok;
|
||||
if (p_hidden) {
|
||||
ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_HIDDEN);
|
||||
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_HIDDEN);
|
||||
} else {
|
||||
ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN);
|
||||
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
|
||||
|
||||
|
|
@ -606,14 +496,15 @@ bool FileAccessWindows::_get_read_only_attribute(const String &p_file) {
|
|||
|
||||
Error FileAccessWindows::_set_read_only_attribute(const String &p_file, bool p_ro) {
|
||||
String file = fix_path(p_file);
|
||||
const Char16String &file_utf16 = file.utf16();
|
||||
|
||||
DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
|
||||
DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data());
|
||||
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
|
||||
BOOL ok;
|
||||
if (p_ro) {
|
||||
ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_READONLY);
|
||||
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_READONLY);
|
||||
} else {
|
||||
ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_READONLY);
|
||||
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_READONLY);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
|
||||
|
||||
|
|
@ -639,6 +530,9 @@ void FileAccessWindows::initialize() {
|
|||
invalid_files.insert(reserved_files[reserved_file_index]);
|
||||
reserved_file_index++;
|
||||
}
|
||||
|
||||
_setmaxstdio(8192);
|
||||
print_verbose(vformat("Maximum number of file handles: %d", _getmaxstdio()));
|
||||
}
|
||||
|
||||
void FileAccessWindows::finalize() {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
class FileAccessWindows : public FileAccess {
|
||||
FILE *f = nullptr;
|
||||
int flags = 0;
|
||||
void check_errors() const;
|
||||
void check_errors(bool p_write = false) const;
|
||||
mutable int prev_op = 0;
|
||||
mutable Error last_error = OK;
|
||||
String path;
|
||||
|
|
@ -69,21 +69,13 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
virtual uint16_t get_16() const override;
|
||||
virtual uint32_t get_32() const override;
|
||||
virtual uint64_t get_64() const override;
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override;
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_dest) override; ///< store a byte
|
||||
virtual void store_16(uint16_t p_dest) override;
|
||||
virtual void store_32(uint32_t p_dest) override;
|
||||
virtual void store_64(uint64_t p_dest) override;
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
|
||||
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
|
||||
|
||||
|
|
|
|||
|
|
@ -35,15 +35,21 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd) {
|
||||
Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd, bool p_blocking) {
|
||||
// Open pipe using handles created by CreatePipe(rfd, wfd, NULL, 4096) call in the OS.execute_with_pipe.
|
||||
_close();
|
||||
|
||||
path_src = String();
|
||||
ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||
ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||
fd[0] = p_rfd;
|
||||
fd[1] = p_wfd;
|
||||
|
||||
if (!p_blocking) {
|
||||
DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
|
||||
SetNamedPipeHandleState(fd[0], &mode, nullptr, nullptr);
|
||||
SetNamedPipeHandleState(fd[1], &mode, nullptr, nullptr);
|
||||
}
|
||||
|
||||
last_error = OK;
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -52,18 +58,18 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
|
|||
_close();
|
||||
|
||||
path_src = p_path;
|
||||
ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||
ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||
|
||||
path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
|
||||
|
||||
HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, nullptr);
|
||||
h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 4096, 4096, 0, nullptr);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
last_error = ERR_FILE_CANT_OPEN;
|
||||
return last_error;
|
||||
}
|
||||
ConnectNamedPipe(h, NULL);
|
||||
ConnectNamedPipe(h, nullptr);
|
||||
}
|
||||
fd[0] = h;
|
||||
fd[1] = h;
|
||||
|
|
@ -73,19 +79,19 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
|
|||
}
|
||||
|
||||
void FileAccessWindowsPipe::_close() {
|
||||
if (fd[0] == 0) {
|
||||
if (fd[0] == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (fd[1] != fd[0]) {
|
||||
CloseHandle(fd[1]);
|
||||
}
|
||||
CloseHandle(fd[0]);
|
||||
fd[0] = 0;
|
||||
fd[1] = 0;
|
||||
fd[0] = nullptr;
|
||||
fd[1] = nullptr;
|
||||
}
|
||||
|
||||
bool FileAccessWindowsPipe::is_open() const {
|
||||
return (fd[0] != 0 || fd[1] != 0);
|
||||
return (fd[0] != nullptr || fd[1] != nullptr);
|
||||
}
|
||||
|
||||
String FileAccessWindowsPipe::get_path() const {
|
||||
|
|
@ -96,24 +102,19 @@ String FileAccessWindowsPipe::get_path_absolute() const {
|
|||
return path_src;
|
||||
}
|
||||
|
||||
uint8_t FileAccessWindowsPipe::get_8() const {
|
||||
ERR_FAIL_COND_V_MSG(fd[0] == 0, 0, "Pipe must be opened before use.");
|
||||
uint64_t FileAccessWindowsPipe::get_length() const {
|
||||
ERR_FAIL_COND_V_MSG(fd[0] == nullptr, -1, "Pipe must be opened before use.");
|
||||
|
||||
uint8_t b;
|
||||
if (!ReadFile(fd[0], &b, 1, nullptr, nullptr)) {
|
||||
last_error = ERR_FILE_CANT_READ;
|
||||
b = '\0';
|
||||
} else {
|
||||
last_error = OK;
|
||||
}
|
||||
return b;
|
||||
DWORD buf_rem = 0;
|
||||
ERR_FAIL_COND_V(!PeekNamedPipe(fd[0], nullptr, 0, nullptr, &buf_rem, nullptr), 0);
|
||||
return buf_rem;
|
||||
}
|
||||
|
||||
uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V_MSG(fd[0] == nullptr, -1, "Pipe must be opened before use.");
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
|
||||
|
||||
DWORD read = -1;
|
||||
DWORD read = 0;
|
||||
if (!ReadFile(fd[0], p_dst, p_length, &read, nullptr) || read != p_length) {
|
||||
last_error = ERR_FILE_CANT_READ;
|
||||
} else {
|
||||
|
|
@ -126,25 +127,18 @@ Error FileAccessWindowsPipe::get_error() const {
|
|||
return last_error;
|
||||
}
|
||||
|
||||
void FileAccessWindowsPipe::store_8(uint8_t p_src) {
|
||||
ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
|
||||
if (!WriteFile(fd[1], &p_src, 1, nullptr, nullptr)) {
|
||||
last_error = ERR_FILE_CANT_WRITE;
|
||||
} else {
|
||||
last_error = OK;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
|
||||
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||
bool FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_V_MSG(fd[1] == nullptr, false, "Pipe must be opened before use.");
|
||||
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
|
||||
|
||||
DWORD read = -1;
|
||||
bool ok = WriteFile(fd[1], p_src, p_length, &read, nullptr);
|
||||
if (!ok || read != p_length) {
|
||||
last_error = ERR_FILE_CANT_WRITE;
|
||||
return false;
|
||||
} else {
|
||||
last_error = OK;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
class FileAccessWindowsPipe : public FileAccess {
|
||||
HANDLE fd[2] = { 0, 0 };
|
||||
HANDLE fd[2] = { nullptr, nullptr };
|
||||
|
||||
mutable Error last_error = OK;
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ class FileAccessWindowsPipe : public FileAccess {
|
|||
void _close();
|
||||
|
||||
public:
|
||||
Error open_existing(HANDLE p_rfd, HANDLE p_wfd);
|
||||
Error open_existing(HANDLE p_rfd, HANDLE p_wfd, bool p_blocking);
|
||||
|
||||
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||
virtual bool is_open() const override; ///< true when file is open
|
||||
|
|
@ -60,19 +60,17 @@ public:
|
|||
virtual void seek(uint64_t p_position) override {}
|
||||
virtual void seek_end(int64_t p_position = 0) override {}
|
||||
virtual uint64_t get_position() const override { return 0; }
|
||||
virtual uint64_t get_length() const override { return 0; }
|
||||
virtual uint64_t get_length() const override;
|
||||
|
||||
virtual bool eof_reached() const override { return false; }
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override {}
|
||||
virtual void store_8(uint8_t p_src) override; ///< store a byte
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
|
||||
virtual bool file_exists(const String &p_name) override { return false; }
|
||||
|
||||
|
|
|
|||
164
engine/drivers/windows/ip_windows.cpp
Normal file
164
engine/drivers/windows/ip_windows.cpp
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/**************************************************************************/
|
||||
/* ip_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "ip_windows.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
|
||||
IPAddress ip;
|
||||
|
||||
if (p_addr->sa_family == AF_INET) {
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
|
||||
ip.set_ipv4((uint8_t *)&(addr->sin_addr));
|
||||
} else if (p_addr->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
|
||||
ip.set_ipv6(addr6->sin6_addr.s6_addr);
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
void IPWindows::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
if (p_type == TYPE_IPV4) {
|
||||
hints.ai_family = AF_INET;
|
||||
} else if (p_type == TYPE_IPV6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_flags = 0;
|
||||
} else {
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
}
|
||||
hints.ai_flags &= ~AI_NUMERICHOST;
|
||||
|
||||
int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result);
|
||||
if (s != 0) {
|
||||
print_verbose("getaddrinfo failed! Cannot resolve hostname.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == nullptr || result->ai_addr == nullptr) {
|
||||
print_verbose("Invalid response from getaddrinfo.");
|
||||
if (result) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct addrinfo *next = result;
|
||||
|
||||
do {
|
||||
if (next->ai_addr == nullptr) {
|
||||
next = next->ai_next;
|
||||
continue;
|
||||
}
|
||||
IPAddress ip = _sockaddr2ip(next->ai_addr);
|
||||
if (ip.is_valid() && !r_addresses.find(ip)) {
|
||||
r_addresses.push_back(ip);
|
||||
}
|
||||
next = next->ai_next;
|
||||
} while (next);
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
void IPWindows::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
|
||||
ULONG buf_size = 1024;
|
||||
IP_ADAPTER_ADDRESSES *addrs;
|
||||
|
||||
while (true) {
|
||||
addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
|
||||
int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
nullptr, addrs, &buf_size);
|
||||
if (err == NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
memfree(addrs);
|
||||
if (err == ERROR_BUFFER_OVERFLOW) {
|
||||
continue; // Will go back and alloc the right size.
|
||||
}
|
||||
|
||||
ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
|
||||
}
|
||||
|
||||
IP_ADAPTER_ADDRESSES *adapter = addrs;
|
||||
|
||||
while (adapter != nullptr) {
|
||||
Interface_Info info;
|
||||
info.name = adapter->AdapterName;
|
||||
info.name_friendly = adapter->FriendlyName;
|
||||
info.index = String::num_uint64(adapter->IfIndex);
|
||||
|
||||
IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
|
||||
while (address != nullptr) {
|
||||
int family = address->Address.lpSockaddr->sa_family;
|
||||
if (family != AF_INET && family != AF_INET6) {
|
||||
continue;
|
||||
}
|
||||
info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
|
||||
address = address->Next;
|
||||
}
|
||||
adapter = adapter->Next;
|
||||
// Only add interface if it has at least one IP.
|
||||
if (info.ip_addresses.size() > 0) {
|
||||
r_interfaces->insert(info.name, info);
|
||||
}
|
||||
}
|
||||
|
||||
memfree(addrs);
|
||||
}
|
||||
|
||||
void IPWindows::make_default() {
|
||||
_create = _create_unix;
|
||||
}
|
||||
|
||||
IP *IPWindows::_create_unix() {
|
||||
return memnew(IPWindows);
|
||||
}
|
||||
|
||||
IPWindows::IPWindows() {
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
54
engine/drivers/windows/ip_windows.h
Normal file
54
engine/drivers/windows/ip_windows.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**************************************************************************/
|
||||
/* ip_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef IP_WINDOWS_H
|
||||
#define IP_WINDOWS_H
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
|
||||
#include "core/io/ip.h"
|
||||
|
||||
class IPWindows : public IP {
|
||||
GDCLASS(IPWindows, IP);
|
||||
|
||||
virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;
|
||||
|
||||
static IP *_create_unix();
|
||||
|
||||
public:
|
||||
virtual void get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const override;
|
||||
|
||||
static void make_default();
|
||||
IPWindows();
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
|
||||
#endif // IP_WINDOWS_H
|
||||
613
engine/drivers/windows/net_socket_winsock.cpp
Normal file
613
engine/drivers/windows/net_socket_winsock.cpp
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
/**************************************************************************/
|
||||
/* net_socket_winsock.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "net_socket_winsock.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <mswsock.h>
|
||||
// Workaround missing flag in MinGW
|
||||
#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
|
||||
#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
|
||||
#endif
|
||||
|
||||
size_t NetSocketWinSock::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
|
||||
memset(p_addr, 0, sizeof(struct sockaddr_storage));
|
||||
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket.
|
||||
|
||||
// IPv6 only socket with IPv4 address.
|
||||
ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);
|
||||
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
|
||||
addr6->sin6_family = AF_INET6;
|
||||
addr6->sin6_port = htons(p_port);
|
||||
if (p_ip.is_valid()) {
|
||||
memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
|
||||
} else {
|
||||
addr6->sin6_addr = in6addr_any;
|
||||
}
|
||||
return sizeof(sockaddr_in6);
|
||||
} else { // IPv4 socket.
|
||||
|
||||
// IPv4 socket with IPv6 address.
|
||||
ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);
|
||||
|
||||
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
|
||||
addr4->sin_family = AF_INET;
|
||||
addr4->sin_port = htons(p_port); // Short, network byte order.
|
||||
|
||||
if (p_ip.is_valid()) {
|
||||
memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
|
||||
} else {
|
||||
addr4->sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
return sizeof(sockaddr_in);
|
||||
}
|
||||
}
|
||||
|
||||
void NetSocketWinSock::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
|
||||
if (p_addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
|
||||
if (r_ip) {
|
||||
r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
|
||||
}
|
||||
if (r_port) {
|
||||
*r_port = ntohs(addr4->sin_port);
|
||||
}
|
||||
} else if (p_addr->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
|
||||
if (r_ip) {
|
||||
r_ip->set_ipv6(addr6->sin6_addr.s6_addr);
|
||||
}
|
||||
if (r_port) {
|
||||
*r_port = ntohs(addr6->sin6_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NetSocket *NetSocketWinSock::_create_func() {
|
||||
return memnew(NetSocketWinSock);
|
||||
}
|
||||
|
||||
void NetSocketWinSock::make_default() {
|
||||
ERR_FAIL_COND(_create != nullptr);
|
||||
|
||||
WSADATA data;
|
||||
WSAStartup(MAKEWORD(2, 2), &data);
|
||||
_create = _create_func;
|
||||
}
|
||||
|
||||
void NetSocketWinSock::cleanup() {
|
||||
ERR_FAIL_COND(_create == nullptr);
|
||||
|
||||
WSACleanup();
|
||||
_create = nullptr;
|
||||
}
|
||||
|
||||
NetSocketWinSock::NetSocketWinSock() {
|
||||
}
|
||||
|
||||
NetSocketWinSock::~NetSocketWinSock() {
|
||||
close();
|
||||
}
|
||||
|
||||
NetSocketWinSock::NetError NetSocketWinSock::_get_socket_error() const {
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAEISCONN) {
|
||||
return ERR_NET_IS_CONNECTED;
|
||||
}
|
||||
if (err == WSAEINPROGRESS || err == WSAEALREADY) {
|
||||
return ERR_NET_IN_PROGRESS;
|
||||
}
|
||||
if (err == WSAEWOULDBLOCK) {
|
||||
return ERR_NET_WOULD_BLOCK;
|
||||
}
|
||||
if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) {
|
||||
return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
|
||||
}
|
||||
if (err == WSAEACCES) {
|
||||
return ERR_NET_UNAUTHORIZED;
|
||||
}
|
||||
if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
|
||||
return ERR_NET_BUFFER_TOO_SMALL;
|
||||
}
|
||||
print_verbose("Socket error: " + itos(err) + ".");
|
||||
return ERR_NET_OTHER;
|
||||
}
|
||||
|
||||
bool NetSocketWinSock::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
|
||||
if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {
|
||||
return false;
|
||||
} else if (!p_for_bind && !p_ip.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
// Check if socket support this IP type.
|
||||
IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Error NetSocketWinSock::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);
|
||||
|
||||
// Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
|
||||
IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type;
|
||||
// This needs to be the proper level for the multicast group, no matter if the socket is dual stacking.
|
||||
int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
|
||||
int ret = -1;
|
||||
|
||||
IPAddress if_ip;
|
||||
uint32_t if_v6id = 0;
|
||||
HashMap<String, IP::Interface_Info> if_info;
|
||||
IP::get_singleton()->get_local_interfaces(&if_info);
|
||||
for (KeyValue<String, IP::Interface_Info> &E : if_info) {
|
||||
IP::Interface_Info &c = E.value;
|
||||
if (c.name != p_if_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if_v6id = (uint32_t)c.index.to_int();
|
||||
if (type == IP::TYPE_IPV6) {
|
||||
break; // IPv6 uses index.
|
||||
}
|
||||
|
||||
for (const IPAddress &F : c.ip_addresses) {
|
||||
if (!F.is_ipv4()) {
|
||||
continue; // Wrong IP type.
|
||||
}
|
||||
if_ip = F;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (level == IPPROTO_IP) {
|
||||
ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER);
|
||||
struct ip_mreq greq;
|
||||
int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
|
||||
memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4);
|
||||
memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4);
|
||||
ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));
|
||||
} else {
|
||||
struct ipv6_mreq greq;
|
||||
int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP;
|
||||
memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16);
|
||||
greq.ipv6mr_interface = if_v6id;
|
||||
ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));
|
||||
}
|
||||
ERR_FAIL_COND_V(ret != 0, FAILED);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream) {
|
||||
_sock = p_sock;
|
||||
_ip_type = p_ip_type;
|
||||
_is_stream = p_is_stream;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) {
|
||||
ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
|
||||
|
||||
int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
|
||||
int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP;
|
||||
int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
|
||||
_sock = socket(family, type, protocol);
|
||||
|
||||
if (_sock == INVALID_SOCKET && ip_type == IP::TYPE_ANY) {
|
||||
// Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
|
||||
// in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
|
||||
ip_type = IP::TYPE_IPV4;
|
||||
family = AF_INET;
|
||||
_sock = socket(family, type, protocol);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(_sock == INVALID_SOCKET, FAILED);
|
||||
_ip_type = ip_type;
|
||||
|
||||
if (family == AF_INET6) {
|
||||
// Select IPv4 over IPv6 mapping.
|
||||
set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
|
||||
}
|
||||
|
||||
if (protocol == IPPROTO_UDP) {
|
||||
// Make sure to disable broadcasting for UDP sockets.
|
||||
// Depending on the OS, this option might or might not be enabled by default. Let's normalize it.
|
||||
set_broadcasting_enabled(false);
|
||||
}
|
||||
|
||||
_is_stream = p_sock_type == TYPE_TCP;
|
||||
|
||||
if (!_is_stream) {
|
||||
// Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when
|
||||
// recv/recvfrom and an ICMP reply was received from a previous send/sendto.
|
||||
unsigned long disable = 0;
|
||||
if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) {
|
||||
print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows.");
|
||||
}
|
||||
if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) {
|
||||
// This feature seems not to be supported on wine.
|
||||
print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows.");
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void NetSocketWinSock::close() {
|
||||
if (_sock != INVALID_SOCKET) {
|
||||
closesocket(_sock);
|
||||
}
|
||||
|
||||
_sock = INVALID_SOCKET;
|
||||
_ip_type = IP::TYPE_NONE;
|
||||
_is_stream = false;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::bind(IPAddress p_addr, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
|
||||
|
||||
sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
|
||||
|
||||
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
|
||||
close();
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::listen(int p_max_pending) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
if (::listen(_sock, p_max_pending) != 0) {
|
||||
_get_socket_error();
|
||||
print_verbose("Failed to listen from socket.");
|
||||
close();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::connect_to_host(IPAddress p_host, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
|
||||
|
||||
if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
|
||||
switch (err) {
|
||||
// We are already connected.
|
||||
case ERR_NET_IS_CONNECTED:
|
||||
return OK;
|
||||
// Still waiting to connect, try again in a while.
|
||||
case ERR_NET_WOULD_BLOCK:
|
||||
case ERR_NET_IN_PROGRESS:
|
||||
return ERR_BUSY;
|
||||
default:
|
||||
print_verbose("Connection to remote host failed.");
|
||||
close();
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::poll(PollType p_type, int p_timeout) const {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
bool ready = false;
|
||||
fd_set rd, wr, ex;
|
||||
fd_set *rdp = nullptr;
|
||||
fd_set *wrp = nullptr;
|
||||
FD_ZERO(&rd);
|
||||
FD_ZERO(&wr);
|
||||
FD_ZERO(&ex);
|
||||
FD_SET(_sock, &ex);
|
||||
struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 };
|
||||
// For blocking operation, pass nullptr timeout pointer to select.
|
||||
struct timeval *tp = nullptr;
|
||||
if (p_timeout >= 0) {
|
||||
// If timeout is non-negative, we want to specify the timeout instead.
|
||||
tp = &timeout;
|
||||
}
|
||||
|
||||
switch (p_type) {
|
||||
case POLL_TYPE_IN:
|
||||
FD_SET(_sock, &rd);
|
||||
rdp = &rd;
|
||||
break;
|
||||
case POLL_TYPE_OUT:
|
||||
FD_SET(_sock, &wr);
|
||||
wrp = ≀
|
||||
break;
|
||||
case POLL_TYPE_IN_OUT:
|
||||
FD_SET(_sock, &rd);
|
||||
FD_SET(_sock, &wr);
|
||||
rdp = &rd;
|
||||
wrp = ≀
|
||||
}
|
||||
// WSAPoll is broken: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/.
|
||||
int ret = select(1, rdp, wrp, &ex, tp);
|
||||
|
||||
if (ret == SOCKET_ERROR) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
if (FD_ISSET(_sock, &ex)) {
|
||||
_get_socket_error();
|
||||
print_verbose("Exception when polling socket.");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (rdp && FD_ISSET(_sock, rdp)) {
|
||||
ready = true;
|
||||
}
|
||||
if (wrp && FD_ISSET(_sock, wrp)) {
|
||||
ready = true;
|
||||
}
|
||||
|
||||
return ready ? OK : ERR_BUSY;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::recv(uint8_t *p_buffer, int p_len, int &r_read) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
r_read = ::recv(_sock, (char *)p_buffer, p_len, 0);
|
||||
|
||||
if (r_read < 0) {
|
||||
NetError err = _get_socket_error();
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
struct sockaddr_storage from;
|
||||
socklen_t len = sizeof(struct sockaddr_storage);
|
||||
memset(&from, 0, len);
|
||||
|
||||
r_read = ::recvfrom(_sock, (char *)p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
|
||||
|
||||
if (r_read < 0) {
|
||||
NetError err = _get_socket_error();
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (from.ss_family == AF_INET) {
|
||||
struct sockaddr_in *sin_from = (struct sockaddr_in *)&from;
|
||||
r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr);
|
||||
r_port = ntohs(sin_from->sin_port);
|
||||
} else if (from.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from;
|
||||
r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr);
|
||||
r_port = ntohs(s6_from->sin6_port);
|
||||
} else {
|
||||
// Unsupported socket family, should never happen.
|
||||
ERR_FAIL_V(FAILED);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
int flags = 0;
|
||||
r_sent = ::send(_sock, (const char *)p_buffer, p_len, flags);
|
||||
|
||||
if (r_sent < 0) {
|
||||
NetError err = _get_socket_error();
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
|
||||
r_sent = ::sendto(_sock, (const char *)p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size);
|
||||
|
||||
if (r_sent < 0) {
|
||||
NetError err = _get_socket_error();
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::set_broadcasting_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
// IPv6 has no broadcast support.
|
||||
if (_ip_type == IP::TYPE_IPV6) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
int par = p_enabled ? 1 : 0;
|
||||
if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, (const char *)&par, sizeof(int)) != 0) {
|
||||
WARN_PRINT("Unable to change broadcast setting.");
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void NetSocketWinSock::set_blocking_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND(!is_open());
|
||||
|
||||
int ret = 0;
|
||||
unsigned long par = p_enabled ? 0 : 1;
|
||||
ret = ioctlsocket(_sock, FIONBIO, &par);
|
||||
if (ret != 0) {
|
||||
WARN_PRINT("Unable to change non-block mode.");
|
||||
}
|
||||
}
|
||||
|
||||
void NetSocketWinSock::set_ipv6_only_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND(!is_open());
|
||||
// This option is only available in IPv6 sockets.
|
||||
ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4);
|
||||
|
||||
int par = p_enabled ? 1 : 0;
|
||||
if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&par, sizeof(int)) != 0) {
|
||||
WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option.");
|
||||
}
|
||||
}
|
||||
|
||||
void NetSocketWinSock::set_tcp_no_delay_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND(!is_open());
|
||||
ERR_FAIL_COND(!_is_stream); // Not TCP.
|
||||
|
||||
int par = p_enabled ? 1 : 0;
|
||||
if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&par, sizeof(int)) < 0) {
|
||||
WARN_PRINT("Unable to set TCP no delay option.");
|
||||
}
|
||||
}
|
||||
|
||||
void NetSocketWinSock::set_reuse_address_enabled(bool p_enabled) {
|
||||
ERR_FAIL_COND(!is_open());
|
||||
|
||||
// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying...
|
||||
// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT
|
||||
}
|
||||
|
||||
bool NetSocketWinSock::is_open() const {
|
||||
return _sock != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int NetSocketWinSock::get_available_bytes() const {
|
||||
ERR_FAIL_COND_V(!is_open(), -1);
|
||||
|
||||
unsigned long len;
|
||||
int ret = ioctlsocket(_sock, FIONREAD, &len);
|
||||
if (ret == -1) {
|
||||
_get_socket_error();
|
||||
print_verbose("Error when checking available bytes on socket.");
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
|
||||
ERR_FAIL_COND_V(!is_open(), FAILED);
|
||||
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t len = sizeof(saddr);
|
||||
if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {
|
||||
_get_socket_error();
|
||||
print_verbose("Error when reading local socket address.");
|
||||
return FAILED;
|
||||
}
|
||||
_set_ip_port(&saddr, r_ip, r_port);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
Ref<NetSocket> out;
|
||||
ERR_FAIL_COND_V(!is_open(), out);
|
||||
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t size = sizeof(their_addr);
|
||||
SOCKET fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
|
||||
if (fd == INVALID_SOCKET) {
|
||||
_get_socket_error();
|
||||
print_verbose("Error when accepting socket connection.");
|
||||
return out;
|
||||
}
|
||||
|
||||
_set_ip_port(&their_addr, &r_ip, &r_port);
|
||||
|
||||
NetSocketWinSock *ns = memnew(NetSocketWinSock);
|
||||
ns->_set_socket(fd, _ip_type, _is_stream);
|
||||
ns->set_blocking_enabled(false);
|
||||
return Ref<NetSocket>(ns);
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
|
||||
return _change_multicast_group(p_multi_address, p_if_name, true);
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
|
||||
return _change_multicast_group(p_multi_address, p_if_name, false);
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
102
engine/drivers/windows/net_socket_winsock.h
Normal file
102
engine/drivers/windows/net_socket_winsock.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**************************************************************************/
|
||||
/* net_socket_winsock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NET_SOCKET_WINSOCK_H
|
||||
#define NET_SOCKET_WINSOCK_H
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "core/io/net_socket.h"
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
class NetSocketWinSock : public NetSocket {
|
||||
private:
|
||||
SOCKET _sock = INVALID_SOCKET;
|
||||
IP::Type _ip_type = IP::TYPE_NONE;
|
||||
bool _is_stream = false;
|
||||
|
||||
enum NetError {
|
||||
ERR_NET_WOULD_BLOCK,
|
||||
ERR_NET_IS_CONNECTED,
|
||||
ERR_NET_IN_PROGRESS,
|
||||
ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
|
||||
ERR_NET_UNAUTHORIZED,
|
||||
ERR_NET_BUFFER_TOO_SMALL,
|
||||
ERR_NET_OTHER,
|
||||
};
|
||||
|
||||
NetError _get_socket_error() const;
|
||||
void _set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream);
|
||||
_FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add);
|
||||
|
||||
protected:
|
||||
static NetSocket *_create_func();
|
||||
|
||||
bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const;
|
||||
|
||||
public:
|
||||
static void make_default();
|
||||
static void cleanup();
|
||||
static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
|
||||
static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
|
||||
|
||||
virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
|
||||
virtual void close() override;
|
||||
virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
|
||||
virtual Error listen(int p_max_pending) override;
|
||||
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
|
||||
virtual Error poll(PollType p_type, int timeout) const override;
|
||||
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
|
||||
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
|
||||
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
|
||||
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
|
||||
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
|
||||
|
||||
virtual bool is_open() const override;
|
||||
virtual int get_available_bytes() const override;
|
||||
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
|
||||
|
||||
virtual Error set_broadcasting_enabled(bool p_enabled) override;
|
||||
virtual void set_blocking_enabled(bool p_enabled) override;
|
||||
virtual void set_ipv6_only_enabled(bool p_enabled) override;
|
||||
virtual void set_tcp_no_delay_enabled(bool p_enabled) override;
|
||||
virtual void set_reuse_address_enabled(bool p_enabled) override;
|
||||
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
|
||||
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
|
||||
|
||||
NetSocketWinSock();
|
||||
~NetSocketWinSock() override;
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
|
||||
#endif // NET_SOCKET_WINSOCK_H
|
||||
59
engine/drivers/windows/thread_windows.cpp
Normal file
59
engine/drivers/windows/thread_windows.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* thread_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "thread_windows.h"
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
typedef HRESULT(WINAPI *SetThreadDescriptionPtr)(HANDLE p_thread, PCWSTR p_thread_description);
|
||||
SetThreadDescriptionPtr w10_SetThreadDescription = nullptr;
|
||||
|
||||
static Error set_name(const String &p_name) {
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
HRESULT res = E_FAIL;
|
||||
if (w10_SetThreadDescription) {
|
||||
res = w10_SetThreadDescription(hThread, (LPCWSTR)p_name.utf16().get_data());
|
||||
}
|
||||
return SUCCEEDED(res) ? OK : ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void init_thread_win() {
|
||||
w10_SetThreadDescription = (SetThreadDescriptionPtr)(void *)GetProcAddress(LoadLibraryW(L"kernel32.dll"), "SetThreadDescription");
|
||||
|
||||
Thread::_set_platform_functions({ set_name });
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
40
engine/drivers/windows/thread_windows.h
Normal file
40
engine/drivers/windows/thread_windows.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**************************************************************************/
|
||||
/* thread_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef THREAD_WINDOWS_H
|
||||
#define THREAD_WINDOWS_H
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
void init_thread_win();
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
|
||||
#endif // THREAD_WINDOWS_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue