Use decomp-toolkit

Former-commit-id: 607753a08f
This commit is contained in:
2022-11-27 23:06:16 -05:00
parent 237b8f77d9
commit 6f994fbe28
11 changed files with 570 additions and 1206 deletions

View File

@@ -1,15 +0,0 @@
CC := gcc
CFLAGS := -O3 -Wall -s
default: all
all: elf2dol metroidbuildinfo
elf2dol: elf2dol.c
$(CC) $(CFLAGS) -o $@ $^
metroidbuildinfo: metroidbuildinfo.c
$(CC) $(CFLAGS) -o $@ $^
clean:
$(RM) elf2dol metroidbuildinfo

View File

@@ -1,224 +0,0 @@
#!/usr/bin/env python3
################################################################################
# Description #
################################################################################
# calcprogress: Used to calculate the progress of the Metroid Prime decomp. #
# Prints to stdout for now, but eventually will have some form of storage, #
# i.e. CSV, so that it can be used for a webpage display. #
# #
# Usage: No arguments needed #
################################################################################
###############################################
# #
# Imports #
# #
###############################################
import os
import sys
import struct
import re
import math
import argparse
import json
###############################################
# #
# Constants #
# #
###############################################
MEM1_HI = 0x81200000
MEM1_LO = 0x80004000
MW_WII_SYMBOL_REGEX = r"^\s*"\
r"(?P<SectOfs>\w{8})\s+"\
r"(?P<Size>\w{6})\s+"\
r"(?P<VirtOfs>\w{8})\s+"\
r"(?P<FileOfs>\w{8})\s+"\
r"(\w{1,2})\s+"\
r"(?P<Symbol>[0-9A-Za-z_<>$@.*]*)\s*"\
r"(?P<Object>\S*)"
MW_GC_SYMBOL_REGEX = r"^\s*"\
r"(?P<SectOfs>\w{8})\s+"\
r"(?P<Size>\w{6})\s+"\
r"(?P<VirtOfs>\w{8})\s+"\
r"(\w{1,2})\s+"\
r"(?P<Symbol>[0-9A-Za-z_<>$@.*]*)\s*"\
r"(?P<Object>\S*)"
REGEX_TO_USE = MW_GC_SYMBOL_REGEX
TEXT_SECTIONS = ["init", "text"]
DATA_SECTIONS = [
"rodata", "data", "bss", "sdata", "sbss", "sdata2", "sbss2",
"ctors", "_ctors", "dtors", "ctors$99", "_ctors$99", "ctors$00", "dtors$99",
"extab", "extabindex", "extab_", "extabindex_", "_extab", "_exidx"
]
# DOL info
TEXT_SECTION_COUNT = 7
DATA_SECTION_COUNT = 11
SECTION_TEXT = 0
SECTION_DATA = 1
# Progress flavor
codeFrac = 1499 # total code "item" amount
dataFrac = 250 # total data "item" amount
codeItem = "energy" # code flavor item
dataItem = "missiles" # data flavor item
###############################################
# #
# Entrypoint #
# #
###############################################
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Calculate progress.")
parser.add_argument("dol", help="Path to DOL")
parser.add_argument("map", help="Path to map")
parser.add_argument("-o", "--output", help="JSON output file")
args = parser.parse_args()
# HACK: Check asm or src in obj_file.mk
# to avoid counting .comm/.lcomm as decompiled
asm_objs = []
with open('obj_files.mk', 'r') as file:
for line in file:
if "asm/" in line:
asm_objs.append(line.strip().rsplit('/', 1)[-1].rstrip('\\'))
# Sum up DOL section sizes
dol_handle = open(args.dol, "rb")
# Seek to virtual addresses
dol_handle.seek(0x48)
# Read virtual addresses
text_starts = list()
for i in range(TEXT_SECTION_COUNT):
text_starts.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
data_starts = list()
for i in range(DATA_SECTION_COUNT):
data_starts.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
# Read lengths
text_sizes = list()
for i in range(TEXT_SECTION_COUNT):
text_sizes.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
data_sizes = list()
for i in range(DATA_SECTION_COUNT):
data_sizes.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
# BSS address + length
bss_start = int.from_bytes(dol_handle.read(4), byteorder='big')
bss_size = int.from_bytes(dol_handle.read(4), byteorder='big')
bss_end = bss_start + bss_size
dol_code_size = 0
dol_data_size = 0
for i in range(DATA_SECTION_COUNT):
# Ignore sections inside BSS
if (data_starts[i] >= bss_start) and (data_starts[i] + data_sizes[i] <= bss_end): continue
dol_data_size += data_sizes[i]
dol_data_size += bss_size
for i in text_sizes:
dol_code_size += i
# Open map file
mapfile = open(args.map, "r")
symbols = mapfile.readlines()
decomp_code_size = 0
decomp_data_size = 0
section_type = None
# Find first section
first_section = 0
while (symbols[first_section].startswith(".") == False and "section layout" not in symbols[first_section]): first_section += 1
assert(first_section < len(symbols)), "Map file contains no sections!!!"
cur_object = None
cur_size = 0
j = 0
for i in range(first_section, len(symbols)):
# New section
if (symbols[i].startswith(".") == True or "section layout" in symbols[i]):
# Grab section name (i.e. ".init section layout" -> "init")
sectionName = re.search(r"\.*(?P<Name>\w+)\s", symbols[i]).group("Name")
# Determine type of section
section_type = SECTION_DATA if (sectionName in DATA_SECTIONS) else SECTION_TEXT
# Parse symbols until we hit the next section declaration
else:
if "UNUSED" in symbols[i]: continue
if "entry of" in symbols[i]:
if j == i - 1:
if section_type == SECTION_TEXT:
decomp_code_size -= cur_size
else:
decomp_data_size -= cur_size
cur_size = 0
#print(f"Line* {j}: {symbols[j]}")
#print(f"Line {i}: {symbols[i]}")
continue
assert(section_type != None), f"Symbol found outside of a section!!!\n{symbols[i]}"
match_obj = re.search(REGEX_TO_USE, symbols[i])
# Should be a symbol in ASM (so we discard it)
if (match_obj == None):
#print(f"Line {i}: {symbols[i]}")
continue
# Has the object file changed?
last_object = cur_object
cur_object = match_obj.group("Object").strip()
if last_object != cur_object or cur_object in asm_objs: continue
# Is the symbol a file-wide section?
symb = match_obj.group("Symbol")
if (symb.startswith("*fill*")) or (symb.startswith(".") and symb[1:] in TEXT_SECTIONS or symb[1:] in DATA_SECTIONS): continue
# For sections that don't start with "."
if (symb in DATA_SECTIONS): continue
# If not, we accumulate the file size
cur_size = int(match_obj.group("Size"), 16)
j = i
if (section_type == SECTION_TEXT):
decomp_code_size += cur_size
else:
decomp_data_size += cur_size
# Calculate percentages
codeCompletionPcnt = (decomp_code_size / dol_code_size) # code completion percent
dataCompletionPcnt = (decomp_data_size / dol_data_size) # data completion percent
bytesPerCodeItem = dol_code_size / codeFrac # bytes per code item
bytesPerDataItem = dol_data_size / dataFrac # bytes per data item
codeCount = math.floor(decomp_code_size / bytesPerCodeItem)
dataCount = math.floor(decomp_data_size / bytesPerDataItem)
print("Progress:")
print(f"\tCode sections: {decomp_code_size} / {dol_code_size}\tbytes in src ({codeCompletionPcnt:%})")
print(f"\tData sections: {decomp_data_size} / {dol_data_size}\tbytes in src ({dataCompletionPcnt:%})")
print("\nYou have {} out of {} {} and collected {} out of {} {}.".format(codeCount, codeFrac, codeItem, dataCount, dataFrac, dataItem))
if args.output:
data = {
"dol": {
"code": decomp_code_size,
"code/total": dol_code_size,
"data": decomp_data_size,
"data/total": dol_data_size,
}
}
with open(args.output, "w") as f:
json.dump(data, f)

View File

@@ -8,50 +8,53 @@ script_dir = os.path.dirname(os.path.realpath(__file__))
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
src_dir = os.path.join(root_dir, "src")
include_dirs = [
os.path.join(root_dir, "include"),
os.path.join(root_dir, "libc"),
os.path.join(root_dir, "include"),
os.path.join(root_dir, "libc"),
]
include_pattern = re.compile(r'^#include\s*[<"](.+?)[>"]$')
guard_pattern = re.compile(r'^#ifndef\s+(.*)$')
guard_pattern = re.compile(r"^#ifndef\s+(.*)$")
defines = set()
def import_h_file(in_file, r_path) -> str:
rel_path = os.path.join(root_dir, r_path, in_file)
if os.path.exists(rel_path):
return import_c_file(rel_path)
return import_c_file(rel_path)
for include_dir in include_dirs:
inc_path = os.path.join(include_dir, in_file)
if os.path.exists(inc_path):
return import_c_file(inc_path)
inc_path = os.path.join(include_dir, in_file)
if os.path.exists(inc_path):
return import_c_file(inc_path)
else:
print("Failed to locate", in_file)
return ""
print("Failed to locate", in_file)
return ""
def import_c_file(in_file) -> str:
in_file = os.path.relpath(in_file, root_dir)
out_text = ''
out_text = ""
with open(in_file) as file:
for idx, line in enumerate(file):
guard_match = guard_pattern.match(line.strip())
if idx == 0:
if guard_match:
if guard_match[1] in defines:
break
defines.add(guard_match[1])
print("Processing file", in_file)
include_match = include_pattern.match(line.strip())
if include_match:
out_text += f"/* \"{in_file}\" line {idx} \"{include_match[1]}\" */\n"
out_text += import_h_file(include_match[1], os.path.dirname(in_file))
out_text += f"/* end \"{include_match[1]}\" */\n"
else:
out_text += line
for idx, line in enumerate(file):
guard_match = guard_pattern.match(line.strip())
if idx == 0:
if guard_match:
if guard_match[1] in defines:
break
defines.add(guard_match[1])
print("Processing file", in_file)
include_match = include_pattern.match(line.strip())
if include_match:
out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n'
out_text += import_h_file(include_match[1], os.path.dirname(in_file))
out_text += f'/* end "{include_match[1]}" */\n'
else:
out_text += line
return out_text
def main():
parser = argparse.ArgumentParser(
description="""Create a context file which can be used for decomp.me"""
@@ -69,4 +72,4 @@ def main():
if __name__ == "__main__":
main()
main()

View File

@@ -1,22 +0,0 @@
from sys import argv
def nextU32(f):
dat = f.read(4)
return int.from_bytes(dat, 'big')
with open(argv[1], 'rb') as dol:
offsets = [nextU32(dol) for i in range(18)]
addresses = [nextU32(dol) for i in range(18)]
sizes = [nextU32(dol) for i in range(18)]
target = int(argv[2], 16)
for i in range(0, 0x18):
offset = offsets[i]
size = sizes[i]
if offset <= target < offset + size:
section = i
delta = target - offset
break
print(hex(addresses[section] + delta))

40
tools/download_dtk.py Normal file
View File

@@ -0,0 +1,40 @@
import argparse
import urllib.request
import os
import stat
from pathlib import Path
REPO = "https://github.com/encounter/decomp-toolkit"
def main():
parser = argparse.ArgumentParser()
parser.add_argument("tag_file", help="file containing GitHub tag")
parser.add_argument("output", type=Path, help="output file path")
args = parser.parse_args()
with open(args.tag_file, "r") as f:
tag = f.readline().rstrip()
uname = os.uname()
suffix = ""
platform = uname.sysname.lower()
if platform == "darwin":
platform = "macos"
elif platform == "windows":
suffix = ".exe"
arch = uname.machine.lower()
if arch == "amd64":
arch = "x86_64"
url = f"{REPO}/releases/download/{tag}/dtk-{platform}-{arch}{suffix}"
output = args.output
# print(f"Downloading {url} to {output}")
urllib.request.urlretrieve(url, output)
st = os.stat(output)
os.chmod(output, st.st_mode | stat.S_IEXEC)
if __name__ == "__main__":
main()

View File

@@ -1,504 +0,0 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#ifndef MAX
//! Get the maximum of two values
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
//! Get the minimum of two values
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof((arr)[0]))
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf32_Ehdr;
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_PAD 7
#define EI_NIDENT 16
#define ELFCLASS32 1
#define ELFDATA2MSB 2
#define EV_CURRENT 1
#define ET_EXEC 2
#define EM_PPC 20
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;
#define PT_LOAD 1
#define PF_R 4
#define PF_W 2
#define PF_X 1
int verbosity = 0;
#if BYTE_ORDER == BIG_ENDIAN
#define swap32(x) (x)
#define swap16(x) (x)
#else
static inline uint32_t swap32(uint32_t v) {
return (v >> 24) | ((v >> 8) & 0x0000FF00) | ((v << 8) & 0x00FF0000) |
(v << 24);
}
static inline uint16_t swap16(uint16_t v) { return (v >> 8) | (v << 8); }
#endif /* BIG_ENDIAN */
typedef struct {
uint32_t text_off[7];
uint32_t data_off[11];
uint32_t text_addr[7];
uint32_t data_addr[11];
uint32_t text_size[7];
uint32_t data_size[11];
uint32_t bss_addr;
uint32_t bss_size;
uint32_t entry;
uint32_t pad[7];
} DOL_hdr;
#define HAVE_BSS 1
#define MAX_TEXT_SEGMENTS 7
#define MAX_DATA_SEGMENTS 11
#define DOL_ALIGNMENT 32
#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1))
typedef struct {
DOL_hdr header;
int text_cnt;
int data_cnt;
uint32_t text_elf_off[7];
uint32_t data_elf_off[11];
uint32_t flags;
FILE* elf;
} DOL_map;
void usage(const char* name) {
fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name);
fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n");
fprintf(stderr, " Options:\n");
fprintf(stderr, " -h Show this help\n");
fprintf(stderr, " -v Be more verbose (twice for even more)\n");
}
#define die(x) \
{ \
fprintf(stderr, x "\n"); \
exit(1); \
}
#define perrordie(x) \
{ \
perror(x); \
exit(1); \
}
void ferrordie(FILE* f, const char* str) {
if (ferror(f)) {
fprintf(stderr, "Error while ");
perrordie(str);
} else if (feof(f)) {
fprintf(stderr, "EOF while %s\n", str);
exit(1);
} else {
fprintf(stderr, "Unknown error while %s\n", str);
exit(1);
}
}
void add_bss(DOL_map* map, uint32_t paddr, uint32_t memsz) {
if (map->flags & HAVE_BSS) {
uint32_t curr_start = swap32(map->header.bss_addr);
uint32_t curr_size = swap32(map->header.bss_size);
if (paddr < curr_start)
map->header.bss_addr = swap32(paddr);
// Total BSS size should be the end of the last bss section minus the
// start of the first bss section.
if (paddr + memsz > curr_start + curr_size)
map->header.bss_size = swap32(paddr + memsz - curr_start);
} else {
map->header.bss_addr = swap32(paddr);
map->header.bss_size = swap32(memsz);
map->flags |= HAVE_BSS;
}
}
void read_elf_segments(DOL_map* map, const char* elf) {
int read, i;
Elf32_Ehdr ehdr;
if (verbosity >= 2)
fprintf(stderr, "Reading ELF file...\n");
map->elf = fopen(elf, "rb");
if (!map->elf)
perrordie("Could not open ELF file");
read = fread(&ehdr, sizeof(ehdr), 1, map->elf);
if (read != 1)
ferrordie(map->elf, "reading ELF header");
if (memcmp(&ehdr.e_ident[0], "\177ELF", 4))
die("Invalid ELF header");
if (ehdr.e_ident[EI_CLASS] != ELFCLASS32)
die("Invalid ELF class");
if (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
die("Invalid ELF byte order");
if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
die("Invalid ELF ident version");
if (swap32(ehdr.e_version) != EV_CURRENT)
die("Invalid ELF version");
if (swap16(ehdr.e_type) != ET_EXEC)
die("ELF is not an executable");
if (swap16(ehdr.e_machine) != EM_PPC)
die("Machine is not PowerPC");
if (!swap32(ehdr.e_entry))
die("ELF has no entrypoint");
map->header.entry = ehdr.e_entry;
if (verbosity >= 2)
fprintf(stderr, "Valid ELF header found\n");
uint16_t phnum = swap16(ehdr.e_phnum);
uint32_t phoff = swap32(ehdr.e_phoff);
Elf32_Phdr* phdrs;
if (!phnum || !phoff)
die("ELF has no program headers");
if (swap16(ehdr.e_phentsize) != sizeof(Elf32_Phdr))
die("Invalid program header entry size");
phdrs = malloc(phnum * sizeof(Elf32_Phdr));
if (fseek(map->elf, phoff, SEEK_SET) < 0)
ferrordie(map->elf, "reading ELF program headers");
read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf);
if (read != phnum)
ferrordie(map->elf, "reading ELF program headers");
for (i = 0; i < phnum; i++) {
if (swap32(phdrs[i].p_type) == PT_LOAD) {
uint32_t offset = swap32(phdrs[i].p_offset);
uint32_t paddr = swap32(phdrs[i].p_vaddr);
uint32_t filesz = swap32(phdrs[i].p_filesz);
uint32_t memsz = swap32(phdrs[i].p_memsz);
uint32_t flags = swap32(phdrs[i].p_flags);
if (memsz) {
if (verbosity >= 2)
fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n",
i, offset, filesz, paddr, memsz, flags);
if (flags & PF_X) {
// TEXT segment
if (!(flags & PF_R))
fprintf(stderr, "Warning: non-readable segment %d\n", i);
if (flags & PF_W)
fprintf(stderr, "Warning: writable and executable segment %d\n", i);
if (filesz > memsz) {
fprintf(stderr,
"Error: TEXT segment %d memory size (0x%x) smaller than "
"file size (0x%x)\n",
i, memsz, filesz);
exit(1);
} else if (memsz > filesz) {
add_bss(map, paddr + filesz, memsz - filesz);
}
if (map->text_cnt >= MAX_TEXT_SEGMENTS) {
die("Error: Too many TEXT segments");
}
map->header.text_addr[map->text_cnt] = swap32(paddr);
map->header.text_size[map->text_cnt] = swap32(filesz);
map->text_elf_off[map->text_cnt] = offset;
map->text_cnt++;
} else {
// DATA or BSS segment
if (!(flags & PF_R))
fprintf(stderr, "Warning: non-readable segment %d\n", i);
if (filesz == 0) {
// BSS segment
add_bss(map, paddr, memsz);
} else {
// DATA segment
if (filesz > memsz) {
fprintf(stderr,
"Error: segment %d memory size (0x%x) is smaller than "
"file size (0x%x)\n",
i, memsz, filesz);
exit(1);
}
if (map->data_cnt >= MAX_DATA_SEGMENTS) {
die("Error: Too many DATA segments");
}
map->header.data_addr[map->data_cnt] = swap32(paddr);
map->header.data_size[map->data_cnt] = swap32(filesz);
map->data_elf_off[map->data_cnt] = offset;
map->data_cnt++;
}
}
} else {
if (verbosity >= 1)
fprintf(stderr, "Skipping empty program header %d\n", i);
}
} else if (verbosity >= 1) {
fprintf(stderr, "Skipping program header %d of type %d\n", i,
swap32(phdrs[i].p_type));
}
}
if (verbosity >= 2) {
fprintf(stderr, "Segments:\n");
for (i = 0; i < map->text_cnt; i++) {
fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n", i,
swap32(map->header.text_addr[i]),
swap32(map->header.text_size[i]), map->text_elf_off[i]);
}
for (i = 0; i < map->data_cnt; i++) {
fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n", i,
swap32(map->header.data_addr[i]),
swap32(map->header.data_size[i]), map->data_elf_off[i]);
}
if (map->flags & HAVE_BSS)
fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n",
swap32(map->header.bss_addr), swap32(map->header.bss_size));
}
}
void map_dol(DOL_map* map) {
uint32_t fpos;
int i;
if (verbosity >= 2)
fprintf(stderr, "Laying out DOL file...\n");
fpos = DOL_ALIGN(sizeof(DOL_hdr));
for (i = 0; i < map->text_cnt; i++) {
if (verbosity >= 2)
fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos);
map->header.text_off[i] = swap32(fpos);
fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i]));
}
for (i = 0; i < map->data_cnt; i++) {
if (verbosity >= 2)
fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos);
map->header.data_off[i] = swap32(fpos);
fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i]));
}
if (map->text_cnt == 0) {
if (verbosity >= 1)
fprintf(stderr,
"Note: adding dummy TEXT segment to work around IOS bug\n");
map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
}
if (map->data_cnt == 0) {
if (verbosity >= 1)
fprintf(stderr,
"Note: adding dummy DATA segment to work around IOS bug\n");
map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
}
}
#define BLOCK (1024 * 1024)
void fcpy(FILE* dst, FILE* src, uint32_t dst_off, uint32_t src_off,
uint32_t size) {
int left = size;
int read;
int written;
int block;
void* blockbuf;
if (fseek(src, src_off, SEEK_SET) < 0)
ferrordie(src, "reading ELF segment data");
if (fseek(dst, dst_off, SEEK_SET) < 0)
ferrordie(dst, "writing DOL segment data");
blockbuf = malloc(MIN(BLOCK, left));
while (left) {
block = MIN(BLOCK, left);
read = fread(blockbuf, 1, block, src);
if (read != block) {
free(blockbuf);
ferrordie(src, "reading ELF segment data");
}
written = fwrite(blockbuf, 1, block, dst);
if (written != block) {
free(blockbuf);
ferrordie(dst, "writing DOL segment data");
}
left -= block;
}
free(blockbuf);
}
void fpad(FILE* dst, uint32_t dst_off, uint32_t size) {
uint32_t i;
if (fseek(dst, dst_off, SEEK_SET) < 0)
ferrordie(dst, "writing DOL segment data");
for (i = 0; i < size; i++)
fputc(0, dst);
}
void write_dol(DOL_map* map, const char* dol) {
FILE* dolf;
int written;
int i;
if (verbosity >= 2)
fprintf(stderr, "Writing DOL file...\n");
dolf = fopen(dol, "wb");
if (!dolf)
perrordie("Could not open DOL file");
if (verbosity >= 2) {
fprintf(stderr, "DOL header:\n");
for (i = 0; i < MAX(1, map->text_cnt); i++)
fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i,
swap32(map->header.text_addr[i]),
swap32(map->header.text_size[i]),
swap32(map->header.text_off[i]));
for (i = 0; i < MAX(1, map->data_cnt); i++)
fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i,
swap32(map->header.data_addr[i]),
swap32(map->header.data_size[i]),
swap32(map->header.data_off[i]));
if (swap32(map->header.bss_addr) && swap32(map->header.bss_size))
fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
swap32(map->header.bss_size));
fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry));
fprintf(stderr, "Writing DOL header...\n");
}
// Write DOL header with aligned text and data section sizes
DOL_hdr aligned_header = map->header;
for (i = 0; i < ARRAY_COUNT(aligned_header.text_size); i++)
aligned_header.text_size[i] =
swap32(DOL_ALIGN(swap32(aligned_header.text_size[i])));
for (i = 0; i < ARRAY_COUNT(aligned_header.data_size); i++)
aligned_header.data_size[i] =
swap32(DOL_ALIGN(swap32(aligned_header.data_size[i])));
written = fwrite(&aligned_header, sizeof(DOL_hdr), 1, dolf);
if (written != 1)
ferrordie(dolf, "writing DOL header");
for (i = 0; i < map->text_cnt; i++) {
uint32_t size = swap32(map->header.text_size[i]);
uint32_t padded_size = DOL_ALIGN(size);
if (verbosity >= 2)
fprintf(stderr, "Writing TEXT segment %d...\n", i);
fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i],
size);
if (padded_size > size)
fpad(dolf, swap32(map->header.text_off[i]) + size, padded_size - size);
}
for (i = 0; i < map->data_cnt; i++) {
uint32_t size = swap32(map->header.data_size[i]);
uint32_t padded_size = DOL_ALIGN(size);
if (verbosity >= 2)
fprintf(stderr, "Writing DATA segment %d...\n", i);
fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i],
size);
if (padded_size > size)
fpad(dolf, swap32(map->header.data_off[i]) + size, padded_size - size);
}
if (verbosity >= 2)
fprintf(stderr, "All done!\n");
fclose(map->elf);
fclose(dolf);
}
int main(int argc, char** argv) {
char** arg;
if (argc < 2) {
usage(argv[0]);
return 1;
}
arg = &argv[1];
argc--;
while (argc && *arg[0] == '-') {
if (!strcmp(*arg, "-h")) {
usage(argv[0]);
return 1;
} else if (!strcmp(*arg, "-v")) {
verbosity++;
} else if (!strcmp(*arg, "--")) {
arg++;
argc--;
break;
} else {
fprintf(stderr, "Unrecognized option %s\n", *arg);
usage(argv[0]);
return 1;
}
arg++;
argc--;
}
if (argc < 2) {
usage(argv[0]);
exit(1);
}
const char* elf_file = arg[0];
const char* dol_file = arg[1];
DOL_map map;
memset(&map, 0, sizeof(map));
read_elf_segments(&map, elf_file);
map_dol(&map);
write_dol(&map, dol_file);
return 0;
}

View File

@@ -1,114 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VERSION_MAX_LEN (size_t)(35)
#define METROID_BUILD_INFO_TAG "!#$MetroidBuildInfo!#$"
void* memmem(const void* l, size_t l_len, const void* s, size_t s_len) {
register char *cur, *last;
const char* cl = (const char*)l;
const char* cs = (const char*)s;
/* we need something to compare */
if (l_len == 0 || s_len == 0)
return NULL;
/* "s" must be smaller or equal to "l" */
if (l_len < s_len)
return NULL;
/* special case where s_len == 1 */
if (s_len == 1)
return memchr(l, (int)*cs, l_len);
/* the last position where its possible to find "s" in "l" */
last = (char*)cl + l_len - s_len;
for (cur = (char*)cl; cur <= last; cur++)
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
return cur;
return NULL;
}
int main(int argc, const char* argv[]) {
if (argc < 3) {
fprintf(stdout, "\t--- METROID BUILD INFO ---\n"
"\tWritten by Phillip \"Antidote\" Stephens\n"
"\tReleased under the MIT License\n\n"
"\tSets the MetroidBuildInfo tag value in a given binary\n"
"\tThe version string can be a maximum of 35 characters,\n"
"\texcluding null terminator\n"
"\t--------------------------\n"
);
fprintf(stdout, "Usage:\n"
"\tmetroidbuildinfo <binary> <build_file>\n");
return -1;
}
/* Let's try to get the source binary */
FILE* source = fopen(argv[1], "rb");
if (!source) {
fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[1]);
return -2;
}
char build_string[36] = {0};
FILE* build = fopen(argv[2], "rb");
if (!build) {
fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[2]);
return -3;
}
size_t read_len = fread(build_string, 1, 35, build);
fclose(build);
if (read_len <= 0) {
fprintf(stderr, "Empty file %s specified for build version!\n", argv[2]);
return -4;
}
build_string[strcspn(build_string, "\r\n")] = '\0';
/* Get source length */
fseek(source, 0, SEEK_END);
size_t source_len = ftell(source);
rewind(source);
void* source_buf = malloc(source_len);
if (source_buf == NULL) {
fprintf(stderr, "Unable to allocate buffer of size %zubytes!\n", source_len);
return -5;
}
fread(source_buf, 1, source_len, source);
fclose(source);
/* Find the build info tag so we can stuff our version info in the binary */
void* ptr =
memmem(source_buf, source_len, METROID_BUILD_INFO_TAG, strlen(METROID_BUILD_INFO_TAG));
if (ptr == NULL) {
fprintf(stderr, "Unable to find build info tag in source!\n");
return -6;
}
/* Lets actually copy over the build string */
strcpy(ptr + strlen(METROID_BUILD_INFO_TAG), build_string);
/* Now attempt to open the target file */
FILE* target = fopen(argv[1], "wb");
if (!target) {
fprintf(stderr, "Unable to open '%s'\nPlease ensure you have write permissions!\n", argv[1]);
return -7;
}
/* Finally write the buffer to the target file */
fwrite(source_buf, 1, source_len, target);
fclose(target);
/* Don't leak */
free(source_buf);
return 0;
}