io_unity 0.3.0

io_unity is a lib for read unity assets
Documentation
pub mod type_tree;

use crate::{def_unity_class, unity_asset_view::UnityAssetViewer};
use binrw::binrw;
use image::{DynamicImage, GrayAlphaImage, RgbImage, RgbaImage};
use num_enum::TryFromPrimitive;

def_unity_class!(Texture2D);

pub trait Texture2DObject {
    fn get_width(&self) -> anyhow::Result<u64>;
    fn get_height(&self) -> anyhow::Result<u64>;
    fn get_texture_format(&self) -> anyhow::Result<TextureFormat>;
    fn get_image_data(&self, viewer: &UnityAssetViewer) -> anyhow::Result<Vec<u8>>;

    fn get_image(&self, viewer: &UnityAssetViewer) -> anyhow::Result<DynamicImage> {
        let data = self.get_image_data(viewer)?;
        let texture_format = self.get_texture_format()?;
        let width = self.get_width()? as usize;
        let height = self.get_height()? as usize;

        match &texture_format {
            TextureFormat::DXT1
            | TextureFormat::DXT3
            | TextureFormat::DXT5
            | TextureFormat::BC4
            | TextureFormat::BC5
            | TextureFormat::BC6H
            | TextureFormat::BC7
            | TextureFormat::DXT1Crunched
            | TextureFormat::DXT5Crunched => {
                let size = width * height * 4;
                let mut output = vec![0; size];
                match &texture_format {
                    TextureFormat::DXT1 => {
                        texpresso::Format::Bc1.decompress(&data, width, height, &mut output)
                    }
                    TextureFormat::DXT3 => {
                        texpresso::Format::Bc2.decompress(&data, width, height, &mut output)
                    }
                    TextureFormat::DXT5 => {
                        texpresso::Format::Bc3.decompress(&data, width, height, &mut output)
                    }

                    TextureFormat::BC4 => {
                        texpresso::Format::Bc4.decompress(&data, width, height, &mut output)
                    }
                    TextureFormat::BC5 => {
                        texpresso::Format::Bc5.decompress(&data, width, height, &mut output)
                    }
                    TextureFormat::BC6H
                    | TextureFormat::BC7
                    | TextureFormat::DXT1Crunched
                    | TextureFormat::DXT5Crunched => {
                        return Err(anyhow!("unsupport {:?}", self.get_texture_format()))
                    }
                    _ => unreachable!(),
                }
                let result = RgbaImage::from_raw(width as u32, height as u32, output)
                    .ok_or(anyhow!("from_raw"))?;
                Ok(DynamicImage::ImageRgba8(result))
            }
            TextureFormat::ASTC_RGB_4x4
            | TextureFormat::ASTC_RGB_5x5
            | TextureFormat::ASTC_RGB_6x6
            | TextureFormat::ASTC_RGB_8x8
            | TextureFormat::ASTC_RGB_10x10
            | TextureFormat::ASTC_RGB_12x12
            | TextureFormat::ASTC_RGBA_4x4
            | TextureFormat::ASTC_RGBA_5x5
            | TextureFormat::ASTC_RGBA_6x6
            | TextureFormat::ASTC_RGBA_8x8
            | TextureFormat::ASTC_RGBA_10x10
            | TextureFormat::ASTC_RGBA_12x12
            | TextureFormat::ASTC_HDR_4x4
            | TextureFormat::ASTC_HDR_5x5
            | TextureFormat::ASTC_HDR_6x6
            | TextureFormat::ASTC_HDR_8x8
            | TextureFormat::ASTC_HDR_10x10
            | TextureFormat::ASTC_HDR_12x12 => {
                let size = width * height;
                let mut output = vec![[0u8; 4]; size];
                let footprint = match &texture_format {
                    TextureFormat::ASTC_RGB_4x4
                    | TextureFormat::ASTC_RGBA_4x4
                    | TextureFormat::ASTC_HDR_4x4 => astc_decode::Footprint::new(4, 4),
                    TextureFormat::ASTC_RGB_5x5
                    | TextureFormat::ASTC_RGBA_5x5
                    | TextureFormat::ASTC_HDR_5x5 => astc_decode::Footprint::new(5, 5),
                    TextureFormat::ASTC_RGB_6x6
                    | TextureFormat::ASTC_RGBA_6x6
                    | TextureFormat::ASTC_HDR_6x6 => astc_decode::Footprint::new(6, 6),
                    TextureFormat::ASTC_RGB_8x8
                    | TextureFormat::ASTC_RGBA_8x8
                    | TextureFormat::ASTC_HDR_8x8 => astc_decode::Footprint::new(8, 8),
                    TextureFormat::ASTC_RGB_10x10
                    | TextureFormat::ASTC_RGBA_10x10
                    | TextureFormat::ASTC_HDR_10x10 => astc_decode::Footprint::new(10, 10),
                    TextureFormat::ASTC_RGB_12x12
                    | TextureFormat::ASTC_RGBA_12x12
                    | TextureFormat::ASTC_HDR_12x12 => astc_decode::Footprint::new(12, 12),
                    _ => unreachable!(),
                };
                astc_decode::astc_decode(
                    &*data,
                    width as u32,
                    height as u32,
                    footprint,
                    |x, y, color| {
                        output[(x as usize + y as usize * width)] = color;
                    },
                )?;

                let result = RgbaImage::from_raw(width as u32, height as u32, output.concat())
                    .ok_or(anyhow!("from_raw"))?;
                Ok(DynamicImage::ImageRgba8(result))
            }
            TextureFormat::Alpha8 => {
                let buff: Vec<[u8; 2]> = data.into_iter().map(|f| [0, f]).collect();
                let result = GrayAlphaImage::from_raw(width as u32, height as u32, buff.concat())
                    .ok_or(anyhow!("from_raw"))?;
                Ok(DynamicImage::ImageLumaA8(result))
            }
            TextureFormat::RGB24 => {
                let result = RgbImage::from_raw(width as u32, height as u32, data.to_vec())
                    .ok_or(anyhow!("from_raw"))?;
                Ok(DynamicImage::ImageRgb8(result))
            }
            TextureFormat::RGBA32 => {
                let result = RgbaImage::from_raw(width as u32, height as u32, data.to_vec())
                    .ok_or(anyhow!("from_raw"))?;
                Ok(DynamicImage::ImageRgba8(result))
            }
            _ => Err(anyhow!(
                "unsupport texture_format: {:?}",
                self.get_texture_format()
            )),
        }
    }
}

#[binrw]
#[brw(repr = u32)]
#[derive(Debug, Eq, PartialEq, TryFromPrimitive, Clone)]
#[repr(u32)]
#[allow(non_camel_case_types)]
pub enum TextureFormat {
    Alpha8 = 1,
    ARGB4444,
    RGB24,
    RGBA32,
    ARGB32,
    ARGBFloat,
    RGB565,
    BGR24,
    R16,
    DXT1,
    DXT3,
    DXT5,
    RGBA4444,
    BGRA32,
    RHalf,
    RGHalf,
    RGBAHalf,
    RFloat,
    RGFloat,
    RGBAFloat,
    YUY2,
    RGB9e5Float,
    RGBFloat,
    BC6H,
    BC7,
    BC4,
    BC5,
    DXT1Crunched,
    DXT5Crunched,
    PVRTC_RGB2,
    PVRTC_RGBA2,
    PVRTC_RGB4,
    PVRTC_RGBA4,
    ETC_RGB4,
    ATC_RGB4,
    ATC_RGBA8,
    EAC_R = 41,
    EAC_R_SIGNED,
    EAC_RG,
    EAC_RG_SIGNED,
    ETC2_RGB,
    ETC2_RGBA1,
    ETC2_RGBA8,
    ASTC_RGB_4x4,
    ASTC_RGB_5x5,
    ASTC_RGB_6x6,
    ASTC_RGB_8x8,
    ASTC_RGB_10x10,
    ASTC_RGB_12x12,
    ASTC_RGBA_4x4,
    ASTC_RGBA_5x5,
    ASTC_RGBA_6x6,
    ASTC_RGBA_8x8,
    ASTC_RGBA_10x10,
    ASTC_RGBA_12x12,
    ETC_RGB4_3DS,
    ETC_RGBA8_3DS,
    RG16,
    R8,
    ETC_RGB4Crunched,
    ETC2_RGBA8Crunched,
    ASTC_HDR_4x4,
    ASTC_HDR_5x5,
    ASTC_HDR_6x6,
    ASTC_HDR_8x8,
    ASTC_HDR_10x10,
    ASTC_HDR_12x12,
    RG32,
    RGB48,
    RGBA64,
}