1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-08-08 07:20:16 +00:00

New relocation-generating program (#1016)

* Update makefiles

* git subrepo clone git@github.com:EllipticEllipsis/fado.git tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "46c4d751a"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "46c4d751a"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* git subrepo pull tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "88114ebce"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "88114ebce"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* A few ideas for computing dependencies

* Remove reserved identifiers from spec.h and util.h
and add required headers

* Fix a couple more headers

* Program for reloc prerequisites

* git subrepo pull tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "36a905f72"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "36a905f72"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Update makefile to make dependency files and use overlay's name

* git subrepo pull tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "43c339a59"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "43c339a59"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Change awk to grep, delete ZAPD files, gitignore elf

* Delete all the cfg files

* Fix memory leaks

* Rename and add coloured errors

* Makefile tweaks
- preprocess spec before grep
- split order prerequisites via phony target to reduce dependency edges
- remove `resources` target
- remove separate overlays targets
- use `$(SPEC)` throughout
- change to using filenames of relocs for overlay names via `$*`
- Rearrange targets to better reflect their categories

* Update gitignore

* Review

* Add a check for the reloc file name

* get_segment_by_name

* get_stmt_id_by_stmt_name

* Cleaning up

* algorithm change

* function rename

* Fix typos

Co-authored-by: angie <angheloalf95@gmail.com>
This commit is contained in:
EllipticEllipsis 2022-02-06 19:40:26 +00:00 committed by GitHub
parent cf048f849a
commit eadc477187
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
511 changed files with 3947 additions and 1104 deletions

310
tools/fado/src/fado.c Normal file
View file

@ -0,0 +1,310 @@
/**
* Code specific to reading and outputting Zelda 64 relocations
*/
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
#include "fado.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fairy/fairy.h"
#include "macros.h"
#include "vc_vector/vc_vector.h"
/* String-finding-related functions */
bool Fado_CheckInProgBitsSections(Elf32_Section section, vc_vector* progBitsSections) {
Elf32_Section* i;
VC_FOREACH(i, progBitsSections) {
if (*i == section) {
return true;
}
}
return false;
}
/**
* For each input file, construct a vector of pointers to the starts of the strings defined in that file.
*/
void Fado_ConstructStringVectors(vc_vector** stringVectors, FairyFileInfo* fileInfo, int numFiles) {
int currentFile;
size_t currentSym;
for (currentFile = 0; currentFile < numFiles; currentFile++) {
FairySym* symtab = fileInfo[currentFile].symtabInfo.sectionData;
stringVectors[currentFile] = vc_vector_create(0x40, sizeof(char**), NULL);
/* Build a vector of pointers to defined symbols' names */
for (currentSym = 0; currentSym < fileInfo[currentFile].symtabInfo.sectionSize / sizeof(FairySym);
currentSym++) {
if ((symtab[currentSym].st_shndx != STN_UNDEF) &&
Fado_CheckInProgBitsSections(symtab[currentSym].st_shndx, fileInfo[currentFile].progBitsSections)) {
/* Have to pass a double pointer so it copies the pointer instead of the start of the string */
char* stringPtr = &fileInfo[currentFile].strtab[symtab[currentSym].st_name];
assert(vc_vector_push_back(stringVectors[currentFile], &stringPtr));
}
}
}
}
bool Fado_FindSymbolNameInOtherFiles(const char* name, int thisFile, vc_vector** stringVectors, int numFiles) {
int currentFile;
char** currentString;
for (currentFile = 0; currentFile < numFiles; currentFile++) {
if (currentFile == thisFile) {
continue;
}
VC_FOREACH(currentString, stringVectors[currentFile]) {
if (strcmp(name, *currentString) == 0) {
FAIRY_DEBUG_PRINTF("Match found for %s\n", name);
return true;
}
}
}
FAIRY_DEBUG_PRINTF("No match found for %s\n", name);
return false;
}
void Fado_DestroyStringVectors(vc_vector** stringVectors, int numFiles) {
int currentFile;
for (currentFile = 0; currentFile < numFiles; currentFile++) {
vc_vector_release(stringVectors[currentFile]);
}
free(stringVectors);
}
typedef struct {
size_t symbolIndex;
int file;
uint32_t relocWord;
} FadoRelocInfo;
/* Construct the Zelda64ovl-compatible reloc word from an ELF reloc */
FadoRelocInfo Fado_MakeReloc(int file, FairySection section, FairyRel* data) {
FadoRelocInfo relocInfo = { 0 };
uint32_t sectionPrefix = 0;
relocInfo.symbolIndex = ELF32_R_SYM(data->r_info);
relocInfo.file = file;
switch (section) {
case FAIRY_SECTION_TEXT:
sectionPrefix = 1;
break;
case FAIRY_SECTION_DATA:
sectionPrefix = 2;
break;
case FAIRY_SECTION_RODATA:
sectionPrefix = 3;
break;
default:
fprintf(stderr, "warning: Relocation section is invalid.\n");
break;
}
relocInfo.relocWord =
((sectionPrefix & 3) << 0x1E) | (ELF32_R_TYPE(data->r_info) << 0x18) | (data->r_offset & 0xFFFFFF);
return relocInfo;
}
static const FairyDefineString relSectionNames[] = {
FAIRY_DEF_STRING(FAIRY_SECTION_, TEXT),
FAIRY_DEF_STRING(FAIRY_SECTION_, DATA),
FAIRY_DEF_STRING(FAIRY_SECTION_, RODATA),
{ 0 },
};
/* Taken from elf.h */
static const FairyDefineString relTypeNames[] = {
FAIRY_DEF_STRING(, R_MIPS_NONE), /* No reloc */
FAIRY_DEF_STRING(, R_MIPS_16), /* Direct 16 bit */
FAIRY_DEF_STRING(, R_MIPS_32), /* Direct 32 bit */
FAIRY_DEF_STRING(, R_MIPS_REL32), /* PC relative 32 bit */
FAIRY_DEF_STRING(, R_MIPS_26), /* Direct 26 bit shifted */
FAIRY_DEF_STRING(, R_MIPS_HI16), /* High 16 bit */
FAIRY_DEF_STRING(, R_MIPS_LO16), /* Low 16 bit */
FAIRY_DEF_STRING(, R_MIPS_GPREL16), /* GP relative 16 bit */
FAIRY_DEF_STRING(, R_MIPS_LITERAL), /* 16 bit literal entry */
FAIRY_DEF_STRING(, R_MIPS_GOT16), /* 16 bit GOT entry */
FAIRY_DEF_STRING(, R_MIPS_PC16), /* PC relative 16 bit */
FAIRY_DEF_STRING(, R_MIPS_CALL16), /* 16 bit GOT entry for function */
FAIRY_DEF_STRING(, R_MIPS_GPREL32), /* GP relative 32 bit */
FAIRY_DEF_STRING(, R_MIPS_SHIFT5),
FAIRY_DEF_STRING(, R_MIPS_SHIFT6),
FAIRY_DEF_STRING(, R_MIPS_64),
FAIRY_DEF_STRING(, R_MIPS_GOT_DISP),
FAIRY_DEF_STRING(, R_MIPS_GOT_PAGE),
FAIRY_DEF_STRING(, R_MIPS_GOT_OFST),
FAIRY_DEF_STRING(, R_MIPS_GOT_HI16),
FAIRY_DEF_STRING(, R_MIPS_GOT_LO16),
FAIRY_DEF_STRING(, R_MIPS_SUB),
FAIRY_DEF_STRING(, R_MIPS_INSERT_A),
FAIRY_DEF_STRING(, R_MIPS_INSERT_B),
FAIRY_DEF_STRING(, R_MIPS_DELETE),
FAIRY_DEF_STRING(, R_MIPS_HIGHER),
FAIRY_DEF_STRING(, R_MIPS_HIGHEST),
FAIRY_DEF_STRING(, R_MIPS_CALL_HI16),
FAIRY_DEF_STRING(, R_MIPS_CALL_LO16),
FAIRY_DEF_STRING(, R_MIPS_SCN_DISP),
FAIRY_DEF_STRING(, R_MIPS_REL16),
FAIRY_DEF_STRING(, R_MIPS_ADD_IMMEDIATE),
FAIRY_DEF_STRING(, R_MIPS_PJUMP),
FAIRY_DEF_STRING(, R_MIPS_RELGOT),
FAIRY_DEF_STRING(, R_MIPS_JALR),
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPMOD32), /* Module number 32 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPREL32), /* Module-relative offset 32 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPMOD64), /* Module number 64 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPREL64), /* Module-relative offset 64 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_GD), /* 16 bit GOT offset for GD */
FAIRY_DEF_STRING(, R_MIPS_TLS_LDM), /* 16 bit GOT offset for LDM */
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPREL_HI16), /* Module-relative offset, high 16 bits */
FAIRY_DEF_STRING(, R_MIPS_TLS_DTPREL_LO16), /* Module-relative offset, low 16 bits */
FAIRY_DEF_STRING(, R_MIPS_TLS_GOTTPREL), /* 16 bit GOT offset for IE */
FAIRY_DEF_STRING(, R_MIPS_TLS_TPREL32), /* TP-relative offset, 32 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_TPREL64), /* TP-relative offset, 64 bit */
FAIRY_DEF_STRING(, R_MIPS_TLS_TPREL_HI16), /* TP-relative offset, high 16 bits */
FAIRY_DEF_STRING(, R_MIPS_TLS_TPREL_LO16), /* TP-relative offset, low 16 bits */
FAIRY_DEF_STRING(, R_MIPS_GLOB_DAT),
FAIRY_DEF_STRING(, R_MIPS_COPY),
FAIRY_DEF_STRING(, R_MIPS_JUMP_SLOT),
FAIRY_DEF_STRING(, R_MIPS_NUM),
};
/**
* Find all the necessary relocations to retain (those defined in any input file), and print them in the appropriate
* format.
*/
void Fado_Relocs(FILE* outputFile, int inputFilesCount, FILE** inputFiles, const char* ovlName) {
/* General information structs */
FairyFileInfo* fileInfos = malloc(inputFilesCount * sizeof(FairyFileInfo));
/* Symbol tables for each file */
FairySym** symtabs = malloc(inputFilesCount * sizeof(FairySym*));
/* Lists of names of symbols defined in files of the overlay */
vc_vector** stringVectors = malloc(inputFilesCount * sizeof(vc_vector*));
/* The relocs in the format we will print */
vc_vector* relocList[FAIRY_SECTION_OTHER]; /* Maximum number of reloc sections */
/* Offset of current file's current section into the overlay's whole section */
uint32_t sectionOffset[FAIRY_SECTION_OTHER] = { 0 };
/* Total number of relocs */
uint32_t relocCount = 0;
/* iterators */
int currentFile;
FairySection section;
size_t relocIndex;
for (currentFile = 0; currentFile < inputFilesCount; currentFile++) {
FAIRY_INFO_PRINTF("Begin initialising file %d info.\n", currentFile);
Fairy_InitFile(&fileInfos[currentFile], inputFiles[currentFile]);
FAIRY_INFO_PRINTF("Initialising file %d info complete.\n", currentFile);
symtabs[currentFile] = fileInfos[currentFile].symtabInfo.sectionData;
}
Fado_ConstructStringVectors(stringVectors, fileInfos, inputFilesCount);
FAIRY_INFO_PRINTF("%s", "symtabs set\n");
/* Construct relocList of all relevant relocs */
for (section = FAIRY_SECTION_TEXT; section < FAIRY_SECTION_OTHER; section++) {
relocList[section] = vc_vector_create(0x100, sizeof(FadoRelocInfo), NULL);
for (currentFile = 0; currentFile < inputFilesCount; currentFile++) {
FairyRel* relSection = fileInfos[currentFile].relocTablesInfo[section].sectionData;
if (relSection != NULL) {
for (relocIndex = 0;
relocIndex < fileInfos[currentFile].relocTablesInfo[section].sectionSize / sizeof(FairyRel);
relocIndex++) {
FadoRelocInfo currentReloc = Fado_MakeReloc(currentFile, section, &relSection[relocIndex]);
if ((symtabs[currentFile][currentReloc.symbolIndex].st_shndx != STN_UNDEF) ||
Fado_FindSymbolNameInOtherFiles(
&fileInfos[currentFile].strtab[symtabs[currentFile][currentReloc.symbolIndex].st_name],
currentFile, stringVectors, inputFilesCount)) {
currentReloc.relocWord += sectionOffset[section];
FAIRY_DEBUG_PRINTF("current section offset: %d\n", sectionOffset[section]);
vc_vector_push_back(relocList[section], &currentReloc);
relocCount++;
}
}
} else {
FAIRY_INFO_PRINTF("%s", "Ignoring empty reloc section\n");
}
sectionOffset[section] += fileInfos[currentFile].progBitsSizes[section];
FAIRY_INFO_PRINTF("section offset: %d\n", sectionOffset[section]);
}
}
{
/* Write header */
fprintf(outputFile, ".section .ovl\n");
fprintf(outputFile, "# %sOverlayInfo\n", ovlName);
fprintf(outputFile, ".word _%sSegmentTextSize\n", ovlName);
fprintf(outputFile, ".word _%sSegmentDataSize\n", ovlName);
fprintf(outputFile, ".word _%sSegmentRoDataSize\n", ovlName);
fprintf(outputFile, ".word _%sSegmentBssSize\n", ovlName);
fprintf(outputFile, "\n.word %d # relocCount\n", relocCount);
/* Write reloc table */
for (section = FAIRY_SECTION_TEXT; section < FAIRY_SECTION_OTHER; section++) {
if (vc_vector_count(relocList[section]) == 0) {
FAIRY_INFO_PRINTF("%s", "Ignoring empty reloc section\n");
continue;
}
fprintf(outputFile, "\n# %s RELOCS\n", Fairy_StringFromDefine(relSectionNames, section));
{
FadoRelocInfo* currentReloc;
VC_FOREACH(currentReloc, relocList[section]) {
fprintf(outputFile, ".word 0x%X # %-11s 0x%06X %s\n", currentReloc->relocWord,
Fairy_StringFromDefine(relTypeNames, (currentReloc->relocWord >> 0x18) & 0x3F),
currentReloc->relocWord & 0xFFFFFF,
Fairy_GetSymbolName(symtabs[currentReloc->file], fileInfos[currentReloc->file].strtab,
currentReloc->symbolIndex));
}
}
}
/* print pads and section size */
for (relocCount += 5; ((relocCount + 1) & 3) != 0; relocCount++) {
fprintf(outputFile, ".word 0\n");
}
fprintf(outputFile, "\n.word 0x%08X # %sOverlayInfoOffset\n", 4 * (relocCount + 1), ovlName);
}
for (currentFile = 0; currentFile < inputFilesCount; currentFile++) {
Fairy_DestroyFile(&fileInfos[currentFile]);
FAIRY_INFO_PRINTF("Freed file %d\n", currentFile);
}
for (section = FAIRY_SECTION_TEXT; section < FAIRY_SECTION_OTHER; section++) {
if (relocList[section] != NULL) {
vc_vector_release(relocList[section]);
}
FAIRY_INFO_PRINTF("Freed relocList[%d]\n", section);
}
Fado_DestroyStringVectors(stringVectors, inputFilesCount);
FAIRY_INFO_PRINTF("%s", "Freed string vectors\n");
free(symtabs);
FAIRY_INFO_PRINTF("%s", "Freed symtabs\n");
free(fileInfos);
}

162
tools/fado/src/help.c Normal file
View file

@ -0,0 +1,162 @@
/**
* Getopt-compatible printing of formatted help.
*/
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
#include "help.h"
#include <assert.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "macros.h"
/* Values of the variables used by Help_PrintHelp. Defaults are taken from common terminal programs like grep */
size_t helpTextWidth = 80;
size_t helpDtIndent = 2;
size_t helpDdIndent = 25;
/**
* Prints a paragraph, word wrapped to helpTextWidth, with a hanging indent. initialColumn is used to determine how wide
* the first line should be, while indentFirstLine should be true if there is no previous text on the line (an ordinary
* paragraph), and false if there is (e.g. as a description list description)
*/
void Help_PrintFlowAndIndent(const char* string, size_t initialColumn, size_t textWidth, size_t hangingIndent,
bool indentFirstLine) {
size_t column = initialColumn;
size_t index = 0;
size_t inLength = strlen(string);
size_t lookAhead;
bool shouldBreak;
assert(initialColumn < textWidth);
assert(hangingIndent < textWidth);
if (indentFirstLine) {
printf("%*s", (int)initialColumn, "");
}
for (; index <= inLength; index++) {
shouldBreak = 0;
if (column == textWidth) {
printf("%c\n%*s", string[index], (int)hangingIndent, "");
column = hangingIndent;
continue;
}
column++;
switch (string[index]) {
case '\0':
return;
case ' ':
if (column == hangingIndent) {
continue;
}
for (lookAhead = 0; lookAhead <= textWidth - column; lookAhead++) {
// printf("%c\n", src[index + lookAhead]);
if (string[index + lookAhead + 1] == ' ' || string[index + lookAhead + 1] == '\0') {
putchar(' ');
shouldBreak = 1;
break;
}
}
if (shouldBreak) { /* Damn shared keywords. */
break;
}
case '\n':
printf("\n%*s", (int)hangingIndent, "");
column = hangingIndent;
break;
default:
putchar(string[index]);
break;
}
}
}
/**
* Prints help in the form
* ```
* prologue (word wrapped)
*
* Positional arguments
* arg1 Description (word
* wrapped)
* arg2 Description (word
* wrapped)
*
* Options
* -a --long-name=ARG Description (word
* wrapped)
*
* epilogue (word wrapped)
* ```
* where the positional arguments are described using the posArgInfo array, and options are fed using the OptInfo array,
* which should both be null-terminated. posArgCount/optCount is the actual number of positional arguments/options: it
* is used to guarantee no garbage is printed even if the user has not null-terminated the array. (optCount is required
* for constructing the getopt option array anyway.)
*/
void Help_PrintHelp(const char* prologue, size_t posArgCount, const PosArgInfo* posArgInfo, size_t optCount,
const OptInfo* optInfo, const char* epilogue) {
size_t i;
size_t dtLength;
int padding;
Help_PrintFlowAndIndent(prologue, 0, helpTextWidth, 0, false);
if (posArgCount != 0) {
printf("\nPositional Argument\n");
for (i = 0; i < optCount; i++) {
if (posArgInfo[i].helpArg == 0) {
break;
}
dtLength = helpDtIndent + strlen(posArgInfo[i].helpArg);
padding = helpDdIndent - dtLength - 2;
printf("%*s%s%*s ", (int)helpDtIndent, "", posArgInfo[i].helpArg, CLAMP_MIN(padding, 0), "");
Help_PrintFlowAndIndent(posArgInfo[i].helpMsg, CLAMP_MIN(dtLength + 2, helpDdIndent), helpTextWidth,
helpDdIndent, false);
printf("\n");
}
}
if (optCount != 0) {
printf("\nOptions\n");
for (i = 0; i < optCount; i++) {
if (optInfo[i].longOpt.val == 0) {
break;
}
dtLength = helpDtIndent + 6 + strlen(optInfo[i].longOpt.name);
if (optInfo[i].helpArg == NULL) {
padding = helpDdIndent - dtLength - 2;
printf("%*s-%c, --%s%*s ", (int)helpDtIndent, "", optInfo[i].longOpt.val, optInfo[i].longOpt.name,
CLAMP_MIN(padding, 0), "");
} else {
dtLength += 1 + strlen(optInfo[i].helpArg);
padding = helpDdIndent - dtLength - 2;
printf("%*s-%c, --%s=%s%*s ", (int)helpDtIndent, "", optInfo[i].longOpt.val, optInfo[i].longOpt.name,
optInfo[i].helpArg, CLAMP_MIN(padding, 0), "");
}
Help_PrintFlowAndIndent(optInfo[i].helpMsg, CLAMP_MIN(dtLength + 2, helpDdIndent), helpTextWidth,
helpDdIndent, false);
printf("\n");
}
}
printf("\n");
Help_PrintFlowAndIndent(epilogue, 0, helpTextWidth, 0, false);
printf("\n");
}

229
tools/fado/src/main.c Normal file
View file

@ -0,0 +1,229 @@
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "macros.h"
#include "fairy/fairy.h"
#include "fado.h"
#include "help.h"
#include "mido.h"
#include "vc_vector/vc_vector.h"
#include "version.inc"
void PrintVersion() {
printf("Fado (Fairy-Assisted relocations for Decompiled Overlays), version %s\n", versionNumber);
printf("Copyright (C) 2021 Elliptic Ellipsis\n");
printf("%s\n", credits);
printf("Repository available at %s.\n", repo);
}
#if defined _WIN32 || defined __CYGWIN__
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
/**
* (Bad) filename-parsing idea to get the overlay name from the filename. Output must be freed separately.
*/
char* GetOverlayNameFromFilename(const char* src) {
char* ret;
const char* ptr;
const char* start = src;
const char* end = src;
for (ptr = src; *ptr != '\0'; ptr++) {
if (*ptr == PATH_SEPARATOR) {
start = end + 1;
end = ptr;
}
}
if (end == src) {
return NULL;
}
ret = malloc((end - start + 1) * sizeof(char));
memcpy(ret, start, end - start);
ret[end - start] = '\0';
return ret;
}
#define OPTSTR "M:n:o:v:hV"
#define USAGE_STRING "Usage: %s [-hV] [-n name] [-o output_file] [-v level] input_files ...\n"
#define HELP_PROLOGUE \
"Fado (Fairy-Assisted relocations for Decompiled Overlays\n" \
"Extract relocations from object files and convert them into the format required by Zelda 64 overlays.\n"
#define HELP_EPILOGUE repo
// clang-format off
static const PosArgInfo posArgInfo[] = {
{ "INPUT_FILE", "Every positional argument is an input file, and there should be at least one input file. All inputs are relocatable .o (object) ELF files" },
{ NULL, NULL }
};
static const OptInfo optInfo[] = {
{ { "make-dependency", required_argument, NULL, 'M' }, "FILE", "Write the output file's Makefile dependencies to FILE" },
{ { "name", required_argument, NULL, 'n' }, "NAME", "Use NAME as the overlay name. Will use the deepest folder name in the input file's path if not specified" },
{ { "output-file", required_argument, NULL, 'o' }, "FILE", "Output to FILE. Will use stdout if none is specified" },
{ { "verbosity", required_argument, NULL, 'v' }, "N", "Verbosity level, one of 0 (None, default), 1 (Info), 2 (Debug)" },
{ { "help", no_argument, NULL, 'h' }, NULL, "Display this message and exit" },
{ { "version", no_argument, NULL, 'V' }, NULL, "Display version information" },
{ { NULL, 0, NULL, '\0' }, NULL, NULL },
};
// clang-format on
static size_t posArgCount = ARRAY_COUNT(optInfo);
static size_t optCount = ARRAY_COUNT(optInfo);
static struct option longOptions[ARRAY_COUNT(optInfo)];
void ConstructLongOpts() {
size_t i;
for (i = 0; i < optCount; i++) {
longOptions[i] = optInfo[i].longOpt;
}
}
int main(int argc, char** argv) {
int opt;
int inputFilesCount;
FILE** inputFiles;
FILE* outputFile = stdout;
char* outputFileName;
char* dependencyFileName = NULL;
char* ovlName = NULL;
ConstructLongOpts();
if (argc < 2) {
printf(USAGE_STRING, argv[0]);
fprintf(stderr, "No input file specified\n");
return EXIT_FAILURE;
}
while (true) {
int optionIndex = 0;
if ((opt = getopt_long(argc, argv, OPTSTR, longOptions, &optionIndex)) == -1) {
break;
}
switch (opt) {
case 'M':
dependencyFileName = optarg;
break;
case 'n':
ovlName = optarg;
break;
case 'o':
outputFileName = optarg;
outputFile = fopen(optarg, "wb");
if (outputFile == NULL) {
fprintf(stderr, "error: unable to open output file '%s' for writing", optarg);
return EXIT_FAILURE;
}
break;
case 'v':
if (sscanf(optarg, "%u", &gVerbosity) == 0) {
fprintf(stderr, "warning: verbosity argument '%s' should be a nonnegative decimal integer", optarg);
}
break;
case 'h':
printf(USAGE_STRING, argv[0]);
Help_PrintHelp(HELP_PROLOGUE, posArgCount, posArgInfo, optCount, optInfo, HELP_EPILOGUE);
return EXIT_FAILURE;
case 'V':
PrintVersion();
return EXIT_FAILURE;
default:
fprintf(stderr, "?? getopt returned character code 0x%X ??\n", opt);
break;
}
}
FAIRY_INFO_PRINTF("%s", "Options processed\n");
{
int i;
inputFilesCount = argc - optind;
if (inputFilesCount == 0) {
fprintf(stderr, "No input files specified. Exiting.\n");
return EXIT_FAILURE;
}
inputFiles = malloc(inputFilesCount * sizeof(FILE*));
for (i = 0; i < inputFilesCount; i++) {
FAIRY_INFO_PRINTF("Using input file %s\n", argv[optind + i]);
inputFiles[i] = fopen(argv[optind + i], "rb");
if (inputFiles[i] == NULL) {
fprintf(stderr, "error: unable to open input file '%s' for reading", argv[optind + i]);
return EXIT_FAILURE;
}
}
FAIRY_INFO_PRINTF("Found %d input file%s\n", inputFilesCount, (inputFilesCount == 1 ? "" : "s"));
if (ovlName == NULL) { // If a name has not been set using an arg
ovlName = GetOverlayNameFromFilename(argv[optind]);
Fado_Relocs(outputFile, inputFilesCount, inputFiles, ovlName);
free(ovlName);
} else {
Fado_Relocs(outputFile, inputFilesCount, inputFiles, ovlName);
}
for (i = 0; i < inputFilesCount; i++) {
fclose(inputFiles[i]);
}
free(inputFiles);
if (outputFile != stdout) {
fclose(outputFile);
}
}
if (dependencyFileName != NULL) {
int fileNameLength = strlen(outputFileName);
char* objectFile = malloc((strlen(outputFileName) + 1) * sizeof(char));
vc_vector* inputFilesVector = vc_vector_create(inputFilesCount, sizeof(char*), NULL);
char* extensionStart;
FILE* dependencyFile = fopen(dependencyFileName, "w");
if (dependencyFile == NULL) {
fprintf(stderr, "error: unable to open dependency file '%s' for writing", dependencyFileName);
return EXIT_FAILURE;
}
strcpy(objectFile, outputFileName);
extensionStart = strrchr(objectFile, '.');
if (extensionStart == objectFile + fileNameLength) {
fprintf(stderr, "error: file name should not end in a '.'");
return EXIT_FAILURE;
}
strcpy(extensionStart, ".o");
vc_vector_append(inputFilesVector, &argv[optind], inputFilesCount);
Mido_WriteDependencyFile(dependencyFile, objectFile, inputFilesVector);
free(objectFile);
vc_vector_release(inputFilesVector);
fclose(dependencyFile);
}
return EXIT_SUCCESS;
}

20
tools/fado/src/mido.c Normal file
View file

@ -0,0 +1,20 @@
#include "mido.h"
#include <stdio.h>
#include "macros.h"
#include "vc_vector/vc_vector.h"
int Mido_WriteDependencyFile(FILE* dependencyFile, const char* relocFile, vc_vector* inputFilesVector) {
char** inputFile;
fprintf(dependencyFile, "%s:", relocFile);
VC_FOREACH(inputFile, inputFilesVector) {
fprintf(dependencyFile, " %s", *inputFile);
}
fputs("\n\n", dependencyFile);
VC_FOREACH(inputFile, inputFilesVector) {
fprintf(dependencyFile, "%s:\n\n", *inputFile);
}
return 0;
}

View file

@ -0,0 +1,5 @@
/* Copyright (C) 2021 Elliptic Ellipsis */
/* SPDX-License-Identifier: AGPL-3.0-only */
const char versionNumber[] = "1.1.1";
const char credits[] = "Written by Elliptic Ellipsis\n Special Thanks: AngheloAlf";
const char repo[] = "https://github.com/EllipticEllipsis/fado/";