mm/tools/buildtools/elf2rom.c
Tharo 7743e5a2c4
Overhaul the build system (#234)
* wip

* fix

* add disassembler

* Disasm builds OK

* Variable addends

* More wip

* Rodata migration implemented

* Cleanup old tools

* Try fix submodule -> subrepo merge

* git subrepo pull --force --remote=https://github.com/zeldaret/ZAPD.git tools/ZAPD

subrepo:
  subdir:   "tools/ZAPD"
  merged:   "602e609"
upstream:
  origin:   "https://github.com/zeldaret/ZAPD.git"
  branch:   "master"
  commit:   "602e609"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* Builds again but assets are totally broken

* git subrepo pull --force tools/asm-processor

subrepo:
  subdir:   "tools/asm-processor"
  merged:   "1ffdb08a"
upstream:
  origin:   "https://github.com/simonlindholm/asm-processor.git"
  branch:   "master"
  commit:   "1ffdb08a"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* More cleanup, move functions.txt and variables.txt to tools/disasm and rm tables

* rm z64compress in preparation for subrepo

* git subrepo clone (merge) https://github.com/z64me/z64compress.git tools/z64compress

subrepo:
  subdir:   "tools/z64compress"
  merged:   "eb11085c"
upstream:
  origin:   "https://github.com/z64me/z64compress.git"
  branch:   "main"
  commit:   "eb11085c"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* Fix asset extraction

* Fix diff-init make rule

* Split code bss

* Split assumed linker bug padding from assembly files

* add filelists for mm.us.rev1

* Maybe working, but I'm not sure

* add overlays to spec

* Add rodata to actos

* Everything compiles

* Make a lot of C files for code

* Add almost every file in code to spec

* whoops

* 3 code files left

* add scenes to spec

* More progress on progress.py

* Fix skelanime in spec

* audio files!

* Fix merge issues

* Fix some C files in code

* Fix remaining code files

* Use existing O1 C files in spec

* reorder boot order in spec

* update spec

* fault.c

* Convert relocs on completed actors, fixbaserom uses current rom name

* more boot files

* Add VT macros and script

* finish already existing boot files

* most of  libultra

* fix 64bits libultra files

* Use C files for libultra, wrap some functions in NON_MATCHING

* Remove duplicate of OS_CLOCK_RATE from fault.c

* C files for fbdemos

* delete dumb files

* bootstrap C files, still need to add them to the spec

* update fixbaserom

* boot OK?

* I forgot to commit the spec

* C for gamestates

* C for kaleido

* Change all includes to ""

* copy actor sizes script from oot

* I forgot to delete those files

* Basic C files for effects

* Add effects initvars names

* Remove mislabelled boot functions from header/txt

* Begin porting bootstrap_fx, some sizes

* Fix <>

* Fix enum

* Fix diff.py

* fix libultra stuff

* update regconvert

* update setup warnings

* add some missing ;

* Fix some makefile stuff and other fixes on some non_matching functions

* add executable flag in extract_baserom and fixbaserom

* fix relative path

* copy assist from oot

* fix map path

* another assist path fix

* Delete C files for handwritten files

* add code_801A51F0 to spec

* add gfxbuffers to spec

* Move rodata to top of each file when possible

* UNK_TYPEs for func_801A51F0

* Remove kaleido rodata from spec

* Update spec and undefined_syms for recent merge

* GCC warnings and fix errors in nonmatchings,

* round percentage numbers

* progress script: format changes

* progress: error on non-existing files

* fix warning in z_scene_table

* Match 2 nonmatchings in z_actor

* Warnings in lightswitch and invadepoh

* Fix warning in z_actor_dlftbls

* I though I fixed this one

* whoops

* Comment out CC_CHECK

* Removed redundant ultra64.h includes

* Update asm_processor, sorted boot_O1 into other folders, completed the fbdemo bootstrap, cleaned up undefined_syms

* Completed gamestates bootstrap

* Split kaleido_scope

* Remove section.h and segment.h, move keep object externs to a common location in variables.h

* Completed effects bootstrap

* Segmented address externs for effects, fbdemos, gamestates and kaleido

* Move actor data externs out of the if 0

* Segmented address externs for actors

* Prepare actionfunc detection

* fix script, how did it even work before

* Fix actionfunc script again, re-introduce some more intermediate prints to the disassembler

* Automated actionFunc detection in actors

* Segmented addresses from player .text

* rm old segment addrs script and fix build

* Move sizes folder to tools

* Make build.py executable

* New Jenkinsfile Prayge

* Remove numpy dependencies

* Add warnings_disasm_current.txt

* my bad

* Update spec and undefined_syms

* Add z_eff_ss_hahen to pametfrog

* git subrepo pull (merge) --force tools/z64compress

subrepo:
  subdir:   "tools/z64compress"
  merged:   "163ca2af"
upstream:
  origin:   "https://github.com/z64me/z64compress.git"
  branch:   "main"
  commit:   "163ca2af"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

* Make z64compress print to stdout

* sneeky commit to update warnings tooling

* test

* Another test

* Mark fixing overlay reloc generating as a TODO

* Update warnings stuff

* Communicate the return code from running z64compress back to the Makefile through the wrapper

* Run formatter, remove extra commented copy of function

* Re-fix some includes

* Convert atan to hex to conform to decided style

* Some tidying up, remove c for fp and the other two handwritten code files

* BSS in z_collision_check & z_scene_proc

* add static back in

* Fix timerintr bss, add file to spec, some cleanup

* Remove externs

* Newline

* Readd enums

* Typo

* Colours

* Comments for hitmark enum values

Co-authored-by: EllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>

* Improvements and suggestions

* Organize and remove unused imports and use env for python3 scripts, delete unused overlay.py

Co-authored-by: angie <angheloalf95@gmail.com>
Co-authored-by: Elliptic Ellipsis <elliptic.ellipsis@gmail.com>
Co-authored-by: engineer124 <engineer124engineer124@gmail.com>
Co-authored-by: EllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>
2021-08-03 23:21:31 -04:00

277 lines
7.2 KiB
C

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "elf32.h"
#include "n64chksum.h"
#include "util.h"
#define ROM_SEG_START_SUFFIX ".rom_start"
#define ROM_SEG_END_SUFFIX ".rom_end"
struct RomSegment
{
const char *name;
const void *data;
int size;
int romStart;
int romEnd;
};
static struct RomSegment *g_romSegments = NULL;
static int g_romSegmentsCount = 0;
static int g_romSize;
static bool parse_number(const char *str, int *num)
{
char *endptr;
long int n = strtol(str, &endptr, 0);
*num = n;
return endptr > str;
}
static unsigned int round_up(unsigned int num, unsigned int multiple)
{
num += multiple - 1;
return num / multiple * multiple;
}
static char *sprintf_alloc(const char *fmt, ...)
{
va_list args;
int size;
char *buffer;
va_start(args, fmt);
size = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args);
buffer = malloc(size);
va_start(args, fmt);
vsprintf(buffer, fmt, args);
va_end(args);
return buffer;
}
static struct RomSegment *add_rom_segment(const char *name)
{
int index = g_romSegmentsCount;
g_romSegmentsCount++;
g_romSegments = realloc(g_romSegments, g_romSegmentsCount * sizeof(*g_romSegments));
g_romSegments[index].name = name;
g_romSegments[index].romStart = -1;
g_romSegments[index].romEnd = -1;
return &g_romSegments[index];
}
static void find_segment_info(struct Elf32 *elf, struct RomSegment *segment)
{
int i;
char *romStartSymName = sprintf_alloc("_%sSegmentRomStart", segment->name);
char *romEndSymName = sprintf_alloc("_%sSegmentRomEnd", segment->name);
segment->romStart = -1;
segment->romEnd = -1;
// TODO: use a hashmap for this instead of an O(n) loop
for (i = 0; i < elf->numsymbols; i++)
{
struct Elf32_Symbol sym;
if (!elf32_get_symbol(elf, &sym, i))
util_fatal_error("invalid or corrupt ELF file");
if (strcmp(sym.name, romStartSymName) == 0)
{
segment->romStart = sym.value;
if (segment->romEnd != -1)
break;
}
else if (strcmp(sym.name, romEndSymName) == 0)
{
segment->romEnd = sym.value;
if (segment->romStart != -1)
break;
}
}
if (segment->romStart == -1)
util_fatal_error("ROM start address of %s is not defined\n", segment->name);
if (segment->romEnd == -1)
util_fatal_error("ROM end address of %s is not defined\n", segment->name);
free(romStartSymName);
free(romEndSymName);
}
static void parse_input_file(const char *filename)
{
struct Elf32 elf;
const void *data;
size_t size;
int i;
data = util_read_whole_file(filename, &size);
if (!elf32_init(&elf, data, size) || elf.machine != ELF_MACHINE_MIPS)
util_fatal_error("%s is not a valid 32-bit MIPS ELF file", filename);
// get ROM segments
// sections of type SHT_PROGBITS and whose name is ..secname are considered ROM segments
for (i = 0; i < elf.shnum; i++)
{
struct Elf32_Section sec;
struct RomSegment *segment;
if (!elf32_get_section(&elf, &sec, i))
util_fatal_error("invalid or corrupt ELF file");
if (sec.type == SHT_PROGBITS && sec.name[0] == '.' && sec.name[1] == '.'
// HACK! ld sometimes marks NOLOAD sections as SHT_PROGBITS for no apparent reason,
// so we must ignore the ..secname.bss sections explicitly
&& strchr(sec.name + 2, '.') == NULL)
{
segment = add_rom_segment(sec.name + 2);
find_segment_info(&elf, segment);
segment->data = elf.data + sec.offset;
}
}
// find ROM size
for (i = 0; i < elf.numsymbols; i++)
{
struct Elf32_Symbol sym;
if (!elf32_get_symbol(&elf, &sym, i))
util_fatal_error("invalid or corrupt ELF file");
if (strcmp(sym.name, "_RomSize") == 0)
{
g_romSize = sym.value;
goto got_rom_size;
}
}
util_fatal_error("could not find symbol _RomSize");
got_rom_size:
// verify segment info
for (i = 0; i < g_romSegmentsCount; i++)
{
if (g_romSegments[i].romStart == -1)
util_fatal_error("segment %s has no ROM start address defined.", g_romSegments[i].name);
if (g_romSegments[i].romEnd == -1)
util_fatal_error("segment %s has no ROM end address defined.", g_romSegments[i].name);
}
}
// Writes the N64 ROM, padding the file size to a multiple of 1 MiB
static void write_rom_file(const char *filename, int cicType)
{
size_t fileSize = round_up(g_romSize, 0x100000);
uint8_t *buffer = calloc(fileSize, 1);
int i;
uint32_t chksum[2];
// write segments
for (i = 0; i < g_romSegmentsCount; i++)
{
int size = g_romSegments[i].romEnd - g_romSegments[i].romStart;
memcpy(buffer + g_romSegments[i].romStart, g_romSegments[i].data, size);
}
// pad the remaining space with 0xFF
for (i = g_romSize; i < (int) fileSize; i++)
buffer[i] = 0xFF;
// write checksum
if (!n64chksum_calculate(buffer, cicType, chksum))
util_fatal_error("invalid cic type %i", cicType);
util_write_uint32_be(buffer + 0x10, chksum[0]);
util_write_uint32_be(buffer + 0x14, chksum[1]);
util_write_whole_file(filename, buffer, fileSize);
free(buffer);
}
static void usage(const char *execname)
{
printf("usage: %s\n", execname);
}
int main(int argc, char **argv)
{
int i;
const char *inputFileName = NULL;
const char *outputFileName = NULL;
int cicType = -1;
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
if (strcmp(argv[i], "-cic") == 0)
{
i++;
if (i >= argc || !parse_number(argv[i], &cicType))
{
fputs("error: expected number after -cic\n", stderr);
goto bad_args;
}
}
else if (strcmp(argv[i], "-help") == 0)
{
usage(argv[0]);
return 0;
}
else
{
fprintf(stderr, "unknown option %s\n", argv[i]);
goto bad_args;
}
}
else
{
if (inputFileName == NULL)
inputFileName = argv[i];
else if (outputFileName == NULL)
outputFileName = argv[i];
else
{
fputs("error: too many parameters specified\n", stderr);
goto bad_args;
}
}
}
if (inputFileName == NULL)
{
fputs("error: no input file specified\n", stderr);
goto bad_args;
}
if (outputFileName == NULL)
{
fputs("error: no output file specified\n", stderr);
goto bad_args;
}
if (cicType == -1)
{
fputs("error: no CIC type specified\n", stderr);
goto bad_args;
}
parse_input_file(inputFileName);
write_rom_file(outputFileName, cicType);
return 0;
bad_args:
usage(argv[0]);
return 1;
}