Last active
February 25, 2017 19:59
-
-
Save yupferris/99238041bde343d20ce2aae6afe3a1ef to your computer and use it in GitHub Desktop.
Sample extraction for Galactic Pinball
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{self, Write, Read, BufWriter}; | |
use std::fs::File; | |
use std::path::Path; | |
const NUM_CHANNELS: usize = 1; | |
const BITS_PER_SAMPLE: usize = 16; | |
fn main() { | |
let rom_file_name = "C:\\msys64\\home\\ferris\\dev\\projects\\vb-rs-corpus\\Galactic Pinball (Japan, USA).vb"; | |
let buf = load(rom_file_name).unwrap(); | |
let src = 0xfff1a15a; // Welcome to space world! | |
//let src = 0xfff24d30; // Cosmic | |
let output = decompress(src, &buf); | |
let output_file_name = "space-world-20khz.wav"; | |
//let output_file_name = "cosmic-20khz.wav"; | |
let file = File::create(output_file_name).unwrap(); | |
let mut writer = BufWriter::new(file); | |
// Sample rate is not exactly 20000; it's very close but there's still some phasing. | |
let sample_rate = 20000; | |
let num_frames = output.len(); | |
let data_chunk_size = num_frames * NUM_CHANNELS * BITS_PER_SAMPLE / 8; | |
// RIFF header | |
write_str(&mut writer, "RIFF").unwrap(); | |
write_u32(&mut writer, (data_chunk_size + 36) as _).unwrap(); | |
write_str(&mut writer, "WAVE").unwrap(); | |
// Format sub-chunk | |
write_str(&mut writer, "fmt ").unwrap(); | |
write_u32(&mut writer, 16).unwrap(); | |
write_u16(&mut writer, 1).unwrap(); // WAVE_FORMAT_PCM | |
write_u16(&mut writer, NUM_CHANNELS as _).unwrap(); | |
write_u32(&mut writer, sample_rate as _).unwrap(); | |
write_u32(&mut writer, (sample_rate * NUM_CHANNELS * BITS_PER_SAMPLE / 8) as _).unwrap(); | |
write_u16(&mut writer, (NUM_CHANNELS * BITS_PER_SAMPLE / 8) as _).unwrap(); | |
write_u16(&mut writer, BITS_PER_SAMPLE as _).unwrap(); | |
// Data sub-chunk | |
write_str(&mut writer, "data").unwrap(); | |
write_u32(&mut writer, data_chunk_size as _).unwrap(); | |
for byte in output { | |
let sample = (byte as u16) << 7; | |
write_u16(&mut writer, sample as _).unwrap(); | |
} | |
println!("Hello, world!"); | |
} | |
pub fn load<P: AsRef<Path>>(file_name: P) -> io::Result<Box<[u8]>> { | |
let mut file = File::open(file_name)?; | |
let mut vec = Vec::new(); | |
file.read_to_end(&mut vec)?; | |
Ok(vec.into_boxed_slice()) | |
} | |
fn read_byte(addr: u32, buf: &[u8]) -> u8 { | |
//println!("Read 0x{:08x}", addr); | |
let offset = (((addr & 0x07ffffff) - 0x07000000) as usize) & (buf.len() - 1); | |
buf[offset] | |
} | |
fn decompress(mut stream_ptr: u32, buf: &[u8]) -> Vec<u8> { | |
let mut output = Vec::new(); | |
let mut nybble = 0; | |
loop { | |
let mut data_byte = read_byte(stream_ptr, buf); | |
if data_byte == 0x0f { | |
let next_data_byte = read_byte(stream_ptr + 1, buf); | |
if next_data_byte == 0x0f { | |
return output; | |
} | |
println!("Set voice volume: 0x{:02x}", next_data_byte); | |
stream_ptr += 2; | |
// Technically this should read data_byte again and then jump to the compare | |
// to 0x0f, but this should be equivalent. | |
continue; | |
} | |
if nybble == 0 { | |
nybble = 1; | |
} else { | |
data_byte <<= 4; | |
stream_ptr += 1; | |
nybble = 0; | |
} | |
//println!("Set envelope data: 0x{:02x}", data_byte); | |
output.push(data_byte); | |
} | |
} | |
fn write_str<B: Write>(writer: &mut BufWriter<B>, value: &str) -> io::Result<()> { | |
writer.write_all(value.as_bytes())?; | |
Ok(()) | |
} | |
fn write_u16<B: Write>(writer: &mut BufWriter<B>, value: u16) -> io::Result<()> { | |
let buf = [value as u8, (value >> 8) as u8]; | |
writer.write_all(&buf)?; | |
Ok(()) | |
} | |
fn write_u32<B: Write>(writer: &mut BufWriter<B>, value: u32) -> io::Result<()> { | |
let buf = [value as u8, (value >> 8) as u8, (value >> 16) as u8, (value >> 24) as u8]; | |
writer.write_all(&buf)?; | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment