From 54eaa5de61f51553fb9202c902d732afef9d21a7 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 22 Feb 2024 18:20:12 +0100 Subject: [PATCH 1/9] Add MSYS support in `rom` tools --- tools/rom/build.c | 76 ++++++++++++--------------------------------- tools/rom/extract.c | 34 ++++---------------- tools/rom/util.h | 57 +++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 95 deletions(-) diff --git a/tools/rom/build.c b/tools/rom/build.c index f5b28e10..819f09cf 100644 --- a/tools/rom/build.c +++ b/tools/rom/build.c @@ -367,7 +367,7 @@ 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); @@ -383,7 +383,7 @@ 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; @@ -674,9 +674,9 @@ bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree entries[child->firstFileId].endOffset = address; continue; } - if (chdir(name) != 0) FATAL("Failed to enter assets directory '%s'\n", name); + if (!ChangeDir(name)) return false; if (!TraverseAndAppendAssets(fpRom, &address, child, entries, child->firstFileId)) return false; - if (chdir("..") != 0) FATAL("Failed to leave assets directory '%s'\n", name); + if (!ChangeDir("..")) return false; } *pAddress = address; @@ -711,7 +711,7 @@ bool AppendAssets( 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 +723,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 (path[1] != '\0' && !ChangeDir(&path[1])) return false; if (!TraverseAndAppendAssets(fpRom, &address, subTree, entries, firstFileId)) return false; - if (chdir(assetsDir) != 0) FATAL("Failed to leave assets directory '%s'\n", path); + if (!ChangeDir(assetsDir)) return false; subTree->addedToFat = true; } @@ -884,7 +884,7 @@ 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; } @@ -921,23 +921,14 @@ 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; header.arm9.offset = address; @@ -957,15 +948,9 @@ int main(int argc, char **argv) { if (!WriteArm9Overlays(fpRom, &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 --------------------- @@ -973,10 +958,7 @@ int main(int argc, char **argv) { header.arm7.offset = address; if (!AppendFile(fpRom, 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) --------------------- @@ -999,10 +981,7 @@ int main(int argc, char **argv) { 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 --------------------- @@ -1010,10 +989,7 @@ int main(int argc, char **argv) { header.bannerOffset = address; if (!WriteBanner(fpRom, &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 --------------------- @@ -1027,15 +1003,9 @@ int main(int argc, char **argv) { 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 --------------------- @@ -1049,18 +1019,12 @@ int main(int argc, char **argv) { 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; 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); diff --git a/tools/rom/extract.c b/tools/rom/extract.c index 5ce4a293..c236b5f8 100644 --- a/tools/rom/extract.c +++ b/tools/rom/extract.c @@ -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 ( @@ -181,7 +171,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 +195,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; } @@ -378,10 +368,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 +382,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); } diff --git a/tools/rom/util.h b/tools/rom/util.h index a1dbf3b7..a757278a 100644 --- a/tools/rom/util.h +++ b/tools/rom/util.h @@ -1,5 +1,5 @@ -#ifndef __FS_H -#define __FS_H +#ifndef __UTIL_H +#define __UTIL_H #include #include @@ -16,11 +16,11 @@ #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 +#if defined(_WIN32) || defined(__MSYS__) +# define __UTIL_WINDOWS # include -# include -# define mkdir(path, mode) mkdir(path) #elif __linux__ +# define __UTIL_LINUX # include # include # include @@ -30,13 +30,48 @@ # 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); +} + bool WcharToUtf8(wchar_t *in, size_t inSize, char *out, size_t outSize, size_t *pResultSize) { -#ifdef _WIN32 +#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 __linux__ +#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; @@ -50,11 +85,11 @@ bool WcharToUtf8(wchar_t *in, size_t inSize, char *out, size_t outSize, size_t * } bool Utf8ToWchar(char *in, size_t inSize, wchar_t *out, size_t outSize) { -#ifdef _WIN32 +#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 __linux__ +#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; @@ -74,7 +109,7 @@ bool Utf8ToWchar(char *in, size_t inSize, wchar_t *out, size_t outSize) { } bool AllocFullPath(const char *path, char **pFullPath) { -#ifdef _WIN32 +#ifdef __UTIL_WINDOWS if (path[0] == '/') { // Remove drive letter, e.g. /c/Projects/ph/ -> /Projects/ph/ const char *root = strchr(path + 1, '/'); @@ -86,7 +121,7 @@ bool AllocFullPath(const char *path, char **pFullPath) { if (resultSize == 0 || resultSize > size) FATAL("Failed to get full path for '%s'\n", path); *pFullPath = fullPath; return true; -#elif __linux__ +#elif defined(__UTIL_LINUX) char *fullPath = realpath(path, NULL); if (fullPath == NULL) FATAL("Failed to get full path for '%s'\n", path); *pFullPath = fullPath; From ca5fbe5385ed82faf5436f58061c001ae54246b9 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 22 Feb 2024 18:23:26 +0100 Subject: [PATCH 2/9] Avoid name collision with `ReadFile` from Win32 --- tools/rom/build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rom/build.c b/tools/rom/build.c index 819f09cf..8e799710 100644 --- a/tools/rom/build.c +++ b/tools/rom/build.c @@ -211,7 +211,7 @@ bool LoadArm9Metadata(Arm9Metadata *pMetadata) { // 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) { +bool ReadFileAlloc(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); @@ -263,7 +263,7 @@ bool WriteArm9Program(FILE *fpRom, size_t *pAddress, uint32_t *pFileSize, uint32 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, From 7a5716c443e5613ece382f09c9dd01d9837a5d3f Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 22 Feb 2024 18:24:49 +0100 Subject: [PATCH 3/9] Use `ChangeDir` in `files.h` --- tools/rom/files.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rom/files.h b/tools/rom/files.h index 6284ed6e..176fc29f 100644 --- a/tools/rom/files.h +++ b/tools/rom/files.h @@ -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); From ea8b5522e74a497948563b561f6fcda45bd25ac5 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 22 Feb 2024 18:29:50 +0100 Subject: [PATCH 4/9] Use relative paths in `rom` tools --- Makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 6cd5cf82..7e608ee2 100644 --- a/Makefile +++ b/Makefile @@ -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 From f37bc0c47e23771d315f432b6ef8ee5361d705d2 Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 23 Feb 2024 19:44:22 +0100 Subject: [PATCH 5/9] Use Win32 file API in `rom` tools --- tools/{rom => include}/util.h | 118 +++++++++++++++- tools/rom/Makefile | 2 +- tools/rom/build.c | 253 ++++++++++++++++------------------ tools/rom/extract.c | 69 +++++----- 4 files changed, 272 insertions(+), 170 deletions(-) rename tools/{rom => include}/util.h (61%) diff --git a/tools/rom/util.h b/tools/include/util.h similarity index 61% rename from tools/rom/util.h rename to tools/include/util.h index a757278a..d1cc32fe 100644 --- a/tools/rom/util.h +++ b/tools/include/util.h @@ -6,7 +6,8 @@ #include #include -#define FATAL(...) do { fprintf(stderr, __VA_ARGS__); return false; } while (0) +#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) @@ -65,6 +66,121 @@ bool ChangeDir(const char *dir) { FATAL("Failed to enter directory '%s' from current directory '%s'\n", dir, cwd); } +typedef struct { + const char *name; +#ifdef __UTIL_WINDOWS + HFILE handle; +#elif defined(__UTIL_LINUX) + FILE *fp; +#endif +} File; + +bool FileOpenRead(const char *name, File *file) { +#ifdef __UTIL_WINDOWS + HFILE 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 + HFILE 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)) return true; + 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 | (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 + DWORD offsetHigh = 0; + DWORD offsetLow = SetFilePointer(file->handle, 0, &offsetHigh, FILE_CURRENT); + return offsetLow | (offsetHigh << 32); +#elif defined(__UTIL_LINUX) + return ftell(file->fp); +#endif +} + +void FileGoTo(const File *file, size_t offset) { +#ifdef __UTIL_WINDOWS + DWORD offsetHigh = offset >> 32; + DWORD offsetLow = offset & 0xffffffff; + SetFilePointer(file->handle, offsetHigh, &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); diff --git a/tools/rom/Makefile b/tools/rom/Makefile index 3cd63d49..201c69b0 100644 --- a/tools/rom/Makefile +++ b/tools/rom/Makefile @@ -1,5 +1,5 @@ CC := gcc -CFLAGS := -g -Wall +CFLAGS := -g -Wall -I../include ifneq ($(DEBUG),1) CFLAGS += -O2 -DNDEBUG diff --git a/tools/rom/build.c b/tools/rom/build.c index 8e799710..20eadb0b 100644 --- a/tools/rom/build.c +++ b/tools/rom/build.c @@ -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,10 +201,10 @@ 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; } @@ -212,11 +212,9 @@ bool LoadArm9Metadata(Arm9Metadata *pMetadata) { // If pFileSize != NULL, this function writes the file's size into *pFileSize. // The buffer can be freed with free(). bool ReadFileAlloc(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); + 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,41 +222,39 @@ bool ReadFileAlloc(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; @@ -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, @@ -371,10 +365,10 @@ bool WriteArm9OverlayFiles( 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; @@ -389,16 +383,16 @@ bool WriteArm9OverlayFiles( 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,16 +656,16 @@ 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 (!ChangeDir(name)) return false; - if (!TraverseAndAppendAssets(fpRom, &address, child, entries, child->firstFileId)) return false; + if (!TraverseAndAppendAssets(rom, &address, child, entries, child->firstFileId)) return false; if (!ChangeDir("..")) return false; } @@ -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,20 +684,18 @@ 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]; @@ -724,7 +712,7 @@ bool AppendAssets( uint16_t firstFileId = subTree->entry == NULL ? numOverlays : subTree->firstFileId; if (path[1] != '\0' && !ChangeDir(&path[1])) return false; - if (!TraverseAndAppendAssets(fpRom, &address, subTree, entries, firstFileId)) 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; @@ -889,8 +878,8 @@ int main(int argc, char **argv) { 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; } @@ -930,22 +919,22 @@ int main(int argc, char **argv) { // --------------------- Write ARM9 program --------------------- 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 (!ChangeDir(rootDir)) return 1; @@ -954,9 +943,9 @@ int main(int argc, char **argv) { // --------------------- 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 (!ChangeDir(ASSETS_SUBDIR)) return 1; @@ -966,17 +955,17 @@ 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)); @@ -985,20 +974,20 @@ int main(int argc, char **argv) { // --------------------- 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 (!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; @@ -1012,7 +1001,7 @@ int main(int argc, char **argv) { 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 --------------------- @@ -1021,11 +1010,11 @@ int main(int argc, char **argv) { 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 (!ChangeDir(rootDir)) return 1; free(readBuffer); - fclose(fpRom); + FileClose(&rom); } diff --git a/tools/rom/extract.c b/tools/rom/extract.c index c236b5f8..b677b76c 100644 --- a/tools/rom/extract.c +++ b/tools/rom/extract.c @@ -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 @@ -41,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; @@ -147,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; @@ -271,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; } @@ -343,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 --------------------- From 0851e8f9bbc53e346722433e6a37a54b6a285b2d Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 23 Feb 2024 19:48:14 +0100 Subject: [PATCH 6/9] Use `HANDLE` instead of `HFILE` --- tools/include/util.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/include/util.h b/tools/include/util.h index d1cc32fe..4c5b7d4a 100644 --- a/tools/include/util.h +++ b/tools/include/util.h @@ -69,7 +69,7 @@ bool ChangeDir(const char *dir) { typedef struct { const char *name; #ifdef __UTIL_WINDOWS - HFILE handle; + HANDLE handle; #elif defined(__UTIL_LINUX) FILE *fp; #endif @@ -77,7 +77,7 @@ typedef struct { bool FileOpenRead(const char *name, File *file) { #ifdef __UTIL_WINDOWS - HFILE handle = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + 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; @@ -96,7 +96,7 @@ bool FileOpenRead(const char *name, File *file) { bool FileOpenWrite(const char *name, File *file) { #ifdef __UTIL_WINDOWS - HFILE handle = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + 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; From 1281ac1340daec4293a79258dd70e05a8f4fe422 Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 23 Feb 2024 19:52:13 +0100 Subject: [PATCH 7/9] Fix type warnings --- tools/include/util.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/include/util.h b/tools/include/util.h index 4c5b7d4a..5c06427c 100644 --- a/tools/include/util.h +++ b/tools/include/util.h @@ -151,7 +151,7 @@ size_t FileSize(const File *file) { #ifdef __UTIL_WINDOWS DWORD sizeHigh; DWORD sizeLow = GetFileSize(file->handle, &sizeHigh); - return sizeLow | (sizeHigh << 32); + return sizeLow | ((size_t)(sizeHigh) << 32); #elif defined(__UTIL_LINUX) size_t pos = ftell(file->fp); fseek(file->fp, 0, SEEK_END); @@ -163,9 +163,9 @@ size_t FileSize(const File *file) { size_t FileOffset(const File *file) { #ifdef __UTIL_WINDOWS - DWORD offsetHigh = 0; + LONG offsetHigh = 0; DWORD offsetLow = SetFilePointer(file->handle, 0, &offsetHigh, FILE_CURRENT); - return offsetLow | (offsetHigh << 32); + return offsetLow | ((size_t)(offsetHigh) << 32); #elif defined(__UTIL_LINUX) return ftell(file->fp); #endif @@ -173,9 +173,9 @@ size_t FileOffset(const File *file) { void FileGoTo(const File *file, size_t offset) { #ifdef __UTIL_WINDOWS - DWORD offsetHigh = offset >> 32; - DWORD offsetLow = offset & 0xffffffff; - SetFilePointer(file->handle, offsetHigh, &offsetHigh, FILE_BEGIN); + 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 From 7c8cbdb6ea168645b23ef281c4ef1afc3c8845bf Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 23 Feb 2024 21:39:18 +0100 Subject: [PATCH 8/9] Fix `FileRead` on Win32 --- tools/include/util.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/include/util.h b/tools/include/util.h index 5c06427c..519eb2fd 100644 --- a/tools/include/util.h +++ b/tools/include/util.h @@ -128,8 +128,9 @@ void FileClose(File *file) { 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)) return true; - if (bytesRead > 0) return bytesRead / size; + 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; From 7e2c500671d2f16a81363efe70a6ed48906e879d Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 23 Feb 2024 21:40:06 +0100 Subject: [PATCH 9/9] Fix `#ifdef` platform detection in `files.h` --- tools/rom/files.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rom/files.h b/tools/rom/files.h index 176fc29f..3b5d8863 100644 --- a/tools/rom/files.h +++ b/tools/rom/files.h @@ -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) {