feat: Implement decompilation for strings file type

This commit is contained in:
Lucas Schwiderski 2022-12-28 18:30:11 +01:00
parent 5cc97959a5
commit 2219f4fab3
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8
6 changed files with 105 additions and 0 deletions

7
CHANGELOG.adoc Normal file
View file

@ -0,0 +1,7 @@
= Changelog
== Unreleased
=== Added
- implement decompilation for `.strings` files

View file

@ -14,6 +14,7 @@ use tokio_stream::StreamExt;
pub enum HashGroup {
Filename,
Filetype,
Strings,
Other,
}
@ -22,6 +23,7 @@ impl From<HashGroup> for sdk::murmur::HashGroup {
match value {
HashGroup::Filename => sdk::murmur::HashGroup::Filename,
HashGroup::Filetype => sdk::murmur::HashGroup::Filetype,
HashGroup::Strings => sdk::murmur::HashGroup::Strings,
HashGroup::Other => sdk::murmur::HashGroup::Other,
}
}

View file

@ -359,6 +359,10 @@ impl BundleFileVariant {
self.header.size
}
pub fn kind(&self) -> u32 {
self.header.variant
}
pub fn data(&self) -> &[u8] {
&self.data
}
@ -543,6 +547,11 @@ impl BundleFile {
);
}
if file_type == BundleFileType::Strings {
let ctx = ctx.read().await;
return strings::decompile(&ctx, &self.variants);
}
let tasks = self.variants.iter().map(|variant| {
let ctx = ctx.clone();

View file

@ -1,2 +1,3 @@
pub mod lua;
pub mod package;
pub mod strings;

View file

@ -0,0 +1,84 @@
use std::collections::HashMap;
use std::io::{Cursor, Read};
use color_eyre::{Report, Result};
use crate::binary::sync::ReadExt;
use crate::bundle::file::{BundleFileVariant, UserFile};
use crate::murmur::HashGroup;
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[serde(untagged)]
pub enum Language {
#[serde(rename = "en")]
English,
#[serde(serialize_with = "Language::serialize_unnamed")]
Unnamed(u32),
}
impl Language {
fn serialize_unnamed<S>(field: &u32, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
ser.serialize_str(&format!("lang_{}", field))
}
}
#[derive(serde::Serialize)]
pub struct Strings(HashMap<String, HashMap<Language, String>>);
fn read_string<R>(r: R) -> Result<String>
where
R: Read,
{
r.bytes()
.take_while(|b| b.as_ref().map(|b| *b != 0).unwrap_or(false))
.map(|b| b.map_err(Report::new))
.collect::<Result<_>>()
.and_then(|bytes| String::from_utf8(bytes).map_err(Report::new))
}
impl Strings {
#[tracing::instrument(skip_all, fields(languages = variants.len()))]
pub fn from_variants(ctx: &crate::Context, variants: &Vec<BundleFileVariant>) -> Result<Self> {
let mut map: HashMap<String, HashMap<Language, String>> = HashMap::new();
for (i, variant) in variants.iter().enumerate() {
let _span = tracing::trace_span!("variant {}", i);
let mut r = Cursor::new(variant.data());
let _header = r.read_u32()?;
let count = r.read_u32()? as usize;
for _ in 0..count {
let name = ctx.lookup_hash_short(r.read_u32()?, HashGroup::Strings);
let address = r.read_u32()? as u64;
let pos = r.position();
r.set_position(address);
let s = read_string(&mut r)?;
r.set_position(pos);
map.entry(name)
.or_default()
.insert(Language::Unnamed(variant.kind()), s);
}
}
Ok(Self(map))
}
#[tracing::instrument(skip_all)]
pub fn to_sjson(&self) -> Result<String> {
serde_sjson::to_string(&self.0).map_err(Report::new)
}
}
#[tracing::instrument(skip_all)]
pub fn decompile(ctx: &crate::Context, variants: &Vec<BundleFileVariant>) -> Result<Vec<UserFile>> {
let strings = Strings::from_variants(ctx, variants)?;
let content = strings.to_sjson()?;
Ok(vec![UserFile::new(content.into_bytes())])
}

View file

@ -11,6 +11,7 @@ use super::{murmurhash64, Murmur32, Murmur64, SEED};
pub enum HashGroup {
Filename,
Filetype,
Strings,
Other,
}
@ -25,6 +26,7 @@ impl std::fmt::Display for HashGroup {
match self {
HashGroup::Filename => write!(f, "filename"),
HashGroup::Filetype => write!(f, "filetype"),
HashGroup::Strings => write!(f, "strings"),
HashGroup::Other => write!(f, "other"),
}
}