### ### file system utility ### extends Object class_name GDBFsUtil const __META_FILENAME_REGEX = "__gdb_fsutil_filename_regex__" const __META_ENVVAR_REGEX = "__gdb_fsutil_envvar_regex__" class SpecialFolder: var path := "" var name := "" func _init(path_ : String, name_ : String): self.path = path_ self.name = name_ ################ # public stuff # ################ func _init() -> void: assert(0, "This class should not be instantiated.") static func find_all_by_name(path : String, name : String, files_only = true) -> Dictionary: var result = [] __find_all_by_name(path, name, result, files_only) return result static func escape_filename(filename : String) -> String: return __get_filename_regex().sub(filename, "_", true) static func is_filename_valid(filename : String) -> bool: return __get_filename_regex().search(filename) == null static func load_image_resource(filename : String): var res = load(filename) if res: return res var img = Image.new() if img.load(filename) == OK: var tex = ImageTexture.new() tex.create_from_image(img) return tex return null #! Retrieve information about the current git branch. #! #! Used to display this information in development builds. #! Returns a dictionary with the following keys: #! - "branch" - the name of the current branch #! - "commit" - the full hash of the latest commit #! - "commit_short" - the shortened hash of the last commit static func get_git_info() -> Dictionary: var file = File.new() if file.open("res://.git/HEAD", File.READ) != OK: return {"branch": "", "commit": "???", "commit_short": "???"} var text = file.get_line() if !text.begins_with("ref:"): return {"branch": "", "commit": text, "commit_short": text.left(7)} var ref = text.right(5).get_file() file.close() if file.open("res://.git/refs/heads/%s" % ref, File.READ) != OK: return {"branch": ref, "commit": "", "commit_short": ""} var commitid = file.get_line() return {"branch": ref, "commit": commitid, "commit_short": commitid.left(7)} static func get_home_folder() -> String: if OS.has_feature("X11"): if OS.has_environment("HOME"): return OS.get_environment("HOME") elif OS.has_feature("Windows"): if OS.has_environment("USERPROFILE"): return OS.get_environment("USERPROFILE") return "/" static func get_special_folders() -> Array: if OS.has_feature("X11"): return __get_xdg_folders() return [] static func replace_environment_variables(string : String) -> String: var matches := __get_envvar_regex().search_all(string) var parts := PoolStringArray() var pos := 0 for the_match in matches: var var_name : String = the_match.strings[-2] if !the_match.strings[-1] else the_match.strings[-1] parts.append(string.substr(pos, the_match.get_start())) parts.append(OS.get_environment(var_name)) pos = the_match.get_end() parts.append(string.substr(pos)) return parts.join("") ################# # private stuff # ################# static func __get_filename_regex() -> RegEx: var tree := GDBUtility.get_scene_tree() if tree.has_meta(__META_FILENAME_REGEX): return tree.get_meta(__META_FILENAME_REGEX) as RegEx var filename_regex := RegEx.new() filename_regex.compile("[^a-zA-Z0-9_\\-\\. ]") tree.set_meta(__META_FILENAME_REGEX, filename_regex) return filename_regex static func __get_envvar_regex() -> RegEx: var tree := GDBUtility.get_scene_tree() if tree.has_meta(__META_ENVVAR_REGEX): return tree.get_meta(__META_ENVVAR_REGEX) as RegEx var envvar_regex := RegEx.new() if OS.has_feature("Windows"): envvar_regex.compile("%(\\w+)%") else: envvar_regex.compile("\\$({(\\w+)}|(\\w+))") tree.set_meta(__META_ENVVAR_REGEX, envvar_regex) return envvar_regex static func __find_all_by_name(path, name, result, files_only): var dir = Directory.new() if dir.open(path) != OK: # print("cannot open dir %s" % path) return dir.list_dir_begin(true) while true: var fname = dir.get_next() if fname == "": break var full_name = path + "/" + fname if fname == name && (!files_only || dir.file_exists(full_name)): result.append(full_name) # dir_exists doesnt work for res:// paths, just attempt to add, will silently fail for files if true: # dir.dir_exists(full_name): __find_all_by_name(full_name, name, result, files_only) dir.list_dir_end() static func __get_xdg_folders() -> Array: var user_dirs_filename := get_home_folder().plus_file(".config/user-dirs.dirs") var user_dirs_file := File.new() if !user_dirs_file.file_exists(user_dirs_filename): return [] var status := user_dirs_file.open(user_dirs_filename, File.READ) if status != OK: printerr("Error fetching XDG user dirs.") return [] var dir_regex := RegEx.new() status = dir_regex.compile("^XDG_([A-Z]+)_DIR=\"([^\"]+)\"$") assert(status == OK) var folders := [] while !user_dirs_file.eof_reached(): var line := user_dirs_file.get_line().strip_edges() var result := dir_regex.search(line) if result == null: continue var folder_name : String = result.strings[1].capitalize() var folder_path : String = replace_environment_variables(result.strings[2]) folders.append(SpecialFolder.new(folder_path, folder_name)) return folders