diff --git a/addons/venus/src/manager.gd b/addons/venus/src/manager.gd
index 39f5bfa..cf8e966 100644
--- a/addons/venus/src/manager.gd
+++ b/addons/venus/src/manager.gd
@@ -32,8 +32,8 @@ var logger: CoreLoggerInstance
## Where Venus was installed to
var path: String = "addons/venus/src"
-var modules: Dictionary = { "standard": [ "splashes", "launcher" ], "sui": [] }
## Contains a list of all modules Venus provides
+var modules: Dictionary = { "standard": [ "splashes", "resourceloader", "launcher" ], "sui": [] }
func _init(core_new: Core) -> void:
name = "VenusManager"
diff --git a/addons/venus/src/modules/launcher.gd b/addons/venus/src/modules/launcher.gd
index c47111f..4a4e5f0 100644
--- a/addons/venus/src/modules/launcher.gd
+++ b/addons/venus/src/modules/launcher.gd
@@ -14,98 +14,17 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+
+## Communication layer between sos!launcher and games/applications.
+##
+## [b]Note: [i]This module is basically a stub and currently serves no purpose.[br]
+## It exists so games and applications that will be featured in the upcoming sos!launcher[br]
+## don't need to be refactored in one go but can be refactored over time instead.[/i][/b]
extends CoreBaseModule
-## Loads resources using [method @GDScript.load] ([code]== true[/code]) or using Venus' dynamic resource loading implementation ([code]== false[/code]).
-var config_load_native: bool = true
-## The path to load resources from. Use [code]res://[/code] if [member config_load_native] is set to [code]true[/code] or some other path if set to [code]false[/code].
-var config_load_path: String = "res://"
+## Reference to Venus' [param resourceloader] module.
+@onready var resourceloader: CoreBaseModule = core.get_custom_module("resourceloader")
-## A list of all format loaders
-var format_loaders: Dictionary = {
- # Godot file types
- ".tscn": PackedScene,
- ".tres": Theme,
- ".gd": GDScript,
-
- # Fonts
- ".ttf": FontFile,
- ".otf": FontFile,
- ".woff": FontFile,
- ".woff2": FontFile,
-
- # Images
- ".png": Image,
- ".jpg": Image,
- ".jpeg": Image,
- ".svg": Image,
- ".ktv": Image,
- ".tga": Image,
- ".webp": Image,
-}
-## The resource cache
-var resource_cache: Dictionary = {}
-
-## Loads a resource. Supports native and dynamic resource loading.
+## Loads a resource. Uses the [param resourceloader] module.
func load_resource(path: String) -> Resource:
- logger.diag("Loading resource located at " + path)
- var resource: Resource = null
-
- # Check if path exists
- if !FileAccess.file_exists(config_load_path + path):
- await logger.crash("Loading file failed: File does not exist")
-
- # Check if found in cache
- if resource_cache.has(path):
- return resource_cache[path]
-
- # Load resource
- if config_load_native: resource = load(config_load_path + path)
- else: resource = await _load_resource_handler(path)
-
- # Check if null
- await _load_resource_nullcheck(path, resource)
-
- # Add to cache
- resource_cache.merge({ path: resource })
-
- return resource
-
-## Handles dynamic resource loading using format loaders ([member format_loaders]).
-func _load_resource_handler(path: String) -> Resource:
- var resource: Resource = null
- var error: Error = Error.OK
- var type: Resource = null
-
- for extension in format_loaders:
- if resource.ends_with(extension):
- type = format_loaders[extension]
- break
-
- # Resource loading
- match(type):
- PackedScene, Theme, GDScript: resource = ResourceLoader.load(config_load_path + path, "", ResourceLoader.CACHE_MODE_IGNORE)
- FontFile:
- resource = FontFile.new()
- error = resource.load_dynamic_font(path)
- Image: resource = Image.load_from_file(path)
- _: await logger.crash("No format loader available for resource located at " + path)
-
- # Check if failed
- await _load_resource_nullcheck(path, resource)
-
- if error != Error.OK:
- await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: %error_string% (%error%)", { "path": path, "error": error, "error_string": error_string(error) }))
-
- # Resource processing
- match(type):
- PackedScene:
- if resource.can_instantiate(): resource = resource.instantiate()
- else: await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: Can't instantiate empty scene", { "path": path }))
-
- return resource
-
-## Simply crashes the app/game if the specified resource is [code]null[/code].
-func _load_resource_nullcheck(path: String, resource: Resource) -> void:
- if resource == null:
- await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: 'resource' is null", { "path": path }))
+ return resourceloader.load_resource(path)
diff --git a/addons/venus/src/modules/resourceloader.gd b/addons/venus/src/modules/resourceloader.gd
new file mode 100644
index 0000000..8a7047c
--- /dev/null
+++ b/addons/venus/src/modules/resourceloader.gd
@@ -0,0 +1,113 @@
+# STAROPENSOURCE CORE MODULES SOURCE FILE
+# Copyright (c) 2024 The StarOpenSource Project & Contributors
+# Licensed under the GNU Affero General Public License v3
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+## Loads resources natively (using [method @GDScript.load]) or dynamically (custom implementation) from user:// or the file system.
+extends CoreBaseModule
+
+## Loads resources using [method @GDScript.load] ([code]== true[/code]) or using Venus' dynamic resource loading implementation ([code]== false[/code]).
+var config_load_native: bool = true
+## The path to load resources from. Use [code]res://[/code] if [member config_load_native] is set to [code]true[/code] or some other path if set to [code]false[/code].
+var config_load_path: String = "res://"
+
+## A list of all format loaders
+var format_loaders: Dictionary = {
+ # Godot file types
+ ".tscn": PackedScene,
+ ".tres": Theme,
+ ".gd": GDScript,
+
+ # Fonts
+ ".ttf": FontFile,
+ ".otf": FontFile,
+ ".woff": FontFile,
+ ".woff2": FontFile,
+
+ # Images
+ ".png": Image,
+ ".jpg": Image,
+ ".jpeg": Image,
+ ".svg": Image,
+ ".ktv": Image,
+ ".tga": Image,
+ ".webp": Image,
+}
+## The resource cache
+var resource_cache: Dictionary = {}
+
+## Loads a resource. Supports native and dynamic resource loading.
+func load_resource(path: String) -> Resource:
+ logger.diag("Loading resource located at " + path)
+ var resource: Resource = null
+
+ # Check if path exists
+ if !FileAccess.file_exists(config_load_path + path):
+ await logger.crash("Loading file failed: File does not exist")
+
+ # Check if found in cache
+ if resource_cache.has(path):
+ return resource_cache[path]
+
+ # Load resource
+ if config_load_native: resource = load(config_load_path + path)
+ else: resource = await _load_resource_handler(path)
+
+ # Check if null
+ await _load_resource_nullcheck(path, resource)
+
+ # Add to cache
+ resource_cache.merge({ path: resource })
+
+ return resource
+
+## Handles dynamic resource loading using format loaders ([member format_loaders]).
+func _load_resource_handler(path: String) -> Resource:
+ var resource: Resource = null
+ var error: Error = Error.OK
+ var type: Resource = null
+
+ for extension in format_loaders:
+ if resource.ends_with(extension):
+ type = format_loaders[extension]
+ break
+
+ # Resource loading
+ match(type):
+ PackedScene, Theme, GDScript: resource = ResourceLoader.load(config_load_path + path, "", ResourceLoader.CACHE_MODE_IGNORE)
+ FontFile:
+ resource = FontFile.new()
+ error = resource.load_dynamic_font(path)
+ Image: resource = Image.load_from_file(path)
+ _: await logger.crash("No format loader available for resource located at " + path)
+
+ # Check if failed
+ await _load_resource_nullcheck(path, resource)
+
+ if error != Error.OK:
+ await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: %error_string% (%error%)", { "path": path, "error": error, "error_string": error_string(error) }))
+
+ # Resource processing
+ match(type):
+ PackedScene:
+ if resource.can_instantiate(): resource = resource.instantiate()
+ else: await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: Can't instantiate empty scene", { "path": path }))
+
+ return resource
+
+## Simply crashes the app/game if the specified resource is [code]null[/code].
+func _load_resource_nullcheck(path: String, resource: Resource) -> void:
+ if resource == null:
+ await logger.crash(core.misc.stringify_variables("Loading file located at %path% failed: 'resource' is null", { "path": path }))