Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileSystem & FIleSystemProtocol to allow custom path prefixes (custom://) #98544

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6b69dd0
Run autoload first pass before MainLoop initializes
TML233 Oct 7, 2024
a454a37
Add _add/remove_current_scene
TML233 Oct 7, 2024
a445e46
Merge branch 'singleton-in-mainloop-script'
TML233 Oct 8, 2024
a021fea
Merge branch 'custom-scene-manager'
TML233 Oct 8, 2024
2fe367b
Merge remote-tracking branch 'upstream/master'
TML233 Oct 22, 2024
f20a3aa
Add FileSystem & FileSystemProtocol
TML233 Oct 23, 2024
87e116b
Register FileSystem singleton
TML233 Oct 23, 2024
6234d39
Change some Windows filesystem
TML233 Oct 23, 2024
bfd3856
Rename file_system to filesystem
TML233 Oct 23, 2024
80138f4
Move all should-be static file methods to FileSystem
TML233 Oct 26, 2024
20402b1
Fix other references to FileAccess
TML233 Oct 26, 2024
17569fe
Merge branch 'master' into filesystem
TML233 Oct 26, 2024
5f9a659
Change path_override to path_disguise
TML233 Oct 26, 2024
b3b2029
Fix format
TML233 Oct 26, 2024
2892a0b
Add fiesystem to test setup
TML233 Oct 26, 2024
4a95175
Add Windows pipe protocol
TML233 Oct 26, 2024
5197f38
Merge branch 'godotengine:master' into master
TML233 Oct 27, 2024
2c258a8
Add thread safe locks
TML233 Oct 27, 2024
de817b8
Reserve gdscript protocol
TML233 Oct 28, 2024
6b63254
Merge branch 'master' into filesystem
TML233 Dec 16, 2024
9e791e7
Merge #98921 #99328
TML233 Dec 16, 2024
adf290b
Add globalize_path_or_fallback
TML233 Dec 16, 2024
62ab267
Add globalize_path & uid://
TML233 Jan 1, 2025
f4d4c82
Merge remote-tracking branch 'origin/master'
TML233 Jan 1, 2025
219339e
Merge branch 'master' into filesystem
TML233 Jan 1, 2025
a20c03c
Fix format
TML233 Jan 1, 2025
caa834e
Add Unix support
TML233 Jan 2, 2025
935211b
Remove redundant lines
TML233 Jan 2, 2025
a21c1ea
Revert "Run autoload first pass before MainLoop initializes"
TML233 Jan 8, 2025
784ef7c
Revert "Add _add/remove_current_scene"
TML233 Jan 8, 2025
9422d2a
Add Android support
TML233 Jan 8, 2025
303bdd7
Fix format
TML233 Jan 8, 2025
0e42e60
Make filesystem protocols respect underlying protocol name
TML233 Jan 8, 2025
4c79d2c
Merge remote-tracking branch 'upstream/master' into filesystem
TML233 Jan 29, 2025
52a2257
Merge upstream
TML233 Jan 29, 2025
b59d300
Fix upstream merge
TML233 Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 3 additions & 31 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
#include "core/io/filesystem.h"
#include "core/io/marshalls.h"
#include "core/io/resource_uid.h"
#include "core/object/script_language.h"
Expand Down Expand Up @@ -154,17 +155,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
}

// Check if we have a special path (like res://) or a protocol identifier.
int p = path.find("://");
bool found = false;
if (p > 0) {
found = true;
for (int i = 0; i < p; i++) {
if (!is_ascii_alphanumeric_char(path[i])) {
found = false;
break;
}
}
}
bool found = FileSystem::try_find_protocol_in_path(path, nullptr, nullptr);
if (found) {
return path;
}
Expand Down Expand Up @@ -256,26 +247,7 @@ void ProjectSettings::add_hidden_prefix(const String &p_prefix) {
}

String ProjectSettings::globalize_path(const String &p_path) const {
if (p_path.begins_with("res://")) {
if (!resource_path.is_empty()) {
return p_path.replace("res:/", resource_path);
}
return p_path.replace("res://", "");
} else if (p_path.begins_with("uid://")) {
const String path = ResourceUID::uid_to_path(p_path);
if (!resource_path.is_empty()) {
return path.replace("res:/", resource_path);
}
return path.replace("res://", "");
} else if (p_path.begins_with("user://")) {
String data_dir = OS::get_singleton()->get_user_data_dir();
if (!data_dir.is_empty()) {
return p_path.replace("user:/", data_dir);
}
return p_path.replace("user://", "");
}

return p_path;
return FileSystem::get_singleton()->globalize_path_or_fallback(p_path);
}

bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
Expand Down
193 changes: 20 additions & 173 deletions core/io/file_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,53 +36,18 @@
#include "core/io/file_access_compressed.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/file_access_pack.h"
#include "core/io/filesystem.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/os/time.h"

FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {};

FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;

bool FileAccess::backup_save = false;
thread_local Error FileAccess::last_file_open_error = OK;

Ref<FileAccess> FileAccess::create(AccessType p_access) {
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
ERR_FAIL_NULL_V(create_func[p_access], nullptr);

Ref<FileAccess> ret = create_func[p_access]();
ret->_set_access_type(p_access);
return ret;
}

bool FileAccess::exists(const String &p_name) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_name)) {
return true;
}

// Using file_exists because it's faster than trying to open the file.
Ref<FileAccess> ret = create_for_path(p_name);
return ret->file_exists(p_name);
}

void FileAccess::_set_access_type(AccessType p_access) {
_access_type = p_access;
}

Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
Ref<FileAccess> ret;
if (p_path.begins_with("res://") || p_path.begins_with("uid://")) {
ret = create(ACCESS_RESOURCES);
} else if (p_path.begins_with("user://")) {
ret = create(ACCESS_USERDATA);
} else if (p_path.begins_with("pipe://")) {
ret = create(ACCESS_PIPE);
} else {
ret = create(ACCESS_FILESYSTEM);
}

return ret;
return FileSystem::get_singleton()->file_exists(p_name);
}

Ref<FileAccess> FileAccess::create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep, Error *r_error) {
Expand Down Expand Up @@ -158,35 +123,8 @@ void FileAccess::_delete_temp() {
DirAccess::remove_absolute(_temp_path);
}

Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
return open_internal(p_path, p_mode_flags);
}

Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
//try packed data first

Ref<FileAccess> ret;
if (!(p_mode_flags & WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) {
ret = PackedData::get_singleton()->try_open_path(p_path);
if (ret.is_valid()) {
if (r_error) {
*r_error = OK;
}
return ret;
}
}

ret = create_for_path(p_path);
Error err = ret->open_internal(p_path, p_mode_flags);

if (r_error) {
*r_error = err;
}
if (err != OK) {
ret.unref();
}

return ret;
return FileSystem::get_singleton()->open_file(p_path, p_mode_flags, r_error);
}

Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags) {
Expand Down Expand Up @@ -248,59 +186,21 @@ Error FileAccess::get_open_error() {
return last_file_open_error;
}

FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) {
return create_func[p_access];
void FileAccess::set_path_disguise(const String &p_path) {
has_path_disguise = true;
path_disguise = p_path;
}

FileAccess::AccessType FileAccess::get_access_type() const {
return _access_type;
void FileAccess::clear_path_disguise() {
has_path_disguise = false;
path_disguise = String();
}

String FileAccess::fix_path(const String &p_path) const {
// Helper used by file accesses that use a single filesystem.

String r_path = p_path.replace("\\", "/");

switch (_access_type) {
case ACCESS_RESOURCES: {
if (ProjectSettings::get_singleton()) {
if (r_path.begins_with("uid://")) {
r_path = ResourceUID::uid_to_path(r_path);
}

if (r_path.begins_with("res://")) {
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
if (!resource_path.is_empty()) {
return r_path.replace("res:/", resource_path);
}
return r_path.replace("res://", "");
}
}

} break;
case ACCESS_USERDATA: {
if (r_path.begins_with("user://")) {
String data_dir = OS::get_singleton()->get_user_data_dir();
if (!data_dir.is_empty()) {
return r_path.replace("user:/", data_dir);
}
return r_path.replace("user://", "");
}

} break;
case ACCESS_PIPE: {
return r_path;
} break;
case ACCESS_FILESYSTEM: {
return r_path;
} break;
case ACCESS_MAX:
break; // Can't happen, but silences warning
String FileAccess::get_path() const {
if (has_path_disguise) {
return path_disguise;
}

return r_path;
return _get_path();
}

/* these are all implemented for ease of porting, then can later be optimized */
uint8_t FileAccess::get_8() const {
uint8_t data = 0;
Expand Down Expand Up @@ -622,84 +522,31 @@ bool FileAccess::store_double(double p_dest) {
}

uint64_t FileAccess::get_modified_time(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return 0;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));

uint64_t mt = fa->_get_modified_time(p_file);
return mt;
return FileSystem::get_singleton()->get_modified_time(p_file);
}

BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return 0;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));

return fa->_get_unix_permissions(p_file);
return FileSystem::get_singleton()->get_unix_permissions(p_file);
}

Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return ERR_UNAVAILABLE;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));

Error err = fa->_set_unix_permissions(p_file, p_permissions);
return err;
return FileSystem::get_singleton()->set_unix_permissions(p_file, p_permissions);
}

bool FileAccess::get_hidden_attribute(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return false;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));

return fa->_get_hidden_attribute(p_file);
return FileSystem::get_singleton()->get_hidden_attribute(p_file);
}

Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return ERR_UNAVAILABLE;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));

Error err = fa->_set_hidden_attribute(p_file, p_hidden);
return err;
return FileSystem::get_singleton()->set_hidden_attribute(p_file, p_hidden);
}

bool FileAccess::get_read_only_attribute(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return false;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));

return fa->_get_read_only_attribute(p_file);
return FileSystem::get_singleton()->get_read_only_attribute(p_file);
}

Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return ERR_UNAVAILABLE;
}

Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));

Error err = fa->_set_read_only_attribute(p_file, p_ro);
return err;
return FileSystem::get_singleton()->set_read_only_attribute(p_file, p_ro);
}

bool FileAccess::store_string(const String &p_string) {
Expand Down
Loading
Loading