Add initial test code
commit
f5984c9739
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
|
perf.data*
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "maraiah"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["marrub"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
memmap = "0.6"
|
||||||
|
#gtk = "0.4"
|
|
@ -0,0 +1,24 @@
|
||||||
|
//! Binary data conversion utilities.
|
||||||
|
|
||||||
|
pub type Ident = [u8; 4];
|
||||||
|
|
||||||
|
pub fn b_iden(b: &[u8]) -> Ident {[b[0], b[1], b[2], b[3]]}
|
||||||
|
|
||||||
|
pub fn b_u32l(b: &[u8]) -> u32 {b[3] as (u32) << 24 | b[2] as (u32) << 16 |
|
||||||
|
b[1] as (u32) << 8 | b[0] as u32}
|
||||||
|
pub fn b_u16l(b: &[u8]) -> u16 {b[1] as (u16) << 8 | b[0] as u16}
|
||||||
|
pub fn b_u32b(b: &[u8]) -> u32 {b[0] as (u32) << 24 | b[1] as (u32) << 16 |
|
||||||
|
b[2] as (u32) << 8 | b[3] as u32}
|
||||||
|
pub fn b_u16b(b: &[u8]) -> u16 {b[0] as (u16) << 8 | b[1] as u16}
|
||||||
|
pub fn b_i32l(b: &[u8]) -> i32 {b_u32l(b) as i32}
|
||||||
|
pub fn b_i16l(b: &[u8]) -> i16 {b_u16l(b) as i16}
|
||||||
|
pub fn b_i32b(b: &[u8]) -> i32 {b_u32b(b) as i32}
|
||||||
|
pub fn b_i16b(b: &[u8]) -> i16 {b_u16b(b) as i16}
|
||||||
|
|
||||||
|
pub fn d_u32b(n: u32) -> [u8; 4] {[(n >> 24) as u8, (n >> 16) as u8,
|
||||||
|
(n >> 8) as u8, (n >> 0) as u8]}
|
||||||
|
pub fn d_u16b(n: u16) -> [u8; 2] {[(n >> 8) as u8, (n >> 0) as u8]}
|
||||||
|
pub fn d_i32b(n: i32) -> [u8; 4] {d_u32b(n as u32)}
|
||||||
|
pub fn d_i16b(n: i16) -> [u8; 2] {d_u16b(n as u16)}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,59 @@
|
||||||
|
//! Image and color representations.
|
||||||
|
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
/// RGBA8 color.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Color
|
||||||
|
{
|
||||||
|
pub r: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub b: u8,
|
||||||
|
pub a: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Image with width and height.
|
||||||
|
pub struct Image
|
||||||
|
{
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
cr: Vec<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color
|
||||||
|
{
|
||||||
|
/// Converts a R5G5B5 format color to RGBA8.
|
||||||
|
pub fn from_r5g5b5(rgb: u16) -> Color
|
||||||
|
{
|
||||||
|
Color{r: (rgb >> 10 ) as u8 * 8,
|
||||||
|
g: (rgb >> 5 & 31) as u8 * 8,
|
||||||
|
b: (rgb & 31) as u8 * 8,
|
||||||
|
a: 255}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image
|
||||||
|
{
|
||||||
|
/// Creates a new Image structure.
|
||||||
|
pub fn new(w: usize, h: usize) -> Image
|
||||||
|
{Image{w, h, cr: Vec::with_capacity(w as usize * h as usize)}}
|
||||||
|
|
||||||
|
pub fn w(&self) -> usize {self.w}
|
||||||
|
pub fn h(&self) -> usize {self.h}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<(usize, usize)> for Image
|
||||||
|
{
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn index(&self, (x, y): (usize, usize)) -> &Color
|
||||||
|
{&self.cr[x + y * self.w]}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<(usize, usize)> for Image
|
||||||
|
{
|
||||||
|
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color
|
||||||
|
{&mut self.cr[x + y * self.w]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,53 @@
|
||||||
|
//! Macintosh archived format header utilities.
|
||||||
|
|
||||||
|
use durandal::bin::*;
|
||||||
|
|
||||||
|
/// Checks for an AppleSingle header. Returns offset to the resource fork.
|
||||||
|
pub fn check_apple_single(b: &[u8]) -> usize
|
||||||
|
{
|
||||||
|
if b_u32b(&b[0..4]) != 0x51600 || b_u32b(&b[4..8]) != 0x20000
|
||||||
|
{return 0}
|
||||||
|
|
||||||
|
let num = b_u16b(&b[24..26]) as usize;
|
||||||
|
|
||||||
|
for i in 0..num
|
||||||
|
{
|
||||||
|
let p = 26 + (12 * i);
|
||||||
|
let fid = b_u32b(&b[p+0..p+ 4]);
|
||||||
|
let ofs = b_u32b(&b[p+4..p+ 8]) as usize;
|
||||||
|
let len = b_u32b(&b[p+8..p+12]) as usize;
|
||||||
|
|
||||||
|
if fid == 1 {return if ofs + len > b.len() {0} else {ofs}}
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for a MacBin header. Returns offset to the resource fork.
|
||||||
|
pub fn check_mac_bin(b: &[u8]) -> usize
|
||||||
|
{
|
||||||
|
if b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 0x81 {return 0}
|
||||||
|
|
||||||
|
let mut crc = 0;
|
||||||
|
|
||||||
|
for i in 0..124 {
|
||||||
|
for j in 8..16
|
||||||
|
{
|
||||||
|
let d = b[i] as (u16) << j;
|
||||||
|
if (d ^ crc) & 0x8000 != 0 {crc = crc << 1 ^ 0x1021}
|
||||||
|
else {crc <<= 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if crc == b_u16b(&b[124..126]) {128} else {0}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a MacBin or AppleSingle header if there is one and returns the
|
||||||
|
/// offset from the start of the header to the resource fork (if one is found.)
|
||||||
|
pub fn try_mac_header(b: &[u8]) -> usize
|
||||||
|
{
|
||||||
|
let ofs = check_mac_bin(b);
|
||||||
|
if ofs != 0 {ofs} else {check_apple_single(b)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,10 @@
|
||||||
|
//! Library for file reading utilities and mac format utilities.
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub mod bin;
|
||||||
|
pub mod machead;
|
||||||
|
pub mod text;
|
||||||
|
pub mod image;
|
||||||
|
pub mod pict;
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,336 @@
|
||||||
|
//! QuickDraw PICT format loader.
|
||||||
|
|
||||||
|
use durandal::image::*;
|
||||||
|
use durandal::bin::*;
|
||||||
|
|
||||||
|
const PACK_DEFAULT: u16 = 0;
|
||||||
|
const PACK_NONE : u16 = 1;
|
||||||
|
const PACK_NOALPHA: u16 = 2;
|
||||||
|
const PACK_RLE16 : u16 = 3;
|
||||||
|
const PACK_RLE32 : u16 = 4;
|
||||||
|
|
||||||
|
struct PixMap
|
||||||
|
{
|
||||||
|
pack: u16,
|
||||||
|
dept: u16,
|
||||||
|
rle : bool,
|
||||||
|
cmap: Vec<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PixMap
|
||||||
|
{
|
||||||
|
fn empty() -> PixMap
|
||||||
|
{PixMap{pack: PACK_DEFAULT, dept: 1, rle: false, cmap: Vec::new()}}
|
||||||
|
|
||||||
|
fn new(b: &[u8], packed: bool) -> (usize, PixMap)
|
||||||
|
{
|
||||||
|
// version = b_u16b(&b[ 0.. 2]);
|
||||||
|
let pack = b_u16b(&b[ 2.. 4]);
|
||||||
|
// packSize = b_u32b(&b[ 4.. 8]) as usize;
|
||||||
|
// horzDPI = b_u32b(&b[ 8..12]);
|
||||||
|
// vertDPI = b_u32b(&b[12..16]);
|
||||||
|
// pixelType = b_u16b(&b[16..18]);
|
||||||
|
let dept = b_u16b(&b[18..20]);
|
||||||
|
// components = b_u16b(&b[20..22]);
|
||||||
|
// compDepth = b_u16b(&b[22..24]);
|
||||||
|
// planeOffs = b_u32b(&b[24..28]);
|
||||||
|
// colorTable = b_u32b(&b[28..32]);
|
||||||
|
// reserved = b_u32b(&b[32..36]);
|
||||||
|
|
||||||
|
let mut px = PixMap{
|
||||||
|
pack,
|
||||||
|
dept,
|
||||||
|
rle: pack == PACK_DEFAULT ||
|
||||||
|
(dept == 16 && pack == PACK_RLE16) ||
|
||||||
|
(dept == 32 && pack == PACK_RLE32),
|
||||||
|
cmap: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// if it's packed we need to grab the color map
|
||||||
|
if packed
|
||||||
|
{
|
||||||
|
// table = b_u32b(&b[36..40]);
|
||||||
|
let dev = b_u16b(&b[40..42]) & 0x8000 != 0;
|
||||||
|
let colo = b_u16b(&b[42..44]) as usize + 1;
|
||||||
|
|
||||||
|
px.cmap.resize(colo as usize, Color{r: 0, g: 0, b: 0, a: 0});
|
||||||
|
for i in 0..colo
|
||||||
|
{
|
||||||
|
let p = 44 + i * 8;
|
||||||
|
let n = (b_u16b(&b[p+0..p+ 2]) & 0xff) as usize;
|
||||||
|
let r = (b_u16b(&b[p+2..p+ 4]) / 257) as u8;
|
||||||
|
let g = (b_u16b(&b[p+6..p+ 8]) / 257) as u8;
|
||||||
|
let b = (b_u16b(&b[p+8..p+10]) / 257) as u8;
|
||||||
|
|
||||||
|
// with device mapping, we ignore the index entirely
|
||||||
|
let n = if dev {i} else {n};
|
||||||
|
|
||||||
|
px.cmap[n] = Color{r, g, b, a: 255};
|
||||||
|
}
|
||||||
|
|
||||||
|
(44 + colo * 8, px)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{(36, px)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read run-length encoded data.
|
||||||
|
fn read_rle<T, F>(b: &[u8], long: bool, rd: F) -> Vec<T>
|
||||||
|
where T: Copy,
|
||||||
|
F: Fn(&mut usize) -> T
|
||||||
|
{
|
||||||
|
let (st, sz) = if long {(2usize, b_u16b(&b[0..2]) as usize)}
|
||||||
|
else {(1usize, b[0] as usize)};
|
||||||
|
|
||||||
|
let mut o = Vec::with_capacity(sz);
|
||||||
|
let mut p = st;
|
||||||
|
|
||||||
|
while p < sz
|
||||||
|
{
|
||||||
|
// size and flags are in one byte, we interpret it as a signed integer
|
||||||
|
// because it's easier to handle
|
||||||
|
let szf = b[(p, p += 1).0] as i8;
|
||||||
|
let sz = if szf < 0 {-szf + 1} else {szf + 1};
|
||||||
|
|
||||||
|
o.reserve(sz as usize);
|
||||||
|
|
||||||
|
// either repeated or unique data
|
||||||
|
if szf < 0 {let d = rd(&mut p); for _ in 0..sz { o.push(d)}}
|
||||||
|
else {for _ in 0..sz {let d = rd(&mut p); o.push(d)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
o
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_rle8(b: &[u8], long: bool) -> Vec<u8>
|
||||||
|
{read_rle(b, long, |p| (b[*p], *p += 1).0)}
|
||||||
|
|
||||||
|
fn read_rle16(b: &[u8], long: bool) -> Vec<u16>
|
||||||
|
{read_rle(b, long, |p| (b_u16b(&b[*p..*p+2]), *p += 2).0)}
|
||||||
|
|
||||||
|
/// Expand packed pixel data based on bit depth.
|
||||||
|
fn expand_data(b: Vec<u8>, depth: u16) -> Result<Vec<u8>, &'static str>
|
||||||
|
{
|
||||||
|
let mut o = Vec::with_capacity(match depth {
|
||||||
|
4 => b.len() * 2,
|
||||||
|
2 => b.len() * 4,
|
||||||
|
1 => b.len() * 8,
|
||||||
|
_ => return Err("invalid bit depth")
|
||||||
|
});
|
||||||
|
|
||||||
|
for ch in b
|
||||||
|
{
|
||||||
|
match depth {
|
||||||
|
4 => for i in 1..=0 {o.push(ch >> i * 4 & 0xfu8)}, // 2 nibbles
|
||||||
|
2 => for i in 3..=0 {o.push(ch >> i * 2 & 0x3u8)}, // 4 dibits
|
||||||
|
1 => for i in 7..=0 {o.push(ch >> i * 1 & 0x1u8)}, // 8 bits
|
||||||
|
_ => return Err("invalid bit depth")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a CopyBits operation.
|
||||||
|
fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result<Image, &str>
|
||||||
|
{
|
||||||
|
let mut p = if !packed {/*baseAddress = b_u32b(&b[0..4]);*/ 4} else {0};
|
||||||
|
|
||||||
|
// get pitch and flags, flags are packed into the upper 2 bits
|
||||||
|
let pf = b_u16b(&b[p..p+2]);
|
||||||
|
let pm = pf & 0x8000 != 0;
|
||||||
|
let pt = pf & 0x3fff;
|
||||||
|
|
||||||
|
let (w, h) = (im.w(), im.h());
|
||||||
|
|
||||||
|
let yb = b_u16b(&b[p+2..p+ 4]) as usize;
|
||||||
|
let xb = b_u16b(&b[p+4..p+ 6]) as usize;
|
||||||
|
let ye = b_u16b(&b[p+6..p+ 8]) as usize;
|
||||||
|
let xe = b_u16b(&b[p+8..p+10]) as usize;
|
||||||
|
|
||||||
|
if xe - xb < w || ye - yb < h {return Err("image size is incorrect")}
|
||||||
|
|
||||||
|
let pxm =
|
||||||
|
if pm {let (pp, pxm) = PixMap::new(&b[p+10..], packed); p += pp; pxm}
|
||||||
|
else {PixMap::empty()};
|
||||||
|
|
||||||
|
if clip {let sz = b_u16b(&b[p..p+2]) as usize; p += sz}
|
||||||
|
|
||||||
|
match pxm.dept {
|
||||||
|
1 | 2 | 4 | 8 =>
|
||||||
|
// uncompressed 8-bit colormap indices
|
||||||
|
if pt < 8 && pxm.dept == 8
|
||||||
|
{
|
||||||
|
for y in 0..h {
|
||||||
|
for x in 0..w
|
||||||
|
{im[(x, y)] = pxm.cmap[b[(p, p += 1).0] as usize].clone()}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLE compressed 1, 2, 4 or 8 bit colormap indices
|
||||||
|
else if pxm.rle
|
||||||
|
{
|
||||||
|
for y in 0..h
|
||||||
|
{
|
||||||
|
let d = read_rle8(&b[p..], pt > 250);
|
||||||
|
let d = if pxm.dept < 8 {expand_data(d, pxm.dept)?} else {d};
|
||||||
|
|
||||||
|
for x in 0..w {im[(x, y)] = pxm.cmap[d[x] as usize].clone()}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
else {Err("invalid configuration")},
|
||||||
|
16 =>
|
||||||
|
// uncompressed R5G5B5
|
||||||
|
if pt < 8 || pxm.pack == PACK_NONE
|
||||||
|
{
|
||||||
|
for y in 0..h {
|
||||||
|
for x in 0..w
|
||||||
|
{im[(x, y)] = Color::from_r5g5b5(b_u16b((&b[p..p+2], p += 2).0))}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLE compressed R5G5B5
|
||||||
|
else if pxm.rle
|
||||||
|
{
|
||||||
|
for y in 0..h
|
||||||
|
{
|
||||||
|
let d = read_rle16(&b[p..], pt > 250);
|
||||||
|
for x in 0..w {im[(x, y)] = Color::from_r5g5b5(d[x])}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
else {Err("invalid configuration")},
|
||||||
|
32 =>
|
||||||
|
// uncompressed RGB8 or ARGB8
|
||||||
|
if pt < 8 || pxm.pack == PACK_NONE || pxm.pack == PACK_NOALPHA
|
||||||
|
{
|
||||||
|
for y in 0..h {
|
||||||
|
for x in 0..w
|
||||||
|
{
|
||||||
|
let a = if pxm.pack != PACK_NOALPHA {(b[p], p += 1).0} else {255};
|
||||||
|
let r = b[p+0];
|
||||||
|
let g = b[p+1];
|
||||||
|
let b = b[p+2];
|
||||||
|
p += 3;
|
||||||
|
im[(x, y)] = Color{r, g, b, a};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLE compressed RGB8
|
||||||
|
else if pxm.rle
|
||||||
|
{
|
||||||
|
for y in 0..h
|
||||||
|
{
|
||||||
|
let d = read_rle8(&b[p..], pt > 250);
|
||||||
|
for x in 0..w
|
||||||
|
{
|
||||||
|
let r = d[x + w * 0];
|
||||||
|
let g = d[x + w * 1];
|
||||||
|
let b = d[x + w * 2];
|
||||||
|
im[(x, y)] = Color{r, g, b, a: 255};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
else {Err("invalid configuration")},
|
||||||
|
_ => Err("invalid bit depth")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a CompressedQuickTime operation.
|
||||||
|
fn read_quicktime_c(_im: Image, _b: &[u8]) -> Result<Image, &str>
|
||||||
|
{Err("compressed quicktime format not implemented")}
|
||||||
|
|
||||||
|
/// Load a PICT image.
|
||||||
|
pub fn load_pict(b: &[u8]) -> Result<Image, &str>
|
||||||
|
{
|
||||||
|
// size = b_u16b(&b[0.. 2]);
|
||||||
|
// top = b_u16b(&b[2.. 4]);
|
||||||
|
// left = b_u16b(&b[4.. 6]);
|
||||||
|
let w = b_u16b(&b[6.. 8]) as usize;
|
||||||
|
let h = b_u16b(&b[8..10]) as usize;
|
||||||
|
let im = Image::new(w, h);
|
||||||
|
let mut p = 10;
|
||||||
|
|
||||||
|
while p < b.len()
|
||||||
|
{
|
||||||
|
let op = b_u16b(&b[p..p+2]);
|
||||||
|
p += 2;
|
||||||
|
match op {
|
||||||
|
0x0098 => return read_bitmap_area(im, &b[p..], true, false), // PackBitsRect
|
||||||
|
0x0099 => return read_bitmap_area(im, &b[p..], true, true ), // PackBitsRgn
|
||||||
|
0x009a => return read_bitmap_area(im, &b[p..], false, false), // DirectBitsRect
|
||||||
|
0x009b => return read_bitmap_area(im, &b[p..], false, true ), // DirectBitsRgn
|
||||||
|
0x8200 => return read_quicktime_c(im, &b[p..]), // CompressedQuickTime
|
||||||
|
0x00ff => break, // OpEndPic
|
||||||
|
// help i'm trapped in an awful metafile format from the 80s
|
||||||
|
0x0000 => (), // NoOp
|
||||||
|
0x001c => (), // HiliteMode
|
||||||
|
0x001e => (), // DefHilite
|
||||||
|
0x0038 => (), // FrameSameRect
|
||||||
|
0x0039 => (), // PaintSameRect
|
||||||
|
0x003a => (), // EraseSameRect
|
||||||
|
0x003b => (), // InvertSameRect
|
||||||
|
0x003c => (), // FillSameRect
|
||||||
|
0x8000 => (), // Reserved
|
||||||
|
0x8100 => (), // Reserved
|
||||||
|
0x0003 => p += 2, // TxFont
|
||||||
|
0x0004 => p += 2, // TxFace
|
||||||
|
0x0005 => p += 2, // TxMode
|
||||||
|
0x0008 => p += 2, // PnMode
|
||||||
|
0x000d => p += 2, // TxSize
|
||||||
|
0x0011 => p += 2, // VersionOp
|
||||||
|
0x0015 => p += 2, // PnLocHFrac
|
||||||
|
0x0016 => p += 2, // ChExtra
|
||||||
|
0x0023 => p += 2, // ShortLineFrom
|
||||||
|
0x00a0 => p += 2, // ShortComment
|
||||||
|
0x02ff => p += 2, // Version
|
||||||
|
0x0006 => p += 4, // SpExtra
|
||||||
|
0x0007 => p += 4, // PnSize
|
||||||
|
0x000b => p += 4, // OvSize
|
||||||
|
0x000c => p += 4, // Origin
|
||||||
|
0x000e => p += 4, // FgCol
|
||||||
|
0x000f => p += 4, // BkCol
|
||||||
|
0x0021 => p += 4, // LineFrom
|
||||||
|
0x001a => p += 6, // RGBFgCol
|
||||||
|
0x001b => p += 6, // RGBBkCol
|
||||||
|
0x001d => p += 6, // TxRatio
|
||||||
|
0x0022 => p += 6, // ShortLine
|
||||||
|
0x0002 => p += 8, // BkPat
|
||||||
|
0x0009 => p += 8, // PnPat
|
||||||
|
0x0010 => p += 8, // TxRatio
|
||||||
|
0x0020 => p += 8, // Line
|
||||||
|
0x002e => p += 8, // GlyphState
|
||||||
|
0x0030 => p += 8, // FrameRect
|
||||||
|
0x0031 => p += 8, // PaintRect
|
||||||
|
0x0032 => p += 8, // EraseRect
|
||||||
|
0x0033 => p += 8, // InvertRect
|
||||||
|
0x0034 => p += 8, // FillRect
|
||||||
|
0x002d => p += 10, // LineJustify
|
||||||
|
0x0c00 => p += 24, // HeaderOp
|
||||||
|
_ =>
|
||||||
|
if op >= 0x100 && op <= 0x7fff {p += ((op as usize & 0xff00) >> 8) * 2}
|
||||||
|
else {return Err("invalid opcode in PICT")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("no image in data")
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
//! Text conversion utilities.
|
||||||
|
|
||||||
|
/// Formats a binary size string for any given number.
|
||||||
|
pub fn to_binsize(n: u64) -> String
|
||||||
|
{
|
||||||
|
let names = ["kB", "MB", "GB", "TB"];
|
||||||
|
|
||||||
|
// empty size
|
||||||
|
if n == 0 {return String::from("empty")}
|
||||||
|
|
||||||
|
// terabytes, gigabytes, megabytes, kilobytes
|
||||||
|
for i in 4..=1
|
||||||
|
{
|
||||||
|
if n >= 1000u64.pow(i)
|
||||||
|
{
|
||||||
|
let x = n as f64 / 1000f64.powi(i as i32);
|
||||||
|
return format!("{:1}{}", x, names[i as usize - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// or, just bytes
|
||||||
|
format!("{} {}", n, if n != 1 {"bytes"} else {"byte"})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes or decodes a string in the terminal encryption format.
|
||||||
|
pub fn fuck_string(s: &[u8]) -> Vec<u8>
|
||||||
|
{
|
||||||
|
let mut v = s.to_vec();
|
||||||
|
let l = s.len();
|
||||||
|
let mut p = 0;
|
||||||
|
|
||||||
|
for _ in 0..l / 4 {p += 2; v[p] ^= 0xfe; v[p + 1] ^= 0xed; p += 2}
|
||||||
|
for _ in 0..l % 4 {v[p] ^= 0xfe; p += 1}
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts input from Mac Roman to a Unicode string.
|
||||||
|
pub fn mac_roman_conv(s: &[u8]) -> String
|
||||||
|
{
|
||||||
|
let tr = [
|
||||||
|
'\u{00c4}', '\u{00c5}', '\u{00c7}', '\u{00c9}',
|
||||||
|
'\u{00d1}', '\u{00d6}', '\u{00dc}', '\u{00e1}',
|
||||||
|
'\u{00e0}', '\u{00e2}', '\u{00e4}', '\u{00e3}',
|
||||||
|
'\u{00e5}', '\u{00e7}', '\u{00e9}', '\u{00e8}',
|
||||||
|
'\u{00ea}', '\u{00eb}', '\u{00ed}', '\u{00ec}',
|
||||||
|
'\u{00ee}', '\u{00ef}', '\u{00f1}', '\u{00f3}',
|
||||||
|
'\u{00f2}', '\u{00f4}', '\u{00f6}', '\u{00f5}',
|
||||||
|
'\u{00fa}', '\u{00f9}', '\u{00fb}', '\u{00fc}',
|
||||||
|
'\u{2020}', '\u{00b0}', '\u{00a2}', '\u{00a3}',
|
||||||
|
'\u{00a7}', '\u{2022}', '\u{00b6}', '\u{00df}',
|
||||||
|
'\u{00ae}', '\u{00a9}', '\u{2122}', '\u{00b4}',
|
||||||
|
'\u{00a8}', '\u{2260}', '\u{00c6}', '\u{00d8}',
|
||||||
|
'\u{221e}', '\u{00b1}', '\u{2264}', '\u{2265}',
|
||||||
|
'\u{00a5}', '\u{00b5}', '\u{2202}', '\u{2211}',
|
||||||
|
'\u{220f}', '\u{03c0}', '\u{222b}', '\u{00aa}',
|
||||||
|
'\u{00ba}', '\u{03a9}', '\u{00e6}', '\u{00f8}',
|
||||||
|
'\u{00bf}', '\u{00a1}', '\u{00ac}', '\u{221a}',
|
||||||
|
'\u{0192}', '\u{2248}', '\u{2206}', '\u{00ab}',
|
||||||
|
'\u{00bb}', '\u{2026}', '\u{00a0}', '\u{00c0}',
|
||||||
|
'\u{00c3}', '\u{00d5}', '\u{0152}', '\u{0153}',
|
||||||
|
'\u{2013}', '\u{2014}', '\u{201c}', '\u{201d}',
|
||||||
|
'\u{2018}', '\u{2019}', '\u{00f7}', '\u{25ca}',
|
||||||
|
'\u{00ff}', '\u{0178}', '\u{2044}', '\u{20ac}',
|
||||||
|
'\u{2039}', '\u{203a}', '\u{fb01}', '\u{fb02}',
|
||||||
|
'\u{2021}', '\u{00b7}', '\u{201a}', '\u{201e}',
|
||||||
|
'\u{2030}', '\u{00c2}', '\u{00ca}', '\u{00c1}',
|
||||||
|
'\u{00cb}', '\u{00c8}', '\u{00cd}', '\u{00ce}',
|
||||||
|
'\u{00cf}', '\u{00cc}', '\u{00d3}', '\u{00d4}',
|
||||||
|
'\u{f8ff}', '\u{00d2}', '\u{00da}', '\u{00db}',
|
||||||
|
'\u{00d9}', '\u{0131}', '\u{02c6}', '\u{02dc}',
|
||||||
|
'\u{00af}', '\u{02d8}', '\u{02d9}', '\u{02da}',
|
||||||
|
'\u{00b8}', '\u{02dd}', '\u{02db}', '\u{02c7}'
|
||||||
|
];
|
||||||
|
|
||||||
|
let l = s.len();
|
||||||
|
let mut v = String::with_capacity(l);
|
||||||
|
|
||||||
|
for i in 0..l
|
||||||
|
{
|
||||||
|
if s[i] == 0 {break}
|
||||||
|
|
||||||
|
if s[i] & 0x80 != 0 {v.push(tr[s[i] as usize & 0x7f])}
|
||||||
|
else if s[i] == b'\r' {v.push('\n')}
|
||||||
|
else {v.push(s[i] as char)}
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,47 @@
|
||||||
|
extern crate memmap;
|
||||||
|
|
||||||
|
pub mod durandal;
|
||||||
|
pub mod marathon;
|
||||||
|
|
||||||
|
use std::{io, fs};
|
||||||
|
use memmap::Mmap;
|
||||||
|
|
||||||
|
fn main() -> io::Result<()>
|
||||||
|
{
|
||||||
|
let fp = fs::File::open("data/Rubicon Map.sceA")?;
|
||||||
|
let mm = unsafe{Mmap::map(&fp)?};
|
||||||
|
println!("{:?}", marathon::wad::Wad::new(&mm));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
extern crate gtk;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk::{Button, Window, WindowType};
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
{
|
||||||
|
if gtk::init().is_err() {
|
||||||
|
println!("failed to initialize GTK");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let win = Window::new(WindowType::Toplevel);
|
||||||
|
win.set_title("GTK test");
|
||||||
|
win.set_default_size(350, 70);
|
||||||
|
let btn = Button::new_with_label("butts");
|
||||||
|
win.add(&btn);
|
||||||
|
win.show_all();
|
||||||
|
|
||||||
|
win.connect_delete_event(|_, _| {
|
||||||
|
gtk::main_quit();
|
||||||
|
Inhibit(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.connect_clicked(|_| {println!("clicc");});
|
||||||
|
|
||||||
|
gtk::main();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,5 @@
|
||||||
|
//! Library for Marathon data formats.
|
||||||
|
|
||||||
|
pub mod wad;
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,100 @@
|
||||||
|
//! Marathon Wad format handling.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use durandal::bin::*;
|
||||||
|
use durandal::machead::try_mac_header;
|
||||||
|
use durandal::text::mac_roman_conv;
|
||||||
|
|
||||||
|
type Chunk <'a> = &'a[u8];
|
||||||
|
type ChunkMap<'a> = BTreeMap<Ident, Chunk<'a>>;
|
||||||
|
type EntryMap<'a> = BTreeMap<u16 , Entry<'a>>;
|
||||||
|
|
||||||
|
pub struct Entry<'a>
|
||||||
|
{
|
||||||
|
map: ChunkMap<'a>,
|
||||||
|
ext: &'a[u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Wad<'a>
|
||||||
|
{
|
||||||
|
ver: u16,
|
||||||
|
dvr: u16,
|
||||||
|
ext: usize,
|
||||||
|
nam: String,
|
||||||
|
ent: EntryMap<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for Entry<'a>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "Entry {{ ")?;
|
||||||
|
for (k, _) in &self.map {write!(f, "{} ", mac_roman_conv(&k[..]))?}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Wad<'a>
|
||||||
|
{
|
||||||
|
pub fn new(b: &[u8]) -> Wad
|
||||||
|
{
|
||||||
|
let b = &b[try_mac_header(b)..];
|
||||||
|
let ver = b_u16b(&b[ 0.. 2]);
|
||||||
|
let dvr = b_u16b(&b[ 2.. 4]);
|
||||||
|
let nam = &b[ 4..68];
|
||||||
|
let crc = b_u32b(&b[68..72]);
|
||||||
|
let dir = b_u32b(&b[72..76]) as usize;
|
||||||
|
let num = b_u16b(&b[76..78]) as usize;
|
||||||
|
let ext = b_u16b(&b[78..80]) as usize;
|
||||||
|
|
||||||
|
println!("{:x}", crc);
|
||||||
|
|
||||||
|
Wad{ver, dvr, ext,
|
||||||
|
nam: mac_roman_conv(nam),
|
||||||
|
ent: get_entries(num, ext, dir, b)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_entries(num: usize, ext: usize, dir: usize, b: &[u8]) -> EntryMap
|
||||||
|
{
|
||||||
|
let mut p = dir;
|
||||||
|
let mut map = EntryMap::new();
|
||||||
|
|
||||||
|
for _ in 0..num
|
||||||
|
{
|
||||||
|
let ofs = b_u32b(&b[p+0..p+ 4]) as usize;
|
||||||
|
let len = b_u32b(&b[p+4..p+ 8]) as usize;
|
||||||
|
let ind = b_u16b(&b[p+8..p+10]);
|
||||||
|
let ent = Entry{map: get_chunks(&b[ ofs.. ofs+len]),
|
||||||
|
ext: &b[p+10..p+10+ext]};
|
||||||
|
|
||||||
|
map.insert(ind, ent);
|
||||||
|
|
||||||
|
p += 10 + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_chunks(b: &[u8]) -> ChunkMap
|
||||||
|
{
|
||||||
|
let mut p = 0;
|
||||||
|
let mut map = ChunkMap::new();
|
||||||
|
|
||||||
|
while p < b.len()
|
||||||
|
{
|
||||||
|
let k = b_iden(&b[p+ 0..p+ 4]);
|
||||||
|
// nx = b_u32b(&b[p+ 4..p+ 8]);
|
||||||
|
let l = b_u32b(&b[p+ 8..p+12]) as usize;
|
||||||
|
// ?? = b_u32b(&b[p+12..p+16]);
|
||||||
|
map.insert(k, &b[p+16..p+ l]);
|
||||||
|
p += l + 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
Loading…
Reference in New Issue