Created
December 3, 2016 15:36
-
-
Save murachue/b52ded0b7968e870b6a310d439ddcb30 to your computer and use it in GitHub Desktop.
CD-ROM image Mode1→Mode2 converter for PlayStation1 homebrew.
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
// mkpsximg.rs | |
// Copyright 2016 Murachue | |
// License: CC-BY | |
use std::fs::File; | |
use std::io; | |
use std::io::{Read, Write}; | |
struct Edc { | |
edc_table: [u32; 256], | |
} | |
impl Edc { | |
fn calc_edc_table() -> [u32; 256] { | |
let mut tab = [0; 256]; | |
for i in 0..256 { | |
let mut x: u32 = i; | |
for _ in 0..8 { | |
let carry = x & 1 != 0; | |
x = x >> 1; | |
if carry { | |
x = x ^ 0xD801_8001; | |
} | |
} | |
tab[i as usize] = x; | |
} | |
return tab; | |
} | |
pub fn new() -> Edc { | |
Edc { edc_table: Self::calc_edc_table() } | |
} | |
pub fn calc_edc(&self, buf: &[u8]) -> u32 { | |
assert!(buf.len() == 0x810 || buf.len() == 0x808 || buf.len() == 0x91C, format!("calc_edc got unexpected length: {}", buf.len())); | |
let mut x: u32 = 0; | |
for i in 0..buf.len() { | |
x = x ^ buf[i] as u32; | |
x= (x >> 8) ^ self.edc_table[(x & 0xFF) as usize]; | |
} | |
return x; | |
} | |
} | |
struct Ecc { | |
gf8_product: [[u16; 256]; 43], | |
} | |
impl Ecc { | |
fn calc_gf8_product() -> [[u16; 256]; 43] { | |
let mut gf8_product = [[0u16; 256]; 43]; | |
let mut gf8_log = [0u8; 256]; | |
let mut gf8_ilog = [0u8; 256]; | |
// initialize gf8_log and gf8_ilog | |
{ | |
let mut x: u8 = 1; | |
for i in 0..0xff { // [0..0xFE] | |
gf8_log[x as usize] = i; | |
gf8_ilog[i as usize] = x; | |
let carry = x & 0x80 != 0; | |
x = x << 1; | |
if carry { | |
x = (x & 0xFF) ^ 0x1D; | |
} | |
} | |
} | |
// calculate gf8_product | |
let subfunc = |a, b| { | |
if a > 0 { | |
let mut aa = gf8_log[a as usize] as i16 - b; | |
if aa < 0 { | |
aa = aa + 255; | |
} | |
assert!(0 <= aa); | |
return gf8_ilog[aa as usize]; | |
} else { | |
assert!(a == 0); | |
return a; | |
} | |
}; | |
for j in 0..43 { // [0..42] | |
let mut xx = gf8_ilog[44 - j]; | |
let mut yy = subfunc(xx ^ 1, 0x19); | |
xx = subfunc(xx, 0x01); | |
xx = subfunc(xx ^ 1, 0x18); | |
xx = gf8_log[xx as usize]; | |
yy = gf8_log[yy as usize]; | |
gf8_product[j][0] = 0; | |
for i in 1..0x100 { // [1..0xFF] | |
let mut x = xx as u16 + gf8_log[i] as u16; | |
if x >= 255 { x = x - 255; } | |
let mut y = yy as u16 + gf8_log[i] as u16; | |
if y >= 255 { y = y - 255; } | |
gf8_product[j][i] = gf8_ilog[x as usize] as u16 + ((gf8_ilog[y as usize] as u16) << 8); | |
} | |
}; | |
return gf8_product; | |
} | |
pub fn new() -> Ecc { | |
Ecc { gf8_product: Self::calc_gf8_product() } | |
} | |
fn calc_parity_part(&self, sector: &mut [u8; 2352], offs: usize, len: usize, j0: usize, step1: usize, step2: usize) { | |
let mut src = 0x00c; | |
let mut dst = 0x81c + offs; | |
let srcmax = dst; | |
for _ in 0..len { | |
let base = src; | |
let mut x = 0x0000; | |
let mut y = 0x0000; | |
for j in j0..43 { | |
x = x ^ self.gf8_product[j][sector[src + 0] as usize]; | |
y = y ^ self.gf8_product[j][sector[src + 1] as usize]; | |
src = src + step1; | |
if step1 == 2 * 44 && src >= srcmax { | |
src = src - 2 * 1118; | |
} | |
} | |
sector[dst + 2 * len + 0] = (x & 0xFF) as u8; | |
sector[dst + 0] = (x >> 8) as u8; | |
sector[dst + 2 * len + 1] = (y & 0xFF) as u8; | |
sector[dst + 1] = (y >> 8) as u8; | |
dst = dst + 2; | |
src = base + step2; | |
} | |
} | |
fn calc_p_parity(&self, sector: &mut [u8; 2352]) { self.calc_parity_part(sector, 0, 43, 19, 2*43, 2); } | |
fn calc_q_parity(&self, sector: &mut [u8; 2352]) { self.calc_parity_part(sector, 43*4, 26, 0, 2*44, 2*43); } | |
pub fn calc_parity(&self, sector: &mut [u8; 2352]) { | |
self.calc_p_parity(sector); | |
self.calc_q_parity(sector); | |
} | |
} | |
struct Mode2Filler { | |
edc: Edc, | |
ecc: Ecc, | |
} | |
impl Mode2Filler { | |
// FIXME dst should &mut[u8;4] | |
fn write_word_le(dst: &mut [u8], value: u32) { | |
dst[0] = ((value >> 0) & 0xFF) as u8; | |
dst[1] = ((value >> 8) & 0xFF) as u8; | |
dst[2] = ((value >> 16) & 0xFF) as u8; | |
dst[3] = ((value >> 24) & 0xFF) as u8; | |
} | |
fn bcdu8(val: u8) -> u8 { | |
return ((val / 10) << 4) | (val % 10); | |
} | |
fn fill_mode2_subheader(buf: &mut[u8; 2352], filenum: u8, channum: u8, submode: u8, codeinfo: u8) { | |
buf[0x010] = filenum; | |
buf[0x011] = channum; | |
buf[0x012] = submode; | |
buf[0x013] = codeinfo; | |
buf[0x014] = buf[0x010]; | |
buf[0x015] = buf[0x011]; | |
buf[0x016] = buf[0x012]; | |
buf[0x017] = buf[0x013]; | |
} | |
fn fill_pos_mode(buf: &mut[u8; 2352], frame: u32, mode: u8) { | |
buf[0x00c] = Self::bcdu8((frame / 75 / 60) as u8); | |
buf[0x00d] = Self::bcdu8((frame / 75 % 60) as u8); | |
buf[0x00e] = Self::bcdu8((frame % 75) as u8); | |
buf[0x00f] = mode; | |
} | |
pub fn fill_mode2form1(&self, buf: &mut[u8; 2352], frame: u32, submode: u8) { | |
// first 16 bytes: 0, FF*10, 0, min, sec, frm, mode | |
for i in 1..11 { buf[i] = 0xFF; } | |
// [12-15] must 0 while calc'ing ECC, and not area of EDC. They can write at last. | |
// Mode2 sub-header (and copy) | |
assert!(submode & 0x20 == 0); | |
Self::fill_mode2_subheader(buf, 0, 0, submode, 0); | |
// [0x018..0x810) should be already read. | |
// Calculate EDC | |
// FIXME could not write in one-line... mut and immut. | |
let edc = self.edc.calc_edc(&buf[0x010..0x818]); | |
Self::write_word_le(&mut buf[0x818..0x81C], edc); | |
// Calculate ECC | |
self.ecc.calc_parity(buf); | |
// Finally fill min/sec/frame/mode | |
Self::fill_pos_mode(buf, frame, 2); | |
} | |
pub fn fill_mode2form2(&self, buf: &mut[u8; 2352], frame: u32, submode: u8) { | |
// first 16 bytes: 0, FF*10, 0, min, sec, frm, mode | |
for i in 1..11 { buf[i] = 0xFF; } | |
Self::fill_pos_mode(buf, frame, 2); | |
// Mode2 sub-header (and copy) | |
assert!(submode & 0x20 != 0); | |
Self::fill_mode2_subheader(buf, 0, 0, submode, 0); | |
// [0x018..0x92C) should be already read. | |
// Calculate EDC | |
// FIXME could not write in one-line... mut and immut. | |
let edc = self.edc.calc_edc(&buf[0x010..0x92C]); | |
Self::write_word_le(&mut buf[0x92C..0x930], edc); | |
} | |
pub fn new() -> Mode2Filler { | |
Mode2Filler { edc: Edc::new(), ecc: Ecc::new() } | |
} | |
} | |
fn do_process(infn: &str, outfn: &str) -> io::Result<()> { | |
let mut inf = try!(File::open(infn)); | |
let mut outf = try!(File::create(outfn)); | |
let filler = Mode2Filler::new(); | |
for i in 0.. { | |
let mut buf = [0u8; 2352]; | |
let rs = try!(inf.read(&mut buf[(16+8)..(16+8+2048)])); | |
if rs == 0 { | |
return Result::Ok(()); | |
} | |
if rs < 2048 { | |
return Result::Err(io::Error::new(io::ErrorKind::UnexpectedEof, format!("Partial read a sector of 2048 bytes ({} bytes)", rs))); | |
} | |
// TODO 0-11=08(Data), 12-15=20(Form2), PVD=09(Data+EOR), EndVD=89(EOF+Data+EOR), PathTab/Dir/File:NotLast=08(Data), Last=89(EOF+Data+EOR) | |
if 12 <= i && i < 16 { | |
filler.fill_mode2form2(&mut buf, 75*2 + i, 0x20); | |
} else { | |
filler.fill_mode2form1(&mut buf, 75*2 + i, 0x08); | |
} | |
try!(outf.write_all(&buf)); | |
} | |
panic!("NOTREACHED (or source image over 4Gi-sector)"); | |
} | |
fn process(infn: &str, outfn: &str) -> i32 { | |
match do_process(infn, outfn) { | |
Ok(_) => { | |
println!("Successfully wrote {}.", outfn); | |
return 0; | |
}, | |
Err(e) => { | |
println!("Error: {}", e); | |
return 1; | |
} | |
} | |
} | |
fn main() { | |
let mut args = std::env::args(); | |
let progname = args.next().unwrap(); | |
if let Some(infn) = args.next() { | |
if let Some(outfn) = args.next() { | |
std::process::exit(process(infn.as_str(), outfn.as_str())); | |
} | |
} | |
writeln!(&mut std::io::stderr(), "Usage: {} <in-file> <out-file>", progname).ok(); | |
std::process::exit(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment