1use std::cell::RefCell;
10use std::env;
11use std::fs;
12use std::io::{self, BufWriter, Read, Write};
13use std::path::Path;
14use std::process::ExitCode;
15use std::rc::Rc;
16
17use ymfm_sys::ffi::{self, AccessClass, ChipType};
18use ymfm_sys::{ChipPtr, InterfaceCallbacks, InterfaceHandler};
19
20type EmulatedTime = i64;
22
23struct ActiveChip {
28 chip: ChipPtr,
29 channels: usize,
30 queue: std::collections::VecDeque<(u32, u8)>,
31 pos: EmulatedTime,
32 step: EmulatedTime,
33 native: Vec<i32>,
34 handler_state: Rc<RefCell<VgmHandlerState>>,
35 pcm_offset: Rc<RefCell<u32>>,
36}
37
38impl ActiveChip {
39 fn new(chip_type: ChipType, clock: u32) -> Self {
40 let state = Rc::new(RefCell::new(VgmHandlerState {
41 data: std::array::from_fn(|_| Vec::new()),
42 }));
43 let pcm_offset = Rc::new(RefCell::new(0u32));
44 let chip = ffi::create_chip_with_callbacks(
45 chip_type,
46 clock,
47 Box::new(InterfaceCallbacks::new(vgm_handler_with_state(Rc::clone(
48 &state,
49 )))),
50 );
51 let channels = chip.channels() as usize;
52 let step: EmulatedTime = 0x1_0000_0000i64 / i64::from(chip.sample_rate());
53 Self {
54 chip,
55 channels,
56 queue: std::collections::VecDeque::new(),
57 pos: 0,
58 step,
59 native: vec![0i32; channels],
60 handler_state: state,
61 pcm_offset,
62 }
63 }
64
65 fn chip_type(&self) -> ChipType {
66 self.chip.chip_type()
67 }
68
69 fn write(&mut self, reg: u32, data: u8) {
73 self.queue.push_back((reg, data));
74 }
75
76 fn write_data(&mut self, access: AccessClass, base: u32, data: &[u8]) {
77 let mut state = self.handler_state.borrow_mut();
78 for (index, value) in data.iter().copied().enumerate() {
79 write_byte(&mut state, access, base + index as u32, value);
80 }
81 }
82
83 fn seek_pcm(&mut self, pos: u32) {
84 *self.pcm_offset.borrow_mut() = pos;
85 }
86
87 fn read_pcm(&mut self) -> u8 {
88 let mut offset = self.pcm_offset.borrow_mut();
89 let state = self.handler_state.borrow();
90 let value = read_byte(&state, AccessClass::Pcm, *offset);
91 *offset = offset.saturating_add(1);
92 value
93 }
94
95 fn generate(
99 &mut self,
100 output_start: EmulatedTime,
101 output_step: EmulatedTime,
102 buffer: &mut [i32],
103 ) {
104 let _ = output_step;
105
106 if let Some((reg, data)) = self.queue.pop_front() {
108 let addr1 = 2 * ((reg >> 8) & 3);
109 let data1 = (reg & 0xff) as u8;
110 let addr2 = addr1
111 + if self.chip_type() == ChipType::Ym2149 {
112 2
113 } else {
114 1
115 };
116 self.chip.pin_mut().write(addr1, data1);
117 self.chip.pin_mut().write(addr2, data);
118 }
119
120 while self.pos <= output_start {
122 self.chip.pin_mut().generate(&mut self.native);
123 self.pos += self.step;
124 }
125
126 let channels = self.channels;
127 let out = &self.native;
128 match self.chip.chip_type() {
129 ChipType::Ym2203 => {
130 let sum = out[0] + out[1 % channels] + out[2 % channels] + out[3 % channels];
131 buffer[0] += sum;
132 buffer[1] += sum;
133 }
134 ChipType::Ym2608 | ChipType::Ym2610 => {
135 buffer[0] += out[0] + out[2 % channels];
136 buffer[1] += out[1 % channels] + out[2 % channels];
137 }
138 ChipType::Ymf278B => {
139 buffer[0] += out[4 % channels];
140 buffer[1] += out[5 % channels];
141 }
142 _ if channels == 1 => {
143 buffer[0] += out[0];
144 buffer[1] += out[0];
145 }
146 _ => {
147 buffer[0] += out[0];
148 buffer[1] += out[1 % channels];
149 }
150 }
151 }
152}
153
154fn read_u32(buffer: &[u8], offset: &mut usize) -> u32 {
156 let value = u32::from_le_bytes(buffer[*offset..*offset + 4].try_into().unwrap());
157 *offset += 4;
158 value
159}
160
161fn find_chip(
164 chips: &mut [ActiveChip],
165 category: ChipType,
166 mut index: u8,
167) -> Option<&mut ActiveChip> {
168 for chip in chips.iter_mut() {
169 if chip.chip_type() == category {
170 if index == 0 {
171 return Some(chip);
172 }
173 index -= 1;
174 }
175 }
176 None
177}
178
179fn write_chip(chips: &mut [ActiveChip], category: ChipType, index: u8, reg: u32, data: u8) {
182 if let Some(chip) = find_chip(chips, category, index) {
183 chip.write(reg, data);
184 }
185}
186
187fn add_chips(chips: &mut Vec<ActiveChip>, chip_type: ChipType, clock: u32, name: &str) {
190 let clock_value = clock & 0x3fff_ffff;
191 let num_chips = if clock & 0x4000_0000 != 0 { 2 } else { 1 };
192 println!(
193 "Adding {}{} @ {}Hz",
194 if num_chips == 2 { "2 x " } else { "" },
195 name,
196 clock_value
197 );
198 for _ in 0..num_chips {
199 chips.push(ActiveChip::new(chip_type, clock_value));
200 }
201
202 if chip_type == ChipType::Ym2608 {
203 match fs::read("ym2608_adpcm_rom.bin") {
204 Ok(rom) => {
205 for chip in chips
206 .iter_mut()
207 .filter(|c| c.chip_type() == ChipType::Ym2608)
208 {
209 chip.write_data(AccessClass::AdpcmA, 0, &rom);
210 }
211 }
212 Err(_) => eprintln!("Warning: YM2608 enabled but ym2608_adpcm_rom.bin not found"),
213 }
214 }
215}
216
217fn add_rom_data(
221 chips: &mut [ActiveChip],
222 category: ChipType,
223 access: AccessClass,
224 buffer: &[u8],
225 mut local_offset: usize,
226 size: u32,
227) {
228 let _length = read_u32(buffer, &mut local_offset);
229 let start = read_u32(buffer, &mut local_offset);
230 for index in 0..2u8 {
231 if let Some(chip) = find_chip(chips, category, index) {
232 chip.write_data(
233 access,
234 start,
235 &buffer[local_offset..local_offset + size as usize],
236 );
237 }
238 }
239}
240
241fn parse_header(buffer: &[u8]) -> (u32, Vec<ActiveChip>) {
244 let mut chips = Vec::new();
245 let mut offset = 4usize;
246
247 let _size = read_u32(buffer, &mut offset);
249
250 let version = read_u32(buffer, &mut offset);
252 if version > 0x171 {
253 eprintln!("Warning: version > 1.71 detected, some things may not work");
254 }
255
256 let clock = read_u32(buffer, &mut offset);
258 if clock != 0 {
259 eprintln!("Warning: clock for SN76489 specified ({clock}), but not supported");
260 }
261
262 let clock = read_u32(buffer, &mut offset);
264 if clock != 0 {
265 add_chips(&mut chips, ChipType::Ym2413, clock, "YM2413");
266 }
267
268 let _gd3_offset = read_u32(buffer, &mut offset);
271 let _total_samples = read_u32(buffer, &mut offset);
272 let _loop_offset = read_u32(buffer, &mut offset);
273 let _loop_samples = read_u32(buffer, &mut offset);
274 let _rate = read_u32(buffer, &mut offset);
275 let _sn76489_extra = read_u32(buffer, &mut offset);
276
277 let clock = read_u32(buffer, &mut offset);
279 if version >= 0x110 && clock != 0 {
280 add_chips(&mut chips, ChipType::Ym2612, clock, "YM2612");
281 }
282
283 let clock = read_u32(buffer, &mut offset);
285 if version >= 0x110 && clock != 0 {
286 add_chips(&mut chips, ChipType::Ym2151, clock, "YM2151");
287 }
288
289 let data_offset = read_u32(buffer, &mut offset);
291 let data_start = if version < 0x150 {
292 0x40
293 } else {
294 data_offset.wrapping_add(offset as u32 - 4)
295 };
296
297 macro_rules! next_field {
300 () => {{
301 if offset + 4 > data_start as usize {
302 return (data_start, chips);
303 }
304 read_u32(buffer, &mut offset)
305 }};
306 }
307
308 let clock = read_u32(buffer, &mut offset);
310 if version >= 0x151 && clock != 0 {
311 eprintln!("Warning: clock for Sega PCM specified, but not supported");
312 }
313
314 let _sega_pcm_if = read_u32(buffer, &mut offset);
316
317 let clock = next_field!();
319 if version >= 0x151 && clock != 0 {
320 eprintln!("Warning: clock for RF5C68 specified, but not supported");
321 }
322
323 let clock = next_field!();
325 if version >= 0x151 && clock != 0 {
326 add_chips(&mut chips, ChipType::Ym2203, clock, "YM2203");
327 }
328
329 let clock = next_field!();
331 if version >= 0x151 && clock != 0 {
332 add_chips(&mut chips, ChipType::Ym2608, clock, "YM2608");
333 }
334
335 let clock = next_field!();
337 if version >= 0x151 && clock != 0 {
338 if clock & 0x8000_0000 != 0 {
339 add_chips(&mut chips, ChipType::Ym2610B, clock, "YM2610B");
340 } else {
341 add_chips(&mut chips, ChipType::Ym2610, clock, "YM2610");
342 }
343 }
344
345 let clock = next_field!();
347 if version >= 0x151 && clock != 0 {
348 add_chips(&mut chips, ChipType::Ym3812, clock, "YM3812");
349 }
350
351 let clock = next_field!();
353 if version >= 0x151 && clock != 0 {
354 add_chips(&mut chips, ChipType::Ym3526, clock, "YM3526");
355 }
356
357 let clock = next_field!();
359 if version >= 0x151 && clock != 0 {
360 add_chips(&mut chips, ChipType::Y8950, clock, "Y8950");
361 }
362
363 let clock = next_field!();
365 if version >= 0x151 && clock != 0 {
366 add_chips(&mut chips, ChipType::Ymf262, clock, "YMF262");
367 }
368
369 let clock = next_field!();
371 if version >= 0x151 && clock != 0 {
372 add_chips(&mut chips, ChipType::Ymf278B, clock, "YMF278B");
373 }
374
375 let clock = next_field!();
377 if version >= 0x151 && clock != 0 {
378 eprintln!("Warning: clock for YMF271 specified, but not supported");
379 }
380
381 let clock = next_field!();
383 if version >= 0x151 && clock != 0 {
384 eprintln!("Warning: clock for YMF280B specified, but not supported");
385 }
386
387 let clock = next_field!();
389 if version >= 0x151 && clock != 0 {
390 eprintln!("Warning: clock for RF5C164 specified, but not supported");
391 }
392
393 let clock = next_field!();
395 if version >= 0x151 && clock != 0 {
396 eprintln!("Warning: clock for PWM specified, but not supported");
397 }
398
399 let clock = next_field!();
401 if version >= 0x151 && clock != 0 {
402 eprintln!("Warning: clock for AY8910 specified, substituting YM2149");
403 add_chips(&mut chips, ChipType::Ym2149, clock, "YM2149");
404 }
405
406 let _ay8910_flags = next_field!();
408
409 let volume_info = next_field!();
411 if volume_info & 0xff != 0 {
412 let modifier = 2f64.powf(f64::from(volume_info & 0xff) / 0x20 as f64);
413 println!(
414 "Volume modifier: {:02X} (={})",
415 volume_info & 0xff,
416 modifier as i32
417 );
418 }
419
420 let clock = next_field!();
422 if version >= 0x161 && clock != 0 {
423 eprintln!("Warning: clock for GameBoy DMG specified, but not supported");
424 }
425
426 let clock = next_field!();
428 if version >= 0x161 && clock != 0 {
429 eprintln!("Warning: clock for NES APU specified, but not supported");
430 }
431
432 let clock = next_field!();
434 if version >= 0x161 && clock != 0 {
435 eprintln!("Warning: clock for MultiPCM specified, but not supported");
436 }
437
438 let clock = next_field!();
440 if version >= 0x161 && clock != 0 {
441 eprintln!("Warning: clock for uPD7759 specified, but not supported");
442 }
443
444 let clock = next_field!();
446 if version >= 0x161 && clock != 0 {
447 eprintln!("Warning: clock for OKIM6258 specified, but not supported");
448 }
449
450 let _flags = next_field!();
452
453 let clock = next_field!();
455 if version >= 0x161 && clock != 0 {
456 eprintln!("Warning: clock for OKIM6295 specified, but not supported");
457 }
458
459 let clock = next_field!();
461 if version >= 0x161 && clock != 0 {
462 eprintln!("Warning: clock for K051649 specified, but not supported");
463 }
464
465 let clock = next_field!();
467 if version >= 0x161 && clock != 0 {
468 eprintln!("Warning: clock for K054539 specified, but not supported");
469 }
470
471 let clock = next_field!();
473 if version >= 0x161 && clock != 0 {
474 eprintln!("Warning: clock for HuC6280 specified, but not supported");
475 }
476
477 let clock = next_field!();
479 if version >= 0x161 && clock != 0 {
480 eprintln!("Warning: clock for C140 specified, but not supported");
481 }
482
483 let clock = next_field!();
485 if version >= 0x161 && clock != 0 {
486 eprintln!("Warning: clock for K053260 specified, but not supported");
487 }
488
489 let clock = next_field!();
491 if version >= 0x161 && clock != 0 {
492 eprintln!("Warning: clock for Pokey specified, but not supported");
493 }
494
495 let clock = next_field!();
497 if version >= 0x161 && clock != 0 {
498 eprintln!("Warning: clock for QSound specified, but not supported");
499 }
500
501 let clock = next_field!();
503 if version >= 0x171 && clock != 0 {
504 eprintln!("Warning: clock for SCSP specified, but not supported");
505 }
506
507 let _extra_header = next_field!();
509
510 let clock = next_field!();
512 if version >= 0x171 && clock != 0 {
513 eprintln!("Warning: clock for WonderSwan specified, but not supported");
514 }
515
516 let clock = next_field!();
518 if version >= 0x171 && clock != 0 {
519 eprintln!("Warning: clock for VSU specified, but not supported");
520 }
521
522 let clock = next_field!();
524 if version >= 0x171 && clock != 0 {
525 eprintln!("Warning: clock for SAA1099 specified, but not supported");
526 }
527
528 let clock = next_field!();
530 if version >= 0x171 && clock != 0 {
531 eprintln!("Warning: clock for ES5503 specified, but not supported");
532 }
533
534 let clock = next_field!();
536 if version >= 0x171 && clock != 0 {
537 eprintln!("Warning: clock for ES5505/ES5506 specified, but not supported");
538 }
539
540 let _es_channels = next_field!();
542
543 let clock = next_field!();
545 if version >= 0x171 && clock != 0 {
546 eprintln!("Warning: clock for X1-010 specified, but not supported");
547 }
548
549 let clock = next_field!();
551 if version >= 0x171 && clock != 0 {
552 eprintln!("Warning: clock for C352 specified, but not supported");
553 }
554
555 let clock = next_field!();
557 if version >= 0x171 && clock != 0 {
558 eprintln!("Warning: clock for GA20 specified, but not supported");
559 }
560
561 (data_start, chips)
562}
563
564fn generate_all(
568 buffer: &[u8],
569 data_start: u32,
570 output_rate: u32,
571 chips: &mut [ActiveChip],
572) -> Vec<i32> {
573 let mut wav_buffer = Vec::new();
574 let mut offset = data_start as usize;
575 let mut done = false;
576 let output_step: EmulatedTime = 0x1_0000_0000i64 / i64::from(output_rate);
577 let mut output_pos: EmulatedTime = 0;
578
579 while !done && offset < buffer.len() {
580 let mut delay: i32 = 0;
581 let cmd = buffer[offset];
582 offset += 1;
583
584 match cmd {
585 0x51 | 0xa1 => {
587 write_chip(
588 chips,
589 ChipType::Ym2413,
590 cmd >> 7,
591 u32::from(buffer[offset]),
592 buffer[offset + 1],
593 );
594 offset += 2;
595 }
596 0x52 | 0xa2 => {
597 write_chip(
598 chips,
599 ChipType::Ym2612,
600 cmd >> 7,
601 u32::from(buffer[offset]),
602 buffer[offset + 1],
603 );
604 offset += 2;
605 }
606 0x53 | 0xa3 => {
607 write_chip(
608 chips,
609 ChipType::Ym2612,
610 cmd >> 7,
611 u32::from(buffer[offset]) | 0x100,
612 buffer[offset + 1],
613 );
614 offset += 2;
615 }
616 0x54 | 0xa4 => {
617 write_chip(
618 chips,
619 ChipType::Ym2151,
620 cmd >> 7,
621 u32::from(buffer[offset]),
622 buffer[offset + 1],
623 );
624 offset += 2;
625 }
626 0x55 | 0xa5 => {
627 write_chip(
628 chips,
629 ChipType::Ym2203,
630 cmd >> 7,
631 u32::from(buffer[offset]),
632 buffer[offset + 1],
633 );
634 offset += 2;
635 }
636 0x56 | 0xa6 => {
637 write_chip(
638 chips,
639 ChipType::Ym2608,
640 cmd >> 7,
641 u32::from(buffer[offset]),
642 buffer[offset + 1],
643 );
644 offset += 2;
645 }
646 0x57 | 0xa7 => {
647 write_chip(
648 chips,
649 ChipType::Ym2608,
650 cmd >> 7,
651 u32::from(buffer[offset]) | 0x100,
652 buffer[offset + 1],
653 );
654 offset += 2;
655 }
656 0x58 | 0xa8 => {
657 write_chip(
658 chips,
659 ChipType::Ym2610,
660 cmd >> 7,
661 u32::from(buffer[offset]),
662 buffer[offset + 1],
663 );
664 offset += 2;
665 }
666 0x59 | 0xa9 => {
667 write_chip(
668 chips,
669 ChipType::Ym2610,
670 cmd >> 7,
671 u32::from(buffer[offset]) | 0x100,
672 buffer[offset + 1],
673 );
674 offset += 2;
675 }
676 0x5a | 0xaa => {
677 write_chip(
678 chips,
679 ChipType::Ym3812,
680 cmd >> 7,
681 u32::from(buffer[offset]),
682 buffer[offset + 1],
683 );
684 offset += 2;
685 }
686 0x5b | 0xab => {
687 write_chip(
688 chips,
689 ChipType::Ym3526,
690 cmd >> 7,
691 u32::from(buffer[offset]),
692 buffer[offset + 1],
693 );
694 offset += 2;
695 }
696 0x5c | 0xac => {
697 write_chip(
698 chips,
699 ChipType::Y8950,
700 cmd >> 7,
701 u32::from(buffer[offset]),
702 buffer[offset + 1],
703 );
704 offset += 2;
705 }
706 0x5e | 0xae => {
707 write_chip(
708 chips,
709 ChipType::Ymf262,
710 cmd >> 7,
711 u32::from(buffer[offset]),
712 buffer[offset + 1],
713 );
714 offset += 2;
715 }
716 0x5f | 0xaf => {
717 write_chip(
718 chips,
719 ChipType::Ymf262,
720 cmd >> 7,
721 u32::from(buffer[offset]) | 0x100,
722 buffer[offset + 1],
723 );
724 offset += 2;
725 }
726
727 0x61 => {
729 delay = i32::from(buffer[offset]) | (i32::from(buffer[offset + 1]) << 8);
730 offset += 2;
731 }
732 0x62 => delay = 735,
734 0x63 => delay = 882,
736 0x66 => done = true,
738
739 0x67 => {
741 let marker = buffer[offset];
742 offset += 1;
743 if marker == 0x66 {
744 let dtype = buffer[offset];
745 offset += 1;
746 let size = read_u32(buffer, &mut offset);
747 let local_offset = offset;
748
749 match dtype {
750 0x01..=0x07 => {}
752
753 0x00 => {
755 if let Some(chip) = find_chip(chips, ChipType::Ym2612, 0) {
756 let len = (size as usize).saturating_sub(8);
757 chip.write_data(
758 AccessClass::Pcm,
759 0,
760 &buffer[local_offset..local_offset + len],
761 );
762 }
763 }
764
765 0x82 => add_rom_data(
767 chips,
768 ChipType::Ym2610,
769 AccessClass::AdpcmA,
770 buffer,
771 local_offset,
772 size - 8,
773 ),
774 0x81 => add_rom_data(
776 chips,
777 ChipType::Ym2608,
778 AccessClass::AdpcmB,
779 buffer,
780 local_offset,
781 size - 8,
782 ),
783 0x83 => add_rom_data(
785 chips,
786 ChipType::Ym2610,
787 AccessClass::AdpcmB,
788 buffer,
789 local_offset,
790 size - 8,
791 ),
792 0x84 | 0x87 => add_rom_data(
794 chips,
795 ChipType::Ymf278B,
796 AccessClass::Pcm,
797 buffer,
798 local_offset,
799 size - 8,
800 ),
801 0x88 => add_rom_data(
803 chips,
804 ChipType::Y8950,
805 AccessClass::AdpcmB,
806 buffer,
807 local_offset,
808 size - 8,
809 ),
810
811 0x80 | 0x85 | 0x86 | 0x89..=0x93 => {}
813 0xc0..=0xc2 | 0xe0 | 0xe1 => {}
815
816 other => {
817 if (0x40..0x7f).contains(&other) {
818 println!("Compressed data block not supported");
819 } else {
820 println!("Unknown data block type {other:#04X}");
821 }
822 }
823 }
824 offset += size as usize;
825 }
826 }
827
828 0x68 => println!("68: PCM RAM write"),
830
831 0xa0 => {
833 write_chip(
834 chips,
835 ChipType::Ym2149,
836 buffer[offset] >> 7,
837 u32::from(buffer[offset] & 0x7f),
838 buffer[offset + 1],
839 );
840 offset += 2;
841 }
842
843 0xd0 => {
845 let reg = (u32::from(buffer[offset] & 0x7f) << 8) | u32::from(buffer[offset + 1]);
846 write_chip(
847 chips,
848 ChipType::Ymf278B,
849 buffer[offset] >> 7,
850 reg,
851 buffer[offset + 2],
852 );
853 offset += 3;
854 }
855
856 0x70..=0x7f => delay = i32::from(cmd & 15) + 1,
857
858 0x80..=0x8f => {
859 if let Some(chip) = find_chip(chips, ChipType::Ym2612, 0) {
860 let sample = chip.read_pcm();
861 chip.write(0x2a, sample);
862 }
863 delay = i32::from(cmd & 15);
864 }
865
866 0x30..=0x3f | 0x4f | 0x50 => offset += 1,
868
869 0x40..=0x4e | 0x5d | 0xb0..=0xbf => offset += 2,
871
872 0xc0..=0xc8 | 0xc9..=0xcf | 0xd1..=0xd6 | 0xd7..=0xdf => offset += 3,
874
875 0xe0 => {
877 let pos = read_u32(buffer, &mut offset);
878 if let Some(chip) = find_chip(chips, ChipType::Ym2612, 0) {
879 chip.seek_pcm(pos);
880 }
881 }
882 0xe1..=0xff => offset += 4,
884
885 _ => {}
887 }
888
889 for _ in 0..delay {
890 let mut outputs = [0i32; 2];
891 for chip in chips.iter_mut() {
892 chip.generate(output_pos, output_step, &mut outputs);
893 }
894 output_pos += output_step;
895 wav_buffer.push(outputs[0]);
896 wav_buffer.push(outputs[1]);
897 }
898 }
899
900 wav_buffer
901}
902
903struct VgmHandlerState {
906 data: [Vec<u8>; 4],
908}
909
910fn data_index(access: AccessClass) -> usize {
912 match access {
913 AccessClass::Io => 0,
914 AccessClass::AdpcmA => 1,
915 AccessClass::AdpcmB => 2,
916 AccessClass::Pcm => 3,
917 _ => 0,
918 }
919}
920
921fn write_byte(state: &mut VgmHandlerState, access: AccessClass, offset: u32, value: u8) {
924 let buffer = &mut state.data[data_index(access)];
925 let index = offset as usize;
926 if buffer.len() <= index {
927 buffer.resize(index + 1, 0);
928 }
929 buffer[index] = value;
930}
931
932fn read_byte(state: &VgmHandlerState, access: AccessClass, offset: u32) -> u8 {
933 state.data[data_index(access)]
934 .get(offset as usize)
935 .copied()
936 .unwrap_or(0)
937}
938
939fn vgm_handler_with_state(state: Rc<RefCell<VgmHandlerState>>) -> InterfaceHandler {
942 InterfaceHandler {
943 write_data: Some(Box::new({
944 let state = Rc::clone(&state);
945 move |access, base, data| {
946 let mut state = state.borrow_mut();
947 for (index, value) in data.iter().copied().enumerate() {
948 write_byte(&mut state, access, base + index as u32, value);
949 }
950 }
951 })),
952 read_data: Some(Box::new({
953 let state = Rc::clone(&state);
954 move |access, base, length| {
955 let state = state.borrow();
956 (0..length)
957 .map(|index| read_byte(&state, access, base + index))
958 .collect()
959 }
960 })),
961 ymfm_external_read: Some(Box::new({
962 let state = Rc::clone(&state);
963 move |access, offset| read_byte(&state.borrow(), access, offset)
964 })),
965 ..Default::default()
966 }
967}
968
969fn write_wav(path: &Path, output_rate: u32, wav_buffer: &[i32]) -> io::Result<()> {
973 let max_scale = wav_buffer
974 .iter()
975 .map(|v| v.unsigned_abs())
976 .max()
977 .unwrap_or(0);
978 let max_scale = if max_scale == 0 {
979 eprintln!("The WAV file data will only contain silence.");
980 1
981 } else {
982 max_scale
983 };
984
985 let samples: Vec<i16> = wav_buffer
986 .iter()
987 .map(|&v| (i64::from(v) * 26000 / i64::from(max_scale)) as i16)
988 .collect();
989
990 let mut out = BufWriter::new(fs::File::create(path)?);
991 let data_len = (samples.len() * 2) as u32;
992 let total_size = 40u32 + data_len;
993 let byte_rate = output_rate * 2 * 2;
994
995 out.write_all(b"RIFF")?;
996 out.write_all(&total_size.to_le_bytes())?;
997 out.write_all(b"WAVE")?;
998 out.write_all(b"fmt ")?;
999 out.write_all(&16u32.to_le_bytes())?; out.write_all(&1u16.to_le_bytes())?; out.write_all(&2u16.to_le_bytes())?; out.write_all(&output_rate.to_le_bytes())?;
1003 out.write_all(&byte_rate.to_le_bytes())?;
1004 out.write_all(&4u16.to_le_bytes())?; out.write_all(&16u16.to_le_bytes())?; out.write_all(b"data")?;
1007 out.write_all(&data_len.to_le_bytes())?;
1008 for sample in &samples {
1009 out.write_all(&sample.to_le_bytes())?;
1010 }
1011 out.flush()
1012}
1013
1014fn print_usage() {
1016 eprintln!("Usage: vgmrender <inputfile|-> -o <outputfile> [-r <rate>]");
1017 eprintln!(" Use '-' as <inputfile> to read VGM data from stdin.");
1018}
1019
1020fn main() -> ExitCode {
1021 let args: Vec<String> = env::args().collect();
1022
1023 let mut input_file = None;
1024 let mut output_file = None;
1025 let mut output_rate: u32 = 44100;
1026 let mut arg_error = false;
1027
1028 let mut i = 1;
1029 while i < args.len() {
1030 let arg = args[i].as_str();
1031 match arg {
1032 "-o" | "--output" => {
1033 i += 1;
1034 output_file = args.get(i).cloned();
1035 }
1036 "-r" | "--samplerate" => {
1037 i += 1;
1038 output_rate = args.get(i).and_then(|s| s.parse().ok()).unwrap_or(44100);
1039 }
1040 "-" => input_file = Some(arg.to_string()),
1041 _ if arg.starts_with('-') => {
1042 eprintln!("Unknown argument: {arg}");
1043 arg_error = true;
1044 }
1045 _ => input_file = Some(arg.to_string()),
1046 }
1047 i += 1;
1048 }
1049
1050 let (Some(input_file), Some(output_file)) = (input_file, output_file) else {
1051 print_usage();
1052 return ExitCode::from(1);
1053 };
1054 if arg_error {
1055 print_usage();
1056 return ExitCode::from(1);
1057 }
1058
1059 let buffer = if input_file == "-" {
1060 let mut stdin = io::stdin();
1061 let mut buffer = Vec::new();
1062 match stdin.read_to_end(&mut buffer) {
1063 Ok(_) => buffer,
1064 Err(err) => {
1065 eprintln!("Error reading VGM data from stdin: {err}");
1066 return ExitCode::from(2);
1067 }
1068 }
1069 } else {
1070 match fs::read(&input_file) {
1071 Ok(buffer) => buffer,
1072 Err(err) => {
1073 eprintln!("Error opening file '{input_file}': {err}");
1074 return ExitCode::from(2);
1075 }
1076 }
1077 };
1078
1079 if buffer.len() < 64 || &buffer[0..4] != b"Vgm " {
1080 eprintln!("File '{input_file}' does not appear to be a valid VGM file");
1081 return ExitCode::from(4);
1082 }
1083
1084 let (data_start, mut chips) = parse_header(&buffer);
1085
1086 if chips.is_empty() {
1087 eprintln!("No compatible chips found, exiting.");
1088 return ExitCode::from(5);
1089 }
1090
1091 let wav_buffer = generate_all(&buffer, data_start, output_rate, &mut chips);
1092
1093 if let Err(err) = write_wav(Path::new(&output_file), output_rate, &wav_buffer) {
1094 eprintln!("Error writing output file '{output_file}': {err}");
1095 return ExitCode::from(6);
1096 }
1097
1098 ExitCode::SUCCESS
1099}