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