Merge pull request #6 from AetiasHax/fix-msys-build

Fix MSYS build
This commit is contained in:
AetiasHax
2024-02-24 09:50:28 +01:00
committed by GitHub
7 changed files with 462 additions and 382 deletions
+11 -11
View File
@@ -15,14 +15,12 @@ else
endif
ROOT := $(shell pwd)
BUILD_DIR := $(ROOT)/build
BUILD_DIR := build
TARGET_DIR := $(BUILD_DIR)/$(REGION_NAME)
TOOLS_DIR := $(ROOT)/tools
BASE_DIR := $(ROOT)/ph_$(REGION_NAME)
LCF_FILE := $(BUILD_DIR)/arm9_linker_script.lcf
OBJS_FILE := $(BUILD_DIR)/arm9_objects.txt
ARM7_BIOS := $(ROOT)/arm7_bios.bin
ASSETS_TXT := $(ROOT)/assets.txt
TOOLS_DIR := tools
BASE_DIR := ph_$(REGION_NAME)
ARM7_BIOS := arm7_bios.bin
ASSETS_TXT := assets.txt
ASM_FILES := $(shell find asm -name *.s)
CXX_FILES := $(shell find src -name *.cpp)
@@ -37,10 +35,12 @@ BASE_ROM := baserom_$(REGION_NAME).nds
CHECKSUM := ph_$(REGION_NAME).sha1
MW_VER := 2.0/sp1p5
MW_ASM := $(TOOLS_DIR)/mwccarm/$(MW_VER)/mwasmarm.exe
MW_CC := $(TOOLS_DIR)/mwccarm/$(MW_VER)/mwccarm.exe
MW_LD := $(TOOLS_DIR)/mwccarm/$(MW_VER)/mwldarm.exe
MW_LICENSE := $(TOOLS_DIR)/mwccarm/license.dat
MW_ASM := $(ROOT)/$(TOOLS_DIR)/mwccarm/$(MW_VER)/mwasmarm.exe
MW_CC := $(ROOT)/$(TOOLS_DIR)/mwccarm/$(MW_VER)/mwccarm.exe
MW_LD := $(ROOT)/$(TOOLS_DIR)/mwccarm/$(MW_VER)/mwldarm.exe
MW_LICENSE := $(ROOT)/$(TOOLS_DIR)/mwccarm/license.dat
LCF_FILE := $(ROOT)/$(BUILD_DIR)/arm9_linker_script.lcf
OBJS_FILE := $(ROOT)/$(BUILD_DIR)/arm9_objects.txt
ASM_FLAGS := -proc arm5te -d $(REGION) -i asm -msgstyle gcc
CC_FLAGS := -proc arm946e -interworking -O4,p -enum int -i include -nolink -d $(REGION) -char signed -lang=c++ -sym on
+264
View File
@@ -0,0 +1,264 @@
#ifndef __UTIL_H
#define __UTIL_H
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__)
#define FATAL(...) do { PRINT_FATAL(__VA_ARGS__); return false; } while (0)
#define WRITE16(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; } while (0)
#define WRITE24(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; ((uint8_t*) buf)[2] = ((val) >> 16) & 0xFF; } while (0)
#define WRITE32(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; ((uint8_t*) buf)[2] = ((val) >> 16) & 0xFF; ((uint8_t*) buf)[3] = ((val) >> 24) & 0xFF; } while (0)
#define READ16(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8))
#define READ24(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8) | (((uint8_t*) buf)[2] << 16))
#define READ32(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8) | (((uint8_t*) buf)[2] << 16) | (((uint8_t*) buf)[3] << 24))
#define REVERSE32(val) ((val >> 24) | ((val & 0xff0000) >> 8) | ((val & 0xff00) << 8) | ((val & 0xff) << 24))
#if defined(_WIN32) || defined(__MSYS__)
# define __UTIL_WINDOWS
# include <Windows.h>
#elif __linux__
# define __UTIL_LINUX
# include <errno.h>
# include <sys/stat.h>
# include <iconv.h>
# include <dirent.h>
# include <unistd.h>
#else
# error "Target platform not supported"
#endif
bool MakeDir(const char *dir) {
#ifdef __UTIL_WINDOWS
if (CreateDirectoryA(dir, NULL)) return true;
if (GetLastError() == ERROR_ALREADY_EXISTS) return true;
FATAL("Failed to make directory '%s'\n", dir);
#elif defined(__UTIL_LINUX)
struct stat dirStat;
if (stat(dir, &dirStat) != 0) {
if (mkdir(dir, 0777) != 0) FATAL("Failed to make directory '%s'\n", dir);
return true;
}
if (!S_ISDIR(dirStat.st_mode)) FATAL("Could not make directory '%s' due to a file with the same name\n", dir);
return true;
#endif
}
bool GetCurrentDir(char *dir, size_t dirSize) {
#ifdef __UTIL_WINDOWS
return GetCurrentDirectory(dirSize, dir) > 0;
#elif defined(__UTIL_LINUX)
return getcwd(dir, dirSize) != NULL;
#endif
}
bool ChangeDir(const char *dir) {
#ifdef __UTIL_WINDOWS
if (SetCurrentDirectory(dir)) return true;
#elif defined(__UTIL_LINUX)
if (chdir(dir) == 0) return true;
#endif
char cwd[256];
if (!GetCurrentDir(cwd, sizeof(cwd))) strcpy(cwd, "[unknown]");
FATAL("Failed to enter directory '%s' from current directory '%s'\n", dir, cwd);
}
typedef struct {
const char *name;
#ifdef __UTIL_WINDOWS
HANDLE handle;
#elif defined(__UTIL_LINUX)
FILE *fp;
#endif
} File;
bool FileOpenRead(const char *name, File *file) {
#ifdef __UTIL_WINDOWS
HANDLE handle = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
file->name = name;
file->handle = handle;
return true;
}
#elif defined(__UTIL_LINUX)
FILE *fp = fopen(name, "rb");
if (fp != NULL) {
file->name = name;
file->fp = fp;
return true;
}
#endif
FATAL("Failed to open file '%s' for reading\n", name);
}
bool FileOpenWrite(const char *name, File *file) {
#ifdef __UTIL_WINDOWS
HANDLE handle = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
file->name = name;
file->handle = handle;
return true;
}
#elif defined(__UTIL_LINUX)
FILE *fp = fopen(name, "wb");
if (fp != NULL) {
file->name = name;
file->fp = fp;
return true;
}
#endif
FATAL("Failed to open file '%s' for writing\n", name);
}
void FileClose(File *file) {
#ifdef __UTIL_WINDOWS
CloseHandle(file->handle);
file->name = NULL;
file->handle = NULL;
#elif defined(__UTIL_LINUX)
fclose(file->fp);
file->name = NULL;
file->fp = NULL;
#endif
}
size_t FileRead(const File *file, void *buf, size_t size, size_t count) {
#ifdef __UTIL_WINDOWS
DWORD bytesRead;
if (ReadFile(file->handle, buf, size * count, &bytesRead, NULL)) {
if (bytesRead > 0) return bytesRead / size;
}
#elif defined(__UTIL_LINUX)
size_t countRead = fread(buf, size, count, file->fp);
if (countRead > 0) return countRead;
#endif
PRINT_FATAL("Failed to read %ld * %ld bytes from '%s'\n", count, size, file->name);
return 0;
}
bool FileWrite(const File *file, const void *buf, size_t size, size_t count) {
#ifdef __UTIL_WINDOWS
if (WriteFile(file->handle, buf, size * count, NULL, NULL)) return true;
#elif defined(__UTIL_LINUX)
if (fwrite(buf, size, count, file->fp) == count) return true;
#endif
FATAL("Failed to write %ld * %ld bytes to '%s'\n", count, size, file->name);
}
size_t FileSize(const File *file) {
#ifdef __UTIL_WINDOWS
DWORD sizeHigh;
DWORD sizeLow = GetFileSize(file->handle, &sizeHigh);
return sizeLow | ((size_t)(sizeHigh) << 32);
#elif defined(__UTIL_LINUX)
size_t pos = ftell(file->fp);
fseek(file->fp, 0, SEEK_END);
size_t size = ftell(file->fp);
fseek(file->fp, pos, SEEK_SET);
return size;
#endif
}
size_t FileOffset(const File *file) {
#ifdef __UTIL_WINDOWS
LONG offsetHigh = 0;
DWORD offsetLow = SetFilePointer(file->handle, 0, &offsetHigh, FILE_CURRENT);
return offsetLow | ((size_t)(offsetHigh) << 32);
#elif defined(__UTIL_LINUX)
return ftell(file->fp);
#endif
}
void FileGoTo(const File *file, size_t offset) {
#ifdef __UTIL_WINDOWS
LONG offsetHigh = offset >> 32;
LONG offsetLow = offset & 0xffffffff;
SetFilePointer(file->handle, offsetLow, &offsetHigh, FILE_BEGIN);
#elif defined(__UTIL_LINUX)
fseek(file->fp, offset, SEEK_SET);
#endif
}
bool WcharToUtf8(wchar_t *in, size_t inSize, char *out, size_t outSize, size_t *pResultSize) {
#ifdef __UTIL_WINDOWS
size_t resultSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, in, inSize / sizeof(wchar_t), out, outSize, NULL, NULL);
if (resultSize == 0) FATAL("Failed to convert to UTF-8\n");
*pResultSize = resultSize;
return true;
#elif defined(__UTIL_LINUX)
iconv_t convDesc = iconv_open("UTF-8", "UTF-16");
if (convDesc == (iconv_t) -1) FATAL("Failed to get conversion description to UTF-8\n");
size_t remainingBytes = outSize;
if (iconv(convDesc, (char**) &in, &inSize, &out, &remainingBytes) == -1) {
FATAL("Failed to convert to UTF-8: %s (%d)\n", strerror(errno), errno);
}
if (inSize > 0) FATAL("Some characters were not converted to UTF-8\n");
*pResultSize = outSize - remainingBytes;
return true;
#endif
}
bool Utf8ToWchar(char *in, size_t inSize, wchar_t *out, size_t outSize) {
#ifdef __UTIL_WINDOWS
size_t resultSize = MultiByteToWideChar(CP_UTF8, 0, in, inSize, out, outSize / sizeof(wchar_t));
if (resultSize == 0) FATAL("Failed to convert from UTF-8: %d\n", GetLastError());
return true;
#elif defined(__UTIL_LINUX)
iconv_t convDesc = iconv_open("UTF-16", "UTF-8");
if (convDesc == (iconv_t) -1) FATAL("Failed to get conversion description from UTF-8\n");
size_t remainingBytes = outSize;
wchar_t *result = out;
if (iconv(convDesc, &in, &inSize, (char**) &out, &remainingBytes) == -1) {
FATAL("Failed to convert from UTF-8: %s (%d)\n", strerror(errno), errno);
}
if (inSize > 0) FATAL("Some characters were not converted from UTF-8\n");
// Remove 0xFEFF header
if (*result == 0xfeff) {
size_t numChars = (outSize - remainingBytes) / sizeof(wchar_t) - 1;
memmove(result, result + 1, numChars * sizeof(wchar_t));
result[numChars] = '\0';
}
return true;
#endif
}
bool AllocFullPath(const char *path, char **pFullPath) {
#ifdef __UTIL_WINDOWS
if (path[0] == '/') {
// Remove drive letter, e.g. /c/Projects/ph/ -> /Projects/ph/
const char *root = strchr(path + 1, '/');
if (root - path == 2) path = root;
}
size_t size = GetFullPathNameA(path, 0, NULL, NULL);
char *fullPath = malloc(size);
size_t resultSize = GetFullPathNameA(path, size, fullPath, NULL);
if (resultSize == 0 || resultSize > size) FATAL("Failed to get full path for '%s'\n", path);
*pFullPath = fullPath;
return true;
#elif defined(__UTIL_LINUX)
char *fullPath = realpath(path, NULL);
if (fullPath == NULL) FATAL("Failed to get full path for '%s'\n", path);
*pFullPath = fullPath;
return true;
#endif
}
void FreeFullPath(char **pFullPath) {
free(*pFullPath);
*pFullPath = NULL;
}
char* SplitLine(char *str) {
while (*str != '\n' && *str != '\r') {
if (*str == '\0') return str;
++str;
}
*str++ = '\0';
while (*str == '\n' || *str == '\r') ++str;
return str;
}
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
CC := gcc
CFLAGS := -g -Wall
CFLAGS := -g -Wall -I../include
ifneq ($(DEBUG),1)
CFLAGS += -O2 -DNDEBUG
+143 -190
View File
@@ -8,7 +8,7 @@
#include "util.h"
#include "files.h"
#define VERSION "1.1"
#define VERSION "1.1.1"
#define BUFFER_SIZE 1024 * 1024
uint8_t *readBuffer = NULL;
@@ -201,22 +201,20 @@ typedef struct {
} Arm9Metadata;
bool LoadArm9Metadata(Arm9Metadata *pMetadata) {
FILE *fp = fopen(ARM9_METADATA_FILE, "rb");
if (fp == NULL) FATAL("Failed to open ARM9 metadata '" ARM9_METADATA_FILE "'\n");
if (fread(pMetadata, sizeof(*pMetadata), 1, fp) != 1) FATAL("Failed to read ARM9 metadata '" ARM9_METADATA_FILE "'\n");
fclose(fp);
File file;
if (!FileOpenRead(ARM9_METADATA_FILE, &file)) return false;
if (FileRead(&file, pMetadata, sizeof(*pMetadata), 1) == 0) return false;
FileClose(&file);
return true;
}
// Reads an entire file into a newly allocated buffer and writes it to *pBuffer if successful.
// If pFileSize != NULL, this function writes the file's size into *pFileSize.
// The buffer can be freed with free().
bool ReadFile(const char *filePath, uint8_t **pBuffer, uint32_t *pFileSize) {
FILE *fp = fopen(filePath, "rb");
if (fp == NULL) FATAL("Failed to open file '%s'\n", filePath);
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
bool ReadFileAlloc(const char *filePath, uint8_t **pBuffer, uint32_t *pFileSize) {
File file;
if (!FileOpenRead(filePath, &file)) return false;
size_t size = FileSize(&file);
uint8_t *buffer = malloc(size);
if (buffer == NULL) FATAL("Failed to allocate buffer for file '%s'\n", filePath);
@@ -224,46 +222,44 @@ bool ReadFile(const char *filePath, uint8_t **pBuffer, uint32_t *pFileSize) {
uint8_t *end = buffer + size;
while (current < end) {
size_t bytesRead = fread(current, 1, BUFFER_SIZE, fp);
if (bytesRead == 0) FATAL("Failed to read from file '%s'\n", filePath);
size_t bytesRead = FileRead(&file, current, 1, BUFFER_SIZE);
if (bytesRead == 0) return false;
current += bytesRead;
}
fclose(fp);
FileClose(&file);
*pBuffer = buffer;
if (pFileSize != NULL) *pFileSize = size;
return true;
}
bool AppendFile(FILE *fpRom, const char *filePath, size_t *pAddress, uint32_t *pFileSize) {
bool AppendFile(File *rom, const char *filePath, size_t *pAddress, uint32_t *pFileSize) {
assert(readBuffer != NULL);
FILE *fp = fopen(filePath, "rb");
if (fp == NULL) FATAL("Failed to open file '%s'\n", filePath);
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
File file;
if (!FileOpenRead(filePath, &file)) return false;
size_t size = FileSize(&file);
size_t bytesWritten = 0;
while (bytesWritten < size) {
size_t bytesRead = fread(readBuffer, 1, BUFFER_SIZE, fp);
if (bytesRead == 0) FATAL("Failed to read from file '%s'\n", filePath);
if (fwrite(readBuffer, bytesRead, 1, fpRom) != 1) FATAL("Failed to write file '%s' to output ROM\n", filePath);
size_t bytesRead = FileRead(&file, readBuffer, 1, BUFFER_SIZE);
if (bytesRead == 0) return false;
if (!FileWrite(rom, readBuffer, bytesRead, 1)) FATAL("Failed to write file '%s' to output ROM\n", filePath);
bytesWritten += bytesRead;
}
fclose(fp);
FileClose(&file);
*pAddress += size;
if (pFileSize != NULL) *pFileSize = size;
return true;
}
bool WriteArm9Program(FILE *fpRom, size_t *pAddress, uint32_t *pFileSize, uint32_t *pSecureArea, const Arm9Metadata *metadata) {
bool WriteArm9Program(File *rom, size_t *pAddress, uint32_t *pFileSize, uint32_t *pSecureArea, const Arm9Metadata *metadata) {
size_t address = *pAddress;
uint8_t *arm9;
uint32_t fileSize;
if (!ReadFile(ARM9_PROGRAM_FILE, &arm9, &fileSize)) return false;
if (!ReadFileAlloc(ARM9_PROGRAM_FILE, &arm9, &fileSize)) return false;
// Write module info, see spAutoloadBlockInfosStart in asm/main.s
// This might seem unsafe since the ARM9 program is compressed, but the addresses we're writing to are in the secure area,
@@ -278,7 +274,7 @@ bool WriteArm9Program(FILE *fpRom, size_t *pAddress, uint32_t *pFileSize, uint32
memcpy(&arm9[offset], &compressedCodeEnd, sizeof(compressedCodeEnd));
offset += compressedCodeEnd;
if (fwrite(arm9, fileSize, 1, fpRom) != 1) FATAL("Failed to write ARM9 program\n");
if (!FileWrite(rom, arm9, fileSize, 1)) FATAL("Failed to write ARM9 program\n");
address += fileSize;
memcpy(pSecureArea, arm9, SECURE_AREA_SIZE);
@@ -289,22 +285,23 @@ bool WriteArm9Program(FILE *fpRom, size_t *pAddress, uint32_t *pFileSize, uint32
return true;
}
bool Align(size_t alignment, FILE *fpRom, size_t *pAddress) {
bool Align(size_t alignment, File *rom, size_t *pAddress) {
assert((alignment & (alignment - 1)) == 0);
size_t mask = alignment - 1;
size_t address = ftell(fpRom);
size_t address = FileOffset(rom);
size_t nextAddr = (address + mask) & ~mask;
char padValue = 0xff;
while (address < nextAddr) {
if (fputc(0xff, fpRom) == -1) FATAL("Failed to pad output ROM at address 0x%lx\n", address);
address += 1;
if (!FileWrite(rom, &padValue, sizeof(padValue), 1)) FATAL("Failed to pad output ROM at address 0x%lx\n", address);
address += sizeof(padValue);
}
*pAddress = address;
return true;
}
bool WriteArm9OverlayTable(
FILE *fpRom,
File *rom,
size_t *pAddress,
Header *pHeader,
const char *overlayDataFile,
@@ -314,39 +311,36 @@ bool WriteArm9OverlayTable(
) {
size_t address = *pAddress;
FILE *fp = fopen(ARM9_OVERLAY_TABLE_FILE, "rb");
if (fp == NULL) FATAL("Failed to open ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n");
fseek(fp, 0, SEEK_END);
size_t tableSize = ftell(fp);
File file;
if (!FileOpenRead(ARM9_OVERLAY_TABLE_FILE, &file)) {
FATAL("Failed to open ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n");
}
size_t tableSize = FileSize(&file);
if (tableSize % sizeof(OverlayEntry) != 0) {
FATAL("ARM9 overlay table has an invalid size (entries must be %ld bytes long)\n", sizeof(OverlayEntry));
}
size_t numOverlays = tableSize / sizeof(OverlayEntry);
fseek(fp, 0, SEEK_SET);
OverlayEntry *entries = malloc(tableSize);
if (entries == NULL) FATAL("Failed to allocate array for ARM9 overlay table entries");
if (fread(entries, tableSize, 1, fp) != 1) FATAL("Failed to read ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n");
fclose(fp);
if (FileRead(&file, entries, tableSize, 1) == 0) FATAL("Failed to read ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n");
FileClose(&file);
fp = fopen(overlayDataFile, "rb");
if (fp == NULL) FATAL("Failed to open ARM9 overlay data file '%s'\n", overlayDataFile);
fseek(fp, 0, SEEK_END);
size_t dataSize = ftell(fp);
if (!FileOpenRead(overlayDataFile, &file)) FATAL("Failed to open ARM9 overlay data file '%s'\n", overlayDataFile);
size_t dataSize = FileSize(&file);
if (dataSize != numOverlays * sizeof(OverlayData)) {
FATAL("ARM9 overlay data file has an invalid size (expected %ld overlays with %ld bytes each)\n", numOverlays, sizeof(OverlayData));
}
fseek(fp, 0, SEEK_SET);
OverlayData *data = malloc(dataSize);
if (data == NULL) FATAL("Failed to allocate array for ARM9 overlay data entries\n");
if (fread(data, dataSize, 1, fp) != 1) FATAL("Failed to read ARM9 overlay data '%s'\n", overlayDataFile);
fclose(fp);
if (FileRead(&file, data, dataSize, 1) == 0) FATAL("Failed to read ARM9 overlay data '%s'\n", overlayDataFile);
FileClose(&file);
for (size_t i = 0; i < numOverlays; ++i) {
entries[i].fileId = data[i].fileId;
}
if (fwrite(entries, tableSize, 1, fpRom) != 1) FATAL("Failed to write ARM9 overlay table\n");
if (!FileWrite(rom, entries, tableSize, 1)) FATAL("Failed to write ARM9 overlay table\n");
*pAddress = address;
*pEntries = entries;
@@ -357,7 +351,7 @@ bool WriteArm9OverlayTable(
}
bool WriteArm9OverlayFiles(
FILE *fpRom,
File *rom,
size_t *pAddress,
size_t numOverlays,
FatEntry *fatEntries,
@@ -367,14 +361,14 @@ bool WriteArm9OverlayFiles(
size_t address = *pAddress;
char fileName[32];
if (chdir(OVERLAYS_SUBDIR) != 0) FATAL("Failed to enter overlays directory '" OVERLAYS_SUBDIR "'\n");
if (!ChangeDir(OVERLAYS_SUBDIR)) return false;
for (size_t ovNum = 0; ovNum < numOverlays; ++ovNum) {
sprintf(fileName, "ov%02ld.lz", ovNum);
if (!Align(512, fpRom, &address)) return false;
if (!Align(512, rom, &address)) return false;
size_t startOffset = address;
uint32_t fileSize = 0;
if (!AppendFile(fpRom, fileName, &address, &fileSize)) return false;
if (!AppendFile(rom, fileName, &address, &fileSize)) return false;
table[ovNum].compressedSize = fileSize;
table[ovNum].isCompressed = true;
uint32_t fileId = data[ovNum].fileId;
@@ -383,22 +377,22 @@ bool WriteArm9OverlayFiles(
fatEntries[fileId].endOffset = address;
}
if (chdir("..") != 0) FATAL("Failed to leave overlays directory '" OVERLAYS_SUBDIR "'\n");
if (!ChangeDir("..")) return false;
*pAddress = address;
return true;
}
bool RewriteArm9OverlayTable(FILE *fpRom, const Header *header, OverlayEntry *entries, size_t numEntries) {
size_t prevAddress = ftell(fpRom);
fseek(fpRom, header->arm9Overlays.offset, SEEK_SET);
if (fwrite(entries, sizeof(*entries), numEntries, fpRom) != numEntries) FATAL("Failed to rewrite ARM9 overlay table\n");
fseek(fpRom, prevAddress, SEEK_SET);
bool RewriteArm9OverlayTable(File *rom, const Header *header, OverlayEntry *entries, size_t numEntries) {
size_t prevAddress = FileOffset(rom);
FileGoTo(rom, header->arm9Overlays.offset);
if (!FileWrite(rom, entries, sizeof(*entries), numEntries)) FATAL("Failed to rewrite ARM9 overlay table\n");
FileGoTo(rom, prevAddress);
return true;
}
bool WriteArm9Overlays(
FILE *fpRom,
File *rom,
size_t *pAddress,
Header *pHeader,
const char *overlayDataFile,
@@ -410,9 +404,9 @@ bool WriteArm9Overlays(
OverlayEntry *entries;
OverlayData *data;
size_t numOverlays;
if (!WriteArm9OverlayTable(fpRom, &address, pHeader, overlayDataFile, &entries, &data, &numOverlays)) return false;
if (!WriteArm9OverlayFiles(fpRom, &address, numOverlays, fatEntries, entries, data)) return false;
if (!RewriteArm9OverlayTable(fpRom, pHeader, entries, numOverlays)) return false;
if (!WriteArm9OverlayTable(rom, &address, pHeader, overlayDataFile, &entries, &data, &numOverlays)) return false;
if (!WriteArm9OverlayFiles(rom, &address, numOverlays, fatEntries, entries, data)) return false;
if (!RewriteArm9OverlayTable(rom, pHeader, entries, numOverlays)) return false;
free(data);
free(entries);
@@ -539,7 +533,7 @@ bool WriteFntSubtable(FileTree *tree, FntContext *pContext) {
return true;
}
bool WriteFnt(FILE *fpRom, size_t *pAddress, FileTree *pRoot, size_t firstFileId, size_t *pNumFiles) {
bool WriteFnt(File *rom, size_t *pAddress, FileTree *pRoot, size_t firstFileId, size_t *pNumFiles) {
size_t address = *pAddress;
FntContext ctx;
@@ -567,9 +561,9 @@ bool WriteFnt(FILE *fpRom, size_t *pAddress, FileTree *pRoot, size_t firstFileId
for (size_t i = 0; i < ctx.tableSize; ++i) {
ctx.table[i].subtableOffset += tableLength;
}
if (fwrite(ctx.table, sizeof(FntEntry), ctx.tableSize, fpRom) != ctx.tableSize) FATAL("Failed to write FNT table\n");
if (!FileWrite(rom, ctx.table, sizeof(FntEntry), ctx.tableSize)) FATAL("Failed to write FNT table\n");
address += ctx.tableSize * sizeof(FntEntry);
if (fwrite(ctx.subtable, ctx.subtableSize, 1, fpRom) != 1) FATAL("Failed to write FNT subtables\n");
if (!FileWrite(rom, ctx.subtable, ctx.subtableSize, 1)) FATAL("Failed to write FNT subtables\n");
address += ctx.subtableSize;
free(ctx.table);
@@ -580,14 +574,14 @@ bool WriteFnt(FILE *fpRom, size_t *pAddress, FileTree *pRoot, size_t firstFileId
return true;
}
bool WriteFat(FILE *fpRom, size_t *pAddress, size_t numFiles) {
bool WriteFat(File *rom, size_t *pAddress, size_t numFiles) {
size_t address = *pAddress;
FatEntry blank;
blank.startOffset = 0;
blank.endOffset = 0;
for (size_t i = 0; i < numFiles; ++i) {
if (fwrite(&blank, sizeof(blank), 1, fpRom) != 1) FATAL("Failed to write blank placeholder FAT entry\n");
if (!FileWrite(rom, &blank, sizeof(blank), 1)) FATAL("Failed to write blank placeholder FAT entry\n");
}
address += sizeof(blank) * numFiles;
@@ -595,47 +589,43 @@ bool WriteFat(FILE *fpRom, size_t *pAddress, size_t numFiles) {
return true;
}
bool ReadTitle(const char *language, const char *file, wchar_t *title, size_t titleSize) {
bool ReadTitle(const char *language, const char *titleFile, wchar_t *title, size_t titleSize) {
char buf[1024];
memset(buf, 0, sizeof(buf));
FILE *fp = fopen(file, "rb");
if (fp == NULL) FATAL("Failed to open %s banner title '%s'\n", language, file);
File file;
if (!FileOpenRead(titleFile, &file)) FATAL("Failed to open %s banner title '%s'\n", language, titleFile);
fseek(fp, 0, SEEK_END);
size_t fileSize = ftell(fp);
if (fileSize > sizeof(buf) - 1) FATAL("Buffer too small for %s banner title '%s'\n", language, file);
fseek(fp, 0, SEEK_SET);
size_t fileSize = FileSize(&file);
if (fileSize > sizeof(buf) - 1) FATAL("Buffer too small for %s banner title '%s'\n", language, titleFile);
if (fread(buf, fileSize, 1, fp) != 1) FATAL("Failed to read %s banner title '%s'\n", language, file);
fclose(fp);
if (FileRead(&file, buf, fileSize, 1) != 1) FATAL("Failed to read %s banner title '%s'\n", language, titleFile);
FileClose(&file);
memset(title, 0, titleSize);
if (!Utf8ToWchar(buf, fileSize, title, titleSize)) return false;
return true;
}
bool WriteBanner(FILE *fpRom, size_t *pAddress) {
bool WriteBanner(File *rom, size_t *pAddress) {
size_t address = *pAddress;
FILE *fp;
File file;
Banner banner;
banner.version = 1;
memset(banner.reserved0, 0, sizeof(banner.reserved0));
fp = fopen(ICON_BITMAP_FILE, "rb");
if (fp == NULL) FATAL("Failed to open banner icon bitmap '" ICON_BITMAP_FILE "'\n");
if (fread(banner.iconBitmap, sizeof(banner.iconBitmap), 1, fp) != 1) {
if (!FileOpenRead(ICON_BITMAP_FILE, &file)) FATAL("Failed to open banner icon bitmap '" ICON_BITMAP_FILE "'\n");
if (FileRead(&file, banner.iconBitmap, sizeof(banner.iconBitmap), 1) != 1) {
FATAL("Failed to read banner icon bitmap '" ICON_BITMAP_FILE "'\n");
}
fclose(fp);
FileClose(&file);
fp = fopen(ICON_PALETTE_FILE, "rb");
if (fp == NULL) FATAL("Failed to open banner icon palette '" ICON_PALETTE_FILE "'\n");
if (fread(banner.iconPalette, sizeof(banner.iconPalette), 1, fp) != 1) {
if (!FileOpenRead(ICON_PALETTE_FILE, &file)) FATAL("Failed to open banner icon palette '" ICON_PALETTE_FILE "'\n");
if (FileRead(&file, banner.iconPalette, sizeof(banner.iconPalette), 1) != 1) {
FATAL("Failed to read banner icon palette '" ICON_PALETTE_FILE "'\n");
}
fclose(fp);
FileClose(&file);
if (!ReadTitle("Japanese", TITLE_JAP_FILE, banner.japaneseTitle, sizeof(banner.japaneseTitle))) return false;
if (!ReadTitle("English", TITLE_ENG_FILE, banner.englishTitle, sizeof(banner.englishTitle))) return false;
@@ -648,14 +638,14 @@ bool WriteBanner(FILE *fpRom, size_t *pAddress) {
uint8_t *crcEnd = (uint8_t*) &banner + sizeof(banner);
banner.crc = Crc(crcStart, crcEnd - crcStart);
if (fwrite(&banner, sizeof(banner), 1, fpRom) != 1) FATAL("Failed to write banner\n");
if (!FileWrite(rom, &banner, sizeof(banner), 1)) FATAL("Failed to write banner\n");
address += sizeof(banner);
*pAddress = address;
return true;
}
bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree, FatEntry *entries, uint16_t firstFileId) {
bool TraverseAndAppendAssets(File *rom, size_t *pAddress, const FileTree *tree, FatEntry *entries, uint16_t firstFileId) {
size_t address = *pAddress;
for (size_t i = 0; i < tree->numChildren; ++i) {
@@ -666,17 +656,17 @@ bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree
strncpy(name, entry->name, entry->length);
name[entry->length] = '\0';
if (!entry->isSubdir) {
if (!Align(512, fpRom, &address)) return false;
if (!Align(512, rom, &address)) return false;
size_t startOffset = address;
if (!AppendFile(fpRom, name, &address, NULL)) return false;
if (!AppendFile(rom, name, &address, NULL)) return false;
// For files, `firstFileId` is the ID of the file
entries[child->firstFileId].startOffset = startOffset;
entries[child->firstFileId].endOffset = address;
continue;
}
if (chdir(name) != 0) FATAL("Failed to enter assets directory '%s'\n", name);
if (!TraverseAndAppendAssets(fpRom, &address, child, entries, child->firstFileId)) return false;
if (chdir("..") != 0) FATAL("Failed to leave assets directory '%s'\n", name);
if (!ChangeDir(name)) return false;
if (!TraverseAndAppendAssets(rom, &address, child, entries, child->firstFileId)) return false;
if (!ChangeDir("..")) return false;
}
*pAddress = address;
@@ -684,7 +674,7 @@ bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree
}
bool AppendAssets(
FILE *fpRom,
File *rom,
size_t *pAddress,
FileTree *root,
FatEntry *entries,
@@ -694,24 +684,22 @@ bool AppendAssets(
size_t address = *pAddress;
if (assetsListFile == NULL) {
if (!TraverseAndAppendAssets(fpRom, &address, root, entries, numOverlays)) return false;
if (!TraverseAndAppendAssets(rom, &address, root, entries, numOverlays)) return false;
*pAddress = address;
return true;
}
FILE *fp = fopen(assetsListFile, "rb");
if (fp == NULL) FATAL("Failed to open assets list file '%s'\n", assetsListFile);
fseek(fp, 0, SEEK_END);
size_t listSize = ftell(fp);
File file;
if (!FileOpenRead(assetsListFile, &file)) FATAL("Failed to open assets list file '%s'\n", assetsListFile);
size_t listSize = FileSize(&file);
char *assetsList = malloc(listSize + 1);
if (assetsList == NULL) FATAL("Failed to allocate string for assets list file '%s'\n", assetsListFile);
fseek(fp, 0, SEEK_SET);
if (fread(assetsList, listSize, 1, fp) != 1) FATAL("Failed to read from assets list file '%s'\n", assetsListFile);
fclose(fp);
if (FileRead(&file, assetsList, listSize, 1) != 1) FATAL("Failed to read from assets list file '%s'\n", assetsListFile);
FileClose(&file);
assetsList[listSize] = '\0';
char assetsDir[256];
if (getcwd(assetsDir, sizeof(assetsDir)) == NULL) FATAL("Failed to get assets directory\n");
if (!GetCurrentDir(assetsDir, sizeof(assetsDir))) FATAL("Failed to get assets directory\n");
char *const listEnd = assetsList + listSize;
for (char *path = assetsList, *next; path < listEnd; path = next) {
@@ -723,9 +711,9 @@ bool AppendAssets(
// First file ID of root directory is given from number of overlays
uint16_t firstFileId = subTree->entry == NULL ? numOverlays : subTree->firstFileId;
if (path[1] != '\0' && chdir(&path[1]) != 0) FATAL("Failed to enter assets directory '%s'\n", path);
if (!TraverseAndAppendAssets(fpRom, &address, subTree, entries, firstFileId)) return false;
if (chdir(assetsDir) != 0) FATAL("Failed to leave assets directory '%s'\n", path);
if (path[1] != '\0' && !ChangeDir(&path[1])) return false;
if (!TraverseAndAppendAssets(rom, &address, subTree, entries, firstFileId)) return false;
if (!ChangeDir(assetsDir)) return false;
subTree->addedToFat = true;
}
@@ -736,24 +724,25 @@ bool AppendAssets(
return true;
}
bool RewriteFat(FILE *fpRom, size_t fatStart, const FatEntry *entries, size_t numFiles) {
fseek(fpRom, fatStart, SEEK_SET);
if (fwrite(entries, sizeof(*entries), numFiles, fpRom) != numFiles) FATAL("Failed to rewrite FAT table\n");
fseek(fpRom, 0, SEEK_END);
bool RewriteFat(File *rom, size_t fatStart, const FatEntry *entries, size_t numFiles) {
size_t prevPos = FileOffset(rom);
FileGoTo(rom, fatStart);
if (!FileWrite(rom, entries, sizeof(*entries), numFiles)) FATAL("Failed to rewrite FAT table\n");
FileGoTo(rom, prevPos);
return true;
}
bool FinalizeHeader(FILE *fpRom, Header *pHeader, const char *arm7bios, uint32_t *secureArea, const Arm9Metadata *metadata) {
bool FinalizeHeader(File *rom, Header *pHeader, const char *arm7bios, uint32_t *secureArea, const Arm9Metadata *metadata) {
Header header;
memcpy(&header, pHeader, sizeof(header));
if (arm7bios != NULL) {
FILE *fp = fopen(arm7bios, "rb");
if (fp == NULL) FATAL("Failed to open ARM7 BIOS '%s'\n", arm7bios);
fseek(fp, 0x30, SEEK_SET);
File file;
if (!FileOpenRead(arm7bios, &file)) FATAL("Failed to open ARM7 BIOS '%s'\n", arm7bios);
FileGoTo(&file, 0x30);
uint8_t encKey[sizeof(Blowfish)];
if (fread(&encKey, sizeof(encKey), 1, fp) != 1) FATAL("Failed to read encrypion key\n");
fclose(fp);
if (FileRead(&file, &encKey, sizeof(encKey), 1) != 1) FATAL("Failed to read encrypion key\n");
FileClose(&file);
if (!BlowfishInit(encKey, pHeader, 3)) return false;
for (size_t i = 2; i < 0x200; i += 2) {
@@ -773,13 +762,13 @@ bool FinalizeHeader(FILE *fpRom, Header *pHeader, const char *arm7bios, uint32_t
header.headerCrc = Crc(&header, offsetof(Header, headerCrc));
size_t prevPos = ftell(fpRom);
fseek(fpRom, 0, SEEK_SET);
if (fwrite(&header, sizeof(header), 1, fpRom) != 1) {
size_t prevPos = FileOffset(rom);
FileGoTo(rom, 0);
if (!FileWrite(rom, &header, sizeof(header), 1)) {
fprintf(stderr, "Failed to rewrite header\n");
return 1;
}
fseek(fpRom, prevPos, SEEK_SET);
FileGoTo(rom, prevPos);
memcpy(pHeader, &header, sizeof(header));
return true;
@@ -884,13 +873,13 @@ int main(int argc, char **argv) {
// --------------------- Set up ---------------------
char rootDir[256];
if (getcwd(rootDir, sizeof(rootDir)) == NULL) {
if (!GetCurrentDir(rootDir, sizeof(rootDir))) {
fprintf(stderr, "Failed to get root directory\n");
return 1;
}
FILE *fpRom = fopen(romFile, "wb");
if (fpRom == NULL) {
File rom;
if (!FileOpenWrite(romFile, &rom)) {
fprintf(stderr, "Failed to open output ROM file '%s'\n", romFile);
return 1;
}
@@ -912,7 +901,7 @@ int main(int argc, char **argv) {
Header header;
InitHeader(&header, &info);
if (fwrite(&header, sizeof(header), 1, fpRom) != 1) {
if (!FileWrite(&rom, &header, sizeof(header), 1)) {
fprintf(stderr, "Failed to write NDS header\n");
return 1;
}
@@ -921,62 +910,44 @@ int main(int argc, char **argv) {
// --------------------- Get canonical file paths ---------------------
if (assetsListFile != NULL && !AllocFullPath(assetsListFile, &assetsListFile)) return 1;
if (chdir(baseDir) != 0) {
fprintf(stderr, "Failed to enter base directory '%s'\n", baseDir);
return 1;
}
if (!ChangeDir(baseDir)) return 1;
char *arm9overlayDataFile = NULL;
if (!AllocFullPath(ARM9_OVERLAY_DATA_FILE, &arm9overlayDataFile)) return 1;
if (chdir(rootDir) != 0) {
fprintf(stderr, "Failed to leave base directory '%s'\n", baseDir);
return 1;
}
if (!ChangeDir(rootDir)) return 1;
// --------------------- Write ARM9 program ---------------------
if (chdir(buildDir) != 0) {
fprintf(stderr, "Failed to enter build directory '%s'\n", buildDir);
return 1;
}
if (!ChangeDir(buildDir)) return 1;
if (!Align(512, fpRom, &address)) return 1;
if (!Align(512, &rom, &address)) return 1;
header.arm9.offset = address;
Arm9Metadata metadata;
if (!LoadArm9Metadata(&metadata)) return 1;
uint32_t secureArea[SECURE_AREA_SIZE / sizeof(uint32_t)];
if (!WriteArm9Program(fpRom, &address, &header.arm9.size, secureArea, &metadata)) return 1;
if (!AppendFile(fpRom, ARM9_FOOTER_FILE, &address, NULL)) return 1;
if (!WriteArm9Program(&rom, &address, &header.arm9.size, secureArea, &metadata)) return 1;
if (!AppendFile(&rom, ARM9_FOOTER_FILE, &address, NULL)) return 1;
// --------------------- Write ARM9 overlay table and overlay files ---------------------
if (!Align(512, fpRom, &address)) return 1;
if (!Align(512, &rom, &address)) return 1;
header.arm9Overlays.offset = address;
size_t numOverlays = 0;
FatEntry overlayEntries[MAX_OVERLAYS];
if (!WriteArm9Overlays(fpRom, &address, &header, arm9overlayDataFile, overlayEntries, &numOverlays)) return 1;
if (!WriteArm9Overlays(&rom, &address, &header, arm9overlayDataFile, overlayEntries, &numOverlays)) return 1;
FreeFullPath(&arm9overlayDataFile);
if (chdir(rootDir) != 0) {
fprintf(stderr, "Failed to leave build directory '%s'\n", buildDir);
return 1;
}
if (!ChangeDir(rootDir)) return 1;
if (chdir(baseDir) != 0) {
fprintf(stderr, "Failed to enter base directory '%s'\n", baseDir);
return 1;
}
if (!ChangeDir(baseDir)) return 1;
// --------------------- Write ARM7 program ---------------------
if (!Align(512, fpRom, &address)) return 1;
if (!Align(512, &rom, &address)) return 1;
header.arm7.offset = address;
if (!AppendFile(fpRom, ARM7_PROGRAM_FILE, &address, &header.arm7.size)) return 1;
if (!AppendFile(&rom, ARM7_PROGRAM_FILE, &address, &header.arm7.size)) return 1;
if (chdir(ASSETS_SUBDIR) != 0) {
fprintf(stderr, "Failed to enter assets directory '" ASSETS_SUBDIR "'\n");
return 1;
}
if (!ChangeDir(ASSETS_SUBDIR)) return 1;
// --------------------- Write file name table (FNT) ---------------------
@@ -984,84 +955,66 @@ int main(int argc, char **argv) {
if (!MakeFileTree(&root)) return false;
if (!SortFileTree(&root, CompareFileTreeNormal)) return false;
if (!Align(512, fpRom, &address)) return 1;
if (!Align(512, &rom, &address)) return 1;
size_t numFiles = 0;
header.fileNames.offset = address;
if (!WriteFnt(fpRom, &address, &root, numOverlays, &numFiles)) return 1;
if (!WriteFnt(&rom, &address, &root, numOverlays, &numFiles)) return 1;
header.fileNames.size = address - header.fileNames.offset;
// --------------------- Write file allocation table (FAT) ---------------------
if (!Align(512, fpRom, &address)) return 1;
if (!Align(512, &rom, &address)) return 1;
header.fileAllocs.offset = address;
if (!WriteFat(fpRom, &address, numFiles)) return 1;
if (!WriteFat(&rom, &address, numFiles)) return 1;
header.fileAllocs.size = address - header.fileAllocs.offset;
FatEntry *fatEntries = malloc(numFiles * sizeof(FatEntry));
memcpy(fatEntries, overlayEntries, numOverlays * sizeof(*fatEntries));
if (chdir("..") != 0) {
fprintf(stderr, "Failed to leave assets directory '" ASSETS_SUBDIR "'\n");
return 1;
}
if (!ChangeDir("..")) return 1;
// --------------------- Write banner ---------------------
if (!Align(512, fpRom, &address)) return false;
if (!Align(512, &rom, &address)) return false;
header.bannerOffset = address;
if (!WriteBanner(fpRom, &address)) return false;
if (!WriteBanner(&rom, &address)) return false;
if (chdir(ASSETS_SUBDIR) != 0) {
fprintf(stderr, "Failed to enter assets directory '" ASSETS_SUBDIR "'\n");
return 1;
}
if (!ChangeDir(ASSETS_SUBDIR)) return 1;
// --------------------- Write assets ---------------------
if (!Align(512, fpRom, &address)) return false;
if (!Align(512, &rom, &address)) return false;
if (!SortFileTree(&root, CompareFileTreeAscii)) return false;
if (!AppendAssets(fpRom, &address, &root, fatEntries, numOverlays, assetsListFile)) return false;
if (!AppendAssets(&rom, &address, &root, fatEntries, numOverlays, assetsListFile)) return false;
if (assetsListFile != NULL) FreeFullPath(&assetsListFile);
if (!RewriteFat(fpRom, header.fileAllocs.offset, fatEntries, numFiles))
if (!RewriteFat(&rom, header.fileAllocs.offset, fatEntries, numFiles))
free(fatEntries);
if (!FreeFileTree(&root)) return false;
if (chdir("..") != 0) {
fprintf(stderr, "Failed to leave assets directory '" ASSETS_SUBDIR "'\n");
return 1;
}
if (!ChangeDir("..")) return 1;
if (chdir(rootDir) != 0) {
fprintf(stderr, "Failed to leave base directory '%s'\n", baseDir);
return 1;
}
if (!ChangeDir(rootDir)) return 1;
// --------------------- Write padding ---------------------
header.romSize = address;
header.capacity = 15 - __builtin_clz(header.romSize);
size_t romEnd = 1 << (17 + header.capacity);
if (!Align(romEnd, fpRom, &address)) return 1;
if (!Align(romEnd, &rom, &address)) return 1;
// --------------------- Update header ---------------------
char *arm7bios = NULL;
if (arm7biosFile != NULL && !AllocFullPath(arm7biosFile, &arm7bios)) return 1;
if (chdir(buildDir) != 0) {
fprintf(stderr, "Failed to enter build directory '%s'\n", buildDir);
return 1;
}
if (!ChangeDir(buildDir)) return 1;
if (!FinalizeHeader(fpRom, &header, arm7bios, secureArea, &metadata)) return false;
if (!FinalizeHeader(&rom, &header, arm7bios, secureArea, &metadata)) return false;
FreeFullPath(&arm7bios);
if (chdir(rootDir) != 0) {
fprintf(stderr, "Failed to leave build directory '%s'\n", buildDir);
return 1;
}
if (!ChangeDir(rootDir)) return 1;
free(readBuffer);
fclose(fpRom);
FileClose(&rom);
}
+39 -64
View File
@@ -8,7 +8,7 @@
#include "ph.h"
#include "util.h"
#define VERSION "1.0.1"
#define VERSION "1.0.2"
#define INDENT 4
// Command line flags for debugging purposes
@@ -24,16 +24,6 @@ void Indent(size_t depth) {
}
}
bool MakeDir(const char *dir) {
struct stat dirStat;
if (stat(dir, &dirStat) != 0) {
if (mkdir(dir, 0777) != 0) FATAL("Failed to make directory '%s'\n", dir);
return true;
}
if (!S_ISDIR(dirStat.st_mode)) FATAL("Could not make directory '%s' due to a file with the same name\n", dir);
return true;
}
bool CheckRegion(const Header *pHeader, BuildInfo *pInfo) {
Region region = pHeader->gamecode[3];
if (
@@ -51,43 +41,42 @@ bool CheckRegion(const Header *pHeader, BuildInfo *pInfo) {
}
bool ExtractArm7(const uint8_t *rom, ProgramOffset *pArm7) {
FILE *fp = fopen(ARM7_PROGRAM_FILE, "wb");
if (fp == NULL) FATAL("Failed to create ARM7 program '" ARM7_PROGRAM_FILE "'\n");
if (fwrite(rom + pArm7->offset, pArm7->size, 1, fp) != 1) FATAL("Failed to write ARM7 program '" ARM7_PROGRAM_FILE "'\n");
fclose(fp);
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 *file, const wchar_t *title, size_t titleSize) {
bool ExtractTitle(const char *language, const char *fileName, const wchar_t *title, size_t titleSize) {
char buf[1024];
FILE *fp = fopen(file, "wb");
if (fp == NULL) FATAL("Failed to create %s banner title '%s'\n", language, file);
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;
if (fputs(buf, fp) == -1) FATAL("Failed to write %s banner title '%s'\n", language, file);
fclose(fp);
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 *fp;
File file;
fp = fopen(ICON_BITMAP_FILE, "wb");
if (fp == NULL) FATAL("Failed to create banner icon bitmap '" ICON_BITMAP_FILE "'\n");
if (fwrite(pBanner->iconBitmap, sizeof(pBanner->iconBitmap), 1, fp) != 1) {
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");
}
fclose(fp);
FileClose(&file);
fp = fopen(ICON_PALETTE_FILE, "wb");
if (fp == NULL) FATAL("Failed to create banner icon palette '" ICON_PALETTE_FILE "'\n");
if (fwrite(pBanner->iconPalette, sizeof(pBanner->iconPalette), 1, fp) != 1) {
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");
}
fclose(fp);
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;
@@ -157,10 +146,10 @@ bool ExtractSubtable(
size_t fileSize = pFatEntry->endOffset - pFatEntry->startOffset;
const uint8_t *pFileBytes = rom + pFatEntry->startOffset;
FILE *fp = fopen(name, "wb");
if (fp == NULL) FATAL("Failed to open assets file '%s'\n", name);
if (fwrite(pFileBytes, fileSize, 1, fp) != 1) FATAL("Failed to write to assets file '%s'\n", name);
fclose(fp);
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;
@@ -181,7 +170,7 @@ bool ExtractSubtable(
printf("%s {\n", name);
}
if (!MakeDir(name)) return false;
if (chdir(name) != 0) FATAL("Failed to enter assets subdirectory '%s'\n", name);
if (!ChangeDir(name)) return false;
uint16_t subdirId = READ_SUBDIR_ID(pSubEntry);
uint16_t subdirIndex = subdirId & 0xfff;
@@ -205,7 +194,7 @@ bool ExtractSubtable(
printf("}\n");
}
if (chdir("..") != 0) FATAL("Failed to leave assets subdirectory '%s'\n", name);
if (!ChangeDir("..")) return false;
subEntryAddr += sizeof(FntSubEntry) + pSubEntry->length + sizeof(subdirId);
pSubEntry = (const FntSubEntry*) subEntryAddr;
}
@@ -281,17 +270,17 @@ 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 *fp = fopen(ARM9_OVERLAY_DATA_FILE, "wb");
if (fp == NULL) FATAL("Failed to open overlay data file '" ARM9_OVERLAY_DATA_FILE "'\n");
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 (fwrite(&data, sizeof(data), 1, fp) != 1) {
FATAL("Failed to write overlay data to '" ARM9_OVERLAY_DATA_FILE "'\n");
}
if (!FileWrite(&file, &data, sizeof(data), 1)) FATAL("Failed to write overlay data to '" ARM9_OVERLAY_DATA_FILE "'\n");
}
fclose(fp);
FileClose(&file);
return true;
}
@@ -353,24 +342,22 @@ int main(int argc, const char **argv) {
// --------------------- Load ROM ---------------------
FILE *fpRom = fopen(romFile, "rb");
if (fpRom == NULL) {
File fileRom;
if (!FileOpenRead(romFile, &fileRom)) {
fprintf(stderr, "Failed to open input ROM '%s'\n", romFile);
return 1;
}
fseek(fpRom, 0, SEEK_END);
size_t romSize = ftell(fpRom);
fseek(fpRom, 0, SEEK_SET);
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 (fread(rom, romSize, 1, fpRom) != 1) {
if (FileRead(&fileRom, rom, romSize, 1) != 1) {
fprintf(stderr, "Failed to read from '%s'\n", romFile);
return 1;
}
fclose(fpRom);
FileClose(&fileRom);
// --------------------- Set up ---------------------
@@ -378,10 +365,7 @@ int main(int argc, const char **argv) {
BuildInfo info;
if (!CheckRegion(pHeader, &info)) return 1;
if (!MakeDir(outDir)) return 1;
if (chdir(outDir) != 0) {
fprintf(stderr, "Failed to enter output directory '%s'\n", outDir);
return 1;
}
if (!ChangeDir(outDir)) return 1;
// --------------------- Extract ARM7 program ---------------------
@@ -395,26 +379,17 @@ int main(int argc, const char **argv) {
// --------------------- Extract assets ---------------------
if (!MakeDir(ASSETS_SUBDIR)) return 1;
if (chdir(ASSETS_SUBDIR) != 0) {
fprintf(stderr, "Failed to enter assets directory '" ASSETS_SUBDIR "'\n");
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 (chdir("..") != 0) {
fprintf(stderr, "Failed to leave assets directory '" ASSETS_SUBDIR "'\n");
return 1;
}
if (!ChangeDir("..")) return 1;
// --------------------- Extract overlay data ---------------------
if (!ExtractOverlayData(rom, pHeader)) return 1;
if (chdir("..") != 0) {
fprintf(stderr, "Failed to leave output directory '%s'\n", outDir);
return 1;
}
if (!ChangeDir("..")) return 1;
free(rom);
}
+4 -4
View File
@@ -18,7 +18,7 @@ typedef struct FileTree {
bool MakeFileTree(FileTree *pTree);
bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) {
#ifdef _WIN32
#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");
@@ -30,7 +30,7 @@ bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *user
if (!callback(name, isDir, userData)) return false;
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
#elif __linux__
#elif defined(__UTIL_LINUX)
DIR *dir = opendir(".");
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
@@ -80,7 +80,7 @@ bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) {
memcpy(entry->name, name, nameLength);
WRITE_SUBDIR_ID(entry, 0);
if (chdir(name) != 0) FATAL("Failed to enter directory '%s'\n", name);
if (!ChangeDir(name)) return false;
FileTree child;
if (!MakeFileTree(&child)) return false;
@@ -89,7 +89,7 @@ bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) {
memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child));
pTree->numChildren += 1;
if (chdir("..") != 0) FATAL("Failed to leave directory '%s'\n", name);
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);
-112
View File
@@ -1,112 +0,0 @@
#ifndef __FS_H
#define __FS_H
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define FATAL(...) do { fprintf(stderr, __VA_ARGS__); return false; } while (0)
#define WRITE16(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; } while (0)
#define WRITE24(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; ((uint8_t*) buf)[2] = ((val) >> 16) & 0xFF; } while (0)
#define WRITE32(buf,val) do { ((uint8_t*) buf)[0] = (val) & 0xFF; ((uint8_t*) buf)[1] = ((val) >> 8) & 0xFF; ((uint8_t*) buf)[2] = ((val) >> 16) & 0xFF; ((uint8_t*) buf)[3] = ((val) >> 24) & 0xFF; } while (0)
#define READ16(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8))
#define READ24(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8) | (((uint8_t*) buf)[2] << 16))
#define READ32(buf) (((uint8_t*) buf)[0] | (((uint8_t*) buf)[1] << 8) | (((uint8_t*) buf)[2] << 16) | (((uint8_t*) buf)[3] << 24))
#define REVERSE32(val) ((val >> 24) | ((val & 0xff0000) >> 8) | ((val & 0xff00) << 8) | ((val & 0xff) << 24))
#ifdef _WIN32
# include <Windows.h>
# include <sys/stat.h>
# define mkdir(path, mode) mkdir(path)
#elif __linux__
# include <errno.h>
# include <sys/stat.h>
# include <iconv.h>
# include <dirent.h>
# include <unistd.h>
#else
# error "Target platform not supported"
#endif
bool WcharToUtf8(wchar_t *in, size_t inSize, char *out, size_t outSize, size_t *pResultSize) {
#ifdef _WIN32
size_t resultSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, in, inSize / sizeof(wchar_t), out, outSize, NULL, NULL);
if (resultSize == 0) FATAL("Failed to convert to UTF-8\n");
*pResultSize = resultSize;
return true;
#elif __linux__
iconv_t convDesc = iconv_open("UTF-8", "UTF-16");
if (convDesc == (iconv_t) -1) FATAL("Failed to get conversion description to UTF-8\n");
size_t remainingBytes = outSize;
if (iconv(convDesc, (char**) &in, &inSize, &out, &remainingBytes) == -1) {
FATAL("Failed to convert to UTF-8: %s (%d)\n", strerror(errno), errno);
}
if (inSize > 0) FATAL("Some characters were not converted to UTF-8\n");
*pResultSize = outSize - remainingBytes;
return true;
#endif
}
bool Utf8ToWchar(char *in, size_t inSize, wchar_t *out, size_t outSize) {
#ifdef _WIN32
size_t resultSize = MultiByteToWideChar(CP_UTF8, 0, in, inSize, out, outSize / sizeof(wchar_t));
if (resultSize == 0) FATAL("Failed to convert from UTF-8: %d\n", GetLastError());
return true;
#elif __linux__
iconv_t convDesc = iconv_open("UTF-16", "UTF-8");
if (convDesc == (iconv_t) -1) FATAL("Failed to get conversion description from UTF-8\n");
size_t remainingBytes = outSize;
wchar_t *result = out;
if (iconv(convDesc, &in, &inSize, (char**) &out, &remainingBytes) == -1) {
FATAL("Failed to convert from UTF-8: %s (%d)\n", strerror(errno), errno);
}
if (inSize > 0) FATAL("Some characters were not converted from UTF-8\n");
// Remove 0xFEFF header
if (*result == 0xfeff) {
size_t numChars = (outSize - remainingBytes) / sizeof(wchar_t) - 1;
memmove(result, result + 1, numChars * sizeof(wchar_t));
result[numChars] = '\0';
}
return true;
#endif
}
bool AllocFullPath(const char *path, char **pFullPath) {
#ifdef _WIN32
if (path[0] == '/') {
// Remove drive letter, e.g. /c/Projects/ph/ -> /Projects/ph/
const char *root = strchr(path + 1, '/');
if (root - path == 2) path = root;
}
size_t size = GetFullPathNameA(path, 0, NULL, NULL);
char *fullPath = malloc(size);
size_t resultSize = GetFullPathNameA(path, size, fullPath, NULL);
if (resultSize == 0 || resultSize > size) FATAL("Failed to get full path for '%s'\n", path);
*pFullPath = fullPath;
return true;
#elif __linux__
char *fullPath = realpath(path, NULL);
if (fullPath == NULL) FATAL("Failed to get full path for '%s'\n", path);
*pFullPath = fullPath;
return true;
#endif
}
void FreeFullPath(char **pFullPath) {
free(*pFullPath);
*pFullPath = NULL;
}
char* SplitLine(char *str) {
while (*str != '\n' && *str != '\r') {
if (*str == '\0') return str;
++str;
}
*str++ = '\0';
while (*str == '\n' || *str == '\r') ++str;
return str;
}
#endif