aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-12 12:41:39 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-12 12:41:39 +0100
commit8bea7b0838ca83af9abf2ec64b679b98c92d472e (patch)
treebf9fb2188bf49aece0304c61842afe5516173cbd
parentcb9a60f45cb8438c58c2f1ecb2f59611dc5d515a (diff)
downloadunity-tools-8bea7b0838ca83af9abf2ec64b679b98c92d472e.tar
unity-tools-8bea7b0838ca83af9abf2ec64b679b98c92d472e.tar.bz2
unity-tools-8bea7b0838ca83af9abf2ec64b679b98c92d472e.tar.zst
many more texture formats
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/textures.rs15
-rw-r--r--src/classes/texture2d.rs77
-rw-r--r--src/lib.rs2
5 files changed, 81 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 101ab5d..1916329 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1033,6 +1033,14 @@ dependencies = [
]
[[package]]
+name = "texture2ddecoder"
+version = "0.1.1"
+source = "git+https://github.com/UniversalGameExtraction/texture2ddecoder#d2b4653fda298f1da39917da86bc509b17879808"
+dependencies = [
+ "paste",
+]
+
+[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1129,6 +1137,7 @@ dependencies = [
"serde_json",
"serde_yml",
"texpresso",
+ "texture2ddecoder",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index a621dd5..e6aa46f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,3 +16,4 @@ serde_yml = "0.0.12"
glam = { version = "0.30.0", features = ["serde"] }
texpresso = "2.0.1"
image = "0.25.5"
+texture2ddecoder = { git = "https://github.com/UniversalGameExtraction/texture2ddecoder" }
diff --git a/src/bin/textures.rs b/src/bin/textures.rs
index 83ffb07..4a35de7 100644
--- a/src/bin/textures.rs
+++ b/src/bin/textures.rs
@@ -31,11 +31,10 @@ fn main() -> anyhow::Result<()> {
.nodes()
.iter()
.find(|n| n.name.ends_with(".resS"))
- .ok_or(anyhow!("no resS file found"))?
- .to_owned();
+ .cloned();
let mut cab = fs.read(&cabfile)?;
- let mut ress = fs2.read(&ressfile)?;
+ let mut ress = ressfile.map(|p| fs2.read(&p)).transpose()?;
let file = read_serialized_file(&mut cab)?;
for ob in file.objects {
cab.seek(SeekFrom::Start(ob.data_offset))?;
@@ -51,6 +50,7 @@ fn main() -> anyhow::Result<()> {
let value = read_value(typetree, file.endianness, &mut cab)?;
let mut texture = Texture2D::from_value(value).unwrap();
if texture.image_data.len() == 0 {
+ let ress = ress.as_mut().unwrap();
ress.seek(SeekFrom::Start(texture.stream_data.offset as u64))?;
ress.by_ref()
.take(texture.stream_data.size as u64)
@@ -60,12 +60,11 @@ fn main() -> anyhow::Result<()> {
"/tmp/tex/{}_{i}.png",
texture.name.replace("/", "-").replace(".", "-")
);
- println!(
- "{path} {}x{} {:?}",
- texture.width, texture.height, texture.format
- );
match texture.to_image() {
- Ok(im) => im.save(path).unwrap(),
+ Ok(im) => {
+ im.save(&path).unwrap();
+ println!("{path}");
+ }
Err(e) => warn!("{e}"),
}
i += 1;
diff --git a/src/classes/texture2d.rs b/src/classes/texture2d.rs
index 2bc1ade..e3f3225 100644
--- a/src/classes/texture2d.rs
+++ b/src/classes/texture2d.rs
@@ -1,7 +1,8 @@
use super::{FromValue, streaminginfo::StreamingInfo};
use crate::object::Value;
use anyhow::{Result, bail};
-use image::{DynamicImage, Rgb, Rgba};
+use image::{DynamicImage, ImageBuffer, Luma, Rgb, Rgba};
+use log::info;
use serde::Serialize;
use std::mem::transmute;
@@ -11,6 +12,7 @@ pub struct Texture2D {
pub height: i32,
pub mip_count: i32,
pub name: String,
+ #[serde(skip)]
pub image_data: Vec<u8>,
pub format: TextureFormat,
pub texture_dimension: i32,
@@ -45,23 +47,70 @@ impl Texture2D {
let w = self.width as usize;
let h = self.height as usize;
use TextureFormat::*;
+ let u32_rgba_buf_to_image = |buf: Vec<u32>| {
+ let buf = buf.into_iter().flat_map(u32::to_be_bytes).collect();
+ let im = ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf).unwrap();
+ im.into()
+ };
match self.format {
- DXT1 | DXT3 | DXT5 => {
- use texpresso::Format::*;
- let mut buf = vec![0u8; w * h * 4];
+ Alpha8 => {
+ let buf = self.image_data.clone();
+ let im =
+ ImageBuffer::<Luma<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf).unwrap();
+ Ok(im.into())
+ }
+ RGBAHalf => {
+ let buf = self
+ .image_data
+ .clone()
+ .into_iter()
+ .array_chunks::<2>()
+ .map(|x| u16::from_be_bytes(x))
+ .map(|x| f16::from_bits(x) as f32)
+ .collect::<Vec<f32>>();
+ Ok(
+ ImageBuffer::<Rgba<f32>, Vec<_>>::from_raw(w as u32, h as u32, buf)
+ .unwrap()
+ .into(),
+ )
+ }
+ DXT1 | DXT3 | DXT5 | BC4 | BC5 => {
+ use texpresso::Format as F;
let format = match self.format {
- DXT1 => Bc1,
- DXT3 => Bc2,
- DXT5 => Bc3,
+ DXT1 => F::Bc1,
+ DXT3 => F::Bc2,
+ DXT5 => F::Bc3,
+ BC4 => F::Bc4,
+ BC5 => F::Bc5,
_ => unreachable!(),
};
+ info!(
+ "decompressing {w}x{h} {:?} ({:?}) texture",
+ format, self.format
+ );
+ let mut buf = vec![0u8; w * h * 4];
format.decompress(&self.image_data, w, h, &mut buf);
- let im = image::ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf)
- .unwrap();
+ let im =
+ ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf).unwrap();
Ok(im.into())
}
+ DXT1Crunched | DXT5Crunched => {
+ let mut buf = vec![0u32; w * h];
+ texture2ddecoder::decode_unity_crunch(&self.image_data, w, h, &mut buf).unwrap();
+ Ok(u32_rgba_buf_to_image(buf))
+ }
+ BC7 => {
+ let mut buf = vec![0u32; w * h];
+ texture2ddecoder::decode_bc7(&self.image_data, w, h, &mut buf).unwrap();
+ Ok(u32_rgba_buf_to_image(buf))
+ }
+ BC6H => {
+ let mut buf = vec![0u32; w * h];
+ texture2ddecoder::decode_bc6(&self.image_data, w, h, &mut buf, false).unwrap();
+ Ok(u32_rgba_buf_to_image(buf))
+ }
RGB24 => {
- let im = image::ImageBuffer::<Rgb<u8>, Vec<_>>::from_raw(
+ let im = ImageBuffer::<Rgb<u8>, Vec<_>>::from_raw(
w as u32,
h as u32,
self.image_data.clone(),
@@ -78,14 +127,14 @@ impl Texture2D {
pix[2] = pix[3];
pix[3] = a;
}
- let im = image::ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf)
- .unwrap();
+ let im =
+ ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf).unwrap();
Ok(im.into())
}
RGBA32 => {
let buf = self.image_data.clone();
- let im = image::ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf)
- .unwrap();
+ let im =
+ ImageBuffer::<Rgba<u8>, Vec<_>>::from_raw(w as u32, h as u32, buf).unwrap();
Ok(im.into())
}
x => bail!("texture format {x:?} not supported"),
diff --git a/src/lib.rs b/src/lib.rs
index 638c3cb..f01eaae 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(array_chunks)]
+#![feature(array_chunks, f16, iter_array_chunks)]
pub mod classes;
pub mod common_strings;
pub mod helper;