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 {
|
pub enum HashGroup {
|
||||||
Filename,
|
Filename,
|
||||||
Filetype,
|
Filetype,
|
||||||
|
Strings,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ impl From<HashGroup> for sdk::murmur::HashGroup {
|
||||||
match value {
|
match value {
|
||||||
HashGroup::Filename => sdk::murmur::HashGroup::Filename,
|
HashGroup::Filename => sdk::murmur::HashGroup::Filename,
|
||||||
HashGroup::Filetype => sdk::murmur::HashGroup::Filetype,
|
HashGroup::Filetype => sdk::murmur::HashGroup::Filetype,
|
||||||
|
HashGroup::Strings => sdk::murmur::HashGroup::Strings,
|
||||||
HashGroup::Other => sdk::murmur::HashGroup::Other,
|
HashGroup::Other => sdk::murmur::HashGroup::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,6 +359,10 @@ impl BundleFileVariant {
|
||||||
self.header.size
|
self.header.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> u32 {
|
||||||
|
self.header.variant
|
||||||
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
&self.data
|
&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 tasks = self.variants.iter().map(|variant| {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod lua;
|
pub mod lua;
|
||||||
pub mod package;
|
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 {
|
pub enum HashGroup {
|
||||||
Filename,
|
Filename,
|
||||||
Filetype,
|
Filetype,
|
||||||
|
Strings,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ impl std::fmt::Display for HashGroup {
|
||||||
match self {
|
match self {
|
||||||
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::Other => write!(f, "other"),
|
HashGroup::Other => write!(f, "other"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue