Remove obsolete ROM tools

This commit is contained in:
Aetias
2024-09-10 20:19:34 +02:00
parent a1fdc1b546
commit 191bc0938d
10 changed files with 0 additions and 2148 deletions
-2
View File
@@ -1,2 +0,0 @@
compress
compress.exe
-22
View File
@@ -1,22 +0,0 @@
CC := gcc
CFLAGS := -g
ifneq ($(DEBUG),1)
CFLAGS += -O2 -DNDEBUG
endif
ifeq ($(OS),Windows_NT)
OUTFILE := compress.exe
else
OUTFILE := compress
endif
.PHONY: all clean
all: $(OUTFILE)
clean:
rm -f $(OUTFILE)
$(OUTFILE): main.c
$(CC) $(CFLAGS) -o $(OUTFILE) main.c
-287
View File
@@ -1,287 +0,0 @@
// Compresses files using a backwards LZ77 algorithm.
// This is used to compress code after linking so that the compressed code matches the base ROM.
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VERSION "1.0"
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#define WRITE16(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; } while (0)
#define WRITE24(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; (buf)[2] = ((val) >> 16) & 0xFF; } while (0)
#define WRITE32(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; (buf)[2] = ((val) >> 16) & 0xFF; (buf)[3] = ((val) >> 24) & 0xFF; } while (0)
#define READ16(buf) ((buf)[0] | ((buf)[1] << 8))
#define READ24(buf) ((buf)[0] | ((buf)[1] << 8) | ((buf)[2] << 16))
#define READ32(buf) ((buf)[0] | ((buf)[1] << 8) | ((buf)[2] << 16) | ((buf)[3] << 24))
#define LEN_BITS 4
#define DIST_BITS 12
#define MIN_SUBSEQ 3
#define LEN_MASK ((1 << (LEN_BITS)) - 1)
#define DIST_MASK ((1 << (DIST_BITS)) - 1)
#define MAX_SUBSEQ ((MIN_SUBSEQ) + LEN_MASK)
#define LOOKAHEAD (1 << (DIST_BITS))
typedef struct {
uint8_t *pos;
uint32_t totalBytesSaved;
uint8_t flags;
} BlockInfo;
bool FindSubsequence(const uint8_t *buf, const uint8_t *start, const uint8_t *end, size_t *pLen, size_t *pDist) {
size_t bestLen = 0;
size_t bestDist = 0;
size_t maxLookahead = min(LOOKAHEAD + MAX_SUBSEQ, end - buf - 1);
for (size_t i = 0; i < maxLookahead; ++i) {
const uint8_t *needle = buf;
const uint8_t *haystack = buf + 1 + i;
if (*needle != *haystack) continue;
size_t len = 0;
while (needle - len >= start && needle[-len] == haystack[-len] && haystack - len > buf && len < MAX_SUBSEQ) {
++len;
}
size_t dist = haystack - needle - MIN_SUBSEQ;
if (len > bestLen && dist <= DIST_MASK) {
bestLen = len;
bestDist = dist;
}
}
if (bestLen < MIN_SUBSEQ) return false;
*pLen = bestLen;
*pDist = bestDist;
return true;
}
bool Compress(const uint8_t *src, uint8_t *dst, size_t size, uint8_t **pResult, size_t *pLen, size_t *pNumIdentical) {
const uint8_t *end = src + size;
const uint8_t *read = end;
uint8_t *writeEnd = dst + size;
uint8_t *write = writeEnd - 1;
uint8_t *pFlags = write;
uint8_t flags;
size_t flagCount = 0;
int32_t bytesSaved = 0;
BlockInfo *blockInfos = malloc(((size + 7) / 8) * sizeof(BlockInfo));
size_t numBlocks = 0;
while (read > src) {
flags <<= 1;
size_t len, dist;
if (FindSubsequence(read - 1, src, end, &len, &dist)) {
// write length-distance pair
size_t pair = (((len - MIN_SUBSEQ) & LEN_MASK) << DIST_BITS) | (dist & DIST_MASK);
read -= len;
write -= 2;
WRITE16(write, pair);
flags |= 1;
bytesSaved += len - 2;
} else {
// write literal
write -= 1;
read -= 1;
*write = *read;
}
flagCount++;
if (flagCount == 8) {
flagCount = 0;
*pFlags = flags;
bytesSaved -= 1;
write -= 1;
pFlags = write;
blockInfos[numBlocks].pos = write;
blockInfos[numBlocks].totalBytesSaved = bytesSaved;
blockInfos[numBlocks].flags = flags;
numBlocks++;
flags = 0;
}
}
if (flagCount != 0) {
blockInfos[numBlocks].pos = write;
blockInfos[numBlocks].totalBytesSaved = bytesSaved;
numBlocks++;
*pFlags = flags << (8 - flagCount);
}
else {
write += 1;
}
size_t numIdentical = 0;
for (int32_t i = 0; i < numBlocks - 1; ++i) {
if (blockInfos[i].totalBytesSaved == bytesSaved) {
size_t flagBytesSaved = 0;
for (; i >= 0; --i, ++flagBytesSaved) {
// Save more bytes by ignoring blocks that have no length-distance pairs in them
if (blockInfos[i].flags != 0) break;
}
numIdentical = blockInfos[i].pos - write;
write += 1;
// See if it's possible to remove some chunks based on the number of flag bytes saved
flags = blockInfos[i].flags;
for (int32_t j = 0; j < 8 && flagBytesSaved > 0; ++j) {
if ((flags & 0x80) != 0) {
if (flagBytesSaved < 2) break;
numIdentical += 2;
flagBytesSaved -= 2;
} else {
numIdentical += 1;
flagBytesSaved -= 1;
}
flags >>= 1;
}
memcpy(write, src, numIdentical);
while (write[numIdentical] == read[numIdentical]) {
numIdentical += 1;
}
break;
}
}
free(blockInfos);
*pResult = write;
*pLen = writeEnd - write;
*pNumIdentical = numIdentical;
return true;
}
bool WriteFooter(FILE *fp, const char *fileName, bool addPadding, uint32_t compressedSize, uint32_t originalSize, uint32_t start, uint32_t numIdentical) {
size_t padding = 0;
if (addPadding) padding = (0x4 - (compressedSize & 0x3)) & 0x3;
for (size_t i = 0; i < padding; ++i) fputc(0xFF, fp);
compressedSize += padding + 8;
uint32_t readOffset = padding + 8;
uint32_t writeOffset = originalSize - compressedSize - start;
compressedSize -= numIdentical;
if (fwrite(&compressedSize, 3, 1, fp) != 1) {
if (fp == stdout) fprintf(stderr, "Failed to write compressed size to stdout\n");
else fprintf(stderr, "Failed to write compressed size to '%s'\n", fileName);
return false;
}
if (fwrite(&readOffset, 1, 1, fp) != 1) {
if (fp == stdout) fprintf(stderr, "Failed to write read offset to stdout\n");
else fprintf(stderr, "Failed to write read offset to '%s'\n", fileName);
return false;
}
if (fwrite(&writeOffset, 4, 1, fp) != 1) {
if (fp == stdout) fprintf(stderr, "Failed to write write offset to stdout\n");
else fprintf(stderr, "Failed to write write offset to '%s'\n", fileName);
return false;
}
return true;
}
void PrintUsage(const char *program) {
printf(
"compress " VERSION "\n"
"\n"
"Usage: %s [-o OUTFILE] [-s START] [-p] -i INFILE\n"
" -o OUTFILE\tWrites result to file instead of stdout\n"
" -s START \tOffset to start compressing from\n"
" -p \tPad compressed data to 4-alignment\n"
" -i INFILE \tInput file to compress",
program
);
}
int main(int argc, const char **argv) {
const char *program = argv[0];
if (argc == 1) {
PrintUsage(program);
return 0;
}
const char *outFile = NULL;
const char *inFile = NULL;
int start = 0;
bool padCompressedBlock = false;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-o") == 0) {
if (++i >= argc) {
fprintf(stderr, "Expected filename after -o\n");
return 1;
}
outFile = argv[i];
} else if (strcmp(argv[i], "-s") == 0) {
if (++i >= argc) {
fprintf(stderr, "Expected start offset after -s\n");
return 1;
}
if (sscanf(argv[i], "%i", &start) != 1) {
fprintf(stderr, "Invalid start offset '%s' could not be parsed\n", argv[i]);
return 1;
}
} else if (strcmp(argv[i], "-p") == 0) {
padCompressedBlock = true;
} else if (strcmp(argv[i], "-i") == 0) {
if (++i >= argc) {
fprintf(stderr, "Expected filename after -i\n");
return 1;
}
inFile = argv[i];
} else {
fprintf(stderr, "Unknown option '%s'\n", argv[i]);
return 1;
}
}
if (inFile == NULL) {
PrintUsage(program);
fprintf(stderr, "Please provide an input file to compress, see usage above.\n");
return 1;
}
FILE *fpIn = fopen(inFile, "rb");
if (fpIn == NULL) {
fprintf(stderr, "Failed to open input file '%s'\n", inFile);
return 1;
}
fseek(fpIn, 0, SEEK_END);
size_t inSize = ftell(fpIn);
uint8_t *buf = malloc(inSize);
if (buf == NULL) {
fprintf(stderr, "Failed to allocate input buffer for '%s'\n", inFile);
return 1;
}
fseek(fpIn, 0, SEEK_SET);
if (fread(buf, inSize, 1, fpIn) != 1) {
fprintf(stderr, "Failed to read input file '%s'\n", inFile);
return 1;
}
fclose(fpIn);
FILE *fpOut = stdout;
if (outFile != NULL) {
fpOut = fopen(outFile, "wb");
if (fpOut == NULL) {
fprintf(stderr, "Failed to open output file '%s'\n", outFile);
return 1;
}
}
if (start > 0 && fwrite(buf, start, 1, fpOut) != 1) {
if (fpOut == stdout) fprintf(stderr, "Failed to write uncompressed header to stdout\n");
else fprintf(stderr, "Failed to write uncompressed header to '%s'\n", outFile);
return 1;
}
uint8_t *outBuf = malloc(inSize - start);
uint8_t *result;
size_t resultLen, numIdentical;
if (!Compress(buf + start, outBuf, inSize - start, &result, &resultLen, &numIdentical)) return 1;
if (fwrite(result, resultLen, 1, fpIn) != 1) {
if (fpOut == stdout) fprintf(stderr, "Failed to write compressed output to stdout\n");
else fprintf(stderr, "Failed to write compressed output to '%s'\n", outFile);
return 1;
}
if (!WriteFooter(fpOut, outFile, padCompressedBlock, resultLen, inSize, start, numIdentical)) return 1;
free(outBuf);
if (outFile != NULL) fclose(fpOut);
free(buf);
}
-4
View File
@@ -1,4 +0,0 @@
extractrom
extractrom.exe
buildrom
buildrom.exe
-29
View File
@@ -1,29 +0,0 @@
CC := gcc
CFLAGS := -g -Wall -I../include
ifneq ($(DEBUG),1)
CFLAGS += -O2 -DNDEBUG
endif
ifeq ($(OS),Windows_NT)
EXTRACTFILE := extractrom.exe
BUILDFILE := buildrom.exe
else
EXTRACTFILE := extractrom
BUILDFILE := buildrom
CFLAGS += -fshort-wchar
endif
.PHONY: all clean
all: $(EXTRACTFILE) $(BUILDFILE)
clean:
rm -f $(EXTRACTFILE)
rm -f $(BUILDFILE)
$(EXTRACTFILE): extract.c
$(CC) $(CFLAGS) -o $(EXTRACTFILE) extract.c
$(BUILDFILE): build.c
$(CC) $(CFLAGS) -o $(BUILDFILE) build.c
-1020
View File
File diff suppressed because it is too large Load Diff
-395
View File
@@ -1,395 +0,0 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rom.h"
#include "ph.h"
#include "util.h"
#define VERSION "1.0.2"
#define INDENT 4
// Command line flags for debugging purposes
bool printFileHierarchy = false;
bool printFileAllocOrder = false;
void Indent(size_t depth) {
char spaces[INDENT + 1];
memset(spaces, ' ', INDENT);
spaces[INDENT] = '\0';
for (size_t i = 0; i < depth; ++i) {
puts(spaces);
}
}
bool CheckRegion(const Header *pHeader, BuildInfo *pInfo) {
Region region = pHeader->gamecode[3];
if (
memcmp(pHeader->gamecode, GAMECODE_PREFIX, 3) != 0 || (
region != REGION_JAPAN &&
region != REGION_USA &&
region != REGION_EUROPE
)
) {
FATAL("Invalid gamecode prefix '%.4s'\n", pHeader->gamecode);
}
pInfo->region = region;
return true;
}
bool ExtractArm7(const uint8_t *rom, ProgramOffset *pArm7) {
File file;
if (!FileOpenWrite(ARM7_PROGRAM_FILE, &file)) FATAL("Failed to create ARM7 program '" ARM7_PROGRAM_FILE "'\n");
if (!FileWrite(&file, rom + pArm7->offset, pArm7->size, 1)) FATAL("Failed to write ARM7 program '" ARM7_PROGRAM_FILE "'\n");
FileClose(&file);
return true;
}
bool ExtractTitle(const char *language, const char *fileName, const wchar_t *title, size_t titleSize) {
char buf[1024];
File file;
if (!FileOpenWrite(fileName, &file)) FATAL("Failed to create %s banner title '%s'\n", language, fileName);
size_t resultSize = 0;
if (!WcharToUtf8((wchar_t*) title, titleSize, buf, sizeof(buf), &resultSize)) return false;
size_t len = strlen(buf);
if (!FileWrite(&file, buf, len, 1)) FATAL("Failed to write %s banner title '%s'\n", language, fileName);
FileClose(&file);
return true;
}
bool ExtractBanner(const Banner *pBanner, const BuildInfo *pInfo) {
if (!MakeDir(BANNER_SUBDIR)) return 1;
File file;
if (!FileOpenWrite(ICON_BITMAP_FILE, &file)) FATAL("Failed to create banner icon bitmap '" ICON_BITMAP_FILE "'\n");
if (!FileWrite(&file, pBanner->iconBitmap, sizeof(pBanner->iconBitmap), 1)) {
FATAL("Failed to write banner icon bitmap '" ICON_BITMAP_FILE "'\n");
}
FileClose(&file);
if (!FileOpenWrite(ICON_PALETTE_FILE, &file)) FATAL("Failed to create banner icon palette '" ICON_PALETTE_FILE "'\n");
if (!FileWrite(&file, pBanner->iconPalette, sizeof(pBanner->iconPalette), 1)) {
FATAL("Failed to write banner icon palette '" ICON_PALETTE_FILE "'\n");
}
FileClose(&file);
if (!ExtractTitle("Japanese", TITLE_JAP_FILE, pBanner->japaneseTitle, sizeof(pBanner->japaneseTitle))) return false;
if (!ExtractTitle("English", TITLE_ENG_FILE, pBanner->englishTitle, sizeof(pBanner->englishTitle))) return false;
if (!ExtractTitle("French", TITLE_FRE_FILE, pBanner->frenchTitle, sizeof(pBanner->frenchTitle))) return false;
if (!ExtractTitle("German", TITLE_GER_FILE, pBanner->germanTitle, sizeof(pBanner->germanTitle))) return false;
if (!ExtractTitle("Italian", TITLE_ITA_FILE, pBanner->italianTitle, sizeof(pBanner->italianTitle))) return false;
if (!ExtractTitle("Spanish", TITLE_SPA_FILE, pBanner->spanishTitle, sizeof(pBanner->spanishTitle))) return false;
return true;
}
typedef struct {
char basePath[256];
size_t basePathLen;
char **filePathList;
size_t numFilePaths;
size_t maxFilePaths;
size_t depth;
} ExtractContext;
bool GrowFilePathList(ExtractContext *pContext, size_t minEntries) {
ExtractContext ctx;
memcpy(&ctx, pContext, sizeof(ctx));
if (minEntries <= ctx.maxFilePaths) return true;
while (minEntries > ctx.maxFilePaths) {
ctx.maxFilePaths *= 2;
}
char **newFilePathList = realloc(ctx.filePathList, ctx.maxFilePaths * sizeof(*ctx.filePathList));
if (newFilePathList == NULL) FATAL("Failed to reallocate file path list to %ld entries\n", ctx.maxFilePaths);
ctx.filePathList = newFilePathList;
// Fill new entries with 0
memset(&ctx.filePathList[pContext->maxFilePaths], 0, (ctx.maxFilePaths - pContext->maxFilePaths) * sizeof(*ctx.filePathList));
memcpy(pContext, &ctx, sizeof(ctx));
return true;
}
bool ExtractSubtable(
const uint8_t *rom,
const uint8_t *fatStart,
const uint8_t *fntStart,
const FntEntry *pFntEntry,
ExtractContext *pContext
) {
ExtractContext ctx;
memcpy(&ctx, pContext, sizeof(ctx));
const uint8_t *subEntryAddr = fntStart + pFntEntry->subtableOffset;
const FntSubEntry *pSubEntry = (const FntSubEntry*) subEntryAddr;
uint16_t fileId = pFntEntry->firstFile;
while(pSubEntry->length > 0) {
char name[128];
memcpy(name, pSubEntry->name, pSubEntry->length);
name[pSubEntry->length] = '\0';
if (!pSubEntry->isSubdir) {
if (printFileHierarchy) {
Indent(ctx.depth);
printf("%d: %s\n", fileId, name);
}
const FatEntry *pFatEntry = (const FatEntry*) fatStart + fileId;
size_t fileSize = pFatEntry->endOffset - pFatEntry->startOffset;
const uint8_t *pFileBytes = rom + pFatEntry->startOffset;
File file;
if (!FileOpenWrite(name, &file)) FATAL("Failed to open assets file '%s'\n", name);
if (!FileWrite(&file, pFileBytes, fileSize, 1)) FATAL("Failed to write to assets file '%s'\n", name);
FileClose(&file);
if (printFileAllocOrder) {
if (!GrowFilePathList(&ctx, fileId + 1)) return false;
char *filePath = malloc(ctx.basePathLen + pSubEntry->length + 1);
if (filePath == NULL) FATAL("Failed to allocate string for file path\n");
sprintf(filePath, "%s%s", ctx.basePath, name);
ctx.filePathList[fileId] = filePath;
}
subEntryAddr += sizeof(FntSubEntry) + pSubEntry->length;
pSubEntry = (const FntSubEntry*) subEntryAddr;
++fileId;
continue;
}
if (printFileHierarchy) {
Indent(ctx.depth);
printf("%s {\n", name);
}
if (!MakeDir(name)) return false;
if (!ChangeDir(name)) return false;
uint16_t subdirId = READ_SUBDIR_ID(pSubEntry);
uint16_t subdirIndex = subdirId & 0xfff;
ctx.depth += 1;
if (printFileAllocOrder) {
size_t basePathSpace = sizeof(ctx.basePath) - ctx.basePathLen;
size_t basePathAdded = snprintf(&ctx.basePath[ctx.basePathLen], basePathSpace, "%s/", name);
if (basePathAdded >= basePathSpace) FATAL("Maximum base path size was exceeded\n");
ctx.basePathLen += basePathAdded;
if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart + subdirIndex, &ctx)) return false;
ctx.basePathLen -= basePathAdded;
memset(&ctx.basePath[ctx.basePathLen], 0, basePathAdded);
} else {
if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart + subdirIndex, &ctx)) return false;
}
ctx.depth -= 1;
if (printFileHierarchy) {
Indent(ctx.depth);
printf("}\n");
}
if (!ChangeDir("..")) return false;
subEntryAddr += sizeof(FntSubEntry) + pSubEntry->length + sizeof(subdirId);
pSubEntry = (const FntSubEntry*) subEntryAddr;
}
memcpy(pContext, &ctx, sizeof(ctx));
return true;
}
typedef struct {
uint32_t fileId;
uint32_t startOffset;
uint32_t endOffset;
} FatInfo;
int32_t CompareFatInfo(const void *a, const void *b) {
const FatInfo *infoA = (FatInfo*) a;
const FatInfo *infoB = (FatInfo*) b;
return infoA->startOffset - infoB->startOffset;
}
bool PrintFileAllocOrder(ExtractContext *ctx, const uint8_t *fatAddr) {
const FatEntry *fatStart = (FatEntry*) fatAddr;
const FatEntry *fat;
for (fat = fatStart; fat->startOffset != 0xFFFFFFFF; ++fat);
size_t numFiles = fat - fatStart;
FatInfo *fatInfo = malloc(numFiles * sizeof(FatInfo));
if (fatInfo == NULL) FATAL("Failed to allocate array for printing file allocation order\n");
for (size_t i = 0; i < numFiles; ++i) {
fatInfo[i].fileId = i;
fatInfo[i].startOffset = fatStart[i].startOffset;
fatInfo[i].endOffset = fatStart[i].endOffset;
}
qsort(fatInfo, numFiles, sizeof(*fatInfo), CompareFatInfo);
for (size_t i = 0; i < numFiles; ++i) {
uint32_t fileId = fatInfo[i].fileId;
if (ctx->filePathList[fileId] == NULL) continue;
printf("%s\n", ctx->filePathList[fileId]);
}
free(fatInfo);
return true;
}
bool ExtractAssets(const uint8_t *rom, const uint8_t *fatStart, const uint8_t *fntStart) {
ExtractContext ctx;
memset(ctx.basePath, 0, sizeof(ctx.basePath));
ctx.basePathLen = 0;
ctx.numFilePaths = 0;
if (printFileAllocOrder) {
ctx.maxFilePaths = 4096;
ctx.filePathList = calloc(ctx.maxFilePaths, sizeof(*ctx.filePathList));
} else {
ctx.maxFilePaths = 0;
ctx.filePathList = NULL;
}
ctx.depth = 0;
if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart, &ctx)) return false;
if (printFileAllocOrder) {
if (!PrintFileAllocOrder(&ctx, fatStart)) return false;
for (size_t i = 0; i < ctx.numFilePaths; ++i) {
if (ctx.filePathList[i] != NULL) free(ctx.filePathList[i]);
}
free(ctx.filePathList);
}
return true;
}
bool ExtractOverlayData(const uint8_t *rom, const Header *header) {
const OverlayEntry *entry = (OverlayEntry*) (rom + header->arm9Overlays.offset);
const OverlayEntry *end = entry + header->arm9Overlays.size / sizeof(OverlayEntry);
File file;
if (!FileOpenWrite(ARM9_OVERLAY_DATA_FILE, &file)) {
FATAL("Failed to open overlay data file '" ARM9_OVERLAY_DATA_FILE "'\n");
}
for(; entry < end; ++entry) {
OverlayData data;
data.fileId = entry->fileId;
if (!FileWrite(&file, &data, sizeof(data), 1)) FATAL("Failed to write overlay data to '" ARM9_OVERLAY_DATA_FILE "'\n");
}
FileClose(&file);
return true;
}
void PrintUsage(const char *program) {
printf(
"extractrom " VERSION "\n"
"\n"
"Usage: %s -i ROMFILE -o OUTDIR [-n] [-a] [-l]\n"
" -o OUTDIR \tDirectory to extract files to\n"
" -i ROMFILE\tROM to extract from\n"
" -n \tPrints file name hierarchy to stdout\n"
" -a \tPrints file allocation order to stdout\n",
program
);
}
int main(int argc, const char **argv) {
// --------------------- Parse command line arguments ---------------------
const char *program = argv[0];
if (argc == 1) {
PrintUsage(program);
return 0;
}
const char *romFile = NULL;
const char *outDir = NULL;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-o") == 0) {
if (++i >= argc) {
fprintf(stderr, "Expected dirname after -o\n");
return 1;
}
outDir = argv[i];
} else if (strcmp(argv[i], "-i") == 0) {
if (++i >= argc) {
fprintf(stderr, "Expected filename after -i\n");
return 1;
}
romFile = argv[i];
} else if (strcmp(argv[i], "-n") == 0) {
printFileHierarchy = true;
} else if (strcmp(argv[i], "-a") == 0) {
printFileAllocOrder = true;
} else {
fprintf(stderr, "Unknown option '%s'\n", argv[i]);
return 1;
}
}
if (romFile == NULL) {
PrintUsage(program);
fprintf(stderr, "Please provide a ROM file, see usage above.\n");
return 1;
}
if (outDir == NULL) {
PrintUsage(program);
fprintf(stderr, "Please provide an output directory, see usage above.\n");
return 1;
}
// --------------------- Load ROM ---------------------
File fileRom;
if (!FileOpenRead(romFile, &fileRom)) {
fprintf(stderr, "Failed to open input ROM '%s'\n", romFile);
return 1;
}
size_t romSize = FileSize(&fileRom);
uint8_t *rom = malloc(romSize);
if (rom == NULL) {
fprintf(stderr, "Failed to allocate buffer for '%s'\n", romFile);
return 1;
}
if (FileRead(&fileRom, rom, romSize, 1) != 1) {
fprintf(stderr, "Failed to read from '%s'\n", romFile);
return 1;
}
FileClose(&fileRom);
// --------------------- Set up ---------------------
Header *pHeader = (Header*) rom;
BuildInfo info;
if (!CheckRegion(pHeader, &info)) return 1;
if (!MakeDir(outDir)) return 1;
if (!ChangeDir(outDir)) return 1;
// --------------------- Extract ARM7 program ---------------------
if (!ExtractArm7(rom, &pHeader->arm7)) return 1;
// --------------------- Extract banner ---------------------
Banner *pBanner = (Banner*) (rom + pHeader->bannerOffset);
if (!ExtractBanner(pBanner, &info)) return 1;
// --------------------- Extract assets ---------------------
if (!MakeDir(ASSETS_SUBDIR)) return 1;
if (!ChangeDir(ASSETS_SUBDIR)) return 1;
const uint8_t *fntStart = rom + pHeader->fileNames.offset;
const uint8_t *fatStart = rom + pHeader->fileAllocs.offset;
if (!ExtractAssets(rom, fatStart, fntStart)) return 1;
if (!ChangeDir("..")) return 1;
// --------------------- Extract overlay data ---------------------
if (!ExtractOverlayData(rom, pHeader)) return 1;
if (!ChangeDir("..")) return 1;
free(rom);
}
-223
View File
@@ -1,223 +0,0 @@
#ifndef __FILES_H
#define __FILES_H
#include <ctype.h>
#include "util.h"
#include "rom.h"
typedef struct FileTree {
struct FileTree *children;
uint16_t numChildren;
uint16_t maxChildren;
uint16_t firstFileId;
bool addedToFat;
FntSubEntry *entry;
} FileTree;
bool MakeFileTree(FileTree *pTree);
bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) {
#ifdef __UTIL_WINDOWS
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA("*", &findData);
if (hFind == INVALID_HANDLE_VALUE) FATAL("Failed to open directory to iterate files\n");
do {
const char *name = findData.cFileName;
if (strcmp(name, ".") == 0) continue;
if (strcmp(name, "..") == 0) continue;
bool isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (!callback(name, isDir, userData)) return false;
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
#elif defined(__UTIL_LINUX)
DIR *dir = opendir(".");
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
const char *name = entry->d_name;
if (strcmp(name, ".") == 0) continue;
if (strcmp(name, "..") == 0) continue;
bool isDir = entry->d_type == DT_DIR;
if (!callback(name, isDir, userData)) return false;
}
closedir(dir);
#endif
return true;
}
bool _GrowFileTreeChildren(FileTree *pTree, size_t minChildren) {
FileTree tree;
memcpy(&tree, pTree, sizeof(tree));
if (tree.maxChildren >= minChildren) return true;
size_t newSize = tree.maxChildren;
if (newSize == 0) newSize = minChildren;
while (newSize < minChildren) newSize *= 2;
if (tree.children == NULL) {
FileTree *children = malloc(newSize * sizeof(FileTree));
if (children == NULL) FATAL("Failed to allocate file tree children\n");
tree.children = children;
} else {
FileTree *children = realloc(tree.children, newSize * sizeof(FileTree));
if (children == NULL) FATAL("Failed to reallocate file tree children\n");
tree.children = children;
}
tree.maxChildren = newSize;
memcpy(pTree, &tree, sizeof(tree));
return true;
}
bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) {
FileTree *pTree = (FileTree*) userData;
size_t nameLength = strlen(name);
if (isDir) {
FntSubEntry *entry = malloc(sizeof(FntSubEntry) + nameLength + 2);
if (entry == NULL) FATAL("Failed to allocate FNT sub entry for directory '%s'\n", name);
entry->isSubdir = true;
entry->length = nameLength;
memcpy(entry->name, name, nameLength);
WRITE_SUBDIR_ID(entry, 0);
if (!ChangeDir(name)) return false;
FileTree child;
if (!MakeFileTree(&child)) return false;
child.entry = entry;
if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false;
memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child));
pTree->numChildren += 1;
if (!ChangeDir("..")) return false;
} else {
FntSubEntry *entry = malloc(sizeof(FntSubEntry) + nameLength);
if (entry == NULL) FATAL("Failed to allocate FNT sub entry for file '%s'\n", name);
entry->isSubdir = false;
entry->length = nameLength;
memcpy(entry->name, name, nameLength);
FileTree child;
child.children = NULL;
child.numChildren = 0;
child.maxChildren = 0;
child.firstFileId = 0;
child.addedToFat = false;
child.entry = entry;
if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false;
memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child));
pTree->numChildren += 1;
}
return true;
}
bool MakeFileTree(FileTree *pTree) {
FileTree tree;
tree.children = NULL;
tree.numChildren = 0;
tree.maxChildren = 0;
tree.addedToFat = false;
if (!_GrowFileTreeChildren(&tree, 64)) return false;
tree.entry = NULL;
if (!IterFiles(_FileTreeFileCallback, &tree)) return false;
memcpy(pTree, &tree, sizeof(tree));
return true;
}
bool FreeFileTree(FileTree *pTree) {
for (size_t i = 0; i < pTree->numChildren; ++i) {
if (!FreeFileTree(&pTree->children[i])) return false;
}
if (pTree->children != NULL) {
free(pTree->children);
pTree->children = NULL;
}
pTree->numChildren = 0;
pTree->maxChildren = 0;
pTree->addedToFat = false;
if (pTree->entry != NULL) {
free(pTree->entry);
pTree->entry = NULL;
}
return true;
}
int CompareFileTreeNormal(const void *a, const void *b) {
FileTree *treeA = (FileTree*) a;
FileTree *treeB = (FileTree*) b;
// Files before directories
bool dirA = treeA->entry == NULL || treeA->entry->isSubdir;
bool dirB = treeB->entry == NULL || treeB->entry->isSubdir;
if (dirA && !dirB) return 1;
if (!dirA && dirB) return -1;
size_t lenA = treeA->entry->length;
size_t lenB = treeB->entry->length;
size_t minSize = (lenA < lenB) ? lenA : lenB;
// Alphabetic name order
const char *nameA = treeA->entry->name;
const char *nameB = treeB->entry->name;
for (size_t i = 0; i < minSize; ++i) {
const char chA = tolower(nameA[i]);
const char chB = tolower(nameB[i]);
if (chA != chB) return chA - chB;
}
// Shortest name first
if (lenA < lenB) return -1;
if (lenA > lenB) return 1;
return 0;
}
int CompareFileTreeAscii(const void *a, const void *b) {
FileTree *treeA = (FileTree*) a;
FileTree *treeB = (FileTree*) b;
size_t lenA = treeA->entry->length;
size_t lenB = treeB->entry->length;
size_t minSize = (lenA < lenB) ? lenA : lenB;
// ASCII order
int cmp = strncmp(treeA->entry->name, treeB->entry->name, minSize);
if (cmp != 0) return cmp;
// Shortest name first
if (lenA < lenB) return -1;
if (lenA > lenB) return 1;
return 0;
}
bool SortFileTree(FileTree *pTree, int (*compare)(const void*, const void*)) {
if (pTree->numChildren <= 1) return true;
FileTree tree;
memcpy(&tree, pTree, sizeof(tree));
qsort(tree.children, tree.numChildren, sizeof(*tree.children), compare);
for (size_t i = 0; i < tree.numChildren; ++i) {
if (!SortFileTree(&tree.children[i], compare)) return false;
}
memcpy(pTree, &tree, sizeof(tree));
return true;
}
FileTree* FindSubTree(FileTree *tree, const char *path) {
if (*path == '/') ++path;
const char *nextPath = strchr(path, '/');
if (nextPath == NULL) return tree;
size_t dirNameLen = nextPath - path;
for (size_t i = 0; i < tree->numChildren; ++i) {
FileTree *child = &tree->children[i];
if (child->entry->length != dirNameLen) continue;
if (strncmp(child->entry->name, path, dirNameLen) != 0) continue;
return FindSubTree(child, nextPath);
}
return NULL;
}
#endif
-42
View File
@@ -1,42 +0,0 @@
#ifndef __PH_H
#define __PH_H
#define TITLE "ZELDA_DS:PH"
#define GAMECODE_PREFIX "AZE"
#define BANNER_SUBDIR "banner"
#define ICON_BITMAP_FILE BANNER_SUBDIR "/icon.bin"
#define ICON_PALETTE_FILE BANNER_SUBDIR "/icon.pal"
#define TITLE_JAP_FILE BANNER_SUBDIR "/title_jap.txt"
#define TITLE_ENG_FILE BANNER_SUBDIR "/title_eng.txt"
#define TITLE_FRE_FILE BANNER_SUBDIR "/title_fre.txt"
#define TITLE_GER_FILE BANNER_SUBDIR "/title_ger.txt"
#define TITLE_ITA_FILE BANNER_SUBDIR "/title_ita.txt"
#define TITLE_SPA_FILE BANNER_SUBDIR "/title_spa.txt"
#define ASSETS_SUBDIR "assets"
#define ARM9_PROGRAM_FILE "arm9.lz"
#define ARM9_FOOTER_FILE "arm9_footer.bin"
#define ARM9_METADATA_FILE "arm9_metadata.bin"
#define ARM9_OVERLAY_TABLE_FILE "arm9_ovt.bin"
#define ARM9_OVERLAY_DATA_FILE "arm9_ovdata.bin"
#define OVERLAYS_SUBDIR "overlays"
#define ARM7_PROGRAM_FILE "arm7.bin"
typedef enum {
REGION_JAPAN = 'J',
REGION_USA = 'E',
REGION_EUROPE = 'P',
} Region;
typedef struct {
Region region;
} BuildInfo;
typedef struct {
uint32_t fileId;
} OverlayData;
#endif
-124
View File
@@ -1,124 +0,0 @@
#ifndef __ROM_H
#define __ROM_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SECURE_AREA_SIZE 0x4000
typedef struct {
/* 00 */ uint32_t offset;
/* 04 */ uint32_t entry;
/* 08 */ uint32_t baseAddr;
/* 0c */ uint32_t size;
/* 10 */
} ProgramOffset;
typedef struct {
/* 0 */ uint32_t offset;
/* 4 */ uint32_t size;
/* 8 */
} TableOffset;
typedef struct {
/* 0000 */ char title[0xc];
/* 000c */ char gamecode[0x4];
/* 0010 */ char makercode[0x2];
/* 0012 */ uint8_t unitcode;
/* 0013 */ uint8_t encSeedSelect;
/* 0014 */ uint8_t capacity;
/* 0015 */ uint8_t reserved0[0x8];
/* 001d */ uint8_t dsRegion;
/* 001e */ uint8_t romVersion;
/* 001f */ uint8_t autostart;
/* 0020 */ ProgramOffset arm9;
/* 0030 */ ProgramOffset arm7;
/* 0040 */ TableOffset fileNames;
/* 0048 */ TableOffset fileAllocs;
/* 0050 */ TableOffset arm9Overlays;
/* 0058 */ TableOffset arm7Overlays;
/* 0060 */ uint32_t normalCmdSetting;
/* 0064 */ uint32_t key1CmdSetting;
/* 0068 */ uint32_t bannerOffset;
/* 006c */ uint16_t secureAreaCrc;
/* 006e */ uint16_t secureAreaDelay;
/* 0070 */ uint32_t arm9AutoloadCallback;
/* 0074 */ uint32_t arm7AutoloadCallback;
/* 0078 */ uint64_t secureAreaDisable;
/* 0080 */ uint32_t romSize;
/* 0084 */ uint32_t headerSize;
/* 0088 */ uint32_t autoloadBlockInfosOffset;
/* 008c */ uint8_t reserved1[0x8];
/* 0094 */ uint16_t romEnd;
/* 0096 */ uint16_t rwEnd;
/* 0098 */ uint8_t reserved2[0x18];
/* 00b0 */ uint8_t reserved3[0x10];
/* 00c0 */ uint8_t logo[0x9c];
/* 015c */ uint16_t logoCrc;
/* 015e */ uint16_t headerCrc;
/* 0160 */ uint32_t debugRomOffset;
/* 0164 */ uint32_t debugSize;
/* 0168 */ uint32_t debugRamAddr;
/* 016c */ uint8_t reserved4[0x4];
/* 0170 */ uint8_t reserved5[0x90];
/* 0200 */ uint8_t reserved6[0xe00];
/* 1000 */ uint8_t reserved7[0x3000];
/* 4000 */
} Header;
typedef struct {
/* 00 */ uint32_t id;
/* 04 */ uint32_t baseAddr;
/* 08 */ uint32_t textSize;
/* 0c */ uint32_t bssSize;
/* 10 */ uint32_t sinitStart;
/* 14 */ uint32_t sinitEnd;
/* 18 */ uint32_t fileId;
/* 1c */ uint32_t compressedSize : 24;
/* 1f */ uint32_t isCompressed : 8;
/* 20 */
} OverlayEntry;
typedef struct {
/* 0000 */ uint16_t version;
/* 0002 */ uint16_t crc;
/* 0004 */ uint8_t reserved0[0x1c];
/* 0020 */ uint8_t iconBitmap[0x200];
/* 0220 */ uint16_t iconPalette[0x10];
/* 0240 */ wchar_t japaneseTitle[0x80];
/* 0340 */ wchar_t englishTitle[0x80];
/* 0440 */ wchar_t frenchTitle[0x80];
/* 0540 */ wchar_t germanTitle[0x80];
/* 0640 */ wchar_t italianTitle[0x80];
/* 0740 */ wchar_t spanishTitle[0x80];
/* 0540 */
} Banner;
typedef struct {
/* 0 */ uint32_t subtableOffset;
/* 4 */ uint16_t firstFile;
/* 6 */ uint16_t parentId;
/* 8 */
} FntEntry;
typedef struct {
/* 0.0 */ uint8_t length : 7;
/* 0.7 */ bool isSubdir : 1;
/* 1.0 */ char name[];
// If isSubdir
/* 1.0 + length */ // uint16_t subdirId;
/* 1.0 + length + 2 */
// Else
/* 1.0 + length */
} FntSubEntry;
#define READ_SUBDIR_ID(entry) READ16(entry + sizeof(*entry) + entry->length);
#define WRITE_SUBDIR_ID(entry,id) WRITE16(entry + sizeof(*entry) + entry->length, id)
typedef struct {
/* 0 */ uint32_t startOffset;
/* 4 */ uint32_t endOffset;
} FatEntry;
#endif