Initial Commit
This commit is contained in:
parent
a05c5b18b8
commit
d765c01039
18 changed files with 1001 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/target
|
||||
/bin
|
||||
/build.ninja
|
||||
/build
|
||||
.ninja_log
|
281
Cargo.lock
generated
Normal file
281
Cargo.lock
generated
Normal file
|
@ -0,0 +1,281 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "donut-decomp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"csv",
|
||||
"goblin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goblin"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1800b95efee8ad4ef04517d4d69f8e209e763b1668f1179aeeedd0e454da55"
|
||||
dependencies = [
|
||||
"log",
|
||||
"plain",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
|
||||
dependencies = [
|
||||
"scroll_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "donut-decomp"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
default-run = "buildgen"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.42"
|
||||
goblin = "0.4.2"
|
||||
clap = "2.33.3"
|
||||
csv = "1.1.6"
|
15
README.md
Normal file
15
README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Kirby's 20th Anniversary Special Edition Decompilation Project
|
||||
|
||||
## Dependencies
|
||||
- Rust
|
||||
- Ninja
|
||||
- On Linux: Wine with 32 bit support
|
||||
- On Macos: Docker
|
||||
- llvm (any recent version)
|
||||
- donut.dol from the NTSC version (sha256 92a9f3662d85e440f2c0d6ea0d30ead1e7bf0eb51be279320b5e9aac6e5216e5), placed in bin/donut/donut.dol
|
||||
- donut.MAP from the NTSC version (sha256 d26fab6e6a58d3dd5abc0c2ba1b745c1095d4b2887b44b3ea7128258a0ec8666), placed in bin/donut/donut.MAP
|
||||
- The following files from "CW for Wii v1.7": license.dat, mwasmeppc.exe, mwcceppc.exe, mwldeppc.exe, all placed in bin/mwcc
|
||||
- The following file from the misc directory: lmgr8c.dll
|
||||
|
||||
## Building
|
||||
You can build by running `cargo run` in the root directory.
|
13
data/objects.txt
Normal file
13
data/objects.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
./build/obj/init_80004000_80006728.o
|
||||
./build/obj/extab_80006740_800068cc.o
|
||||
./build/obj/extabindex_800068e0_800069e4.o
|
||||
./build/obj/text_80006a00_80406244.o
|
||||
./build/obj/ctors_80406260_80406530.o
|
||||
./build/obj/dtors_80406540_8040654c.o
|
||||
./build/obj/rodata_80406560_80421030.o
|
||||
./build/obj/data_80421040_804966fc.o
|
||||
./build/obj/bss_80496700_8055640c.o
|
||||
./build/obj/sdata_80556420_8055c6d0.o
|
||||
./build/obj/sbss_8055c6e0_8055df74.o
|
||||
./build/obj/sdata2_8055df80_805643b0.o
|
||||
./build/obj/sbss2_805643c0_805643fc.o
|
1
data/ranges.csv
Normal file
1
data/ranges.csv
Normal file
|
@ -0,0 +1 @@
|
|||
StartAddress,EndAddress,Section,File,function
|
|
2
data/symbols.csv
Normal file
2
data/symbols.csv
Normal file
|
@ -0,0 +1,2 @@
|
|||
Address,SymbolName
|
||||
80006310,__start
|
|
BIN
misc/lmgr8c.dll
Executable file
BIN
misc/lmgr8c.dll
Executable file
Binary file not shown.
7
misc/lmgr8c.dll.c
Normal file
7
misc/lmgr8c.dll.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Implmeentation of lmgr8c which hopefully is enough to run mw(asm|cc|ld)eppc
|
||||
|
||||
__declspec(dllexport) void __cdecl lp_checkin(void) {}
|
||||
__declspec(dllexport) int __cdecl lp_checkout(void) { return 0; }
|
||||
__declspec(dllexport) const char *__cdecl lp_errstring(void) {
|
||||
return "no error";
|
||||
}
|
5
misc/lmgr8c.dll.def
Normal file
5
misc/lmgr8c.dll.def
Normal file
|
@ -0,0 +1,5 @@
|
|||
LIBRARY lmgr8c
|
||||
EXPORTS
|
||||
lp_checkin @ 189
|
||||
lp_checkout @ 190
|
||||
lp_errstring @ 191
|
12
rules.ninja
Normal file
12
rules.ninja
Normal file
|
@ -0,0 +1,12 @@
|
|||
rule extract-object
|
||||
command = cargo run --quiet --release --bin extract-dol -- $in $out $start $end
|
||||
|
||||
rule directory
|
||||
command = mkdir $out
|
||||
|
||||
rule genlcf
|
||||
command = cargo run --quiet --release --bin genlcf -- $out
|
||||
|
||||
rule ccld
|
||||
command = ./run-exe.sh bin/mwcc/mwldeppc.exe $in -o $out -lcf $lcf -fp hard -linkmode moreram -map $map
|
||||
|
14
run-exe.sh
Executable file
14
run-exe.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#if [[ $(uname -m) != 'x86_64' ]]; then
|
||||
# echo "Can currently only run on x86_64"
|
||||
# exit 1
|
||||
#fi
|
||||
|
||||
#if [[ $OSTYPE == 'darwin' ]]; then
|
||||
# docker run -it --rm --volume $PWD/build/wine:/home/wineuser/.wine --volume $PWD:$PWD -w $PWD scottyhardy/docker-wine wine "$@"
|
||||
#else
|
||||
wine "$@"
|
||||
#fi
|
72
src/bin/buildgen.rs
Normal file
72
src/bin/buildgen.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter, Write},
|
||||
num::ParseIntError,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use donut_decomp::{dol::Dol, range::Ranges};
|
||||
|
||||
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
|
||||
u32::from_str_radix(s.trim_start_matches("0x"), 16)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut ninja = donut_decomp::ninja::NinjaFile::new("build.ninja")?;
|
||||
let mut ranges = Ranges::new();
|
||||
let dolfile = Dol::new(BufReader::new(File::open("bin/donut/donut.dol")?))?;
|
||||
ranges
|
||||
.add_range(0x80004000, 0x80006728)
|
||||
.add_range(0x80006740, 0x800068cc)
|
||||
.add_range(0x800068e0, 0x800069e4)
|
||||
.add_range(0x80006a00, 0x80406244)
|
||||
.add_range(0x80406260, 0x80406530)
|
||||
.add_range(0x80406540, 0x8040654c)
|
||||
.add_range(0x80406560, 0x80421030)
|
||||
.add_range(0x80421040, 0x804966fc)
|
||||
.add_range(0x80496700, 0x8055640c)
|
||||
.add_range(0x80556420, 0x8055c6d0)
|
||||
.add_range(0x8055c6e0, 0x8055df74)
|
||||
.add_range(0x8055df80, 0x805643b0)
|
||||
.add_range(0x805643c0, 0x805643fc);
|
||||
|
||||
let mut rdr = csv::Reader::from_reader(BufReader::new(File::open("data/ranges.csv")?));
|
||||
|
||||
for result in rdr.records() {
|
||||
let result = result?;
|
||||
ranges.remove_range(
|
||||
parse_hex(result.get(0).unwrap())?,
|
||||
parse_hex(result.get(1).unwrap())?,
|
||||
);
|
||||
}
|
||||
|
||||
let mut objs = vec![];
|
||||
|
||||
for (start, end) in ranges.into_iter() {
|
||||
let objfile = format!(
|
||||
"build/obj/{}_{:08x}_{:08x}.o",
|
||||
dolfile.get_section_name(start),
|
||||
start,
|
||||
end
|
||||
);
|
||||
ninja.emit_extract("bin/donut/donut.dol", &objfile, start, end)?;
|
||||
objs.push(objfile);
|
||||
}
|
||||
|
||||
ninja.emit_genlcf("build/donut.lcf")?;
|
||||
ninja.emit_ccld("build/donut.elf", "build/donut.lcf", &objs)?;
|
||||
|
||||
let mut obj_list = BufWriter::new(File::create("data/objects.txt")?);
|
||||
for obj in objs.iter() {
|
||||
obj_list.write_all(b"./")?;
|
||||
obj_list.write_all(obj.as_bytes())?;
|
||||
obj_list.write_all(b"\n")?;
|
||||
}
|
||||
|
||||
Command::new("ninja")
|
||||
.arg("build/donut.elf")
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
Ok(())
|
||||
}
|
50
src/bin/extract-dol.rs
Normal file
50
src/bin/extract-dol.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use std::{fs::File, io::BufReader, num::ParseIntError};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{App, Arg};
|
||||
|
||||
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
|
||||
u32::from_str_radix(s.trim_start_matches("0x"), 16)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let matches = App::new("DOL file extractor")
|
||||
.version("0.0")
|
||||
.author("Charlotte D. <darkkirb@darkkirb.de>")
|
||||
.about("Extracts part of a DOL file section")
|
||||
.arg(
|
||||
Arg::with_name("INPUT")
|
||||
.help("Input DOL file")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("OUTPUT")
|
||||
.help("Output ELF file")
|
||||
.required(true)
|
||||
.index(2),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("START_ADDRESS")
|
||||
.help("Start Address")
|
||||
.required(true)
|
||||
.index(3),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("END_ADDRESS")
|
||||
.help("End Address")
|
||||
.required(true)
|
||||
.index(4),
|
||||
)
|
||||
.get_matches();
|
||||
let input = matches.value_of("INPUT").unwrap();
|
||||
let output = matches.value_of("OUTPUT").unwrap();
|
||||
let start_address = parse_hex(matches.value_of("START_ADDRESS").unwrap())?;
|
||||
let end_address = parse_hex(matches.value_of("END_ADDRESS").unwrap())?;
|
||||
donut_decomp::dol::Dol::new(BufReader::new(File::open(input)?))?.extract_from_to(
|
||||
start_address,
|
||||
end_address,
|
||||
output,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
90
src/bin/genlcf.rs
Normal file
90
src/bin/genlcf.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, BufWriter, Write},
|
||||
num::ParseIntError,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{App, Arg};
|
||||
|
||||
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
|
||||
u32::from_str_radix(s.trim_start_matches("0x"), 16)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let matches = App::new("LCF generator")
|
||||
.version("0.0")
|
||||
.author("Charlotte D. <darkkirb@darkkirb.de>")
|
||||
.about("Generates an LCF file")
|
||||
.arg(
|
||||
Arg::with_name("OUTPUT")
|
||||
.help("Output LCF file")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.get_matches();
|
||||
let output = matches.value_of("OUTPUT").unwrap();
|
||||
let mut out = BufWriter::new(File::create(output)?);
|
||||
out.write_all(
|
||||
r#"ENTRY(__start)
|
||||
MEMORY {
|
||||
text : origin = 0x80004000
|
||||
// Dummy address
|
||||
binary_blobs : origin = 0xA0000000
|
||||
}
|
||||
SECTIONS {
|
||||
GROUP:{
|
||||
.init ALIGN(0x20):{}
|
||||
extab_ ALIGN(0x20):{}
|
||||
extabindex_ ALIGN(0x20):{}
|
||||
.text ALIGN(0x20):{}
|
||||
.ctors ALIGN(0x20):{}
|
||||
.dtors ALIGN(0x20):{}
|
||||
.rodata ALIGN(0x20):{}
|
||||
.data ALIGN(0x20):{}
|
||||
.bss ALIGN(0x80):{}
|
||||
.sdata ALIGN(0x20):{}
|
||||
.sbss ALIGN(0x20):{}
|
||||
.sdata2 ALIGN(0x20):{}
|
||||
.sbss2 ALIGN(0x20):{}
|
||||
.stack ALIGN(0x100):{}
|
||||
} > text
|
||||
GROUP:{
|
||||
binary_blobs ALIGN(0x20):{}
|
||||
} > binary_blobs
|
||||
|
||||
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;
|
||||
_stack_end = _f_sbss2 + SIZEOF(.sbss2);
|
||||
_db_stack_addr = (_stack_addr + 0x2000);
|
||||
_db_stack_end = _stack_addr;
|
||||
__ArenaLo = (_db_stack_addr + 0x1f) & ~0x1f;
|
||||
__ArenaHi = 0x81700000;
|
||||
"#
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
let mut rdr = csv::Reader::from_reader(BufReader::new(File::open("data/symbols.csv")?));
|
||||
|
||||
for result in rdr.records() {
|
||||
let result = result?;
|
||||
out.write_all(
|
||||
format!(
|
||||
" {} = 0x{:08x};\n",
|
||||
result.get(1).unwrap(),
|
||||
parse_hex(result.get(0).unwrap())?
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
out.write_all(b"}\n")?;
|
||||
|
||||
let reader = BufReader::new(File::open("data/objects.txt")?);
|
||||
out.write_all(b"FORCEFILES { \n")?;
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
out.write_all(format!("\"{}\"\n", line).as_bytes())?;
|
||||
}
|
||||
out.write_all(b"}\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
222
src/dol/mod.rs
Normal file
222
src/dol/mod.rs
Normal file
|
@ -0,0 +1,222 @@
|
|||
//! DOL utilities
|
||||
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
pub const SECTION_NAMES: &[&str] = &[
|
||||
".init",
|
||||
".text",
|
||||
".text2",
|
||||
".text3",
|
||||
".text4",
|
||||
".text5",
|
||||
".text6",
|
||||
"extab",
|
||||
"extabindex",
|
||||
".ctors",
|
||||
".dtors",
|
||||
".rodata",
|
||||
".data",
|
||||
".sdata",
|
||||
".sdata2",
|
||||
".data8",
|
||||
".data9",
|
||||
".data10",
|
||||
".bss",
|
||||
".sbss",
|
||||
".sbss2",
|
||||
];
|
||||
|
||||
pub const SECTION_BITS: &[&str] = &[
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load,code",
|
||||
"alloc,readonly,load",
|
||||
"alloc,readonly,load",
|
||||
"alloc,readonly,load",
|
||||
"alloc,readonly,load",
|
||||
"alloc,readonly,load",
|
||||
"alloc,load",
|
||||
"alloc,load",
|
||||
"alloc,load",
|
||||
"alloc,load",
|
||||
"alloc,load",
|
||||
"alloc,load",
|
||||
"alloc",
|
||||
"alloc",
|
||||
"alloc",
|
||||
];
|
||||
|
||||
pub const SECTION_FILE_NAMES: &[&str] = &[
|
||||
"init",
|
||||
"text",
|
||||
"text2",
|
||||
"text3",
|
||||
"text4",
|
||||
"text5",
|
||||
"text6",
|
||||
"extab",
|
||||
"extabindex",
|
||||
"ctors",
|
||||
"dtors",
|
||||
"rodata",
|
||||
"data",
|
||||
"sdata",
|
||||
"sdata2",
|
||||
"data8",
|
||||
"data9",
|
||||
"data10",
|
||||
"bss",
|
||||
"sbss",
|
||||
"sbss2",
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dol<R: Read + Seek + ?Sized> {
|
||||
file_offsets: [u32; 18],
|
||||
loading_addresses: [u32; 18],
|
||||
section_sizes: [u32; 18],
|
||||
bss_address: u32,
|
||||
bss_size: u32,
|
||||
entry_point: u32,
|
||||
reader: R,
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> Dol<R> {
|
||||
pub fn new(mut reader: R) -> Result<Self> {
|
||||
let mut file_offsets: [u32; 18] = [0; 18];
|
||||
let mut loading_addresses: [u32; 18] = [0; 18];
|
||||
let mut section_sizes: [u32; 18] = [0; 18];
|
||||
|
||||
for file_offset in file_offsets.iter_mut() {
|
||||
let mut u32_buf = [0u8; 4];
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
*file_offset = u32::from_be_bytes(u32_buf);
|
||||
}
|
||||
for loading_address in loading_addresses.iter_mut() {
|
||||
let mut u32_buf = [0u8; 4];
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
*loading_address = u32::from_be_bytes(u32_buf);
|
||||
}
|
||||
for section_size in section_sizes.iter_mut() {
|
||||
let mut u32_buf = [0u8; 4];
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
*section_size = u32::from_be_bytes(u32_buf);
|
||||
}
|
||||
let mut u32_buf = [0u8; 4];
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
let bss_address = u32::from_be_bytes(u32_buf);
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
let bss_size = u32::from_be_bytes(u32_buf);
|
||||
reader.read_exact(&mut u32_buf)?;
|
||||
let entry_point = u32::from_be_bytes(u32_buf);
|
||||
Ok(Self {
|
||||
file_offsets,
|
||||
loading_addresses,
|
||||
section_sizes,
|
||||
bss_address,
|
||||
bss_size,
|
||||
entry_point,
|
||||
reader,
|
||||
})
|
||||
}
|
||||
fn is_in_bss(&self, address: u32) -> Option<usize> {
|
||||
if address >= self.loading_addresses[13] + self.section_sizes[13]
|
||||
&& address < self.loading_addresses[14]
|
||||
{
|
||||
return Some(19); // SBSS
|
||||
}
|
||||
if address >= self.loading_addresses[14] + self.section_sizes[14] {
|
||||
return Some(20); // SBSS2
|
||||
}
|
||||
if address >= self.bss_address && address < self.bss_address + self.bss_size {
|
||||
return Some(18);
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn read_va_block(&mut self, address: u32, block: &mut [u8]) -> Result<usize> {
|
||||
for i in 0..18 {
|
||||
if address < self.loading_addresses[i]
|
||||
|| address >= self.loading_addresses[i] + self.section_sizes[i]
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.reader.seek(SeekFrom::Start(
|
||||
(self.file_offsets[i] + (address - self.loading_addresses[i])) as u64,
|
||||
))?;
|
||||
self.reader.read_exact(block)?;
|
||||
return Ok(i);
|
||||
}
|
||||
if let Some(v) = self.is_in_bss(address) {
|
||||
for c in block.iter_mut() {
|
||||
*c = 0;
|
||||
}
|
||||
return Ok(v);
|
||||
}
|
||||
anyhow::bail!("Unknown virtual address!");
|
||||
}
|
||||
pub fn extract_range(&mut self, address: u32, size: u32, outname: &str) -> Result<()> {
|
||||
let mut data = vec![0u8; size as usize];
|
||||
let binfile_name = format!("{}.bin", outname);
|
||||
let section = self.read_va_block(address, &mut data)?;
|
||||
{
|
||||
let mut binfile = File::create(&binfile_name)?;
|
||||
binfile.write_all(&data)?;
|
||||
}
|
||||
Command::new("objcopy")
|
||||
.arg("-I")
|
||||
.arg("binary")
|
||||
.arg("-O")
|
||||
.arg("elf32-big")
|
||||
.arg(&binfile_name)
|
||||
.arg(outname)
|
||||
.arg("--rename-section")
|
||||
.arg(format!(
|
||||
".data={},{}",
|
||||
SECTION_NAMES[section], SECTION_BITS[section]
|
||||
))
|
||||
.arg("--change-section-address")
|
||||
.arg(format!(".data={}", address))
|
||||
.arg("--strip-all")
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
fs::remove_file(&binfile_name)?;
|
||||
Command::new("objcopy")
|
||||
.arg("--format")
|
||||
.arg("elf32-big")
|
||||
.arg("--add-symbol")
|
||||
.arg(format!(
|
||||
"_LOC_{:08x}={}:0x{:08x},global",
|
||||
address, SECTION_NAMES[section], address
|
||||
))
|
||||
.arg(outname)
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn extract_from_to(&mut self, start: u32, end: u32, outname: &str) -> Result<()> {
|
||||
self.extract_range(start, end - start, outname)
|
||||
}
|
||||
pub fn get_section_name(&self, addr: u32) -> &str {
|
||||
for i in 0..18 {
|
||||
if addr >= self.loading_addresses[i]
|
||||
&& addr < self.loading_addresses[i] + self.section_sizes[i]
|
||||
{
|
||||
return SECTION_FILE_NAMES[i];
|
||||
}
|
||||
}
|
||||
if let Some(section) = self.is_in_bss(addr) {
|
||||
return SECTION_FILE_NAMES[section];
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
}
|
130
src/ninja/mod.rs
Normal file
130
src/ninja/mod.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
//! Ninja file generator
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NinjaFile {
|
||||
emitted_targets: HashSet<PathBuf>,
|
||||
writer: BufWriter<File>,
|
||||
}
|
||||
|
||||
impl NinjaFile {
|
||||
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let mut this = Self {
|
||||
emitted_targets: HashSet::new(),
|
||||
writer: BufWriter::new(File::create(path)?),
|
||||
};
|
||||
this.writer.write_all(b"include rules.ninja\n")?;
|
||||
Ok(this)
|
||||
}
|
||||
pub fn emit_mkdir(&mut self, dir: impl AsRef<Path>) -> Result<()> {
|
||||
let dir = dir.as_ref();
|
||||
if self.emitted_targets.contains(dir) {
|
||||
return Ok(());
|
||||
}
|
||||
self.emitted_targets.insert(dir.to_owned());
|
||||
match dir.parent() {
|
||||
Some(parent) if parent.to_string_lossy() != "" => {
|
||||
self.writer.write_all(
|
||||
format!(
|
||||
"build {}: directory || {}\n",
|
||||
dir.display(),
|
||||
parent.display()
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
self.emit_mkdir(parent)?;
|
||||
}
|
||||
_ => {
|
||||
self.writer
|
||||
.write_all(format!("build {}: directory\n", dir.display()).as_bytes())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn emit_extract(
|
||||
&mut self,
|
||||
dolfile: impl AsRef<Path>,
|
||||
objfile: impl AsRef<Path>,
|
||||
start_addr: u32,
|
||||
end_addr: u32,
|
||||
) -> Result<()> {
|
||||
let dolfile = dolfile.as_ref();
|
||||
let objfile = objfile.as_ref();
|
||||
if self.emitted_targets.contains(objfile) {
|
||||
return Ok(());
|
||||
}
|
||||
self.emitted_targets.insert(objfile.to_owned());
|
||||
let parent = objfile.parent().unwrap();
|
||||
self.writer.write_all(
|
||||
format!(
|
||||
r#"build {}: extract-object {} || {}
|
||||
start = {:08x}
|
||||
end = {:08x}
|
||||
"#,
|
||||
objfile.display(),
|
||||
dolfile.display(),
|
||||
parent.display(),
|
||||
start_addr,
|
||||
end_addr
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
self.emit_mkdir(parent)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn emit_genlcf(&mut self, outfile: impl AsRef<Path>) -> Result<()> {
|
||||
let outfile = outfile.as_ref();
|
||||
if self.emitted_targets.contains(outfile) {
|
||||
return Ok(());
|
||||
}
|
||||
self.emitted_targets.insert(outfile.to_owned());
|
||||
let parent = outfile.parent().unwrap();
|
||||
self.writer.write_all(
|
||||
format!(
|
||||
"build {}: genlcf data/symbols.csv || {}\n",
|
||||
outfile.display(),
|
||||
parent.display()
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
self.emit_mkdir(parent)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn emit_ccld(
|
||||
&mut self,
|
||||
outfile: impl AsRef<Path>,
|
||||
lcffile: impl AsRef<Path>,
|
||||
objects: &[impl AsRef<Path>],
|
||||
) -> Result<()> {
|
||||
let outfile = outfile.as_ref();
|
||||
let lcffile = lcffile.as_ref();
|
||||
if self.emitted_targets.contains(outfile) {
|
||||
return Ok(());
|
||||
}
|
||||
self.emitted_targets.insert(outfile.to_owned());
|
||||
let parent = outfile.parent().unwrap();
|
||||
self.writer
|
||||
.write_all(format!("build {}: ccld ", outfile.display()).as_bytes())?;
|
||||
for object in objects.iter() {
|
||||
self.writer
|
||||
.write_all(format!("{} ", object.as_ref().display()).as_bytes())?;
|
||||
}
|
||||
self.writer.write_all(
|
||||
format!(
|
||||
"| {} data/objects.txt || {}\n lcf={}\n map=build/donut.map\n",
|
||||
lcffile.display(),
|
||||
parent.display(),
|
||||
lcffile.display()
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
self.emit_mkdir(parent)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
69
src/range.rs
Normal file
69
src/range.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Ranges {
|
||||
range_list: BTreeMap<u32, u32>,
|
||||
}
|
||||
|
||||
impl Ranges {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
range_list: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn add_range(&mut self, start: u32, end: u32) -> &mut Self {
|
||||
self.range_list.insert(start, end);
|
||||
self.merge_ranges();
|
||||
self
|
||||
}
|
||||
pub fn remove_range(&mut self, start: u32, end: u32) -> &mut Self {
|
||||
let mut range_start = 0;
|
||||
let mut range_end = 0;
|
||||
// Find the range it is part of
|
||||
for (curr_start, curr_end) in self.range_list.iter() {
|
||||
if *curr_start > start || *curr_end < end {
|
||||
continue;
|
||||
}
|
||||
range_start = *curr_start;
|
||||
range_end = *curr_end;
|
||||
}
|
||||
self.split_range(range_start, start);
|
||||
self.split_range(start, end);
|
||||
self.split_range(end, range_end);
|
||||
self.range_list.remove(&start);
|
||||
self.merge_ranges();
|
||||
self
|
||||
}
|
||||
fn merge_ranges(&mut self) {
|
||||
let mut last_start = 0;
|
||||
let mut last_end = 0;
|
||||
let mut block_gc_list = vec![];
|
||||
let mut block_updates = BTreeMap::new();
|
||||
for (curr_start, curr_end) in self.range_list.iter() {
|
||||
if *curr_start == *curr_end {
|
||||
block_gc_list.push(*curr_start);
|
||||
} else if *curr_start <= last_end {
|
||||
block_gc_list.push(*curr_start);
|
||||
last_end = *curr_end;
|
||||
block_updates.insert(last_start, last_end);
|
||||
} else {
|
||||
last_start = *curr_start;
|
||||
last_end = *curr_end;
|
||||
}
|
||||
}
|
||||
for (block_start, block_end) in block_updates.into_iter() {
|
||||
self.range_list.insert(block_start, block_end);
|
||||
}
|
||||
for block_start in block_gc_list.into_iter() {
|
||||
self.range_list.remove(&block_start);
|
||||
}
|
||||
}
|
||||
fn split_range(&mut self, start: u32, new_end: u32) {
|
||||
let end_addr = self.range_list[&start];
|
||||
self.range_list.insert(start, new_end);
|
||||
self.range_list.insert(new_end, end_addr);
|
||||
}
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (u32, u32)> {
|
||||
self.range_list.into_iter()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue