feat: Implement decompilation for strings file type
This commit is contained in:
parent
5cc97959a5
commit
2219f4fab3
6 changed files with 105 additions and 0 deletions
7
CHANGELOG.adoc
Normal file
7
CHANGELOG.adoc
Normal file
|
@ -0,0 +1,7 @@
|
|||
= Changelog
|
||||
|
||||
== Unreleased
|
||||
|
||||
=== Added
|
||||
|
||||
- implement decompilation for `.strings` files
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod lua;
|
||||
pub mod package;
|
||||
pub mod strings;
|
||||
|
|
84
lib/sdk/src/filetype/strings.rs
Normal file
84
lib/sdk/src/filetype/strings.rs
Normal 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())])
|
||||
}
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue