Skip to content

Commit 146c4d2

Browse files
committed
Add header_type and custom_type to extract config
Extract configuration is now emitted in the output config, so tooling can load and perform their own tasks on extracted assets without having to parse YAML. `header_type`: - `symbol` (default): Emit a full symbol declaration. - `raw`: Emit raw array data (for wrapping in your own declaration) - `none`: Don't emit a header at all. (For custom processing) `custom_type`/`custom_data`: Passed through to the output config as-is for custom tasks.
1 parent f984bc3 commit 146c4d2

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
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 <luke@street.dev>"]
55
license = "MIT OR Apache-2.0"
6-
version = "1.1.4"
6+
version = "1.2.0"
77
edition = "2021"
88
publish = false
99
repository = "https://github.com/encounter/decomp-toolkit"

src/cmd/dol.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
fs::DirBuilder,
66
io::{Cursor, Seek, Write},
77
mem::take,
8+
str::FromStr,
89
time::Instant,
910
};
1011

@@ -36,7 +37,7 @@ use crate::{
3637
},
3738
util::{
3839
asm::write_asm,
39-
bin2c::bin2c,
40+
bin2c::{bin2c, HeaderKind},
4041
comment::MWComment,
4142
config::{
4243
apply_splits_file, apply_symbols_file, is_auto_symbol, signed_hex_serde,
@@ -303,6 +304,20 @@ pub struct ExtractConfig {
303304
/// Path is relative to `out_dir/include`.
304305
#[serde(with = "unix_path_serde_option", default, skip_serializing_if = "Option::is_none")]
305306
pub header: Option<Utf8UnixPathBuf>,
307+
/// The type for the extracted symbol in the header file. By default, the header will emit
308+
/// a full symbol declaration (a.k.a. `symbol`), but this can be set to `raw` to emit the raw
309+
/// data as a byte array. `none` avoids emitting a header entirely, in which case the `header`
310+
/// field can be used by external asset processing.
311+
#[serde(default, skip_serializing_if = "Option::is_none")]
312+
pub header_type: Option<String>,
313+
/// A user-defined type for use with external asset processing. This value is simply passed
314+
/// through to the `custom_type` field in the output config.
315+
#[serde(default, skip_serializing_if = "Option::is_none")]
316+
pub custom_type: Option<String>,
317+
/// User-defined data for use with external asset processing. This value is simply passed
318+
/// through to the `custom_data` field in the output config.
319+
#[serde(default, skip_serializing_if = "Option::is_none")]
320+
pub custom_data: Option<serde_json::Value>,
306321
}
307322

308323
/// A relocation that should be blocked.
@@ -364,6 +379,19 @@ pub struct OutputModule {
364379
pub ldscript: Utf8UnixPathBuf,
365380
pub entry: Option<String>,
366381
pub units: Vec<OutputUnit>,
382+
pub extract: Vec<OutputExtract>,
383+
}
384+
385+
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
386+
pub struct OutputExtract {
387+
pub symbol: String,
388+
#[serde(with = "unix_path_serde_option")]
389+
pub binary: Option<Utf8UnixPathBuf>,
390+
#[serde(with = "unix_path_serde_option")]
391+
pub header: Option<Utf8UnixPathBuf>,
392+
pub header_type: String,
393+
pub custom_type: Option<String>,
394+
pub custom_data: Option<serde_json::Value>,
367395
}
368396

369397
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Hash)]
@@ -938,6 +966,7 @@ fn split_write_obj(
938966
ldscript: out_dir.join("ldscript.lcf").with_unix_encoding(),
939967
units: Vec::with_capacity(split_objs.len()),
940968
entry,
969+
extract: Vec::with_capacity(module.config.extract.len()),
941970
};
942971
for (unit, split_obj) in module.obj.link_order.iter().zip(&split_objs) {
943972
let out_obj = write_elf(split_obj, config.export_all)?;
@@ -975,14 +1004,34 @@ fn split_write_obj(
9751004
write_if_changed(&out_path, data)?;
9761005
}
9771006

978-
if let Some(header) = &extract.header {
979-
let header_string = bin2c(symbol, section, data);
980-
let out_path = base_dir.join("include").join(header.with_encoding());
981-
if let Some(parent) = out_path.parent() {
982-
DirBuilder::new().recursive(true).create(parent)?;
1007+
let header_kind = match extract.header_type.as_deref() {
1008+
Some(value) => match HeaderKind::from_str(value) {
1009+
Ok(kind) => kind,
1010+
Err(()) => bail!("Invalid header type '{}'", value),
1011+
},
1012+
_ => HeaderKind::Symbol,
1013+
};
1014+
1015+
if header_kind != HeaderKind::None {
1016+
if let Some(header) = &extract.header {
1017+
let header_string = bin2c(symbol, section, data, header_kind);
1018+
let out_path = base_dir.join("include").join(header.with_encoding());
1019+
if let Some(parent) = out_path.parent() {
1020+
DirBuilder::new().recursive(true).create(parent)?;
1021+
}
1022+
write_if_changed(&out_path, header_string.as_bytes())?;
9831023
}
984-
write_if_changed(&out_path, header_string.as_bytes())?;
9851024
}
1025+
1026+
// Copy to output config
1027+
out_config.extract.push(OutputExtract {
1028+
symbol: symbol.name.clone(),
1029+
binary: extract.binary.clone(),
1030+
header: extract.header.clone(),
1031+
header_type: header_kind.to_string(),
1032+
custom_type: extract.custom_type.clone(),
1033+
custom_data: extract.custom_data.clone(),
1034+
});
9861035
}
9871036

9881037
// Generate ldscript.lcf

src/util/bin2c.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::{fmt, str::FromStr};
2+
13
use crate::obj::{ObjSection, ObjSectionKind, ObjSymbol};
24

35
const PROLOGUE: &str = r#"
@@ -13,8 +15,50 @@ const PROLOGUE: &str = r#"
1315
1416
"#;
1517

18+
/// The output header type.
19+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20+
pub enum HeaderKind {
21+
/// Do not generate a header. (Used for custom processing)
22+
None,
23+
/// A full symbol definition.
24+
Symbol,
25+
/// Raw array data.
26+
Raw,
27+
}
28+
29+
impl FromStr for HeaderKind {
30+
type Err = ();
31+
32+
fn from_str(s: &str) -> Result<Self, Self::Err> {
33+
match s {
34+
"none" => Ok(Self::None),
35+
"symbol" => Ok(Self::Symbol),
36+
"raw" => Ok(Self::Raw),
37+
_ => Err(()),
38+
}
39+
}
40+
}
41+
42+
impl fmt::Display for HeaderKind {
43+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44+
match self {
45+
Self::None => write!(f, "none"),
46+
Self::Symbol => write!(f, "symbol"),
47+
Self::Raw => write!(f, "raw"),
48+
}
49+
}
50+
}
51+
1652
/// Converts a binary blob into a C array.
17-
pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
53+
pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8], kind: HeaderKind) -> String {
54+
match kind {
55+
HeaderKind::None => String::new(),
56+
HeaderKind::Symbol => bin2c_symbol(symbol, section, data),
57+
HeaderKind::Raw => bin2c_raw(data),
58+
}
59+
}
60+
61+
fn bin2c_symbol(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
1862
let mut output = String::new();
1963
output.push_str(PROLOGUE);
2064
output.push_str(&format!(
@@ -41,3 +85,19 @@ pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
4185
output.push_str("\n};\n");
4286
output
4387
}
88+
89+
fn bin2c_raw(data: &[u8]) -> String {
90+
let mut output = String::new();
91+
for (i, byte) in data.iter().enumerate() {
92+
if i > 0 {
93+
if i % 16 == 0 {
94+
output.push('\n');
95+
} else {
96+
output.push(' ');
97+
}
98+
}
99+
output.push_str(&format!("0x{:02X},", byte));
100+
}
101+
output.push('\n');
102+
output
103+
}

0 commit comments

Comments
 (0)