Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions kidfile/src/archive_formats/afs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,28 @@ pub const ENTRY_AFS: Decoder<Archive> = Decoder {
entry_ranges.push((offset, len));
}
end = end.next_multiple_of(0x800);
let info_present = file.len() > end;
for i in 0..count {
let pos = end as usize + i * 48;
let mut name_buf = [0u8; 32];
file.read_chunk_exact(&mut name_buf, pos).map_err(|_| "could not read entry name")?;
let len = name_buf.iter().position(|x| *x == 0).unwrap_or(32);
let year = file.read_u16(pos + 32)?;
let month = file.read_u16(pos + 34)?;
let day = file.read_u16(pos + 36)?;
let hour = file.read_u16(pos + 38)?;
let minute = file.read_u16(pos + 40)?;
let second = file.read_u16(pos + 42)?;
let name = String::from_utf8(name_buf[0..len].to_vec()).map_err(|_| "entry name is not valid UTF-8")?;
let mut name = format!("{}.BIN", i);
let mut year = 2000;
let mut month = 1;
let mut day = 1;
let mut hour = 0;
let mut minute = 0;
let mut second = 0;
if info_present {
let pos = end as usize + i * 48;
let mut name_buf = [0u8; 32];
file.read_chunk_exact(&mut name_buf, pos).map_err(|_| "could not read entry name")?;
let len = name_buf.iter().position(|x| *x == 0).unwrap_or(32);
year = file.read_u16(pos + 32)?;
month = file.read_u16(pos + 34)?;
day = file.read_u16(pos + 36)?;
hour = file.read_u16(pos + 38)?;
minute = file.read_u16(pos + 40)?;
second = file.read_u16(pos + 42)?;
name = String::from_utf8(name_buf[0..len].to_vec()).map_err(|_| "entry name is not valid UTF-8")?;
}
entries.push(ArchiveEntry {
name: name.clone(),
data: file.subfile(entry_ranges[i].0, entry_ranges[i].1).unwrap(),
Expand Down
184 changes: 100 additions & 84 deletions kidfile/src/image_formats/pvr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,104 +13,120 @@ pub const ENTRY_PVR: Decoder<Image> = Decoder {
Certainty::certain_if(file.starts_with_at(b"PVRT", file_start) || file.starts_with_at(b"PVPL", file_start))
},
decode: |file| {
let mut file_start = if file.starts_with(b"GBIX") {16} else {0};
let mut palette_bytes = Default::default();
if file.starts_with_at(b"PVPL", file_start) {
let palette_len = file.read_u32(file_start + 4)? as usize;
palette_bytes = unsafe {Box::new_uninit_slice(palette_len - 8).assume_init()};
file.read_chunk_exact(&mut palette_bytes, file_start + 16).map_err(|_| "PVPL length field is incorrect")?;
file_start += palette_len + 8;
};
let file_len = file.read_u32(file_start + 4)? as usize;
let mut buf = unsafe {Box::new_uninit_slice(file_len + 8).assume_init()};
file.read_chunk_exact(&mut buf, file_start).map_err(|_| "PVRT length field is incorrect")?;
if !buf.starts_with(b"PVRT") {
return Err(format!("PVRT header not found, expected at {:#X}", file_start));
let mut file_start = 0;
let mut tex_start = 0;
let mut tex_len = 0;
let mut palettes = Vec::new();
while file_start < file.len() {
let file_len = file.read_u32(file_start + 4)? as usize;
if file.starts_with_at(b"PVPL", file_start) {
let mut palette_bytes = Default::default();
palette_bytes = unsafe {Box::new_uninit_slice(file_len - 8).assume_init()};
file.read_chunk_exact(&mut palette_bytes, file_start + 16).map_err(|_| "PVPL length field is incorrect")?;
palettes.push(palette_bytes);
} else if file.starts_with_at(b"PVRT", file_start) {
tex_start = file_start;
tex_len = file_len;
}
file_start += file_len + 8;
}
if tex_len <= 0 {
return Err("PVRT header not found in file".to_string());
}
let mut buf = unsafe {Box::new_uninit_slice(tex_len + 8).assume_init()};
file.read_chunk_exact(&mut buf, tex_start).map_err(|_| "PVRT length field is incorrect")?;
let pixel_fmt = buf.read_u8(8)?;
let twiddle_type = buf.read_u8(9)?;
println!("twiddle type {twiddle_type}");
let width = buf.read_u16(12)? as usize;
let height = buf.read_u16(14)? as usize;
let mut frame = match pixel_fmt {
0 | 1 | 2 => {
if twiddle_type == 3 { // vq compression
let codebook = buf.get(16..16 + 2048).ok_or("not enough 16-bit VQ codebook data")?;
let indices = buf.get(16 + 2048..16 + 2048 + width * height / 4).ok_or("not enough VQ index data")?;
let mut pixels = vec![0u8; width * height * 2];
for block_y in 0..height / 2 {
let twiddled_block_y = bit_twiddle(block_y);
for block_x in 0..width / 2 {
let twiddled_block_idx = twiddled_block_y | bit_twiddle(block_x) << 1;
let codebook_pos = indices[twiddled_block_idx] as usize * 8;
let x = block_x * 2;
let y = block_y * 2;
pixels[(y * width + x) * 2] = codebook[codebook_pos];
pixels[(y * width + x) * 2 + 1] = codebook[codebook_pos + 1];
pixels[(y * width + x + 1) * 2] = codebook[codebook_pos + 4];
pixels[(y * width + x + 1) * 2 + 1] = codebook[codebook_pos + 5];
pixels[((y + 1) * width + x) * 2] = codebook[codebook_pos + 2];
pixels[((y + 1) * width + x) * 2 + 1] = codebook[codebook_pos + 3];
pixels[((y + 1) * width + x + 1) * 2] = codebook[codebook_pos + 6];
pixels[((y + 1) * width + x + 1) * 2 + 1] = codebook[codebook_pos + 7];
let mut frames = Vec::new();
if palettes.len() == 0 {
let palette_bytes = unsafe {Box::new_uninit_slice(0).assume_init()};
palettes.push(palette_bytes);
}
for palette_bytes in palettes.iter() {
let mut frame = match pixel_fmt {
0 | 1 | 2 => {
if twiddle_type == 3 { // vq compression
let codebook = buf.get(16..16 + 2048).ok_or("not enough 16-bit VQ codebook data")?;
let indices = buf.get(16 + 2048..16 + 2048 + width * height / 4).ok_or("not enough VQ index data")?;
let mut pixels = vec![0u8; width * height * 2];
for block_y in 0..height / 2 {
let twiddled_block_y = bit_twiddle(block_y);
for block_x in 0..width / 2 {
let twiddled_block_idx = twiddled_block_y | bit_twiddle(block_x) << 1;
let codebook_pos = indices[twiddled_block_idx] as usize * 8;
let x = block_x * 2;
let y = block_y * 2;
pixels[(y * width + x) * 2] = codebook[codebook_pos];
pixels[(y * width + x) * 2 + 1] = codebook[codebook_pos + 1];
pixels[(y * width + x + 1) * 2] = codebook[codebook_pos + 4];
pixels[(y * width + x + 1) * 2 + 1] = codebook[codebook_pos + 5];
pixels[((y + 1) * width + x) * 2] = codebook[codebook_pos + 2];
pixels[((y + 1) * width + x) * 2 + 1] = codebook[codebook_pos + 3];
pixels[((y + 1) * width + x + 1) * 2] = codebook[codebook_pos + 6];
pixels[((y + 1) * width + x + 1) * 2 + 1] = codebook[codebook_pos + 7];
}
}
if pixel_fmt == 0 {
Frame::from_bgra5551(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgra5551Vq8)
} else if pixel_fmt == 1 {
Frame::from_bgr565(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgr565Vq8)
} else {
Frame::from_bgra4444(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgra4444Vq8)
}
}
if pixel_fmt == 0 {
Frame::from_bgra5551(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgra5551Vq8)
} else if pixel_fmt == 1 {
Frame::from_bgr565(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgr565Vq8)
} else {
Frame::from_bgra4444(width as u32, height as u32, &pixels).with_og_fmt(PixelFormat::Bgra4444Vq8)
if pixel_fmt == 0 {
Frame::from_bgra5551(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGRA5551")?)
} else if pixel_fmt == 1 {
Frame::from_bgr565(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGR565")?)
} else {
Frame::from_bgra4444(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGRA4444")?)
}
}
} else {
if pixel_fmt == 0 {
Frame::from_bgra5551(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGRA5551")?)
} else if pixel_fmt == 1 {
Frame::from_bgr565(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGR565")?)
}
5 => {
if twiddle_type == 7 && palette_bytes.is_empty(){
return Err("file needs external palette, unimplemented".into());
} else if palette_bytes.is_empty() {
Frame::from_bgra_clut4(
width as u32, height as u32,
buf.get(16..16 + 1024).ok_or("not enough palette data for BGRA clut4")?,
buf.get(16 + 1024..16 + 1024 + width * height / 2).ok_or("not enough index data for BGRA clut4")?
)
} else {
Frame::from_bgra4444(width as u32, height as u32, buf.get(16..16 + width * height * 2).ok_or("not enough pixel data for BGRA4444")?)
Frame::from_bgra_clut4(
width as u32, height as u32,
&palette_bytes,
buf.get(16..16 + width * height / 2).ok_or("not enough data for BGRA clut4")?
)
}
}
}
5 => {
if twiddle_type == 7 {
return Err("file needs external palette, unimplemented".into());
} else if palette_bytes.is_empty() {
Frame::from_bgra_clut4(
width as u32, height as u32,
buf.get(16..16 + 1024).ok_or("not enough palette data for BGRA clut4")?,
buf.get(16 + 1024..16 + 1024 + width * height / 2).ok_or("not enough index data for BGRA clut4")?
)
} else {
Frame::from_bgra_clut4(
width as u32, height as u32,
&palette_bytes,
buf.get(16..16 + width * height / 2).ok_or("not enough data for BGRA clut4")?
)
}
}
6 => {
if twiddle_type == 7 {
return Err("file needs external palette, unimplemented".into());
} else if palette_bytes.is_empty() {
Frame::from_bgra_clut8(
width as u32, height as u32,
buf.get(16..16 + 1024).ok_or("not enough palette data for BGRA clut8")?,
buf.get(16 + 1024..16 + 1024 + width * height).ok_or("not enough index data for BGRA clut8")?
)
} else {
Frame::from_bgra_clut8(
width as u32, height as u32,
&palette_bytes,
buf.get(16..16 + width * height).ok_or("not enough data for BGRA clut8")?
)
6 => {
if twiddle_type == 7 && palette_bytes.is_empty(){
return Err("file needs external palette, unimplemented".into());
} else if palette_bytes.is_empty() {
Frame::from_bgra_clut8(
width as u32, height as u32,
buf.get(16..16 + 1024).ok_or("not enough palette data for BGRA clut8")?,
buf.get(16 + 1024..16 + 1024 + width * height).ok_or("not enough index data for BGRA clut8")?
)
} else {
Frame::from_bgra_clut8(
width as u32, height as u32,
&palette_bytes,
buf.get(16..16 + width * height).ok_or("not enough data for BGRA clut8")?
)
}
}
_ => return Err(format!("unhandled PVR pixel format {pixel_fmt}"))
};
if [1, 2, 5, 6, 7, 8, 13].contains(&twiddle_type) {
frame = frame.twiddled_dc();
}
_ => return Err(format!("unhandled PVR pixel format {pixel_fmt}"))
};
if [1, 2, 5, 6, 7, 8, 13].contains(&twiddle_type) {
frame = frame.twiddled_dc();
frames.push(frame);
}
Ok(Image {frames: Box::new([frame])})
Ok(Image {frames: frames.into_boxed_slice()})
}
};