summaryrefslogtreecommitdiff
path: root/core/os
diff options
context:
space:
mode:
Diffstat (limited to 'core/os')
-rw-r--r--core/os/copymem.h50
-rw-r--r--core/os/dir_access.cpp416
-rw-r--r--core/os/dir_access.h141
-rw-r--r--core/os/file_access.cpp673
-rw-r--r--core/os/file_access.h198
-rw-r--r--core/os/keyboard.cpp6
-rw-r--r--core/os/keyboard.h4
-rw-r--r--core/os/main_loop.h2
-rw-r--r--core/os/memory.cpp1
-rw-r--r--core/os/memory.h2
-rw-r--r--core/os/midi_driver.cpp10
-rw-r--r--core/os/os.cpp70
-rw-r--r--core/os/os.h43
-rw-r--r--core/os/pool_allocator.cpp5
-rw-r--r--core/os/thread.cpp28
-rw-r--r--core/os/thread.h6
-rw-r--r--core/os/time.cpp433
-rw-r--r--core/os/time.h109
18 files changed, 640 insertions, 1557 deletions
diff --git a/core/os/copymem.h b/core/os/copymem.h
deleted file mode 100644
index 6fd559356c..0000000000
--- a/core/os/copymem.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*************************************************************************/
-/* copymem.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef COPYMEM_H
-#define COPYMEM_H
-
-#include "core/typedefs.h"
-
-#ifdef PLATFORM_COPYMEM
-
-#include "platform_copymem.h" // included from platform/<current_platform>/platform_copymem.h"
-
-#else
-
-#include <string.h>
-
-#define copymem(to, from, count) memcpy(to, from, count)
-#define zeromem(to, count) memset(to, 0, count)
-#define movemem(to, from, count) memmove(to, from, count)
-
-#endif
-
-#endif // COPYMEM_H
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
deleted file mode 100644
index b7c3a17ba9..0000000000
--- a/core/os/dir_access.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/*************************************************************************/
-/* dir_access.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "dir_access.h"
-
-#include "core/config/project_settings.h"
-#include "core/os/file_access.h"
-#include "core/os/memory.h"
-#include "core/os/os.h"
-
-String DirAccess::_get_root_path() const {
- switch (_access_type) {
- case ACCESS_RESOURCES:
- return ProjectSettings::get_singleton()->get_resource_path();
- case ACCESS_USERDATA:
- return OS::get_singleton()->get_user_data_dir();
- default:
- return "";
- }
-}
-
-String DirAccess::_get_root_string() const {
- switch (_access_type) {
- case ACCESS_RESOURCES:
- return "res://";
- case ACCESS_USERDATA:
- return "user://";
- default:
- return "";
- }
-}
-
-int DirAccess::get_current_drive() {
- String path = get_current_dir().to_lower();
- for (int i = 0; i < get_drive_count(); i++) {
- String d = get_drive(i).to_lower();
- if (path.begins_with(d)) {
- return i;
- }
- }
-
- return 0;
-}
-
-bool DirAccess::drives_are_shortcuts() {
- return false;
-}
-
-static Error _erase_recursive(DirAccess *da) {
- List<String> dirs;
- List<String> files;
-
- da->list_dir_begin();
- String n = da->get_next();
- while (n != String()) {
- if (n != "." && n != "..") {
- if (da->current_is_dir()) {
- dirs.push_back(n);
- } else {
- files.push_back(n);
- }
- }
-
- n = da->get_next();
- }
-
- da->list_dir_end();
-
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- Error err = da->change_dir(E->get());
- if (err == OK) {
- err = _erase_recursive(da);
- if (err) {
- da->change_dir("..");
- return err;
- }
- err = da->change_dir("..");
- if (err) {
- return err;
- }
- err = da->remove(da->get_current_dir().plus_file(E->get()));
- if (err) {
- return err;
- }
- } else {
- return err;
- }
- }
-
- for (List<String>::Element *E = files.front(); E; E = E->next()) {
- Error err = da->remove(da->get_current_dir().plus_file(E->get()));
- if (err) {
- return err;
- }
- }
-
- return OK;
-}
-
-Error DirAccess::erase_contents_recursive() {
- return _erase_recursive(this);
-}
-
-Error DirAccess::make_dir_recursive(String p_dir) {
- if (p_dir.length() < 1) {
- return OK;
- }
-
- String full_dir;
-
- if (p_dir.is_rel_path()) {
- //append current
- full_dir = get_current_dir().plus_file(p_dir);
-
- } else {
- full_dir = p_dir;
- }
-
- full_dir = full_dir.replace("\\", "/");
-
- //int slices = full_dir.get_slice_count("/");
-
- String base;
-
- if (full_dir.begins_with("res://")) {
- base = "res://";
- } else if (full_dir.begins_with("user://")) {
- base = "user://";
- } else if (full_dir.begins_with("/")) {
- base = "/";
- } else if (full_dir.find(":/") != -1) {
- base = full_dir.substr(0, full_dir.find(":/") + 2);
- } else {
- ERR_FAIL_V(ERR_INVALID_PARAMETER);
- }
-
- full_dir = full_dir.replace_first(base, "").simplify_path();
-
- Vector<String> subdirs = full_dir.split("/");
-
- String curpath = base;
- for (int i = 0; i < subdirs.size(); i++) {
- curpath = curpath.plus_file(subdirs[i]);
- Error err = make_dir(curpath);
- if (err != OK && err != ERR_ALREADY_EXISTS) {
- ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
- }
- }
-
- return OK;
-}
-
-String DirAccess::fix_path(String p_path) const {
- switch (_access_type) {
- case ACCESS_RESOURCES: {
- if (ProjectSettings::get_singleton()) {
- if (p_path.begins_with("res://")) {
- String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- if (resource_path != "") {
- return p_path.replace_first("res:/", resource_path);
- }
- return p_path.replace_first("res://", "");
- }
- }
-
- } break;
- case ACCESS_USERDATA: {
- if (p_path.begins_with("user://")) {
- String data_dir = OS::get_singleton()->get_user_data_dir();
- if (data_dir != "") {
- return p_path.replace_first("user:/", data_dir);
- }
- return p_path.replace_first("user://", "");
- }
-
- } break;
- case ACCESS_FILESYSTEM: {
- return p_path;
- } break;
- case ACCESS_MAX:
- break; // Can't happen, but silences warning
- }
-
- return p_path;
-}
-
-DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr };
-
-DirAccess *DirAccess::create_for_path(const String &p_path) {
- DirAccess *da = nullptr;
- if (p_path.begins_with("res://")) {
- da = create(ACCESS_RESOURCES);
- } else if (p_path.begins_with("user://")) {
- da = create(ACCESS_USERDATA);
- } else {
- da = create(ACCESS_FILESYSTEM);
- }
-
- return da;
-}
-
-DirAccess *DirAccess::open(const String &p_path, Error *r_error) {
- DirAccess *da = create_for_path(p_path);
-
- ERR_FAIL_COND_V_MSG(!da, nullptr, "Cannot create DirAccess for path '" + p_path + "'.");
- Error err = da->change_dir(p_path);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- memdelete(da);
- return nullptr;
- }
-
- return da;
-}
-
-DirAccess *DirAccess::create(AccessType p_access) {
- DirAccess *da = create_func[p_access] ? create_func[p_access]() : nullptr;
- if (da) {
- da->_access_type = p_access;
- }
-
- return da;
-}
-
-String DirAccess::get_full_path(const String &p_path, AccessType p_access) {
- DirAccess *d = DirAccess::create(p_access);
- if (!d) {
- return p_path;
- }
-
- d->change_dir(p_path);
- String full = d->get_current_dir();
- memdelete(d);
- return full;
-}
-
-Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
- //printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
- Error err;
- FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
-
- if (err) {
- ERR_PRINT("Failed to open " + p_from);
- return err;
- }
-
- FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
- if (err) {
- fsrc->close();
- memdelete(fsrc);
- ERR_PRINT("Failed to open " + p_to);
- return err;
- }
-
- fsrc->seek_end(0);
- int size = fsrc->get_position();
- fsrc->seek(0);
- err = OK;
- while (size--) {
- if (fsrc->get_error() != OK) {
- err = fsrc->get_error();
- break;
- }
- if (fdst->get_error() != OK) {
- err = fdst->get_error();
- break;
- }
-
- fdst->store_8(fsrc->get_8());
- }
-
- if (err == OK && p_chmod_flags != -1) {
- fdst->close();
- err = FileAccess::set_unix_permissions(p_to, p_chmod_flags);
- // If running on a platform with no chmod support (i.e., Windows), don't fail
- if (err == ERR_UNAVAILABLE) {
- err = OK;
- }
- }
-
- memdelete(fsrc);
- memdelete(fdst);
-
- return err;
-}
-
-// Changes dir for the current scope, returning back to the original dir
-// when scope exits
-class DirChanger {
- DirAccess *da;
- String original_dir;
-
-public:
- DirChanger(DirAccess *p_da, String p_dir) :
- da(p_da),
- original_dir(p_da->get_current_dir()) {
- p_da->change_dir(p_dir);
- }
-
- ~DirChanger() {
- da->change_dir(original_dir);
- }
-};
-
-Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags) {
- List<String> dirs;
-
- String curdir = get_current_dir();
- list_dir_begin();
- String n = get_next();
- while (n != String()) {
- if (n != "." && n != "..") {
- if (current_is_dir()) {
- dirs.push_back(n);
- } else {
- const String &rel_path = n;
- if (!n.is_rel_path()) {
- list_dir_end();
- return ERR_BUG;
- }
- Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags);
- if (err) {
- list_dir_end();
- return err;
- }
- }
- }
-
- n = get_next();
- }
-
- list_dir_end();
-
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- String rel_path = E->get();
- String target_dir = p_to + rel_path;
- if (!p_target_da->dir_exists(target_dir)) {
- Error err = p_target_da->make_dir(target_dir);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
- }
-
- Error err = change_dir(E->get());
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");
-
- err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags);
- if (err) {
- change_dir("..");
- ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
- }
- err = change_dir("..");
- ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back.");
- }
-
- return OK;
-}
-
-Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) {
- ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");
-
- DirAccess *target_da = DirAccess::create_for_path(p_to);
- ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");
-
- if (!target_da->dir_exists(p_to)) {
- Error err = target_da->make_dir_recursive(p_to);
- if (err) {
- memdelete(target_da);
- }
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
- }
-
- if (!p_to.ends_with("/")) {
- p_to = p_to + "/";
- }
-
- DirChanger dir_changer(this, p_from);
- Error err = _copy_dir(target_da, p_to, p_chmod_flags);
- memdelete(target_da);
-
- return err;
-}
-
-bool DirAccess::exists(String p_dir) {
- DirAccess *da = DirAccess::create_for_path(p_dir);
- bool valid = da->change_dir(p_dir) == OK;
- memdelete(da);
- return valid;
-}
diff --git a/core/os/dir_access.h b/core/os/dir_access.h
deleted file mode 100644
index c49c4cc4b8..0000000000
--- a/core/os/dir_access.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*************************************************************************/
-/* dir_access.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef DIR_ACCESS_H
-#define DIR_ACCESS_H
-
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-
-//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
-class DirAccess {
-public:
- enum AccessType {
- ACCESS_RESOURCES,
- ACCESS_USERDATA,
- ACCESS_FILESYSTEM,
- ACCESS_MAX
- };
-
- typedef DirAccess *(*CreateFunc)();
-
-private:
- AccessType _access_type = ACCESS_FILESYSTEM;
- static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object
-
- Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags);
-
-protected:
- String _get_root_path() const;
- String _get_root_string() const;
-
- String fix_path(String p_path) const;
-
- template <class T>
- static DirAccess *_create_builtin() {
- return memnew(T);
- }
-
-public:
- virtual Error list_dir_begin() = 0; ///< This starts dir listing
- virtual String get_next() = 0;
- virtual bool current_is_dir() const = 0;
- virtual bool current_is_hidden() const = 0;
-
- virtual void list_dir_end() = 0; ///<
-
- virtual int get_drive_count() = 0;
- virtual String get_drive(int p_drive) = 0;
- virtual int get_current_drive();
- virtual bool drives_are_shortcuts();
-
- virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success
- virtual String get_current_dir(bool p_include_drive = true) = 0; ///< return current dir location
- virtual Error make_dir(String p_dir) = 0;
- virtual Error make_dir_recursive(String p_dir);
- virtual Error erase_contents_recursive(); //super dangerous, use with care!
-
- virtual bool file_exists(String p_file) = 0;
- virtual bool dir_exists(String p_dir) = 0;
- static bool exists(String p_dir);
- virtual size_t get_space_left() = 0;
-
- Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1);
- virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1);
- virtual Error rename(String p_from, String p_to) = 0;
- virtual Error remove(String p_name) = 0;
-
- // Meant for editor code when we want to quickly remove a file without custom
- // handling (e.g. removing a cache file).
- static void remove_file_or_error(String p_path) {
- DirAccess *da = create(ACCESS_FILESYSTEM);
- if (da->file_exists(p_path)) {
- if (da->remove(p_path) != OK) {
- ERR_FAIL_MSG("Cannot remove file or directory: " + p_path);
- }
- }
- memdelete(da);
- }
-
- virtual String get_filesystem_type() const = 0;
- static String get_full_path(const String &p_path, AccessType p_access);
- static DirAccess *create_for_path(const String &p_path);
-
- static DirAccess *create(AccessType p_access);
-
- template <class T>
- static void make_default(AccessType p_access) {
- create_func[p_access] = _create_builtin<T>;
- }
-
- static DirAccess *open(const String &p_path, Error *r_error = nullptr);
-
- DirAccess() {}
- virtual ~DirAccess() {}
-};
-
-struct DirAccessRef {
- _FORCE_INLINE_ DirAccess *operator->() {
- return f;
- }
-
- operator bool() const { return f != nullptr; }
-
- DirAccess *f;
-
- DirAccessRef(DirAccess *fa) { f = fa; }
- ~DirAccessRef() {
- if (f) {
- memdelete(f);
- }
- }
-};
-
-#endif // DIR_ACCESS_H
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
deleted file mode 100644
index 5a3df88619..0000000000
--- a/core/os/file_access.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/*************************************************************************/
-/* file_access.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "file_access.h"
-
-#include "core/config/project_settings.h"
-#include "core/crypto/crypto_core.h"
-#include "core/io/file_access_pack.h"
-#include "core/io/marshalls.h"
-#include "core/os/os.h"
-
-FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr };
-
-FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;
-
-bool FileAccess::backup_save = false;
-
-FileAccess *FileAccess::create(AccessType p_access) {
- ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
-
- 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;
- }
-
- FileAccess *f = open(p_name, READ);
- if (!f) {
- return false;
- }
- memdelete(f);
- return true;
-}
-
-void FileAccess::_set_access_type(AccessType p_access) {
- _access_type = p_access;
-}
-
-FileAccess *FileAccess::create_for_path(const String &p_path) {
- FileAccess *ret = nullptr;
- if (p_path.begins_with("res://")) {
- ret = create(ACCESS_RESOURCES);
- } else if (p_path.begins_with("user://")) {
- ret = create(ACCESS_USERDATA);
-
- } else {
- ret = create(ACCESS_FILESYSTEM);
- }
-
- return ret;
-}
-
-Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
- return _open(p_path, p_mode_flags);
-}
-
-FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
- //try packed data first
-
- FileAccess *ret = nullptr;
- if (!(p_mode_flags & WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) {
- ret = PackedData::get_singleton()->try_open_path(p_path);
- if (ret) {
- if (r_error) {
- *r_error = OK;
- }
- return ret;
- }
- }
-
- ret = create_for_path(p_path);
- Error err = ret->_open(p_path, p_mode_flags);
-
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- memdelete(ret);
- ret = nullptr;
- }
-
- return ret;
-}
-
-FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) {
- return create_func[p_access];
-}
-
-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("res://")) {
- String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- if (resource_path != "") {
- 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 != "") {
- return r_path.replace("user:/", data_dir);
- }
- return r_path.replace("user://", "");
- }
-
- } break;
- case ACCESS_FILESYSTEM: {
- return r_path;
- } break;
- case ACCESS_MAX:
- break; // Can't happen, but silences warning
- }
-
- return r_path;
-}
-
-/* these are all implemented for ease of porting, then can later be optimized */
-
-uint16_t FileAccess::get_16() const {
- uint16_t res;
- uint8_t a, b;
-
- a = get_8();
- b = get_8();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 8;
- res |= a;
-
- return res;
-}
-
-uint32_t FileAccess::get_32() const {
- uint32_t res;
- uint16_t a, b;
-
- a = get_16();
- b = get_16();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 16;
- res |= a;
-
- return res;
-}
-
-uint64_t FileAccess::get_64() const {
- uint64_t res;
- uint32_t a, b;
-
- a = get_32();
- b = get_32();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 32;
- res |= a;
-
- return res;
-}
-
-float FileAccess::get_float() const {
- MarshallFloat m;
- m.i = get_32();
- return m.f;
-}
-
-real_t FileAccess::get_real() const {
- if (real_is_double) {
- return get_double();
- } else {
- return get_float();
- }
-}
-
-double FileAccess::get_double() const {
- MarshallDouble m;
- m.l = get_64();
- return m.d;
-}
-
-String FileAccess::get_token() const {
- CharString token;
-
- char32_t c = get_8();
-
- while (!eof_reached()) {
- if (c <= ' ') {
- if (token.length()) {
- break;
- }
- } else {
- token += c;
- }
- c = get_8();
- }
-
- return String::utf8(token.get_data());
-}
-
-class CharBuffer {
- Vector<char> vector;
- char stack_buffer[256];
-
- char *buffer = nullptr;
- int capacity = 0;
- int written = 0;
-
- bool grow() {
- if (vector.resize(next_power_of_2(1 + written)) != OK) {
- return false;
- }
-
- if (buffer == stack_buffer) { // first chunk?
-
- for (int i = 0; i < written; i++) {
- vector.write[i] = stack_buffer[i];
- }
- }
-
- buffer = vector.ptrw();
- capacity = vector.size();
- ERR_FAIL_COND_V(written >= capacity, false);
-
- return true;
- }
-
-public:
- _FORCE_INLINE_ CharBuffer() :
- buffer(stack_buffer),
- capacity(sizeof(stack_buffer) / sizeof(char)) {
- }
-
- _FORCE_INLINE_ void push_back(char c) {
- if (written >= capacity) {
- ERR_FAIL_COND(!grow());
- }
-
- buffer[written++] = c;
- }
-
- _FORCE_INLINE_ const char *get_data() const {
- return buffer;
- }
-};
-
-String FileAccess::get_line() const {
- CharBuffer line;
-
- char32_t c = get_8();
-
- while (!eof_reached()) {
- if (c == '\n' || c == '\0') {
- line.push_back(0);
- return String::utf8(line.get_data());
- } else if (c != '\r') {
- line.push_back(c);
- }
-
- c = get_8();
- }
- line.push_back(0);
- return String::utf8(line.get_data());
-}
-
-Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
- ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>());
-
- String l;
- int qc = 0;
- do {
- if (eof_reached()) {
- break;
- }
-
- l += get_line() + "\n";
- qc = 0;
- for (int i = 0; i < l.length(); i++) {
- if (l[i] == '"') {
- qc++;
- }
- }
-
- } while (qc % 2);
-
- l = l.substr(0, l.length() - 1);
-
- Vector<String> strings;
-
- bool in_quote = false;
- String current;
- for (int i = 0; i < l.length(); i++) {
- char32_t c = l[i];
- char32_t s[2] = { 0, 0 };
-
- if (!in_quote && c == p_delim[0]) {
- strings.push_back(current);
- current = String();
- } else if (c == '"') {
- if (l[i + 1] == '"' && in_quote) {
- s[0] = '"';
- current += s;
- i++;
- } else {
- in_quote = !in_quote;
- }
- } else {
- s[0] = c;
- current += s;
- }
- }
-
- strings.push_back(current);
-
- return strings;
-}
-
-int FileAccess::get_buffer(uint8_t *p_dst, int p_length) const {
- int i = 0;
- for (i = 0; i < p_length && !eof_reached(); i++) {
- p_dst[i] = get_8();
- }
-
- return i;
-}
-
-String FileAccess::get_as_utf8_string() const {
- Vector<uint8_t> sourcef;
- int len = get_len();
- sourcef.resize(len + 1);
-
- uint8_t *w = sourcef.ptrw();
- int r = get_buffer(w, len);
- ERR_FAIL_COND_V(r != len, String());
- w[len] = 0;
-
- String s;
- if (s.parse_utf8((const char *)w)) {
- return String();
- }
- return s;
-}
-
-void FileAccess::store_16(uint16_t p_dest) {
- uint8_t a, b;
-
- a = p_dest & 0xFF;
- b = p_dest >> 8;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_8(a);
- store_8(b);
-}
-
-void FileAccess::store_32(uint32_t p_dest) {
- uint16_t a, b;
-
- a = p_dest & 0xFFFF;
- b = p_dest >> 16;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_16(a);
- store_16(b);
-}
-
-void FileAccess::store_64(uint64_t p_dest) {
- uint32_t a, b;
-
- a = p_dest & 0xFFFFFFFF;
- b = p_dest >> 32;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_32(a);
- store_32(b);
-}
-
-void FileAccess::store_real(real_t p_real) {
- if (sizeof(real_t) == 4) {
- store_float(p_real);
- } else {
- store_double(p_real);
- }
-}
-
-void FileAccess::store_float(float p_dest) {
- MarshallFloat m;
- m.f = p_dest;
- store_32(m.i);
-}
-
-void FileAccess::store_double(double p_dest) {
- MarshallDouble m;
- m.d = p_dest;
- store_64(m.l);
-}
-
-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;
- }
-
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
-
- uint64_t mt = fa->_get_modified_time(p_file);
- memdelete(fa);
- return mt;
-}
-
-uint32_t 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;
- }
-
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
-
- uint32_t mt = fa->_get_unix_permissions(p_file);
- memdelete(fa);
- return mt;
-}
-
-Error FileAccess::set_unix_permissions(const String &p_file, uint32_t 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;
- }
-
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
-
- Error err = fa->_set_unix_permissions(p_file, p_permissions);
- memdelete(fa);
- return err;
-}
-
-void FileAccess::store_string(const String &p_string) {
- if (p_string.length() == 0) {
- return;
- }
-
- CharString cs = p_string.utf8();
- store_buffer((uint8_t *)&cs[0], cs.length());
-}
-
-void FileAccess::store_pascal_string(const String &p_string) {
- CharString cs = p_string.utf8();
- store_32(cs.length());
- store_buffer((uint8_t *)&cs[0], cs.length());
-}
-
-String FileAccess::get_pascal_string() {
- uint32_t sl = get_32();
- CharString cs;
- cs.resize(sl + 1);
- get_buffer((uint8_t *)cs.ptr(), sl);
- cs[sl] = 0;
-
- String ret;
- ret.parse_utf8(cs.ptr());
-
- return ret;
-}
-
-void FileAccess::store_line(const String &p_line) {
- store_string(p_line);
- store_8('\n');
-}
-
-void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
- ERR_FAIL_COND(p_delim.length() != 1);
-
- String line = "";
- int size = p_values.size();
- for (int i = 0; i < size; ++i) {
- String value = p_values[i];
-
- if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) {
- value = "\"" + value.replace("\"", "\"\"") + "\"";
- }
- if (i < size - 1) {
- value += p_delim;
- }
-
- line += value;
- }
-
- store_line(line);
-}
-
-void FileAccess::store_buffer(const uint8_t *p_src, int p_length) {
- for (int i = 0; i < p_length; i++) {
- store_8(p_src[i]);
- }
-}
-
-Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) {
- FileAccess *f = FileAccess::open(p_path, READ, r_error);
- if (!f) {
- if (r_error) { // if error requested, do not throw error
- return Vector<uint8_t>();
- }
- ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'.");
- }
- Vector<uint8_t> data;
- data.resize(f->get_len());
- f->get_buffer(data.ptrw(), data.size());
- memdelete(f);
- return data;
-}
-
-String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
- Error err;
- Vector<uint8_t> array = get_file_as_array(p_path, &err);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- if (r_error) {
- return String();
- }
- ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'.");
- }
-
- String ret;
- ret.parse_utf8((const char *)array.ptr(), array.size());
- return ret;
-}
-
-String FileAccess::get_md5(const String &p_file) {
- FileAccess *f = FileAccess::open(p_file, READ);
- if (!f) {
- return String();
- }
-
- CryptoCore::MD5Context ctx;
- ctx.start();
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
-
- unsigned char hash[16];
- ctx.finish(hash);
-
- memdelete(f);
-
- return String::md5(hash);
-}
-
-String FileAccess::get_multiple_md5(const Vector<String> &p_file) {
- CryptoCore::MD5Context ctx;
- ctx.start();
-
- for (int i = 0; i < p_file.size(); i++) {
- FileAccess *f = FileAccess::open(p_file[i], READ);
- ERR_CONTINUE(!f);
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
- memdelete(f);
- }
-
- unsigned char hash[16];
- ctx.finish(hash);
-
- return String::md5(hash);
-}
-
-String FileAccess::get_sha256(const String &p_file) {
- FileAccess *f = FileAccess::open(p_file, READ);
- if (!f) {
- return String();
- }
-
- CryptoCore::SHA256Context ctx;
- ctx.start();
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
-
- unsigned char hash[32];
- ctx.finish(hash);
-
- memdelete(f);
- return String::hex_encode_buffer(hash, 32);
-}
diff --git a/core/os/file_access.h b/core/os/file_access.h
deleted file mode 100644
index 1c78204c1d..0000000000
--- a/core/os/file_access.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*************************************************************************/
-/* file_access.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef FILE_ACCESS_H
-#define FILE_ACCESS_H
-
-#include "core/math/math_defs.h"
-#include "core/os/memory.h"
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-
-/**
- * Multi-Platform abstraction for accessing to files.
- */
-
-class FileAccess {
-public:
- enum AccessType {
- ACCESS_RESOURCES,
- ACCESS_USERDATA,
- ACCESS_FILESYSTEM,
- ACCESS_MAX
- };
-
- typedef void (*FileCloseFailNotify)(const String &);
-
- typedef FileAccess *(*CreateFunc)();
- bool endian_swap = false;
- bool real_is_double = false;
-
- virtual uint32_t _get_unix_permissions(const String &p_file) = 0;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0;
-
-protected:
- String fix_path(const String &p_path) const;
- virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file
- virtual uint64_t _get_modified_time(const String &p_file) = 0;
-
- static FileCloseFailNotify close_fail_notify;
-
-private:
- static bool backup_save;
-
- AccessType _access_type = ACCESS_FILESYSTEM;
- static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */
- template <class T>
- static FileAccess *_create_builtin() {
- return memnew(T);
- }
-
-public:
- static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
-
- virtual void _set_access_type(AccessType p_access);
-
- enum ModeFlags {
- READ = 1,
- WRITE = 2,
- READ_WRITE = 3,
- WRITE_READ = 7,
- };
-
- virtual void close() = 0; ///< close a file
- virtual bool is_open() const = 0; ///< true when file is open
-
- virtual String get_path() const { return ""; } /// returns the path for the current open file
- virtual String get_path_absolute() const { return ""; } /// returns the absolute path for the current open file
-
- virtual void seek(size_t p_position) = 0; ///< seek to a given position
- virtual void seek_end(int64_t p_position = 0) = 0; ///< seek from the end of file
- virtual size_t get_position() const = 0; ///< get position in the file
- virtual size_t get_len() const = 0; ///< get size of the file
-
- virtual bool eof_reached() const = 0; ///< reading passed EOF
-
- virtual uint8_t get_8() const = 0; ///< get a byte
- virtual uint16_t get_16() const; ///< get 16 bits uint
- virtual uint32_t get_32() const; ///< get 32 bits uint
- virtual uint64_t get_64() const; ///< get 64 bits uint
-
- virtual float get_float() const;
- virtual double get_double() const;
- virtual real_t get_real() const;
-
- virtual int get_buffer(uint8_t *p_dst, int p_length) const; ///< get an array of bytes
- virtual String get_line() const;
- virtual String get_token() const;
- virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
- virtual String get_as_utf8_string() const;
-
- /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
- * It's not about the current CPU type but file formats.
- * this flags get reset to false (little endian) on each open
- */
-
- virtual void set_endian_swap(bool p_swap) { endian_swap = p_swap; }
- inline bool get_endian_swap() const { return endian_swap; }
-
- virtual Error get_error() const = 0; ///< get last error
-
- virtual void flush() = 0;
- virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
- virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
- virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
- virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
-
- virtual void store_float(float p_dest);
- virtual void store_double(double p_dest);
- virtual void store_real(real_t p_real);
-
- virtual void store_string(const String &p_string);
- virtual void store_line(const String &p_line);
- virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
-
- virtual void store_pascal_string(const String &p_string);
- virtual String get_pascal_string();
-
- virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes
-
- virtual bool file_exists(const String &p_name) = 0; ///< return true if a file exists
-
- virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType
-
- static FileAccess *create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files.
- static FileAccess *create_for_path(const String &p_path);
- static FileAccess *open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
- 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 uint32_t get_unix_permissions(const String &p_file);
- static Error set_unix_permissions(const String &p_file, uint32_t p_permissions);
-
- static void set_backup_save(bool p_enable) { backup_save = p_enable; };
- static bool is_backup_save_enabled() { return backup_save; };
-
- static String get_md5(const String &p_file);
- static String get_sha256(const String &p_file);
- static String get_multiple_md5(const Vector<String> &p_file);
-
- static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = nullptr);
- static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
-
- template <class T>
- static void make_default(AccessType p_access) {
- create_func[p_access] = _create_builtin<T>;
- }
-
- FileAccess() {}
- virtual ~FileAccess() {}
-};
-
-struct FileAccessRef {
- _FORCE_INLINE_ FileAccess *operator->() {
- return f;
- }
-
- operator bool() const { return f != nullptr; }
-
- FileAccess *f;
-
- operator FileAccess *() { return f; }
-
- FileAccessRef(FileAccess *fa) { f = fa; }
- ~FileAccessRef() {
- if (f) {
- memdelete(f);
- }
- }
-};
-
-#endif // FILE_ACCESS_H
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index 4b2cafd8fe..4c5f0b5220 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -60,7 +60,7 @@ static const _KeyCodeText _keycodes[] = {
{KEY_PAGEUP ,"PageUp"},
{KEY_PAGEDOWN ,"PageDown"},
{KEY_SHIFT ,"Shift"},
- {KEY_CONTROL ,"Control"},
+ {KEY_CTRL ,"Ctrl"},
#ifdef OSX_ENABLED
{KEY_META ,"Command"},
#else
@@ -314,7 +314,7 @@ bool keycode_has_unicode(uint32_t p_keycode) {
case KEY_PAGEUP:
case KEY_PAGEDOWN:
case KEY_SHIFT:
- case KEY_CONTROL:
+ case KEY_CTRL:
case KEY_META:
case KEY_ALT:
case KEY_CAPSLOCK:
@@ -401,7 +401,7 @@ String keycode_get_string(uint32_t p_code) {
codestr += "+";
}
if (p_code & KEY_MASK_CTRL) {
- codestr += find_keycode_name(KEY_CONTROL);
+ codestr += find_keycode_name(KEY_CTRL);
codestr += "+";
}
if (p_code & KEY_MASK_META) {
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 3ef70e786f..33f9213c4e 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -45,7 +45,7 @@ enum {
SPKEY = (1 << 24)
};
-enum KeyList {
+enum Key {
/* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */
KEY_ESCAPE = SPKEY | 0x01,
KEY_TAB = SPKEY | 0x02,
@@ -68,7 +68,7 @@ enum KeyList {
KEY_PAGEUP = SPKEY | 0x13,
KEY_PAGEDOWN = SPKEY | 0x14,
KEY_SHIFT = SPKEY | 0x15,
- KEY_CONTROL = SPKEY | 0x16,
+ KEY_CTRL = SPKEY | 0x16,
KEY_META = SPKEY | 0x17,
KEY_ALT = SPKEY | 0x18,
KEY_CAPSLOCK = SPKEY | 0x19,
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 25a09fe98f..34e944709b 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -32,7 +32,7 @@
#define MAIN_LOOP_H
#include "core/input/input_event.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
class MainLoop : public Object {
diff --git a/core/os/memory.cpp b/core/os/memory.cpp
index 5910cb0e7b..a756c1d5dd 100644
--- a/core/os/memory.cpp
+++ b/core/os/memory.cpp
@@ -31,7 +31,6 @@
#include "memory.h"
#include "core/error/error_macros.h"
-#include "core/os/copymem.h"
#include "core/templates/safe_refcount.h"
#include <stdio.h>
diff --git a/core/os/memory.h b/core/os/memory.h
index 10e678103d..9d09626b8c 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -80,7 +80,7 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
#define memalloc(m_size) Memory::alloc_static(m_size)
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
-#define memfree(m_size) Memory::free_static(m_size)
+#define memfree(m_mem) Memory::free_static(m_mem)
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp
index a8be84c56c..ee87346dfc 100644
--- a/core/os/midi_driver.cpp
+++ b/core/os/midi_driver.cpp
@@ -45,23 +45,23 @@ void MIDIDriver::set_singleton() {
void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) {
Ref<InputEventMIDI> event;
- event.instance();
+ event.instantiate();
uint32_t param_position = 1;
if (length >= 1) {
if (data[0] >= 0xF0) {
// channel does not apply to system common messages
event->set_channel(0);
- event->set_message(data[0]);
+ event->set_message(MIDIMessage(data[0]));
last_received_message = data[0];
} else if ((data[0] & 0x80) == 0x00) {
// running status
event->set_channel(last_received_message & 0xF);
- event->set_message(last_received_message >> 4);
+ event->set_message(MIDIMessage(last_received_message >> 4));
param_position = 0;
} else {
event->set_channel(data[0] & 0xF);
- event->set_message(data[0] >> 4);
+ event->set_message(MIDIMessage(data[0] >> 4));
param_position = 1;
last_received_message = data[0];
}
@@ -112,6 +112,8 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_
event->set_pressure(data[param_position]);
}
break;
+ default:
+ break;
}
Input *id = Input::get_singleton();
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 182bab4058..535eee4797 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -32,8 +32,8 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/midi_driver.h"
#include "core/version_generated.gen.h"
#include "servers/audio_server.h"
@@ -47,37 +47,8 @@ OS *OS::get_singleton() {
return singleton;
}
-uint32_t OS::get_ticks_msec() const {
- return get_ticks_usec() / 1000;
-}
-
-String OS::get_iso_date_time(bool local) const {
- OS::Date date = get_date(local);
- OS::Time time = get_time(local);
-
- String timezone;
- if (!local) {
- TimeZoneInfo zone = get_time_zone_info();
- if (zone.bias >= 0) {
- timezone = "+";
- }
- timezone = timezone + itos(zone.bias / 60).pad_zeros(2) + itos(zone.bias % 60).pad_zeros(2);
- } else {
- timezone = "Z";
- }
-
- return itos(date.year).pad_zeros(2) +
- "-" +
- itos(date.month).pad_zeros(2) +
- "-" +
- itos(date.day).pad_zeros(2) +
- "T" +
- itos(time.hour).pad_zeros(2) +
- ":" +
- itos(time.min).pad_zeros(2) +
- ":" +
- itos(time.sec).pad_zeros(2) +
- timezone;
+uint64_t OS::get_ticks_msec() const {
+ return get_ticks_usec() / 1000ULL;
}
double OS::get_unix_time() const {
@@ -106,10 +77,18 @@ void OS::add_logger(Logger *p_logger) {
}
void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) {
+ if (!_stderr_enabled) {
+ return;
+ }
+
_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
}
void OS::print(const char *p_format, ...) {
+ if (!_stdout_enabled) {
+ return;
+ }
+
va_list argp;
va_start(argp, p_format);
@@ -119,6 +98,10 @@ void OS::print(const char *p_format, ...) {
}
void OS::printerr(const char *p_format, ...) {
+ if (!_stderr_enabled) {
+ return;
+ }
+
va_list argp;
va_start(argp, p_format);
@@ -163,6 +146,22 @@ bool OS::is_stdout_debug_enabled() const {
return _debug_stdout;
}
+bool OS::is_stdout_enabled() const {
+ return _stdout_enabled;
+}
+
+bool OS::is_stderr_enabled() const {
+ return _stderr_enabled;
+}
+
+void OS::set_stdout_enabled(bool p_enabled) {
+ _stdout_enabled = p_enabled;
+}
+
+void OS::set_stderr_enabled(bool p_enabled) {
+ _stderr_enabled = p_enabled;
+}
+
void OS::dump_memory_to_file(const char *p_file) {
//Memory::dump_static_mem_to_file(p_file);
}
@@ -282,6 +281,11 @@ String OS::get_user_data_dir() const {
return ".";
}
+// Android OS path to app's external data storage
+String OS::get_external_data_dir() const {
+ return get_user_data_dir();
+};
+
// Absolute path to res://
String OS::get_resource_dir() const {
return ProjectSettings::get_singleton()->get_resource_path();
diff --git a/core/os/os.h b/core/os/os.h
index e41d788e12..301718a8b3 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -58,8 +58,8 @@ class OS {
int _orientation;
bool _allow_hidpi = false;
bool _allow_layered = false;
- bool _use_vsync;
- bool _vsync_via_compositor;
+ bool _stdout_enabled = true;
+ bool _stderr_enabled = true;
char *last_error;
@@ -156,17 +156,17 @@ public:
virtual void yield();
- enum Weekday {
- DAY_SUNDAY,
- DAY_MONDAY,
- DAY_TUESDAY,
- DAY_WEDNESDAY,
- DAY_THURSDAY,
- DAY_FRIDAY,
- DAY_SATURDAY
+ enum Weekday : uint8_t {
+ WEEKDAY_SUNDAY,
+ WEEKDAY_MONDAY,
+ WEEKDAY_TUESDAY,
+ WEEKDAY_WEDNESDAY,
+ WEEKDAY_THURSDAY,
+ WEEKDAY_FRIDAY,
+ WEEKDAY_SATURDAY,
};
- enum Month {
+ enum Month : uint8_t {
/// Start at 1 to follow Windows SYSTEMTIME structure
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
MONTH_JANUARY = 1,
@@ -180,21 +180,21 @@ public:
MONTH_SEPTEMBER,
MONTH_OCTOBER,
MONTH_NOVEMBER,
- MONTH_DECEMBER
+ MONTH_DECEMBER,
};
struct Date {
- int year;
+ int64_t year;
Month month;
- int day;
+ uint8_t day;
Weekday weekday;
bool dst;
};
struct Time {
- int hour;
- int min;
- int sec;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
};
struct TimeZoneInfo {
@@ -205,20 +205,24 @@ public:
virtual Date get_date(bool local = false) const = 0;
virtual Time get_time(bool local = false) const = 0;
virtual TimeZoneInfo get_time_zone_info() const = 0;
- virtual String get_iso_date_time(bool local = false) const;
virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const = 0;
virtual void add_frame_delay(bool p_can_draw);
virtual uint64_t get_ticks_usec() const = 0;
- uint32_t get_ticks_msec() const;
+ uint64_t get_ticks_msec() const;
virtual bool is_userfs_persistent() const { return true; }
bool is_stdout_verbose() const;
bool is_stdout_debug_enabled() const;
+ bool is_stdout_enabled() const;
+ bool is_stderr_enabled() const;
+ void set_stdout_enabled(bool p_enabled);
+ void set_stderr_enabled(bool p_enabled);
+
virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; }
virtual void initialize_debugging() {}
@@ -245,6 +249,7 @@ public:
virtual String get_bundle_resource_dir() const;
virtual String get_user_data_dir() const;
+ virtual String get_external_data_dir() const;
virtual String get_resource_dir() const;
enum SystemDir {
diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp
index b538ca0c96..74e9c24e04 100644
--- a/core/os/pool_allocator.cpp
+++ b/core/os/pool_allocator.cpp
@@ -31,7 +31,6 @@
#include "pool_allocator.h"
#include "core/error/error_macros.h"
-#include "core/os/copymem.h"
#include "core/os/memory.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -42,7 +41,7 @@
do { \
void *_dst = &((unsigned char *)pool)[m_to_pos]; \
void *_src = &((unsigned char *)pool)[(m_entry).pos]; \
- movemem(_dst, _src, aligned((m_entry).len)); \
+ memmove(_dst, _src, aligned((m_entry).len)); \
(m_entry).pos = m_to_pos; \
} while (0);
@@ -136,7 +135,7 @@ void PoolAllocator::compact_up(int p_from) {
for (int i = entry_count - 1; i >= p_from; i--) {
Entry &entry = entry_array[entry_indices[i]];
- /* determine hole size to nextious entry */
+ /* determine hole size for next entry */
int hole_size = next_entry_end_pos - (entry.pos + aligned(entry.len));
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index aea370787d..73e31bdb3d 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -41,9 +41,13 @@ void (*Thread::set_priority_func)(Thread::Priority) = nullptr;
void (*Thread::init_func)() = nullptr;
void (*Thread::term_func)() = nullptr;
-Thread::ID Thread::main_thread_id = 1;
-SafeNumeric<Thread::ID> Thread::last_thread_id{ 1 };
-thread_local Thread::ID Thread::caller_id = 1;
+uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) {
+ static std::hash<std::thread::id> hasher;
+ return hasher(p_t);
+}
+
+Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id());
+thread_local Thread::ID Thread::caller_id = 0;
void Thread::_set_platform_funcs(
Error (*p_set_name_func)(const String &),
@@ -57,7 +61,7 @@ void Thread::_set_platform_funcs(
}
void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
- Thread::caller_id = p_self->id;
+ Thread::caller_id = _thread_id_hash(p_self->thread.get_id());
if (set_priority_func) {
set_priority_func(p_settings.priority);
}
@@ -73,7 +77,7 @@ void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_cal
}
void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
@@ -81,22 +85,22 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_
std::thread empty_thread;
thread.swap(empty_thread);
}
- id = last_thread_id.increment();
std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
thread.swap(new_thread);
+ id = _thread_id_hash(thread.get_id());
}
bool Thread::is_started() const {
- return id != 0;
+ return id != _thread_id_hash(std::thread::id());
}
void Thread::wait_to_finish() {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish.");
thread.join();
std::thread empty_thread;
thread.swap(empty_thread);
- id = 0;
+ id = _thread_id_hash(std::thread::id());
}
}
@@ -108,8 +112,12 @@ Error Thread::set_name(const String &p_name) {
return ERR_UNAVAILABLE;
}
+Thread::Thread() {
+ caller_id = _thread_id_hash(std::this_thread::get_id());
+}
+
Thread::~Thread() {
- if (id != 0) {
+ if (id != _thread_id_hash(std::thread::id())) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
diff --git a/core/os/thread.h b/core/os/thread.h
index 76f5be182e..17ac82c650 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -62,9 +62,10 @@ private:
friend class Main;
static ID main_thread_id;
- static SafeNumeric<Thread::ID> last_thread_id;
- ID id = 0;
+ static uint64_t _thread_id_hash(const std::thread::id &p_t);
+
+ ID id = _thread_id_hash(std::thread::id());
static thread_local ID caller_id;
std::thread thread;
@@ -97,6 +98,7 @@ public:
///< waits until thread is finished, and deallocates it.
void wait_to_finish();
+ Thread();
~Thread();
#else
_FORCE_INLINE_ ID get_id() const { return 0; }
diff --git a/core/os/time.cpp b/core/os/time.cpp
new file mode 100644
index 0000000000..a185029969
--- /dev/null
+++ b/core/os/time.cpp
@@ -0,0 +1,433 @@
+/*************************************************************************/
+/* time.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "time.h"
+
+#include "core/os/os.h"
+
+#define UNIX_EPOCH_YEAR_AD 1970 // 1970
+#define SECONDS_PER_DAY (24 * 60 * 60) // 86400
+#define IS_LEAP_YEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
+#define YEAR_SIZE(year) (IS_LEAP_YEAR(year) ? 366 : 365)
+
+#define YEAR_KEY "year"
+#define MONTH_KEY "month"
+#define DAY_KEY "day"
+#define WEEKDAY_KEY "weekday"
+#define HOUR_KEY "hour"
+#define MINUTE_KEY "minute"
+#define SECOND_KEY "second"
+#define DST_KEY "dst"
+
+// Table of number of days in each month (for regular year and leap year).
+static const uint8_t MONTH_DAYS_TABLE[2][12] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+VARIANT_ENUM_CAST(Time::Month);
+VARIANT_ENUM_CAST(Time::Weekday);
+
+#define UNIX_TIME_TO_HMS \
+ uint8_t hour, minute, second; \
+ { \
+ /* The time of the day (in seconds since start of day). */ \
+ uint32_t day_clock = Math::posmod(p_unix_time_val, SECONDS_PER_DAY); \
+ /* On x86 these 4 lines can be optimized to only 2 divisions. */ \
+ second = day_clock % 60; \
+ day_clock /= 60; \
+ minute = day_clock % 60; \
+ hour = day_clock / 60; \
+ }
+
+#define UNIX_TIME_TO_YMD \
+ int64_t year; \
+ Month month; \
+ uint8_t day; \
+ /* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
+ int64_t day_number = Math::floor(p_unix_time_val / (double)SECONDS_PER_DAY); \
+ { \
+ int64_t day_number_copy = day_number; \
+ year = UNIX_EPOCH_YEAR_AD; \
+ uint8_t month_zero_index = 0; \
+ while (day_number_copy >= YEAR_SIZE(year)) { \
+ day_number_copy -= YEAR_SIZE(year); \
+ year++; \
+ } \
+ while (day_number_copy < 0) { \
+ year--; \
+ day_number_copy += YEAR_SIZE(year); \
+ } \
+ /* After the above, day_number now represents the day of the year (0-index). */ \
+ while (day_number_copy >= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]) { \
+ day_number_copy -= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]; \
+ month_zero_index++; \
+ } \
+ /* After the above, day_number now represents the day of the month (0-index). */ \
+ month = (Month)(month_zero_index + 1); \
+ day = day_number_copy + 1; \
+ }
+
+#define VALIDATE_YMDHMS \
+ ERR_FAIL_COND_V_MSG(month == 0, 0, "Invalid month value of: " + itos(month) + ", months are 1-indexed and cannot be 0. See the Time.Month enum for valid values."); \
+ ERR_FAIL_COND_V_MSG(month > 12, 0, "Invalid month value of: " + itos(month) + ". See the Time.Month enum for valid values."); \
+ ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + "."); \
+ ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + "."); \
+ ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + " (leap seconds are not supported)."); \
+ /* Do this check after month is tested as valid. */ \
+ ERR_FAIL_COND_V_MSG(day == 0, 0, "Invalid day value of: " + itos(month) + ", days are 1-indexed and cannot be 0."); \
+ uint8_t days_in_this_month = MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month - 1]; \
+ ERR_FAIL_COND_V_MSG(day > days_in_this_month, 0, "Invalid day value of: " + itos(day) + " which is larger than the maximum for this month, " + itos(days_in_this_month) + ".");
+
+#define YMD_TO_DAY_NUMBER \
+ /* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
+ int64_t day_number = day - 1; \
+ /* Add the days in the months to day_number. */ \
+ for (int i = 0; i < month - 1; i++) { \
+ day_number += MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][i]; \
+ } \
+ /* Add the days in the years to day_number. */ \
+ if (year >= UNIX_EPOCH_YEAR_AD) { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD; iyear < year; iyear++) { \
+ day_number += YEAR_SIZE(iyear); \
+ } \
+ } else { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD - 1; iyear >= year; iyear--) { \
+ day_number -= YEAR_SIZE(iyear); \
+ } \
+ }
+
+#define PARSE_ISO8601_STRING \
+ int64_t year = UNIX_EPOCH_YEAR_AD; \
+ Month month = MONTH_JANUARY; \
+ uint8_t day = 1; \
+ uint8_t hour = 0; \
+ uint8_t minute = 0; \
+ uint8_t second = 0; \
+ { \
+ bool has_date = false, has_time = false; \
+ String date, time; \
+ if (p_datetime.find_char('T') > 0) { \
+ has_date = has_time = true; \
+ PackedStringArray array = p_datetime.split("T"); \
+ date = array[0]; \
+ time = array[1]; \
+ } else if (p_datetime.find_char(' ') > 0) { \
+ has_date = has_time = true; \
+ PackedStringArray array = p_datetime.split(" "); \
+ date = array[0]; \
+ time = array[1]; \
+ } else if (p_datetime.find_char('-', 1) > 0) { \
+ has_date = true; \
+ date = p_datetime; \
+ } else if (p_datetime.find_char(':') > 0) { \
+ has_time = true; \
+ time = p_datetime; \
+ } \
+ /* Set the variables from the contents of the string. */ \
+ if (has_date) { \
+ PackedInt32Array array = date.split_ints("-", false); \
+ year = array[0]; \
+ month = (Month)array[1]; \
+ day = array[2]; \
+ /* Handle negative years. */ \
+ if (p_datetime.find_char('-') == 0) { \
+ year *= -1; \
+ } \
+ } \
+ if (has_time) { \
+ PackedInt32Array array = time.split_ints(":", false); \
+ hour = array[0]; \
+ minute = array[1]; \
+ second = array[2]; \
+ } \
+ }
+
+#define EXTRACT_FROM_DICTIONARY \
+ /* Get all time values from the dictionary. If it doesn't exist, set the */ \
+ /* values to the default values for Unix epoch (1970-01-01 00:00:00). */ \
+ int64_t year = p_datetime.has(YEAR_KEY) ? int64_t(p_datetime[YEAR_KEY]) : UNIX_EPOCH_YEAR_AD; \
+ Month month = Month((p_datetime.has(MONTH_KEY)) ? uint8_t(p_datetime[MONTH_KEY]) : 1); \
+ uint8_t day = p_datetime.has(DAY_KEY) ? uint8_t(p_datetime[DAY_KEY]) : 1; \
+ uint8_t hour = p_datetime.has(HOUR_KEY) ? uint8_t(p_datetime[HOUR_KEY]) : 0; \
+ uint8_t minute = p_datetime.has(MINUTE_KEY) ? uint8_t(p_datetime[MINUTE_KEY]) : 0; \
+ uint8_t second = p_datetime.has(SECOND_KEY) ? uint8_t(p_datetime[SECOND_KEY]) : 0;
+
+Time *Time::singleton = nullptr;
+
+Time *Time::get_singleton() {
+ if (!singleton) {
+ memnew(Time);
+ }
+ return singleton;
+}
+
+Dictionary Time::get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ UNIX_TIME_TO_YMD
+ Dictionary datetime;
+ datetime[YEAR_KEY] = year;
+ datetime[MONTH_KEY] = (uint8_t)month;
+ datetime[DAY_KEY] = day;
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+ datetime[HOUR_KEY] = hour;
+ datetime[MINUTE_KEY] = minute;
+ datetime[SECOND_KEY] = second;
+
+ return datetime;
+}
+
+Dictionary Time::get_date_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_YMD
+ Dictionary datetime;
+ datetime[YEAR_KEY] = year;
+ datetime[MONTH_KEY] = (uint8_t)month;
+ datetime[DAY_KEY] = day;
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+
+ return datetime;
+}
+
+Dictionary Time::get_time_dict_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ Dictionary datetime;
+ datetime[HOUR_KEY] = hour;
+ datetime[MINUTE_KEY] = minute;
+ datetime[SECOND_KEY] = second;
+
+ return datetime;
+}
+
+String Time::get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space) const {
+ UNIX_TIME_TO_HMS
+ UNIX_TIME_TO_YMD
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+
+ return timestamp;
+}
+
+String Time::get_date_string_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_YMD
+ // Android is picky about the types passed to make Variant, so we need a cast.
+ return vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+}
+
+String Time::get_time_string_from_unix_time(int64_t p_unix_time_val) const {
+ UNIX_TIME_TO_HMS
+ return vformat("%02d:%02d:%02d", hour, minute, second);
+}
+
+Dictionary Time::get_datetime_dict_from_string(String p_datetime, bool p_weekday) const {
+ PARSE_ISO8601_STRING
+ Dictionary dict;
+ dict[YEAR_KEY] = year;
+ dict[MONTH_KEY] = (uint8_t)month;
+ dict[DAY_KEY] = day;
+ if (p_weekday) {
+ YMD_TO_DAY_NUMBER
+ // Unix epoch was a Thursday (day 0 aka 1970-01-01).
+ dict[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
+ }
+ dict[HOUR_KEY] = hour;
+ dict[MINUTE_KEY] = minute;
+ dict[SECOND_KEY] = second;
+
+ return dict;
+}
+
+String Time::get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space) const {
+ ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), "", "Invalid datetime Dictionary: Dictionary is empty.");
+ EXTRACT_FROM_DICTIONARY
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+ return timestamp;
+}
+
+int64_t Time::get_unix_time_from_datetime_dict(Dictionary p_datetime) const {
+ ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
+ EXTRACT_FROM_DICTIONARY
+ VALIDATE_YMDHMS
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const {
+ PARSE_ISO8601_STRING
+ VALIDATE_YMDHMS
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+Dictionary Time::get_datetime_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary datetime;
+ datetime[YEAR_KEY] = date.year;
+ datetime[MONTH_KEY] = (uint8_t)date.month;
+ datetime[DAY_KEY] = date.day;
+ datetime[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ datetime[DST_KEY] = date.dst;
+ datetime[HOUR_KEY] = time.hour;
+ datetime[MINUTE_KEY] = time.minute;
+ datetime[SECOND_KEY] = time.second;
+ return datetime;
+}
+
+Dictionary Time::get_date_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ Dictionary date_dictionary;
+ date_dictionary[YEAR_KEY] = date.year;
+ date_dictionary[MONTH_KEY] = (uint8_t)date.month;
+ date_dictionary[DAY_KEY] = date.day;
+ date_dictionary[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ date_dictionary[DST_KEY] = date.dst;
+ return date_dictionary;
+}
+
+Dictionary Time::get_time_dict_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary time_dictionary;
+ time_dictionary[HOUR_KEY] = time.hour;
+ time_dictionary[MINUTE_KEY] = time.minute;
+ time_dictionary[SECOND_KEY] = time.second;
+ return time_dictionary;
+}
+
+String Time::get_datetime_string_from_system(bool p_utc, bool p_use_space) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ }
+
+ return timestamp;
+}
+
+String Time::get_date_string_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ // Android is picky about the types passed to make Variant, so we need a cast.
+ return vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+}
+
+String Time::get_time_string_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ return vformat("%02d:%02d:%02d", time.hour, time.minute, time.second);
+}
+
+Dictionary Time::get_time_zone_from_system() const {
+ OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
+ Dictionary timezone;
+ timezone["bias"] = info.bias;
+ timezone["name"] = info.name;
+ return timezone;
+}
+
+double Time::get_unix_time_from_system() const {
+ return OS::get_singleton()->get_unix_time();
+}
+
+uint64_t Time::get_ticks_msec() const {
+ return OS::get_singleton()->get_ticks_msec();
+}
+
+uint64_t Time::get_ticks_usec() const {
+ return OS::get_singleton()->get_ticks_usec();
+}
+
+void Time::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_unix_time", "unix_time_val"), &Time::get_datetime_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_date_dict_from_unix_time", "unix_time_val"), &Time::get_date_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_time_dict_from_unix_time", "unix_time_val"), &Time::get_time_dict_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_unix_time", "unix_time_val", "use_space"), &Time::get_datetime_string_from_unix_time, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_string_from_unix_time", "unix_time_val"), &Time::get_date_string_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_time_string_from_unix_time", "unix_time_val"), &Time::get_time_string_from_unix_time);
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_string", "datetime", "weekday"), &Time::get_datetime_dict_from_string);
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_dict", "datetime", "use_space"), &Time::get_datetime_string_from_dict);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_dict", "datetime"), &Time::get_unix_time_from_datetime_dict);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_string", "datetime"), &Time::get_unix_time_from_datetime_string);
+
+ ClassDB::bind_method(D_METHOD("get_datetime_dict_from_system", "utc"), &Time::get_datetime_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_dict_from_system", "utc"), &Time::get_date_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_dict_from_system", "utc"), &Time::get_time_dict_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_system", "utc", "use_space"), &Time::get_datetime_string_from_system, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_date_string_from_system", "utc"), &Time::get_date_string_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_string_from_system", "utc"), &Time::get_time_string_from_system, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_time_zone_from_system"), &Time::get_time_zone_from_system);
+ ClassDB::bind_method(D_METHOD("get_unix_time_from_system"), &Time::get_unix_time_from_system);
+ ClassDB::bind_method(D_METHOD("get_ticks_msec"), &Time::get_ticks_msec);
+ ClassDB::bind_method(D_METHOD("get_ticks_usec"), &Time::get_ticks_usec);
+
+ BIND_ENUM_CONSTANT(MONTH_JANUARY);
+ BIND_ENUM_CONSTANT(MONTH_FEBRUARY);
+ BIND_ENUM_CONSTANT(MONTH_MARCH);
+ BIND_ENUM_CONSTANT(MONTH_APRIL);
+ BIND_ENUM_CONSTANT(MONTH_MAY);
+ BIND_ENUM_CONSTANT(MONTH_JUNE);
+ BIND_ENUM_CONSTANT(MONTH_JULY);
+ BIND_ENUM_CONSTANT(MONTH_AUGUST);
+ BIND_ENUM_CONSTANT(MONTH_SEPTEMBER);
+ BIND_ENUM_CONSTANT(MONTH_OCTOBER);
+ BIND_ENUM_CONSTANT(MONTH_NOVEMBER);
+ BIND_ENUM_CONSTANT(MONTH_DECEMBER);
+
+ BIND_ENUM_CONSTANT(WEEKDAY_SUNDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_MONDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_TUESDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_WEDNESDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_THURSDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_FRIDAY);
+ BIND_ENUM_CONSTANT(WEEKDAY_SATURDAY);
+}
+
+Time::Time() {
+ ERR_FAIL_COND_MSG(singleton, "Singleton for Time already exists.");
+ singleton = this;
+}
+
+Time::~Time() {
+ singleton = nullptr;
+}
diff --git a/core/os/time.h b/core/os/time.h
new file mode 100644
index 0000000000..4325f93d56
--- /dev/null
+++ b/core/os/time.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* time.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TIME_H
+#define TIME_H
+
+#include "core/object/class_db.h"
+
+// This Time class conforms with as many of the ISO 8601 standards as possible.
+// * As per ISO 8601:2004 4.3.2.1, all dates follow the Proleptic Gregorian
+// calendar. As such, the day before 1582-10-15 is 1582-10-14, not 1582-10-04.
+// See: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
+// * As per ISO 8601:2004 3.4.2 and 4.1.2.4, the year before 1 AD (aka 1 BC)
+// is number "0", with the year before that (2 BC) being "-1", etc.
+// Conversion methods assume "the same timezone", and do not handle DST.
+// Leap seconds are not handled, they must be done manually if desired.
+// Suffixes such as "Z" are not handled, you need to strip them away manually.
+
+class Time : public Object {
+ GDCLASS(Time, Object);
+ static void _bind_methods();
+ static Time *singleton;
+
+public:
+ static Time *get_singleton();
+
+ enum Month : uint8_t {
+ /// Start at 1 to follow Windows SYSTEMTIME structure
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
+ MONTH_JANUARY = 1,
+ MONTH_FEBRUARY,
+ MONTH_MARCH,
+ MONTH_APRIL,
+ MONTH_MAY,
+ MONTH_JUNE,
+ MONTH_JULY,
+ MONTH_AUGUST,
+ MONTH_SEPTEMBER,
+ MONTH_OCTOBER,
+ MONTH_NOVEMBER,
+ MONTH_DECEMBER,
+ };
+
+ enum Weekday : uint8_t {
+ WEEKDAY_SUNDAY,
+ WEEKDAY_MONDAY,
+ WEEKDAY_TUESDAY,
+ WEEKDAY_WEDNESDAY,
+ WEEKDAY_THURSDAY,
+ WEEKDAY_FRIDAY,
+ WEEKDAY_SATURDAY,
+ };
+
+ // Methods that convert times.
+ Dictionary get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_date_dict_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_time_dict_from_unix_time(int64_t p_unix_time_val) const;
+ String get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space = false) const;
+ String get_date_string_from_unix_time(int64_t p_unix_time_val) const;
+ String get_time_string_from_unix_time(int64_t p_unix_time_val) const;
+ Dictionary get_datetime_dict_from_string(String p_datetime, bool p_weekday = true) const;
+ String get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space = false) const;
+ int64_t get_unix_time_from_datetime_dict(Dictionary p_datetime) const;
+ int64_t get_unix_time_from_datetime_string(String p_datetime) const;
+
+ // Methods that get information from OS.
+ Dictionary get_datetime_dict_from_system(bool p_utc = false) const;
+ Dictionary get_date_dict_from_system(bool p_utc = false) const;
+ Dictionary get_time_dict_from_system(bool p_utc = false) const;
+ String get_datetime_string_from_system(bool p_utc = false, bool p_use_space = false) const;
+ String get_date_string_from_system(bool p_utc = false) const;
+ String get_time_string_from_system(bool p_utc = false) const;
+ Dictionary get_time_zone_from_system() const;
+ double get_unix_time_from_system() const;
+ uint64_t get_ticks_msec() const;
+ uint64_t get_ticks_usec() const;
+
+ Time();
+ virtual ~Time();
+};
+
+#endif // TIME_H