From a1710af289ac328da84f824dff1bfa7a490d962f Mon Sep 17 00:00:00 2001
From: Thar0 <17233964+Thar0@users.noreply.github.com>
Date: Sun, 1 Sep 2024 23:40:22 +0100
Subject: [PATCH 1/3] [Audio 7/?] Extract sequences to assembly
Co-authored-by: MNGoldenEagle <17274702+MNGoldenEagle@users.noreply.github.com>
Co-authored-by: zelda2774 <69368340+zelda2774@users.noreply.github.com>
---
assets/xml/audio/sequences/seq_0.xml | 1 +
assets/xml/audio/sequences/seq_1.xml | 1 +
assets/xml/audio/sequences/seq_10.xml | 1 +
assets/xml/audio/sequences/seq_100.xml | 1 +
assets/xml/audio/sequences/seq_101.xml | 1 +
assets/xml/audio/sequences/seq_102.xml | 1 +
assets/xml/audio/sequences/seq_103.xml | 1 +
assets/xml/audio/sequences/seq_104.xml | 1 +
assets/xml/audio/sequences/seq_105.xml | 1 +
assets/xml/audio/sequences/seq_106.xml | 1 +
assets/xml/audio/sequences/seq_107.xml | 1 +
assets/xml/audio/sequences/seq_108.xml | 1 +
assets/xml/audio/sequences/seq_109.xml | 1 +
assets/xml/audio/sequences/seq_11.xml | 1 +
assets/xml/audio/sequences/seq_12.xml | 1 +
assets/xml/audio/sequences/seq_13.xml | 1 +
assets/xml/audio/sequences/seq_14.xml | 1 +
assets/xml/audio/sequences/seq_15.xml | 1 +
assets/xml/audio/sequences/seq_16.xml | 1 +
assets/xml/audio/sequences/seq_17.xml | 1 +
assets/xml/audio/sequences/seq_18.xml | 1 +
assets/xml/audio/sequences/seq_19.xml | 1 +
assets/xml/audio/sequences/seq_2.xml | 1 +
assets/xml/audio/sequences/seq_20.xml | 1 +
assets/xml/audio/sequences/seq_21.xml | 1 +
assets/xml/audio/sequences/seq_22.xml | 1 +
assets/xml/audio/sequences/seq_23.xml | 1 +
assets/xml/audio/sequences/seq_24.xml | 1 +
assets/xml/audio/sequences/seq_25.xml | 1 +
assets/xml/audio/sequences/seq_26.xml | 1 +
assets/xml/audio/sequences/seq_27.xml | 1 +
assets/xml/audio/sequences/seq_28.xml | 1 +
assets/xml/audio/sequences/seq_29.xml | 1 +
assets/xml/audio/sequences/seq_3.xml | 1 +
assets/xml/audio/sequences/seq_30.xml | 1 +
assets/xml/audio/sequences/seq_31.xml | 1 +
assets/xml/audio/sequences/seq_32.xml | 1 +
assets/xml/audio/sequences/seq_33.xml | 1 +
assets/xml/audio/sequences/seq_34.xml | 1 +
assets/xml/audio/sequences/seq_35.xml | 1 +
assets/xml/audio/sequences/seq_36.xml | 1 +
assets/xml/audio/sequences/seq_37.xml | 1 +
assets/xml/audio/sequences/seq_38.xml | 1 +
assets/xml/audio/sequences/seq_39.xml | 1 +
assets/xml/audio/sequences/seq_4.xml | 1 +
assets/xml/audio/sequences/seq_40.xml | 1 +
assets/xml/audio/sequences/seq_41.xml | 1 +
assets/xml/audio/sequences/seq_42.xml | 1 +
assets/xml/audio/sequences/seq_43.xml | 1 +
assets/xml/audio/sequences/seq_44.xml | 1 +
assets/xml/audio/sequences/seq_45.xml | 1 +
assets/xml/audio/sequences/seq_46.xml | 1 +
assets/xml/audio/sequences/seq_47.xml | 1 +
assets/xml/audio/sequences/seq_48.xml | 1 +
assets/xml/audio/sequences/seq_49.xml | 1 +
assets/xml/audio/sequences/seq_5.xml | 1 +
assets/xml/audio/sequences/seq_50.xml | 1 +
assets/xml/audio/sequences/seq_51.xml | 1 +
assets/xml/audio/sequences/seq_52.xml | 1 +
assets/xml/audio/sequences/seq_53.xml | 1 +
assets/xml/audio/sequences/seq_54.xml | 1 +
assets/xml/audio/sequences/seq_55.xml | 1 +
assets/xml/audio/sequences/seq_56.xml | 1 +
assets/xml/audio/sequences/seq_57.xml | 1 +
assets/xml/audio/sequences/seq_58.xml | 1 +
assets/xml/audio/sequences/seq_59.xml | 1 +
assets/xml/audio/sequences/seq_6.xml | 1 +
assets/xml/audio/sequences/seq_60.xml | 1 +
assets/xml/audio/sequences/seq_61.xml | 1 +
assets/xml/audio/sequences/seq_62.xml | 1 +
assets/xml/audio/sequences/seq_63.xml | 1 +
assets/xml/audio/sequences/seq_64.xml | 1 +
assets/xml/audio/sequences/seq_65.xml | 1 +
assets/xml/audio/sequences/seq_66.xml | 1 +
assets/xml/audio/sequences/seq_67.xml | 1 +
assets/xml/audio/sequences/seq_68.xml | 1 +
assets/xml/audio/sequences/seq_69.xml | 1 +
assets/xml/audio/sequences/seq_7.xml | 1 +
assets/xml/audio/sequences/seq_70.xml | 1 +
assets/xml/audio/sequences/seq_71.xml | 1 +
assets/xml/audio/sequences/seq_72.xml | 1 +
assets/xml/audio/sequences/seq_73.xml | 1 +
assets/xml/audio/sequences/seq_74.xml | 1 +
assets/xml/audio/sequences/seq_75.xml | 1 +
assets/xml/audio/sequences/seq_76.xml | 1 +
assets/xml/audio/sequences/seq_77.xml | 1 +
assets/xml/audio/sequences/seq_78.xml | 1 +
assets/xml/audio/sequences/seq_79.xml | 1 +
assets/xml/audio/sequences/seq_8.xml | 1 +
assets/xml/audio/sequences/seq_80.xml | 1 +
assets/xml/audio/sequences/seq_81.xml | 1 +
assets/xml/audio/sequences/seq_82.xml | 1 +
assets/xml/audio/sequences/seq_83.xml | 1 +
assets/xml/audio/sequences/seq_84.xml | 1 +
assets/xml/audio/sequences/seq_85.xml | 1 +
assets/xml/audio/sequences/seq_86.xml | 1 +
assets/xml/audio/sequences/seq_88.xml | 1 +
assets/xml/audio/sequences/seq_89.xml | 1 +
assets/xml/audio/sequences/seq_9.xml | 1 +
assets/xml/audio/sequences/seq_90.xml | 1 +
assets/xml/audio/sequences/seq_91.xml | 1 +
assets/xml/audio/sequences/seq_92.xml | 1 +
assets/xml/audio/sequences/seq_93.xml | 1 +
assets/xml/audio/sequences/seq_94.xml | 1 +
assets/xml/audio/sequences/seq_95.xml | 1 +
assets/xml/audio/sequences/seq_96.xml | 1 +
assets/xml/audio/sequences/seq_97.xml | 1 +
assets/xml/audio/sequences/seq_98.xml | 1 +
assets/xml/audio/sequences/seq_99.xml | 1 +
tools/audio/extraction/audio_extract.py | 154 +-
.../audio/extraction/disassemble_sequence.py | 1325 +++++++++++++++++
tools/audio_extraction.py | 36 +-
112 files changed, 1616 insertions(+), 8 deletions(-)
create mode 100644 assets/xml/audio/sequences/seq_0.xml
create mode 100644 assets/xml/audio/sequences/seq_1.xml
create mode 100644 assets/xml/audio/sequences/seq_10.xml
create mode 100644 assets/xml/audio/sequences/seq_100.xml
create mode 100644 assets/xml/audio/sequences/seq_101.xml
create mode 100644 assets/xml/audio/sequences/seq_102.xml
create mode 100644 assets/xml/audio/sequences/seq_103.xml
create mode 100644 assets/xml/audio/sequences/seq_104.xml
create mode 100644 assets/xml/audio/sequences/seq_105.xml
create mode 100644 assets/xml/audio/sequences/seq_106.xml
create mode 100644 assets/xml/audio/sequences/seq_107.xml
create mode 100644 assets/xml/audio/sequences/seq_108.xml
create mode 100644 assets/xml/audio/sequences/seq_109.xml
create mode 100644 assets/xml/audio/sequences/seq_11.xml
create mode 100644 assets/xml/audio/sequences/seq_12.xml
create mode 100644 assets/xml/audio/sequences/seq_13.xml
create mode 100644 assets/xml/audio/sequences/seq_14.xml
create mode 100644 assets/xml/audio/sequences/seq_15.xml
create mode 100644 assets/xml/audio/sequences/seq_16.xml
create mode 100644 assets/xml/audio/sequences/seq_17.xml
create mode 100644 assets/xml/audio/sequences/seq_18.xml
create mode 100644 assets/xml/audio/sequences/seq_19.xml
create mode 100644 assets/xml/audio/sequences/seq_2.xml
create mode 100644 assets/xml/audio/sequences/seq_20.xml
create mode 100644 assets/xml/audio/sequences/seq_21.xml
create mode 100644 assets/xml/audio/sequences/seq_22.xml
create mode 100644 assets/xml/audio/sequences/seq_23.xml
create mode 100644 assets/xml/audio/sequences/seq_24.xml
create mode 100644 assets/xml/audio/sequences/seq_25.xml
create mode 100644 assets/xml/audio/sequences/seq_26.xml
create mode 100644 assets/xml/audio/sequences/seq_27.xml
create mode 100644 assets/xml/audio/sequences/seq_28.xml
create mode 100644 assets/xml/audio/sequences/seq_29.xml
create mode 100644 assets/xml/audio/sequences/seq_3.xml
create mode 100644 assets/xml/audio/sequences/seq_30.xml
create mode 100644 assets/xml/audio/sequences/seq_31.xml
create mode 100644 assets/xml/audio/sequences/seq_32.xml
create mode 100644 assets/xml/audio/sequences/seq_33.xml
create mode 100644 assets/xml/audio/sequences/seq_34.xml
create mode 100644 assets/xml/audio/sequences/seq_35.xml
create mode 100644 assets/xml/audio/sequences/seq_36.xml
create mode 100644 assets/xml/audio/sequences/seq_37.xml
create mode 100644 assets/xml/audio/sequences/seq_38.xml
create mode 100644 assets/xml/audio/sequences/seq_39.xml
create mode 100644 assets/xml/audio/sequences/seq_4.xml
create mode 100644 assets/xml/audio/sequences/seq_40.xml
create mode 100644 assets/xml/audio/sequences/seq_41.xml
create mode 100644 assets/xml/audio/sequences/seq_42.xml
create mode 100644 assets/xml/audio/sequences/seq_43.xml
create mode 100644 assets/xml/audio/sequences/seq_44.xml
create mode 100644 assets/xml/audio/sequences/seq_45.xml
create mode 100644 assets/xml/audio/sequences/seq_46.xml
create mode 100644 assets/xml/audio/sequences/seq_47.xml
create mode 100644 assets/xml/audio/sequences/seq_48.xml
create mode 100644 assets/xml/audio/sequences/seq_49.xml
create mode 100644 assets/xml/audio/sequences/seq_5.xml
create mode 100644 assets/xml/audio/sequences/seq_50.xml
create mode 100644 assets/xml/audio/sequences/seq_51.xml
create mode 100644 assets/xml/audio/sequences/seq_52.xml
create mode 100644 assets/xml/audio/sequences/seq_53.xml
create mode 100644 assets/xml/audio/sequences/seq_54.xml
create mode 100644 assets/xml/audio/sequences/seq_55.xml
create mode 100644 assets/xml/audio/sequences/seq_56.xml
create mode 100644 assets/xml/audio/sequences/seq_57.xml
create mode 100644 assets/xml/audio/sequences/seq_58.xml
create mode 100644 assets/xml/audio/sequences/seq_59.xml
create mode 100644 assets/xml/audio/sequences/seq_6.xml
create mode 100644 assets/xml/audio/sequences/seq_60.xml
create mode 100644 assets/xml/audio/sequences/seq_61.xml
create mode 100644 assets/xml/audio/sequences/seq_62.xml
create mode 100644 assets/xml/audio/sequences/seq_63.xml
create mode 100644 assets/xml/audio/sequences/seq_64.xml
create mode 100644 assets/xml/audio/sequences/seq_65.xml
create mode 100644 assets/xml/audio/sequences/seq_66.xml
create mode 100644 assets/xml/audio/sequences/seq_67.xml
create mode 100644 assets/xml/audio/sequences/seq_68.xml
create mode 100644 assets/xml/audio/sequences/seq_69.xml
create mode 100644 assets/xml/audio/sequences/seq_7.xml
create mode 100644 assets/xml/audio/sequences/seq_70.xml
create mode 100644 assets/xml/audio/sequences/seq_71.xml
create mode 100644 assets/xml/audio/sequences/seq_72.xml
create mode 100644 assets/xml/audio/sequences/seq_73.xml
create mode 100644 assets/xml/audio/sequences/seq_74.xml
create mode 100644 assets/xml/audio/sequences/seq_75.xml
create mode 100644 assets/xml/audio/sequences/seq_76.xml
create mode 100644 assets/xml/audio/sequences/seq_77.xml
create mode 100644 assets/xml/audio/sequences/seq_78.xml
create mode 100644 assets/xml/audio/sequences/seq_79.xml
create mode 100644 assets/xml/audio/sequences/seq_8.xml
create mode 100644 assets/xml/audio/sequences/seq_80.xml
create mode 100644 assets/xml/audio/sequences/seq_81.xml
create mode 100644 assets/xml/audio/sequences/seq_82.xml
create mode 100644 assets/xml/audio/sequences/seq_83.xml
create mode 100644 assets/xml/audio/sequences/seq_84.xml
create mode 100644 assets/xml/audio/sequences/seq_85.xml
create mode 100644 assets/xml/audio/sequences/seq_86.xml
create mode 100644 assets/xml/audio/sequences/seq_88.xml
create mode 100644 assets/xml/audio/sequences/seq_89.xml
create mode 100644 assets/xml/audio/sequences/seq_9.xml
create mode 100644 assets/xml/audio/sequences/seq_90.xml
create mode 100644 assets/xml/audio/sequences/seq_91.xml
create mode 100644 assets/xml/audio/sequences/seq_92.xml
create mode 100644 assets/xml/audio/sequences/seq_93.xml
create mode 100644 assets/xml/audio/sequences/seq_94.xml
create mode 100644 assets/xml/audio/sequences/seq_95.xml
create mode 100644 assets/xml/audio/sequences/seq_96.xml
create mode 100644 assets/xml/audio/sequences/seq_97.xml
create mode 100644 assets/xml/audio/sequences/seq_98.xml
create mode 100644 assets/xml/audio/sequences/seq_99.xml
create mode 100644 tools/audio/extraction/disassemble_sequence.py
diff --git a/assets/xml/audio/sequences/seq_0.xml b/assets/xml/audio/sequences/seq_0.xml
new file mode 100644
index 00000000000..6ff3cd1e81f
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_0.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_1.xml b/assets/xml/audio/sequences/seq_1.xml
new file mode 100644
index 00000000000..a377674df91
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_1.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_10.xml b/assets/xml/audio/sequences/seq_10.xml
new file mode 100644
index 00000000000..3ae59e65cda
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_10.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_100.xml b/assets/xml/audio/sequences/seq_100.xml
new file mode 100644
index 00000000000..9f2c90fdfe0
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_100.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_101.xml b/assets/xml/audio/sequences/seq_101.xml
new file mode 100644
index 00000000000..7b16f1459e9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_101.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_102.xml b/assets/xml/audio/sequences/seq_102.xml
new file mode 100644
index 00000000000..2c99af7ce9e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_102.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_103.xml b/assets/xml/audio/sequences/seq_103.xml
new file mode 100644
index 00000000000..ca023064d3c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_103.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_104.xml b/assets/xml/audio/sequences/seq_104.xml
new file mode 100644
index 00000000000..443716b538d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_104.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_105.xml b/assets/xml/audio/sequences/seq_105.xml
new file mode 100644
index 00000000000..2d3660d8bb3
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_105.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_106.xml b/assets/xml/audio/sequences/seq_106.xml
new file mode 100644
index 00000000000..0f998e0c144
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_106.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_107.xml b/assets/xml/audio/sequences/seq_107.xml
new file mode 100644
index 00000000000..0e2d815a183
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_107.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_108.xml b/assets/xml/audio/sequences/seq_108.xml
new file mode 100644
index 00000000000..6549605c735
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_108.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_109.xml b/assets/xml/audio/sequences/seq_109.xml
new file mode 100644
index 00000000000..e16afa0cabb
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_109.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_11.xml b/assets/xml/audio/sequences/seq_11.xml
new file mode 100644
index 00000000000..d829182044d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_11.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_12.xml b/assets/xml/audio/sequences/seq_12.xml
new file mode 100644
index 00000000000..004d2f9806e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_12.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_13.xml b/assets/xml/audio/sequences/seq_13.xml
new file mode 100644
index 00000000000..e3bfd4973e9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_13.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_14.xml b/assets/xml/audio/sequences/seq_14.xml
new file mode 100644
index 00000000000..3b5f867fc66
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_14.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_15.xml b/assets/xml/audio/sequences/seq_15.xml
new file mode 100644
index 00000000000..144ba0eb9ab
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_15.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_16.xml b/assets/xml/audio/sequences/seq_16.xml
new file mode 100644
index 00000000000..5f9da240b89
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_16.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_17.xml b/assets/xml/audio/sequences/seq_17.xml
new file mode 100644
index 00000000000..b1aff37312b
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_17.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_18.xml b/assets/xml/audio/sequences/seq_18.xml
new file mode 100644
index 00000000000..ddd090b2669
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_18.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_19.xml b/assets/xml/audio/sequences/seq_19.xml
new file mode 100644
index 00000000000..bfd25ca10c1
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_19.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_2.xml b/assets/xml/audio/sequences/seq_2.xml
new file mode 100644
index 00000000000..e4c8bbb868c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_2.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_20.xml b/assets/xml/audio/sequences/seq_20.xml
new file mode 100644
index 00000000000..a6fe2f18575
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_20.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_21.xml b/assets/xml/audio/sequences/seq_21.xml
new file mode 100644
index 00000000000..c28b5b5eb57
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_21.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_22.xml b/assets/xml/audio/sequences/seq_22.xml
new file mode 100644
index 00000000000..bef36b8cf03
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_22.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_23.xml b/assets/xml/audio/sequences/seq_23.xml
new file mode 100644
index 00000000000..9e6887876e8
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_23.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_24.xml b/assets/xml/audio/sequences/seq_24.xml
new file mode 100644
index 00000000000..409278dda5a
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_24.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_25.xml b/assets/xml/audio/sequences/seq_25.xml
new file mode 100644
index 00000000000..09340fb59cc
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_25.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_26.xml b/assets/xml/audio/sequences/seq_26.xml
new file mode 100644
index 00000000000..c5ad33f0259
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_26.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_27.xml b/assets/xml/audio/sequences/seq_27.xml
new file mode 100644
index 00000000000..8599a4786a3
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_27.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_28.xml b/assets/xml/audio/sequences/seq_28.xml
new file mode 100644
index 00000000000..e85d05d8ec8
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_28.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_29.xml b/assets/xml/audio/sequences/seq_29.xml
new file mode 100644
index 00000000000..380bc35861b
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_29.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_3.xml b/assets/xml/audio/sequences/seq_3.xml
new file mode 100644
index 00000000000..c243327093e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_3.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_30.xml b/assets/xml/audio/sequences/seq_30.xml
new file mode 100644
index 00000000000..4e90f42d429
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_30.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_31.xml b/assets/xml/audio/sequences/seq_31.xml
new file mode 100644
index 00000000000..b55ec5aeac7
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_31.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_32.xml b/assets/xml/audio/sequences/seq_32.xml
new file mode 100644
index 00000000000..8c9f3f6edf0
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_32.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_33.xml b/assets/xml/audio/sequences/seq_33.xml
new file mode 100644
index 00000000000..18a1fa3ba51
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_33.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_34.xml b/assets/xml/audio/sequences/seq_34.xml
new file mode 100644
index 00000000000..10ba75422ee
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_34.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_35.xml b/assets/xml/audio/sequences/seq_35.xml
new file mode 100644
index 00000000000..db22ec7e08d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_35.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_36.xml b/assets/xml/audio/sequences/seq_36.xml
new file mode 100644
index 00000000000..e854c41fe39
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_36.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_37.xml b/assets/xml/audio/sequences/seq_37.xml
new file mode 100644
index 00000000000..5f28eae9504
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_37.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_38.xml b/assets/xml/audio/sequences/seq_38.xml
new file mode 100644
index 00000000000..c4aff602bf9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_38.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_39.xml b/assets/xml/audio/sequences/seq_39.xml
new file mode 100644
index 00000000000..9f287053d06
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_39.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_4.xml b/assets/xml/audio/sequences/seq_4.xml
new file mode 100644
index 00000000000..83226cb9bde
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_4.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_40.xml b/assets/xml/audio/sequences/seq_40.xml
new file mode 100644
index 00000000000..16d0cdfd85e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_40.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_41.xml b/assets/xml/audio/sequences/seq_41.xml
new file mode 100644
index 00000000000..10260aff934
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_41.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_42.xml b/assets/xml/audio/sequences/seq_42.xml
new file mode 100644
index 00000000000..ce925ccce51
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_42.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_43.xml b/assets/xml/audio/sequences/seq_43.xml
new file mode 100644
index 00000000000..6521d276b4d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_43.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_44.xml b/assets/xml/audio/sequences/seq_44.xml
new file mode 100644
index 00000000000..b0e475b24fd
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_44.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_45.xml b/assets/xml/audio/sequences/seq_45.xml
new file mode 100644
index 00000000000..8045d8867d9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_45.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_46.xml b/assets/xml/audio/sequences/seq_46.xml
new file mode 100644
index 00000000000..89d3e57892d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_46.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_47.xml b/assets/xml/audio/sequences/seq_47.xml
new file mode 100644
index 00000000000..ea3f85fea17
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_47.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_48.xml b/assets/xml/audio/sequences/seq_48.xml
new file mode 100644
index 00000000000..f6a38e21c67
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_48.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_49.xml b/assets/xml/audio/sequences/seq_49.xml
new file mode 100644
index 00000000000..a5e2b007e7c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_49.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_5.xml b/assets/xml/audio/sequences/seq_5.xml
new file mode 100644
index 00000000000..227091a4d48
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_5.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_50.xml b/assets/xml/audio/sequences/seq_50.xml
new file mode 100644
index 00000000000..a4576f01b1b
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_50.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_51.xml b/assets/xml/audio/sequences/seq_51.xml
new file mode 100644
index 00000000000..bbf71629112
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_51.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_52.xml b/assets/xml/audio/sequences/seq_52.xml
new file mode 100644
index 00000000000..a5607162429
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_52.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_53.xml b/assets/xml/audio/sequences/seq_53.xml
new file mode 100644
index 00000000000..11e66475c01
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_53.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_54.xml b/assets/xml/audio/sequences/seq_54.xml
new file mode 100644
index 00000000000..6eca31b638e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_54.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_55.xml b/assets/xml/audio/sequences/seq_55.xml
new file mode 100644
index 00000000000..92bc87e71d9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_55.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_56.xml b/assets/xml/audio/sequences/seq_56.xml
new file mode 100644
index 00000000000..9a6353398d2
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_56.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_57.xml b/assets/xml/audio/sequences/seq_57.xml
new file mode 100644
index 00000000000..f56c11e517e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_57.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_58.xml b/assets/xml/audio/sequences/seq_58.xml
new file mode 100644
index 00000000000..8a4d98cf673
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_58.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_59.xml b/assets/xml/audio/sequences/seq_59.xml
new file mode 100644
index 00000000000..851a9eb0966
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_59.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_6.xml b/assets/xml/audio/sequences/seq_6.xml
new file mode 100644
index 00000000000..89bcc0292b9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_6.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_60.xml b/assets/xml/audio/sequences/seq_60.xml
new file mode 100644
index 00000000000..430004df531
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_60.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_61.xml b/assets/xml/audio/sequences/seq_61.xml
new file mode 100644
index 00000000000..ff247006d07
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_61.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_62.xml b/assets/xml/audio/sequences/seq_62.xml
new file mode 100644
index 00000000000..eb81883c079
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_62.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_63.xml b/assets/xml/audio/sequences/seq_63.xml
new file mode 100644
index 00000000000..504a2275fa1
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_63.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_64.xml b/assets/xml/audio/sequences/seq_64.xml
new file mode 100644
index 00000000000..27aa4dce739
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_64.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_65.xml b/assets/xml/audio/sequences/seq_65.xml
new file mode 100644
index 00000000000..88523a8ccd7
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_65.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_66.xml b/assets/xml/audio/sequences/seq_66.xml
new file mode 100644
index 00000000000..6fcd739ecc0
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_66.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_67.xml b/assets/xml/audio/sequences/seq_67.xml
new file mode 100644
index 00000000000..2024815cd90
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_67.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_68.xml b/assets/xml/audio/sequences/seq_68.xml
new file mode 100644
index 00000000000..4c742e4258a
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_68.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_69.xml b/assets/xml/audio/sequences/seq_69.xml
new file mode 100644
index 00000000000..816578f813c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_69.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_7.xml b/assets/xml/audio/sequences/seq_7.xml
new file mode 100644
index 00000000000..dc239bbe1a4
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_7.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_70.xml b/assets/xml/audio/sequences/seq_70.xml
new file mode 100644
index 00000000000..096f4546690
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_70.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_71.xml b/assets/xml/audio/sequences/seq_71.xml
new file mode 100644
index 00000000000..10cca85e139
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_71.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_72.xml b/assets/xml/audio/sequences/seq_72.xml
new file mode 100644
index 00000000000..c878ddbbbd8
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_72.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_73.xml b/assets/xml/audio/sequences/seq_73.xml
new file mode 100644
index 00000000000..91708ec863d
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_73.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_74.xml b/assets/xml/audio/sequences/seq_74.xml
new file mode 100644
index 00000000000..2f96ac0ae35
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_74.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_75.xml b/assets/xml/audio/sequences/seq_75.xml
new file mode 100644
index 00000000000..c84e2f21b81
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_75.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_76.xml b/assets/xml/audio/sequences/seq_76.xml
new file mode 100644
index 00000000000..6b5a83d7bcb
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_76.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_77.xml b/assets/xml/audio/sequences/seq_77.xml
new file mode 100644
index 00000000000..49a57e600fc
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_77.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_78.xml b/assets/xml/audio/sequences/seq_78.xml
new file mode 100644
index 00000000000..87bc6239102
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_78.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_79.xml b/assets/xml/audio/sequences/seq_79.xml
new file mode 100644
index 00000000000..ab9aa1d72e3
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_79.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_8.xml b/assets/xml/audio/sequences/seq_8.xml
new file mode 100644
index 00000000000..1eda8f8fa26
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_8.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_80.xml b/assets/xml/audio/sequences/seq_80.xml
new file mode 100644
index 00000000000..40c8e926d9e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_80.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_81.xml b/assets/xml/audio/sequences/seq_81.xml
new file mode 100644
index 00000000000..14c49a90de7
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_81.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_82.xml b/assets/xml/audio/sequences/seq_82.xml
new file mode 100644
index 00000000000..ef15bad311c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_82.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_83.xml b/assets/xml/audio/sequences/seq_83.xml
new file mode 100644
index 00000000000..efb27b53ff5
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_83.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_84.xml b/assets/xml/audio/sequences/seq_84.xml
new file mode 100644
index 00000000000..1249f1a9ebf
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_84.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_85.xml b/assets/xml/audio/sequences/seq_85.xml
new file mode 100644
index 00000000000..ecf38de03d2
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_85.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_86.xml b/assets/xml/audio/sequences/seq_86.xml
new file mode 100644
index 00000000000..261337ad180
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_86.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_88.xml b/assets/xml/audio/sequences/seq_88.xml
new file mode 100644
index 00000000000..485f2b98ba2
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_88.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_89.xml b/assets/xml/audio/sequences/seq_89.xml
new file mode 100644
index 00000000000..b872412aafb
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_89.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_9.xml b/assets/xml/audio/sequences/seq_9.xml
new file mode 100644
index 00000000000..06422b86c32
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_9.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_90.xml b/assets/xml/audio/sequences/seq_90.xml
new file mode 100644
index 00000000000..f56e6b0d3d1
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_90.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_91.xml b/assets/xml/audio/sequences/seq_91.xml
new file mode 100644
index 00000000000..304b90e6af0
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_91.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_92.xml b/assets/xml/audio/sequences/seq_92.xml
new file mode 100644
index 00000000000..a79594534b7
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_92.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_93.xml b/assets/xml/audio/sequences/seq_93.xml
new file mode 100644
index 00000000000..979b7594f0c
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_93.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_94.xml b/assets/xml/audio/sequences/seq_94.xml
new file mode 100644
index 00000000000..c78b040d5f4
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_94.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_95.xml b/assets/xml/audio/sequences/seq_95.xml
new file mode 100644
index 00000000000..cbdb82e6c83
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_95.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_96.xml b/assets/xml/audio/sequences/seq_96.xml
new file mode 100644
index 00000000000..3bdec9ed887
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_96.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_97.xml b/assets/xml/audio/sequences/seq_97.xml
new file mode 100644
index 00000000000..5eab58ee471
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_97.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_98.xml b/assets/xml/audio/sequences/seq_98.xml
new file mode 100644
index 00000000000..98e9e9e0f6e
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_98.xml
@@ -0,0 +1 @@
+
diff --git a/assets/xml/audio/sequences/seq_99.xml b/assets/xml/audio/sequences/seq_99.xml
new file mode 100644
index 00000000000..2e6a92c50c9
--- /dev/null
+++ b/assets/xml/audio/sequences/seq_99.xml
@@ -0,0 +1 @@
+
diff --git a/tools/audio/extraction/audio_extract.py b/tools/audio/extraction/audio_extract.py
index ea4f8612c30..de364329272 100644
--- a/tools/audio/extraction/audio_extract.py
+++ b/tools/audio/extraction/audio_extract.py
@@ -6,7 +6,6 @@
import os, shutil, time
from dataclasses import dataclass
-from enum import auto, Enum
from multiprocessing.pool import ThreadPool
from typing import Dict, List, Tuple, Union
from xml.etree import ElementTree
@@ -15,11 +14,8 @@
from .audio_tables import AudioCodeTable, AudioCodeTableEntry, AudioStorageMedium
from .audiotable import AudioTableData, AudioTableFile, AudioTableSample
from .audiobank_file import AudiobankFile
-from .util import align, debugm, error, incbin, program_get
-
-class MMLVersion(Enum):
- OOT = auto()
- MM = auto()
+from .disassemble_sequence import CMD_SPEC, SequenceDisassembler, MMLVersion
+from .util import align, debugm, error, incbin, program_get, XMLWriter
@dataclass
class GameVersionInfo:
@@ -41,6 +37,8 @@ class GameVersionInfo:
fake_banks : Dict[int, int]
# Contains audiotable indices that suffer from a buffer clearing bug
audiotable_buffer_bugs : Tuple[int]
+ # Sequence disassembly hacks
+ seq_disas_hacks : Dict[int, Tuple[Tuple[int]]]
SAMPLECONV_PATH = f"{os.path.dirname(os.path.realpath(__file__))}/../sampleconv/sampleconv"
@@ -176,6 +174,135 @@ def extract_samplebank(pool : ThreadPool, extracted_dir : str, sample_banks : Li
if not BASEROM_DEBUG:
shutil.rmtree(f"{base_path}/aifc")
+def disassemble_one_sequence(extracted_dir : str, version_info : GameVersionInfo, soundfonts : List[AudiobankFile],
+ enum_names : List[str], id : int, data : bytes, name : str, filename : str,
+ fonts : memoryview):
+ out_filename = f"{extracted_dir}/assets/audio/sequences/{filename}.seq"
+ disas = SequenceDisassembler(id, data, version_info.seq_disas_hacks.get(id, None), CMD_SPEC,
+ version_info.mml_version, out_filename, name,
+ [soundfonts[i] for i in fonts], enum_names)
+ disas.analyze()
+ disas.emit()
+
+def extract_sequences(audioseq_seg : memoryview, extracted_dir : str, version_info : GameVersionInfo, write_xml : bool,
+ sequence_table : AudioCodeTable, sequence_font_table : memoryview,
+ sequence_xmls : Dict[int, Element], soundfonts : List[AudiobankFile]):
+
+ sequence_font_table_cvg = [0] * len(sequence_font_table)
+
+ seq_enum_names = version_info.seq_enum_names
+ handwritten_sequences = version_info.handwritten_sequences
+
+ # We should have as many enum names as sequences that require extraction
+ assert len(seq_enum_names) == len(sequence_table)
+
+ if BASEROM_DEBUG:
+ os.makedirs(f"{extracted_dir}/baserom_audiotest/audioseq_files", exist_ok=True)
+
+ os.makedirs(f"{extracted_dir}/assets/audio/sequences", exist_ok=True)
+ if write_xml:
+ os.makedirs(f"assets/xml/audio/sequences", exist_ok=True)
+
+ all_fonts = []
+ disas_jobs = []
+
+ for i,entry in enumerate(sequence_table):
+ entry : AudioCodeTableEntry
+
+ # extract font indices
+ font_data_offset = (sequence_font_table[2 * i + 0] << 8) | (sequence_font_table[2 * i + 1])
+ num_fonts = sequence_font_table[font_data_offset]
+ font_data_offset += 1
+ fonts = sequence_font_table[font_data_offset:font_data_offset+num_fonts]
+
+ all_fonts.append(fonts)
+
+ # mark coverage for sequence font table
+ sequence_font_table_cvg[2 * i + 0] = 1
+ sequence_font_table_cvg[2 * i + 1] = 1
+ for j in range(font_data_offset-1,font_data_offset+num_fonts):
+ sequence_font_table_cvg[j] = 1
+
+ if entry.size != 0:
+ # Real sequence, queue extraction
+
+ seq_data = bytearray(entry.data(audioseq_seg, sequence_table.rom_addr))
+
+ ext = ".prg" if i in handwritten_sequences else ""
+
+ if BASEROM_DEBUG:
+ # Extract original sequence binary for comparison
+ with open(f"{extracted_dir}/baserom_audiotest/audioseq_files/seq_{i}{ext}.aseq", "wb") as outfile:
+ outfile.write(seq_data)
+
+ extraction_xml = sequence_xmls.get(i, None)
+ if extraction_xml is None:
+ sequence_filename = f"seq_{i}"
+ sequence_name = f"Sequence_{i}"
+ else:
+ sequence_filename = extraction_xml[0]
+ sequence_name = extraction_xml[1].attrib["Name"]
+
+ # Write extraction xml entry
+ if write_xml:
+ xml = XMLWriter()
+
+ xml.write_element("Sequence", {
+ "Name" : sequence_name,
+ "Index" : i,
+ })
+
+ with open(f"assets/xml/audio/sequences/{sequence_filename}.xml", "w") as outfile:
+ outfile.write(str(xml))
+
+ if i in handwritten_sequences:
+ # skip "handwritten" sequences
+ continue
+
+ disas_jobs.append((i, seq_data, sequence_name, sequence_filename, fonts))
+ else:
+ # Pointer to another sequence, checked later
+ pass
+
+ # Check full coverage
+ try:
+ if align(sequence_font_table_cvg.index(0), 16) != len(sequence_font_table_cvg):
+ # does not pad to full size, fail
+ assert False, "Sequence font table missing data"
+ # pads to full size, good
+ except ValueError:
+ pass # fully covered, good
+
+ # Check consistency of font data for the same sequence accessed via pointers
+
+ for i,entry in enumerate(sequence_table):
+ entry : AudioCodeTableEntry
+
+ # Fonts for this entry
+ fonts = all_fonts[i]
+
+ if entry.size != 0:
+ # real, ignore
+ pass
+ else:
+ # pointer, check that the fonts for this entry are the same as the fonts for the other
+ j = entry.rom_addr
+
+ fonts2 = all_fonts[j]
+
+ assert fonts == fonts2, \
+ f"Font mismatch: Pointer {i} against Real {j}. This is a limitation of the build process."
+
+ # Disassemble to text
+
+ with ThreadPool(processes=os.cpu_count()) as pool:
+ # multiprocess sequence disassembly
+ async_results = [pool.apply_async(disassemble_one_sequence,
+ args=(extracted_dir, version_info, soundfonts, seq_enum_names, *job))
+ for job in disas_jobs]
+ # block until done
+ [res.get() for res in async_results]
+
def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : str, read_xml : bool, write_xml : bool):
print("Setting up...")
@@ -184,6 +311,7 @@ def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : st
code_seg = None
audiotable_seg = None
audiobank_seg = None
+ audioseq_seg = None
with open(f"{extracted_dir}/baserom/code", "rb") as infile:
code_seg = memoryview(infile.read())
@@ -194,6 +322,9 @@ def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : st
with open(f"{extracted_dir}/baserom/Audiobank", "rb") as infile:
audiobank_seg = memoryview(infile.read())
+ with open(f"{extracted_dir}/baserom/Audioseq", "rb") as infile:
+ audioseq_seg = memoryview(infile.read())
+
# ==================================================================================================================
# Collect audio tables
# ==================================================================================================================
@@ -318,3 +449,14 @@ def walk_xmls(out_dict : Dict[int, Tuple[str, Element]], path : str, typename :
# write the extraction xml if specified
if write_xml:
sf.write_extraction_xml(f"assets/xml/audio/soundfonts/{sf.file_name}.xml")
+
+ # ==================================================================================================================
+ # Extract sequences
+ # ==================================================================================================================
+
+ print("Extracting sequences...")
+
+ extract_sequences(audioseq_seg, extracted_dir, version_info, write_xml, sequence_table, sequence_font_table,
+ sequence_xmls, soundfonts)
+
+ print("Done")
diff --git a/tools/audio/extraction/disassemble_sequence.py b/tools/audio/extraction/disassemble_sequence.py
new file mode 100644
index 00000000000..ab6b484dd3c
--- /dev/null
+++ b/tools/audio/extraction/disassemble_sequence.py
@@ -0,0 +1,1325 @@
+#!/usr/bin/env python3
+# SPDX-FileCopyrightText: © 2024 ZeldaRET
+# SPDX-License-Identifier: CC0-1.0
+#
+# Audio Sequence Disassembler
+#
+
+"""
+The approach for sequence disassembly is roughly as follows:
+
+```
+ Set COVERAGE=[0 for _ in range(len(data))]
+ Set REF_QUEUE=[]
+
+ Set OFFSET=0
+ Set SECTION=SEQ
+1:
+ Begin sequential disassembly at OFFSET using section type SECTION
+ Collect reference labels and section types into REF_QUEUE
+ Update entries in COVERAGE to 1 as bytes are read
+ End disassembly at `end` instruction
+
+ If REF_QUEUE is not empty:
+ Pop a reference from REF_QUEUE
+ Set OFFSET=loc(reference)
+ Set SECTION=section(reference)
+ goto 1
+
+ If Any 0s in COVERAGE:
+ Set OFFSET=(index of first 0 in COVERAGE)
+ Set SECTION=guess_section(OFFSET) (make a heuristic guess for section based on neighbors)
+ goto 1
+```
+
+There are some additional subtleties for handling padding and uncommon sections like `array`.
+
+For tables used in `dyncall`s, we have to rely on external information to provide the location and size of tables as
+there is no reliable heuristic for identifying table sizes.
+"""
+
+from dataclasses import dataclass
+from enum import Enum, auto
+from typing import Callable, Dict, List, Tuple
+
+from .audiobank_file import AudiobankFile
+
+"""
+TODO
+
+sequence beginning with testchan 0 is a buffer (?)
+OR any ldseq is an array and an array of 0 is a buffer (?)
+
+detect section overlaps and mark them as bugged in the output
+"""
+
+pitch_names = (
+ "A0", "BF0", "B0", "C1", "DF1", "D1", "EF1", "E1", "F1", "GF1", "G1", "AF1", "A1", "BF1", "B1", "C2",
+ "DF2", "D2", "EF2", "E2", "F2", "GF2", "G2", "AF2", "A2", "BF2", "B2", "C3", "DF3", "D3", "EF3", "E3",
+ "F3", "GF3", "G3", "AF3", "A3", "BF3", "B3", "C4", "DF4", "D4", "EF4", "E4", "F4", "GF4", "G4", "AF4",
+ "A4", "BF4", "B4", "C5", "DF5", "D5", "EF5", "E5", "F5", "GF5", "G5", "AF5", "A5", "BF5", "B5", "C6",
+ "DF6", "D6", "EF6", "E6", "F6", "GF6", "G6", "AF6", "A6", "BF6", "B6", "C7", "DF7", "D7", "EF7", "E7",
+ "F7", "GF7", "G7", "AF7", "A7", "BF7", "B7", "C8", "DF8", "D8", "EF8", "E8", "F8", "GF8", "G8", "AF8",
+ "A8", "BF8", "B8", "C9", "DF9", "D9", "EF9", "E9", "F9", "GF9", "G9", "AF9", "A9", "BF9", "B9", "C10",
+ "DF10", "D10", "EF10", "E10", "F10", "BFNEG1", "BNEG1", "C0", "DF0", "D0", "EF0", "E0", "F0", "GF0", "G0", "AF0",
+)
+
+#
+# VERSIONS
+#
+
+class MMLVersion(Enum):
+ OOT = auto()
+ MM = auto()
+
+VERSION_ALL = (MMLVersion.OOT, MMLVersion.MM)
+
+#
+# SECTIONS
+#
+
+class SqSection(Enum):
+ SEQ = auto()
+ CHAN = auto()
+ LAYER = auto()
+ ARRAY = auto()
+ TABLE = auto()
+ ENVELOPE = auto()
+ FILTER = auto()
+ UNKNOWN = auto()
+
+SECTION_ALL = (SqSection.SEQ, SqSection.CHAN, SqSection.LAYER)
+
+#
+# ARGS
+#
+
+def maybe_hex(n):
+ if n < 10:
+ return f"{n}"
+ else:
+ return f"0x{n:X}"
+
+def sign_extend(x, n):
+ sgn = 1 << (n - 1)
+ return (x & (sgn - 1)) - (x & sgn)
+
+class MMLArg:
+ def __init__(self, disas):
+ self.value = self.read(disas)
+
+ def read(self, disas):
+ raise NotImplementedError()
+
+ def analyze(self, disas):
+ pass
+
+ def emit(self, disas):
+ return str(self.value)
+
+class MMLArgBits(MMLArg):
+ def read(self, disas):
+ return disas.read_bits(type(self).NBITS)
+
+class ArgU8(MMLArg):
+ def read(self, disas):
+ return disas.read_u8()
+
+class ArgU4x2(MMLArg):
+ def read(self, disas):
+ return disas.read_u8()
+
+ def emit(self, disas):
+ return f"{(self.value >> 4) & 0xF}, {self.value & 0xF}"
+
+class ArgSeqId(ArgU8):
+ def emit(self, disas):
+ return disas.all_seq_names[self.value]
+
+class ArgFontId(ArgU8): # TODO
+ def read(self, disas):
+ return disas.read_u8()
+
+class ArgPitchU8(ArgU8):
+ def emit(self, disas):
+ return f"PITCH_{pitch_names[self.value]}"
+
+class ArgS8(MMLArg):
+ def read(self, disas):
+ return sign_extend(disas.read_u8(), 8)
+
+class ArgU16(MMLArg):
+ def read(self, disas):
+ return disas.read_u16()
+
+class ArgS16(MMLArg):
+ def read(self, disas):
+ return sign_extend(disas.read_u16(), 16)
+
+class ArgHex8(ArgU8):
+ def emit(self, disas):
+ return f"0x{self.value:02X}"
+
+class ArgHex16(ArgU16):
+ def emit(self, disas):
+ return f"0x{self.value:04X}"
+
+class ArgBitField16(ArgU16):
+ def emit(self, disas):
+ return bin(self.value)
+
+class ArgInstr(ArgU8):
+ def emit(self, disas):
+ builtins = {
+ 126 : "FONTANY_INSTR_SFX",
+ 127 : "FONTANY_INSTR_DRUM",
+ 128 : "FONTANY_INSTR_SAWTOOTH",
+ 129 : "FONTANY_INSTR_TRIANGLE",
+ 130 : "FONTANY_INSTR_SINE",
+ 131 : "FONTANY_INSTR_SQUARE",
+ 132 : "FONTANY_INSTR_NOISE",
+ 133 : "FONTANY_INSTR_BELL",
+ 134 : "FONTANY_INSTR_8PULSE",
+ 135 : "FONTANY_INSTR_4PULSE",
+ 136 : "FONTANY_INSTR_ASM_NOISE",
+ }
+ if self.value in builtins:
+ return builtins[self.value]
+
+ # Check against first font only, this is fine for 99% of cases since most sequences use just one font
+ font0 : AudiobankFile = disas.used_fonts[0]
+
+ if self.value in font0.instrument_index_map:
+ name = f"SF{font0.bank_num}_{font0.instrument_name(self.value)}"
+ else:
+ print(f"Invalid instrument sourced from {font0.name}: {self.value}")
+ name = f"{self.value} /* invalid instrument */"
+ return name
+
+class ArgVar(MMLArg):
+ def read(self, disas):
+ ret = disas.read_u8()
+ if ret & 0x80:
+ ret = ((ret << 8) & 0x7F00) | disas.read_u8()
+ if ret < 128 and disas.insn_begin not in disas.force_long:
+ print(f"Unnecessary use of long immediate encoding @ 0x{disas.insn_begin:X}: {ret}")
+ disas.force_long.add(disas.insn_begin)
+
+ return ret
+
+class ArgPortamentoMode(ArgHex8):
+ def read(self, disas):
+ ret = disas.read_u8()
+ disas.portamento_is_special = (ret & 0x80) != 0
+ return ret
+
+class ArgStereoConfig(ArgU8):
+ def emit(self, disas):
+ assert (self.value & 0b11000000) == 0
+ type = (self.value >> 4) & 0b11
+ strong_right = (self.value >> 3) & 1
+ strong_left = (self.value >> 2) & 1
+ strong_rvrb_right = (self.value >> 1) & 1
+ strong_rvrb_left = (self.value >> 0) & 1
+ return f"{type}, {strong_right}, {strong_left}, {strong_rvrb_right}, {strong_rvrb_left}"
+
+class ArgPortamentoTime(ArgVar):
+ def read(self, disas):
+ if disas.portamento_is_special:
+ return disas.read_u8()
+ else:
+ return super().read(disas)
+
+class ArgBits3(MMLArgBits):
+ NBITS = 3
+
+class ArgBits4(MMLArgBits):
+ NBITS = 4
+
+class IOPort3(ArgBits3):
+ def emit(self, disas):
+ assert self.value in range(0,8)
+ return f"IO_PORT_{self.value}"
+
+class IOPort8(ArgU8):
+ def emit(self, disas):
+ if self.value in range(0,8):
+ return f"IO_PORT_{self.value}"
+ else:
+ return f"{self.value} # BAD IO PORT NUMBER"
+
+class ArgPitch(MMLArgBits):
+ NBITS = 6
+
+ def emit(self, disas):
+ return f"PITCH_{pitch_names[self.value]}"
+
+class ArgAddr(ArgHex16):
+ def analyze(self, disas):
+ disas.add_ref(self.value)
+
+ def emit(self, disas):
+ value = self.value
+
+ target_section = SqSection.UNKNOWN
+ for frag in disas.fragments:
+ if value in range(frag.start,frag.end):
+ target_section = frag.section
+ break
+
+ addend = disas.addends.get(disas.pos, 0)
+ if disas.cur_section in (SqSection.SEQ, SqSection.CHAN, SqSection.LAYER, SqSection.ENVELOPE) and addend == 0:
+ # turn a label that's partway inside an instruction into a label beginning at the instruction + an addend
+ for start,end in disas.insn_ranges:
+ if value in range(start,end):
+ addend = value - start
+ value = start
+ break
+
+ prefix = {
+ SqSection.SEQ : "SEQ",
+ SqSection.CHAN : "CHAN",
+ SqSection.LAYER : "LAYER",
+ SqSection.ARRAY : "ARRAY",
+ SqSection.TABLE : "TABLE",
+ SqSection.ENVELOPE : "ENVELOPE",
+ SqSection.FILTER : "FILTER",
+ SqSection.UNKNOWN : "UNK",
+ }[target_section]
+
+ if addend != 0:
+ return f"{prefix}_{value:04X} + {maybe_hex(addend)}"
+ else:
+ return f"{prefix}_{value:04X}"
+
+class ArgRelAddr8(ArgAddr):
+ def read(self, disas):
+ rel_offset = sign_extend(disas.read_u8(), 8)
+ return disas.pos + rel_offset
+
+class ArgRelAddr16(ArgAddr):
+ def read(self, disas):
+ rel_offset = sign_extend(disas.read_u16(), 16)
+ return disas.pos + rel_offset
+
+class ArgSectionPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, disas.cur_section)
+
+class ArgBigSectionPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, disas.cur_section, big=True)
+
+class ArgRelSectionPtr(ArgRelAddr8):
+ def analyze(self, disas):
+ disas.add_ref(self.value, disas.cur_section)
+
+class ArgSeqPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.SEQ, big=True)
+
+class ArgChanPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.CHAN, big=True)
+
+ def emit(self, disas):
+ return f"CHAN_{self.value:04X}"
+
+class ArgRelChanPtr(ArgRelAddr16):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.CHAN, big=True)
+
+ def emit(self, disas):
+ return f"CHAN_{self.value:04X}"
+
+class ArgLayerPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.LAYER, big=True)
+
+ def emit(self, disas):
+ return f"LAYER_{self.value:04X}"
+
+class ArgRelLayerPtr(ArgRelAddr16):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.LAYER, big=True)
+
+ def emit(self, disas):
+ return f"LAYER_{self.value:04X}"
+
+class ArgArrayPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.ARRAY, big=True)
+
+class ArgEnvPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.ENVELOPE, big=True)
+
+ def emit(self, disas):
+ return f"ENVELOPE_{self.value:04X}"
+
+class ArgFilterPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.FILTER, big=True)
+
+ def emit(self, disas):
+ return f"FILTER_{self.value:04X}"
+
+class ArgTblPtr(ArgAddr):
+ def analyze(self, disas):
+ # if disas.pos in (0x5FD0,):
+ # return
+ disas.add_ref(self.value, SqSection.TABLE, big=True)
+
+ def emit(self, disas):
+ return f"TABLE_{self.value:04X}"
+
+class ArgUnkPtr(ArgAddr):
+ def analyze(self, disas):
+ disas.add_ref(self.value, SqSection.UNKNOWN)
+
+#
+# COMMANDS
+#
+
+@dataclass
+class MMLCmd:
+ cmd_id : int
+ mnemonic : str
+ args : Tuple[MMLArg] = ()
+ is_branch : bool = False
+ is_branch_unconditional : bool = False
+ is_terminal : bool = False
+ handler : Callable = None
+ sections : Tuple[SqSection] = SECTION_ALL
+ version : Tuple[MMLVersion] = VERSION_ALL
+
+def nesting_decr(cmd, disas):
+ disas.nesting -= 1
+ if disas.nesting < 0:
+ disas.nesting = 0
+
+def nesting_incr(cmd, disas):
+ disas.nesting += 1
+
+def set_short(cmd, disas):
+ disas.large_notes = False
+
+def set_large(cmd, disas):
+ disas.large_notes = True
+
+#
+# NOTE: Changes here must be reflected in aseq.h for re-assembly
+#
+CMD_SPEC = (
+ #
+ # Control Flow Commands
+ #
+ MMLCmd(0xFF, 'end', is_terminal=True, handler=nesting_decr),
+ MMLCmd(0xFE, 'delay1'),
+ MMLCmd(0xFD, 'delay', args=(ArgVar,), sections=(SqSection.SEQ, SqSection.CHAN,)),
+ MMLCmd(0xFC, 'call', args=(ArgBigSectionPtr,)),
+ MMLCmd(0xFB, 'jump', args=(ArgSectionPtr,), is_branch=True, is_branch_unconditional=True),
+ MMLCmd(0xFA, 'beqz', args=(ArgSectionPtr,), is_branch=True),
+ MMLCmd(0xF9, 'bltz', args=(ArgSectionPtr,), is_branch=True),
+ MMLCmd(0xF8, 'loop', args=(ArgU8,), handler=nesting_incr),
+ MMLCmd(0xF7, 'loopend', handler=nesting_decr),
+ MMLCmd(0xF6, 'break', handler=nesting_decr),
+ MMLCmd(0xF5, 'bgez', args=(ArgSectionPtr,), is_branch=True),
+ MMLCmd(0xF4, 'rjump', args=(ArgRelSectionPtr,), is_branch=True, is_branch_unconditional=True),
+ MMLCmd(0xF3, 'rbeqz', args=(ArgRelSectionPtr,), is_branch=True),
+ MMLCmd(0xF2, 'rbltz', args=(ArgRelSectionPtr,), is_branch=True),
+
+ #
+ # SEQ commands
+ #
+ # non-argbits commands
+ MMLCmd(0xF1, 'allocnotelist', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xF0, 'freenotelist', sections=(SqSection.SEQ,)),
+ MMLCmd(0xEF, 'unk_EF', sections=(SqSection.SEQ,), args=(ArgS16, ArgU8,)),
+ MMLCmd(0xDF, 'transpose', sections=(SqSection.SEQ,), args=(ArgS8,)),
+ MMLCmd(0xDE, 'rtranspose', sections=(SqSection.SEQ,), args=(ArgS8,)),
+ MMLCmd(0xDD, 'tempo', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xDC, 'tempochg', sections=(SqSection.SEQ,), args=(ArgS8,)),
+ MMLCmd(0xDB, 'vol', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xDA, 'volmode', sections=(SqSection.SEQ,), args=(ArgU8, ArgS16)),
+ MMLCmd(0xD9, "volscale", sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xD7, 'initchan', sections=(SqSection.SEQ,), args=(ArgBitField16,)),
+ MMLCmd(0xD6, 'freechan', sections=(SqSection.SEQ,), args=(ArgBitField16,)),
+ MMLCmd(0xD5, 'mutescale', sections=(SqSection.SEQ,), args=(ArgS8,)),
+ MMLCmd(0xD4, 'mute', sections=(SqSection.SEQ,)),
+ MMLCmd(0xD3, 'mutebhv', sections=(SqSection.SEQ,), args=(ArgHex8,)),
+ MMLCmd(0xD2, 'ldshortvelarr', sections=(SqSection.SEQ,), args=(ArgArrayPtr,)), # length 16
+ MMLCmd(0xD1, 'ldshortgatearr', sections=(SqSection.SEQ,), args=(ArgArrayPtr,)), # length 16
+ MMLCmd(0xD0, 'notealloc', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xCE, 'rand', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xCD, 'dyncall', sections=(SqSection.SEQ,), args=(ArgTblPtr,)),
+ MMLCmd(0xCC, 'ldi', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xC9, 'and', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xC8, 'sub', sections=(SqSection.SEQ,), args=(ArgU8,)),
+ MMLCmd(0xC7, 'stseq', sections=(SqSection.SEQ,), args=(ArgU8, ArgAddr,)),
+ MMLCmd(0xC6, 'stop', sections=(SqSection.SEQ,)),
+ MMLCmd(0xC5, 'scriptctr', sections=(SqSection.SEQ,), args=(ArgU16,)),
+ MMLCmd(0xC4, 'runseq', sections=(SqSection.SEQ,), args=(ArgU8, ArgSeqId,)),
+ MMLCmd(0xC3, 'mutechan', sections=(SqSection.SEQ,), args=(ArgS16,), version=(MMLVersion.MM,)),
+ # argbits commands
+ MMLCmd(0x00, 'testchan', sections=(SqSection.SEQ,), args=(ArgBits4,)),
+ MMLCmd(0x40, 'stopchan', sections=(SqSection.SEQ,), args=(ArgBits4,)),
+ MMLCmd(0x50, 'subio', sections=(SqSection.SEQ,), args=(IOPort3,)),
+ MMLCmd(0x60, 'ldres', sections=(SqSection.SEQ,), args=(ArgBits4, ArgU8, ArgU8,)),
+ MMLCmd(0x70, 'stio', sections=(SqSection.SEQ,), args=(IOPort3,)),
+ MMLCmd(0x80, 'ldio', sections=(SqSection.SEQ,), args=(IOPort3,)),
+ MMLCmd(0x90, 'ldchan', sections=(SqSection.SEQ,), args=(ArgBits4, ArgChanPtr,)),
+ MMLCmd(0xA0, 'rldchan', sections=(SqSection.SEQ,), args=(ArgBits4, ArgRelChanPtr,)),
+ MMLCmd(0xB0, 'ldseq', sections=(SqSection.SEQ,), args=(ArgBits4, ArgSeqId, ArgUnkPtr,)),
+
+ #
+ # CHAN commands
+ #
+ # non-argbits commands
+ MMLCmd(0xF1, 'allocnotelist', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xF0, 'freenotelist', sections=(SqSection.CHAN,)),
+ MMLCmd(0xEE, 'bendfine', sections=(SqSection.CHAN,), args=(ArgS8,)),
+ MMLCmd(0xED, 'gain', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xEC, 'vibreset', sections=(SqSection.CHAN,)),
+ MMLCmd(0xEB, 'fontinstr', sections=(SqSection.CHAN,), args=(ArgFontId, ArgInstr)),
+ MMLCmd(0xEA, 'stop', sections=(SqSection.CHAN,)),
+ MMLCmd(0xE9, 'notepri', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xE8, 'params', sections=(SqSection.CHAN,), args=(ArgU8, ArgU8, ArgU8, ArgS8, ArgS8, ArgU8, ArgU8, ArgU8,)),
+ MMLCmd(0xE7, 'ldparams', sections=(SqSection.CHAN,), args=(ArgAddr,)),
+ MMLCmd(0xE6, 'samplebook', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xE5, 'reverbidx', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xE4, 'dyncall', sections=(SqSection.CHAN,)),
+ MMLCmd(0xE3, 'vibdelay', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xE2, 'vibdepthgrad', sections=(SqSection.CHAN,), args=(ArgU8, ArgU8, ArgU8,)),
+ MMLCmd(0xE1, 'vibfreqgrad', sections=(SqSection.CHAN,), args=(ArgU8, ArgU8, ArgU8,)),
+ MMLCmd(0xE0, 'volexp', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xDF, 'vol', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xDE, 'freqscale', sections=(SqSection.CHAN,), args=(ArgU16,)),
+ MMLCmd(0xDD, 'pan', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xDC, 'panweight', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xDB, 'transpose', sections=(SqSection.CHAN,), args=(ArgS8,)),
+ MMLCmd(0xDA, 'env', sections=(SqSection.CHAN,), args=(ArgEnvPtr,)),
+ MMLCmd(0xD9, 'releaserate', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD8, 'vibdepth', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD7, 'vibfreq', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD4, 'reverb', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD3, 'bend', sections=(SqSection.CHAN,), args=(ArgS8,)),
+ MMLCmd(0xD2, 'sustain', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD1, 'notealloc', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xD0, 'effects', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xCF, 'stptrtoseq', sections=(SqSection.CHAN,), args=(ArgAddr,)),
+ MMLCmd(0xCE, 'ldptr', sections=(SqSection.CHAN,), args=(ArgAddr,)),
+ MMLCmd(0xCD, 'stopchan', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xCC, 'ldi', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xCB, 'ldseq', sections=(SqSection.CHAN,), args=(ArgUnkPtr,)),
+ MMLCmd(0xCA, 'mutebhv', sections=(SqSection.CHAN,), args=(ArgHex8,)),
+ MMLCmd(0xC9, 'and', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xC8, 'sub', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xC7, 'stseq', sections=(SqSection.CHAN,), args=(ArgU8, ArgAddr,)),
+ MMLCmd(0xC6, 'font', sections=(SqSection.CHAN,), args=(ArgFontId,)),
+ MMLCmd(0xC5, 'dyntbllookup', sections=(SqSection.CHAN,)),
+ MMLCmd(0xC4, 'noshort', sections=(SqSection.CHAN,), handler=set_large),
+ MMLCmd(0xC3, 'short', sections=(SqSection.CHAN,), handler=set_short),
+ MMLCmd(0xC2, 'dyntbl', sections=(SqSection.CHAN,), args=(ArgTblPtr,)),
+ MMLCmd(0xC1, 'instr', sections=(SqSection.CHAN,), args=(ArgInstr,)),
+ MMLCmd(0xBE, 'unk_BE', sections=(SqSection.CHAN,), args=(ArgU8,), version=(MMLVersion.MM,)),
+ MMLCmd(0xBD, 'randptr', sections=(SqSection.CHAN,), args=(ArgU16, ArgU16,), version=(MMLVersion.OOT,)),
+ MMLCmd(0xBD, 'samplestart', sections=(SqSection.CHAN,), args=(ArgU8,), version=(MMLVersion.MM,)),
+ MMLCmd(0xBC, 'ptradd', sections=(SqSection.CHAN,), args=(ArgHex16,)),
+ MMLCmd(0xBB, 'combfilter', sections=(SqSection.CHAN,), args=(ArgU8, ArgU16)),
+ MMLCmd(0xBA, 'randgate', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xB9, 'randvel', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xB8, 'rand', sections=(SqSection.CHAN,), args=(ArgU8,)),
+ MMLCmd(0xB7, 'randtoptr', sections=(SqSection.CHAN,), args=(ArgU16,)),
+ MMLCmd(0xB6, 'dyntblv', sections=(SqSection.CHAN,)),
+ MMLCmd(0xB5, 'dyntbltoptr', sections=(SqSection.CHAN,)),
+ MMLCmd(0xB4, 'ptrtodyntbl', sections=(SqSection.CHAN,)),
+ MMLCmd(0xB3, 'filter', sections=(SqSection.CHAN,), args=(ArgU4x2,)),
+ MMLCmd(0xB2, 'ldseqtoptr', sections=(SqSection.CHAN,), args=(ArgTblPtr,)),
+ MMLCmd(0xB1, 'freefilter', sections=(SqSection.CHAN,)),
+ MMLCmd(0xB0, 'ldfilter', sections=(SqSection.CHAN,), args=(ArgFilterPtr,)),
+ MMLCmd(0xAA, 'unk_AA', sections=(SqSection.CHAN,), args=(), version=(MMLVersion.MM,)),
+ MMLCmd(0xA8, 'randptr', sections=(SqSection.CHAN,), args=(ArgU16, ArgU16,), version=(MMLVersion.MM,)),
+ MMLCmd(0xA7, 'unk_A7', sections=(SqSection.CHAN,), args=(ArgVar,), version=(MMLVersion.MM,)),
+ MMLCmd(0xA6, 'unk_A6', sections=(SqSection.CHAN,), args=(ArgVar, ArgVar,), version=(MMLVersion.MM,)),
+ MMLCmd(0xA5, 'unk_A5', sections=(SqSection.CHAN,), args=(), version=(MMLVersion.MM,)),
+ MMLCmd(0xA4, 'unk_A4', sections=(SqSection.CHAN,), args=(ArgVar,), version=(MMLVersion.MM,)),
+ MMLCmd(0xA3, 'unk_A3', sections=(SqSection.CHAN,), args=(), version=(MMLVersion.MM,)),
+ MMLCmd(0xA2, 'unk_A2', sections=(SqSection.CHAN,), args=(ArgVar,), version=(MMLVersion.MM,)),
+ MMLCmd(0xA1, 'unk_A1', sections=(SqSection.CHAN,), args=(), version=(MMLVersion.MM,)),
+ MMLCmd(0xA0, 'unk_A0', sections=(SqSection.CHAN,), args=(ArgVar,), version=(MMLVersion.MM,)),
+ # argbits commands
+ MMLCmd(0x00, 'cdelay', sections=(SqSection.CHAN,), args=(ArgBits4,)),
+ MMLCmd(0x10, 'sample', sections=(SqSection.CHAN,), args=(ArgBits3, ArgAddr,)),
+ MMLCmd(0x18, 'sampleptr', sections=(SqSection.CHAN,), args=(ArgBits3, ArgAddr,)),
+ MMLCmd(0x20, 'ldchan', sections=(SqSection.CHAN,), args=(ArgBits4, ArgChanPtr,)),
+ MMLCmd(0x30, 'stcio', sections=(SqSection.CHAN,), args=(ArgBits4, IOPort8,)),
+ MMLCmd(0x40, 'ldcio', sections=(SqSection.CHAN,), args=(ArgBits4, IOPort8,)),
+ MMLCmd(0x50, 'subio', sections=(SqSection.CHAN,), args=(IOPort3,)),
+ MMLCmd(0x60, 'ldio', sections=(SqSection.CHAN,), args=(IOPort3,)),
+ MMLCmd(0x70, 'stio', sections=(SqSection.CHAN,), args=(IOPort3,)),
+ MMLCmd(0x78, 'rldlayer', sections=(SqSection.CHAN,), args=(ArgBits3, ArgRelLayerPtr,)),
+ MMLCmd(0x80, 'testlayer', sections=(SqSection.CHAN,), args=(ArgBits3,)),
+ MMLCmd(0x88, 'ldlayer', sections=(SqSection.CHAN,), args=(ArgBits3, ArgLayerPtr,)),
+ MMLCmd(0x90, 'dellayer', sections=(SqSection.CHAN,), args=(ArgBits3,)),
+ MMLCmd(0x98, 'dynldlayer', sections=(SqSection.CHAN,), args=(ArgBits3,)),
+
+ #
+ # LAYER commands
+ #
+ # non-argbits commands
+ MMLCmd(0xC0, 'ldelay', sections=(SqSection.LAYER,), args=(ArgVar,)),
+ MMLCmd(0xC1, 'shortvel', sections=(SqSection.LAYER,), args=(ArgU8,)),
+ MMLCmd(0xC2, 'transpose', sections=(SqSection.LAYER,), args=(ArgS8,)),
+ MMLCmd(0xC3, 'shortdelay', sections=(SqSection.LAYER,), args=(ArgVar,)),
+ MMLCmd(0xC4, 'legato', sections=(SqSection.LAYER,)),
+ MMLCmd(0xC5, 'nolegato', sections=(SqSection.LAYER,)),
+ MMLCmd(0xC6, 'instr', sections=(SqSection.LAYER,), args=(ArgInstr,)),
+ MMLCmd(0xC7, 'portamento', sections=(SqSection.LAYER,), args=(ArgPortamentoMode, ArgPitchU8, ArgPortamentoTime,)),
+ MMLCmd(0xC8, 'noportamento', sections=(SqSection.LAYER,)),
+ MMLCmd(0xC9, 'shortgate', sections=(SqSection.LAYER,), args=(ArgU8,)),
+ MMLCmd(0xCA, 'notepan', sections=(SqSection.LAYER,), args=(ArgU8,)),
+ MMLCmd(0xCB, 'env', sections=(SqSection.LAYER,), args=(ArgEnvPtr, ArgU8,)),
+ MMLCmd(0xCC, 'nodrumpan', sections=(SqSection.LAYER,)),
+ MMLCmd(0xCD, 'stereo', sections=(SqSection.LAYER,), args=(ArgStereoConfig,)),
+ MMLCmd(0xCE, 'bendfine', sections=(SqSection.LAYER,), args=(ArgS8,)),
+ MMLCmd(0xCF, 'releaserate', sections=(SqSection.LAYER,), args=(ArgU8,)),
+ MMLCmd(0xD0, 'ldshortvel', sections=(SqSection.LAYER,), args=(ArgBits4,)),
+ MMLCmd(0xE0, 'ldshortgate', sections=(SqSection.LAYER,), args=(ArgBits4,)),
+ MMLCmd(0xF0, 'unk_F0', sections=(SqSection.LAYER,), args=(ArgS16,), version=(MMLVersion.MM,)),
+ MMLCmd(0xF1, 'surroundeffect', sections=(SqSection.LAYER,), args=(ArgU8,), version=(MMLVersion.MM,)),
+ # argbits commands
+ # large layer
+ MMLCmd(0x00, 'notedvg', sections=(SqSection.LAYER,), args=(ArgPitch, ArgVar, ArgU8, ArgU8,)),
+ MMLCmd(0x40, 'notedv', sections=(SqSection.LAYER,), args=(ArgPitch, ArgVar, ArgU8,)),
+ MMLCmd(0x80, 'notevg', sections=(SqSection.LAYER,), args=(ArgPitch, ArgU8, ArgU8,)),
+ # small layer
+ MMLCmd(0x00, 'shortdvg', sections=(SqSection.LAYER,), args=(ArgPitch, ArgVar,)),
+ MMLCmd(0x40, 'shortdv', sections=(SqSection.LAYER,), args=(ArgPitch,)),
+ MMLCmd(0x80, 'shortvg', sections=(SqSection.LAYER,), args=(ArgPitch,)),
+)
+
+#
+# DISASSEMBLER
+#
+
+class SequenceFragment:
+ def __init__(self, disas, section, data, start, end):
+ assert len(data) == end - start , f"Bad: got {len(data)} bytes for range [{start}:{end}] {data}"
+
+ self.section = section
+ self.data = data
+ self.start = start
+ self.end = end
+ self.disas = disas
+
+ def __str__(self):
+ return f"Fragment ({self.section}) [{self.start}, {self.end}]" # , {{{self.data}}}
+
+ def __lt__(self, other):
+ return self.start < other.start
+
+ @staticmethod
+ def merge(frag1, frag2):
+ if frag1 == frag2:
+ return frag1
+
+ # assert frag1.section == frag2.section , "Tried to merge fragments of different section"
+ if frag1.section != frag2.section:
+ return None
+
+ # don't merge envelopes or tables ever
+ if frag1.section in (SqSection.ENVELOPE, SqSection.TABLE):
+ return None
+
+ min_start = min(frag1.start, frag2.start)
+ max_start = max(frag1.start, frag2.start)
+ min_end = min(frag1.end, frag2.end)
+ max_end = max(frag1.end, frag2.end)
+
+ # assert max_start <= min_end , "Tried to merge disjoint fragments"
+ if max_start > min_end:
+ return None
+
+ data1, data2 = frag1.data, frag2.data
+ if frag2.start < frag1.start:
+ data1, data2 = data2, data1
+
+ if min_end == max_end:
+ # data1 contains data2
+ return SequenceFragment(frag1.disas, frag1.section, data1, min_start, max_end)
+
+ assert data1[max_start:] == data2[:len(data1)-max_start] , \
+ f"Data does not agree on overlap between\n{frag1}\n{frag2}\n{data2[:len(data1)-max_start]}"
+
+ return SequenceFragment(frag1.disas, frag1.section, data1[:max_start] + data2, min_start, max_end)
+
+class SequenceDisassembler:
+
+ def __init__(self, seq_num : int, data : bytes, tables, cmds : Tuple[MMLCmd], version : MMLVersion, outpath : str,
+ seq_name : str, used_fonts : List[AudiobankFile], all_seq_names):
+ self.seq_num = seq_num
+ self.seq_name = seq_name
+ self.used_fonts = used_fonts
+
+ self.all_seq_names = all_seq_names
+
+ self.pos = 0
+ self.insn_begin = 0
+ self.data = data
+ self.hit_eof = False
+ self.cur_section = SqSection.SEQ
+ self.nesting = 0
+ self.portamento_is_special = False
+ self.large_notes = True
+
+ self.outpath = outpath
+
+ self.cmds : Dict[SqSection, Dict[int, MMLCmd]] = {
+ SqSection.SEQ : {},
+ SqSection.CHAN : {},
+ SqSection.LAYER : {},
+ }
+
+ # preprocess command list into dictionary, possibly duplicating into several id keys if any lsbits are used
+ # as an arg
+ for cmd in cmds:
+ # ignore commands not in this version
+ if version not in cmd.version:
+ continue
+
+ # find number of lsbits that don't contribute to the command id
+ if len(cmd.args) > 0 and issubclass(cmd.args[0], MMLArgBits):
+ nbits = cmd.args[0].NBITS
+ else:
+ nbits = 0
+
+ id = cmd.cmd_id
+
+ for section in cmd.sections:
+ cmds_s = self.cmds[section]
+
+ for i in range(1 << nbits):
+ new = cmd
+ old = cmds_s.get(id + i, None)
+ if old is not None:
+ assert old.mnemonic in ("notedvg", "notedv", "notevg"), (old.mnemonic, cmd.mnemonic)
+ new = (old, cmd)
+
+ cmds_s[id + i] = new
+
+ self.force_long = set()
+
+ self.insn_ranges = []
+
+ self.coverage = [0] * len(self.data)
+
+ self.fragments = []
+
+ self.branch_targets = {}
+ self.big_labels = set()
+
+ self.all_ranges = []
+
+ self.decode_list = []
+ self.all_seen = []
+
+ self.tables = tables
+ self.table_cache = set()
+
+ self.addends = {}
+
+ self.unused = []
+
+ # general helpers
+
+ def read_bits(self, nbits):
+ return self.bits_val
+
+ def read_u8(self):
+ if self.hit_eof:
+ raise Exception()
+
+ if self.pos == len(self.data):
+ self.hit_eof = True
+ ret = None
+ else:
+ ret = self.data[self.pos]
+ self.pos += 1
+ return ret
+
+ def read_u16(self):
+ return (self.read_u8() << 8) | self.read_u8()
+
+ def read_s16(self):
+ return sign_extend(self.read_u16(), 16)
+
+ def lookup_cmd(self, id : int) -> MMLCmd:
+ # lookup command info
+ cmd : MMLCmd = self.cmds[self.cur_section].get(id, None)
+ assert cmd is not None , (self.cur_section, id, self.cmds)
+
+ if isinstance(cmd, tuple):
+ # select based on whether we're dealing with large or short notes
+ cmd = cmd[int(not self.large_notes)]
+
+ # part of the command byte may be an arg, save the value
+ mask = 0
+ if len(cmd.args) > 0 and issubclass(cmd.args[0], MMLArgBits):
+ mask = (1 << cmd.args[0].NBITS) - 1
+ self.bits_val = id & mask
+
+ return cmd
+
+ #
+ # analysis helpers
+ #
+
+ def register_addend(self, pos, value):
+ self.addends[pos] = value
+
+ def add_branch_target(self, value, section, big=False):
+ self.branch_targets[value] = section
+ if big:
+ self.big_labels.add(value)
+
+ def add_ref(self, value, section=None, big=False):
+ if section is None:
+ # print(f"TODO Unhandled addr ref 0x{value:04X}")
+ self.add_branch_target(value, SqSection.UNKNOWN)
+ return
+
+ self.add_branch_target(value, section, big=big)
+
+ # print(f"Referenced {section} at 0x{value:04X}")
+ self.add_job(value, section, self.cur_section)
+
+ def add_job(self, value, section, from_section=None):
+ if value not in self.all_seen:
+ # print(f"New job {section} at 0x{value:04X}")
+
+ self.all_seen.append(value)
+ self.decode_list.append((value, section, from_section or section))
+
+ def merge_frags(self):
+ self.fragments = list(sorted(self.fragments))
+
+ if len(self.fragments) < 2:
+ return
+
+ i = 0
+ while i != len(self.fragments) - 1:
+ frag1 = self.fragments[i]
+ frag2 = self.fragments[i + 1]
+ merged = SequenceFragment.merge(frag1, frag2)
+ if merged is not None:
+ self.fragments[i] = merged
+ del self.fragments[i + 1]
+ else:
+ i += 1
+
+ #
+ # analysis handlers
+ #
+
+ def analyze_code(self): # sequence, channel, layer
+ start_pos = self.pos
+
+ # print(f" << [0x{start_pos:X}/0x{len(self.data):X}] :: {self.cur_section} >>")
+
+ self.insn_begin = start_pos
+ cmd_byte = self.read_u8()
+ cmd = self.lookup_cmd(cmd_byte)
+
+ assert cmd is not None , f"Bad command ID 0x{cmd_byte:02X} for section {self.cur_section.name} at 0x{start_pos:X}"
+ # print(hex(cmd_byte))
+
+ args = [argtype(self) for argtype in cmd.args]
+
+ raw_data = self.data[start_pos:self.pos]
+
+ self.insn_ranges.append((start_pos, self.pos))
+
+ for i in range(start_pos,self.pos):
+ self.coverage[i] = self.cur_section
+
+ # print(f"/* 0x{start_pos:04X} [{' '.join([f'0x{b:02X}' for b in raw_data]):24}] */ {cmd.mnemonic:11} {', '.join([arg.emit(self) for arg in args])}".strip())
+
+ for arg in args:
+ arg.analyze(self)
+
+ if cmd.handler is not None:
+ cmd.handler(cmd, self)
+
+ self.fragments.append(SequenceFragment(self, self.cur_section, raw_data, start_pos, self.pos))
+
+ if not (cmd.is_branch_unconditional or cmd.is_terminal):
+ self.decode_list.append((self.pos, self.cur_section, self.cur_section))
+
+ def analyze_table(self):
+ # print(f"Table from {self.refd_from} at 0x{self.pos:04X}")
+
+ assert self.tables is not None, "Found a table but no table spec provided."
+
+ for offset, num_entries, addend, section in self.tables:
+ if self.pos in range(offset, offset + 2 * num_entries):
+ break
+ else:
+ assert False , f"Found table at {self.pos:04X} but no entry number provided"
+
+ start_pos = self.pos = offset
+ if start_pos in self.table_cache:
+ return
+
+ for _ in range(num_entries):
+ curpos = self.pos
+ cur = self.read_u16() - addend
+ if cur >= len(self.data) - 1:
+ assert False , "Bad table pointer"
+ # print(hex(cur))
+
+ self.add_branch_target(cur, section, big=True)
+ self.add_job(cur, section, section)
+ self.register_addend(curpos, addend)
+
+ self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
+ self.table_cache.add(start_pos)
+
+ def analyze_array(self):
+ start_pos = self.pos
+
+ # TODO better heuristic than just hardcoding 16 lmao
+ # it would be better to wait until later to resize arrays though, up to the next identified fragment
+ # ARRAY + UNK + OTHER -> ARRAY + OTHER
+ for _ in range(16):
+ assert self.read_u8() == 0
+
+ self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
+
+ def analyze_filter(self):
+ start_pos = self.pos
+
+ for _ in range(8):
+ assert self.read_u16() == 0
+
+ self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
+
+ def analyze_envelope(self):
+ # print(f"Envelope from {self.refd_from} at 0x{self.pos:04X}")
+ start_pos = self.pos
+
+ while True: # dangerous
+ delay = self.read_s16()
+ arg = self.read_s16()
+ if delay < 0:
+ break
+
+ self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
+
+ def analyze_unknown(self):
+ self.fragments.append(SequenceFragment(self, self.cur_section, self.data[self.pos:self.pos+2], self.pos, self.pos+2))
+
+ def analyze(self):
+ # mark offset 0 as a SEQ section
+ self.add_branch_target(0, SqSection.SEQ, big=True)
+ self.decode_list.append((0, SqSection.SEQ, SqSection.SEQ))
+
+ # analyze all sections, following branches to locate new sections
+ while len(self.decode_list) != 0:
+ self.pos, self.cur_section, self.refd_from = self.decode_list.pop()
+
+ if self.pos >= len(self.data):
+ # ignore sections that begin past the end of the file
+ # TODO should be an error or warning?
+ continue
+
+ # execute handler based on section
+ {
+ SqSection.SEQ : self.analyze_code,
+ SqSection.CHAN : self.analyze_code,
+ SqSection.LAYER : self.analyze_code,
+ SqSection.TABLE : self.analyze_table,
+ SqSection.ARRAY : self.analyze_array,
+ SqSection.FILTER : self.analyze_filter,
+ SqSection.ENVELOPE : self.analyze_envelope,
+ SqSection.UNKNOWN : self.analyze_unknown,
+ }[self.cur_section]()
+
+ # merge fragments
+ self.merge_frags()
+
+ # update coverage
+ self.final_cvg = [0] * len(self.data)
+ for frag in self.fragments:
+ for i in range(frag.start,frag.end):
+ self.final_cvg[i] = frag.section
+
+ # resolve gaps in coverage
+ while True:
+ # keeps going until there's no zeros except for padding
+ try:
+ first_zero_idx = self.final_cvg.index(0)
+ except ValueError:
+ break # no more gaps
+
+ # there was a gap, handle it
+
+ if ((first_zero_idx + 0xF) & ~0xF) == len(self.data) and \
+ all(b == 0 for b in self.final_cvg[first_zero_idx:]) and \
+ all(b == 0 for b in self.data[first_zero_idx:]):
+ # there's only padding left, we're done
+ break
+ else:
+ # resolve non-padding gaps with heuristics
+
+ # TODO any unknown data after a `jump` in a sequence frag should extend the sequence frag
+ # TODO any unknown data before a filter should be a balign 16
+
+ last_zero_idx = first_zero_idx
+ while self.final_cvg[last_zero_idx] == 0 and last_zero_idx < len(self.final_cvg)-1:
+ self.final_cvg[last_zero_idx] = SqSection.UNKNOWN
+ last_zero_idx += 1
+
+ num_unk = last_zero_idx - first_zero_idx
+
+ emit_unk = True
+
+ prev_frag = None
+ prev_frag_idx = None
+ next_frag = None
+ next_frag_idx = None
+
+ for i,frag in enumerate(self.fragments):
+ if frag.end == first_zero_idx:
+ prev_frag = frag
+ prev_frag_idx = i
+ # print("prev", i, file=sys.stderr)
+ elif frag.start == last_zero_idx:
+ next_frag = frag
+ next_frag_idx = i
+ # print("next", i, file=sys.stderr)
+ # print(self.fragments[prev_frag_idx + 1], file=sys.stderr)
+
+ # SEQ + UNK -> SEQ
+ if prev_frag is not None:
+ if prev_frag.section == SqSection.SEQ:
+ self.fragments[prev_frag_idx] = SequenceFragment(self, SqSection.SEQ,
+ self.data[prev_frag.start:last_zero_idx],
+ prev_frag.start, last_zero_idx)
+ emit_unk = False
+
+ if next_frag is not None:
+ # UNK + FILTER -> FILTER
+ if next_frag.section == SqSection.FILTER and num_unk < 16:
+ emit_unk = False
+
+ # UNK + TABLE -> TABLE
+ if next_frag.section == SqSection.TABLE and num_unk < 2:
+ emit_unk = False
+
+ if prev_frag is not None and next_frag is not None:
+ # LAYER + UNK + LAYER -> LAYER LAYER LAYER
+ if prev_frag.section == SqSection.LAYER and next_frag.section == SqSection.LAYER:
+ self.fragments.append(SequenceFragment(self, SqSection.LAYER, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+ emit_unk = False
+
+ # LAYER + UNK + CHANNEL -> LAYER LAYER CHANNEL
+ if prev_frag.section == SqSection.LAYER and next_frag.section == SqSection.CHAN:
+ self.fragments.append(SequenceFragment(self, SqSection.LAYER, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+ emit_unk = False
+
+ # TABLE + UNK + ENVELOPE -> TABLE + ENVELOPE.. + ENVELOPE
+ if prev_frag.section == SqSection.TABLE and next_frag.section == SqSection.ENVELOPE:
+ self.fragments.append(SequenceFragment(self, SqSection.ENVELOPE, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+ emit_unk = False
+
+ # ENVELOPE + UNK + ENVELOPE -> ENVELOPE + ENVELOPE.. + ENVELOPE
+ if prev_frag.section == SqSection.ENVELOPE and next_frag.section == SqSection.ENVELOPE:
+ self.fragments.append(SequenceFragment(self, SqSection.ENVELOPE, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+ emit_unk = False
+
+ if prev_frag is not None and next_frag is None:
+ # ENVELOPE + UNK + END -> ENVELOPE + ENVELOPE.. + FILTER.. + END
+ if prev_frag.section == SqSection.ENVELOPE:
+ if all(b == 0 for b in self.data[first_zero_idx:]):
+ for k in range(first_zero_idx, len(self.data), 16):
+ if k + 16 > len(self.data):
+ # padding
+ break
+ self.fragments.append(SequenceFragment(self, SqSection.FILTER, self.data[k:k+16], k, k + 16))
+ else:
+ self.fragments.append(SequenceFragment(self, SqSection.ENVELOPE, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+ emit_unk = False
+
+ if emit_unk:
+ # leave it unknown for now, TODO make reasonable guesses
+ self.add_branch_target(first_zero_idx, SqSection.UNKNOWN)
+ self.fragments.append(SequenceFragment(self, SqSection.UNKNOWN, self.data[first_zero_idx:last_zero_idx], first_zero_idx, last_zero_idx))
+
+ #
+ # disas helpers
+ #
+
+ def label_section_prefix(self, section):
+ return {
+ SqSection.SEQ : f"SEQ",
+ SqSection.CHAN : f"CHAN",
+ SqSection.LAYER : f"LAYER",
+ SqSection.ARRAY : f"ARRAY",
+ SqSection.TABLE : f"TABLE",
+ SqSection.ENVELOPE : f"ENVELOPE",
+ SqSection.FILTER : f"FILTER",
+ SqSection.UNKNOWN : f"UNK",
+ }[section]
+
+ def label_name(self, value, section, force_big=False):
+ if value in self.big_labels or force_big:
+ prefix = {
+ SqSection.SEQ : ".sequence ",
+ SqSection.CHAN : ".channel ",
+ SqSection.LAYER : ".layer ",
+ SqSection.ARRAY : ".array ",
+ SqSection.TABLE : ".table ",
+ SqSection.ENVELOPE : ".envelope ",
+ SqSection.FILTER : ".filter ",
+ SqSection.UNKNOWN : "",
+ }[section]
+ suffix = ""
+ else:
+ prefix = ""
+ suffix = ":"
+
+ return f"{prefix}{self.label_section_prefix(section)}_{value:04X}{suffix}"
+
+ def emit_branch_target_real(self, outfile, value, section, force_big=False):
+ if section is SqSection.UNKNOWN:
+ for frag in self.fragments:
+ if value in range(frag.start, frag.end):
+ section = frag.section
+ break
+
+ outfile.write(f"{self.label_name(value, section, force_big)}\n")
+
+ def emit_branch_target(self, outfile, start, end, force_big=False):
+ did_emit = False
+ for b_tgt in self.branch_targets:
+ if b_tgt in range(start,end):
+ #print(b_tgt, self.branch_targets[b_tgt])
+ # assert self.cur_section == b_sect
+ self.emit_branch_target_real(outfile, start, self.branch_targets[b_tgt], force_big)
+ did_emit = True
+ return did_emit
+
+ #
+ # disas handlers
+ #
+
+ def disas_section(self, frag : SequenceFragment, outfile):
+ force_big_lbl = False
+
+ if self.pos == frag.start:
+ # If the previous frag is not the same type as this frag, force the first label to be a big label
+ for frag2 in self.fragments:
+ frag2 : SequenceFragment
+
+ if frag2.end == frag.start:
+ if frag2.section != frag.section:
+ force_big_lbl = True
+ break
+
+ while self.pos < frag.end:
+ start_pos = self.pos
+ self.insn_begin = start_pos
+
+ cmd_byte = self.read_u8()
+ cmd = self.lookup_cmd(cmd_byte)
+ mnemonic = cmd.mnemonic
+
+ # Hacky fixups for commands using long var encodings when it was not necessary for them to do so, the usual
+ # macros for re-assembly only select the long encoding when necessary so switch to special macros that
+ # always use the long encoding unconditionally.
+ if self.insn_begin in self.force_long:
+ if mnemonic == "notedv":
+ mnemonic = "noteldv"
+ elif mnemonic in ("delay", "ldelay"):
+ mnemonic = "lldelay"
+ else:
+ assert False , mnemonic
+
+ args = [argtype(self) for argtype in cmd.args]
+ raw_data = self.data[start_pos:self.pos]
+
+ self.emit_branch_target(outfile, start_pos, self.pos, force_big_lbl)
+ force_big_lbl = False
+
+ args_str = ', '.join([arg.emit(self) for arg in args])
+ data_str = ' '.join([f'0x{b:02X}' for b in raw_data])
+
+ outfile.write(f"/* 0x{start_pos:04X} [{data_str:24}] */ {mnemonic:11} {args_str}".strip() + "\n")
+
+ if cmd.is_terminal or cmd.is_branch_unconditional:
+ outfile.write("\n")
+
+ def disas_table(self, frag : SequenceFragment, outfile):
+ base_pos = self.pos
+
+ while self.pos < frag.end:
+ start_pos = self.pos
+
+ addend = self.addends.get(start_pos, 0)
+
+ ent = self.read_u16() - addend
+
+ # outfile.write(".balign 2\n\n")
+ self.emit_branch_target(outfile, start_pos, self.pos)
+
+ #found_sectype = None
+ #for offset,_,_,section_type in self.tables:
+ # if offset == base_pos:
+ # found_sectype = section_type
+ # break
+ #else:
+ # assert False
+
+ section = self.branch_targets.get(ent, None)
+
+ # TODO
+ if section is None:
+ section = SqSection.UNKNOWN
+
+ if addend != 0:
+ addend_str = f" + {addend}"
+ else:
+ addend_str = ""
+
+ # TODO proper label name
+ outfile.write(f" entry {self.label_section_prefix(section)}_{ent:04X}{addend_str}\n")
+
+ outfile.write("\n")
+
+ def disas_filter(self, frag : SequenceFragment, outfile):
+ start_pos = self.pos
+
+ num_filters, align = divmod(len(frag.data), 2 * 8)
+
+ assert all(b == 0 for b in frag.data)
+ assert align == 0
+
+ # outfile.write(".balign 16\n\n")
+
+ for n in range(num_filters):
+ self.emit_branch_target_real(outfile, start_pos + n * 2 * 8, SqSection.FILTER, force_big=True)
+ outfile.write(" filter 0, 0, 0, 0, 0, 0, 0, 0\n\n")
+
+ def disas_envelope(self, frag : SequenceFragment, outfile):
+ start_pos = self.pos
+
+ self.emit_branch_target(outfile, start_pos, frag.end)
+
+ while self.pos < frag.end:
+ delay = self.read_s16()
+ arg = self.read_s16()
+
+ if delay == 0 and arg == 0:
+ outfile.write(" disable\n")
+ elif delay == -1 and arg == 0:
+ outfile.write(" hang\n")
+ elif delay == -2:
+ outfile.write(f" goto {arg}\n")
+ elif delay == -3 and arg == 0:
+ outfile.write(" restart\n")
+ else:
+ assert delay > 0
+ outfile.write(f" point {delay}, {arg}\n")
+
+ if delay < 0 and self.pos not in self.branch_targets:
+ outfile.write("\n")
+ self.emit_branch_target_real(outfile, self.pos, frag.section)
+
+ outfile.write("\n")
+ #assert self.pos == frag.end, f"{self.pos:X} {frag.end:X}"
+
+ def disas_array(self, frag : SequenceFragment, outfile):
+ self.emit_branch_target(outfile, frag.start, frag.end)
+
+ array_data = self.data[frag.start:frag.end]
+ if all(b == 0 for b in array_data):
+ outfile.write(f".fill 0x{len(array_data):X}\n\n")
+ else:
+ for b in array_data:
+ outfile.write(f".byte 0x{b:2X}\n")
+ outfile.write("\n")
+
+ def disas_unknown(self, frag : SequenceFragment, outfile):
+ start_pos = self.pos
+
+ # emit_branch_target(start_pos, frag.end)
+
+ prev = start_pos
+ for b_tgt in sorted(self.branch_targets):
+ if b_tgt in range(start_pos+1,frag.end):
+ # emit data between this branch target and the previous
+ outfile.write(" .byte " + ", ".join(f"0x{b:02X}" for b in self.data[prev:b_tgt]) + "\n\n")
+ if b_tgt in range(start_pos,frag.end):
+ # emit the branch target
+ self.emit_branch_target_real(outfile, b_tgt, SqSection.UNKNOWN)
+ prev = b_tgt
+
+ # write any remaining data if the final branch target was not the end of the frag
+ if prev != frag.end:
+ outfile.write(" .byte " + ", ".join(f"0x{b:02X}" for b in self.data[prev:frag.end]) + "\n\n")
+
+ #
+ # emit disassembled text
+ #
+
+ def emit(self):
+ with open(self.outpath, "w") as outfile:
+ # emit header
+ outfile.write("#include \"aseq.h\"\n")
+
+ # emit fonts
+ for font in self.used_fonts:
+ outfile.write(f"#include \"{font.file_name}.h\"\n")
+ outfile.write("\n")
+
+ outfile.write(f".startseq {self.seq_name}\n\n")
+
+ # emit fragments
+ for frag in sorted(self.fragments):
+ frag : SequenceFragment
+
+ self.cur_section = frag.section
+ self.pos = frag.start
+
+ {
+ SqSection.SEQ : self.disas_section,
+ SqSection.CHAN : self.disas_section,
+ SqSection.LAYER : self.disas_section,
+ SqSection.TABLE : self.disas_table,
+ SqSection.ARRAY : self.disas_array,
+ SqSection.FILTER : self.disas_filter,
+ SqSection.ENVELOPE : self.disas_envelope,
+ SqSection.UNKNOWN : self.disas_unknown,
+ }[frag.section](frag, outfile)
+
+ outfile.write(f".endseq {self.seq_name}\n")
+
+if __name__ == '__main__':
+ import sys
+
+ in_path = sys.argv[1]
+ out_path = sys.argv[2]
+
+ with open(in_path, "rb") as infile:
+ data = bytearray(infile.read())
+
+ class FontDummy:
+ def __init__(self, file_name) -> None:
+ self.name = file_name
+ self.file_name = file_name
+ self.instrument_index_map = {}
+
+ disas = SequenceDisassembler(0, data, None, CMD_SPEC, MMLVersion.MM, out_path, "", [FontDummy("wow")], [])
+ disas.analyze()
+ disas.emit()
diff --git a/tools/audio_extraction.py b/tools/audio_extraction.py
index da30273b75c..a4650c60655 100644
--- a/tools/audio_extraction.py
+++ b/tools/audio_extraction.py
@@ -9,7 +9,8 @@
import version_config
-from audio.extraction.audio_extract import extract_audio_for_version, GameVersionInfo, MMLVersion
+from audio.extraction.audio_extract import extract_audio_for_version, GameVersionInfo
+from audio.extraction.disassemble_sequence import MMLVersion, SqSection
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="baserom audio asset extractor")
@@ -153,6 +154,36 @@
# Some audiotable banks have a buffer clearing bug. Indicate which banks suffer from this.
audiotable_buffer_bugs = (0,)
+ # Tables have no clear start and end in a sequence. Mark the locations of all tables that appear in sequences.
+ seq_disas_hacks = {
+ # sequence number : ((start offset, number of entries, addend, section_type), ...)
+ 0 : (
+ (0x00E1, 128, 0, SqSection.CHAN),
+ (0x0EE3, 80, 0, SqSection.CHAN),
+ (0x16D5, 248, 0, SqSection.CHAN),
+ (0x315E, 499, 0, SqSection.CHAN),
+ (0x5729, 72, 0, SqSection.CHAN),
+ (0x5EE5, 8, 0, SqSection.CHAN),
+ (0x5FF2, 128, 0, SqSection.CHAN),
+ ),
+ 1 : (
+ (0x0192, 20, 0, SqSection.LAYER),
+ (0x01BA, 20, 0, SqSection.LAYER),
+ (0x01E2, 20, 0, SqSection.LAYER),
+ (0x020A, 20, 0, SqSection.LAYER),
+ (0x0232, 20, 1, SqSection.LAYER),
+ (0x025A, 20, 1, SqSection.LAYER),
+ (0x0282, 20, 1, SqSection.LAYER),
+ ),
+ 2 : (
+ (0x00BC, 2, 0, SqSection.SEQ),
+ (0x00C0, 2, 0, SqSection.ARRAY),
+ ),
+ 109 : (
+ (0x0646, 16, 0, SqSection.CHAN),
+ ),
+ }
+
version_info = GameVersionInfo(MMLVersion.OOT,
soundfont_table_code_offset,
seq_font_table_code_offset,
@@ -161,6 +192,7 @@
seq_enum_names,
handwritten_sequences,
fake_banks,
- audiotable_buffer_bugs)
+ audiotable_buffer_bugs,
+ seq_disas_hacks)
extract_audio_for_version(version_info, args.extracted_dir, args.read_xml, args.write_xml)
From 5e447f6f953d8ec90ea4d5ac59f61f7d37d53fbe Mon Sep 17 00:00:00 2001
From: Thar0 <17233964+Thar0@users.noreply.github.com>
Date: Mon, 2 Sep 2024 23:39:43 +0100
Subject: [PATCH 2/3] Suggested changes, some extra sequence disassembler
cleanup
---
tools/audio/extraction/audio_extract.py | 28 ++--
.../audio/extraction/disassemble_sequence.py | 145 ++++++------------
tools/audio_extraction.py | 42 ++---
3 files changed, 83 insertions(+), 132 deletions(-)
diff --git a/tools/audio/extraction/audio_extract.py b/tools/audio/extraction/audio_extract.py
index de364329272..e4d764c32fd 100644
--- a/tools/audio/extraction/audio_extract.py
+++ b/tools/audio/extraction/audio_extract.py
@@ -4,7 +4,7 @@
# Extract audio files
#
-import os, shutil, time
+import multiprocessing, os, shutil, time
from dataclasses import dataclass
from multiprocessing.pool import ThreadPool
from typing import Dict, List, Tuple, Union
@@ -14,7 +14,7 @@
from .audio_tables import AudioCodeTable, AudioCodeTableEntry, AudioStorageMedium
from .audiotable import AudioTableData, AudioTableFile, AudioTableSample
from .audiobank_file import AudiobankFile
-from .disassemble_sequence import CMD_SPEC, SequenceDisassembler, MMLVersion
+from .disassemble_sequence import CMD_SPEC, SequenceDisassembler, SequenceTableSpec, MMLVersion
from .util import align, debugm, error, incbin, program_get, XMLWriter
@dataclass
@@ -37,8 +37,8 @@ class GameVersionInfo:
fake_banks : Dict[int, int]
# Contains audiotable indices that suffer from a buffer clearing bug
audiotable_buffer_bugs : Tuple[int]
- # Sequence disassembly hacks
- seq_disas_hacks : Dict[int, Tuple[Tuple[int]]]
+ # Sequence disassembly table specs
+ seq_disas_tables : Dict[int, Tuple[SequenceTableSpec]]
SAMPLECONV_PATH = f"{os.path.dirname(os.path.realpath(__file__))}/../sampleconv/sampleconv"
@@ -178,7 +178,7 @@ def disassemble_one_sequence(extracted_dir : str, version_info : GameVersionInfo
enum_names : List[str], id : int, data : bytes, name : str, filename : str,
fonts : memoryview):
out_filename = f"{extracted_dir}/assets/audio/sequences/{filename}.seq"
- disas = SequenceDisassembler(id, data, version_info.seq_disas_hacks.get(id, None), CMD_SPEC,
+ disas = SequenceDisassembler(id, data, version_info.seq_disas_tables.get(id, None), CMD_SPEC,
version_info.mml_version, out_filename, name,
[soundfonts[i] for i in fonts], enum_names)
disas.analyze()
@@ -206,6 +206,8 @@ def extract_sequences(audioseq_seg : memoryview, extracted_dir : str, version_in
all_fonts = []
disas_jobs = []
+ t = time.time()
+
for i,entry in enumerate(sequence_table):
entry : AudioCodeTableEntry
@@ -247,6 +249,8 @@ def extract_sequences(audioseq_seg : memoryview, extracted_dir : str, version_in
if write_xml:
xml = XMLWriter()
+ xml.write_comment("This file is only for extraction of vanilla data.")
+
xml.write_element("Sequence", {
"Name" : sequence_name,
"Index" : i,
@@ -295,13 +299,11 @@ def extract_sequences(audioseq_seg : memoryview, extracted_dir : str, version_in
# Disassemble to text
- with ThreadPool(processes=os.cpu_count()) as pool:
- # multiprocess sequence disassembly
- async_results = [pool.apply_async(disassemble_one_sequence,
- args=(extracted_dir, version_info, soundfonts, seq_enum_names, *job))
- for job in disas_jobs]
- # block until done
- [res.get() for res in async_results]
+ for job in disas_jobs:
+ disassemble_one_sequence(extracted_dir, version_info, soundfonts, seq_enum_names, *job)
+
+ dt = time.time() - t
+ print(f"Sequences extraction took {dt:.3f}")
def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : str, read_xml : bool, write_xml : bool):
print("Setting up...")
@@ -458,5 +460,3 @@ def walk_xmls(out_dict : Dict[int, Tuple[str, Element]], path : str, typename :
extract_sequences(audioseq_seg, extracted_dir, version_info, write_xml, sequence_table, sequence_font_table,
sequence_xmls, soundfonts)
-
- print("Done")
diff --git a/tools/audio/extraction/disassemble_sequence.py b/tools/audio/extraction/disassemble_sequence.py
index ab6b484dd3c..af92598e0b8 100644
--- a/tools/audio/extraction/disassemble_sequence.py
+++ b/tools/audio/extraction/disassemble_sequence.py
@@ -36,15 +36,8 @@
For tables used in `dyncall`s, we have to rely on external information to provide the location and size of tables as
there is no reliable heuristic for identifying table sizes.
-"""
-
-from dataclasses import dataclass
-from enum import Enum, auto
-from typing import Callable, Dict, List, Tuple
-from .audiobank_file import AudiobankFile
-"""
TODO
sequence beginning with testchan 0 is a buffer (?)
@@ -53,6 +46,12 @@
detect section overlaps and mark them as bugged in the output
"""
+from dataclasses import dataclass
+from enum import Enum, auto
+from typing import Callable, Dict, List, Optional, Tuple
+
+from .audiobank_file import AudiobankFile
+
pitch_names = (
"A0", "BF0", "B0", "C1", "DF1", "D1", "EF1", "E1", "F1", "GF1", "G1", "AF1", "A1", "BF1", "B1", "C2",
"DF2", "D2", "EF2", "E2", "F2", "GF2", "G2", "AF2", "A2", "BF2", "B2", "C3", "DF3", "D3", "EF3", "E3",
@@ -79,14 +78,18 @@ class MMLVersion(Enum):
#
class SqSection(Enum):
- SEQ = auto()
- CHAN = auto()
- LAYER = auto()
- ARRAY = auto()
- TABLE = auto()
- ENVELOPE = auto()
- FILTER = auto()
- UNKNOWN = auto()
+ SEQ = ("SEQ", ".sequence")
+ CHAN = ("CHAN", ".channel")
+ LAYER = ("LAYER", ".layer")
+ ARRAY = ("ARRAY", ".array")
+ TABLE = ("TABLE", ".table")
+ ENVELOPE = ("ENVELOPE", ".envelope")
+ FILTER = ("FILTER", ".filter")
+ UNKNOWN = ("UNK", "")
+
+ def __init__(self, prefix, lbl_prefix):
+ self.prefix = prefix
+ self.lbl_prefix = lbl_prefix
SECTION_ALL = (SqSection.SEQ, SqSection.CHAN, SqSection.LAYER)
@@ -276,17 +279,7 @@ def emit(self, disas):
value = start
break
- prefix = {
- SqSection.SEQ : "SEQ",
- SqSection.CHAN : "CHAN",
- SqSection.LAYER : "LAYER",
- SqSection.ARRAY : "ARRAY",
- SqSection.TABLE : "TABLE",
- SqSection.ENVELOPE : "ENVELOPE",
- SqSection.FILTER : "FILTER",
- SqSection.UNKNOWN : "UNK",
- }[target_section]
-
+ prefix = target_section.prefix
if addend != 0:
return f"{prefix}_{value:04X} + {maybe_hex(addend)}"
else:
@@ -366,8 +359,6 @@ def emit(self, disas):
class ArgTblPtr(ArgAddr):
def analyze(self, disas):
- # if disas.pos in (0x5FD0,):
- # return
disas.add_ref(self.value, SqSection.TABLE, big=True)
def emit(self, disas):
@@ -614,7 +605,7 @@ def __init__(self, disas, section, data, start, end):
self.disas = disas
def __str__(self):
- return f"Fragment ({self.section}) [{self.start}, {self.end}]" # , {{{self.data}}}
+ return f"Fragment ({self.section}) [{self.start}, {self.end}]"
def __lt__(self, other):
return self.start < other.start
@@ -624,7 +615,6 @@ def merge(frag1, frag2):
if frag1 == frag2:
return frag1
- # assert frag1.section == frag2.section , "Tried to merge fragments of different section"
if frag1.section != frag2.section:
return None
@@ -637,7 +627,6 @@ def merge(frag1, frag2):
min_end = min(frag1.end, frag2.end)
max_end = max(frag1.end, frag2.end)
- # assert max_start <= min_end , "Tried to merge disjoint fragments"
if max_start > min_end:
return None
@@ -654,10 +643,20 @@ def merge(frag1, frag2):
return SequenceFragment(frag1.disas, frag1.section, data1[:max_start] + data2, min_start, max_end)
+@dataclass
+class SequenceTableSpec:
+ start_offset : int
+ num_entries : int
+ addend : int
+ sectype : SqSection
+
+ def contains_loc(self, pos):
+ return pos in range(self.start_offset, self.start_offset + 2 * self.num_entries)
+
class SequenceDisassembler:
- def __init__(self, seq_num : int, data : bytes, tables, cmds : Tuple[MMLCmd], version : MMLVersion, outpath : str,
- seq_name : str, used_fonts : List[AudiobankFile], all_seq_names):
+ def __init__(self, seq_num : int, data : bytes, tables : Optional[Tuple[SequenceTableSpec]], cmds : Tuple[MMLCmd],
+ version : MMLVersion, outpath : str, seq_name : str, used_fonts : List[AudiobankFile], all_seq_names):
self.seq_num = seq_num
self.seq_name = seq_name
self.used_fonts = used_fonts
@@ -681,8 +680,8 @@ def __init__(self, seq_num : int, data : bytes, tables, cmds : Tuple[MMLCmd], ve
SqSection.LAYER : {},
}
- # preprocess command list into dictionary, possibly duplicating into several id keys if any lsbits are used
- # as an arg
+ # preprocess command list into dictionary, possibly duplicating into
+ # several id keys if any lsbits are used as an arg
for cmd in cmds:
# ignore commands not in this version
if version not in cmd.version:
@@ -724,7 +723,7 @@ def __init__(self, seq_num : int, data : bytes, tables, cmds : Tuple[MMLCmd], ve
self.decode_list = []
self.all_seen = []
- self.tables = tables
+ self.tables : Optional[Tuple[SequenceTableSpec]] = tables
self.table_cache = set()
self.addends = {}
@@ -785,19 +784,15 @@ def add_branch_target(self, value, section, big=False):
def add_ref(self, value, section=None, big=False):
if section is None:
- # print(f"TODO Unhandled addr ref 0x{value:04X}")
self.add_branch_target(value, SqSection.UNKNOWN)
return
self.add_branch_target(value, section, big=big)
- # print(f"Referenced {section} at 0x{value:04X}")
self.add_job(value, section, self.cur_section)
def add_job(self, value, section, from_section=None):
if value not in self.all_seen:
- # print(f"New job {section} at 0x{value:04X}")
-
self.all_seen.append(value)
self.decode_list.append((value, section, from_section or section))
@@ -857,30 +852,27 @@ def analyze_code(self): # sequence, channel, layer
self.decode_list.append((self.pos, self.cur_section, self.cur_section))
def analyze_table(self):
- # print(f"Table from {self.refd_from} at 0x{self.pos:04X}")
-
assert self.tables is not None, "Found a table but no table spec provided."
- for offset, num_entries, addend, section in self.tables:
- if self.pos in range(offset, offset + 2 * num_entries):
+ for table_spec in self.tables:
+ if table_spec.contains_loc(self.pos):
break
else:
assert False , f"Found table at {self.pos:04X} but no entry number provided"
- start_pos = self.pos = offset
+ start_pos = self.pos = table_spec.offset
if start_pos in self.table_cache:
return
- for _ in range(num_entries):
+ for _ in range(table_spec.num_entries):
curpos = self.pos
- cur = self.read_u16() - addend
+ cur = self.read_u16() - table_spec.addend
if cur >= len(self.data) - 1:
assert False , "Bad table pointer"
- # print(hex(cur))
- self.add_branch_target(cur, section, big=True)
- self.add_job(cur, section, section)
- self.register_addend(curpos, addend)
+ self.add_branch_target(cur, table_spec.sectype, big=True)
+ self.add_job(cur, table_spec.sectype, table_spec.sectype)
+ self.register_addend(curpos, table_spec.addend)
self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
self.table_cache.add(start_pos)
@@ -888,7 +880,7 @@ def analyze_table(self):
def analyze_array(self):
start_pos = self.pos
- # TODO better heuristic than just hardcoding 16 lmao
+ # TODO better heuristic than just hardcoding 16...
# it would be better to wait until later to resize arrays though, up to the next identified fragment
# ARRAY + UNK + OTHER -> ARRAY + OTHER
for _ in range(16):
@@ -905,7 +897,6 @@ def analyze_filter(self):
self.fragments.append(SequenceFragment(self, self.cur_section, self.data[start_pos:self.pos], start_pos, self.pos))
def analyze_envelope(self):
- # print(f"Envelope from {self.refd_from} at 0x{self.pos:04X}")
start_pos = self.pos
while True: # dangerous
@@ -993,12 +984,9 @@ def analyze(self):
if frag.end == first_zero_idx:
prev_frag = frag
prev_frag_idx = i
- # print("prev", i, file=sys.stderr)
elif frag.start == last_zero_idx:
next_frag = frag
next_frag_idx = i
- # print("next", i, file=sys.stderr)
- # print(self.fragments[prev_frag_idx + 1], file=sys.stderr)
# SEQ + UNK -> SEQ
if prev_frag is not None:
@@ -1060,36 +1048,15 @@ def analyze(self):
# disas helpers
#
- def label_section_prefix(self, section):
- return {
- SqSection.SEQ : f"SEQ",
- SqSection.CHAN : f"CHAN",
- SqSection.LAYER : f"LAYER",
- SqSection.ARRAY : f"ARRAY",
- SqSection.TABLE : f"TABLE",
- SqSection.ENVELOPE : f"ENVELOPE",
- SqSection.FILTER : f"FILTER",
- SqSection.UNKNOWN : f"UNK",
- }[section]
-
def label_name(self, value, section, force_big=False):
if value in self.big_labels or force_big:
- prefix = {
- SqSection.SEQ : ".sequence ",
- SqSection.CHAN : ".channel ",
- SqSection.LAYER : ".layer ",
- SqSection.ARRAY : ".array ",
- SqSection.TABLE : ".table ",
- SqSection.ENVELOPE : ".envelope ",
- SqSection.FILTER : ".filter ",
- SqSection.UNKNOWN : "",
- }[section]
+ lbl_prefix = section.lbl_prefix + " "
suffix = ""
else:
- prefix = ""
+ lbl_prefix = ""
suffix = ":"
- return f"{prefix}{self.label_section_prefix(section)}_{value:04X}{suffix}"
+ return f"{lbl_prefix}{section.prefix}_{value:04X}{suffix}"
def emit_branch_target_real(self, outfile, value, section, force_big=False):
if section is SqSection.UNKNOWN:
@@ -1104,8 +1071,6 @@ def emit_branch_target(self, outfile, start, end, force_big=False):
did_emit = False
for b_tgt in self.branch_targets:
if b_tgt in range(start,end):
- #print(b_tgt, self.branch_targets[b_tgt])
- # assert self.cur_section == b_sect
self.emit_branch_target_real(outfile, start, self.branch_targets[b_tgt], force_big)
did_emit = True
return did_emit
@@ -1170,17 +1135,8 @@ def disas_table(self, frag : SequenceFragment, outfile):
ent = self.read_u16() - addend
- # outfile.write(".balign 2\n\n")
self.emit_branch_target(outfile, start_pos, self.pos)
- #found_sectype = None
- #for offset,_,_,section_type in self.tables:
- # if offset == base_pos:
- # found_sectype = section_type
- # break
- #else:
- # assert False
-
section = self.branch_targets.get(ent, None)
# TODO
@@ -1193,7 +1149,7 @@ def disas_table(self, frag : SequenceFragment, outfile):
addend_str = ""
# TODO proper label name
- outfile.write(f" entry {self.label_section_prefix(section)}_{ent:04X}{addend_str}\n")
+ outfile.write(f" entry {section.prefix}_{ent:04X}{addend_str}\n")
outfile.write("\n")
@@ -1205,8 +1161,6 @@ def disas_filter(self, frag : SequenceFragment, outfile):
assert all(b == 0 for b in frag.data)
assert align == 0
- # outfile.write(".balign 16\n\n")
-
for n in range(num_filters):
self.emit_branch_target_real(outfile, start_pos + n * 2 * 8, SqSection.FILTER, force_big=True)
outfile.write(" filter 0, 0, 0, 0, 0, 0, 0, 0\n\n")
@@ -1237,7 +1191,6 @@ def disas_envelope(self, frag : SequenceFragment, outfile):
self.emit_branch_target_real(outfile, self.pos, frag.section)
outfile.write("\n")
- #assert self.pos == frag.end, f"{self.pos:X} {frag.end:X}"
def disas_array(self, frag : SequenceFragment, outfile):
self.emit_branch_target(outfile, frag.start, frag.end)
@@ -1253,8 +1206,6 @@ def disas_array(self, frag : SequenceFragment, outfile):
def disas_unknown(self, frag : SequenceFragment, outfile):
start_pos = self.pos
- # emit_branch_target(start_pos, frag.end)
-
prev = start_pos
for b_tgt in sorted(self.branch_targets):
if b_tgt in range(start_pos+1,frag.end):
diff --git a/tools/audio_extraction.py b/tools/audio_extraction.py
index a4650c60655..9c3a2026488 100644
--- a/tools/audio_extraction.py
+++ b/tools/audio_extraction.py
@@ -10,7 +10,7 @@
import version_config
from audio.extraction.audio_extract import extract_audio_for_version, GameVersionInfo
-from audio.extraction.disassemble_sequence import MMLVersion, SqSection
+from audio.extraction.disassemble_sequence import MMLVersion, SequenceTableSpec, SqSection
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="baserom audio asset extractor")
@@ -155,32 +155,32 @@
audiotable_buffer_bugs = (0,)
# Tables have no clear start and end in a sequence. Mark the locations of all tables that appear in sequences.
- seq_disas_hacks = {
- # sequence number : ((start offset, number of entries, addend, section_type), ...)
+ seq_disas_tables = {
+ # sequence number : (spec, ...)
0 : (
- (0x00E1, 128, 0, SqSection.CHAN),
- (0x0EE3, 80, 0, SqSection.CHAN),
- (0x16D5, 248, 0, SqSection.CHAN),
- (0x315E, 499, 0, SqSection.CHAN),
- (0x5729, 72, 0, SqSection.CHAN),
- (0x5EE5, 8, 0, SqSection.CHAN),
- (0x5FF2, 128, 0, SqSection.CHAN),
+ SequenceTableSpec(0x00E1, 128, 0, SqSection.CHAN),
+ SequenceTableSpec(0x0EE3, 80, 0, SqSection.CHAN),
+ SequenceTableSpec(0x16D5, 248, 0, SqSection.CHAN),
+ SequenceTableSpec(0x315E, 499, 0, SqSection.CHAN),
+ SequenceTableSpec(0x5729, 72, 0, SqSection.CHAN),
+ SequenceTableSpec(0x5EE5, 8, 0, SqSection.CHAN),
+ SequenceTableSpec(0x5FF2, 128, 0, SqSection.CHAN),
),
1 : (
- (0x0192, 20, 0, SqSection.LAYER),
- (0x01BA, 20, 0, SqSection.LAYER),
- (0x01E2, 20, 0, SqSection.LAYER),
- (0x020A, 20, 0, SqSection.LAYER),
- (0x0232, 20, 1, SqSection.LAYER),
- (0x025A, 20, 1, SqSection.LAYER),
- (0x0282, 20, 1, SqSection.LAYER),
+ SequenceTableSpec(0x0192, 20, 0, SqSection.LAYER),
+ SequenceTableSpec(0x01BA, 20, 0, SqSection.LAYER),
+ SequenceTableSpec(0x01E2, 20, 0, SqSection.LAYER),
+ SequenceTableSpec(0x020A, 20, 0, SqSection.LAYER),
+ SequenceTableSpec(0x0232, 20, 1, SqSection.LAYER),
+ SequenceTableSpec(0x025A, 20, 1, SqSection.LAYER),
+ SequenceTableSpec(0x0282, 20, 1, SqSection.LAYER),
),
2 : (
- (0x00BC, 2, 0, SqSection.SEQ),
- (0x00C0, 2, 0, SqSection.ARRAY),
+ SequenceTableSpec(0x00BC, 2, 0, SqSection.SEQ),
+ SequenceTableSpec(0x00C0, 2, 0, SqSection.ARRAY),
),
109 : (
- (0x0646, 16, 0, SqSection.CHAN),
+ SequenceTableSpec(0x0646, 16, 0, SqSection.CHAN),
),
}
@@ -193,6 +193,6 @@
handwritten_sequences,
fake_banks,
audiotable_buffer_bugs,
- seq_disas_hacks)
+ seq_disas_tables)
extract_audio_for_version(version_info, args.extracted_dir, args.read_xml, args.write_xml)
From cce8ddbb4a6c912c3461004dce92e9bf2e41f2db Mon Sep 17 00:00:00 2001
From: Thar0 <17233964+Thar0@users.noreply.github.com>
Date: Tue, 3 Sep 2024 00:29:46 +0100
Subject: [PATCH 3/3] Remove unused multiprocessing import and regen
assets/xml/audio/sequences
---
assets/xml/audio/sequences/seq_0.xml | 1 +
assets/xml/audio/sequences/seq_1.xml | 1 +
assets/xml/audio/sequences/seq_10.xml | 1 +
assets/xml/audio/sequences/seq_100.xml | 1 +
assets/xml/audio/sequences/seq_101.xml | 1 +
assets/xml/audio/sequences/seq_102.xml | 1 +
assets/xml/audio/sequences/seq_103.xml | 1 +
assets/xml/audio/sequences/seq_104.xml | 1 +
assets/xml/audio/sequences/seq_105.xml | 1 +
assets/xml/audio/sequences/seq_106.xml | 1 +
assets/xml/audio/sequences/seq_107.xml | 1 +
assets/xml/audio/sequences/seq_108.xml | 1 +
assets/xml/audio/sequences/seq_109.xml | 1 +
assets/xml/audio/sequences/seq_11.xml | 1 +
assets/xml/audio/sequences/seq_12.xml | 1 +
assets/xml/audio/sequences/seq_13.xml | 1 +
assets/xml/audio/sequences/seq_14.xml | 1 +
assets/xml/audio/sequences/seq_15.xml | 1 +
assets/xml/audio/sequences/seq_16.xml | 1 +
assets/xml/audio/sequences/seq_17.xml | 1 +
assets/xml/audio/sequences/seq_18.xml | 1 +
assets/xml/audio/sequences/seq_19.xml | 1 +
assets/xml/audio/sequences/seq_2.xml | 1 +
assets/xml/audio/sequences/seq_20.xml | 1 +
assets/xml/audio/sequences/seq_21.xml | 1 +
assets/xml/audio/sequences/seq_22.xml | 1 +
assets/xml/audio/sequences/seq_23.xml | 1 +
assets/xml/audio/sequences/seq_24.xml | 1 +
assets/xml/audio/sequences/seq_25.xml | 1 +
assets/xml/audio/sequences/seq_26.xml | 1 +
assets/xml/audio/sequences/seq_27.xml | 1 +
assets/xml/audio/sequences/seq_28.xml | 1 +
assets/xml/audio/sequences/seq_29.xml | 1 +
assets/xml/audio/sequences/seq_3.xml | 1 +
assets/xml/audio/sequences/seq_30.xml | 1 +
assets/xml/audio/sequences/seq_31.xml | 1 +
assets/xml/audio/sequences/seq_32.xml | 1 +
assets/xml/audio/sequences/seq_33.xml | 1 +
assets/xml/audio/sequences/seq_34.xml | 1 +
assets/xml/audio/sequences/seq_35.xml | 1 +
assets/xml/audio/sequences/seq_36.xml | 1 +
assets/xml/audio/sequences/seq_37.xml | 1 +
assets/xml/audio/sequences/seq_38.xml | 1 +
assets/xml/audio/sequences/seq_39.xml | 1 +
assets/xml/audio/sequences/seq_4.xml | 1 +
assets/xml/audio/sequences/seq_40.xml | 1 +
assets/xml/audio/sequences/seq_41.xml | 1 +
assets/xml/audio/sequences/seq_42.xml | 1 +
assets/xml/audio/sequences/seq_43.xml | 1 +
assets/xml/audio/sequences/seq_44.xml | 1 +
assets/xml/audio/sequences/seq_45.xml | 1 +
assets/xml/audio/sequences/seq_46.xml | 1 +
assets/xml/audio/sequences/seq_47.xml | 1 +
assets/xml/audio/sequences/seq_48.xml | 1 +
assets/xml/audio/sequences/seq_49.xml | 1 +
assets/xml/audio/sequences/seq_5.xml | 1 +
assets/xml/audio/sequences/seq_50.xml | 1 +
assets/xml/audio/sequences/seq_51.xml | 1 +
assets/xml/audio/sequences/seq_52.xml | 1 +
assets/xml/audio/sequences/seq_53.xml | 1 +
assets/xml/audio/sequences/seq_54.xml | 1 +
assets/xml/audio/sequences/seq_55.xml | 1 +
assets/xml/audio/sequences/seq_56.xml | 1 +
assets/xml/audio/sequences/seq_57.xml | 1 +
assets/xml/audio/sequences/seq_58.xml | 1 +
assets/xml/audio/sequences/seq_59.xml | 1 +
assets/xml/audio/sequences/seq_6.xml | 1 +
assets/xml/audio/sequences/seq_60.xml | 1 +
assets/xml/audio/sequences/seq_61.xml | 1 +
assets/xml/audio/sequences/seq_62.xml | 1 +
assets/xml/audio/sequences/seq_63.xml | 1 +
assets/xml/audio/sequences/seq_64.xml | 1 +
assets/xml/audio/sequences/seq_65.xml | 1 +
assets/xml/audio/sequences/seq_66.xml | 1 +
assets/xml/audio/sequences/seq_67.xml | 1 +
assets/xml/audio/sequences/seq_68.xml | 1 +
assets/xml/audio/sequences/seq_69.xml | 1 +
assets/xml/audio/sequences/seq_7.xml | 1 +
assets/xml/audio/sequences/seq_70.xml | 1 +
assets/xml/audio/sequences/seq_71.xml | 1 +
assets/xml/audio/sequences/seq_72.xml | 1 +
assets/xml/audio/sequences/seq_73.xml | 1 +
assets/xml/audio/sequences/seq_74.xml | 1 +
assets/xml/audio/sequences/seq_75.xml | 1 +
assets/xml/audio/sequences/seq_76.xml | 1 +
assets/xml/audio/sequences/seq_77.xml | 1 +
assets/xml/audio/sequences/seq_78.xml | 1 +
assets/xml/audio/sequences/seq_79.xml | 1 +
assets/xml/audio/sequences/seq_8.xml | 1 +
assets/xml/audio/sequences/seq_80.xml | 1 +
assets/xml/audio/sequences/seq_81.xml | 1 +
assets/xml/audio/sequences/seq_82.xml | 1 +
assets/xml/audio/sequences/seq_83.xml | 1 +
assets/xml/audio/sequences/seq_84.xml | 1 +
assets/xml/audio/sequences/seq_85.xml | 1 +
assets/xml/audio/sequences/seq_86.xml | 1 +
assets/xml/audio/sequences/seq_88.xml | 1 +
assets/xml/audio/sequences/seq_89.xml | 1 +
assets/xml/audio/sequences/seq_9.xml | 1 +
assets/xml/audio/sequences/seq_90.xml | 1 +
assets/xml/audio/sequences/seq_91.xml | 1 +
assets/xml/audio/sequences/seq_92.xml | 1 +
assets/xml/audio/sequences/seq_93.xml | 1 +
assets/xml/audio/sequences/seq_94.xml | 1 +
assets/xml/audio/sequences/seq_95.xml | 1 +
assets/xml/audio/sequences/seq_96.xml | 1 +
assets/xml/audio/sequences/seq_97.xml | 1 +
assets/xml/audio/sequences/seq_98.xml | 1 +
assets/xml/audio/sequences/seq_99.xml | 1 +
tools/audio/extraction/audio_extract.py | 2 +-
110 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/assets/xml/audio/sequences/seq_0.xml b/assets/xml/audio/sequences/seq_0.xml
index 6ff3cd1e81f..d2050652c62 100644
--- a/assets/xml/audio/sequences/seq_0.xml
+++ b/assets/xml/audio/sequences/seq_0.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_1.xml b/assets/xml/audio/sequences/seq_1.xml
index a377674df91..668006ad5ef 100644
--- a/assets/xml/audio/sequences/seq_1.xml
+++ b/assets/xml/audio/sequences/seq_1.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_10.xml b/assets/xml/audio/sequences/seq_10.xml
index 3ae59e65cda..7a7d5454a5d 100644
--- a/assets/xml/audio/sequences/seq_10.xml
+++ b/assets/xml/audio/sequences/seq_10.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_100.xml b/assets/xml/audio/sequences/seq_100.xml
index 9f2c90fdfe0..554641913d3 100644
--- a/assets/xml/audio/sequences/seq_100.xml
+++ b/assets/xml/audio/sequences/seq_100.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_101.xml b/assets/xml/audio/sequences/seq_101.xml
index 7b16f1459e9..a8dc3db4863 100644
--- a/assets/xml/audio/sequences/seq_101.xml
+++ b/assets/xml/audio/sequences/seq_101.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_102.xml b/assets/xml/audio/sequences/seq_102.xml
index 2c99af7ce9e..264a9fb3b84 100644
--- a/assets/xml/audio/sequences/seq_102.xml
+++ b/assets/xml/audio/sequences/seq_102.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_103.xml b/assets/xml/audio/sequences/seq_103.xml
index ca023064d3c..27b66e955b6 100644
--- a/assets/xml/audio/sequences/seq_103.xml
+++ b/assets/xml/audio/sequences/seq_103.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_104.xml b/assets/xml/audio/sequences/seq_104.xml
index 443716b538d..c0560a0fd36 100644
--- a/assets/xml/audio/sequences/seq_104.xml
+++ b/assets/xml/audio/sequences/seq_104.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_105.xml b/assets/xml/audio/sequences/seq_105.xml
index 2d3660d8bb3..11f30d8f768 100644
--- a/assets/xml/audio/sequences/seq_105.xml
+++ b/assets/xml/audio/sequences/seq_105.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_106.xml b/assets/xml/audio/sequences/seq_106.xml
index 0f998e0c144..3efe9de557e 100644
--- a/assets/xml/audio/sequences/seq_106.xml
+++ b/assets/xml/audio/sequences/seq_106.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_107.xml b/assets/xml/audio/sequences/seq_107.xml
index 0e2d815a183..1b37819a3e1 100644
--- a/assets/xml/audio/sequences/seq_107.xml
+++ b/assets/xml/audio/sequences/seq_107.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_108.xml b/assets/xml/audio/sequences/seq_108.xml
index 6549605c735..d02cc020957 100644
--- a/assets/xml/audio/sequences/seq_108.xml
+++ b/assets/xml/audio/sequences/seq_108.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_109.xml b/assets/xml/audio/sequences/seq_109.xml
index e16afa0cabb..27064158e7d 100644
--- a/assets/xml/audio/sequences/seq_109.xml
+++ b/assets/xml/audio/sequences/seq_109.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_11.xml b/assets/xml/audio/sequences/seq_11.xml
index d829182044d..233917f2a0b 100644
--- a/assets/xml/audio/sequences/seq_11.xml
+++ b/assets/xml/audio/sequences/seq_11.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_12.xml b/assets/xml/audio/sequences/seq_12.xml
index 004d2f9806e..de48f973023 100644
--- a/assets/xml/audio/sequences/seq_12.xml
+++ b/assets/xml/audio/sequences/seq_12.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_13.xml b/assets/xml/audio/sequences/seq_13.xml
index e3bfd4973e9..048b3efda63 100644
--- a/assets/xml/audio/sequences/seq_13.xml
+++ b/assets/xml/audio/sequences/seq_13.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_14.xml b/assets/xml/audio/sequences/seq_14.xml
index 3b5f867fc66..79d3238c61a 100644
--- a/assets/xml/audio/sequences/seq_14.xml
+++ b/assets/xml/audio/sequences/seq_14.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_15.xml b/assets/xml/audio/sequences/seq_15.xml
index 144ba0eb9ab..c54018295e1 100644
--- a/assets/xml/audio/sequences/seq_15.xml
+++ b/assets/xml/audio/sequences/seq_15.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_16.xml b/assets/xml/audio/sequences/seq_16.xml
index 5f9da240b89..5267d04126a 100644
--- a/assets/xml/audio/sequences/seq_16.xml
+++ b/assets/xml/audio/sequences/seq_16.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_17.xml b/assets/xml/audio/sequences/seq_17.xml
index b1aff37312b..0d6d306b3b4 100644
--- a/assets/xml/audio/sequences/seq_17.xml
+++ b/assets/xml/audio/sequences/seq_17.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_18.xml b/assets/xml/audio/sequences/seq_18.xml
index ddd090b2669..af445ff0dcf 100644
--- a/assets/xml/audio/sequences/seq_18.xml
+++ b/assets/xml/audio/sequences/seq_18.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_19.xml b/assets/xml/audio/sequences/seq_19.xml
index bfd25ca10c1..000b84c2c69 100644
--- a/assets/xml/audio/sequences/seq_19.xml
+++ b/assets/xml/audio/sequences/seq_19.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_2.xml b/assets/xml/audio/sequences/seq_2.xml
index e4c8bbb868c..35b67cbddbc 100644
--- a/assets/xml/audio/sequences/seq_2.xml
+++ b/assets/xml/audio/sequences/seq_2.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_20.xml b/assets/xml/audio/sequences/seq_20.xml
index a6fe2f18575..35824b77fde 100644
--- a/assets/xml/audio/sequences/seq_20.xml
+++ b/assets/xml/audio/sequences/seq_20.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_21.xml b/assets/xml/audio/sequences/seq_21.xml
index c28b5b5eb57..c4ab27602fc 100644
--- a/assets/xml/audio/sequences/seq_21.xml
+++ b/assets/xml/audio/sequences/seq_21.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_22.xml b/assets/xml/audio/sequences/seq_22.xml
index bef36b8cf03..cac61f1e648 100644
--- a/assets/xml/audio/sequences/seq_22.xml
+++ b/assets/xml/audio/sequences/seq_22.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_23.xml b/assets/xml/audio/sequences/seq_23.xml
index 9e6887876e8..ac69e86045c 100644
--- a/assets/xml/audio/sequences/seq_23.xml
+++ b/assets/xml/audio/sequences/seq_23.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_24.xml b/assets/xml/audio/sequences/seq_24.xml
index 409278dda5a..2d3386a35d3 100644
--- a/assets/xml/audio/sequences/seq_24.xml
+++ b/assets/xml/audio/sequences/seq_24.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_25.xml b/assets/xml/audio/sequences/seq_25.xml
index 09340fb59cc..ef233be23d9 100644
--- a/assets/xml/audio/sequences/seq_25.xml
+++ b/assets/xml/audio/sequences/seq_25.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_26.xml b/assets/xml/audio/sequences/seq_26.xml
index c5ad33f0259..590a5165f4c 100644
--- a/assets/xml/audio/sequences/seq_26.xml
+++ b/assets/xml/audio/sequences/seq_26.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_27.xml b/assets/xml/audio/sequences/seq_27.xml
index 8599a4786a3..5d8ff12348c 100644
--- a/assets/xml/audio/sequences/seq_27.xml
+++ b/assets/xml/audio/sequences/seq_27.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_28.xml b/assets/xml/audio/sequences/seq_28.xml
index e85d05d8ec8..4520532db07 100644
--- a/assets/xml/audio/sequences/seq_28.xml
+++ b/assets/xml/audio/sequences/seq_28.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_29.xml b/assets/xml/audio/sequences/seq_29.xml
index 380bc35861b..2ca7f3e712e 100644
--- a/assets/xml/audio/sequences/seq_29.xml
+++ b/assets/xml/audio/sequences/seq_29.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_3.xml b/assets/xml/audio/sequences/seq_3.xml
index c243327093e..8aa3b1ff0f1 100644
--- a/assets/xml/audio/sequences/seq_3.xml
+++ b/assets/xml/audio/sequences/seq_3.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_30.xml b/assets/xml/audio/sequences/seq_30.xml
index 4e90f42d429..a3dead9ba4b 100644
--- a/assets/xml/audio/sequences/seq_30.xml
+++ b/assets/xml/audio/sequences/seq_30.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_31.xml b/assets/xml/audio/sequences/seq_31.xml
index b55ec5aeac7..9a1cfe9f279 100644
--- a/assets/xml/audio/sequences/seq_31.xml
+++ b/assets/xml/audio/sequences/seq_31.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_32.xml b/assets/xml/audio/sequences/seq_32.xml
index 8c9f3f6edf0..bb76497a802 100644
--- a/assets/xml/audio/sequences/seq_32.xml
+++ b/assets/xml/audio/sequences/seq_32.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_33.xml b/assets/xml/audio/sequences/seq_33.xml
index 18a1fa3ba51..15bc25a25c7 100644
--- a/assets/xml/audio/sequences/seq_33.xml
+++ b/assets/xml/audio/sequences/seq_33.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_34.xml b/assets/xml/audio/sequences/seq_34.xml
index 10ba75422ee..4d218864310 100644
--- a/assets/xml/audio/sequences/seq_34.xml
+++ b/assets/xml/audio/sequences/seq_34.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_35.xml b/assets/xml/audio/sequences/seq_35.xml
index db22ec7e08d..4d1e7327e49 100644
--- a/assets/xml/audio/sequences/seq_35.xml
+++ b/assets/xml/audio/sequences/seq_35.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_36.xml b/assets/xml/audio/sequences/seq_36.xml
index e854c41fe39..63173ecbb4a 100644
--- a/assets/xml/audio/sequences/seq_36.xml
+++ b/assets/xml/audio/sequences/seq_36.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_37.xml b/assets/xml/audio/sequences/seq_37.xml
index 5f28eae9504..bd6813f6207 100644
--- a/assets/xml/audio/sequences/seq_37.xml
+++ b/assets/xml/audio/sequences/seq_37.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_38.xml b/assets/xml/audio/sequences/seq_38.xml
index c4aff602bf9..dc85ce39535 100644
--- a/assets/xml/audio/sequences/seq_38.xml
+++ b/assets/xml/audio/sequences/seq_38.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_39.xml b/assets/xml/audio/sequences/seq_39.xml
index 9f287053d06..5cef3087926 100644
--- a/assets/xml/audio/sequences/seq_39.xml
+++ b/assets/xml/audio/sequences/seq_39.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_4.xml b/assets/xml/audio/sequences/seq_4.xml
index 83226cb9bde..6311553183b 100644
--- a/assets/xml/audio/sequences/seq_4.xml
+++ b/assets/xml/audio/sequences/seq_4.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_40.xml b/assets/xml/audio/sequences/seq_40.xml
index 16d0cdfd85e..c58c83c2a83 100644
--- a/assets/xml/audio/sequences/seq_40.xml
+++ b/assets/xml/audio/sequences/seq_40.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_41.xml b/assets/xml/audio/sequences/seq_41.xml
index 10260aff934..a74ce40600e 100644
--- a/assets/xml/audio/sequences/seq_41.xml
+++ b/assets/xml/audio/sequences/seq_41.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_42.xml b/assets/xml/audio/sequences/seq_42.xml
index ce925ccce51..3298f00c4da 100644
--- a/assets/xml/audio/sequences/seq_42.xml
+++ b/assets/xml/audio/sequences/seq_42.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_43.xml b/assets/xml/audio/sequences/seq_43.xml
index 6521d276b4d..9e0d2f92806 100644
--- a/assets/xml/audio/sequences/seq_43.xml
+++ b/assets/xml/audio/sequences/seq_43.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_44.xml b/assets/xml/audio/sequences/seq_44.xml
index b0e475b24fd..7f4efdd7643 100644
--- a/assets/xml/audio/sequences/seq_44.xml
+++ b/assets/xml/audio/sequences/seq_44.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_45.xml b/assets/xml/audio/sequences/seq_45.xml
index 8045d8867d9..1f23ccd5faa 100644
--- a/assets/xml/audio/sequences/seq_45.xml
+++ b/assets/xml/audio/sequences/seq_45.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_46.xml b/assets/xml/audio/sequences/seq_46.xml
index 89d3e57892d..af7340d3d53 100644
--- a/assets/xml/audio/sequences/seq_46.xml
+++ b/assets/xml/audio/sequences/seq_46.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_47.xml b/assets/xml/audio/sequences/seq_47.xml
index ea3f85fea17..7b9b46489af 100644
--- a/assets/xml/audio/sequences/seq_47.xml
+++ b/assets/xml/audio/sequences/seq_47.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_48.xml b/assets/xml/audio/sequences/seq_48.xml
index f6a38e21c67..0cd313d2bd9 100644
--- a/assets/xml/audio/sequences/seq_48.xml
+++ b/assets/xml/audio/sequences/seq_48.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_49.xml b/assets/xml/audio/sequences/seq_49.xml
index a5e2b007e7c..c5aa4bbb6aa 100644
--- a/assets/xml/audio/sequences/seq_49.xml
+++ b/assets/xml/audio/sequences/seq_49.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_5.xml b/assets/xml/audio/sequences/seq_5.xml
index 227091a4d48..a1a398f4635 100644
--- a/assets/xml/audio/sequences/seq_5.xml
+++ b/assets/xml/audio/sequences/seq_5.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_50.xml b/assets/xml/audio/sequences/seq_50.xml
index a4576f01b1b..e2e3adcd262 100644
--- a/assets/xml/audio/sequences/seq_50.xml
+++ b/assets/xml/audio/sequences/seq_50.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_51.xml b/assets/xml/audio/sequences/seq_51.xml
index bbf71629112..2b6ba4fceeb 100644
--- a/assets/xml/audio/sequences/seq_51.xml
+++ b/assets/xml/audio/sequences/seq_51.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_52.xml b/assets/xml/audio/sequences/seq_52.xml
index a5607162429..17f6f8ea8ff 100644
--- a/assets/xml/audio/sequences/seq_52.xml
+++ b/assets/xml/audio/sequences/seq_52.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_53.xml b/assets/xml/audio/sequences/seq_53.xml
index 11e66475c01..e9f0a82eaec 100644
--- a/assets/xml/audio/sequences/seq_53.xml
+++ b/assets/xml/audio/sequences/seq_53.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_54.xml b/assets/xml/audio/sequences/seq_54.xml
index 6eca31b638e..a902858ca60 100644
--- a/assets/xml/audio/sequences/seq_54.xml
+++ b/assets/xml/audio/sequences/seq_54.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_55.xml b/assets/xml/audio/sequences/seq_55.xml
index 92bc87e71d9..361afcebd2e 100644
--- a/assets/xml/audio/sequences/seq_55.xml
+++ b/assets/xml/audio/sequences/seq_55.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_56.xml b/assets/xml/audio/sequences/seq_56.xml
index 9a6353398d2..5710f771e58 100644
--- a/assets/xml/audio/sequences/seq_56.xml
+++ b/assets/xml/audio/sequences/seq_56.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_57.xml b/assets/xml/audio/sequences/seq_57.xml
index f56c11e517e..b0f2c8573cb 100644
--- a/assets/xml/audio/sequences/seq_57.xml
+++ b/assets/xml/audio/sequences/seq_57.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_58.xml b/assets/xml/audio/sequences/seq_58.xml
index 8a4d98cf673..39cc2607865 100644
--- a/assets/xml/audio/sequences/seq_58.xml
+++ b/assets/xml/audio/sequences/seq_58.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_59.xml b/assets/xml/audio/sequences/seq_59.xml
index 851a9eb0966..cf7393a72ee 100644
--- a/assets/xml/audio/sequences/seq_59.xml
+++ b/assets/xml/audio/sequences/seq_59.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_6.xml b/assets/xml/audio/sequences/seq_6.xml
index 89bcc0292b9..f1d11762563 100644
--- a/assets/xml/audio/sequences/seq_6.xml
+++ b/assets/xml/audio/sequences/seq_6.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_60.xml b/assets/xml/audio/sequences/seq_60.xml
index 430004df531..2fdaac058ff 100644
--- a/assets/xml/audio/sequences/seq_60.xml
+++ b/assets/xml/audio/sequences/seq_60.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_61.xml b/assets/xml/audio/sequences/seq_61.xml
index ff247006d07..f7add6ba575 100644
--- a/assets/xml/audio/sequences/seq_61.xml
+++ b/assets/xml/audio/sequences/seq_61.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_62.xml b/assets/xml/audio/sequences/seq_62.xml
index eb81883c079..ca00f1bf033 100644
--- a/assets/xml/audio/sequences/seq_62.xml
+++ b/assets/xml/audio/sequences/seq_62.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_63.xml b/assets/xml/audio/sequences/seq_63.xml
index 504a2275fa1..d7cb6e7fe63 100644
--- a/assets/xml/audio/sequences/seq_63.xml
+++ b/assets/xml/audio/sequences/seq_63.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_64.xml b/assets/xml/audio/sequences/seq_64.xml
index 27aa4dce739..95eb98c73f4 100644
--- a/assets/xml/audio/sequences/seq_64.xml
+++ b/assets/xml/audio/sequences/seq_64.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_65.xml b/assets/xml/audio/sequences/seq_65.xml
index 88523a8ccd7..897347d02c6 100644
--- a/assets/xml/audio/sequences/seq_65.xml
+++ b/assets/xml/audio/sequences/seq_65.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_66.xml b/assets/xml/audio/sequences/seq_66.xml
index 6fcd739ecc0..85f5ff52220 100644
--- a/assets/xml/audio/sequences/seq_66.xml
+++ b/assets/xml/audio/sequences/seq_66.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_67.xml b/assets/xml/audio/sequences/seq_67.xml
index 2024815cd90..36dfd9bbfbb 100644
--- a/assets/xml/audio/sequences/seq_67.xml
+++ b/assets/xml/audio/sequences/seq_67.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_68.xml b/assets/xml/audio/sequences/seq_68.xml
index 4c742e4258a..6644e0b601e 100644
--- a/assets/xml/audio/sequences/seq_68.xml
+++ b/assets/xml/audio/sequences/seq_68.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_69.xml b/assets/xml/audio/sequences/seq_69.xml
index 816578f813c..59ee837c808 100644
--- a/assets/xml/audio/sequences/seq_69.xml
+++ b/assets/xml/audio/sequences/seq_69.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_7.xml b/assets/xml/audio/sequences/seq_7.xml
index dc239bbe1a4..5b099b03e89 100644
--- a/assets/xml/audio/sequences/seq_7.xml
+++ b/assets/xml/audio/sequences/seq_7.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_70.xml b/assets/xml/audio/sequences/seq_70.xml
index 096f4546690..5e7966d8118 100644
--- a/assets/xml/audio/sequences/seq_70.xml
+++ b/assets/xml/audio/sequences/seq_70.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_71.xml b/assets/xml/audio/sequences/seq_71.xml
index 10cca85e139..f0b9c5166c5 100644
--- a/assets/xml/audio/sequences/seq_71.xml
+++ b/assets/xml/audio/sequences/seq_71.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_72.xml b/assets/xml/audio/sequences/seq_72.xml
index c878ddbbbd8..f674c78d6a3 100644
--- a/assets/xml/audio/sequences/seq_72.xml
+++ b/assets/xml/audio/sequences/seq_72.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_73.xml b/assets/xml/audio/sequences/seq_73.xml
index 91708ec863d..2548961701e 100644
--- a/assets/xml/audio/sequences/seq_73.xml
+++ b/assets/xml/audio/sequences/seq_73.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_74.xml b/assets/xml/audio/sequences/seq_74.xml
index 2f96ac0ae35..b1da5b9923d 100644
--- a/assets/xml/audio/sequences/seq_74.xml
+++ b/assets/xml/audio/sequences/seq_74.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_75.xml b/assets/xml/audio/sequences/seq_75.xml
index c84e2f21b81..2bbceeb8267 100644
--- a/assets/xml/audio/sequences/seq_75.xml
+++ b/assets/xml/audio/sequences/seq_75.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_76.xml b/assets/xml/audio/sequences/seq_76.xml
index 6b5a83d7bcb..368881c4301 100644
--- a/assets/xml/audio/sequences/seq_76.xml
+++ b/assets/xml/audio/sequences/seq_76.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_77.xml b/assets/xml/audio/sequences/seq_77.xml
index 49a57e600fc..0eed7ea92cf 100644
--- a/assets/xml/audio/sequences/seq_77.xml
+++ b/assets/xml/audio/sequences/seq_77.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_78.xml b/assets/xml/audio/sequences/seq_78.xml
index 87bc6239102..372b10db228 100644
--- a/assets/xml/audio/sequences/seq_78.xml
+++ b/assets/xml/audio/sequences/seq_78.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_79.xml b/assets/xml/audio/sequences/seq_79.xml
index ab9aa1d72e3..cfd002b51b7 100644
--- a/assets/xml/audio/sequences/seq_79.xml
+++ b/assets/xml/audio/sequences/seq_79.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_8.xml b/assets/xml/audio/sequences/seq_8.xml
index 1eda8f8fa26..d5b49b5d123 100644
--- a/assets/xml/audio/sequences/seq_8.xml
+++ b/assets/xml/audio/sequences/seq_8.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_80.xml b/assets/xml/audio/sequences/seq_80.xml
index 40c8e926d9e..2d7f51a5a65 100644
--- a/assets/xml/audio/sequences/seq_80.xml
+++ b/assets/xml/audio/sequences/seq_80.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_81.xml b/assets/xml/audio/sequences/seq_81.xml
index 14c49a90de7..2534949364f 100644
--- a/assets/xml/audio/sequences/seq_81.xml
+++ b/assets/xml/audio/sequences/seq_81.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_82.xml b/assets/xml/audio/sequences/seq_82.xml
index ef15bad311c..98d1bff9b75 100644
--- a/assets/xml/audio/sequences/seq_82.xml
+++ b/assets/xml/audio/sequences/seq_82.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_83.xml b/assets/xml/audio/sequences/seq_83.xml
index efb27b53ff5..e44b8d6ff50 100644
--- a/assets/xml/audio/sequences/seq_83.xml
+++ b/assets/xml/audio/sequences/seq_83.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_84.xml b/assets/xml/audio/sequences/seq_84.xml
index 1249f1a9ebf..55dc374a4c5 100644
--- a/assets/xml/audio/sequences/seq_84.xml
+++ b/assets/xml/audio/sequences/seq_84.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_85.xml b/assets/xml/audio/sequences/seq_85.xml
index ecf38de03d2..1f024e0923a 100644
--- a/assets/xml/audio/sequences/seq_85.xml
+++ b/assets/xml/audio/sequences/seq_85.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_86.xml b/assets/xml/audio/sequences/seq_86.xml
index 261337ad180..f6acb8a9fe5 100644
--- a/assets/xml/audio/sequences/seq_86.xml
+++ b/assets/xml/audio/sequences/seq_86.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_88.xml b/assets/xml/audio/sequences/seq_88.xml
index 485f2b98ba2..a96383096cf 100644
--- a/assets/xml/audio/sequences/seq_88.xml
+++ b/assets/xml/audio/sequences/seq_88.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_89.xml b/assets/xml/audio/sequences/seq_89.xml
index b872412aafb..62026869f8f 100644
--- a/assets/xml/audio/sequences/seq_89.xml
+++ b/assets/xml/audio/sequences/seq_89.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_9.xml b/assets/xml/audio/sequences/seq_9.xml
index 06422b86c32..f6671b1e6e0 100644
--- a/assets/xml/audio/sequences/seq_9.xml
+++ b/assets/xml/audio/sequences/seq_9.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_90.xml b/assets/xml/audio/sequences/seq_90.xml
index f56e6b0d3d1..1f1d9bde08d 100644
--- a/assets/xml/audio/sequences/seq_90.xml
+++ b/assets/xml/audio/sequences/seq_90.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_91.xml b/assets/xml/audio/sequences/seq_91.xml
index 304b90e6af0..e2bee2cb32b 100644
--- a/assets/xml/audio/sequences/seq_91.xml
+++ b/assets/xml/audio/sequences/seq_91.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_92.xml b/assets/xml/audio/sequences/seq_92.xml
index a79594534b7..3129a417d5f 100644
--- a/assets/xml/audio/sequences/seq_92.xml
+++ b/assets/xml/audio/sequences/seq_92.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_93.xml b/assets/xml/audio/sequences/seq_93.xml
index 979b7594f0c..0c6b204cb9d 100644
--- a/assets/xml/audio/sequences/seq_93.xml
+++ b/assets/xml/audio/sequences/seq_93.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_94.xml b/assets/xml/audio/sequences/seq_94.xml
index c78b040d5f4..59805445e3e 100644
--- a/assets/xml/audio/sequences/seq_94.xml
+++ b/assets/xml/audio/sequences/seq_94.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_95.xml b/assets/xml/audio/sequences/seq_95.xml
index cbdb82e6c83..cdc31a61b10 100644
--- a/assets/xml/audio/sequences/seq_95.xml
+++ b/assets/xml/audio/sequences/seq_95.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_96.xml b/assets/xml/audio/sequences/seq_96.xml
index 3bdec9ed887..402fc8b8b93 100644
--- a/assets/xml/audio/sequences/seq_96.xml
+++ b/assets/xml/audio/sequences/seq_96.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_97.xml b/assets/xml/audio/sequences/seq_97.xml
index 5eab58ee471..57263221256 100644
--- a/assets/xml/audio/sequences/seq_97.xml
+++ b/assets/xml/audio/sequences/seq_97.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_98.xml b/assets/xml/audio/sequences/seq_98.xml
index 98e9e9e0f6e..5bd56596b0e 100644
--- a/assets/xml/audio/sequences/seq_98.xml
+++ b/assets/xml/audio/sequences/seq_98.xml
@@ -1 +1,2 @@
+
diff --git a/assets/xml/audio/sequences/seq_99.xml b/assets/xml/audio/sequences/seq_99.xml
index 2e6a92c50c9..d550855a0ac 100644
--- a/assets/xml/audio/sequences/seq_99.xml
+++ b/assets/xml/audio/sequences/seq_99.xml
@@ -1 +1,2 @@
+
diff --git a/tools/audio/extraction/audio_extract.py b/tools/audio/extraction/audio_extract.py
index e4d764c32fd..05ab781072d 100644
--- a/tools/audio/extraction/audio_extract.py
+++ b/tools/audio/extraction/audio_extract.py
@@ -4,7 +4,7 @@
# Extract audio files
#
-import multiprocessing, os, shutil, time
+import os, shutil, time
from dataclasses import dataclass
from multiprocessing.pool import ThreadPool
from typing import Dict, List, Tuple, Union