From 85d3be80701798a6c69dad7b002ba5762df180d6 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:47:58 +0300 Subject: [PATCH] [FileAccess] Implement `get_size` and `get_access_time` methods. --- core/io/file_access.cpp | 27 ++++++- core/io/file_access.h | 4 ++ core/io/file_access_compressed.cpp | 16 +++++ core/io/file_access_compressed.h | 2 + core/io/file_access_encrypted.cpp | 22 +++++- core/io/file_access_encrypted.h | 2 + core/io/file_access_memory.h | 3 + core/io/file_access_pack.h | 17 +++++ core/io/file_access_zip.h | 4 +- doc/classes/FileAccess.xml | 14 ++++ drivers/unix/file_access_unix.cpp | 37 ++++++++-- drivers/unix/file_access_unix.h | 2 + drivers/unix/file_access_unix_pipe.h | 2 + drivers/windows/file_access_windows.cpp | 72 +++++++++++++++++++ drivers/windows/file_access_windows.h | 2 + drivers/windows/file_access_windows_pipe.h | 2 + platform/android/file_access_android.cpp | 4 ++ platform/android/file_access_android.h | 2 + .../file_access_filesystem_jandroid.cpp | 36 +++++++++- .../android/file_access_filesystem_jandroid.h | 4 ++ .../godotengine/godot/io/file/AssetData.kt | 2 + .../godotengine/godot/io/file/DataAccess.kt | 18 +++++ .../godot/io/file/FileAccessHandler.kt | 30 ++++++++ .../org/godotengine/godot/io/file/FileData.kt | 30 ++++++++ .../godot/io/file/MediaStoreData.kt | 14 ++++ 25 files changed, 359 insertions(+), 9 deletions(-) diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 9d8479b2dc2..51fd9e49006 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -629,8 +629,29 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { Ref 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 fa->_get_modified_time(p_file); +} + +uint64_t FileAccess::get_access_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 fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_access_time(p_file); +} + +int64_t FileAccess::get_size(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 PackedData::get_singleton()->get_size(p_file); + } + + Ref fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), -1, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_size(p_file); } BitField FileAccess::get_unix_permissions(const String &p_file) { @@ -963,6 +984,8 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists); ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_access_time", "file"), &FileAccess::get_access_time); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_size", "file"), &FileAccess::get_size); ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions); ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions); diff --git a/core/io/file_access.h b/core/io/file_access.h index 7c8bddf402d..0a7244dfe1e 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -104,6 +104,8 @@ protected: virtual String fix_path(const String &p_path) const; virtual Error open_internal(const String &p_path, int p_mode_flags) = 0; ///< open a file virtual uint64_t _get_modified_time(const String &p_file) = 0; + virtual uint64_t _get_access_time(const String &p_file) = 0; + virtual int64_t _get_size(const String &p_file) = 0; virtual void _set_access_type(AccessType p_access); static FileCloseFailNotify close_fail_notify; @@ -238,6 +240,8 @@ public: static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); + static uint64_t get_access_time(const String &p_file); + static int64_t get_size(const String &p_file); static BitField get_unix_permissions(const String &p_file); static Error set_unix_permissions(const String &p_file, BitField p_permissions); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index b7300dedf16..5fe30ff42c9 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -332,6 +332,22 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { } } +uint64_t FileAccessCompressed::_get_access_time(const String &p_file) { + if (f.is_valid()) { + return f->get_access_time(p_file); + } else { + return 0; + } +} + +int64_t FileAccessCompressed::_get_size(const String &p_file) { + if (f.is_valid()) { + return f->get_size(p_file); + } else { + return -1; + } +} + BitField FileAccessCompressed::_get_unix_permissions(const String &p_file) { if (f.is_valid()) { return f->_get_unix_permissions(p_file); diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 6bc8870f857..91d6f54e8ee 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -93,6 +93,8 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint64_t _get_access_time(const String &p_file) override; + virtual int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override; virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index f2749157b3d..a2fef2a5ada 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -265,7 +265,27 @@ bool FileAccessEncrypted::file_exists(const String &p_name) { } uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { - return 0; + if (file.is_valid()) { + return file->get_modified_time(p_file); + } else { + return 0; + } +} + +uint64_t FileAccessEncrypted::_get_access_time(const String &p_file) { + if (file.is_valid()) { + return file->get_access_time(p_file); + } else { + return 0; + } +} + +int64_t FileAccessEncrypted::_get_size(const String &p_file) { + if (file.is_valid()) { + return file->get_size(p_file); + } else { + return -1; + } } BitField FileAccessEncrypted::_get_unix_permissions(const String &p_file) { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 75844b83aac..873572b2c96 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -86,6 +86,8 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint64_t _get_access_time(const String &p_file) override; + virtual int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override; virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index e9e6ab39ce2..d9d76a6be37 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -65,6 +65,9 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override { return -1; } + virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return FAILED; } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index bfd2083872b..4d4fd505c3c 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -126,6 +126,8 @@ public: _FORCE_INLINE_ Ref try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); + _FORCE_INLINE_ int64_t get_size(const String &p_path); + _FORCE_INLINE_ Ref try_open_directory(const String &p_path); _FORCE_INLINE_ bool has_directory(const String &p_path); @@ -164,6 +166,8 @@ class FileAccessPack : public FileAccess { Ref f; virtual Error open_internal(const String &p_path, int p_mode_flags) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override { return -1; } virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return FAILED; } @@ -199,6 +203,19 @@ public: FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file); }; +int64_t PackedData::get_size(const String &p_path) { + String simplified_path = p_path.simplify_path(); + PathMD5 pmd5(simplified_path.md5_buffer()); + HashMap::Iterator E = files.find(pmd5); + if (!E) { + return -1; // File not found. + } + if (E->value.offset == 0) { + return -1; // File was erased. + } + return E->value.size; +} + Ref PackedData::try_open_path(const String &p_path) { String simplified_path = p_path.simplify_path().trim_prefix("res://"); PathMD5 pmd5(simplified_path.md5_buffer()); diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 295d391cb82..bc892047532 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -101,7 +101,9 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists - virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo + virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override { return -1; } virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return FAILED; } diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 03a1146a913..f94645b5e09 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -123,6 +123,13 @@ Returns the next 64 bits from the file as an integer. See [method store_64] for details on what values can be stored and retrieved this way. + + + + + Returns the last time the [param file] was accessed in Unix timestamp format, or [code]0[/code] on error. This Unix timestamp can be converted to another format using the [Time] singleton. + + @@ -280,6 +287,13 @@ Returns an SHA-256 [String] representing the file at the given path or an empty [String] on failure. + + + + + Returns file size in bytes, or [code]-1[/code] on error. + + diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 9999a319974..f3316076051 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -336,16 +336,19 @@ bool FileAccessUnix::file_exists(const String &p_path) { uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - struct stat status = {}; - int err = stat(file.utf8().get_data(), &status); + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); if (!err) { - uint64_t modified_time = status.st_mtime; + uint64_t modified_time = 0; + if ((st.st_mode & S_IFMT) == S_IFLNK || (st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFDIR) == S_IFDIR) { + modified_time = st.st_mtime; + } #ifdef ANDROID_ENABLED // Workaround for GH-101007 //FIXME: After saving, all timestamps (st_mtime, st_ctime, st_atime) are set to the same value. // After exporting or after some time, only 'modified_time' resets to a past timestamp. - uint64_t created_time = status.st_ctime; + uint64_t created_time = st.st_ctime; if (modified_time < created_time) { modified_time = created_time; } @@ -356,6 +359,32 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { } } +uint64_t FileAccessUnix::_get_access_time(const String &p_file) { + String file = fix_path(p_file); + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + + if (!err) { + if ((st.st_mode & S_IFMT) == S_IFLNK || (st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFDIR) == S_IFDIR) { + return st.st_atime; + } + } + ERR_FAIL_V_MSG(0, "Failed to get access time for: " + p_file + ""); +} + +int64_t FileAccessUnix::_get_size(const String &p_file) { + String file = fix_path(p_file); + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + + if (!err) { + if ((st.st_mode & S_IFMT) == S_IFLNK || (st.st_mode & S_IFMT) == S_IFREG) { + return st.st_size; + } + } + ERR_FAIL_V_MSG(-1, "Failed to get size for: " + p_file + ""); +} + BitField FileAccessUnix::_get_unix_permissions(const String &p_file) { String file = fix_path(p_file); struct stat status = {}; diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 1273909ee5c..326137e71b6 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -80,6 +80,8 @@ public: virtual bool file_exists(const String &p_path) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint64_t _get_access_time(const String &p_file) override; + virtual int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override; virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override; diff --git a/drivers/unix/file_access_unix_pipe.h b/drivers/unix/file_access_unix_pipe.h index 2ab0f57239f..bf3f6bc851c 100644 --- a/drivers/unix/file_access_unix_pipe.h +++ b/drivers/unix/file_access_unix_pipe.h @@ -75,6 +75,8 @@ public: virtual bool file_exists(const String &p_path) override { return false; } virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override { return -1; } virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return ERR_UNAVAILABLE; } diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 6835b510df7..c261b0d54ec 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -453,6 +453,78 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { return 0; } +uint64_t FileAccessWindows::_get_access_time(const String &p_file) { + if (is_path_invalid(p_file)) { + return 0; + } + + String file = fix_path(p_file); + if (file.ends_with("\\") && file != "\\") { + file = file.substr(0, file.length() - 1); + } + + HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + if (handle != INVALID_HANDLE_VALUE) { + FILETIME ft_create, ft_access; + + bool status = GetFileTime(handle, &ft_create, &ft_access, nullptr); + + CloseHandle(handle); + + if (status) { + uint64_t ret = 0; + + // If access time is invalid, fallback to creation time. + if (ft_access.dwHighDateTime == 0 && ft_access.dwLowDateTime == 0) { + ret = ft_create.dwHighDateTime; + ret <<= 32; + ret |= ft_create.dwLowDateTime; + } else { + ret = ft_access.dwHighDateTime; + ret <<= 32; + ret |= ft_access.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; + } + } + } + + ERR_FAIL_V_MSG(0, "Failed to get access time for: " + p_file + ""); +} + +int64_t FileAccessWindows::_get_size(const String &p_file) { + if (is_path_invalid(p_file)) { + return 0; + } + + String file = fix_path(p_file); + if (file.ends_with("\\") && file != "\\") { + file = file.substr(0, file.length() - 1); + } + + DWORD file_attr = GetFileAttributesW((LPCWSTR)(file.utf16().get_data())); + HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + if (handle != INVALID_HANDLE_VALUE && !(file_attr & FILE_ATTRIBUTE_DIRECTORY)) { + LARGE_INTEGER fsize; + + bool status = GetFileSizeEx(handle, &fsize); + + CloseHandle(handle); + + if (status) { + return (int64_t)fsize.QuadPart; + } + } + ERR_FAIL_V_MSG(-1, "Failed to get size for: " + p_file + ""); +} + BitField FileAccessWindows::_get_unix_permissions(const String &p_file) { return 0; } diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 036d891e39e..8f6e83cb28c 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -79,6 +79,8 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists uint64_t _get_modified_time(const String &p_file) override; + uint64_t _get_access_time(const String &p_file) override; + int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override; virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override; diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h index f851299a3b5..c554b7efe1f 100644 --- a/drivers/windows/file_access_windows_pipe.h +++ b/drivers/windows/file_access_windows_pipe.h @@ -74,6 +74,8 @@ public: virtual bool file_exists(const String &p_name) override { return false; } uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override { return -1; } virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return ERR_UNAVAILABLE; } diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index 193ceed9ead..c57206ce8c5 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -132,6 +132,10 @@ uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const return r; } +int64_t FileAccessAndroid::_get_size(const String &p_file) { + return AAsset_getLength64(asset); +} + Error FileAccessAndroid::get_error() const { return eof ? ERR_FILE_EOF : OK; // not sure what else it may happen } diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index 2ba441b3a10..05cc42f6220 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -77,6 +77,8 @@ public: virtual bool file_exists(const String &p_path) override; // return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } + virtual uint64_t _get_access_time(const String &p_file) override { return 0; } + virtual int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return ERR_UNAVAILABLE; } diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp index 5a1d85a6691..a047fb6a8dc 100644 --- a/platform/android/file_access_filesystem_jandroid.cpp +++ b/platform/android/file_access_filesystem_jandroid.cpp @@ -53,7 +53,9 @@ jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr; jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr; jmethodID FileAccessFilesystemJAndroid::_file_exists = nullptr; jmethodID FileAccessFilesystemJAndroid::_file_last_modified = nullptr; +jmethodID FileAccessFilesystemJAndroid::_file_last_accessed = nullptr; jmethodID FileAccessFilesystemJAndroid::_file_resize = nullptr; +jmethodID FileAccessFilesystemJAndroid::_file_size = nullptr; String FileAccessFilesystemJAndroid::get_path() const { return path_src; @@ -300,7 +302,7 @@ bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) { uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) { if (_file_last_modified) { JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL_V(env, false); + ERR_FAIL_NULL_V(env, 0); String path = fix_path(p_file).simplify_path(); jstring js = env->NewStringUTF(path.utf8().get_data()); @@ -312,6 +314,36 @@ uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) } } +uint64_t FileAccessFilesystemJAndroid::_get_access_time(const String &p_file) { + if (_file_last_accessed) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, 0); + + String path = fix_path(p_file).simplify_path(); + jstring js = env->NewStringUTF(path.utf8().get_data()); + uint64_t result = env->CallLongMethod(file_access_handler, _file_last_accessed, js); + env->DeleteLocalRef(js); + return result; + } else { + return 0; + } +} + +int64_t FileAccessFilesystemJAndroid::_get_size(const String &p_file) { + if (_file_size) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, -1); + + String path = fix_path(p_file).simplify_path(); + jstring js = env->NewStringUTF(path.utf8().get_data()); + int64_t result = env->CallLongMethod(file_access_handler, _file_size, js); + env->DeleteLocalRef(js); + return result; + } else { + return -1; + } +} + void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) { JNIEnv *env = get_jni_env(); file_access_handler = env->NewGlobalRef(p_file_access_handler); @@ -332,7 +364,9 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) { _file_flush = env->GetMethodID(cls, "fileFlush", "(I)V"); _file_exists = env->GetMethodID(cls, "fileExists", "(Ljava/lang/String;)Z"); _file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J"); + _file_last_accessed = env->GetMethodID(cls, "fileLastAccessed", "(Ljava/lang/String;)J"); _file_resize = env->GetMethodID(cls, "fileResize", "(IJ)I"); + _file_size = env->GetMethodID(cls, "fileSize", "(Ljava/lang/String;)J"); } void FileAccessFilesystemJAndroid::terminate() { diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h index aabfcacde22..8258f2e553c 100644 --- a/platform/android/file_access_filesystem_jandroid.h +++ b/platform/android/file_access_filesystem_jandroid.h @@ -51,7 +51,9 @@ class FileAccessFilesystemJAndroid : public FileAccess { static jmethodID _file_close; static jmethodID _file_exists; static jmethodID _file_last_modified; + static jmethodID _file_last_accessed; static jmethodID _file_resize; + static jmethodID _file_size; int id; String absolute_path; @@ -91,6 +93,8 @@ public: static void terminate(); virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint64_t _get_access_time(const String &p_file) override; + virtual int64_t _get_size(const String &p_file) override; virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override { return ERR_UNAVAILABLE; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/AssetData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/AssetData.kt index d6994eb3498..d5e696c5e41 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/AssetData.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/AssetData.kt @@ -66,6 +66,8 @@ internal class AssetData(context: Context, private val filePath: String, accessF } fun fileLastModified(path: String) = 0L + fun fileLastAccessed(path: String) = 0L + fun fileSize(path: String) = -1L fun delete(path: String) = false diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt index 319ec3ec48d..0a0d8d77858 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt @@ -132,6 +132,24 @@ internal abstract class DataAccess { } } + fun fileLastAccessed(storageScope: StorageScope, context: Context, path: String): Long { + return when(storageScope) { + StorageScope.APP -> FileData.fileLastAccessed(path) + StorageScope.ASSETS -> AssetData.fileLastAccessed(path) + StorageScope.SHARED -> MediaStoreData.fileLastAccessed(context, path) + StorageScope.UNKNOWN -> 0L + } + } + + fun fileSize(storageScope: StorageScope, context: Context, path: String): Long { + return when(storageScope) { + StorageScope.APP -> FileData.fileSize(path) + StorageScope.ASSETS -> AssetData.fileSize(path) + StorageScope.SHARED -> MediaStoreData.fileSize(context, path) + StorageScope.UNKNOWN -> -1L + } + } + fun removeFile(storageScope: StorageScope, context: Context, path: String): Boolean { return when(storageScope) { StorageScope.APP -> FileData.delete(path) diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt index 21aee3395eb..1e2f3ca06c1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt @@ -228,6 +228,21 @@ class FileAccessHandler(val context: Context) { } } + fun fileLastAccessed(filepath: String?): Long { + val storageScope = storageScopeIdentifier.identifyStorageScope(filepath) + if (storageScope == StorageScope.UNKNOWN) { + return 0L + } + + return try { + filepath?.let { + DataAccess.fileLastAccessed(storageScope, context, it) + } ?: 0L + } catch (e: SecurityException) { + 0L + } + } + fun fileResize(fileId: Int, length: Long): Int { if (!hasFileId(fileId)) { return Error.FAILED.toNativeValue() @@ -236,6 +251,21 @@ class FileAccessHandler(val context: Context) { return files[fileId].resize(length).toNativeValue() } + fun fileSize(filepath: String?): Long { + val storageScope = storageScopeIdentifier.identifyStorageScope(filepath) + if (storageScope == StorageScope.UNKNOWN) { + return -1L + } + + return try { + filepath?.let { + DataAccess.fileSize(storageScope, context, it) + } ?: -1L + } catch (e: SecurityException) { + -1L + } + } + fun fileGetPosition(fileId: Int): Long { if (!hasFileId(fileId)) { return 0L diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt index 873daada3cf..42e59ca8c55 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt @@ -30,10 +30,16 @@ package org.godotengine.godot.io.file +import android.os.* import java.io.File import java.io.FileOutputStream import java.io.RandomAccessFile import java.nio.channels.FileChannel +import java.nio.file.attribute.BasicFileAttributes +import java.nio.file.Files +import java.nio.file.FileSystems +import java.nio.file.Path +import java.util.concurrent.TimeUnit /** * Implementation of [DataAccess] which handles regular (not scoped) file access and interactions. @@ -59,6 +65,30 @@ internal class FileData(filePath: String, accessFlag: FileAccessFlags) : DataAcc } } + fun fileLastAccessed(filepath: String): Long { + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Files.readAttributes(FileSystems.getDefault().getPath(filepath), BasicFileAttributes::class.java).lastAccessTime().to(TimeUnit.SECONDS) + } else { + 0L + } + } catch (e: SecurityException) { + 0L + } + } + + fun fileSize(filepath: String): Long { + return try { + if (File(filepath).isFile) { + File(filepath).length() + } else { + -1L + } + } catch (e: SecurityException) { + -1L + } + } + fun delete(filepath: String): Boolean { return try { File(filepath).delete() diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt index 46bd465e90e..e78b29472fe 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt @@ -212,6 +212,20 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi return dataItem.dateModified.toLong() / 1000L } + fun fileLastAccessed(@Suppress("UNUSED_PARAMETER") context: Context, @Suppress("UNUSED_PARAMETER") path: String): Long { + return 0L + } + + fun fileSize(context: Context, path: String): Long { + val result = queryByPath(context, path) + if (result.isEmpty()) { + return -1L + } + + val dataItem = result[0] + return dataItem.size.toLong() + } + fun rename(context: Context, from: String, to: String): Boolean { // Ensure the source exists. val sources = queryByPath(context, from)