Skip to content

Instantly share code, notes, and snippets.

@murachue
Created December 3, 2016 15:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save murachue/b52ded0b7968e870b6a310d439ddcb30 to your computer and use it in GitHub Desktop.
Save murachue/b52ded0b7968e870b6a310d439ddcb30 to your computer and use it in GitHub Desktop.
CD-ROM image Mode1→Mode2 converter for PlayStation1 homebrew.
// 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