use anyhow::Result;
use image::{DynamicImage, RgbaImage};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GameCubeTextureFormat {
CMPR, I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, }
impl GameCubeTextureFormat {
pub fn from_gx_format(format: u8) -> Option<Self> {
match format {
0x00 => Some(Self::I4),
0x01 => Some(Self::I8),
0x02 => Some(Self::IA4),
0x03 => Some(Self::IA8),
0x04 => Some(Self::RGB565),
0x05 => Some(Self::RGB5A3),
0x06 => Some(Self::RGBA8),
0x08 => Some(Self::CMPR),
_ => None,
}
}
pub fn decode(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
match self {
Self::CMPR => Self::decode_cmpr(data, width, height),
Self::I4 => Self::decode_i4(data, width, height),
Self::I8 => Self::decode_i8(data, width, height),
Self::IA4 => Self::decode_ia4(data, width, height),
Self::IA8 => Self::decode_ia8(data, width, height),
Self::RGB565 => Self::decode_rgb565(data, width, height),
Self::RGB5A3 => Self::decode_rgb5a3(data, width, height),
Self::RGBA8 => Self::decode_rgba8(data, width, height),
}
}
fn decode_cmpr(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
Ok(image)
}
fn decode_i4(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let pixels_per_byte = 2;
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
let byte_idx = (y * width + x) / pixels_per_byte;
if (byte_idx as usize) < data.len() {
let byte = data[byte_idx as usize];
let pixel_idx = (x % pixels_per_byte) as usize;
let intensity = if pixel_idx == 0 {
((byte >> 4) & 0xF) * 17
} else {
(byte & 0xF) * 17
};
image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, 255]));
}
data_idx += 1;
}
}
Ok(image)
}
fn decode_i8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
if data_idx < data.len() {
let intensity = data[data_idx];
image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, 255]));
data_idx += 1;
}
}
}
Ok(image)
}
fn decode_ia4(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let pixels_per_byte = 2;
for y in 0..height {
for x in 0..width {
let byte_idx = ((y * width + x) / pixels_per_byte) as usize;
if byte_idx < data.len() {
let byte = data[byte_idx];
let pixel_idx = (x % pixels_per_byte) as usize;
let (intensity, alpha) = if pixel_idx == 0 {
(((byte >> 4) & 0xF) * 17, ((byte >> 7) & 0x1) * 255)
} else {
((byte & 0xF) * 17, ((byte >> 3) & 0x1) * 255)
};
image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, alpha]));
}
}
}
Ok(image)
}
fn decode_ia8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
if data_idx + 1 < data.len() {
let intensity = data[data_idx];
let alpha = data[data_idx + 1];
image.put_pixel(x, y, image::Rgba([intensity, intensity, intensity, alpha]));
data_idx += 2;
}
}
}
Ok(image)
}
fn decode_rgb565(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
if data_idx + 1 < data.len() {
let word = u16::from_be_bytes([data[data_idx], data[data_idx + 1]]);
let r = ((word >> 11) & 0x1F) as u8 * 8;
let g = ((word >> 5) & 0x3F) as u8 * 4;
let b = (word & 0x1F) as u8 * 8;
image.put_pixel(x, y, image::Rgba([r, g, b, 255]));
data_idx += 2;
}
}
}
Ok(image)
}
fn decode_rgb5a3(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
if data_idx + 1 < data.len() {
let word = u16::from_be_bytes([data[data_idx], data[data_idx + 1]]);
if (word & 0x8000) != 0 {
let r = ((word >> 10) & 0x1F) as u8 * 8;
let g = ((word >> 5) & 0x1F) as u8 * 8;
let b = (word & 0x1F) as u8 * 8;
image.put_pixel(x, y, image::Rgba([r, g, b, 255]));
} else {
let a = ((word >> 12) & 0x7) as u8 * 32;
let r = ((word >> 8) & 0xF) as u8 * 16;
let g = ((word >> 4) & 0xF) as u8 * 16;
let b = (word & 0xF) as u8 * 16;
image.put_pixel(x, y, image::Rgba([r, g, b, a]));
}
data_idx += 2;
}
}
}
Ok(image)
}
fn decode_rgba8(data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let mut image = RgbaImage::new(width, height);
let mut data_idx = 0;
for y in 0..height {
for x in 0..width {
if data_idx + 3 < data.len() {
let r = data[data_idx];
let g = data[data_idx + 1];
let b = data[data_idx + 2];
let a = data[data_idx + 3];
image.put_pixel(x, y, image::Rgba([r, g, b, a]));
data_idx += 4;
}
}
}
Ok(image)
}
}