Compare commits
2 Commits
b0de07fda8
...
b5fe707c1a
Author | SHA1 | Date | |
---|---|---|---|
b5fe707c1a | |||
88757cf007 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Dinoleaf LLC
|
||||
|
||||
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.
|
@ -1,5 +1,8 @@
|
||||
# Not unlike factory pattern.
|
||||
|
||||
# This code's been directly written in due to time and lazy, but
|
||||
# will be thrown into submodules eventually to clean readability up.
|
||||
|
||||
## Recursively creates virtual file systems.
|
||||
static func create_meta_accessor(file_accessors:Array[VFileAccess])->VFileAccess:
|
||||
var vfiler := VFileAccess.new()
|
||||
@ -39,8 +42,11 @@ static func create_meta_accessor(file_accessors:Array[VFileAccess])->VFileAccess
|
||||
|
||||
|
||||
## A basic file accessor.
|
||||
static func create_file_access(root:String = "./")->VFileAccess:
|
||||
var vfiler := VFileAccess.new(root)
|
||||
static func create_file_access(
|
||||
root:String = "./",
|
||||
support_files:Dictionary[String,Callable] = VFileAccess.IMPORTS.DEFAULT_SUPPORTED_FILES
|
||||
)->VFileAccess:
|
||||
var vfiler := VFileAccess.new(root, support_files)
|
||||
|
||||
vfiler._get_buffer = FileAccess.get_file_as_bytes # thanks for being static
|
||||
|
||||
@ -68,7 +74,7 @@ static func create_file_access(root:String = "./")->VFileAccess:
|
||||
|
||||
static func create_writeonly_zip_access(
|
||||
zip_path:String,
|
||||
append := ZIPPacker.ZipAppend.APPEND_CREATE
|
||||
append := ZIPPacker.ZipAppend.APPEND_CREATE,
|
||||
)->VFileAccess:
|
||||
var vfiler := VFileAccess.new()
|
||||
if not is_instance_valid(vfiler): return null
|
||||
@ -106,18 +112,23 @@ static func create_writeonly_zip_access(
|
||||
|
||||
## A single zip file accessor, but with write functions disabled.
|
||||
## TODO option to keep zips open, for OS to flag files as in-use.
|
||||
static func create_readonly_zip_access(zip_path:String)->VFileAccess:
|
||||
var vfiler := create_bulk_readonly_zip_access([zip_path])
|
||||
static func create_readonly_zip_access(
|
||||
zip_path:String, keep_open:bool = false,
|
||||
support_files:Dictionary[String,Callable] = VFileAccess.IMPORTS.DEFAULT_SUPPORTED_FILES
|
||||
)->VFileAccess:
|
||||
var vfiler := create_bulk_readonly_zip_access([zip_path], keep_open, support_files)
|
||||
vfiler._get_stuff = func()->Variant: return vfiler.get_stuff()[0]
|
||||
return vfiler
|
||||
|
||||
## A multi-zip readonly accessor. Allows for multiple zips to load with overrides.
|
||||
## TODO option to keep zips open, for OS to flag files as in-use.
|
||||
## [param if_missing_zip(String)] executes if a requested file is not found.
|
||||
static func create_bulk_readonly_zip_access(
|
||||
zip_paths:Array[String], keep_open:bool = false,
|
||||
if_missing_zip := func _ignore(_zip_path:String)->bool: return false
|
||||
support_files:Dictionary[String,Callable] = VFileAccess.IMPORTS.DEFAULT_SUPPORTED_FILES,
|
||||
if_missing_zip := func _ignore(_zip_path:String)->bool: return false,
|
||||
)->VFileAccess:
|
||||
var vfiler := VFileAccess.new("")
|
||||
var vfiler := VFileAccess.new("", support_files)
|
||||
|
||||
var readers:Array[ZIPReader] = []
|
||||
readers.assign(zip_paths.map(
|
||||
|
37
vfs.gd
37
vfs.gd
@ -2,6 +2,8 @@
|
||||
##
|
||||
## File access across different methods sucks.
|
||||
## Here's something to abstract that away and suck a little less.
|
||||
##
|
||||
## Written for Godot 4.4
|
||||
|
||||
class_name VFileAccess extends RefCounted
|
||||
|
||||
@ -14,24 +16,40 @@ const IMPORTS:GDScript = preload("vfs_loaders.gd")
|
||||
var root:String = ""
|
||||
|
||||
## Callable(bytes:PackedByteArray, [...])->Variant
|
||||
var supported_files:Dictionary[String,Callable] = IMPORTS.DEFAULT_SUPPORTED_FILES
|
||||
var supported_files:Dictionary[String,Callable] = {}
|
||||
|
||||
## Get any stuff we might be using in our closures.
|
||||
var _get_stuff:Callable = func()->Variant: return null
|
||||
## How can we write to a file?
|
||||
var _write_file:Callable = func(_abs_path:String, data:Variant)->Error: return ERR_CANT_OPEN
|
||||
var _write_file:Callable = func(_abs_path:String, data:Variant)->Error:
|
||||
return ERR_CANT_OPEN
|
||||
## How do we get bytes from this?
|
||||
var _get_buffer:Callable = func(_abs_path:String)->PackedByteArray: return PackedByteArray()
|
||||
var _get_buffer:Callable = func(_abs_path:String)->PackedByteArray:
|
||||
return PackedByteArray()
|
||||
## What determines if a file exists?
|
||||
var _file_exists:Callable = func(_abs_path:String)->bool: return false
|
||||
var _file_exists:Callable = func(_abs_path:String)->bool:
|
||||
return false
|
||||
## How do we get files at a subdirectory?
|
||||
var _get_files_at:Callable = func(_abs_path:String)->Array[String]: return []
|
||||
var _get_files_at:Callable = func(_abs_path:String)->Array[String]:
|
||||
return []
|
||||
## Shutdown code here.
|
||||
var _close:Callable = func()->void: pass
|
||||
|
||||
## Default
|
||||
func _init(root_dir:String = "./")->void:
|
||||
func _init(
|
||||
root_dir:String = "./",
|
||||
support_files = IMPORTS.DEFAULT_SUPPORTED_FILES
|
||||
)->void:
|
||||
self.root = root_dir
|
||||
self.supported_files = support_files.duplicate(true)
|
||||
|
||||
#region Static ops
|
||||
|
||||
static func copy_file(from:VFileAccess, to:VFileAccess)->bool:
|
||||
push_warning("Copy file unimplemented")
|
||||
return false
|
||||
|
||||
#endregion
|
||||
|
||||
## I never won awards for naming things correctly.
|
||||
func get_stuff()->Variant:
|
||||
@ -54,6 +72,13 @@ func get_supported_files()->Array[String]:
|
||||
return supported_files.keys()
|
||||
|
||||
|
||||
func load_supported_bulk(
|
||||
paths:Array[String],
|
||||
ext_override:String = ""
|
||||
)->Array[Variant]:
|
||||
return paths.map(load_supported.bind(ext_override))
|
||||
|
||||
|
||||
## Load a supported file. If [param path]'s extension matches a supported file,
|
||||
## it will use that loader and return whatever it's supposed to.
|
||||
## [param ext_override] allows for selecting a specific loader by key.
|
||||
|
@ -14,10 +14,15 @@ static func validate_loader(loader:Callable)->bool:
|
||||
static func load_bin(buffer:PackedByteArray)->PackedByteArray: return buffer
|
||||
|
||||
static func load_png(buffer:PackedByteArray)->Image:
|
||||
var img:Image
|
||||
var img := Image.new()
|
||||
img.load_png_from_buffer(buffer)
|
||||
return img
|
||||
|
||||
static func load_jpg(buffer:PackedByteArray)->Image:
|
||||
var img := Image.new()
|
||||
img.load_jpg_from_buffer(buffer)
|
||||
return img
|
||||
|
||||
static func load_mp3(buffer:PackedByteArray)->AudioStreamMP3:
|
||||
var sfx := AudioStreamMP3.new()
|
||||
sfx.data = buffer
|
||||
@ -32,19 +37,39 @@ static func load_txt(buffer:PackedByteArray)->String:
|
||||
var txt:String = buffer.get_string_from_utf8()
|
||||
return txt
|
||||
|
||||
static func load_wav(buffer:PackedByteArray)->AudioStreamWAV:
|
||||
var sfx := AudioStreamWAV.new()
|
||||
push_warning("""
|
||||
Cannot automatically parse wav because dynamic Godot parsing wav is currently manual and awful.
|
||||
Despite this, somehow Godot's editor importer seems to handle parsing wav just fine.""")
|
||||
return sfx
|
||||
|
||||
# can't use CONST since Callables are technically instanced dynamically
|
||||
|
||||
static var DEFAULT_SUPPORTED_FILES:Dictionary[String,Callable] = {
|
||||
"bin": load_bin,
|
||||
"txt": load_txt,
|
||||
"png": load_png,
|
||||
"jpg": load_jpg,
|
||||
"jpeg": load_jpg,
|
||||
"mp3": load_mp3,
|
||||
"ogg": load_ogg,
|
||||
"wav": load_wav,
|
||||
}
|
||||
|
||||
static var DATA_FILES:Dictionary[String,Callable] = {
|
||||
"bin": load_bin,
|
||||
"txt": load_txt,
|
||||
}
|
||||
|
||||
|
||||
static var IMAGE_FILES:Dictionary[String,Callable] = {
|
||||
"png": load_png,
|
||||
"jpg": load_jpg,
|
||||
"jpeg": load_jpg,
|
||||
}
|
||||
|
||||
static var AUDIO_FILES:Dictionary[String,Callable] = {
|
||||
"mp3": load_mp3,
|
||||
"ogg": load_ogg,
|
||||
"wav": load_wav,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user