Skip to content

Commit dd60128

Browse files
committed
Extract embedded assets to binary and C header
Adds an "extract" list to project configuration: ``` extract: - symbol: SomeData binary: Lib/SomeData.bin header: Lib/SomeData.inc ``` This example extracts the data of symbol `SomeData` to `out_dir/bin/Lib/SomeData.bin`, and a C array representation to `out_dir/include/Lib/SomeData.inc`. Resolves #11
1 parent 038354a commit dd60128

File tree

5 files changed

+90
-8
lines changed

5 files changed

+90
-8
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "decomp-toolkit"
33
description = "Yet another GameCube/Wii decompilation toolkit."
44
authors = ["Luke Street <[email protected]>"]
55
license = "MIT OR Apache-2.0"
6-
version = "0.6.1"
6+
version = "0.6.2"
77
edition = "2021"
88
publish = false
99
repository = "https://github.com/encounter/decomp-toolkit"

src/cmd/dol.rs

+58-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::{
3737
},
3838
util::{
3939
asm::write_asm,
40+
bin2c::bin2c,
4041
comment::MWComment,
4142
config::{
4243
apply_splits_file, apply_symbols_file, is_auto_symbol, write_splits_file,
@@ -252,6 +253,22 @@ pub struct ModuleConfig {
252253
/// Overrides links to other modules.
253254
#[serde(skip_serializing_if = "is_default")]
254255
pub links: Option<Vec<String>>,
256+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
257+
pub extract: Vec<ExtractConfig>,
258+
}
259+
260+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
261+
pub struct ExtractConfig {
262+
/// The name of the symbol to extract.
263+
pub symbol: String,
264+
/// If specified, the symbol's data will be extracted to the given file.
265+
/// Path is relative to `out_dir/bin`.
266+
#[serde(with = "path_slash_serde_option", default, skip_serializing_if = "Option::is_none")]
267+
pub binary: Option<PathBuf>,
268+
/// If specified, the symbol's data will be extracted to the given file as a C array.
269+
/// Path is relative to `out_dir/include`.
270+
#[serde(with = "path_slash_serde_option", default, skip_serializing_if = "Option::is_none")]
271+
pub header: Option<PathBuf>,
255272
}
256273

257274
impl ModuleConfig {
@@ -776,6 +793,7 @@ fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
776793
fn split_write_obj(
777794
module: &mut ModuleInfo,
778795
config: &ProjectConfig,
796+
base_dir: &Path,
779797
out_dir: &Path,
780798
no_update: bool,
781799
) -> Result<OutputModule> {
@@ -856,6 +874,37 @@ fn split_write_obj(
856874
write_if_changed(&out_path, &out_obj)?;
857875
}
858876

877+
// Write extracted files
878+
for extract in &module.config.extract {
879+
let (_, symbol) = module
880+
.obj
881+
.symbols
882+
.by_name(&extract.symbol)?
883+
.with_context(|| format!("Failed to locate symbol '{}'", extract.symbol))?;
884+
let section_index = symbol
885+
.section
886+
.with_context(|| format!("Symbol '{}' has no section", extract.symbol))?;
887+
let section = &module.obj.sections[section_index];
888+
let data = section.symbol_data(symbol)?;
889+
890+
if let Some(binary) = &extract.binary {
891+
let out_path = base_dir.join("bin").join(binary);
892+
if let Some(parent) = out_path.parent() {
893+
DirBuilder::new().recursive(true).create(parent)?;
894+
}
895+
write_if_changed(&out_path, data)?;
896+
}
897+
898+
if let Some(header) = &extract.header {
899+
let header_string = bin2c(symbol, section, data);
900+
let out_path = base_dir.join("include").join(header);
901+
if let Some(parent) = out_path.parent() {
902+
DirBuilder::new().recursive(true).create(parent)?;
903+
}
904+
write_if_changed(&out_path, header_string.as_bytes())?;
905+
}
906+
}
907+
859908
// Generate ldscript.lcf
860909
let ldscript_template = if let Some(template) = &module.config.ldscript_template {
861910
Some(fs::read_to_string(template).with_context(|| {
@@ -894,7 +943,8 @@ fn write_if_changed(path: &Path, contents: &[u8]) -> Result<()> {
894943
return Ok(());
895944
}
896945
}
897-
fs::write(path, contents)?;
946+
fs::write(path, contents)
947+
.with_context(|| format!("Failed to write file '{}'", path.display()))?;
898948
Ok(())
899949
}
900950

@@ -1134,15 +1184,14 @@ fn split(args: SplitArgs) -> Result<()> {
11341184
let _span =
11351185
info_span!("module", name = %config.base.name(), id = dol.obj.module_id).entered();
11361186
dol_result = Some(
1137-
split_write_obj(&mut dol, &config, &args.out_dir, args.no_update).with_context(
1138-
|| {
1187+
split_write_obj(&mut dol, &config, &args.out_dir, &args.out_dir, args.no_update)
1188+
.with_context(|| {
11391189
format!(
11401190
"While processing object '{}' (module ID {})",
11411191
config.base.file_name(),
11421192
dol.obj.module_id
11431193
)
1144-
},
1145-
),
1194+
}),
11461195
);
11471196
});
11481197
// Modules
@@ -1155,7 +1204,7 @@ fn split(args: SplitArgs) -> Result<()> {
11551204
info_span!("module", name = %module.config.name(), id = module.obj.module_id)
11561205
.entered();
11571206
let out_dir = args.out_dir.join(module.config.name().as_ref());
1158-
split_write_obj(module, &config, &out_dir, args.no_update).with_context(
1207+
split_write_obj(module, &config, &args.out_dir, &out_dir, args.no_update).with_context(
11591208
|| {
11601209
format!(
11611210
"While processing object '{}' (module {} ID {})",
@@ -1697,6 +1746,7 @@ fn config(args: ConfigArgs) -> Result<()> {
16971746
force_active: vec![],
16981747
ldscript_template: None,
16991748
links: None,
1749+
extract: vec![],
17001750
},
17011751
selfile: None,
17021752
selfile_hash: None,
@@ -1733,6 +1783,7 @@ fn config(args: ConfigArgs) -> Result<()> {
17331783
force_active: vec![],
17341784
ldscript_template: None,
17351785
links: None,
1786+
extract: vec![],
17361787
}));
17371788
}
17381789
Some(ext) if ext.eq_ignore_ascii_case(OsStr::new("sel")) => {
@@ -1750,6 +1801,7 @@ fn config(args: ConfigArgs) -> Result<()> {
17501801
force_active: vec![],
17511802
ldscript_template: None,
17521803
links: None,
1804+
extract: vec![],
17531805
});
17541806
}
17551807
_ => bail!("Unknown file extension: '{}'", path.display()),

src/util/bin2c.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::obj::{ObjSection, ObjSectionKind, ObjSymbol};
2+
3+
/// Converts a binary blob into a C array.
4+
pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
5+
let mut output = String::new();
6+
output.push_str(&format!(
7+
"// {} (size: {:#X}, address: {:#X}, section: {})\n",
8+
symbol.name, symbol.size, symbol.address, section.name
9+
));
10+
if symbol.flags.is_local() {
11+
output.push_str("static ");
12+
}
13+
if section.kind == ObjSectionKind::ReadOnlyData {
14+
output.push_str("const ");
15+
}
16+
output.push_str("unsigned char ");
17+
output.push_str(symbol.demangled_name.as_deref().unwrap_or(symbol.name.as_str()));
18+
output.push_str("[] = {");
19+
for (i, byte) in data.iter().enumerate() {
20+
if i % 16 == 0 {
21+
output.push_str("\n ");
22+
} else {
23+
output.push(' ');
24+
}
25+
output.push_str(&format!("0x{:02X},", byte));
26+
}
27+
output.push_str("\n};\n");
28+
output
29+
}

src/util/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{borrow::Cow, ops::Deref};
22

33
pub mod alf;
44
pub mod asm;
5+
pub mod bin2c;
56
pub mod comment;
67
pub mod config;
78
pub mod dep;

0 commit comments

Comments
 (0)