diff --git a/addons/venus/src/manager.gd b/addons/venus/src/manager.gd
index 3a1d824..84eb572 100644
--- a/addons/venus/src/manager.gd
+++ b/addons/venus/src/manager.gd
@@ -21,7 +21,7 @@ var core: Core
var logger: CoreLoggerInstance
var path: String = "addons/venus/src"
-var modules: Dictionary = { "standard": [ "splashes" ], "sui": [] }
+var modules: Dictionary = { "standard": [ "splashes", "launcher" ], "sui": [] }
func _init(core_new: Core) -> void:
name = "Venus manager"
diff --git a/addons/venus/src/modules/launcher.gd b/addons/venus/src/modules/launcher.gd
new file mode 100644
index 0000000..c47111f
--- /dev/null
+++ b/addons/venus/src/modules/launcher.gd
@@ -0,0 +1,111 @@
+# 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 .
+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 }))
diff --git a/addons/venus/src/modules/splashes.gd b/addons/venus/src/modules/splashes.gd
index cbf2604..0b0a8d9 100644
--- a/addons/venus/src/modules/splashes.gd
+++ b/addons/venus/src/modules/splashes.gd
@@ -16,6 +16,7 @@
# along with this program. If not, see .
extends CoreBaseModule
+## Contains all categories and their splashes
var splashes: Dictionary = {}
# +++ module +++
diff --git a/project.godot b/project.godot
index 74321d5..765d922 100644
--- a/project.godot
+++ b/project.godot
@@ -22,10 +22,18 @@ boot_splash/bg_color=Color(0, 0, 0, 1)
window/size/viewport_width=1440
window/size/viewport_height=810
+[editor]
+
+export/convert_text_resources_to_binary=false
+
[editor_plugins]
enabled=PackedStringArray("res://addons/SUI/plugin.cfg", "res://addons/venus/plugin.cfg")
+[filesystem]
+
+import/blender/enabled=false
+
[rendering]
renderer/rendering_method="gl_compatibility"