-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathswdl.rs
1318 lines (1248 loc) · 55.5 KB
/
swdl.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use core::panic;
use std::fmt::Display;
use std::io::{Read, Write, Seek, SeekFrom, Cursor};
use bevy_reflect::Reflect;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use phf::phf_map;
use serde::{Serialize, Deserialize};
use crate::peek_magic;
use crate::dtype::{*};
use crate::deserialize_with;
pub mod sf2;
/// By default, all unknown bytes that do not have a consistent pattern of values in the EoS roms are included in the XML.
/// However, a subset of these not 100% purpose-certain bytes is 80% or something of values that have "typical" values.
/// Setting this to true will strip all those somewhat certain bytes from the Serde serialization process, and replace them
/// with their typical values.
const fn serde_use_common_values_for_unknowns<T>(_: &T) -> bool {
true
}
//// NOTE: Any struct fields starting with an _ indicates that that struct field will be ignored when writing, with its appropriate value generate on-the-fly based on the other fields
//// NOTE ON STRUCT XML SERIALIZATION:
/// Fields with defined values are skipped, filled with default (magic, label, etc)
/// Fields known to be the same across all the files are skipped, filled with default
/// Fields that are automatically generated read markers (like flen, chunklen, and labels) are skipped, filled with zeroes
/// Other fields that are automatically generated (ktps, etc.)
/// Fields of unknown purpose are *intentionally left alone*
/// Any other fields are also left alone
#[derive(Debug, Clone, Default, Reflect)]
pub struct DSEString<const U: u8> {
inner: [u8; 16]
}
impl<const U: u8> TryFrom<String> for DSEString<U> {
type Error = DSEError;
fn try_from(value: String) -> Result<DSEString<U>, Self::Error> {
if !value.is_ascii() {
return Err(DSEError::DSEStringConversionNonASCII(value));
}
if value.as_bytes().len() > 15 {
return Err(DSEError::DSEStringConversionLengthError(value.clone(), value.as_bytes().len()));
}
let mut buf: [u8; 16] = [U; 16];
for (i, &c) in value.as_bytes().iter().chain(std::iter::once(&0x00)).enumerate() {
buf[i] = c;
}
Ok(DSEString { inner: buf })
}
}
impl<const U: u8> Display for DSEString<U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", std::str::from_utf8(
&self.inner[..self.inner.as_ref().iter().position(|&x| x == 0).expect("Invalid DSE string! Null terminator not found!!")]
).expect("Invalid DSE string! Non-ASCII (actually, not even UTF-8) characters found!!"))
}
}
impl<const U: u8> AutoReadWrite for DSEString<U> { }
impl<const U: u8> Serialize for DSEString<U> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
self.to_string().serialize(serializer)
}
}
impl<'de, const U: u8> Deserialize<'de> for DSEString<U> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de> {
Ok(DSEString::try_from(String::deserialize(deserializer)?).unwrap())
}
}
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct SWDLHeader {
/// Note: 4-bytes represented as one u32
#[serde(default = "GenericDefaultU32::<0x6C647773>::value")]
#[serde(skip_serializing)]
pub magicn: u32,
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(rename = "@unk18")]
pub unk18: u32, // Always zeroes (hijacked for flags)
#[serde(default)]
#[serde(skip_serializing)]
pub flen: u32,
#[serde(default = "GenericDefaultU16::<0x415>::value")]
#[serde(rename = "@version")]
pub version: u16,
pub unk1: u8,
pub unk2: u8,
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing)]
pub unk3: u32, // Always zeroes
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing)]
pub unk4: u32, // Always zeroes
#[serde(rename = "@year")]
pub year: u16,
#[serde(rename = "@month")]
pub month: u8,
#[serde(rename = "@day")]
pub day: u8,
#[serde(rename = "@hour")]
pub hour: u8,
#[serde(rename = "@minute")]
pub minute: u8,
#[serde(rename = "@second")]
pub second: u8,
#[serde(rename = "@centisecond")]
pub centisecond: u8, // unsure
#[serde(rename = "@fname")]
pub fname: DSEString<0xAA>,
/// Note: 4-bytes represented as one u32
#[serde(default = "GenericDefaultU32::<0xAAAAAA00>::value")]
#[serde(skip_serializing)]
pub unk10: u32, // Always 0x00AA AAAA (little endian)
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing)]
pub unk11: u32, // Always zeroes
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing)]
pub unk12: u32, // Always zeroes
#[serde(default = "GenericDefaultU32::<0x10>::value")]
#[serde(skip_serializing)]
pub unk13: u32, // Always 0x10
#[serde(default)]
#[serde(skip_serializing)]
pub pcmdlen: u32, // Length of "pcmd" chunk if there is one. If not, is null! If set to 0xAAAA0000 (The 0000 may contains something else), the file refers to samples inside an external "pcmd" chunk, inside another SWDL !
/// Note: 2-bytes represented as one u16
#[serde(default)]
#[serde(skip_serializing)]
pub unk14: u16, // Always zeroes (The technical documentation on Project Pokemon describes this as 4 bytes, but in my testing for bgm0016.swd at least, it's 2 bytes. I've modified it here)
#[serde(default)]
#[serde(skip_serializing)]
pub nbwavislots: u16,
#[serde(default)]
#[serde(skip_serializing)]
pub nbprgislots: u16,
pub unk17: u16,
#[serde(default)]
#[serde(skip_serializing)]
pub wavilen: u32
}
impl Default for SWDLHeader {
fn default() -> Self {
SWDLHeader {
magicn: 0x6C647773,
unk18: 0,
flen: 0,
version: 0x415,
unk1: 0x00, // Random value. These just need to match with the SWD file's corresponding SMD file.
unk2: 0xFF, // Random value. These just need to match with the SWD file's corresponding SMD file.
unk3: 0,
unk4: 0,
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
centisecond: 0,
fname: DSEString::<0xAA>::default(),
unk10: 0xAAAAAA00,
unk11: 0,
unk12: 0,
unk13: 0x10,
pcmdlen: 0,
unk14: 0,
nbwavislots: 0,
nbprgislots: 0,
unk17: 524, // I'm not sure what this is so I'll just use the value from bgm0001
wavilen: 0
}
}
}
impl AutoReadWrite for SWDLHeader { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct ChunkHeader {
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing)]
pub label: u32, // Always "wavi" {0x77, 0x61, 0x76, 0x69}
#[serde(default)]
#[serde(skip_serializing)]
pub unk1: u16, // Always 0.
#[serde(default = "GenericDefaultU16::<0x415>::value")]
#[serde(skip_serializing)]
pub unk2: u16, // Always 0x1504
#[serde(default = "GenericDefaultU32::<0x10>::value")]
#[serde(skip_serializing)]
pub chunkbeg: u32, // Seems to always be 0x10, possibly the start of the chunk data.
#[serde(default)]
#[serde(skip_serializing)]
pub chunklen: u32, // Length of the chunk data.
}
impl Default for ChunkHeader {
fn default() -> ChunkHeader {
ChunkHeader {
label: 0,
unk1: 0,
unk2: 0x415,
chunkbeg: 0x10,
chunklen: 0
}
}
}
impl AutoReadWrite for ChunkHeader { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct ADSRVolumeEnvelope {
#[serde(rename = "@envon")]
pub envon: bool, // Volume envelope on
#[serde(rename = "@envmult")]
pub envmult: u8, // If not == 0, is used as multiplier for envelope paramters, and the 16bits lookup table is used for parameter durations. If 0, the 32bits duration lookup table is used instead. This value has no effects on volume parameters, like sustain, and atkvol.
#[serde(default = "GenericDefaultU8::<0x1>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk19: u8, // Usually 0x1
#[serde(default = "GenericDefaultU8::<0x3>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk20: u8, // Usually 0x3
#[serde(default = "GenericDefaultU16::<0xFF03>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk21: u16, // Usually 0x03FF (little endian -253)
#[serde(default = "GenericDefaultU16::<0xFFFF>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk22: u16, // Usually 0xFFFF
#[serde(rename = "@atkvol")]
pub atkvol: i8, // Sample volume envelope attack volume (0-127) Higher values towards 0x7F means the volume at which the attack phase begins at is louder. Doesn't shorten the attack time.
#[serde(rename = "@attack")]
pub attack: i8, // Sample volume envelope attack (0-127) 126 is ~10 secs
#[serde(rename = "@decay")]
pub decay: i8, // Sample volume envelope decay (0-127) Time it takes for note to fall in volume to sustain volume after hitting attack stage
#[serde(rename = "@sustain")]
pub sustain: i8, // Sample volume envelope sustain (0-127) Note stays at this until noteoff
#[serde(rename = "@hold")]
pub hold: i8, // Sample volume envelope hold (0-127) After attack, do not immediately start decaying towards the sustain level. Keep the full volume for some time based on the hold value here.
#[serde(rename = "@decay2")]
pub decay2: i8, // Sample volume envelope decay 2 (0-127) Time it takes for note to fade after hitting sustain volume.
#[serde(rename = "@release")]
pub release: i8, // Kinda similar to decay2, but I'd hazard a guess that this controls release *after* note off while `decay2` is release while the note is still pressed.
#[serde(default = "GenericDefaultI8::<-1>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk57: i8 // Usually 0xFF
}
impl Default for ADSRVolumeEnvelope {
fn default() -> Self {
ADSRVolumeEnvelope {
envon: false,
envmult: 0,
unk19: 0x1,
unk20: 0x3,
unk21: 0xFF03,
unk22: 0xFFFF,
atkvol: 0,
attack: 0,
decay: 0,
sustain: 0,
hold: 0,
decay2: 0,
release: 0,
unk57: -1
}
}
}
impl ADSRVolumeEnvelope {
/// Returns an alternative "default value" of `ADSRVolumeEnvelope` based on observations of common values inside the game's swdl soundtrack.
pub fn default2() -> Self {
let mut default = Self::default();
// These params are the default for all samples in the WAVI section as seen from the bgm0001.swd and bgm.swd files.
default.envon = true;
default.envmult = 1;
default.atkvol = 0;
default.attack = 0;
default.decay = 0;
default.sustain = 127;
default.hold = 0;
default.decay2 = 127;
default.release = 40;
default
}
}
impl AutoReadWrite for ADSRVolumeEnvelope { }
#[derive(Debug, Default, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct Tuning {
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(rename = "@ftune")]
ftune: u8, // Pitch fine tuning, ranging from 0 to 255 with 255 representing +100 cents and 0 representing no change.
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(rename = "@ctune")]
ctune: i8 // Coarse tuning, possibly in semitones(?). Default is -7
}
impl Tuning {
pub fn new(ftune: u8, ctune: i8) -> Tuning {
Tuning { ftune, ctune }
}
pub fn from_cents(mut cents: i64) -> Tuning {
let mut sign = 1;
if cents == 0 {
return Tuning::new(0, 0);
} else if cents < 0 {
sign = -1;
}
cents = cents.abs();
let mut ctune = 0;
let mut ftune = cents;
while ftune >= 100 {
ftune -= 100;
ctune += 1;
}
ctune = sign * ctune;
ftune = sign * ftune;
if ftune < 0 {
ftune += 100;
ctune -= 1;
}
let ftune = ((ftune as f64 / 100.0) * 255.0).round() as u8;
Tuning::new(ftune, ctune as i8)
}
pub fn ftune(&self) -> u8 {
self.ftune
}
pub fn ctune(&self) -> i8 {
self.ctune
}
pub fn to_cents(&self) -> i64 {
self.ctune as i64 * 100 + ((self.ftune as f64 / 255.0) * 100.0).round() as i64
}
pub fn add_semitones(&mut self, semitones: i64) {
self.add_cents(semitones * 100);
}
pub fn add_cents(&mut self, cents: i64) {
*self = Self::from_cents(self.to_cents() + cents);
}
}
impl AutoReadWrite for Tuning { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct SampleInfo {
#[serde(default = "GenericDefaultU16::<0xAA01>::value")]
#[serde(skip_serializing)]
pub unk1: u16, // Entry marker? Always 0x01AA
#[serde(rename = "@id")]
pub id: u16,
#[serde(flatten)]
#[serde(rename = "@tuning")]
pub tuning: Tuning,
#[serde(rename = "@rootkey")]
pub rootkey: i8, // MIDI note
#[serde(default)]
#[serde(skip_serializing)]
pub ktps: i8, // Key transpose. Diff between rootkey and 60.
#[serde(rename = "@volume")]
pub volume: i8, // Volume of the sample.
#[serde(rename = "@pan")]
pub pan: i8, // Pan of the sample.
#[serde(default)]
#[serde(skip_serializing)]
pub unk5: u8, // Possibly Keygroup parameter for the sample. Always 0x00.
#[serde(default = "GenericDefaultU8::<0x02>::value")]
#[serde(skip_serializing)]
pub unk58: u8, // Always 0x02
#[serde(default = "GenericDefaultU16::<0x0000>::value")]
#[serde(skip_serializing)]
pub unk6: u16, // Always 0x0000
/// Note: 2-bytes represented as one u16
#[serde(default = "GenericDefaultU16::<0xAAAA>::value")]
#[serde(skip_serializing)]
pub unk7: u16, // 0xAA padding.
#[serde(default = "GenericDefaultU16::<0x415>::value")]
#[serde(skip_serializing)]
pub unk59: u16, // Always 0x1504.
#[serde(rename = "@smplfmt")]
pub smplfmt: u16, // Sample format. 0x0000: 8-bit PCM, 0x0100: 16-bits PCM, 0x0200: 4-bits ADPCM, 0x0300: Possibly PSG
#[serde(default = "GenericDefaultU8::<0x09>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk9: u8, // Often 0x09
#[serde(rename = "@smplloop")]
pub smplloop: bool, // true = looped, false = not looped
#[serde(default = "GenericDefaultU16::<0x0801>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk10: u16, // Often 0x0108
#[serde(default = "GenericDefaultU16::<0x0400>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk11: u16, // Often 0004 (Possible typo, 0x0400)
#[serde(default = "GenericDefaultU16::<0x0101>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk12: u16, // Often 0x0101
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk13: u32, // Often 0x0000 0000
#[serde(rename = "@smplrate")]
pub smplrate: u32, // Sample rate in hertz
#[serde(rename = "@smplpos")]
pub smplpos: u32, // Offset of the sound sample in the "pcmd" chunk when there is one. Otherwise, possibly offset of the exact sample among all the sample data loaded in memory? (The value usually doesn't match the main bank's)
#[serde(rename = "@loopbeg")]
pub loopbeg: u32, // The position in bytes divided by 4, the loop begins at, from smplpos. ( multiply by 4 to get size in bytes ) Adding loopbeg + looplen gives the sample's length ! (For ADPCM samples, the 4 bytes preamble is counted in the loopbeg!) (P.s. the division by 4 might be because in a Stereo 16-bit PCM signal, 4 bytes is one sample (16-bit l, then 16-bit r))
#[serde(rename = "@looplen")]
pub looplen: u32, // The length of the loop in bytes, divided by 4. ( multiply by 4 to get size in bytes ) Adding loopbeg + looplen gives the sample's length !
pub volume_envelope: ADSRVolumeEnvelope
}
impl Default for SampleInfo {
fn default() -> Self {
SampleInfo {
unk1: 0xAA01,
id: 0,
tuning: Tuning::new(0, 0),
rootkey: 0,
ktps: 0,
volume: 0,
pan: 0,
unk5: 0x00,
unk58: 0x02,
unk6: 0x0000,
unk7: 0xAAAA,
unk59: 0x415,
smplfmt: 0x0100,
unk9: 0x09,
smplloop: false,
unk10: 0x0801,
unk11: 0x0400,
unk12: 0x0101,
unk13: 0,
smplrate: 0,
smplpos: 0,
loopbeg: 0,
looplen: 0,
volume_envelope: ADSRVolumeEnvelope::default()
}
}
}
impl IsSelfIndexed for SampleInfo {
fn is_self_indexed(&self) -> Option<usize> {
Some(self.id as usize)
}
fn change_self_index(&mut self, new_index: usize) -> Result<(), DSEError> {
self.id = new_index.try_into().map_err(|_| DSEError::Placeholder())?;
Ok(())
}
}
impl AutoReadWrite for SampleInfo { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct ProgramInfoHeader {
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(rename = "@id")]
pub id: u16, // Index of the pointer in the pointer table. Also corresponding to the program ID in the corresponding SMDL file!
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing)]
pub nbsplits: u16, // Nb of samples mapped to this preset in the split table.
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(rename = "@prgvol")]
pub prgvol: i8, // Volume of the entire program.
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(rename = "@prgpan")]
pub prgpan: i8, // Pan of the entire program (0-127, 64 mid, 127 right, 0 left)
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk3: u8, // Most of the time 0x00
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default = "GenericDefaultU8::<0x0F>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub thatFbyte: u8, // Most of the time 0x0F
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default = "GenericDefaultU16::<0x200>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk4: u16, // Most of the time 0x200
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk5: u8, // Most of the time is 0x00
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing)]
pub nblfos: u8, // Nb of entries in the LFO table.
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(rename = "@PadByte")]
pub PadByte: u8, // Most of the time is 0xAA, or 0x00. Value here used as the delimiter and padding later between the LFOTable and the SplitEntryTable (and more)
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk7: u8, // Most of the time is 0x0
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk8: u8, // Most of the time is 0x0
#[serde(deserialize_with = "deserialize_with::flattened_xml_attr")]
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk9: u8, // Most of the time is 0x0
}
impl Default for ProgramInfoHeader {
fn default() -> Self {
ProgramInfoHeader {
id: 0,
nbsplits: 0,
prgvol: 0,
prgpan: 0,
unk3: 0,
thatFbyte: 0x0F,
unk4: 0x200,
unk5: 0,
nblfos: 0,
PadByte: 0xAA,
unk7: 0,
unk8: 0,
unk9: 0
}
}
}
impl IsSelfIndexed for ProgramInfoHeader {
fn is_self_indexed(&self) -> Option<usize> {
Some(self.id as usize)
}
fn change_self_index(&mut self, new_index: usize) -> Result<(), DSEError> {
self.id = new_index.try_into().map_err(|_| DSEError::Placeholder())?;
Ok(())
}
}
impl AutoReadWrite for ProgramInfoHeader { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct LFOEntry {
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk34: u8, // Unknown, usually 0x00. Does seem to have an effect with a certain combination of other values in the other parameters.
#[serde(default)]
#[serde(rename = "@unk52_lfo_on")]
pub unk52: u8, // Unknown, usually 0x00. Most of the time, value is 1 when the LFO is in use.
#[serde(rename = "@dest")]
pub dest: u8, // 0x0: disabled, 0x1: pitch, 0x2: volume, 0x3: pan, 0x4: lowpass/cutoff filter?
#[serde(rename = "@wshape")]
pub wshape: u8, // Shape/function of the waveform. When the LFO is disabled, its always 1.
#[serde(rename = "@rate")]
pub rate: u16, // Rate at which the LFO "oscillate". May or may not be in Hertz.
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk29: u16, // uint16? Changing the value seems to induce feedback or resonance. (Corrupting engine?)
#[serde(rename = "@depth")]
pub depth: u16, // The depth parameter of the LFO.
#[serde(rename = "@delay")]
pub delay: u16, // Delay in ms before the LFO's effect is applied after the sample begins playing. (Per-note LFOs! So fancy!)
#[serde(default)]
#[serde(rename = "@unk32_fadeout")]
pub unk32: u16, // Unknown, usually 0x0000. Possibly fade-out in ms.
#[serde(default)]
#[serde(rename = "@unk33_lowpassfreq")]
pub unk33: u16, // Unknown, usually 0x0000. Possibly an extra parameter? Or a cutoff/lowpass filter's frequency cutoff?
}
impl Default for LFOEntry {
fn default() -> Self {
LFOEntry {
unk34: 0,
unk52: 0,
dest: 0,
wshape: 1,
rate: 0,
unk29: 0,
depth: 0,
delay: 0,
unk32: 0,
unk33: 0
}
}
}
impl IsSelfIndexed for LFOEntry {
fn is_self_indexed(&self) -> Option<usize> {
None
}
fn change_self_index(&mut self, _: usize) -> Result<(), DSEError> {
Err(DSEError::Invalid("LFO entries do not have indices!!".to_string()))
}
}
impl AutoReadWrite for LFOEntry { }
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct SplitEntry {
#[serde(default)]
#[serde(skip_serializing)]
pub unk10: u8, // A leading 0.
#[serde(rename = "@id")]
pub id: u8, // The Index of the sample in the SplitsTbl! (So, a simple array with elements that reference the index of itself)
#[serde(default = "GenericDefaultU8::<0x02>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk11: u8, // Unknown. Is always the same value as offset 0x1A below! (It doesn't seem to match kgrpid, so I'm wondering which byte this might be referring to:::: It refers to unk22, the one after kgrpid) (Possibly "bend range" according to assumptions made from teh DSE screenshots) (Could it maybe affect how some tracks sound if it is ever defined and we discards it?)
#[serde(default = "GenericDefaultU8::<0x01>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk25: u8, // Unknown. Possibly a boolean.
#[serde(rename = "@lowkey")]
pub lowkey: i8, // Usually 0x00. Lowest MIDI key this sample can play on.
#[serde(rename = "@hikey")]
pub hikey: i8, // Usually 0x7F. Highest MIDI key this sample can play on.
#[serde(default)]
#[serde(skip_serializing)]
pub lowkey2: i8, // A copy of lowkey, for unknown purpose.
#[serde(default)]
#[serde(skip_serializing)]
pub hikey2: i8, // A copy of hikey, for unknown purpose.
#[serde(rename = "@lovel")]
pub lovel: i8, // Lowest note velocity the sample is played on. (0-127) (DSE has velocity layers!)
#[serde(rename = "@hivel")]
pub hivel: i8, // Highest note velocity the sample is played on. (0-127)
#[serde(default)]
#[serde(skip_serializing)]
pub lovel2: i8, // A copy of lovel, for unknown purpose. Usually 0x00.
#[serde(default)]
#[serde(skip_serializing)]
pub hivel2: i8, // A copy of hivel, for unknown purpose. Usually 0x7F.
/// Note: 4-bytes represented as one u32
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
///
/// Addendum 8/1/2023: PadByte doesn't seem to always match what's in here. For example in track 43, while the padding *should* be 0x00's at 0x740, it is instead 0xAA's.
pub unk16: u32, // Usually the same value as "PadByte", or 0. Possibly padding.
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
/// Note: 2-bytes represented as one u16
///
/// Addendum 8/1/2023: PadByte doesn't seem to always match what's in here. For example in track 43, while the padding *should* be 0x00's at 0x740, it is instead 0xAA's.
pub unk17: u16, // Usually the same value as "PadByte", or 0. Possibly padding.
#[serde(rename = "@SmplID")]
pub SmplID: u16, // The ID/index of sample in the "wavi" chunk's lookup table.
#[serde(flatten)]
#[serde(rename = "@tuning")]
pub tuning: Tuning,
#[serde(rename = "@rootkey")]
pub rootkey: i8, // Note at which the sample is sampled at!
#[serde(default)]
#[serde(skip_serializing)]
pub ktps: i8, // Key transpose. Diff between rootkey and 60.
#[serde(rename = "@smplvol")]
pub smplvol: i8, // Volume of the sample
#[serde(rename = "@smplpan")]
pub smplpan: i8, // Pan of the sample
#[serde(rename = "@kgrpid")]
pub kgrpid: u8, // Keygroup ID of the keygroup this split belongs to!
#[serde(default = "GenericDefaultU8::<0x02>::value")]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk22: u8, // Unknown, possibly a flag. Usually 0x02. Matches unk11
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk23: u16, // Unknown, usually 0000.
/// Note: 2-bytes represented as one u16
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
///
/// Addendum 8/1/2023: PadByte doesn't seem to always match what's in here. For example in track 43, while the padding *should* be 0x00's at 0x740, it is instead 0xAA's.
pub unk24: u16, // Usually the same value as "PadByte", or 0. Possibly padding?
// After here, the last 16 bytes are for the volume enveloped. They override the sample's original volume envelope!
pub volume_envelope: ADSRVolumeEnvelope
}
impl Default for SplitEntry {
fn default() -> Self {
SplitEntry {
unk10: 0,
id: 0,
unk11: 0x02,
unk25: 0x01,
lowkey: 0,
hikey: 0,
lowkey2: 0,
hikey2: 0,
lovel: 0,
hivel: 0,
lovel2: 0,
hivel2: 0,
unk16: 0,
unk17: 0,
SmplID: 0,
tuning: Tuning::new(0, 0),
rootkey: 0,
ktps: 0,
smplvol: 0,
smplpan: 0,
kgrpid: 0x0,
unk22: 0x02,
unk23: 0,
unk24: 0,
volume_envelope: ADSRVolumeEnvelope::default()
}
}
}
impl IsSelfIndexed for SplitEntry {
fn is_self_indexed(&self) -> Option<usize> {
Some(self.id as usize)
}
fn change_self_index(&mut self, new_index: usize) -> Result<(), DSEError> {
self.id = new_index.try_into().map_err(|_| DSEError::Placeholder())?;
Ok(())
}
}
impl AutoReadWrite for SplitEntry { }
#[derive(Debug, Clone, Default, Reflect, Serialize, Deserialize)]
pub struct _ProgramInfoDelimiter {
pub delimiter: [u8; 16],
}
impl AutoReadWrite for _ProgramInfoDelimiter { }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgramInfo {
#[serde(flatten)]
pub header: ProgramInfoHeader,
#[serde(default)]
#[serde(skip_serializing_if = "Table::table_is_empty")]
pub lfo_table: Table<LFOEntry>,
#[serde(default)]
#[serde(skip_serializing)]
pub _delimiter: _ProgramInfoDelimiter,
#[serde(default)]
#[serde(skip_serializing_if = "Table::table_is_empty")]
pub splits_table: Table<SplitEntry>
}
impl IsSelfIndexed for ProgramInfo {
fn is_self_indexed(&self) -> Option<usize> {
self.header.is_self_indexed()
}
fn change_self_index(&mut self, new_index: usize) -> Result<(), DSEError> {
self.header.change_self_index(new_index)
}
}
impl Default for ProgramInfo {
fn default() -> ProgramInfo {
ProgramInfo {
header: ProgramInfoHeader::default(),
lfo_table: Table::new(4), // Rough estimate
_delimiter: _ProgramInfoDelimiter::default(),
splits_table: Table::new(8) // Rough estimate
}
}
}
impl ReadWrite for ProgramInfo {
fn write_to_file<W: Read + Write + Seek>(&self, writer: &mut W) -> Result<usize, DSEError> {
let mut bytes_written = self.header.write_to_file(writer)?;
bytes_written += self.lfo_table.write_to_file(writer)?;
// bytes_written += self._delimiter.write_to_file(writer)?;
bytes_written += vec![self.header.PadByte; 16].write_to_file(writer)?;
bytes_written += self.splits_table.write_to_file(writer)?;
Ok(bytes_written)
}
fn read_from_file<R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), DSEError> {
self.header.read_from_file(reader)?;
self.lfo_table.set_read_params(self.header.nblfos as usize);
self.lfo_table.read_from_file(reader)?;
self._delimiter.read_from_file(reader)?;
self.splits_table.set_read_params(self.header.nbsplits as usize);
self.splits_table.read_from_file(reader)?;
Ok(())
}
}
#[derive(Debug, Clone, Default, Reflect, Serialize, Deserialize)]
pub struct Keygroup {
#[serde(rename = "@id")]
pub id: u16, // Index/ID of the keygroup
#[serde(rename = "@poly")]
pub poly: i8, // Polyphony. Max number of simultaneous notes played. 0 to 15. -1 means disabled. (Technical documentation describes this field as unsigned, but I've switched it to signed since -1 is off instead of 255 being off)
#[serde(rename = "@priority")]
pub priority: u8, // Priority over the assignment of voice channels for members of this group. 0-possibly 99, default is 8. Higher is higeher priority.
#[serde(rename = "@vclow")]
pub vclow: i8, // Lowest voice channel the group may use. Usually between 0 and 15
#[serde(rename = "@vchigh")]
pub vchigh: i8, // Highest voice channel this group may use. 0-15 (While not explicitly stated in the documentation, this value being i8 makes sense as the first keygroup typically has this set to 255 which makes more sense interpreted as -1 disabled)
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk50: u8, // Unown
#[serde(default)]
#[serde(skip_serializing_if = "serde_use_common_values_for_unknowns")]
pub unk51: u8, // Unknown
}
impl IsSelfIndexed for Keygroup {
fn is_self_indexed(&self) -> Option<usize> {
Some(self.id as usize)
}
fn change_self_index(&mut self, new_index: usize) -> Result<(), DSEError> {
self.id = new_index.try_into().map_err(|_| DSEError::Placeholder())?;
Ok(())
}
}
impl AutoReadWrite for Keygroup { }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WAVIChunk {
#[serde(default)]
#[serde(skip_serializing)]
_read_n: usize,
#[serde(default)]
#[serde(skip_serializing)]
pub header: ChunkHeader,
pub data: PointerTable<SampleInfo>
}
impl WAVIChunk {
pub fn new(nbwavislots: usize) -> WAVIChunk {
WAVIChunk {
_read_n: nbwavislots,
header: ChunkHeader::default(),
data: PointerTable::new(nbwavislots, 0) // Temporarily 0
}
}
pub fn set_read_params(&mut self, nbwavislots: usize) {
self._read_n = nbwavislots;
}
}
impl WAVIChunk {
pub fn write_to_file<P: Pointer<LittleEndian>, W: Read + Write + Seek>(&self, writer: &mut W) -> Result<usize, DSEError> {
Ok(self.header.write_to_file(writer)? + self.data.write_to_file::<P, _>(writer).map_err(|e| match e {
DSEError::Placeholder() => DSEError::PointerTableTooLarge(DSEBlockType::SwdlWavi),
_ => e
})?)
}
pub fn read_from_file<P: Pointer<LittleEndian>, R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), DSEError> {
self.header.read_from_file(reader)?;
self.data.set_read_params(self._read_n, self.header.chunklen);
self.data.read_from_file::<P, _>(reader)?;
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PRGIChunk {
#[serde(default)]
#[serde(skip_serializing)]
_read_n: usize,
#[serde(default)]
#[serde(skip_serializing)]
pub header: ChunkHeader,
pub data: PointerTable<ProgramInfo>
}
impl PRGIChunk {
pub fn new(nbprgislots: usize) -> PRGIChunk {
PRGIChunk {
_read_n: nbprgislots,
header: ChunkHeader::default(),
data: PointerTable::new(nbprgislots, 0) // Temporarily 0
}
}
pub fn set_read_params(&mut self, nbprgislots: usize) {
self._read_n = nbprgislots;
}
}
impl PRGIChunk {
pub fn write_to_file<P: Pointer<LittleEndian>, W: Read + Write + Seek>(&self, writer: &mut W) -> Result<usize, DSEError> {
Ok(self.header.write_to_file(writer)? + self.data.write_to_file::<P, _>(writer).map_err(|e| match e {
DSEError::Placeholder() => DSEError::PointerTableTooLarge(DSEBlockType::SwdlPrgi),
_ => e
})?)
}
pub fn read_from_file<P: Pointer<LittleEndian>, R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), DSEError> {
self.header.read_from_file(reader)?;
self.data.set_read_params(self._read_n, self.header.chunklen);
self.data.read_from_file::<P, _>(reader)?;
Ok(())
}
}
#[derive(Debug, Clone, Default, Reflect, Serialize, Deserialize)]
pub struct _KeygroupsSampleDataDelimiter {
pub delimiter: [u8; 8],
}
impl AutoReadWrite for _KeygroupsSampleDataDelimiter { }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KGRPChunk {
#[serde(default)]
#[serde(skip_serializing)]
pub header: ChunkHeader,
pub data: Table<Keygroup>,
#[serde(default)]
#[serde(skip_serializing)]
pub _padding: Option<_KeygroupsSampleDataDelimiter>
}
impl Default for KGRPChunk {
fn default() -> KGRPChunk {
KGRPChunk {
header: ChunkHeader::default(),
data: Table::new(0),
_padding: None
}
}
}
impl ReadWrite for KGRPChunk {
fn write_to_file<W: Read + Write + Seek>(&self, writer: &mut W) -> Result<usize, DSEError> {
Ok(self.header.write_to_file(writer)? + self.data.write_to_file(writer)? + if self.data.objects.len() % 2 == 1 { vec![0x67, 0xC0, 0x40, 0x00, 0x88, 0x00, 0xFF, 0x04].write_to_file(writer)? } else { 0 })
// Ok(self.header.write_to_file(writer)? + self.data.write_to_file(writer)? + if let Some(pad) = &self._padding { pad.write_to_file(writer)? } else { 0 })
}
fn read_from_file<R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), DSEError> {
self.header.read_from_file(reader)?;
self.data.set_read_params(self.header.chunklen as usize / 8);
self.data.read_from_file(reader)?;
self._padding = Some(_KeygroupsSampleDataDelimiter::default());
self._padding.as_mut().unwrap().read_from_file(reader)?;
// "pcmd" {0x70, 0x63, 0x6D, 0x64}
// "eod\20" {0x65, 0x6F, 0x64, 0x20}
if &self._padding.as_ref().unwrap().delimiter[..4] == &[0x70, 0x63, 0x6D, 0x64] ||
&self._padding.as_ref().unwrap().delimiter[..4] == &[0x65, 0x6F, 0x64, 0x20] {
self._padding = None;
reader.seek(SeekFrom::Current(-8))?;
}
Ok(())
}
}
mod base64 {
use serde::{Serialize, Deserialize};
use serde::{Deserializer, Serializer};
use base64::{Engine as _, engine::general_purpose};
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = general_purpose::STANDARD.encode(v);
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = String::deserialize(d)?;
general_purpose::STANDARD.decode(base64)
.map_err(|e| serde::de::Error::custom(e))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PCMDChunk {
#[serde(default)]
#[serde(skip_serializing)]
pub header: ChunkHeader,
#[serde(with = "base64")]
pub data: Vec<u8>,
#[serde(default)]
#[serde(skip_serializing)]
pub _padding: Vec<u8>
}
impl Default for PCMDChunk {
fn default() -> Self {
PCMDChunk {
header: ChunkHeader::default(),
data: Vec::new(),
_padding: Vec::new()
}
}
}
impl ReadWrite for PCMDChunk {
fn write_to_file<W: Read + Write + Seek>(&self, writer: &mut W) -> Result<usize, DSEError> {
let len = self.header.write_to_file(writer)? + self.data.write_to_file(writer)?;
let len_aligned = ((len - 1) | 15) + 1; // Round the length of the pcmd chunk in bytes to the next multiple of 16
let padding_zero = len_aligned - len;
for _ in 0..padding_zero {
writer.write_u8(0)?;
}
Ok(len_aligned)
}