aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/textures.rs84
-rw-r--r--src/classes/assetinfo.rs6
-rw-r--r--src/classes/mod.rs9
-rw-r--r--src/classes/streaminginfo.rs22
-rw-r--r--src/classes/texture2d.rs35
5 files changed, 113 insertions, 43 deletions
diff --git a/src/bin/textures.rs b/src/bin/textures.rs
index 5cfaaa7..83ffb07 100644
--- a/src/bin/textures.rs
+++ b/src/bin/textures.rs
@@ -1,10 +1,12 @@
+use anyhow::anyhow;
+use log::warn;
use std::{
env::args,
fs::{File, create_dir_all},
- io::{BufReader, Seek, SeekFrom},
+ io::{BufReader, Read, Seek, SeekFrom},
};
use unity_tools::{
- classes::{FromValue, texture2d::Texture2d},
+ classes::{FromValue, texture2d::Texture2D},
object::read_value,
serialized_file::read_serialized_file,
unityfs::UnityFS,
@@ -12,37 +14,61 @@ use unity_tools::{
fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
- let file = BufReader::new(File::open(args().nth(1).unwrap())?);
- let mut fs = UnityFS::open(file)?;
+ let file = || BufReader::new(File::open(args().nth(1).unwrap()).unwrap());
+ let mut fs = UnityFS::open(file())?;
+ let mut fs2 = UnityFS::open(file())?;
let mut i = 0;
create_dir_all("/tmp/tex").unwrap();
- for node in fs.nodes().to_vec() {
- if node.name.ends_with(".resource") || node.name.ends_with(".resS") {
- continue;
- }
- let mut cab = fs.read(&node)?;
- let file = read_serialized_file(&mut cab)?;
- for ob in file.objects {
- cab.seek(SeekFrom::Start(ob.data_offset))?;
- let typetree = if ob.type_id < 0 {
- unimplemented!()
- } else {
- &file.types[ob.type_id as usize]
- };
- if let Some(typetree) = &typetree.type_tree {
- if typetree.type_string != "Texture2D" {
- continue;
- }
- let value = read_value(typetree, file.endianness, &mut cab)?;
- let texture = Texture2d::from_value(value).unwrap();
- if texture.image_data.len() > 0 {
- let path = format!("/tmp/tex/{i}.png");
- println!("{path}");
- texture.to_image().unwrap().save(path).unwrap();
- i += 1;
- }
+
+ let cabfile = fs
+ .nodes()
+ .iter()
+ .find(|n| !n.name.ends_with(".resource") && !n.name.ends_with(".resS"))
+ .ok_or(anyhow!("no CAB file found"))?
+ .to_owned();
+ let ressfile = fs2
+ .nodes()
+ .iter()
+ .find(|n| n.name.ends_with(".resS"))
+ .ok_or(anyhow!("no resS file found"))?
+ .to_owned();
+
+ let mut cab = fs.read(&cabfile)?;
+ let mut ress = fs2.read(&ressfile)?;
+ let file = read_serialized_file(&mut cab)?;
+ for ob in file.objects {
+ cab.seek(SeekFrom::Start(ob.data_offset))?;
+ let typetree = if ob.type_id < 0 {
+ unimplemented!()
+ } else {
+ &file.types[ob.type_id as usize]
+ };
+ if let Some(typetree) = &typetree.type_tree {
+ if typetree.type_string != "Texture2D" {
+ continue;
+ }
+ let value = read_value(typetree, file.endianness, &mut cab)?;
+ let mut texture = Texture2D::from_value(value).unwrap();
+ if texture.image_data.len() == 0 {
+ ress.seek(SeekFrom::Start(texture.stream_data.offset as u64))?;
+ ress.by_ref()
+ .take(texture.stream_data.size as u64)
+ .read_to_end(&mut texture.image_data)?;
+ }
+ let path = format!(
+ "/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(),
+ Err(e) => warn!("{e}"),
}
+ i += 1;
}
}
diff --git a/src/classes/assetinfo.rs b/src/classes/assetinfo.rs
index 9679373..9fad90a 100644
--- a/src/classes/assetinfo.rs
+++ b/src/classes/assetinfo.rs
@@ -1,4 +1,4 @@
-use super::{gameobject::GameObject, pptr::PPtr};
+use super::{FromValue, gameobject::GameObject, pptr::PPtr};
use crate::object::Value;
use anyhow::Result;
use serde::Serialize;
@@ -9,8 +9,8 @@ pub struct AssetInfo {
pub preload_size: i32,
pub asset: PPtr<GameObject>,
}
-impl AssetInfo {
- pub fn from_value(v: Value) -> Result<Self> {
+impl FromValue for AssetInfo {
+ fn from_value(v: Value) -> Result<Self> {
let mut fields = v.as_class("AssetInfo").unwrap();
Ok(AssetInfo {
preload_index: fields["preloadIndex"].as_i32().unwrap(),
diff --git a/src/classes/mod.rs b/src/classes/mod.rs
index be2d5da..f7b89cb 100644
--- a/src/classes/mod.rs
+++ b/src/classes/mod.rs
@@ -1,9 +1,10 @@
pub mod assetinfo;
pub mod gameobject;
pub mod pptr;
+pub mod streaminginfo;
+pub mod texture2d;
pub mod transform;
pub mod vectors;
-pub mod texture2d;
use crate::object::Value;
use anyhow::Result;
@@ -12,6 +13,8 @@ use gameobject::GameObject;
use pptr::PPtr;
use serde::Serialize;
use std::collections::BTreeMap;
+use streaminginfo::StreamingInfo;
+use texture2d::Texture2D;
use transform::Transform;
#[derive(Debug, Serialize)]
@@ -20,6 +23,8 @@ pub enum HValue {
GameObject(GameObject),
Transform(Transform),
PPtr(PPtr),
+ Texture2D(Texture2D),
+ StreamingInfo(StreamingInfo),
Pair(Box<HValue>, Box<HValue>),
Value([Value; 1]),
@@ -41,6 +46,8 @@ impl HValue {
"AssetInfo" => Self::AssetInfo(AssetInfo::from_value(value)?),
"GameObject" => Self::GameObject(GameObject::from_value(value)?),
"Transform" => Self::Transform(Transform::from_value(value)?),
+ "Texture2D" => Self::Texture2D(Texture2D::from_value(value)?),
+ "StreamingInfo" => Self::StreamingInfo(StreamingInfo::from_value(value)?),
_ => {
let Value::Object { class, fields } = value else {
unreachable!()
diff --git a/src/classes/streaminginfo.rs b/src/classes/streaminginfo.rs
new file mode 100644
index 0000000..3ab271d
--- /dev/null
+++ b/src/classes/streaminginfo.rs
@@ -0,0 +1,22 @@
+use crate::object::Value;
+use anyhow::Result;
+use serde::Serialize;
+
+use super::FromValue;
+
+#[derive(Debug, Serialize)]
+pub struct StreamingInfo {
+ pub offset: u32,
+ pub path: String,
+ pub size: u32,
+}
+impl FromValue for StreamingInfo {
+ fn from_value(v: Value) -> Result<Self> {
+ let fields = v.as_class("StreamingInfo").unwrap();
+ Ok(StreamingInfo {
+ offset: fields["offset"].as_u32().unwrap(),
+ size: fields["size"].as_u32().unwrap(),
+ path: fields["path"].to_owned().as_string().unwrap(),
+ })
+ }
+}
diff --git a/src/classes/texture2d.rs b/src/classes/texture2d.rs
index 372aee7..2bc1ade 100644
--- a/src/classes/texture2d.rs
+++ b/src/classes/texture2d.rs
@@ -1,4 +1,4 @@
-use super::FromValue;
+use super::{FromValue, streaminginfo::StreamingInfo};
use crate::object::Value;
use anyhow::{Result, bail};
use image::{DynamicImage, Rgb, Rgba};
@@ -6,20 +6,21 @@ use serde::Serialize;
use std::mem::transmute;
#[derive(Debug, Serialize)]
-pub struct Texture2d {
+pub struct Texture2D {
pub width: i32,
pub height: i32,
pub mip_count: i32,
pub name: String,
pub image_data: Vec<u8>,
- pub texture_format: TextureFormat,
+ pub format: TextureFormat,
pub texture_dimension: i32,
+ pub stream_data: StreamingInfo,
}
-impl FromValue for Texture2d {
+impl FromValue for Texture2D {
fn from_value(v: Value) -> Result<Self> {
let mut fields = v.as_class("Texture2D").unwrap();
- Ok(Texture2d {
+ Ok(Texture2D {
width: fields.remove("m_Width").unwrap().as_i32().unwrap(),
height: fields.remove("m_Height").unwrap().as_i32().unwrap(),
mip_count: fields.remove("m_MipCount").unwrap().as_i32().unwrap(),
@@ -28,25 +29,33 @@ impl FromValue for Texture2d {
.unwrap()
.as_i32()
.unwrap(),
- texture_format: unsafe {
+ format: unsafe {
transmute::<_, TextureFormat>(
fields.remove("m_TextureFormat").unwrap().as_i32().unwrap(),
)
},
name: fields.remove("m_Name").unwrap().as_string().unwrap(),
image_data: fields.remove("image data").unwrap().as_typeless().unwrap(),
+ stream_data: fields.remove("m_StreamData").unwrap().parse().unwrap(),
})
}
}
-impl Texture2d {
+impl Texture2D {
pub fn to_image(&self) -> Result<DynamicImage> {
let w = self.width as usize;
let h = self.height as usize;
use TextureFormat::*;
- match self.texture_format {
- DXT5 => {
+ match self.format {
+ DXT1 | DXT3 | DXT5 => {
+ use texpresso::Format::*;
let mut buf = vec![0u8; w * h * 4];
- texpresso::Format::Bc3.decompress(&self.image_data, w, h, &mut buf);
+ let format = match self.format {
+ DXT1 => Bc1,
+ DXT3 => Bc2,
+ DXT5 => Bc3,
+ _ => unreachable!(),
+ };
+ 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();
Ok(im.into())
@@ -73,6 +82,12 @@ impl Texture2d {
.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();
+ Ok(im.into())
+ }
x => bail!("texture format {x:?} not supported"),
}
}