WIP: Implement texture files #191
4 changed files with 30 additions and 17 deletions
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::murmur::{Dictionary, HashGroup, IdString64, Murmur32, Murmur64};
|
use crate::murmur::{Dictionary, HashGroup, IdString32, IdString64, Murmur32, Murmur64};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CmdLine {
|
pub struct CmdLine {
|
||||||
|
@ -87,17 +87,17 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_hash_short<M>(&self, hash: M, group: HashGroup) -> String
|
pub fn lookup_hash_short<M>(&self, hash: M, group: HashGroup) -> IdString32
|
||||||
where
|
where
|
||||||
M: Into<Murmur32>,
|
M: Into<Murmur32>,
|
||||||
{
|
{
|
||||||
let hash = hash.into();
|
let hash = hash.into();
|
||||||
if let Some(s) = self.lookup.lookup_short(hash, group) {
|
if let Some(s) = self.lookup.lookup_short(hash, group) {
|
||||||
tracing::debug!(%hash, string = s, "Murmur32 lookup successful");
|
tracing::debug!(%hash, string = s, "Murmur32 lookup successful");
|
||||||
s.to_owned()
|
s.to_string().into()
|
||||||
} else {
|
} else {
|
||||||
tracing::debug!(%hash, "Murmur32 lookup failed");
|
tracing::debug!(%hash, "Murmur32 lookup failed");
|
||||||
format!("{hash:08X}")
|
hash.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use color_eyre::{Report, Result};
|
||||||
|
|
||||||
use crate::binary::sync::ReadExt;
|
use crate::binary::sync::ReadExt;
|
||||||
use crate::bundle::file::{BundleFileVariant, UserFile};
|
use crate::bundle::file::{BundleFileVariant, UserFile};
|
||||||
use crate::murmur::HashGroup;
|
use crate::murmur::{HashGroup, IdString32};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
@ -26,7 +26,7 @@ impl Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct Strings(HashMap<String, HashMap<Language, String>>);
|
pub struct Strings(HashMap<IdString32, HashMap<Language, String>>);
|
||||||
|
|
||||||
fn read_string<R>(r: R) -> Result<String>
|
fn read_string<R>(r: R) -> Result<String>
|
||||||
where
|
where
|
||||||
|
@ -42,7 +42,7 @@ where
|
||||||
impl Strings {
|
impl Strings {
|
||||||
#[tracing::instrument(skip_all, fields(languages = variants.len()))]
|
#[tracing::instrument(skip_all, fields(languages = variants.len()))]
|
||||||
pub fn from_variants(ctx: &crate::Context, variants: &Vec<BundleFileVariant>) -> Result<Self> {
|
pub fn from_variants(ctx: &crate::Context, variants: &Vec<BundleFileVariant>) -> Result<Self> {
|
||||||
let mut map: HashMap<String, HashMap<Language, String>> = HashMap::new();
|
let mut map: HashMap<IdString32, HashMap<Language, String>> = HashMap::new();
|
||||||
|
|
||||||
for (i, variant) in variants.iter().enumerate() {
|
for (i, variant) in variants.iter().enumerate() {
|
||||||
let _span = tracing::trace_span!("variant {}", i);
|
let _span = tracing::trace_span!("variant {}", i);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use tokio::fs;
|
||||||
|
|
||||||
use crate::binary::sync::{ReadExt, WriteExt};
|
use crate::binary::sync::{ReadExt, WriteExt};
|
||||||
use crate::bundle::file::UserFile;
|
use crate::bundle::file::UserFile;
|
||||||
use crate::murmur::{IdString32, IdString64};
|
use crate::murmur::{HashGroup, IdString32, IdString64};
|
||||||
use crate::{BundleFile, BundleFileType, BundleFileVariant};
|
use crate::{BundleFile, BundleFileType, BundleFileVariant};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -86,8 +86,12 @@ struct Texture {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texture {
|
impl Texture {
|
||||||
#[tracing::instrument(skip(r, stream_r))]
|
#[tracing::instrument(skip(ctx, r, stream_r))]
|
||||||
fn from_binary(mut r: impl Read + Seek, mut stream_r: Option<impl Read>) -> Result<Self> {
|
fn from_binary(
|
||||||
|
ctx: &crate::Context,
|
||||||
|
mut r: impl Read + Seek,
|
||||||
|
mut stream_r: Option<impl Read>,
|
||||||
|
) -> Result<Self> {
|
||||||
// Looking at the executable in IDA, there is one other valid value: `2`.
|
// Looking at the executable in IDA, there is one other valid value: `2`.
|
||||||
// If this ever comes up in the game data, I'll have to reverse engineer the
|
// If this ever comes up in the game data, I'll have to reverse engineer the
|
||||||
// (de)compression algorithm through IDA.
|
// (de)compression algorithm through IDA.
|
||||||
|
@ -187,7 +191,7 @@ impl Texture {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let category = r.read_u32().map(IdString32::from)?;
|
let category = ctx.lookup_hash_short(r.read_u32()?, HashGroup::TextureCategory);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
category,
|
category,
|
||||||
|
@ -265,8 +269,9 @@ struct TextureDefinitionOutput {
|
||||||
category: String,
|
category: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(data), fields(buf_len = data.as_ref().len()))]
|
#[tracing::instrument(skip(ctx, data), fields(buf_len = data.as_ref().len()))]
|
||||||
pub(crate) async fn decompile_data(
|
pub(crate) async fn decompile_data(
|
||||||
|
ctx: &crate::Context,
|
||||||
name: String,
|
name: String,
|
||||||
data: impl AsRef<[u8]>,
|
data: impl AsRef<[u8]>,
|
||||||
stream_file_name: Option<PathBuf>,
|
stream_file_name: Option<PathBuf>,
|
||||||
|
@ -281,7 +286,7 @@ pub(crate) async fn decompile_data(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let texture = Texture::from_binary(&mut r, stream_r.as_mut())?;
|
let texture = Texture::from_binary(ctx, &mut r, stream_r.as_mut())?;
|
||||||
let files = texture.to_user_files(name);
|
let files = texture.to_user_files(name);
|
||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
@ -300,7 +305,7 @@ pub(crate) async fn decompile(
|
||||||
None => PathBuf::from("bundle").join(name),
|
None => PathBuf::from("bundle").join(name),
|
||||||
});
|
});
|
||||||
|
|
||||||
return decompile_data(name, variant.data(), stream_file_name).await;
|
return decompile_data(ctx, name, variant.data(), stream_file_name).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(file_name) = variant.data_file_name() else {
|
let Some(file_name) = variant.data_file_name() else {
|
||||||
|
@ -323,7 +328,7 @@ pub(crate) async fn decompile(
|
||||||
"Provide a game directory in the config file or make sure the `data` directory is next to the provided bundle."
|
"Provide a game directory in the config file or make sure the `data` directory is next to the provided bundle."
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
decompile_data(name, &data, None).await
|
decompile_data(ctx, name, &data, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(sjson, name), fields(sjson_len = sjson.as_ref().len(), name = %name.display()))]
|
#[tracing::instrument(skip(sjson, name), fields(sjson_len = sjson.as_ref().len(), name = %name.display()))]
|
||||||
|
|
|
@ -12,12 +12,19 @@ pub enum HashGroup {
|
||||||
Filename,
|
Filename,
|
||||||
Filetype,
|
Filetype,
|
||||||
Strings,
|
Strings,
|
||||||
|
TextureCategory,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HashGroup {
|
impl HashGroup {
|
||||||
pub fn all() -> [Self; 3] {
|
pub fn all() -> [Self; 5] {
|
||||||
[Self::Filename, Self::Filetype, Self::Other]
|
[
|
||||||
|
Self::Filename,
|
||||||
|
Self::Filetype,
|
||||||
|
Self::Strings,
|
||||||
|
Self::TextureCategory,
|
||||||
|
Self::Other,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +34,7 @@ impl std::fmt::Display for HashGroup {
|
||||||
HashGroup::Filename => write!(f, "filename"),
|
HashGroup::Filename => write!(f, "filename"),
|
||||||
HashGroup::Filetype => write!(f, "filetype"),
|
HashGroup::Filetype => write!(f, "filetype"),
|
||||||
HashGroup::Strings => write!(f, "strings"),
|
HashGroup::Strings => write!(f, "strings"),
|
||||||
|
HashGroup::TextureCategory => write!(f, "texture-category"),
|
||||||
HashGroup::Other => write!(f, "other"),
|
HashGroup::Other => write!(f, "other"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue