mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-13 07:06:16 +00:00
Updates for build system integration
- Use a config file as input to `dol split` - Add depfile output - Adjust splits config writing
This commit is contained in:
117
src/cmd/dol.rs
117
src/cmd/dol.rs
@@ -8,6 +8,7 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use argp::FromArgs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
analysis::{
|
||||
@@ -24,6 +25,7 @@ use crate::{
|
||||
util::{
|
||||
asm::write_asm,
|
||||
config::{apply_splits, parse_symbol_line, write_splits, write_symbols},
|
||||
dep::DepFile,
|
||||
dol::process_dol,
|
||||
elf::{process_elf, write_elf},
|
||||
file::{map_file, map_reader, touch},
|
||||
@@ -60,25 +62,45 @@ pub struct InfoArgs {
|
||||
#[argp(subcommand, name = "split")]
|
||||
pub struct SplitArgs {
|
||||
#[argp(positional)]
|
||||
/// input file
|
||||
in_file: PathBuf,
|
||||
/// input configuration file
|
||||
config: PathBuf,
|
||||
#[argp(positional)]
|
||||
/// output directory
|
||||
out_dir: PathBuf,
|
||||
#[argp(option, short = 's')]
|
||||
/// path to symbols file
|
||||
symbols_file: Option<PathBuf>,
|
||||
#[argp(option, short = 'p')]
|
||||
/// path to splits file
|
||||
splits_file: Option<PathBuf>,
|
||||
#[argp(option, short = 'e')]
|
||||
/// ELF file to validate against (debugging only)
|
||||
elf_file: Option<PathBuf>,
|
||||
#[argp(switch)]
|
||||
/// skip updating splits & symbol files (for build systems)
|
||||
no_update: bool,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bool_true() -> bool { true }
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ProjectConfig {
|
||||
pub object: PathBuf,
|
||||
pub splits: Option<PathBuf>,
|
||||
pub symbols: Option<PathBuf>,
|
||||
// Analysis options
|
||||
#[serde(default = "bool_true")]
|
||||
pub detect_objects: bool,
|
||||
#[serde(default = "bool_true")]
|
||||
pub detect_strings: bool,
|
||||
#[serde(default = "bool_true")]
|
||||
pub write_asm: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct OutputUnit {
|
||||
pub object: PathBuf,
|
||||
pub name: String,
|
||||
pub autogenerated: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct OutputConfig {
|
||||
pub units: Vec<OutputUnit>,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Info(c_args) => info(c_args),
|
||||
@@ -128,10 +150,20 @@ fn info(args: InfoArgs) -> Result<()> {
|
||||
}
|
||||
|
||||
fn split(args: SplitArgs) -> Result<()> {
|
||||
log::info!("Loading {}", args.in_file.display());
|
||||
let mut obj = process_dol(&args.in_file)?;
|
||||
log::info!("Loading {}", args.config.display());
|
||||
let mut config_file = File::open(&args.config)
|
||||
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
|
||||
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
|
||||
|
||||
if let Some(splits_path) = &args.splits_file {
|
||||
let out_config_path = args.out_dir.join("config.json");
|
||||
let mut dep = DepFile::new(out_config_path.clone());
|
||||
|
||||
log::info!("Loading {}", config.object.display());
|
||||
let mut obj = process_dol(&config.object)?;
|
||||
dep.push(config.object.clone());
|
||||
|
||||
if let Some(splits_path) = &config.splits {
|
||||
dep.push(splits_path.clone());
|
||||
if splits_path.is_file() {
|
||||
let map = map_file(splits_path)?;
|
||||
apply_splits(map_reader(&map), &mut obj)?;
|
||||
@@ -140,7 +172,8 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
|
||||
let mut state = AnalyzerState::default();
|
||||
|
||||
if let Some(symbols_path) = &args.symbols_file {
|
||||
if let Some(symbols_path) = &config.symbols {
|
||||
dep.push(symbols_path.clone());
|
||||
if symbols_path.is_file() {
|
||||
let map = map_file(symbols_path)?;
|
||||
for result in map_reader(&map).lines() {
|
||||
@@ -176,14 +209,21 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
log::info!("Applying relocations");
|
||||
tracker.apply(&mut obj, false)?;
|
||||
|
||||
log::info!("Detecting object boundaries");
|
||||
detect_object_boundaries(&mut obj)?;
|
||||
if config.detect_objects {
|
||||
log::info!("Detecting object boundaries");
|
||||
detect_object_boundaries(&mut obj)?;
|
||||
}
|
||||
|
||||
log::info!("Detecting strings");
|
||||
detect_strings(&mut obj)?;
|
||||
if config.detect_strings {
|
||||
log::info!("Detecting strings");
|
||||
detect_strings(&mut obj)?;
|
||||
}
|
||||
|
||||
log::info!("Adjusting splits");
|
||||
update_splits(&mut obj)?;
|
||||
|
||||
if !args.no_update {
|
||||
if let Some(symbols_path) = &args.symbols_file {
|
||||
if let Some(symbols_path) = &config.symbols {
|
||||
let mut symbols_writer = BufWriter::new(
|
||||
File::create(symbols_path)
|
||||
.with_context(|| format!("Failed to create '{}'", symbols_path.display()))?,
|
||||
@@ -191,7 +231,7 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
write_symbols(&mut symbols_writer, &obj)?;
|
||||
}
|
||||
|
||||
if let Some(splits_path) = &args.splits_file {
|
||||
if let Some(splits_path) = &config.splits {
|
||||
let mut splits_writer = BufWriter::new(
|
||||
File::create(splits_path)
|
||||
.with_context(|| format!("Failed to create '{}'", splits_path.display()))?,
|
||||
@@ -200,9 +240,6 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Adjusting splits");
|
||||
update_splits(&mut obj)?;
|
||||
|
||||
log::info!("Splitting {} objects", obj.link_order.len());
|
||||
let split_objs = split_obj(&obj)?;
|
||||
|
||||
@@ -224,13 +261,17 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
};
|
||||
}
|
||||
|
||||
let mut units_file = BufWriter::new(File::create(args.out_dir.join("units.txt"))?);
|
||||
let mut out_config = OutputConfig::default();
|
||||
for unit in &obj.link_order {
|
||||
let object = file_map
|
||||
.get(unit)
|
||||
.ok_or_else(|| anyhow!("Failed to find object file for unit '{unit}'"))?;
|
||||
let out_path = obj_dir.join(obj_path_for_unit(unit));
|
||||
writeln!(units_file, "{}:{}", out_path.display(), unit)?;
|
||||
out_config.units.push(OutputUnit {
|
||||
object: out_path.clone(),
|
||||
name: unit.clone(),
|
||||
autogenerated: false,
|
||||
});
|
||||
if let Some(parent) = out_path.parent() {
|
||||
DirBuilder::new().recursive(true).create(parent)?;
|
||||
}
|
||||
@@ -239,7 +280,11 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
file.write_all(object)?;
|
||||
file.flush()?;
|
||||
}
|
||||
units_file.flush()?;
|
||||
{
|
||||
let mut out_file = BufWriter::new(File::create(&out_config_path)?);
|
||||
serde_json::to_writer_pretty(&mut out_file, &out_config)?;
|
||||
out_file.flush()?;
|
||||
}
|
||||
|
||||
// Generate ldscript.lcf
|
||||
fs::write(args.out_dir.join("ldscript.lcf"), generate_ldscript(&obj)?)?;
|
||||
@@ -256,14 +301,26 @@ fn split(args: SplitArgs) -> Result<()> {
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
// (debugging) validate against ELF
|
||||
if let Some(file) = &args.elf_file {
|
||||
validate(&obj, file, &state)?;
|
||||
// Write dep file
|
||||
{
|
||||
let dep_path = args.out_dir.join("dep");
|
||||
let mut dep_file = BufWriter::new(
|
||||
File::create(&dep_path)
|
||||
.with_context(|| format!("Failed to create dep file '{}'", dep_path.display()))?,
|
||||
);
|
||||
dep.write(&mut dep_file)?;
|
||||
dep_file.flush()?;
|
||||
}
|
||||
|
||||
// (debugging) validate against ELF
|
||||
// if let Some(file) = &args.elf_file {
|
||||
// validate(&obj, file, &state)?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -> Result<()> {
|
||||
let real_obj = process_elf(elf_file)?;
|
||||
for real_section in &real_obj.sections {
|
||||
|
||||
Reference in New Issue
Block a user