GD-VirtualFileAccess/README.md
2024-12-03 13:06:22 -05:00

59 lines
3.9 KiB
Markdown

# GD-VirtualFileAccess
A small set of scripts to make loading data from various sources less awful.
## I don't get it.
Crop and Claw 2 is being written with moddability and engine-agnosticism in mind, using a combination of one or many zip archives and standard file access. There is a custom editor and runtime, both written as Godot games, which need to share some common file loading tricks. VFileAccess is written as a container class for abstracting away some file IO to allow for some brief and simple interfacing with uncertain sources of data.
For example, the runtime loads the base game package it expects, then mods, then allows for listening to a mods directory outside of zip archives. To do all this, we need to be able to load various file formats (png, ogg, specialized non-Godot formats, etc) into the engine and convert them to usable assets. This is obnoxious because loading and searching for files is different depending on if you are loading from directories or zip archives, and you have to manually cascade through zips and directories to find override assets. What's worse is more complications are added when new sources of file data could be encountered, such as streaming assets directly from a server. In order to reduce the pains, these scripts help build a dynamic virtual file loader.
## This thing's confusing.
No. I just hacked the code together quickly. Suppose you want to a folder or two...
```
# Open a folder
var vfiler:VFileAccess = VFileAccess.CREATE.create_file_access("/path/to/gamer/dir/")
var message:String = vfiler.load_supported("file.txt")
# Need to load from a zip? Reserve access with this...
var vfiler:VFileAccess = VFileAccess.CREATE.create_readonly_zip_access("./gamer.zip")
var message:String = vfiler.load_supported("file.txt")
# Need to load multiple zip files that can let you check each for a single file and return the first instance found?
var vfiler:VFileAccess = VFileAccess.CREATE.create_readonly_zip_access("./gamer.zip")
var message:String = vfiler.load_supported("file.txt")
```
# Adding supported file extensions.
There's two ways to obtain data from a file. `VFileAccess.get_buffer("filename.txt)` returns a `PackedByteArray` which you can parse yourself. `VFileAccess.load_supported("filename.txt")` will allow you to automatically receive a Variant based on the file extension. You can pass a second argument in with an override extension, such as ("filename.dat", "txt"), and receive a String as if it were just a .txt.
The power of this comes from how supported extensions are built. VFileAccess does not have any built-in context for file loading. Instead, you can define custom loaders and pass them in as arguments. By default, `VFileAccess.IMPORTS.DEFAULT_SUPPORTED_FILES` is used. This can be overridden when using the default creator functions, or passed in when writing your own. See `vfs_imports.gd` for options.
```
# allow importing all default supported files
var vfiler:VFileAccess = VFileAccess.CREATE.create_file_access("/path/to/gamer/dir/", VFileAccess.IMPORTS.DEFAULT_SUPPORTED_FILES)
# only allow importing images
var vfiler:VFileAccess = VFileAccess.CREATE.create_file_access("/path/to/gamer/dir/", VFileAccess.IMPORTS.IMAGE_FILES)
# only allow a custom format you defined elsewhere in your program
static func load_custom_file_format(buffer:PackedByteArray)->Object:
# parse bytes into a usable data type here.
return Object.new()
var my_imports:Dictionary[String,Callable] = {"cff", load_custom_file_format}
var vfiler:VFileAccess = VFileAccess.CREATE.create_file_access("/path/to/gamer/dir/", my_imports)
var result = vfiler.load_supported("file.cff")
```
This is extremely powerful since you have no need to modify the source code for VFileAccess nor its dependency scripts. You can freely pass your own importers or integrate other plugins for importers, as long as the import function you use takes a `PackedByteArray` and returns something of value.