1use arr_macro::arr;
2use num_enum::{IntoPrimitive, TryFromPrimitive};
3
4use crate::{
5 songs::{Song, V4_1_OFFSETS, V4_OFFSETS},
6 Instrument, Version,
7};
8
9#[repr(u8)]
10#[allow(non_camel_case_types)]
11#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Copy, Clone, Default, Debug)]
12pub enum MoveKind {
13 #[default]
14 EQ,
15 INS,
16 PHR,
17 CHN,
18 TBL,
19}
20
21pub trait RemapperDescriptorBuilder {
22 fn moved(&mut self, kind: MoveKind, from: usize, to: usize);
23}
24
25fn make_mapping<const C: usize>(offset: u8) -> [u8; C] {
26 let mut arr = [0 as u8; C];
27 for i in 0..arr.len() {
28 arr[i] = i as u8 + offset;
29 }
30
31 arr
32}
33
34pub struct EqMapping {
35 pub mapping: Vec<u8>,
38
39 pub to_move: Vec<u8>,
42}
43
44impl EqMapping {
45 pub fn default_ver(ver: Version) -> EqMapping {
46 if ver.at_least(4, 1) {
47 EqMapping {
48 mapping: vec![0; V4_1_OFFSETS.instrument_eq_count],
49 to_move: vec![],
50 }
51 } else {
52 EqMapping {
53 mapping: vec![0; V4_OFFSETS.instrument_eq_count],
54 to_move: vec![],
55 }
56 }
57 }
58
59 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
60 for ix in &self.to_move {
61 let ixu = *ix as usize;
62 builder.moved(MoveKind::EQ, ixu, self.mapping[ixu] as usize)
63 }
64 }
65
66 pub fn print(&self) -> String {
67 let mut acc = String::new();
68
69 for e in self.to_move.iter() {
70 let new_ix = self.mapping[*e as usize];
71 acc = format!("{acc} Eq {e} => {new_ix}\n");
72 }
73
74 acc
75 }
76}
77
78pub struct InstrumentMapping {
80 pub mapping: [u8; Song::N_INSTRUMENTS],
83
84 pub to_move: Vec<u8>,
87}
88
89impl InstrumentMapping {
90 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
91 for ix in &self.to_move {
92 let ixu = *ix as usize;
93 builder.moved(MoveKind::INS, ixu, self.mapping[ixu] as usize)
94 }
95 }
96
97 pub fn print(&self) -> String {
98 let mut acc = String::new();
99
100 for e in self.to_move.iter() {
101 let new_ix = self.mapping[*e as usize];
102 acc = format!("{acc} instr {e} => {new_ix}\n");
103 }
104
105 acc
106 }
107}
108
109impl Default for InstrumentMapping {
110 fn default() -> Self {
111 Self {
112 mapping: make_mapping(0),
113 to_move: vec![],
114 }
115 }
116}
117
118pub struct TableMapping {
119 pub mapping: [u8; Song::N_TABLES],
121
122 pub to_move: Vec<u8>,
124}
125
126impl TableMapping {
127 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
128 for ix in &self.to_move {
129 let ixu = *ix as usize;
130 builder.moved(MoveKind::TBL, ixu, self.mapping[ixu] as usize)
131 }
132 }
133
134 pub fn remap_table(&mut self, from: u8, to: u8) {
135 self.mapping[from as usize] = to;
136 self.to_move.push(from);
137 }
138}
139
140impl Default for TableMapping {
141 fn default() -> Self {
142 Self {
143 mapping: make_mapping(Song::N_TABLES as u8),
144 to_move: vec![],
145 }
146 }
147}
148
149pub struct PhraseMapping {
150 pub mapping: [u8; Song::N_PHRASES],
153
154 pub to_move: Vec<u8>,
157}
158
159impl PhraseMapping {
160 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
161 for ix in &self.to_move {
162 let ixu = *ix as usize;
163 builder.moved(MoveKind::PHR, ixu, self.mapping[ixu] as usize)
164 }
165 }
166
167 pub fn print(&self) -> String {
168 let mut acc = String::new();
169
170 for e in self.to_move.iter() {
171 let new_ix = self.mapping[*e as usize];
172 acc = format!("{acc} phrase {e} => {new_ix}\n");
173 }
174
175 acc
176 }
177}
178
179impl Default for PhraseMapping {
180 fn default() -> Self {
181 Self {
182 mapping: make_mapping(0),
183 to_move: vec![],
184 }
185 }
186}
187
188pub struct ChainMapping {
189 pub mapping: [u8; Song::N_CHAINS],
190 pub to_move: Vec<u8>,
191}
192
193impl ChainMapping {
194 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
195 for ix in &self.to_move {
196 let ixu = *ix as usize;
197 builder.moved(MoveKind::CHN, ixu, self.mapping[ixu] as usize)
198 }
199 }
200
201 pub fn print(&self) -> String {
202 let mut acc = String::new();
203
204 for e in self.to_move.iter() {
205 let new_ix = self.mapping[*e as usize];
206 acc = format!("{acc} chain {e} => {new_ix}\n");
207 }
208
209 acc
210 }
211}
212
213impl Default for ChainMapping {
214 fn default() -> Self {
215 Self {
216 mapping: make_mapping(0),
217 to_move: vec![],
218 }
219 }
220}
221
222pub struct Remapper {
223 pub eq_mapping: EqMapping,
224 pub instrument_mapping: InstrumentMapping,
225 pub table_mapping: TableMapping,
226 pub phrase_mapping: PhraseMapping,
227 pub chain_mapping: ChainMapping,
228}
229
230fn find_referenced_eq(song: &Song) -> Vec<bool> {
232 let mut allocated_eqs = vec![false; song.eqs.len()];
234
235 for instr in &song.instruments {
236 match instr.equ() {
237 None => {}
238 Some(eq) => {
239 let equ = eq as usize;
240 if equ < allocated_eqs.len() {
241 allocated_eqs[equ] = true
242 }
243 }
244 }
245 }
246
247 allocated_eqs
250}
251
252fn find_allocated_instruments(song: &Song) -> [bool; Song::N_INSTRUMENTS] {
253 let mut allocated_instr = arr![false; 128];
254
255 for (i, instr) in song.instruments.iter().enumerate() {
256 match instr {
257 Instrument::None => {}
258 _ => allocated_instr[i] = true,
259 }
260 }
261
262 allocated_instr
263}
264
265fn find_referenced_phrases(song: &Song) -> [bool; Song::N_PHRASES] {
266 let mut allocated_phrases = arr![false; 255];
267 for chain in &song.chains {
268 for step in &chain.steps {
269 let phrase = step.phrase as usize;
270 if phrase < Song::N_PHRASES {
271 allocated_phrases[phrase] = true;
272 }
273 }
274 }
275
276 for (phrase_id, phrase) in song.phrases.iter().enumerate() {
277 if !phrase.is_empty() {
278 allocated_phrases[phrase_id] = true;
279 }
280 }
281
282 allocated_phrases
283}
284
285fn find_referenced_chains(song: &Song) -> [bool; Song::N_CHAINS] {
286 let mut allocated_chains = arr![false; 255];
287 for chain in song.song.steps.iter() {
288 let chain = *chain as usize;
289 if chain < Song::N_CHAINS {
290 allocated_chains[chain] = true;
291 }
292 }
293
294 for (i, chain) in song.chains.iter().enumerate() {
295 if !chain.is_empty() {
296 allocated_chains[i] = true
297 }
298 }
299
300 allocated_chains
301}
302
303fn try_allocate(allocation_state: &[bool], previous_id: u8) -> Option<usize> {
305 let prev = previous_id as usize;
306 if !allocation_state[prev] {
307 Some(prev)
308 } else {
309 match allocation_state[prev..].iter().position(|v| !v) {
310 Some(p) => Some(p + prev),
312 None => allocation_state.iter().position(|v| !v),
314 }
315 }
316}
317
318impl Remapper {
319 pub fn default_ver(ver: Version) -> Self {
320 Self {
321 eq_mapping: EqMapping::default_ver(ver),
322 instrument_mapping: Default::default(),
323 table_mapping: Default::default(),
324 phrase_mapping: Default::default(),
325 chain_mapping: Default::default(),
326 }
327 }
328
329 pub fn describe<T: RemapperDescriptorBuilder>(&self, builder: &mut T) {
330 self.eq_mapping.describe(builder);
331 self.instrument_mapping.describe(builder);
332 self.table_mapping.describe(builder);
333 self.phrase_mapping.describe(builder);
334 self.chain_mapping.describe(builder);
335 }
336
337 pub fn out_chain(&self, chain_id: u8) -> u8 {
338 self.chain_mapping.mapping[chain_id as usize]
339 }
340
341 pub fn print(&self) -> String {
342 let eq = self.eq_mapping.print();
343 let instr = self.instrument_mapping.print();
344 let phrase = self.phrase_mapping.print();
345 let chain = self.chain_mapping.print();
346 format!("{eq}\n{instr}\n{phrase}\n{chain}")
347 }
348
349 fn allocate_chains<'a, IT>(
350 from_song: &Song,
351 to_song: &Song,
352 phrase_mapping: &PhraseMapping,
353 from_chains_ids: IT,
354 ) -> Result<ChainMapping, String>
355 where
356 IT: Iterator<Item = &'a u8>,
357 {
358 let mut seen_chain: [bool; Song::N_CHAINS] = arr![false; 255];
359 let mut allocated_chains = find_referenced_chains(to_song);
360 let mut mapping: [u8; Song::N_CHAINS] = make_mapping(0);
361 let mut to_move = vec![];
362
363 for chain_id in from_chains_ids {
364 let chain_id = *chain_id as usize;
365 if chain_id >= Song::N_CHAINS || seen_chain[chain_id] {
366 continue;
367 }
368
369 seen_chain[chain_id] = true;
370 let to_chain = from_song.chains[chain_id].map(phrase_mapping);
371
372 match to_song
373 .chains
374 .iter()
375 .position(|c| c.steps == to_chain.steps)
376 {
377 Some(c) => mapping[chain_id] = c as u8,
378 None => match try_allocate(&allocated_chains, chain_id as u8) {
379 None => {
380 return Err(format!(
381 "No more available chain slots for chain {chain_id}"
382 ))
383 }
384 Some(free_slot) => {
385 allocated_chains[free_slot] = true;
386 mapping[chain_id] = free_slot as u8;
387 to_move.push(chain_id as u8);
388 }
389 },
390 }
391 }
392
393 Ok(ChainMapping { mapping, to_move })
394 }
395
396 fn allocate_phrases<'a, IT>(
397 from_song: &Song,
398 to_song: &Song,
399 instrument_mapping: &InstrumentMapping,
400 from_chains_ids: IT,
401 ) -> Result<PhraseMapping, String>
402 where
403 IT: Iterator<Item = &'a u8>,
404 {
405 let mut allocated_phrases = find_referenced_phrases(to_song);
406
407 let mut seen_phrase: [bool; Song::N_PHRASES] = arr![false; 0xFF];
408 let mut phrase_mapping: [u8; Song::N_PHRASES] = arr![0 as u8; 0xFF];
409
410 let mut to_move = vec![];
411
412 for chain_id in from_chains_ids {
413 let from_chain = &from_song.chains[*chain_id as usize];
414
415 for chain_step in from_chain.steps.iter() {
416 let phrase_ix = chain_step.phrase as usize;
417
418 if phrase_ix >= Song::N_PHRASES || seen_phrase[phrase_ix] {
419 continue;
420 }
421
422 seen_phrase[phrase_ix] = true;
423 let phrase = from_song.phrases[phrase_ix].map_instruments(&instrument_mapping);
424 match to_song.phrases.iter().position(|p| p.steps == phrase.steps) {
425 Some(known) => phrase_mapping[phrase_ix] = known as u8,
426 None => match try_allocate(&allocated_phrases, phrase_ix as u8) {
427 None => {
428 return Err(format!(
429 "No more available phrase slots for phrase {phrase_ix}"
430 ))
431 }
432 Some(slot) => {
433 to_move.push(phrase_ix as u8);
434 allocated_phrases[slot] = true;
435 phrase_mapping[phrase_ix] = slot as u8;
436 }
437 },
438 }
439 }
440 }
441
442 Ok(PhraseMapping {
443 mapping: phrase_mapping,
444 to_move,
445 })
446 }
447
448 fn allocate_eq_and_instruments<'a, IT>(
450 from_song: &Song,
451 to_song: &Song,
452 from_chains_ids: IT,
453 ) -> Result<(EqMapping, InstrumentMapping), String>
454 where
455 IT: Iterator<Item = &'a u8>,
456 {
457 let mut instrument_flags: [bool; Song::N_INSTRUMENTS] = arr![false; 128];
459 let mut eq_flags = vec![false; from_song.eqs.len()];
461 let mut allocated_eqs = find_referenced_eq(to_song);
463 let mut allocated_instruments = find_allocated_instruments(to_song);
464 let mut instrument_mapping = InstrumentMapping::default();
465 let mut eq_mapping = EqMapping::default_ver(to_song.version);
467
468 for chain_id in from_chains_ids {
469 let from_chain = &from_song.chains[*chain_id as usize];
470
471 for chain_step in &from_chain.steps {
472 let phrase_id = chain_step.phrase as usize;
473 if phrase_id >= Song::N_PHRASES {
474 continue;
475 }
476
477 let phrase = &from_song.phrases[phrase_id];
478
479 for step in &phrase.steps {
480 let instr_ix = step.instrument as usize;
481
482 if instr_ix >= Song::N_INSTRUMENTS || instrument_flags[instr_ix] {
484 continue;
485 }
486
487 let mut instr = from_song.instruments[instr_ix].clone();
488
489 if let Some(equ) = instr.equ() {
491 let equ = equ as usize;
492
493 if equ < eq_flags.len() && !eq_flags[equ] {
494 eq_flags[equ as usize] = true;
495 let from_eq = &from_song.eqs[equ];
496 match to_song.eqs.iter().position(|to_eq| to_eq == from_eq) {
498 Some(eq_idx) if (eq_idx as usize) < eq_mapping.mapping.len() => {
499 eq_mapping.mapping[equ] = eq_idx as u8
500 }
501 Some(_) | None => match try_allocate(&allocated_eqs, equ as u8) {
502 None => {
503 return Err(format!(
504 "No more available eqs for instrument {instr_ix}"
505 ))
506 }
507 Some(eq_slot) => {
508 allocated_eqs[eq_slot] = true;
509 eq_mapping.mapping[equ] = eq_slot as u8;
510 eq_mapping.to_move.push(equ as u8);
511 }
512 },
513 }
514
515 instr.set_eq(eq_mapping.mapping[equ]);
517 }
518 }
519
520 instrument_flags[instr_ix] = true;
521 match to_song.instruments.iter().position(|i| i == &instr) {
522 Some(to_instr_ix) => {
524 instrument_mapping.mapping[instr_ix] = to_instr_ix as u8
525 }
526 None => match try_allocate(&allocated_instruments, instr_ix as u8) {
528 None => {
529 return Err(format!(
530 "No more available instrument slots for instrument {instr_ix}"
531 ))
532 }
533 Some(to_instr_ix) => {
534 instrument_mapping.mapping[instr_ix] = to_instr_ix as u8;
535 allocated_instruments[to_instr_ix] = true;
536 instrument_mapping.to_move.push(instr_ix as u8)
537 }
538 },
539 }
540 }
541 }
542 }
543
544 Ok((eq_mapping, instrument_mapping))
545 }
546
547 pub fn create<'a, IT>(from_song: &Song, to_song: &Song, chains: IT) -> Result<Remapper, String>
548 where
549 IT: Iterator<Item = &'a u8>,
550 {
551 let chain_vec: Vec<u8> = chains.map(|v| *v).collect();
552
553 let (eq_mapping, instrument_mapping) =
555 Remapper::allocate_eq_and_instruments(from_song, to_song, chain_vec.iter())?;
556 let phrase_mapping =
557 Remapper::allocate_phrases(from_song, to_song, &instrument_mapping, chain_vec.iter())?;
558
559 let chain_mapping =
560 Remapper::allocate_chains(from_song, to_song, &phrase_mapping, chain_vec.iter())?;
561
562 let table_mapping = Default::default();
563
564 Ok(Self {
565 eq_mapping,
566 instrument_mapping,
567 table_mapping,
568 phrase_mapping,
569 chain_mapping,
570 })
571 }
572
573 pub fn renumber(&self, song: &mut Song) {
575 for equ in self.eq_mapping.to_move.iter() {
577 let equ = *equ as usize;
578 let to_index = self.eq_mapping.mapping[equ];
579 song.eqs[to_index as usize] = song.eqs[equ].clone();
580 song.eqs[equ].clear();
581 }
582
583 for instr_id in self.instrument_mapping.to_move.iter() {
585 let instr_id = *instr_id as usize;
586 let to_index = self.instrument_mapping.mapping[instr_id] as usize;
587 let instr = song.instruments[instr_id].clone();
588
589 song.tables[to_index] = song.tables[instr_id].clone();
590 song.instruments[to_index] = instr;
591 song.instruments[instr_id] = Instrument::None;
592 }
593
594 for table_id in self.table_mapping.to_move.iter() {
596 let table_id = *table_id as usize;
597 let to_index = self.table_mapping.mapping[table_id] as usize;
598 let table = song.tables[table_id].clone();
599
600 song.tables[to_index] = table;
601 song.tables[table_id].clear();
602 }
603
604 let eq_count = song.eq_count();
606 for instr_id in 0..Song::N_INSTRUMENTS {
607 let instr = &mut song.instruments[instr_id];
608
609 if let Some(eq) = instr.equ() {
610 let eq = eq as usize;
611 if eq < eq_count {
612 instr.set_eq(self.eq_mapping.mapping[eq]);
613 }
614 }
615 }
616
617 for phrase_id in self.phrase_mapping.to_move.iter() {
619 let phrase_id = *phrase_id as usize;
620 let to_index = self.phrase_mapping.mapping[phrase_id];
621 song.phrases[to_index as usize] = song.phrases[phrase_id].clone();
622 song.phrases[phrase_id].clear()
623 }
624
625 for phrase_id in 0..Song::N_PHRASES {
627 song.phrases[phrase_id] =
628 song.phrases[phrase_id].map_instruments(&self.instrument_mapping);
629 }
630
631 for chain_id in self.chain_mapping.to_move.iter() {
633 let chain_id = *chain_id as usize;
634 let to_index = self.chain_mapping.mapping[chain_id];
635 song.chains[to_index as usize] = song.chains[chain_id].clone();
636 song.chains[chain_id].clear();
637 }
638
639 for chain_id in 0..Song::N_CHAINS {
641 song.chains[chain_id] = song.chains[chain_id].map(&self.phrase_mapping)
642 }
643 }
644
645 pub fn apply(&self, from: &Song, to: &mut Song) {
647 for equ in self.eq_mapping.to_move.iter() {
648 let equ = *equ as usize;
649 let to_index = self.eq_mapping.mapping[equ];
650 to.eqs[to_index as usize] = from.eqs[equ].clone();
651 }
652
653 for instr_id in self.instrument_mapping.to_move.iter() {
654 let instr_id = *instr_id as usize;
655 let to_index = self.instrument_mapping.mapping[instr_id] as usize;
656 let mut instr = from.instruments[instr_id].clone();
657
658 if let Some(eq) = instr.equ() {
659 let eq = eq as usize;
660 if eq < to.eq_count() {
661 instr.set_eq(self.eq_mapping.mapping[eq]);
662 }
663 }
664
665 to.tables[to_index] = from.tables[instr_id].clone();
666 to.instruments[to_index] = instr;
667 }
668
669 for table_id in self.table_mapping.to_move.iter() {
671 let table_id = *table_id as usize;
672 let to_index = self.table_mapping.mapping[table_id] as usize;
673 to.tables[to_index] = from.tables[table_id].clone();
674 }
675
676 for phrase_id in self.phrase_mapping.to_move.iter() {
677 let phrase_id = *phrase_id as usize;
678 let to_index = self.phrase_mapping.mapping[phrase_id];
679 to.phrases[to_index as usize] =
680 from.phrases[phrase_id].map_instruments(&self.instrument_mapping);
681 }
682
683 for chain_id in self.chain_mapping.to_move.iter() {
684 let chain_id = *chain_id as usize;
685 let to_index = self.chain_mapping.mapping[chain_id];
686 to.chains[to_index as usize] = from.chains[chain_id].map(&self.phrase_mapping);
687 }
688 }
689}