Skip to content

Commit

Permalink
Add header_type and custom_type to extract config
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
encounter committed Oct 28, 2024
1 parent f984bc3 commit 146c4d2
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <[email protected]>"]
license = "MIT OR Apache-2.0"
version = "1.1.4"
version = "1.2.0"
edition = "2021"
publish = false
repository = "https://github.com/encounter/decomp-toolkit"
Expand Down
63 changes: 56 additions & 7 deletions src/cmd/dol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
fs::DirBuilder,
io::{Cursor, Seek, Write},
mem::take,
str::FromStr,
time::Instant,
};

Expand Down Expand Up @@ -36,7 +37,7 @@ use crate::{
},
util::{
asm::write_asm,
bin2c::bin2c,
bin2c::{bin2c, HeaderKind},
comment::MWComment,
config::{
apply_splits_file, apply_symbols_file, is_auto_symbol, signed_hex_serde,
Expand Down Expand Up @@ -303,6 +304,20 @@ pub struct ExtractConfig {
/// Path is relative to `out_dir/include`.
#[serde(with = "unix_path_serde_option", default, skip_serializing_if = "Option::is_none")]
pub header: Option<Utf8UnixPathBuf>,
/// The type for the extracted symbol in the header file. By default, the header will emit
/// a full symbol declaration (a.k.a. `symbol`), but this can be set to `raw` to emit the raw
/// data as a byte array. `none` avoids emitting a header entirely, in which case the `header`
/// field can be used by external asset processing.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub header_type: Option<String>,
/// A user-defined type for use with external asset processing. This value is simply passed
/// through to the `custom_type` field in the output config.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_type: Option<String>,
/// User-defined data for use with external asset processing. This value is simply passed
/// through to the `custom_data` field in the output config.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_data: Option<serde_json::Value>,
}

/// A relocation that should be blocked.
Expand Down Expand Up @@ -364,6 +379,19 @@ pub struct OutputModule {
pub ldscript: Utf8UnixPathBuf,
pub entry: Option<String>,
pub units: Vec<OutputUnit>,
pub extract: Vec<OutputExtract>,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OutputExtract {
pub symbol: String,
#[serde(with = "unix_path_serde_option")]
pub binary: Option<Utf8UnixPathBuf>,
#[serde(with = "unix_path_serde_option")]
pub header: Option<Utf8UnixPathBuf>,
pub header_type: String,
pub custom_type: Option<String>,
pub custom_data: Option<serde_json::Value>,
}

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

if let Some(header) = &extract.header {
let header_string = bin2c(symbol, section, data);
let out_path = base_dir.join("include").join(header.with_encoding());
if let Some(parent) = out_path.parent() {
DirBuilder::new().recursive(true).create(parent)?;
let header_kind = match extract.header_type.as_deref() {
Some(value) => match HeaderKind::from_str(value) {
Ok(kind) => kind,
Err(()) => bail!("Invalid header type '{}'", value),
},
_ => HeaderKind::Symbol,
};

if header_kind != HeaderKind::None {
if let Some(header) = &extract.header {
let header_string = bin2c(symbol, section, data, header_kind);
let out_path = base_dir.join("include").join(header.with_encoding());
if let Some(parent) = out_path.parent() {
DirBuilder::new().recursive(true).create(parent)?;
}
write_if_changed(&out_path, header_string.as_bytes())?;
}
write_if_changed(&out_path, header_string.as_bytes())?;
}

// Copy to output config
out_config.extract.push(OutputExtract {
symbol: symbol.name.clone(),
binary: extract.binary.clone(),
header: extract.header.clone(),
header_type: header_kind.to_string(),
custom_type: extract.custom_type.clone(),
custom_data: extract.custom_data.clone(),
});
}

// Generate ldscript.lcf
Expand Down
62 changes: 61 additions & 1 deletion src/util/bin2c.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{fmt, str::FromStr};

use crate::obj::{ObjSection, ObjSectionKind, ObjSymbol};

const PROLOGUE: &str = r#"
Expand All @@ -13,8 +15,50 @@ const PROLOGUE: &str = r#"
"#;

/// The output header type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HeaderKind {
/// Do not generate a header. (Used for custom processing)
None,
/// A full symbol definition.
Symbol,
/// Raw array data.
Raw,
}

impl FromStr for HeaderKind {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"none" => Ok(Self::None),
"symbol" => Ok(Self::Symbol),
"raw" => Ok(Self::Raw),
_ => Err(()),
}
}
}

impl fmt::Display for HeaderKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::Symbol => write!(f, "symbol"),
Self::Raw => write!(f, "raw"),
}
}
}

/// Converts a binary blob into a C array.
pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8], kind: HeaderKind) -> String {
match kind {
HeaderKind::None => String::new(),
HeaderKind::Symbol => bin2c_symbol(symbol, section, data),
HeaderKind::Raw => bin2c_raw(data),
}
}

fn bin2c_symbol(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
let mut output = String::new();
output.push_str(PROLOGUE);
output.push_str(&format!(
Expand All @@ -41,3 +85,19 @@ pub fn bin2c(symbol: &ObjSymbol, section: &ObjSection, data: &[u8]) -> String {
output.push_str("\n};\n");
output
}

fn bin2c_raw(data: &[u8]) -> String {
let mut output = String::new();
for (i, byte) in data.iter().enumerate() {
if i > 0 {
if i % 16 == 0 {
output.push('\n');
} else {
output.push(' ');
}
}
output.push_str(&format!("0x{:02X},", byte));
}
output.push('\n');
output
}

0 comments on commit 146c4d2

Please sign in to comment.