diff --git a/addons/fmod/.gitignore b/addons/fmod/.gitignore
new file mode 100644
index 0000000..f723ea1
--- /dev/null
+++ b/addons/fmod/.gitignore
@@ -0,0 +1,5 @@
+*.dll
+*.a
+*.dylib
+*.so
+!libs/
\ No newline at end of file
diff --git a/addons/fmod/FmodEditorExportPluginProxy.gd b/addons/fmod/FmodEditorExportPluginProxy.gd
new file mode 100644
index 0000000..028845e
--- /dev/null
+++ b/addons/fmod/FmodEditorExportPluginProxy.gd
@@ -0,0 +1,5 @@
+# Here because gdextension does not support editor export plugin for now.
+# https://github.com/godotengine/godot/issues/80593
+@tool
+class_name FmodEditorExportPluginProxy
+extends FmodEditorExportPlugin
diff --git a/addons/fmod/FmodManager.gd b/addons/fmod/FmodManager.gd
new file mode 100644
index 0000000..81f4d5d
--- /dev/null
+++ b/addons/fmod/FmodManager.gd
@@ -0,0 +1,11 @@
+@tool
+extends Node
+
+func _ready():
+ process_mode = PROCESS_MODE_ALWAYS
+
+func _process(delta):
+ FmodServer.update()
+
+func _notification(what):
+ FmodServer.notification(what)
diff --git a/addons/fmod/FmodPlugin.gd b/addons/fmod/FmodPlugin.gd
new file mode 100644
index 0000000..901620f
--- /dev/null
+++ b/addons/fmod/FmodPlugin.gd
@@ -0,0 +1,76 @@
+@tool
+class_name FmodPlugin extends EditorPlugin
+
+
+const ADDON_PATH = "res://addons/fmod"
+
+
+@onready var theme = get_editor_interface().get_base_control().get_theme()
+
+
+var fmod_bank_explorer_window: PackedScene = load("res://addons/fmod/tool/ui/FmodBankExplorer.tscn")
+var bank_explorer: FmodBankExplorer
+var fmod_button: Button
+var export_plugin = FmodEditorExportPluginProxy.new()
+var emitter_inspector_plugin = FmodEmitterPropertyInspectorPlugin.new(self)
+var bank_loader_inspector_plugin = FmodBankLoaderPropertyInspectorPlugin.new(self)
+
+
+func _init():
+ add_autoload_singleton("FmodManager", "res://addons/fmod/FmodManager.gd")
+ fmod_button = Button.new()
+ fmod_button.icon = load("res://addons/fmod/icons/fmod_icon.svg")
+ fmod_button.text = "Fmod Explorer"
+
+ fmod_button.pressed.connect(_on_project_explorer_button_clicked)
+
+ add_control_to_container(EditorPlugin.CONTAINER_TOOLBAR, fmod_button)
+
+ bank_explorer = fmod_bank_explorer_window.instantiate()
+ bank_explorer.theme = get_editor_interface().get_base_control().get_theme()
+ bank_explorer.visible = false
+ add_child(bank_explorer)
+
+ add_inspector_plugin(bank_loader_inspector_plugin)
+ add_inspector_plugin(emitter_inspector_plugin)
+
+
+func _on_project_explorer_button_clicked():
+ bank_explorer.should_display_copy_buttons = true
+ bank_explorer.should_display_select_button = false
+ _popup_project_explorer(FmodBankExplorer.ToDisplayFlags.BANKS | FmodBankExplorer.ToDisplayFlags.BUSES | FmodBankExplorer.ToDisplayFlags.VCA | FmodBankExplorer.ToDisplayFlags.EVENTS)
+
+
+func open_project_explorer_events(on_select_callable: Callable):
+ _open_project_explorer(FmodBankExplorer.ToDisplayFlags.BANKS | FmodBankExplorer.ToDisplayFlags.EVENTS, on_select_callable)
+
+
+func open_project_explorer_bank(on_select_callable: Callable):
+ _open_project_explorer(FmodBankExplorer.ToDisplayFlags.BANKS, on_select_callable)
+
+
+func _open_project_explorer(display_flag: int, on_select_callable: Callable):
+ bank_explorer.should_display_copy_buttons = false
+ bank_explorer.should_display_select_button = true
+ _popup_project_explorer(display_flag, on_select_callable)
+
+
+func _popup_project_explorer(to_display: int, callable: Callable = Callable()):
+ if bank_explorer.visible == true:
+ bank_explorer.close_window()
+ return
+ bank_explorer.regenerate_tree(to_display, callable)
+ bank_explorer.popup_centered()
+
+
+func _enter_tree():
+ add_export_plugin(export_plugin)
+
+
+func _exit_tree():
+ remove_inspector_plugin(emitter_inspector_plugin)
+ remove_inspector_plugin(bank_loader_inspector_plugin)
+
+ remove_control_from_container(EditorPlugin.CONTAINER_TOOLBAR, fmod_button)
+ fmod_button.queue_free()
+ remove_export_plugin(export_plugin)
diff --git a/addons/fmod/fmod.gdextension b/addons/fmod/fmod.gdextension
new file mode 100644
index 0000000..bc101df
--- /dev/null
+++ b/addons/fmod/fmod.gdextension
@@ -0,0 +1,101 @@
+[configuration]
+entry_symbol = "fmod_library_init"
+compatibility_minimum = 4.2
+
+[libraries]
+windows.editor.x86_64 = "res://addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.dll"
+windows.debug.x86_64 = "res://addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.dll"
+windows.release.x86_64 = "res://addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.dll"
+macos.editor = "res://addons/fmod/libs/macos/libGodotFmod.macos.editor.framework"
+macos.debug = "res://addons/fmod/libs/macos/libGodotFmod.macos.template_debug.framework"
+macos.release = "res://addons/fmod/libs/macos/libGodotFmod.macos.template_release.framework"
+linux.editor.x86_64 = "res://addons/fmod/libs/linux/libGodotFmod.linux.editor.x86_64.so"
+linux.debug.x86_64 = "res://addons/fmod/libs/linux/libGodotFmod.linux.template_debug.x86_64.so"
+linux.release.x86_64 = "res://addons/fmod/libs/linux/libGodotFmod.linux.template_release.x86_64.so"
+android.debug.x86_64 = "res://addons/fmod/libs/android/x86_64/libGodotFmod.android.template_debug.x86_64.so"
+android.release.x86_64 = "res://addons/fmod/libs/android/x86_64/libGodotFmod.android.template_release.x86_64.so"
+android.debug.arm64 = "res://addons/fmod/libs/android/arm64/libGodotFmod.android.template_debug.arm64.so"
+android.release.arm64 = "res://addons/fmod/libs/android/arm64/libGodotFmod.android.template_release.arm64.so"
+ios.debug = "res://addons/fmod/libs/ios/libGodotFmod.ios.template_debug.universal.dylib"
+ios.release = "res://addons/fmod/libs/ios/libGodotFmod.ios.template_release.universal.dylib"
+
+[icons]
+FmodEventEmitter2D = "res://addons/fmod/icons/fmod_icon.svg"
+FmodEventEmitter3D = "res://addons/fmod/icons/fmod_icon.svg"
+FmodListener2D = "res://addons/fmod/icons/fmod_icon.svg"
+FmodListener3D = "res://addons/fmod/icons/fmod_icon.svg"
+FmodBankLoader = "res://addons/fmod/icons/fmod_icon.svg"
+
+[dependencies]
+windows.editor.x86_64 = {
+"libs/windows/fmodL.dll": "",
+"libs/windows/fmodstudioL.dll": ""
+}
+windows.debug.x86_64 = {
+"libs/windows/fmodL.dll": "",
+"libs/windows/fmodstudioL.dll": ""
+}
+windows.release.x86_64 = {
+"libs/windows/fmod.dll": "",
+"libs/windows/fmodstudio.dll": ""
+}
+linux.editor.x86_64 = {
+"libs/linux/libfmodL.so": "",
+"libs/linux/libfmodL.so.13": "",
+"libs/linux/libfmodL.so.13.12": "",
+"libs/linux/libfmodstudioL.so": "",
+"libs/linux/libfmodstudioL.so.13": "",
+"libs/linux/libfmodstudioL.so.13.12": ""
+}
+linux.debug.x86_64 = {
+"libs/linux/libfmodL.so": "",
+"libs/linux/libfmodL.so.13": "",
+"libs/linux/libfmodL.so.13.12": "",
+"libs/linux/libfmodstudioL.so": "",
+"libs/linux/libfmodstudioL.so.13": "",
+"libs/linux/libfmodstudioL.so.13.12": ""
+}
+linux.release.x86_64 = {
+"libs/linux/libfmod.so": "",
+"libs/linux/libfmod.so.13": "",
+"libs/linux/libfmod.so.13.12": "",
+"libs/linux/libfmodstudio.so": "",
+"libs/linux/libfmodstudio.so.13": "",
+"libs/linux/libfmodstudio.so.13.12": ""
+}
+macos.editor = {
+"libs/macos/libfmodL.dylib": "",
+"libs/macos/libfmodstudioL.dylib": ""
+}
+macos.debug = {
+"libs/macos/libfmodL.dylib": "",
+"libs/macos/libfmodstudioL.dylib": ""
+}
+macos.release = {
+"libs/macos/libfmod.dylib": "",
+"libs/macos/libfmodstudio.dylib": ""
+}
+android.debug.x86_64 = {
+"libs/android/x86_64/libfmodL.so": "",
+"libs/android/x86_64/libfmodstudioL.so": "",
+}
+android.release.x86_64 = {
+"libs/android/x86_64/libfmod.so": "",
+"libs/android/x86_64/libfmodstudio.so": "",
+}
+android.debug.arm64 = {
+"libs/android/arm64/libfmodL.so": "",
+"libs/android/arm64/libfmodstudioL.so": "",
+}
+android.release.arm64 = {
+"libs/android/arm64/libfmod.so": "",
+"libs/android/arm64/libfmodstudio.so": "",
+}
+ios.debug = {
+"libs/ios/libfmodL_iphoneos.a": "",
+"libs/ios/libfmodstudioL_iphoneos.a": ""
+}
+ios.release = {
+"libs/ios/libfmod_iphoneos.a": "",
+"libs/ios/libfmodstudio_iphoneos.a": ""
+}
\ No newline at end of file
diff --git a/addons/fmod/icons/bank_icon.svg b/addons/fmod/icons/bank_icon.svg
new file mode 100644
index 0000000..55643fd
--- /dev/null
+++ b/addons/fmod/icons/bank_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/bank_icon.svg.import b/addons/fmod/icons/bank_icon.svg.import
new file mode 100644
index 0000000..7959a54
--- /dev/null
+++ b/addons/fmod/icons/bank_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://o2chsr07oeqs"
+path="res://.godot/imported/bank_icon.svg-8de6c7bff09a67352e162b3c61b601de.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/bank_icon.svg"
+dest_files=["res://.godot/imported/bank_icon.svg-8de6c7bff09a67352e162b3c61b601de.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/bus_icon.svg b/addons/fmod/icons/bus_icon.svg
new file mode 100644
index 0000000..7aaedce
--- /dev/null
+++ b/addons/fmod/icons/bus_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/bus_icon.svg.import b/addons/fmod/icons/bus_icon.svg.import
new file mode 100644
index 0000000..904ef45
--- /dev/null
+++ b/addons/fmod/icons/bus_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dj1kag4aeg58t"
+path="res://.godot/imported/bus_icon.svg-f536ffd3c4893e79a9d3cb9a1b4fb50c.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/bus_icon.svg"
+dest_files=["res://.godot/imported/bus_icon.svg-f536ffd3c4893e79a9d3cb9a1b4fb50c.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/c_parameter_icon.svg b/addons/fmod/icons/c_parameter_icon.svg
new file mode 100644
index 0000000..ad230d3
--- /dev/null
+++ b/addons/fmod/icons/c_parameter_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/c_parameter_icon.svg.import b/addons/fmod/icons/c_parameter_icon.svg.import
new file mode 100644
index 0000000..09f585b
--- /dev/null
+++ b/addons/fmod/icons/c_parameter_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cmvcqfsl167te"
+path="res://.godot/imported/c_parameter_icon.svg-04115c2482c9a6874356ffdc09c41db0.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/c_parameter_icon.svg"
+dest_files=["res://.godot/imported/c_parameter_icon.svg-04115c2482c9a6874356ffdc09c41db0.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/d_parameter_icon.svg b/addons/fmod/icons/d_parameter_icon.svg
new file mode 100644
index 0000000..8a77bdc
--- /dev/null
+++ b/addons/fmod/icons/d_parameter_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/d_parameter_icon.svg.import b/addons/fmod/icons/d_parameter_icon.svg.import
new file mode 100644
index 0000000..d5ef9cf
--- /dev/null
+++ b/addons/fmod/icons/d_parameter_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dgna04txtwnyb"
+path="res://.godot/imported/d_parameter_icon.svg-d339e4e3f950ae8593b999ef51579136.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/d_parameter_icon.svg"
+dest_files=["res://.godot/imported/d_parameter_icon.svg-d339e4e3f950ae8593b999ef51579136.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/event_icon.svg b/addons/fmod/icons/event_icon.svg
new file mode 100644
index 0000000..779dd80
--- /dev/null
+++ b/addons/fmod/icons/event_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/event_icon.svg.import b/addons/fmod/icons/event_icon.svg.import
new file mode 100644
index 0000000..fd49058
--- /dev/null
+++ b/addons/fmod/icons/event_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cmpgmbn3y4svl"
+path="res://.godot/imported/event_icon.svg-4e6e2103d076f95b7bef82f079e433e6.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/event_icon.svg"
+dest_files=["res://.godot/imported/event_icon.svg-4e6e2103d076f95b7bef82f079e433e6.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/fmod_emitter.png b/addons/fmod/icons/fmod_emitter.png
new file mode 100644
index 0000000..ec46926
--- /dev/null
+++ b/addons/fmod/icons/fmod_emitter.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:37f711e44a8e20ef12568b84c78e0c93721c01065f36ea7544aef71ffec6ea7c
+size 535
diff --git a/addons/fmod/icons/fmod_emitter.png.import b/addons/fmod/icons/fmod_emitter.png.import
new file mode 100644
index 0000000..b12e6e8
--- /dev/null
+++ b/addons/fmod/icons/fmod_emitter.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cotpb54utx6d6"
+path="res://.godot/imported/fmod_emitter.png-6783a287e298e2a04e64a6deaa6fe366.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/fmod_emitter.png"
+dest_files=["res://.godot/imported/fmod_emitter.png-6783a287e298e2a04e64a6deaa6fe366.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/fmod/icons/fmod_icon.svg b/addons/fmod/icons/fmod_icon.svg
new file mode 100644
index 0000000..09edb0f
--- /dev/null
+++ b/addons/fmod/icons/fmod_icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/fmod_icon.svg.import b/addons/fmod/icons/fmod_icon.svg.import
new file mode 100644
index 0000000..cd78f8b
--- /dev/null
+++ b/addons/fmod/icons/fmod_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bwqq5q7kodk40"
+path="res://.godot/imported/fmod_icon.svg-cca7eb13231881fafaea0d598d407ea3.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/fmod_icon.svg"
+dest_files=["res://.godot/imported/fmod_icon.svg-cca7eb13231881fafaea0d598d407ea3.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/snapshot_icon.svg b/addons/fmod/icons/snapshot_icon.svg
new file mode 100644
index 0000000..68fa37c
--- /dev/null
+++ b/addons/fmod/icons/snapshot_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/snapshot_icon.svg.import b/addons/fmod/icons/snapshot_icon.svg.import
new file mode 100644
index 0000000..f06df82
--- /dev/null
+++ b/addons/fmod/icons/snapshot_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b4jxbh86chubi"
+path="res://.godot/imported/snapshot_icon.svg-7b517248819b3685844766808fbce2a5.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/snapshot_icon.svg"
+dest_files=["res://.godot/imported/snapshot_icon.svg-7b517248819b3685844766808fbce2a5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/icons/vca_icon.svg b/addons/fmod/icons/vca_icon.svg
new file mode 100644
index 0000000..157ae1e
--- /dev/null
+++ b/addons/fmod/icons/vca_icon.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/addons/fmod/icons/vca_icon.svg.import b/addons/fmod/icons/vca_icon.svg.import
new file mode 100644
index 0000000..fb5c289
--- /dev/null
+++ b/addons/fmod/icons/vca_icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://crsj4jjaeq87a"
+path="res://.godot/imported/vca_icon.svg-def43f27fe148a7a0b18c7dcaab20c79.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/fmod/icons/vca_icon.svg"
+dest_files=["res://.godot/imported/vca_icon.svg-def43f27fe148a7a0b18c7dcaab20c79.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/fmod/libs/android/arm64/CopyPast_Fmod_Libs_Here.txt b/addons/fmod/libs/android/arm64/CopyPast_Fmod_Libs_Here.txt
new file mode 100644
index 0000000..e69de29
diff --git a/addons/fmod/libs/iOS/CopyPast_Fmod_Libs_Here.txt b/addons/fmod/libs/iOS/CopyPast_Fmod_Libs_Here.txt
new file mode 100644
index 0000000..e69de29
diff --git a/addons/fmod/libs/linux/CopyPast_Fmod_Libs_Here.txt b/addons/fmod/libs/linux/CopyPast_Fmod_Libs_Here.txt
new file mode 100644
index 0000000..e69de29
diff --git a/addons/fmod/libs/linux/libfmod.so.13 b/addons/fmod/libs/linux/libfmod.so.13
new file mode 100644
index 0000000..c8b478c
Binary files /dev/null and b/addons/fmod/libs/linux/libfmod.so.13 differ
diff --git a/addons/fmod/libs/linux/libfmod.so.13.12 b/addons/fmod/libs/linux/libfmod.so.13.12
new file mode 100644
index 0000000..c8b478c
Binary files /dev/null and b/addons/fmod/libs/linux/libfmod.so.13.12 differ
diff --git a/addons/fmod/libs/linux/libfmodL.so.13 b/addons/fmod/libs/linux/libfmodL.so.13
new file mode 100644
index 0000000..bb6345f
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodL.so.13 differ
diff --git a/addons/fmod/libs/linux/libfmodL.so.13.12 b/addons/fmod/libs/linux/libfmodL.so.13.12
new file mode 100644
index 0000000..bb6345f
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodL.so.13.12 differ
diff --git a/addons/fmod/libs/linux/libfmodstudio.so.13 b/addons/fmod/libs/linux/libfmodstudio.so.13
new file mode 100644
index 0000000..d1a7f35
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodstudio.so.13 differ
diff --git a/addons/fmod/libs/linux/libfmodstudio.so.13.12 b/addons/fmod/libs/linux/libfmodstudio.so.13.12
new file mode 100644
index 0000000..d1a7f35
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodstudio.so.13.12 differ
diff --git a/addons/fmod/libs/linux/libfmodstudioL.so.13 b/addons/fmod/libs/linux/libfmodstudioL.so.13
new file mode 100644
index 0000000..d3a4b1d
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodstudioL.so.13 differ
diff --git a/addons/fmod/libs/linux/libfmodstudioL.so.13.12 b/addons/fmod/libs/linux/libfmodstudioL.so.13.12
new file mode 100644
index 0000000..d3a4b1d
Binary files /dev/null and b/addons/fmod/libs/linux/libfmodstudioL.so.13.12 differ
diff --git a/addons/fmod/libs/macos/CopyPast_Fmod_Libs_Here.txt b/addons/fmod/libs/macos/CopyPast_Fmod_Libs_Here.txt
new file mode 100644
index 0000000..e69de29
diff --git a/addons/fmod/libs/macos/libGodotFmod.macos.editor.framework/libGodotFmod.macos.editor b/addons/fmod/libs/macos/libGodotFmod.macos.editor.framework/libGodotFmod.macos.editor
new file mode 100644
index 0000000..fa3a3c9
Binary files /dev/null and b/addons/fmod/libs/macos/libGodotFmod.macos.editor.framework/libGodotFmod.macos.editor differ
diff --git a/addons/fmod/libs/macos/libGodotFmod.macos.template_debug.framework/libGodotFmod.macos.template_debug b/addons/fmod/libs/macos/libGodotFmod.macos.template_debug.framework/libGodotFmod.macos.template_debug
new file mode 100755
index 0000000..a21e152
Binary files /dev/null and b/addons/fmod/libs/macos/libGodotFmod.macos.template_debug.framework/libGodotFmod.macos.template_debug differ
diff --git a/addons/fmod/libs/macos/libGodotFmod.macos.template_release.framework/libGodotFmod.macos.template_release b/addons/fmod/libs/macos/libGodotFmod.macos.template_release.framework/libGodotFmod.macos.template_release
new file mode 100644
index 0000000..46d8421
Binary files /dev/null and b/addons/fmod/libs/macos/libGodotFmod.macos.template_release.framework/libGodotFmod.macos.template_release differ
diff --git a/addons/fmod/libs/windows/CopyPast_Fmod_Libs_Here.txt b/addons/fmod/libs/windows/CopyPast_Fmod_Libs_Here.txt
new file mode 100644
index 0000000..e69de29
diff --git a/addons/fmod/libs/windows/fmodL_vc.lib b/addons/fmod/libs/windows/fmodL_vc.lib
new file mode 100644
index 0000000..554d0c8
Binary files /dev/null and b/addons/fmod/libs/windows/fmodL_vc.lib differ
diff --git a/addons/fmod/libs/windows/fmod_vc.lib b/addons/fmod/libs/windows/fmod_vc.lib
new file mode 100644
index 0000000..f708333
Binary files /dev/null and b/addons/fmod/libs/windows/fmod_vc.lib differ
diff --git a/addons/fmod/libs/windows/fmodstudioL_vc.lib b/addons/fmod/libs/windows/fmodstudioL_vc.lib
new file mode 100644
index 0000000..8bd00b7
Binary files /dev/null and b/addons/fmod/libs/windows/fmodstudioL_vc.lib differ
diff --git a/addons/fmod/libs/windows/fmodstudio_vc.lib b/addons/fmod/libs/windows/fmodstudio_vc.lib
new file mode 100644
index 0000000..1bb00b6
Binary files /dev/null and b/addons/fmod/libs/windows/fmodstudio_vc.lib differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.exp b/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.exp
new file mode 100644
index 0000000..86d7163
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.exp differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.lib b/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.lib
new file mode 100644
index 0000000..e649ebe
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.lib differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.exp b/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.exp
new file mode 100644
index 0000000..92c96ab
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.exp differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.lib b/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.lib
new file mode 100644
index 0000000..b64820e
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.template_debug.x86_64.lib differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.exp b/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.exp
new file mode 100644
index 0000000..9945682
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.exp differ
diff --git a/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.lib b/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.lib
new file mode 100644
index 0000000..0791660
Binary files /dev/null and b/addons/fmod/libs/windows/libGodotFmod.windows.template_release.x86_64.lib differ
diff --git a/addons/fmod/plugin.cfg b/addons/fmod/plugin.cfg
new file mode 100644
index 0000000..d0144cf
--- /dev/null
+++ b/addons/fmod/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="FMOD GDExtension"
+description="FMOD GDExtension for Godot Engine 4.0"
+author="Utopia-rise, Tristan Grespinet, Pierre-Thomas Meisels"
+version="4.0.0"
+script="FmodPlugin.gd"
diff --git a/addons/fmod/tool/inspectors/FmodBankLoaderPropertyInspectorPlugin.gd b/addons/fmod/tool/inspectors/FmodBankLoaderPropertyInspectorPlugin.gd
new file mode 100644
index 0000000..94f2880
--- /dev/null
+++ b/addons/fmod/tool/inspectors/FmodBankLoaderPropertyInspectorPlugin.gd
@@ -0,0 +1,20 @@
+class_name FmodBankLoaderPropertyInspectorPlugin extends EditorInspectorPlugin
+
+static var bank_icon = load("res://addons/fmod/icons/bank_icon.svg")
+
+var _open_project_explorer_callable: Callable
+
+func _init(plugin: FmodPlugin):
+ _open_project_explorer_callable = plugin.open_project_explorer_bank
+
+func _can_handle(object: Object):
+ return object is FmodBankLoader
+
+func _parse_property(object: Object, type: Variant.Type, name: String, hint_type: PropertyHint, hint_string: String, usage_flags: int, wide: bool):
+ return name == "bank_paths"
+
+func _parse_category(object: Object, category: String):
+ if category != "FmodBankLoader":
+ return
+ var editor_property := FmodBankPathEditorProperty.new(_open_project_explorer_callable)
+ add_property_editor_for_multiple_properties("Fmod banks", PackedStringArray(["bank_paths"]), editor_property)
diff --git a/addons/fmod/tool/inspectors/FmodEmitterPropertyInspectorPlugin.gd b/addons/fmod/tool/inspectors/FmodEmitterPropertyInspectorPlugin.gd
new file mode 100644
index 0000000..bc58619
--- /dev/null
+++ b/addons/fmod/tool/inspectors/FmodEmitterPropertyInspectorPlugin.gd
@@ -0,0 +1,21 @@
+class_name FmodEmitterPropertyInspectorPlugin extends EditorInspectorPlugin
+
+var _open_project_explorer_callable: Callable
+var _event_editor_property_scene: PackedScene = load("res://addons/fmod/tool/property_editors/FmodEventEditorProperty.tscn")
+
+func _init(plugin: FmodPlugin):
+ _open_project_explorer_callable = plugin.open_project_explorer_events
+
+func _can_handle(object: Object):
+ return object is FmodEventEmitter2D or \
+ object is FmodEventEmitter3D
+
+func _parse_property(object: Object, type: Variant.Type, name: String, hint_type: PropertyHint, hint_string: String, usage_flags: int, wide: bool):
+ return name == "event_name" || name == "event_guid"
+
+func _parse_category(object: Object, category: String):
+ if category != "FmodEventEmitter2D" and category != "FmodEventEmitter3D":
+ return
+ var editor_property := _event_editor_property_scene.instantiate()
+ editor_property.initialize(_open_project_explorer_callable, "event_name", "event_guid")
+ add_property_editor_for_multiple_properties("Fmod event", PackedStringArray(["event_name", "event_guid"]), editor_property)
diff --git a/addons/fmod/tool/property_editors/FmodBankPathEditorProperty.gd b/addons/fmod/tool/property_editors/FmodBankPathEditorProperty.gd
new file mode 100644
index 0000000..64a784e
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodBankPathEditorProperty.gd
@@ -0,0 +1,95 @@
+class_name FmodBankPathEditorProperty extends EditorProperty
+
+var path_property_name := "bank_paths"
+
+var ui: Control
+var last_selected_index := -1
+
+func _init(open_project_explorer_callable: Callable):
+ ui = load("res://addons/fmod/tool/property_editors/FmodBankPathsPropertyEditorUi.tscn").instantiate()
+ add_child(ui)
+ var add_button: Button = ui.get_node("%AddButton")
+
+ var open_project_explorer_event = func open_project_explorer_event():
+ open_project_explorer_callable.call(self._set_path_and_guid)
+ add_button.pressed.connect(open_project_explorer_event)
+
+ var remove_button: Button = ui.get_node("%RemoveButton")
+ remove_button.pressed.connect(_on_remove_button)
+
+ var manual_add_button: Button = ui.get_node("%ManualAddButton")
+ manual_add_button.pressed.connect(_on_manual_add_button)
+
+ var up_button: Button = ui.get_node("%UpButton")
+ up_button.pressed.connect(_on_up_button)
+
+ var down_button: Button = ui.get_node("%DownButton")
+ down_button.pressed.connect(_on_down_button)
+
+func _update_property():
+ var bank_list: ItemList = ui.get_node("%BankList")
+ bank_list.clear()
+ var bank_paths: Array = get_edited_object()[path_property_name]
+ for path in bank_paths:
+ bank_list.add_item(path)
+
+ if last_selected_index == -1:
+ return
+ bank_list.select(last_selected_index)
+ last_selected_index = -1
+
+func _set_path_and_guid(path: String, _cancel: String):
+ var bank_list: ItemList = ui.get_node("%BankList")
+ var current_bank_paths: Array = get_edited_object()[path_property_name]
+
+ if current_bank_paths.has(path):
+ return
+
+ var bank_paths := Array(current_bank_paths)
+ bank_paths.append(path)
+ emit_changed(path_property_name, bank_paths)
+
+func _on_remove_button():
+ var bank_list: ItemList = ui.get_node("%BankList")
+ var current_bank_paths: Array = get_edited_object()[path_property_name]
+ var bank_paths := Array(current_bank_paths)
+ var selected_items_indexes: PackedInt32Array = bank_list.get_selected_items()
+ if selected_items_indexes.is_empty():
+ return
+ var item = bank_list.get_item_text(selected_items_indexes[0])
+ var in_list_index = bank_paths.find(item)
+ bank_paths.remove_at(in_list_index)
+ last_selected_index = in_list_index if in_list_index < bank_paths.size() else in_list_index - 1
+ emit_changed(path_property_name, bank_paths)
+
+func _on_manual_add_button():
+ var manual_add_line_edit: LineEdit = ui.get_node("%ManualAddLineEdit")
+ var to_add: String = manual_add_line_edit.text
+ if not to_add.begins_with("res://") || not to_add.ends_with(".bank"):
+ return
+ _set_path_and_guid(to_add, "")
+ manual_add_line_edit.text = ""
+
+func _on_up_button():
+ _on_move_button(false)
+
+func _on_down_button():
+ _on_move_button(true)
+
+func _on_move_button(is_down: bool):
+ var bank_list: ItemList = ui.get_node("%BankList")
+ var current_bank_paths: Array = get_edited_object()[path_property_name]
+ var bank_paths := Array(current_bank_paths)
+ var selected_items_indexes: PackedInt32Array = bank_list.get_selected_items()
+ if selected_items_indexes.is_empty():
+ return
+ var item = bank_list.get_item_text(selected_items_indexes[0])
+ var in_list_index = bank_paths.find(item)
+ var boundary = current_bank_paths.size() - 1 if is_down else 0
+ if in_list_index == boundary:
+ return
+ var new_index = in_list_index + 1 if is_down else in_list_index - 1
+ bank_paths.remove_at(in_list_index)
+ bank_paths.insert(new_index, item)
+ last_selected_index = new_index
+ emit_changed(path_property_name, bank_paths)
diff --git a/addons/fmod/tool/property_editors/FmodBankPathsPropertyEditorUi.tscn b/addons/fmod/tool/property_editors/FmodBankPathsPropertyEditorUi.tscn
new file mode 100644
index 0000000..3ed97a1
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodBankPathsPropertyEditorUi.tscn
@@ -0,0 +1,60 @@
+[gd_scene load_steps=2 format=3 uid="uid://dtlwk8wdeal3h"]
+
+[ext_resource type="Texture2D" uid="uid://bkqdeaojmbbaj" path="res://addons/fmod/icons/bank_icon.svg" id="1_11c48"]
+
+[node name="FmodBankPathsPropertyEditorUi" type="VBoxContainer"]
+offset_right = 92.0
+offset_bottom = 43.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="HBoxContainer" type="HBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="AddButton" type="Button" parent="HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+text = "+"
+icon = ExtResource("1_11c48")
+icon_alignment = 2
+
+[node name="RemoveButton" type="Button" parent="HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+text = "-"
+
+[node name="VSeparator" type="VSeparator" parent="HBoxContainer"]
+layout_mode = 2
+
+[node name="UpButton" type="Button" parent="HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+text = "↑"
+
+[node name="DownButton" type="Button" parent="HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+text = "↓"
+
+[node name="BankList" type="ItemList" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_vertical = 3
+auto_height = true
+
+[node name="HBoxContainer2" type="HBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="ManualAddLineEdit" type="LineEdit" parent="HBoxContainer2"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="ManualAddButton" type="Button" parent="HBoxContainer2"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "+"
diff --git a/addons/fmod/tool/property_editors/FmodEventEditorProperty.gd b/addons/fmod/tool/property_editors/FmodEventEditorProperty.gd
new file mode 100644
index 0000000..bd69ac8
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodEventEditorProperty.gd
@@ -0,0 +1,80 @@
+@tool class_name FmodEventEditorProperty extends FmodPathEditorProperty
+
+
+static var EVENT_PARAMETER_PREFIX_FOR_PROPERTIES = "event_parameter"
+
+var former_event_description: FmodEventDescription
+
+func _update_property():
+ super()
+ if get_edited_object().event_name == "":
+ return
+ _update_parameters()
+ var event_description: FmodEventDescription = FmodServer.get_event_from_guid(get_edited_object().event_guid)
+ former_event_description = event_description
+
+func _set_path_and_guid(path: String, guid: String):
+ super(path, guid)
+ if get_edited_object().event_name == "":
+ return
+ _update_parameters()
+ former_event_description = FmodServer.get_event_from_guid(get_edited_object().event_guid)
+
+func _update_parameters():
+ var event_description: FmodEventDescription = FmodServer.get_event_from_guid(get_edited_object().event_guid)
+
+ if former_event_description != null and event_description != former_event_description:
+ get_edited_object().tool_remove_all_parameters()
+
+ var map_to_property_name = func map_to_property_name(dict: Dictionary):
+ return dict["name"]
+ var filter_fmod_parameter_property = func filter_fmod_parameter_property(parameter_name: String):
+ return parameter_name.begins_with(EVENT_PARAMETER_PREFIX_FOR_PROPERTIES)
+
+ var filter_property_id = func filter_property_id(property: String):
+ return property.ends_with("/id")
+ var existing_property_ids = get_edited_object().get_property_list().map(map_to_property_name).filter(filter_fmod_parameter_property).filter(filter_property_id)
+
+ var map_property_name_to_parameter_name = func map_property_name_to_parameter_name(parameter: String):
+ return parameter.split("/")[1]
+ var existing_parameter_names = existing_property_ids.map(map_property_name_to_parameter_name)
+
+ var map_property_id_to_parameter_id_value = func map_property_id_to_parameter_id_value(property: String):
+ return get_edited_object()[property]
+ var existing_parameter_ids = existing_property_ids.map(map_property_id_to_parameter_id_value)
+
+ var property_matching = existing_parameter_ids.map(func(id): return false)
+
+ for param in event_description.get_parameters():
+ var parameter_name = param.get_name()
+ var parameter_id_param = "%s/%s/id" % [EVENT_PARAMETER_PREFIX_FOR_PROPERTIES, parameter_name]
+ var parameter_value_param = "%s/%s/value" % [EVENT_PARAMETER_PREFIX_FOR_PROPERTIES, parameter_name]
+ var parameter_min_value_param = "%s/%s/min_value" % [EVENT_PARAMETER_PREFIX_FOR_PROPERTIES, parameter_name]
+ var parameter_max_value_param = "%s/%s/max_value" % [EVENT_PARAMETER_PREFIX_FOR_PROPERTIES, parameter_name]
+ var parameter_default_value_param = "%s/%s/default_value" % [EVENT_PARAMETER_PREFIX_FOR_PROPERTIES, parameter_name]
+
+ var existing_property_name_index = existing_property_ids.find(parameter_id_param)
+ var are_properties_already_in_node = existing_property_name_index != -1
+
+ var parameter_id = param.get_id()
+
+ if are_properties_already_in_node:
+ property_matching[existing_property_name_index] = existing_parameter_ids[existing_property_name_index] == parameter_id
+
+ if not are_properties_already_in_node or get_edited_object()[parameter_id_param] == null:
+ get_edited_object()[parameter_id_param] = parameter_id
+ if not are_properties_already_in_node or get_edited_object()[parameter_value_param] == null:
+ get_edited_object()[parameter_value_param] = param.get_default_value()
+ if not are_properties_already_in_node or get_edited_object()[parameter_min_value_param] == null:
+ get_edited_object()[parameter_min_value_param] = param.get_minimum()
+ if not are_properties_already_in_node or get_edited_object()[parameter_max_value_param] == null:
+ get_edited_object()[parameter_max_value_param] = param.get_maximum()
+ if not are_properties_already_in_node or get_edited_object()[parameter_default_value_param] == null:
+ get_edited_object()[parameter_default_value_param] = param.get_default_value()
+
+ for i in property_matching.size():
+ if not property_matching[i]:
+ get_edited_object().tool_remove_parameter(existing_parameter_ids[i])
+ pass
+
+ get_edited_object().notify_property_list_changed()
diff --git a/addons/fmod/tool/property_editors/FmodEventEditorProperty.tscn b/addons/fmod/tool/property_editors/FmodEventEditorProperty.tscn
new file mode 100644
index 0000000..71f2070
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodEventEditorProperty.tscn
@@ -0,0 +1,7 @@
+[gd_scene load_steps=3 format=3 uid="uid://cowfthogh1n7i"]
+
+[ext_resource type="PackedScene" uid="uid://cujo3xq0erren" path="res://addons/fmod/tool/property_editors/FmodPathEditorProperty.tscn" id="1_xvpec"]
+[ext_resource type="Script" path="res://addons/fmod/tool/property_editors/FmodEventEditorProperty.gd" id="2_nkhkm"]
+
+[node name="FmodEventEditorProperty" instance=ExtResource("1_xvpec")]
+script = ExtResource("2_nkhkm")
diff --git a/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.gd b/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.gd
new file mode 100644
index 0000000..d0bc90a
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.gd
@@ -0,0 +1,10 @@
+@tool class_name FmodGuidAndPathPropertyEditorUi extends HBoxContainer
+
+
+func set_callables(window_callable: Callable, path_callable: Callable, guid_callable: Callable):
+ %EventExplorerButton.pressed.connect(window_callable)
+ %PathLineEdit.text_changed.connect(path_callable)
+ %GuidLineEdit.text_changed.connect(guid_callable)
+
+func set_icon(icon: Texture2D):
+ %EventExplorerButton.icon = icon
diff --git a/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.tscn b/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.tscn
new file mode 100644
index 0000000..f912a6e
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.tscn
@@ -0,0 +1,28 @@
+[gd_scene load_steps=3 format=3 uid="uid://hy04frgfhtgu"]
+
+[ext_resource type="Script" path="res://addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.gd" id="1_eao7t"]
+[ext_resource type="Texture2D" uid="uid://dca7c6jnnpl0f" path="res://addons/fmod/icons/event_icon.svg" id="1_kuu6i"]
+
+[node name="FmodGuidAndPathPropertyEditorUi" type="HBoxContainer"]
+offset_right = 1152.0
+offset_bottom = 66.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource("1_eao7t")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="PathLineEdit" type="LineEdit" parent="VBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+
+[node name="GuidLineEdit" type="LineEdit" parent="VBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+
+[node name="EventExplorerButton" type="Button" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+icon = ExtResource("1_kuu6i")
diff --git a/addons/fmod/tool/property_editors/FmodPathEditorProperty.gd b/addons/fmod/tool/property_editors/FmodPathEditorProperty.gd
new file mode 100644
index 0000000..f6ba7d0
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodPathEditorProperty.gd
@@ -0,0 +1,40 @@
+@tool class_name FmodPathEditorProperty extends EditorProperty
+
+var ui: Control
+var guid_property: String
+var path_property: String
+var regex := RegEx.new()
+
+var default_line_edit_modulate: Color
+
+func initialize(open_project_explorer_callable: Callable, path_prop: String, guid_prop: String):
+ regex.compile("{\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}}")
+ guid_property = guid_prop
+ path_property = path_prop
+ var guid_and_path_ui: FmodGuidAndPathPropertyEditorUi = %FmodGuidAndPathPropertyEditorUi
+
+ default_line_edit_modulate = guid_and_path_ui.get_node("%GuidLineEdit").modulate
+
+ var open_project_explorer_event = func open_project_explorer_event():
+ open_project_explorer_callable.call(self._set_path_and_guid)
+ guid_and_path_ui.set_callables(open_project_explorer_event, _set_path, _set_guid)
+
+func _update_property():
+ var guid_and_path_ui = %FmodGuidAndPathPropertyEditorUi
+ guid_and_path_ui.get_node("%PathLineEdit").text = get_edited_object()[path_property]
+ guid_and_path_ui.get_node("%GuidLineEdit").text = get_edited_object()[guid_property]
+
+func _set_path(path: String):
+ emit_changed(path_property, path)
+
+func _set_guid(guid: String):
+ var line_edit := %FmodGuidAndPathPropertyEditorUi.get_node("%GuidLineEdit") as LineEdit
+ if not regex.search(guid):
+ line_edit.modulate = Color.RED
+ return
+ line_edit.modulate = default_line_edit_modulate
+ emit_changed(guid_property, guid)
+
+func _set_path_and_guid(path: String, guid: String):
+ _set_path(path)
+ _set_guid(guid)
diff --git a/addons/fmod/tool/property_editors/FmodPathEditorProperty.tscn b/addons/fmod/tool/property_editors/FmodPathEditorProperty.tscn
new file mode 100644
index 0000000..4a8d0c2
--- /dev/null
+++ b/addons/fmod/tool/property_editors/FmodPathEditorProperty.tscn
@@ -0,0 +1,14 @@
+[gd_scene load_steps=3 format=3 uid="uid://cujo3xq0erren"]
+
+[ext_resource type="Script" path="res://addons/fmod/tool/property_editors/FmodPathEditorProperty.gd" id="1_4e4vx"]
+[ext_resource type="PackedScene" uid="uid://hy04frgfhtgu" path="res://addons/fmod/tool/property_editors/FmodGuidAndPathPropertyEditorUi.tscn" id="2_nvtqg"]
+
+[node name="FmodPathEditorProperty" type="EditorProperty"]
+script = ExtResource("1_4e4vx")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="FmodGuidAndPathPropertyEditorUi" parent="VBoxContainer" instance=ExtResource("2_nvtqg")]
+unique_name_in_owner = true
+layout_mode = 2
diff --git a/addons/fmod/tool/ui/EventParametersDisplay.gd b/addons/fmod/tool/ui/EventParametersDisplay.gd
new file mode 100644
index 0000000..6c2410c
--- /dev/null
+++ b/addons/fmod/tool/ui/EventParametersDisplay.gd
@@ -0,0 +1,21 @@
+@tool class_name EventParametersDisplay extends ScrollContainer
+
+static var parameter_display_scene: PackedScene = load("res://addons/fmod/tool/ui/ParameterDisplay.tscn")
+
+func set_fmod_event(event: FmodEventDescription) -> bool: # returns false if there were no parameters
+ for child in %ParameterDisplaysContainer.get_children():
+ %ParameterDisplaysContainer.remove_child(child)
+ child.queue_free()
+
+ var event_parameters: Array = event.get_parameters()
+ if event_parameters:
+ show()
+ for parameter : FmodParameterDescription in event_parameters:
+ var parameter_display: ParameterDisplay = parameter_display_scene.instantiate()
+ parameter_display.set_parameter(parameter)
+ if %ParameterDisplaysContainer.get_child_count() > 0:
+ %ParameterDisplaysContainer.add_child(HSeparator.new())
+ %ParameterDisplaysContainer.add_child(parameter_display)
+ return true # we had parameters to show!
+ else:
+ return false # no parameters to visualise
diff --git a/addons/fmod/tool/ui/EventParametersDisplay.tscn b/addons/fmod/tool/ui/EventParametersDisplay.tscn
new file mode 100644
index 0000000..e7a2bb3
--- /dev/null
+++ b/addons/fmod/tool/ui/EventParametersDisplay.tscn
@@ -0,0 +1,19 @@
+[gd_scene load_steps=2 format=3 uid="uid://cppeyr1ke5wre"]
+
+[ext_resource type="Script" path="res://addons/fmod/tool/ui/EventParametersDisplay.gd" id="1_2l58q"]
+
+[node name="EventParametersDisplay" type="ScrollContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource("1_2l58q")
+
+[node name="ParameterDisplaysContainer" type="VBoxContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
diff --git a/addons/fmod/tool/ui/EventParametersWindow.tscn b/addons/fmod/tool/ui/EventParametersWindow.tscn
new file mode 100644
index 0000000..2d6520b
--- /dev/null
+++ b/addons/fmod/tool/ui/EventParametersWindow.tscn
@@ -0,0 +1,7 @@
+[gd_scene load_steps=2 format=3 uid="uid://skp8awewyl85"]
+
+[ext_resource type="PackedScene" uid="uid://cppeyr1ke5wre" path="res://addons/fmod/tool/ui/EventParametersDisplay.tscn" id="1_clkxg"]
+
+[node name="EventParametersWindow" type="Window"]
+
+[node name="EventParametersDisplay" parent="." instance=ExtResource("1_clkxg")]
diff --git a/addons/fmod/tool/ui/EventPlayControls.gd b/addons/fmod/tool/ui/EventPlayControls.gd
new file mode 100644
index 0000000..7bf580c
--- /dev/null
+++ b/addons/fmod/tool/ui/EventPlayControls.gd
@@ -0,0 +1,52 @@
+@tool
+extends PanelContainer
+
+@export var play_button : Button
+@export var stop_button : Button
+@export var fade_out_toggle : CheckButton
+var event_instance : FmodEvent
+
+func _ready() -> void:
+ hide()
+ play_button.icon = EditorInterface.get_editor_theme().get_icon("Play", "EditorIcons")
+ stop_button.icon = EditorInterface.get_editor_theme().get_icon("Stop", "EditorIcons")
+ play_button.pressed.connect(on_play_button_pressed)
+ stop_button.pressed.connect(on_stop_button_pressed)
+
+
+func set_fmod_event(_event_description : FmodEventDescription) -> void:
+ stop_and_release_instance() # always stop and release if a previous one is active
+ event_instance = FmodServer.create_event_instance_with_guid(_event_description.get_guid())
+ show()
+
+
+func play_event() -> void:
+ if event_instance:
+ event_instance.start()
+
+
+func stop_event() -> void:
+ if event_instance:
+ var stop_mode : int = FmodServer.FMOD_STUDIO_STOP_IMMEDIATE
+ if fade_out_toggle.button_pressed:
+ stop_mode = FmodServer.FMOD_STUDIO_STOP_ALLOWFADEOUT
+
+ event_instance.stop(stop_mode)
+
+
+func _exit_tree() -> void:
+ stop_and_release_instance()
+
+
+func stop_and_release_instance() -> void:
+ if event_instance:
+ event_instance.stop(0)
+ event_instance.release()
+
+
+func on_play_button_pressed() -> void:
+ play_event()
+
+
+func on_stop_button_pressed() -> void:
+ stop_event()
diff --git a/addons/fmod/tool/ui/FmodBankExplorer.gd b/addons/fmod/tool/ui/FmodBankExplorer.gd
new file mode 100644
index 0000000..415d495
--- /dev/null
+++ b/addons/fmod/tool/ui/FmodBankExplorer.gd
@@ -0,0 +1,256 @@
+@tool class_name FmodBankExplorer extends Window
+
+enum ToDisplayFlags {
+ BANKS = 1,
+ BUSES = 2,
+ VCA = 4,
+ EVENTS = 8
+}
+
+static var _fmod_icon = load("res://addons/fmod/icons/fmod_icon.svg")
+static var _vca_icon = load("res://addons/fmod/icons/vca_icon.svg")
+static var _bank_icon = load("res://addons/fmod/icons/bank_icon.svg")
+static var _event_icon = load("res://addons/fmod/icons/event_icon.svg")
+static var _bus_icon = load("res://addons/fmod/icons/bus_icon.svg")
+static var _snapshot_icon = load("res://addons/fmod/icons/snapshot_icon.svg")
+
+signal emit_path_and_guid(path: String, guid: String)
+
+var tree: Tree
+@onready var copy_path_button := %PathLabel.get_child(0)
+@onready var copy_guid_button := %GuidLabel.get_child(0)
+
+var should_display_copy_buttons = true
+var should_display_select_button = false
+
+var _current_select_callable: Callable
+
+var base_color: Color
+var contrast: float
+var background_color: Color
+
+func _ready():
+ var main_window_size = get_parent().get_window().size
+ size = main_window_size * 0.5
+
+ var copy_texture : Texture = EditorInterface.get_editor_theme().get_icon("ActionCopy", "EditorIcons")
+ copy_guid_button.icon = copy_texture
+ copy_path_button.icon = copy_texture
+ copy_guid_button.visible = false
+ copy_path_button.visible = false
+ copy_path_button.pressed.connect(_on_copy_path_button)
+ copy_guid_button.pressed.connect(_on_copy_guid_button)
+
+ var emit_path_and_guid_callable = func emit_path_and_guid_callable():
+ var selected_item = tree.get_selected()
+ if selected_item == null:
+ return
+ var selected_fmod_element = selected_item.get_metadata(0)
+ if selected_fmod_element == null:
+ return
+
+ var path = selected_fmod_element.get_godot_res_path() if selected_fmod_element is FmodBank else selected_fmod_element.get_path()
+ emit_path_and_guid.emit(path, selected_fmod_element.get_guid())
+ %SelectButton.pressed.connect(emit_path_and_guid_callable)
+ %SelectButton.pressed.connect(close_window)
+ %CloseButton.pressed.connect(close_window)
+ close_requested.connect(close_window)
+
+ tree = %Tree
+ tree.item_selected.connect(_on_item_selected)
+
+ tree.columns = 1
+ regenerate_tree(ToDisplayFlags.BANKS | ToDisplayFlags.BUSES | ToDisplayFlags.VCA | ToDisplayFlags.EVENTS)
+
+ %RefreshBanksButton.pressed.connect(on_refresh_banks_button_pressed)
+
+
+func regenerate_tree(to_display: int, callable: Callable = Callable()):
+ %SelectButton.visible = should_display_select_button
+ if _current_select_callable != Callable() && _current_select_callable.get_object() != null:
+ emit_path_and_guid.disconnect(_current_select_callable)
+ _current_select_callable = callable
+
+ tree.clear()
+ var root_item := tree.create_item()
+ root_item.set_text(0, "Fmod objects")
+ root_item.set_icon(0, _fmod_icon)
+
+ for bank in FmodServer.get_all_banks():
+ var fmod_bank := bank as FmodBank
+
+ var bank_item := tree.create_item(root_item)
+ bank_item.set_text(0, fmod_bank.get_godot_res_path())
+ bank_item.set_icon(0, _bank_icon)
+ bank_item.set_metadata(0, bank)
+
+ if to_display & ToDisplayFlags.BUSES:
+ var buses_item := tree.create_item(bank_item)
+ buses_item.set_text(0, "Buses")
+ buses_item.set_icon(0, _bus_icon)
+
+ var buses := fmod_bank.get_bus_list()
+ buses.sort_custom(sort_by_path)
+ _add_elements_as_tree(buses, buses_item)
+
+ if to_display & ToDisplayFlags.VCA:
+ var vca_item := tree.create_item(bank_item)
+ vca_item.set_text(0, "VCAs")
+ vca_item.set_icon(0, _vca_icon)
+
+ var vcas := fmod_bank.get_vca_list()
+ vcas.sort_custom(sort_by_path)
+ _add_elements_as_tree(vcas, vca_item)
+
+ if to_display & ToDisplayFlags.EVENTS:
+ var events_item := tree.create_item(bank_item)
+ events_item.set_text(0, "Events")
+ events_item.set_icon(0, _event_icon)
+
+ var events := fmod_bank.get_description_list()
+ events.sort_custom(sort_by_path)
+ _add_elements_as_tree(events, events_item)
+
+ if copy_path_button.visible:
+ copy_path_button.visible = should_display_copy_buttons
+
+ if copy_guid_button.visible:
+ copy_guid_button.visible = should_display_copy_buttons
+
+ if _current_select_callable != Callable():
+ print(_current_select_callable)
+ emit_path_and_guid.connect(_current_select_callable)
+
+ %SelectButton.visible = should_display_select_button and %GuidLabel.text != ""
+
+
+func _add_elements_as_tree(elements: Array, parent: TreeItem):
+ var stack = Array()
+ for element in elements:
+ _add_element_to_stack(stack, parent, element)
+
+
+func _add_element_to_stack(stack: Array, parent_root: TreeItem, path_element):
+ var fmod_path: String = path_element.get_path()
+ var path_parts := fmod_path.split("/")
+ if path_parts[path_parts.size() - 1] == "":
+ path_parts.remove_at(path_parts.size() - 1)
+
+ for i in range(0, path_parts.size()):
+ var path_part = path_parts[i]
+ path_part = path_part if path_part != "bus:" else "Master"
+
+ if i >= stack.size():
+ var parent_item = parent_root if stack.is_empty() else stack[stack.size() - 1]
+ var tree_item = tree.create_item(parent_item)
+ tree_item.set_text(0, path_part)
+ if i == path_parts.size() - 1:
+ tree_item.set_metadata(0, path_element)
+ tree_item.set_icon(0, _get_icon_for_fmod_path(fmod_path))
+ stack.append(tree_item)
+ continue
+
+ if stack[i].get_text(0) != path_part:
+ for j in range(stack.size() - 1, i - 1, -1):
+ stack.remove_at(j)
+
+ var parent_item = parent_root if stack.is_empty() else stack[stack.size() - 1]
+ var tree_item = tree.create_item(parent_item)
+ tree_item.set_text(0, path_part)
+ if i == path_parts.size() - 1:
+ tree_item.set_metadata(0, path_element)
+ tree_item.set_icon(0, _get_icon_for_fmod_path(fmod_path))
+ stack.append(tree_item)
+
+
+func _on_item_selected():
+ var metadata = tree.get_selected().get_metadata(0)
+ if metadata == null or !metadata.get_guid():
+ %PathsBG.hide()
+ %EventPlayControls.hide()
+ copy_path_button.visible = false
+ copy_guid_button.visible = false
+ %SelectButton.visible = false
+ %ParametersLabel.visible = false
+ %ParametersContainer.visible = false
+ return
+ %GuidLabel.set_text(metadata.get_guid())
+ %PathLabel.set_text(metadata.get_path())
+ %PathsBG.show()
+ if should_display_copy_buttons:
+ copy_path_button.visible = true
+ copy_guid_button.visible = true
+ if should_display_select_button:
+ %SelectButton.visible = true
+
+ if metadata is FmodEventDescription:
+ %EventPlayControls.set_fmod_event(metadata)
+ var _show_parameter_controls : bool = %EventParametersDisplay.set_fmod_event(metadata)
+ %ParametersLabel.visible = _show_parameter_controls
+ %ParametersContainer.visible = _show_parameter_controls
+ return
+ %EventPlayControls.hide()
+ %EventParametersDisplay.hide()
+ %ParametersLabel.visible = false
+ %ParametersContainer.visible = false
+
+func _on_copy_path_button():
+ DisplayServer.clipboard_set(%PathLabel.text)
+
+func _on_copy_guid_button():
+ DisplayServer.clipboard_set(%GuidLabel.text)
+
+
+func on_refresh_banks_button_pressed() -> void:
+ # unload banks
+ var current_banks : Array = FmodServer.get_all_banks()
+ for i : FmodBank in current_banks:
+ FmodServer.unload_bank(i.get_godot_res_path())
+ # get the banks to load
+ var path : String = ProjectSettings.get_setting("Fmod/General/banks_path", "")
+ if !path:
+ printerr("No banks path set in the project settings (Fmod/General/banks_path)")
+ return # no path set in the project settings
+ var bank_paths_to_load : Array[String]
+ var dir : DirAccess = DirAccess.open(path)
+ if dir:
+ dir.list_dir_begin()
+ var file_name : String = dir.get_next()
+ while file_name != "":
+ if dir.current_is_dir():
+ pass # the found item is a directory
+ else:
+ if file_name.ends_with(".bank"):
+ bank_paths_to_load.append(path + "/" + file_name)
+ file_name = dir.get_next()
+ if bank_paths_to_load:
+ # sort to first load the master bank and its strings
+ bank_paths_to_load.sort_custom(func(path1 : String, path2 : String) -> bool: return path1.contains("Master"))
+ else:
+ printerr("Could not find any banks in the specified directory")
+ for bank_path : String in bank_paths_to_load:
+ FmodServer.load_bank(bank_path, FmodServer.FMOD_STUDIO_LOAD_BANK_NORMAL)
+ else:
+ printerr("Couldn't access bank path, please make sure you specified a folder with the banks as direct children")
+
+ regenerate_tree(ToDisplayFlags.BANKS | ToDisplayFlags.BUSES | ToDisplayFlags.VCA | ToDisplayFlags.EVENTS)
+
+
+func close_window():
+ %EventPlayControls.stop_event()
+ visible = false
+
+static func _get_icon_for_fmod_path(fmod_path: String) -> Texture2D:
+ var icon: Texture2D = null
+ if fmod_path.begins_with("bus:/"):
+ icon = _bus_icon
+ elif fmod_path.begins_with("event:/"):
+ icon = _event_icon
+ elif fmod_path.begins_with("vca:/"):
+ icon = _vca_icon
+ elif fmod_path.begins_with("snapshot:/"):
+ icon = _snapshot_icon
+ return icon
+
+static func sort_by_path(a, b):
+ return a.get_path().casecmp_to(b.get_path()) < 0
diff --git a/addons/fmod/tool/ui/FmodBankExplorer.tscn b/addons/fmod/tool/ui/FmodBankExplorer.tscn
new file mode 100644
index 0000000..909fdb8
--- /dev/null
+++ b/addons/fmod/tool/ui/FmodBankExplorer.tscn
@@ -0,0 +1,348 @@
+[gd_scene load_steps=17 format=3 uid="uid://nr38urn226al"]
+
+[ext_resource type="Script" path="res://addons/fmod/tool/ui/FmodBankExplorer.gd" id="1_ekqus"]
+[ext_resource type="Script" path="res://addons/fmod/tool/ui/EventPlayControls.gd" id="2_mleop"]
+[ext_resource type="PackedScene" uid="uid://cppeyr1ke5wre" path="res://addons/fmod/tool/ui/EventParametersDisplay.tscn" id="2_uoyg8"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wrr0m"]
+bg_color = Color(0, 0, 0, 0.247059)
+border_width_left = 1
+border_width_top = 1
+border_width_right = 1
+border_width_bottom = 1
+border_color = Color(1, 1, 1, 0.207843)
+corner_radius_top_left = 5
+corner_radius_top_right = 5
+corner_radius_bottom_right = 5
+corner_radius_bottom_left = 5
+
+[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_2pbsy"]
+
+[sub_resource type="LabelSettings" id="LabelSettings_3jkpq"]
+font_size = 18
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0awfk"]
+bg_color = Color(0, 0, 0, 0.14902)
+border_width_left = 1
+border_width_top = 1
+border_width_right = 1
+border_width_bottom = 1
+border_color = Color(0.8, 0.8, 0.8, 0.145098)
+corner_radius_top_left = 5
+corner_radius_top_right = 5
+corner_radius_bottom_right = 5
+corner_radius_bottom_left = 5
+
+[sub_resource type="LabelSettings" id="LabelSettings_d4isr"]
+
+[sub_resource type="Image" id="Image_bwsay"]
+data = {
+"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
+"format": "RGBA8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
+
+[sub_resource type="ImageTexture" id="ImageTexture_sxu7n"]
+image = SubResource("Image_bwsay")
+
+[sub_resource type="Image" id="Image_6755r"]
+data = {
+"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
+"format": "RGBA8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
+
+[sub_resource type="ImageTexture" id="ImageTexture_hxrge"]
+image = SubResource("Image_6755r")
+
+[sub_resource type="Image" id="Image_05gpv"]
+data = {
+"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 177, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 177, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 176, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
+"format": "RGBA8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
+
+[sub_resource type="ImageTexture" id="ImageTexture_72iew"]
+image = SubResource("Image_05gpv")
+
+[sub_resource type="InputEventKey" id="InputEventKey_w47tf"]
+device = -1
+keycode = 4194305
+
+[sub_resource type="Shortcut" id="Shortcut_rarey"]
+events = [SubResource("InputEventKey_w47tf")]
+
+[node name="FmodBankExplorer" type="Window"]
+title = "Fmod banks explorer"
+initial_position = 2
+size = Vector2i(1720, 684)
+script = ExtResource("1_ekqus")
+
+[node name="BGPanel" type="Panel" parent="."]
+unique_name_in_owner = true
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="WindowMargin" type="MarginContainer" parent="."]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_constants/margin_left = 16
+theme_override_constants/margin_top = 16
+theme_override_constants/margin_right = 16
+theme_override_constants/margin_bottom = 16
+
+[node name="VBoxContainer" type="VBoxContainer" parent="WindowMargin"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="TopPanel" type="PanelContainer" parent="WindowMargin/VBoxContainer"]
+custom_minimum_size = Vector2(0, 42.315)
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="WindowMargin/VBoxContainer/TopPanel"]
+layout_mode = 2
+alignment = 2
+
+[node name="RefreshBanksButton" type="Button" parent="WindowMargin/VBoxContainer/TopPanel/HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "Refresh Banks"
+
+[node name="BaseColorPanel" type="PanelContainer" parent="WindowMargin/VBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_vertical = 3
+theme_override_styles/panel = SubResource("StyleBoxFlat_wrr0m")
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel"]
+layout_mode = 2
+size_flags_vertical = 3
+theme_override_constants/margin_left = 8
+theme_override_constants/margin_top = 8
+theme_override_constants/margin_right = 8
+theme_override_constants/margin_bottom = 8
+
+[node name="HSplitContainer" type="HSplitContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer"]
+layout_mode = 2
+
+[node name="Tree" type="Tree" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+size_flags_stretch_ratio = 0.54
+theme_override_styles/panel = SubResource("StyleBoxEmpty_2pbsy")
+hide_root = true
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_constants/margin_left = 8
+theme_override_constants/margin_top = 8
+theme_override_constants/margin_right = 8
+theme_override_constants/margin_bottom = 8
+
+[node name="RightPanelContent" type="VBoxContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="PathsLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent"]
+visible = false
+layout_mode = 2
+size_flags_vertical = 1
+text = "ID:"
+label_settings = SubResource("LabelSettings_3jkpq")
+justification_flags = 35
+
+[node name="PathsBG" type="PanelContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent"]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_styles/panel = SubResource("StyleBoxFlat_0awfk")
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG"]
+layout_mode = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
+
+[node name="PathContainer" type="HBoxContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer"]
+layout_mode = 2
+
+[node name="TitleLabelContainer" type="VBoxContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer"]
+layout_mode = 2
+
+[node name="PathTitleLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/TitleLabelContainer"]
+layout_mode = 2
+size_flags_vertical = 6
+text = "Path:"
+label_settings = SubResource("LabelSettings_d4isr")
+
+[node name="GuidTitleLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/TitleLabelContainer"]
+layout_mode = 2
+size_flags_vertical = 6
+text = "GUID: "
+label_settings = SubResource("LabelSettings_d4isr")
+
+[node name="ValueContainer" type="VBoxContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="PathLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/ValueContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 6
+text = "asdfasdfasdfasdf"
+
+[node name="CopyPathLabel" type="Button" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/ValueContainer/PathLabel"]
+visible = false
+layout_mode = 1
+anchors_preset = 6
+anchor_left = 1.0
+anchor_top = 0.5
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = 5.57001
+offset_top = -15.5
+offset_right = 36.57
+offset_bottom = 15.5
+grow_horizontal = 0
+grow_vertical = 2
+icon = SubResource("ImageTexture_sxu7n")
+
+[node name="GuidLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/ValueContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 6
+text = "asdfasdf"
+vertical_alignment = 1
+
+[node name="CopyGuidLabel" type="Button" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/PathsBG/MarginContainer/PathContainer/ValueContainer/GuidLabel"]
+visible = false
+layout_mode = 1
+anchors_preset = 6
+anchor_left = 1.0
+anchor_top = 0.5
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = 6.095
+offset_top = -15.5
+offset_right = 37.095
+offset_bottom = 15.5
+grow_horizontal = 0
+grow_vertical = 2
+icon = SubResource("ImageTexture_sxu7n")
+
+[node name="ParametersLabel" type="Label" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent"]
+unique_name_in_owner = true
+visible = false
+custom_minimum_size = Vector2(0, 45)
+layout_mode = 2
+text = "Parameters:"
+label_settings = SubResource("LabelSettings_3jkpq")
+vertical_alignment = 2
+
+[node name="ParametersContainer" type="PanelContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent"]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+size_flags_vertical = 3
+theme_override_styles/panel = SubResource("StyleBoxFlat_0awfk")
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/ParametersContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
+
+[node name="EventParametersDisplay" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/ParametersContainer/MarginContainer" instance=ExtResource("2_uoyg8")]
+unique_name_in_owner = true
+layout_mode = 2
+
+[node name="EventPlayControls" type="PanelContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent" node_paths=PackedStringArray("play_button", "stop_button", "fade_out_toggle")]
+unique_name_in_owner = true
+visible = false
+custom_minimum_size = Vector2(0, 55.44)
+layout_mode = 2
+size_flags_vertical = 10
+size_flags_stretch_ratio = 0.1
+theme_override_styles/panel = SubResource("StyleBoxFlat_0awfk")
+script = ExtResource("2_mleop")
+play_button = NodePath("MarginContainer/HBoxContainer/PlayEventButton")
+stop_button = NodePath("MarginContainer/HBoxContainer/StopEventButton")
+fade_out_toggle = NodePath("MarginContainer/HBoxContainer/FadeoutToggle")
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/EventPlayControls"]
+layout_mode = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
+
+[node name="HBoxContainer" type="HBoxContainer" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/EventPlayControls/MarginContainer"]
+layout_mode = 2
+
+[node name="PlayEventButton" type="Button" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/EventPlayControls/MarginContainer/HBoxContainer"]
+layout_mode = 2
+text = "Play"
+icon = SubResource("ImageTexture_hxrge")
+
+[node name="StopEventButton" type="Button" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/EventPlayControls/MarginContainer/HBoxContainer"]
+layout_mode = 2
+text = "Stop"
+icon = SubResource("ImageTexture_72iew")
+
+[node name="FadeoutToggle" type="CheckButton" parent="WindowMargin/VBoxContainer/BaseColorPanel/MarginContainer/HSplitContainer/MarginContainer/RightPanelContent/EventPlayControls/MarginContainer/HBoxContainer"]
+layout_mode = 2
+text = "Allow fade out"
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/margin_top = 8
+
+[node name="HBoxContainer" type="HBoxContainer" parent="WindowMargin/VBoxContainer/MarginContainer"]
+layout_mode = 2
+alignment = 1
+
+[node name="MarginContainer2" type="MarginContainer" parent="WindowMargin/VBoxContainer/MarginContainer/HBoxContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 8
+theme_override_constants/margin_right = 8
+
+[node name="SelectButton" type="Button" parent="WindowMargin/VBoxContainer/MarginContainer/HBoxContainer/MarginContainer2"]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+size_flags_horizontal = 4
+text = "Select"
+
+[node name="MarginContainer" type="MarginContainer" parent="WindowMargin/VBoxContainer/MarginContainer/HBoxContainer"]
+layout_mode = 2
+theme_override_constants/margin_left = 8
+theme_override_constants/margin_right = 8
+
+[node name="CloseButton" type="Button" parent="WindowMargin/VBoxContainer/MarginContainer/HBoxContainer/MarginContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 4
+shortcut = SubResource("Shortcut_rarey")
+text = "Close"
diff --git a/addons/fmod/tool/ui/ParameterDisplay.gd b/addons/fmod/tool/ui/ParameterDisplay.gd
new file mode 100644
index 0000000..a3328c3
--- /dev/null
+++ b/addons/fmod/tool/ui/ParameterDisplay.gd
@@ -0,0 +1,50 @@
+@tool class_name ParameterDisplay extends MarginContainer
+
+var parameter: FmodParameterDescription
+
+func set_parameter(p_parameter: FmodParameterDescription):
+ show()
+ parameter = p_parameter
+
+func display_value_selector(should: bool):
+ %ValueSetterContainer.visible = should
+
+func _ready():
+ if parameter == null:
+ hide()
+ return
+ var minimum_value = parameter.get_minimum()
+ var maximum_value = parameter.get_maximum()
+ var default_value = parameter.get_default_value()
+
+ var copy_icon : Texture = EditorInterface.get_editor_theme().get_icon("ActionCopy", "EditorIcons")
+ %NameCopyButton.icon = copy_icon
+ %IdCopyButton.icon = copy_icon
+
+
+ %NameLabel.text = parameter.get_name()
+ %IdLabel.text = str(parameter.get_id())
+ %RangeLabel.text = "[%s, %s]" % [minimum_value, maximum_value]
+ %DefaultValueLabel.text = str(default_value)
+ %NameCopyButton.pressed.connect(_on_copy_name_button)
+ %IdCopyButton.pressed.connect(_on_copy_id_button)
+ %BackToDefaultButton.pressed.connect(_on_default_value_button)
+
+ %ValueSlider.min_value = minimum_value
+ %ValueSlider.max_value = maximum_value
+ %ValueSlider.value = default_value
+
+ _on_slider_value_changed(%ValueSlider.value)
+ %ValueSlider.value_changed.connect(_on_slider_value_changed)
+
+func _on_copy_name_button():
+ DisplayServer.clipboard_set(%NameLabel.text)
+
+func _on_copy_id_button():
+ DisplayServer.clipboard_set(%IdLabel.text)
+
+func _on_default_value_button():
+ %ValueSlider.value = parameter.get_default_value()
+
+func _on_slider_value_changed(value: float):
+ %CurrentValueLabel.text = str(value)
diff --git a/addons/fmod/tool/ui/ParameterDisplay.tscn b/addons/fmod/tool/ui/ParameterDisplay.tscn
new file mode 100644
index 0000000..de0b97d
--- /dev/null
+++ b/addons/fmod/tool/ui/ParameterDisplay.tscn
@@ -0,0 +1,144 @@
+[gd_scene load_steps=2 format=3 uid="uid://bfdldojk5i6u3"]
+
+[ext_resource type="Script" path="res://addons/fmod/tool/ui/ParameterDisplay.gd" id="1_fxyw8"]
+
+[node name="ParameterDisplay" type="MarginContainer"]
+offset_right = 168.0
+offset_bottom = 160.0
+size_flags_horizontal = 3
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+script = ExtResource("1_fxyw8")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="VBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="TitleContainer" type="VBoxContainer" parent="VBoxContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 20
+
+[node name="NameTitle" type="Label" parent="VBoxContainer/VBoxContainer/TitleContainer"]
+layout_mode = 2
+size_flags_vertical = 10
+text = "Name: "
+
+[node name="IdTitle" type="Label" parent="VBoxContainer/VBoxContainer/TitleContainer"]
+layout_mode = 2
+size_flags_vertical = 10
+text = "ID: "
+
+[node name="RangeTitle" type="Label" parent="VBoxContainer/VBoxContainer/TitleContainer"]
+layout_mode = 2
+size_flags_vertical = 10
+text = "Range: "
+
+[node name="DefaultValueTitle" type="Label" parent="VBoxContainer/VBoxContainer/TitleContainer"]
+layout_mode = 2
+size_flags_vertical = 10
+text = "Default value: "
+
+[node name="ContentContainer" type="VBoxContainer" parent="VBoxContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 20
+
+[node name="NameLabel" type="Label" parent="VBoxContainer/VBoxContainer/ContentContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 10
+
+[node name="NameCopyButton" type="Button" parent="VBoxContainer/VBoxContainer/ContentContainer/NameLabel"]
+unique_name_in_owner = true
+layout_mode = 1
+anchors_preset = 6
+anchor_left = 1.0
+anchor_top = 0.5
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = 9.0
+offset_top = -15.5
+offset_right = 40.0
+offset_bottom = 15.5
+grow_horizontal = 0
+grow_vertical = 2
+size_flags_vertical = 10
+
+[node name="IdLabel" type="Label" parent="VBoxContainer/VBoxContainer/ContentContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 10
+
+[node name="IdCopyButton" type="Button" parent="VBoxContainer/VBoxContainer/ContentContainer/IdLabel"]
+unique_name_in_owner = true
+layout_mode = 1
+anchors_preset = 6
+anchor_left = 1.0
+anchor_top = 0.5
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = 9.0
+offset_top = -15.5
+offset_right = 40.0
+offset_bottom = 15.5
+grow_horizontal = 0
+grow_vertical = 2
+size_flags_vertical = 10
+
+[node name="RangeLabel" type="Label" parent="VBoxContainer/VBoxContainer/ContentContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 10
+
+[node name="DefaultValueLabel" type="Label" parent="VBoxContainer/VBoxContainer/ContentContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 10
+
+[node name="ValueSetterContainer" type="VBoxContainer" parent="VBoxContainer"]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+
+[node name="HSeparator" type="HSeparator" parent="VBoxContainer/ValueSetterContainer"]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/ValueSetterContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/ValueSetterContainer/HBoxContainer"]
+layout_mode = 2
+text = "Set value: "
+
+[node name="ValueSlider" type="HSlider" parent="VBoxContainer/ValueSetterContainer/HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 4
+
+[node name="BackToDefaultButton" type="Button" parent="VBoxContainer/ValueSetterContainer/HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+text = "Default"
+
+[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/ValueSetterContainer"]
+layout_mode = 2
+
+[node name="CurrentValueTitleLabel" type="Label" parent="VBoxContainer/ValueSetterContainer/HBoxContainer2"]
+layout_mode = 2
+text = "Current value: "
+
+[node name="CurrentValueLabel" type="Label" parent="VBoxContainer/ValueSetterContainer/HBoxContainer2"]
+unique_name_in_owner = true
+layout_mode = 2
+
+[node name="Button" type="Button" parent="VBoxContainer/ValueSetterContainer"]
+layout_mode = 2
+text = "Select"
diff --git a/addons/fmod/tool/ui/TestFmodBankExplorer.tscn b/addons/fmod/tool/ui/TestFmodBankExplorer.tscn
new file mode 100644
index 0000000..5368f06
--- /dev/null
+++ b/addons/fmod/tool/ui/TestFmodBankExplorer.tscn
@@ -0,0 +1,12 @@
+[gd_scene load_steps=2 format=3 uid="uid://f4i35731qm63"]
+
+[ext_resource type="PackedScene" uid="uid://nr38urn226al" path="res://addons/fmod/tool/ui/FmodBankExplorer.tscn" id="1_0ul6h"]
+
+[node name="TestFmodBankExplorer" type="Node2D"]
+
+[node name="FmodBankLoader" type="FmodBankLoader" parent="."]
+bank_paths = ["res://assets/Banks/Master.strings.bank", "res://assets/Banks/Master.bank", "res://assets/Banks/Music.bank", "res://assets/Banks/Vehicles.bank"]
+
+[node name="FmodBankExplorer" parent="FmodBankLoader" instance=ExtResource("1_0ul6h")]
+initial_position = 0
+position = Vector2i(0, 36)