dw2003's executable is now built from assembly.

This commit is contained in:
Filipe Rodrigues 2022-10-24 05:20:58 +01:00
parent b4029f4d01
commit 21930dc8de
12 changed files with 230741 additions and 7 deletions

13
.vscode/settings.json vendored
View File

@ -1,4 +1,13 @@
{
"cSpell.words": ["bitflags", "byteorder", "cdrom", "seeked", "thiserror"],
"python.formatting.provider": "yapf"
"cSpell.words": [
"bitflags",
"byteorder",
"cdrom",
"psexe",
"seeked",
"thiserror"
],
"python.formatting.provider": "yapf",
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.linkedProjects": ["tools/Cargo.toml"]
}

View File

@ -8,7 +8,7 @@
<directory_tree>
<file name="SYSTEM.CNF" type="data" source="dw2003/SYSTEM.CNF" />
<file name="SLES_039.36" type="data" source="dw2003/SLES_039.36" />
<file name="SLES_039.36" type="data" source="build/psexe/dw2003/SLES_039.36" />
<dir name="AAA">
<dir name="PRO">

BIN
dw2003/SLES_039.36 (Stored with Git LFS)

Binary file not shown.

8
dw2003/SLES_039.36.ld Normal file
View File

@ -0,0 +1,8 @@
SECTIONS {
_.text = 0x80010000;
.text _.text : {
KEEP(*(.text));
}
}
ENTRY(start)

230419
dw2003/SLES_039.36.s Normal file

File diff suppressed because it is too large Load Diff

2
tools/.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[build]
dep-info-basedir = ".."

49
tools/Cargo.lock generated
View File

@ -143,6 +143,18 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "ddw3-mkpsexe"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"clap",
"ddw3-logger",
"goblin",
"tracing",
]
[[package]]
name = "ddw3-uncd"
version = "0.1.0"
@ -208,6 +220,17 @@ dependencies = [
"syn",
]
[[package]]
name = "goblin"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -318,6 +341,12 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -419,6 +448,26 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "semver"
version = "1.0.14"

View File

@ -9,6 +9,9 @@ members = [
"ddw3-iso9660",
"ddw3-uniso",
# Psexe
"ddw3-mkpsexe",
# Util
"ddw3-bytes",
"ddw3-util",

View File

@ -0,0 +1,24 @@
[package]
edition = "2021"
name = "ddw3-mkpsexe"
version = "0.1.0"
[dependencies]
# Ddw3
ddw3-logger = { path = "../ddw3-logger" }
# Cmd
clap = { version = "4.0.11", features = ["derive"] }
# Logging
tracing = "0.1.37"
# Error handling
anyhow = "1.0.65"
# Elf
goblin = "0.5.4"
# Util
byteorder = "1.4.3"

View File

@ -0,0 +1,20 @@
//! Arguments
// Imports
use std::path::PathBuf;
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(clap::Parser)]
#[clap(author, version, about)]
pub struct Args {
/// Input file
pub input_file: PathBuf,
/// Output file
#[clap(long = "output", short = 'o')]
pub output_file: PathBuf,
/// License file
#[clap(long = "license")]
pub license_file: PathBuf,
}

View File

@ -0,0 +1,133 @@
//! `.psexe` packer
// Features
#![feature(seek_stream_len)]
// Modules
mod args;
// Imports
use {
anyhow::Context,
args::Args,
byteorder::{ByteOrder, LittleEndian},
clap::Parser,
goblin::Object,
std::{
fs,
io::{Seek, SeekFrom, Write},
},
};
fn main() -> Result<(), anyhow::Error> {
// Initialize the logger
ddw3_logger::init().context("Unable to initialize logger")?;
// Get all args
let args = Args::parse();
tracing::trace!(?args, "Arguments");
// Open the input file, parse it, and then get it as an elf
let input_contents = fs::read(args.input_file).context("Unable to read input file")?;
let object = Object::parse(&input_contents).context("Unable to parse input file")?;
let elf = match object {
Object::Elf(elf) => elf,
object => anyhow::bail!("Expected elf input file, found {object:?}"),
};
// Find the `.text` section
let text_header = elf
.section_headers
.iter()
.find(|section| {
let name = match elf.shdr_strtab.get_at(section.sh_name) {
Some(name) => name,
None => {
tracing::warn!("Skipping section with invalid name {section:?}");
return false;
},
};
name == ".text"
})
.context("Unable to find `.text` section")?;
tracing::trace!(?text_header);
// Get the contents
let text_range = text_header
.file_range()
.context("Unable to get `.text` section's span")?;
let text = &input_contents[text_range];
let pc0 = elf.entry.try_into().context("Start address didn't fit into a `u32`")?;
let text_base = text_header
.sh_addr
.try_into()
.context("`.text` section address didn't fit into a `u32`")?;
tracing::trace!(pc0 = format!("{pc0:#x}"), text_base = format!("{text_base:#x}"));
// Create the output file
let mut output_file = fs::File::create(&args.output_file).context("Unable to create output file")?;
// Skip the header
output_file
.seek(SeekFrom::Current(0x800))
.context("Unable to seek past header")?;
// Write all content and pad to `0x800`
output_file.write_all(text).context("Unable to write all data")?;
let file_size = output_file
.stream_len()
.context("Unable to get output file length")?
.checked_sub(0x800)
.context("Reported output file size was less than `0x800` after writing header & data")?;
let file_size = match file_size % 0x800 == 0 {
true => file_size,
false => {
let new_file_size = (file_size + 0x7ff) / 0x800 * 0x800;
tracing::debug!("Padding output file of length {file_size:#x} to {new_file_size:#x}");
output_file
.set_len(new_file_size)
.context("Unable to pad output file")?;
new_file_size
},
};
let file_size = file_size.try_into().context("File size didn't fit into a `u32`")?;
tracing::trace!(file_size = format!("{file_size:#x}"));
// Then go back and write the header
output_file.rewind().context("Unable to rewind output file")?;
let license = fs::read(args.license_file).context("Unable to read license file")?;
self::write_header(&mut output_file, pc0, text_base, file_size, &license)
.context("Unable to write output file header")?;
Ok(())
}
/// Writes the output file header
fn write_header(
output_file: &mut fs::File,
pc0: u32,
text_base: u32,
file_size: u32,
license_text: &[u8],
) -> Result<(), anyhow::Error> {
let mut header = [0; 0x800];
header[0x0..0x8].copy_from_slice(b"PS-X EXE");
LittleEndian::write_u32(&mut header[0x10..0x14], pc0);
LittleEndian::write_u32(&mut header[0x18..0x1c], text_base);
LittleEndian::write_u32(&mut header[0x1c..0x20], file_size);
// TODO: Hard hardcode these?
LittleEndian::write_u32(&mut header[0x30..0x34], 0x801ffff0);
LittleEndian::write_u32(&mut header[0x34..0x38], 0x0);
anyhow::ensure!(license_text.len() <= 0x800 - 0x4c, "License text is too big");
header[0x4c..0x4c + license_text.len()].copy_from_slice(license_text);
output_file
.write_all(&header)
.context("Unable to write to output file")?;
Ok(())
}

View File

@ -2,6 +2,8 @@
alias:
# External tools
# prettier-ignore
as : mips-elf-as
ld : mips-elf-ld
mkpsxiso : mkpsxiso
cargo : cargo
sha256sum: sha256sum
@ -11,13 +13,16 @@ alias:
generate_compare_deps: tools/generate_compare_deps.py
# Cargo tools
mkpsexe: $(tools_dir)/ddw3-mkpsexe
# Directories
build_dir : build
tools_dir : $(build_dir)/tools
misc_dir : $(build_dir)/misc
psx_iso_dir: $(build_dir)/iso
asm_dir : $(build_dir)/asm
elf_dir : $(build_dir)/elf
psexe_dir : $(build_dir)/psexe
# Files
checksums : checksums.sha256
@ -36,6 +41,22 @@ alias:
psx_iso_deps: $(psx_iso_dir)/^(path).d
psx_iso_xml : ^(path).xml
# Assembly
asm_obj : $(asm_dir)/^(path).o
asm_obj_deps: $(asm_dir)/^(path).d
asm_src : ^(path).s
# Elf
# TODO: Not hardcode where the object files come from
elf : $(elf_dir)/^(path).elf
elf_obj: $(asm_dir)/^(path).o
elf_ld : ^(path).ld
# Elf
# TODO: Not hardcode where the elf or license come from
psexe : $(psexe_dir)/^(path)
psexe_elf: $(elf_dir)/^(path).elf
# By default built the final `iso`s
default:
- $(dw3_psx_iso)
@ -128,3 +149,52 @@ rules:
exec:
- [touch, $(dummy_buffer)]
- [truncate, --size=35283682, $(dummy_buffer)]
# Psexe
# TODO: Hardcode less
psexe:
out: [$(psexe)]
deps:
- $(psexe_elf)
- $(license_psexe_eu)
- $(mkpsexe)
- static: $(psexe::dir_name)/
exec:
- - $(mkpsexe)
- $(psexe_elf)
- -o
- $(psexe)
- --license
- $(license_psexe_eu)
# Elf
# TODO: Hardcode les
elf:
out: [$(elf)]
deps: [$(elf_obj), $(elf_ld), static: $(elf::dir_name)/]
exec:
- - $(ld)
- $(elf_obj)
- -o
- $(elf)
- -EL
- --script
- $(elf_ld)
- --warn-section-align
# Assembly
asm:
out: [$(asm_obj), deps_file: $(asm_obj_deps)]
deps: [$(asm_src), static: $(asm_obj::dir_name)/]
exec:
- - $(as)
- -MD
- $(asm_obj_deps)
- $(asm_src)
- -o
- $(asm_obj)
- -EL
- -mips1
- -march=r3000
- -msoft-float
- -O2