mirror of
https://github.com/zeldaret/oot.git
synced 2025-02-26 02:44:33 +00:00
git subrepo pull (merge) tools/fado (#1501)
subrepo: subdir: "tools/fado" merged: "8d896ee97" upstream: origin: "git@github.com:EllipticEllipsis/fado.git" branch: "master" commit: "8d896ee97" git-subrepo: version: "0.4.5" origin: "git@github.com:ingydotnet/git-subrepo.git" commit: "dbb99be"
This commit is contained in:
parent
9f0b7bb8a3
commit
d4a6b21d46
11 changed files with 160 additions and 101 deletions
|
@ -6,7 +6,7 @@
|
||||||
[subrepo]
|
[subrepo]
|
||||||
remote = git@github.com:EllipticEllipsis/fado.git
|
remote = git@github.com:EllipticEllipsis/fado.git
|
||||||
branch = master
|
branch = master
|
||||||
commit = a0fa828089353ba48e378b281c23100c247b1c92
|
commit = 8d896ee97d565508755584803c409fc33bb0c953
|
||||||
parent = eadc477187888e1ae078d021b4a00b1366f0c9a4
|
parent = 9f09505d34619883748a7dab05071883281c14fd
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.3
|
cmdver = 0.4.5
|
||||||
|
|
|
@ -7,9 +7,9 @@ Contains
|
||||||
- **Fado** a program for generating the `.ovl`/relocation section for Zelda64 overlay files
|
- **Fado** a program for generating the `.ovl`/relocation section for Zelda64 overlay files
|
||||||
- **Mido** an automatic dependency file generator
|
- **Mido** an automatic dependency file generator
|
||||||
|
|
||||||
Compatible with both IDO and GCC (although [see below](N_B)).
|
Compatible with both IDO and GCC (although [see below](N_B)). Both ordinary MIPS REL sections and RELA sections are now supported.
|
||||||
|
|
||||||
Format is the standard "Zelda64" .ovl section, with the relocs divided by section, as used by
|
Output format is the standard "Zelda64" .ovl section, with the relocs divided by section, as used by
|
||||||
- *The Legend of Zelda: Ocarina of Time* (all Nintendo 64/Gamecube/iQue releases)
|
- *The Legend of Zelda: Ocarina of Time* (all Nintendo 64/Gamecube/iQue releases)
|
||||||
- *The Legend of Zelda: Majora's Mask* (all Nintendo 64/Gamecube releases)
|
- *The Legend of Zelda: Majora's Mask* (all Nintendo 64/Gamecube releases)
|
||||||
|
|
||||||
|
|
|
@ -473,6 +473,14 @@ typedef struct {
|
||||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||||
} Elf32_Rel;
|
} Elf32_Rel;
|
||||||
|
|
||||||
|
/* Relocation table entry with addend (in section of type SHT_RELA). */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Elf32_Addr r_offset; /* Address */
|
||||||
|
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||||
|
Elf32_Sword r_addend; /* Addend */
|
||||||
|
} Elf32_Rela;
|
||||||
|
|
||||||
/* How to extract and insert information held in the r_info field. */
|
/* How to extract and insert information held in the r_info field. */
|
||||||
|
|
||||||
#define ELF32_R_SYM(val) ((val) >> 8)
|
#define ELF32_R_SYM(val) ((val) >> 8)
|
||||||
|
|
|
@ -105,7 +105,7 @@ bool Fairy_StartsWith(const char* string, const char* initial) {
|
||||||
|
|
||||||
FairyFileHeader* Fairy_ReadFileHeader(FairyFileHeader* header, FILE* file) {
|
FairyFileHeader* Fairy_ReadFileHeader(FairyFileHeader* header, FILE* file) {
|
||||||
fseek(file, 0, SEEK_SET);
|
fseek(file, 0, SEEK_SET);
|
||||||
assert(fread(header, 0x34, 1, file) != 0);
|
assert(fread(header, sizeof(char), 0x34, file) == 0x34);
|
||||||
|
|
||||||
if (!Fairy_VerifyMagic(header->e_ident)) {
|
if (!Fairy_VerifyMagic(header->e_ident)) {
|
||||||
fprintf(stderr, "Not a valid ELF file.\n");
|
fprintf(stderr, "Not a valid ELF file.\n");
|
||||||
|
@ -150,7 +150,7 @@ FairySecHeader* Fairy_ReadSectionTable(FairySecHeader* sectionTable, FILE* file,
|
||||||
size_t tableSize = number * entrySize;
|
size_t tableSize = number * entrySize;
|
||||||
|
|
||||||
fseek(file, tableOffset, SEEK_SET);
|
fseek(file, tableOffset, SEEK_SET);
|
||||||
assert(fread(sectionTable, tableSize, 1, file) != 0);
|
assert(fread(sectionTable, sizeof(char), tableSize, file) == tableSize);
|
||||||
|
|
||||||
/* Since the section table happens to only have entries of width 4, we can byteswap it by pretending it is a raw
|
/* Since the section table happens to only have entries of width 4, we can byteswap it by pretending it is a raw
|
||||||
* uint32_t array */
|
* uint32_t array */
|
||||||
|
@ -165,13 +165,21 @@ FairySecHeader* Fairy_ReadSectionTable(FairySecHeader* sectionTable, FILE* file,
|
||||||
return sectionTable;
|
return sectionTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
FairySym* Fairy_ReadSymbolTable(FairySym* symbolTable, FILE* file, size_t tableOffset, size_t tableSize) {
|
size_t Fairy_ReadSymbolTable(FairySym** symbolTableOut, FILE* file, size_t tableOffset, size_t tableSize) {
|
||||||
size_t number = tableSize / sizeof(FairySym);
|
size_t number = tableSize / sizeof(FairySym);
|
||||||
|
FairySym* symbolTable = malloc(tableSize);
|
||||||
|
|
||||||
fseek(file, tableOffset, SEEK_SET);
|
*symbolTableOut = NULL;
|
||||||
assert(fread(symbolTable, tableSize, 1, file) != 0);
|
|
||||||
|
|
||||||
/* Reend the variables that are larger than bytes */
|
if (symbolTable == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (fseek(file, tableOffset, SEEK_SET) != 0 || fread(symbolTable, sizeof(char), tableSize, file) != tableSize) {
|
||||||
|
free(symbolTable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reend the variables that are wider than bytes */
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < number; i++) {
|
for (i = 0; i < number; i++) {
|
||||||
|
@ -182,31 +190,65 @@ FairySym* Fairy_ReadSymbolTable(FairySym* symbolTable, FILE* file, size_t tableO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbolTable;
|
*symbolTableOut = symbolTable;
|
||||||
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Can be used for both the section header string table and the strtab */
|
/* Can be used for both the section header string table and the strtab */
|
||||||
char* Fairy_ReadStringTable(char* stringTable, FILE* file, size_t tableOffset, size_t tableSize) {
|
char* Fairy_ReadStringTable(char* stringTable, FILE* file, size_t tableOffset, size_t tableSize) {
|
||||||
fseek(file, tableOffset, SEEK_SET);
|
fseek(file, tableOffset, SEEK_SET);
|
||||||
assert(fread(stringTable, tableSize, 1, file) != 0);
|
assert(fread(stringTable, sizeof(char), tableSize, file) == tableSize);
|
||||||
return stringTable;
|
return stringTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* offset and number are attained from the section table */
|
/* offset and number are attained from the section table, the returned pointer must be freed */
|
||||||
FairyRel* Fairy_ReadRelocs(FairyRel* relocTable, FILE* file, size_t offset, size_t size) {
|
size_t Fairy_ReadRelocs(FairyRela** relocsOut, FILE* file, int type, size_t offset, size_t size) {
|
||||||
fseek(file, offset, SEEK_SET);
|
/* Final size of the relocation table, relocations of type SHT_REL need more space for extra addend of 0 */
|
||||||
assert(fread(relocTable, size, 1, file) != 0);
|
size_t finalSize = (type == SHT_REL) ? ((size * sizeof(FairyRela)) / sizeof(FairyRel)) : size;
|
||||||
|
void* readBuf = malloc(size);
|
||||||
|
FairyRela* relocTable = malloc(finalSize);
|
||||||
|
|
||||||
/* Reend the variables that are larger than bytes */
|
*relocsOut = NULL;
|
||||||
|
|
||||||
|
if (readBuf == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (relocTable == NULL) {
|
||||||
|
free(readBuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (fseek(file, offset, SEEK_SET) != 0 || fread(readBuf, sizeof(char), size, file) != size) {
|
||||||
|
free(readBuf);
|
||||||
|
free(relocTable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reend the variables that are wider than bytes */
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
uint32_t* data = (uint32_t*)relocTable;
|
uint32_t* data = (uint32_t*)readBuf;
|
||||||
for (i = 0; i < size / sizeof(uint32_t); i++) {
|
for (i = 0; i < size / sizeof(uint32_t); i++) {
|
||||||
data[i] = REEND32(data[i]);
|
data[i] = REEND32(data[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return relocTable;
|
/* Make the relocation table, for SHT_REL sections add an addend of 0 */
|
||||||
|
if (type == SHT_REL) {
|
||||||
|
size_t i;
|
||||||
|
FairyRel* rel = (FairyRel*)readBuf;
|
||||||
|
|
||||||
|
for (i = 0; i < size / sizeof(FairyRel); i++) {
|
||||||
|
relocTable[i].r_info = rel[i].r_info;
|
||||||
|
relocTable[i].r_offset = rel[i].r_offset;
|
||||||
|
relocTable[i].r_addend = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(relocTable, readBuf, size);
|
||||||
|
}
|
||||||
|
free(readBuf);
|
||||||
|
|
||||||
|
*relocsOut = relocTable;
|
||||||
|
return finalSize / sizeof(FairyRela);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Fairy_GetSectionName(FairySecHeader* sectionTable, char* shstrtab, size_t index) {
|
char* Fairy_GetSectionName(FairySecHeader* sectionTable, char* shstrtab, size_t index) {
|
||||||
|
@ -240,7 +282,8 @@ void Fairy_InitFile(FairyFileInfo* fileInfo, FILE* file) {
|
||||||
|
|
||||||
shstrtab = malloc(sectionTable[fileHeader.e_shstrndx].sh_size * sizeof(char));
|
shstrtab = malloc(sectionTable[fileHeader.e_shstrndx].sh_size * sizeof(char));
|
||||||
fseek(file, sectionTable[fileHeader.e_shstrndx].sh_offset, SEEK_SET);
|
fseek(file, sectionTable[fileHeader.e_shstrndx].sh_offset, SEEK_SET);
|
||||||
assert(fread(shstrtab, sectionTable[fileHeader.e_shstrndx].sh_size, 1, file) != 0);
|
assert(fread(shstrtab, sizeof(char), sectionTable[fileHeader.e_shstrndx].sh_size, file) ==
|
||||||
|
sectionTable[fileHeader.e_shstrndx].sh_size);
|
||||||
|
|
||||||
/* Search for the sections we need */
|
/* Search for the sections we need */
|
||||||
{
|
{
|
||||||
|
@ -251,6 +294,8 @@ void Fairy_InitFile(FairyFileInfo* fileInfo, FILE* file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (currentIndex = 0; currentIndex < fileHeader.e_shnum; currentIndex++) {
|
for (currentIndex = 0; currentIndex < fileHeader.e_shnum; currentIndex++) {
|
||||||
|
size_t off = 0;
|
||||||
|
|
||||||
currentSection = sectionTable[currentIndex];
|
currentSection = sectionTable[currentIndex];
|
||||||
|
|
||||||
switch (currentSection.sh_type) {
|
switch (currentSection.sh_type) {
|
||||||
|
@ -299,10 +344,11 @@ void Fairy_InitFile(FairyFileInfo* fileInfo, FILE* file) {
|
||||||
|
|
||||||
case SHT_SYMTAB:
|
case SHT_SYMTAB:
|
||||||
if (strcmp(&shstrtab[currentSection.sh_name + 1], "symtab") == 0) {
|
if (strcmp(&shstrtab[currentSection.sh_name + 1], "symtab") == 0) {
|
||||||
fileInfo->symtabInfo.sectionSize = currentSection.sh_size;
|
fileInfo->symtabInfo.sectionType = SHT_SYMTAB;
|
||||||
fileInfo->symtabInfo.sectionData = malloc(currentSection.sh_size);
|
fileInfo->symtabInfo.sectionEntrySize = sizeof(FairySym);
|
||||||
Fairy_ReadSymbolTable(fileInfo->symtabInfo.sectionData, file, currentSection.sh_offset,
|
fileInfo->symtabInfo.sectionEntryCount =
|
||||||
currentSection.sh_size);
|
Fairy_ReadSymbolTable((FairySym**)&fileInfo->symtabInfo.sectionData, file,
|
||||||
|
currentSection.sh_offset, currentSection.sh_size);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -314,30 +360,32 @@ void Fairy_InitFile(FairyFileInfo* fileInfo, FILE* file) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SHT_RELA:
|
||||||
|
off += 1;
|
||||||
case SHT_REL:
|
case SHT_REL:
|
||||||
|
off += 5;
|
||||||
/* This assumes only one reloc section of each name */
|
/* This assumes only one reloc section of each name */
|
||||||
// TODO: is this a problem?
|
// TODO: is this a problem?
|
||||||
{
|
{
|
||||||
FairySection relocSection = FAIRY_SECTION_OTHER;
|
FairySection relocSection = FAIRY_SECTION_OTHER;
|
||||||
|
|
||||||
/* Ignore the first 5 chars, which will always be ".rel." */
|
/* Ignore the first 5/6 chars, which will always be ".rel."/".rela." */
|
||||||
if (strcmp(&shstrtab[currentSection.sh_name + 5], "text") == 0) {
|
if (strcmp(&shstrtab[currentSection.sh_name + off], "text") == 0) {
|
||||||
relocSection = FAIRY_SECTION_TEXT;
|
relocSection = FAIRY_SECTION_TEXT;
|
||||||
FAIRY_DEBUG_PRINTF("%s", "Found rel.text section\n");
|
} else if (strcmp(&shstrtab[currentSection.sh_name + off], "data") == 0) {
|
||||||
} else if (strcmp(&shstrtab[currentSection.sh_name + 5], "data") == 0) {
|
|
||||||
relocSection = FAIRY_SECTION_DATA;
|
relocSection = FAIRY_SECTION_DATA;
|
||||||
FAIRY_DEBUG_PRINTF("%s", "Found rel.data section\n");
|
} else if (strcmp(&shstrtab[currentSection.sh_name + off], "rodata") == 0) {
|
||||||
} else if (strcmp(&shstrtab[currentSection.sh_name + 5], "rodata") == 0) {
|
|
||||||
relocSection = FAIRY_SECTION_RODATA;
|
relocSection = FAIRY_SECTION_RODATA;
|
||||||
FAIRY_DEBUG_PRINTF("%s", "Found rel.rodata section\n");
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
FAIRY_DEBUG_PRINTF("Found %s section\n", &shstrtab[currentSection.sh_name]);
|
||||||
|
|
||||||
fileInfo->relocTablesInfo[relocSection].sectionSize = currentSection.sh_size;
|
fileInfo->relocTablesInfo[relocSection].sectionType = SHT_RELA;
|
||||||
fileInfo->relocTablesInfo[relocSection].sectionData = malloc(currentSection.sh_size);
|
fileInfo->relocTablesInfo[relocSection].sectionEntrySize = sizeof(FairyRela);
|
||||||
Fairy_ReadRelocs(fileInfo->relocTablesInfo[relocSection].sectionData, file,
|
fileInfo->relocTablesInfo[relocSection].sectionEntryCount =
|
||||||
currentSection.sh_offset, currentSection.sh_size);
|
Fairy_ReadRelocs((FairyRela**)&fileInfo->relocTablesInfo[relocSection].sectionData, file,
|
||||||
|
currentSection.sh_type, currentSection.sh_offset, currentSection.sh_size);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ typedef Elf32_Ehdr FairyFileHeader;
|
||||||
typedef Elf32_Shdr FairySecHeader;
|
typedef Elf32_Shdr FairySecHeader;
|
||||||
typedef Elf32_Sym FairySym;
|
typedef Elf32_Sym FairySym;
|
||||||
typedef Elf32_Rel FairyRel;
|
typedef Elf32_Rel FairyRel;
|
||||||
|
typedef Elf32_Rela FairyRela;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int define;
|
int define;
|
||||||
|
@ -32,7 +33,9 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* sectionData;
|
void* sectionData;
|
||||||
size_t sectionSize;
|
int sectionType;
|
||||||
|
size_t sectionEntryCount;
|
||||||
|
size_t sectionEntrySize;
|
||||||
} FairySectionInfo;
|
} FairySectionInfo;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -60,9 +63,9 @@ bool Fairy_StartsWith(const char* string, const char* initial);
|
||||||
|
|
||||||
FairyFileHeader* Fairy_ReadFileHeader(FairyFileHeader* header, FILE* file);
|
FairyFileHeader* Fairy_ReadFileHeader(FairyFileHeader* header, FILE* file);
|
||||||
FairySecHeader* Fairy_ReadSectionTable(FairySecHeader* sectionTable, FILE* file, size_t tableOffset, size_t number);
|
FairySecHeader* Fairy_ReadSectionTable(FairySecHeader* sectionTable, FILE* file, size_t tableOffset, size_t number);
|
||||||
FairySym* Fairy_ReadSymbolTable(FairySym* symbolTable, FILE* file, size_t tableOffset, size_t tableSize);
|
|
||||||
char* Fairy_ReadStringTable(char* stringTable, FILE* file, size_t tableOffset, size_t tableSize);
|
char* Fairy_ReadStringTable(char* stringTable, FILE* file, size_t tableOffset, size_t tableSize);
|
||||||
FairyRel* Fairy_ReadRelocs(FairyRel* relocTable, FILE* file, size_t offset, size_t number);
|
size_t Fairy_ReadSymbolTable(FairySym** symbolTableOut, FILE* file, size_t tableOffset, size_t tableSize);
|
||||||
|
size_t Fairy_ReadRelocs(FairyRela** relocsOut, FILE* file, int type, size_t offset, size_t size);
|
||||||
|
|
||||||
char* Fairy_GetSectionName(FairySecHeader* sectionTable, char* shstrtab, size_t index);
|
char* Fairy_GetSectionName(FairySecHeader* sectionTable, char* shstrtab, size_t index);
|
||||||
char* Fairy_GetSymbolName(FairySym* symtab, char* strtab, size_t index);
|
char* Fairy_GetSymbolName(FairySym* symtab, char* strtab, size_t index);
|
||||||
|
|
|
@ -32,7 +32,7 @@ void Fairy_PrintSymbolTable(FILE* inputFile) {
|
||||||
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
||||||
|
|
||||||
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
||||||
assert(fread(shstrtab, sectionTable[shstrndx].sh_size, 1, inputFile) != 0);
|
assert(fread(shstrtab, sizeof(char), sectionTable[shstrndx].sh_size, inputFile) == sectionTable[shstrndx].sh_size);
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t currentIndex;
|
size_t currentIndex;
|
||||||
|
@ -44,9 +44,8 @@ void Fairy_PrintSymbolTable(FILE* inputFile) {
|
||||||
case SHT_SYMTAB:
|
case SHT_SYMTAB:
|
||||||
if (strcmp(&shstrtab[currentHeader.sh_name], ".symtab") == 0) {
|
if (strcmp(&shstrtab[currentHeader.sh_name], ".symtab") == 0) {
|
||||||
printf("symtab found\n");
|
printf("symtab found\n");
|
||||||
symbolTableNum = currentHeader.sh_size / sizeof(FairySym);
|
symbolTableNum = Fairy_ReadSymbolTable(&symbolTable, inputFile, currentHeader.sh_offset,
|
||||||
symbolTable = malloc(currentHeader.sh_size);
|
currentHeader.sh_size);
|
||||||
Fairy_ReadSymbolTable(symbolTable, inputFile, currentHeader.sh_offset, currentHeader.sh_size);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -74,7 +73,8 @@ void Fairy_PrintSymbolTable(FILE* inputFile) {
|
||||||
printf("and mallocked\n");
|
printf("and mallocked\n");
|
||||||
fseek(inputFile, sectionTable[strtabndx].sh_offset, SEEK_SET);
|
fseek(inputFile, sectionTable[strtabndx].sh_offset, SEEK_SET);
|
||||||
printf("file offset sought: %X\n", sectionTable[strtabndx].sh_offset);
|
printf("file offset sought: %X\n", sectionTable[strtabndx].sh_offset);
|
||||||
assert(fread(strtab, sectionTable[strtabndx].sh_size, 1, inputFile) != 0);
|
assert(fread(strtab, sizeof(char), sectionTable[strtabndx].sh_size, inputFile) ==
|
||||||
|
sectionTable[strtabndx].sh_size);
|
||||||
printf("file read\n");
|
printf("file read\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ void Fairy_PrintSymbolTable(FILE* inputFile) {
|
||||||
void Fairy_PrintRelocs(FILE* inputFile) {
|
void Fairy_PrintRelocs(FILE* inputFile) {
|
||||||
FairyFileHeader fileHeader;
|
FairyFileHeader fileHeader;
|
||||||
FairySecHeader* sectionTable;
|
FairySecHeader* sectionTable;
|
||||||
FairyRel* relocs;
|
FairyRela* relocs;
|
||||||
size_t shstrndx;
|
size_t shstrndx;
|
||||||
char* shstrtab;
|
char* shstrtab;
|
||||||
size_t currentSection;
|
size_t currentSection;
|
||||||
|
@ -131,29 +131,28 @@ void Fairy_PrintRelocs(FILE* inputFile) {
|
||||||
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
||||||
|
|
||||||
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
||||||
assert(fread(shstrtab, sectionTable[shstrndx].sh_size, 1, inputFile) != 0);
|
assert(fread(shstrtab, sizeof(char), sectionTable[shstrndx].sh_size, inputFile) == sectionTable[shstrndx].sh_size);
|
||||||
|
|
||||||
for (currentSection = 0; currentSection < fileHeader.e_shnum; currentSection++) {
|
for (currentSection = 0; currentSection < fileHeader.e_shnum; currentSection++) {
|
||||||
if (sectionTable[currentSection].sh_type != SHT_REL) {
|
size_t nRelocs;
|
||||||
|
|
||||||
|
if (sectionTable[currentSection].sh_type != SHT_REL || sectionTable[currentSection].sh_type != SHT_RELA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
printf("Section size: %d\n", sectionTable[currentSection].sh_size);
|
printf("Section size: %d\n", sectionTable[currentSection].sh_size);
|
||||||
|
|
||||||
relocs = malloc(sectionTable[currentSection].sh_size * sizeof(char));
|
nRelocs = Fairy_ReadRelocs(&relocs, inputFile, sectionTable[currentSection].sh_type,
|
||||||
|
sectionTable[currentSection].sh_offset, sectionTable[currentSection].sh_size);
|
||||||
Fairy_ReadRelocs(relocs, inputFile, sectionTable[currentSection].sh_offset,
|
|
||||||
sectionTable[currentSection].sh_size);
|
|
||||||
|
|
||||||
// fseek(inputFile, sectionTable[currentSection].sh_offset, SEEK_SET);
|
// fseek(inputFile, sectionTable[currentSection].sh_offset, SEEK_SET);
|
||||||
// assert(fread(relocs, sectionTable[currentSection].sh_size, 1, inputFile) != 0);
|
// assert(fread(relocs, sizeof(char), sectionTable[currentSection].sh_size, inputFile) ==
|
||||||
|
// sectionTable[currentSection].sh_size);
|
||||||
|
|
||||||
printf("Relocs in section [%2zd]: %s:\n", currentSection, shstrtab + sectionTable[currentSection].sh_name);
|
printf("Relocs in section [%2zd]: %s:\n", currentSection, shstrtab + sectionTable[currentSection].sh_name);
|
||||||
printf("Offset Info Type Symbol\n");
|
printf("Offset Info Type Symbol\n");
|
||||||
{
|
{
|
||||||
size_t currentReloc;
|
size_t currentReloc;
|
||||||
for (currentReloc = 0; currentReloc < sectionTable[currentSection].sh_size / sizeof(*relocs);
|
for (currentReloc = 0; currentReloc < nRelocs; currentReloc++) {
|
||||||
currentReloc++) {
|
|
||||||
|
|
||||||
printf("%08X,%08X ", relocs[currentReloc].r_offset, relocs[currentReloc].r_info);
|
printf("%08X,%08X ", relocs[currentReloc].r_offset, relocs[currentReloc].r_info);
|
||||||
|
|
||||||
switch (ELF32_R_TYPE(relocs[currentReloc].r_info)) {
|
switch (ELF32_R_TYPE(relocs[currentReloc].r_info)) {
|
||||||
|
@ -212,7 +211,7 @@ void Fairy_PrintSectionTable(FILE* inputFile) {
|
||||||
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
||||||
|
|
||||||
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
||||||
assert(fread(shstrtab, sectionTable[shstrndx].sh_size, 1, inputFile) != 0);
|
assert(fread(shstrtab, sizeof(char), sectionTable[shstrndx].sh_size, inputFile) == sectionTable[shstrndx].sh_size);
|
||||||
|
|
||||||
printf("[Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n");
|
printf("[Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n");
|
||||||
for (currentSection = 0; currentSection < fileHeader.e_shnum; currentSection++) {
|
for (currentSection = 0; currentSection < fileHeader.e_shnum; currentSection++) {
|
||||||
|
@ -261,7 +260,7 @@ const char* relSectionStrings[] = {
|
||||||
".rodata",
|
".rodata",
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t Fairy_PackReloc(FairyOverlayRelSection sec, FairyRel rel) {
|
static uint32_t Fairy_PackReloc(FairyOverlayRelSection sec, FairyRela rel) {
|
||||||
return (sec << 0x1E) | (ELF32_R_TYPE(rel.r_info) << 0x18) | rel.r_offset;
|
return (sec << 0x1E) | (ELF32_R_TYPE(rel.r_info) << 0x18) | rel.r_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +289,7 @@ void Fairy_PrintSectionSizes(FairySecHeader* sectionTable, FILE* inputFile, size
|
||||||
bool strtabFound = false;
|
bool strtabFound = false;
|
||||||
/* Count the reloc sections */
|
/* Count the reloc sections */
|
||||||
for (currentSection = 0; currentSection < number; currentSection++) {
|
for (currentSection = 0; currentSection < number; currentSection++) {
|
||||||
if (sectionTable[currentSection].sh_type == SHT_REL) {
|
if (sectionTable[currentSection].sh_type == SHT_REL || sectionTable[currentSection].sh_type == SHT_RELA) {
|
||||||
relocSectionsCount++;
|
relocSectionsCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,6 +300,8 @@ void Fairy_PrintSectionSizes(FairySecHeader* sectionTable, FILE* inputFile, size
|
||||||
|
|
||||||
/* Find the section sizes and the reloc sections */
|
/* Find the section sizes and the reloc sections */
|
||||||
for (currentSection = 0; currentSection < number; currentSection++) {
|
for (currentSection = 0; currentSection < number; currentSection++) {
|
||||||
|
size_t off = 0;
|
||||||
|
|
||||||
currentHeader = sectionTable[currentSection];
|
currentHeader = sectionTable[currentSection];
|
||||||
sectionName = &shstrtab[currentHeader.sh_name + 1]; /* ignore the initial '.' */
|
sectionName = &shstrtab[currentHeader.sh_name + 1]; /* ignore the initial '.' */
|
||||||
switch (currentHeader.sh_type) {
|
switch (currentHeader.sh_type) {
|
||||||
|
@ -329,17 +330,19 @@ void Fairy_PrintSectionSizes(FairySecHeader* sectionTable, FILE* inputFile, size
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SHT_RELA:
|
||||||
|
off += 1;
|
||||||
case SHT_REL:
|
case SHT_REL:
|
||||||
relocSectionIndices[currentRelocSection] = currentSection;
|
relocSectionIndices[currentRelocSection] = currentSection;
|
||||||
sectionName += 4; /* ignore the "rel." part */
|
off += 4; /* ignore the "rel."/"rela." part */
|
||||||
if (Fairy_StartsWith(sectionName, "rodata")) {
|
if (Fairy_StartsWith(§ionName[off], "rodata")) {
|
||||||
printf(".rel.rodata\n");
|
printf("%s\n", sectionName);
|
||||||
relocSectionSection[currentRelocSection] = REL_SECTION_RODATA;
|
relocSectionSection[currentRelocSection] = REL_SECTION_RODATA;
|
||||||
} else if (Fairy_StartsWith(sectionName, "data")) {
|
} else if (Fairy_StartsWith(§ionName[off], "data")) {
|
||||||
printf(".rel.data\n");
|
printf("%s\n", sectionName);
|
||||||
relocSectionSection[currentRelocSection] = REL_SECTION_DATA;
|
relocSectionSection[currentRelocSection] = REL_SECTION_DATA;
|
||||||
} else if (Fairy_StartsWith(sectionName, "text")) {
|
} else if (Fairy_StartsWith(§ionName[off], "text")) {
|
||||||
printf(".rel.text\n");
|
printf("%s\n", sectionName);
|
||||||
relocSectionSection[currentRelocSection] = REL_SECTION_TEXT;
|
relocSectionSection[currentRelocSection] = REL_SECTION_TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,36 +376,34 @@ void Fairy_PrintSectionSizes(FairySecHeader* sectionTable, FILE* inputFile, size
|
||||||
printf(".word 0x%08X # .bss size\n\n", bssSize);
|
printf(".word 0x%08X # .bss size\n\n", bssSize);
|
||||||
|
|
||||||
if (!symtabFound) {
|
if (!symtabFound) {
|
||||||
fprintf(stderr, "Symbol table not found");
|
fprintf(stderr, "Symbol table not found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Obtain the symbol table */
|
/* Obtain the symbol table */
|
||||||
symtab = malloc(symtabHeader.sh_size);
|
|
||||||
|
|
||||||
// TODO: Consider replacing this with a lighter-weight read: sufficient to get the name, shndx
|
// TODO: Consider replacing this with a lighter-weight read: sufficient to get the name, shndx
|
||||||
Fairy_ReadSymbolTable(symtab, inputFile, symtabHeader.sh_offset, symtabHeader.sh_size);
|
Fairy_ReadSymbolTable(&symtab, inputFile, symtabHeader.sh_offset, symtabHeader.sh_size);
|
||||||
|
|
||||||
if (!strtabFound) {
|
if (!strtabFound) {
|
||||||
fprintf(stderr, "String table not found");
|
fprintf(stderr, "String table not found\n");
|
||||||
} else {
|
} else {
|
||||||
/* Obtain the string table */
|
/* Obtain the string table */
|
||||||
strtab = malloc(strtabHeader.sh_size);
|
strtab = malloc(strtabHeader.sh_size);
|
||||||
fseek(inputFile, strtabHeader.sh_offset, SEEK_SET);
|
fseek(inputFile, strtabHeader.sh_offset, SEEK_SET);
|
||||||
assert(fread(strtab, strtabHeader.sh_size, 1, inputFile) != 0);
|
assert(fread(strtab, sizeof(char), strtabHeader.sh_size, inputFile) == strtabHeader.sh_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do single-file relocs */
|
/* Do single-file relocs */
|
||||||
{
|
{
|
||||||
FairyRel* relocs;
|
FairyRela* relocs;
|
||||||
for (currentSection = 0; currentSection < relocSectionsCount; currentSection++) {
|
for (currentSection = 0; currentSection < relocSectionsCount; currentSection++) {
|
||||||
size_t currentReloc;
|
size_t currentReloc;
|
||||||
size_t sectionRelocCount;
|
size_t nRelocs;
|
||||||
currentHeader = sectionTable[relocSectionIndices[currentSection]];
|
|
||||||
sectionRelocCount = currentHeader.sh_size / sizeof(FairyRel);
|
|
||||||
relocs = malloc(currentHeader.sh_size);
|
|
||||||
Fairy_ReadRelocs(relocs, inputFile, currentHeader.sh_offset, currentHeader.sh_size);
|
|
||||||
|
|
||||||
for (currentReloc = 0; currentReloc < sectionRelocCount; currentReloc++) {
|
currentHeader = sectionTable[relocSectionIndices[currentSection]];
|
||||||
|
nRelocs = Fairy_ReadRelocs(&relocs, inputFile, currentHeader.sh_type, currentHeader.sh_offset,
|
||||||
|
currentHeader.sh_size);
|
||||||
|
|
||||||
|
for (currentReloc = 0; currentReloc < nRelocs; currentReloc++) {
|
||||||
FairySym symbol = symtab[ELF32_R_SYM(relocs[currentReloc].r_info)];
|
FairySym symbol = symtab[ELF32_R_SYM(relocs[currentReloc].r_info)];
|
||||||
if (symbol.st_shndx == SHN_UNDEF) {
|
if (symbol.st_shndx == SHN_UNDEF) {
|
||||||
continue; // TODO: this is where multifile has to look elsewhere
|
continue; // TODO: this is where multifile has to look elsewhere
|
||||||
|
@ -453,7 +454,7 @@ void PrintZeldaReloc(FILE* inputFile) {
|
||||||
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
shstrtab = malloc(sectionTable[shstrndx].sh_size * sizeof(char));
|
||||||
|
|
||||||
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
fseek(inputFile, sectionTable[shstrndx].sh_offset, SEEK_SET);
|
||||||
assert(fread(shstrtab, sectionTable[shstrndx].sh_size, 1, inputFile) != 0);
|
assert(fread(shstrtab, sizeof(char), sectionTable[shstrndx].sh_size, inputFile) == sectionTable[shstrndx].sh_size);
|
||||||
|
|
||||||
Fairy_PrintSectionSizes(sectionTable, inputFile, fileHeader.e_shentsize * fileHeader.e_shnum, shstrtab);
|
Fairy_PrintSectionSizes(sectionTable, inputFile, fileHeader.e_shentsize * fileHeader.e_shnum, shstrtab);
|
||||||
|
|
||||||
|
|
|
@ -112,15 +112,15 @@ bool vc_vector_is_equals(vc_vector* vector1, vc_vector* vector2) {
|
||||||
return memcmp(vector1->data, vector2->data, size_vector1) == 0;
|
return memcmp(vector1->data, vector2->data, size_vector1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float vc_vector_get_growth_factor() {
|
float vc_vector_get_growth_factor(void) {
|
||||||
return GROWTH_FACTOR;
|
return GROWTH_FACTOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t vc_vector_get_default_count_of_elements() {
|
size_t vc_vector_get_default_count_of_elements(void) {
|
||||||
return DEFAULT_COUNT_OF_ELEMENTS;
|
return DEFAULT_COUNT_OF_ELEMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t vc_vector_struct_size() {
|
size_t vc_vector_struct_size(void) {
|
||||||
return sizeof(vc_vector);
|
return sizeof(vc_vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ void vc_vector_release(vc_vector* vector);
|
||||||
bool vc_vector_is_equals(vc_vector* vector1, vc_vector* vector2);
|
bool vc_vector_is_equals(vc_vector* vector1, vc_vector* vector2);
|
||||||
|
|
||||||
// Returns constant value of the vector growth factor.
|
// Returns constant value of the vector growth factor.
|
||||||
float vc_vector_get_growth_factor();
|
float vc_vector_get_growth_factor(void);
|
||||||
|
|
||||||
// Returns constant value of the vector default count of elements.
|
// Returns constant value of the vector default count of elements.
|
||||||
size_t vc_vector_get_default_count_of_elements();
|
size_t vc_vector_get_default_count_of_elements(void);
|
||||||
|
|
||||||
// Returns constant value of the vector struct size.
|
// Returns constant value of the vector struct size.
|
||||||
size_t vc_vector_struct_size();
|
size_t vc_vector_struct_size(void);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Element access
|
// Element access
|
||||||
|
|
|
@ -40,8 +40,7 @@ void Fado_ConstructStringVectors(vc_vector** stringVectors, FairyFileInfo* fileI
|
||||||
stringVectors[currentFile] = vc_vector_create(0x40, sizeof(char**), NULL);
|
stringVectors[currentFile] = vc_vector_create(0x40, sizeof(char**), NULL);
|
||||||
|
|
||||||
/* Build a vector of pointers to defined symbols' names */
|
/* Build a vector of pointers to defined symbols' names */
|
||||||
for (currentSym = 0; currentSym < fileInfo[currentFile].symtabInfo.sectionSize / sizeof(FairySym);
|
for (currentSym = 0; currentSym < fileInfo[currentFile].symtabInfo.sectionEntryCount; currentSym++) {
|
||||||
currentSym++) {
|
|
||||||
if ((symtab[currentSym].st_shndx != STN_UNDEF) &&
|
if ((symtab[currentSym].st_shndx != STN_UNDEF) &&
|
||||||
Fado_CheckInProgBitsSections(symtab[currentSym].st_shndx, fileInfo[currentFile].progBitsSections)) {
|
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 */
|
/* Have to pass a double pointer so it copies the pointer instead of the start of the string */
|
||||||
|
@ -86,7 +85,7 @@ typedef struct {
|
||||||
} FadoRelocInfo;
|
} FadoRelocInfo;
|
||||||
|
|
||||||
/* Construct the Zelda64ovl-compatible reloc word from an ELF reloc */
|
/* Construct the Zelda64ovl-compatible reloc word from an ELF reloc */
|
||||||
FadoRelocInfo Fado_MakeReloc(int file, FairySection section, FairyRel* data) {
|
FadoRelocInfo Fado_MakeReloc(int file, FairySection section, FairyRela* data) {
|
||||||
FadoRelocInfo relocInfo = { 0 };
|
FadoRelocInfo relocInfo = { 0 };
|
||||||
uint32_t sectionPrefix = 0;
|
uint32_t sectionPrefix = 0;
|
||||||
|
|
||||||
|
@ -223,11 +222,10 @@ void Fado_Relocs(FILE* outputFile, int inputFilesCount, FILE** inputFiles, const
|
||||||
relocList[section] = vc_vector_create(0x100, sizeof(FadoRelocInfo), NULL);
|
relocList[section] = vc_vector_create(0x100, sizeof(FadoRelocInfo), NULL);
|
||||||
|
|
||||||
for (currentFile = 0; currentFile < inputFilesCount; currentFile++) {
|
for (currentFile = 0; currentFile < inputFilesCount; currentFile++) {
|
||||||
FairyRel* relSection = fileInfos[currentFile].relocTablesInfo[section].sectionData;
|
FairyRela* relSection = fileInfos[currentFile].relocTablesInfo[section].sectionData;
|
||||||
if (relSection != NULL) {
|
|
||||||
|
|
||||||
for (relocIndex = 0;
|
if (relSection != NULL) {
|
||||||
relocIndex < fileInfos[currentFile].relocTablesInfo[section].sectionSize / sizeof(FairyRel);
|
for (relocIndex = 0; relocIndex < fileInfos[currentFile].relocTablesInfo[section].sectionEntryCount;
|
||||||
relocIndex++) {
|
relocIndex++) {
|
||||||
FadoRelocInfo currentReloc = Fado_MakeReloc(currentFile, section, &relSection[relocIndex]);
|
FadoRelocInfo currentReloc = Fado_MakeReloc(currentFile, section, &relSection[relocIndex]);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include "version.inc"
|
#include "version.inc"
|
||||||
|
|
||||||
void PrintVersion() {
|
void PrintVersion(void) {
|
||||||
printf("Fado (Fairy-Assisted relocations for Decompiled Overlays), version %s\n", versionNumber);
|
printf("Fado (Fairy-Assisted relocations for Decompiled Overlays), version %s\n", versionNumber);
|
||||||
printf("Copyright (C) 2021 Elliptic Ellipsis\n");
|
printf("Copyright (C) 2021 Elliptic Ellipsis\n");
|
||||||
printf("%s\n", credits);
|
printf("%s\n", credits);
|
||||||
|
@ -88,7 +88,7 @@ static size_t posArgCount = ARRAY_COUNT(posArgInfo);
|
||||||
static size_t optCount = ARRAY_COUNT(optInfo);
|
static size_t optCount = ARRAY_COUNT(optInfo);
|
||||||
static struct option longOptions[ARRAY_COUNT(optInfo)];
|
static struct option longOptions[ARRAY_COUNT(optInfo)];
|
||||||
|
|
||||||
void ConstructLongOpts() {
|
void ConstructLongOpts(void) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < optCount; i++) {
|
for (i = 0; i < optCount; i++) {
|
||||||
|
@ -133,14 +133,15 @@ int main(int argc, char** argv) {
|
||||||
outputFileName = optarg;
|
outputFileName = optarg;
|
||||||
outputFile = fopen(optarg, "wb");
|
outputFile = fopen(optarg, "wb");
|
||||||
if (outputFile == NULL) {
|
if (outputFile == NULL) {
|
||||||
fprintf(stderr, "error: unable to open output file '%s' for writing", optarg);
|
fprintf(stderr, "error: unable to open output file '%s' for writing\n", optarg);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
if (sscanf(optarg, "%u", &gVerbosity) == 0) {
|
if (sscanf(optarg, "%u", &gVerbosity) == 0) {
|
||||||
fprintf(stderr, "warning: verbosity argument '%s' should be a nonnegative decimal integer", optarg);
|
fprintf(stderr, "warning: verbosity argument '%s' should be a nonnegative decimal integer\n",
|
||||||
|
optarg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ int main(int argc, char** argv) {
|
||||||
FAIRY_INFO_PRINTF("Using input file %s\n", argv[optind + i]);
|
FAIRY_INFO_PRINTF("Using input file %s\n", argv[optind + i]);
|
||||||
inputFiles[i] = fopen(argv[optind + i], "rb");
|
inputFiles[i] = fopen(argv[optind + i], "rb");
|
||||||
if (inputFiles[i] == NULL) {
|
if (inputFiles[i] == NULL) {
|
||||||
fprintf(stderr, "error: unable to open input file '%s' for reading", argv[optind + i]);
|
fprintf(stderr, "error: unable to open input file '%s' for reading\n", argv[optind + i]);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,14 +215,14 @@ int main(int argc, char** argv) {
|
||||||
FILE* dependencyFile = fopen(dependencyFileName, "w");
|
FILE* dependencyFile = fopen(dependencyFileName, "w");
|
||||||
|
|
||||||
if (dependencyFile == NULL) {
|
if (dependencyFile == NULL) {
|
||||||
fprintf(stderr, "error: unable to open dependency file '%s' for writing", dependencyFileName);
|
fprintf(stderr, "error: unable to open dependency file '%s' for writing\n", dependencyFileName);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(objectFile, outputFileName);
|
strcpy(objectFile, outputFileName);
|
||||||
extensionStart = strrchr(objectFile, '.');
|
extensionStart = strrchr(objectFile, '.');
|
||||||
if (extensionStart == objectFile + fileNameLength) {
|
if (extensionStart == objectFile + fileNameLength) {
|
||||||
fprintf(stderr, "error: file name should not end in a '.'");
|
fprintf(stderr, "error: file name should not end in a '.'\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
strcpy(extensionStart, ".o");
|
strcpy(extensionStart, ".o");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Copyright (C) 2021 Elliptic Ellipsis */
|
/* Copyright (C) 2021 Elliptic Ellipsis */
|
||||||
/* SPDX-License-Identifier: AGPL-3.0-only */
|
/* SPDX-License-Identifier: AGPL-3.0-only */
|
||||||
const char versionNumber[] = "1.2.1";
|
const char versionNumber[] = "1.3.1";
|
||||||
const char credits[] = "Written by Elliptic Ellipsis\nand AngheloAlf";
|
const char credits[] = "Written by Elliptic Ellipsis\nwith additions from AngheloAlf and Tharo";
|
||||||
const char repo[] = "https://github.com/EllipticEllipsis/fado/";
|
const char repo[] = "https://github.com/EllipticEllipsis/fado/";
|
||||||
|
|
Loading…
Add table
Reference in a new issue