diff --git a/ndsfactory/fatstruct.h b/ndsfactory/fatstruct.h index 70eb1a8..0ba0de7 100644 --- a/ndsfactory/fatstruct.h +++ b/ndsfactory/fatstruct.h @@ -6,3 +6,8 @@ struct FatRange { uint32_t startAddr; uint32_t endAddr; }; + +struct FatFileID { + uint16_t id; + std::string path; +}; \ No newline at end of file diff --git a/ndsfactory/ndsfactory.cpp b/ndsfactory/ndsfactory.cpp index f80b7bd..c055481 100644 --- a/ndsfactory/ndsfactory.cpp +++ b/ndsfactory/ndsfactory.cpp @@ -30,12 +30,11 @@ NDSFactory::NDSFactory() bool NDSFactory::loadRomHeader(const std::string& romPath, std::vector& romHeader) { std::streampos headerSize = sizeof(NDSHeader); - std::ifstream romFile (romPath, std::ios::in|std::ios::binary|std::ios::ate); + std::ifstream romFile (romPath, std::ios::binary); if (romFile.is_open()) { romHeader.resize(static_cast(headerSize)); - romFile.seekg (0, std::ios::beg); romFile.read (romHeader.data(), headerSize); romFile.close(); @@ -46,8 +45,8 @@ bool NDSFactory::loadRomHeader(const std::string& romPath, std::vector& ro bool NDSFactory::dumpDataFromFile(const std::string& romPath, const std::string& savePath, uint32_t startAddr, uint32_t size) { - std::ifstream romFile (romPath, std::ios::in|std::ios::binary|std::ios::ate); - std::ofstream savedFile (savePath, std::ios::out|std::ios::binary|std::ios::ate); + std::ifstream romFile (romPath, std::ios::binary); + std::ofstream savedFile (savePath, std::ios::binary); if (romFile.is_open() && savedFile.is_open()) { std::vector dumpBuffer(size); @@ -77,7 +76,7 @@ bool NDSFactory::logToFile(const std::string& logPath, const std::string& log) bool NDSFactory::readBytesFromFile(std::vector& byteBuffer, const std::string& romPath, uint32_t startAddr, uint32_t size) { - std::ifstream romFile (romPath, std::ios::in|std::ios::binary|std::ios::ate); + std::ifstream romFile (romPath, std::ios::binary); if (romFile.is_open()) { romFile.seekg (startAddr, std::ios::beg); @@ -90,34 +89,36 @@ bool NDSFactory::readBytesFromFile(std::vector& byteBuffer, const std::str bool NDSFactory::writeSectionToFile(const std::string& sectionPath, const std::string& savePath, uint32_t startAddr, uint32_t size) { - std::ifstream sectionFile (sectionPath, std::ios::in|std::ios::binary|std::ios::ate); - std::ofstream savedFile (savePath, std::ios::out|std::ios::binary|std::ios::app); - if (sectionFile.is_open() && savedFile.is_open()) + std::ifstream sectionFile (sectionPath, std::ios::binary); + if (sectionFile.is_open()) { std::vector dumpBuffer(size); - sectionFile.seekg (0, std::ios::beg); sectionFile.read (dumpBuffer.data(), size); sectionFile.close(); - - savedFile.seekp(startAddr, std::ios::beg); - savedFile.write(dumpBuffer.data(), size); - savedFile.close(); - return true; + return writeBytesToFile(dumpBuffer, savePath, startAddr, size); } return false; } bool NDSFactory::writeBytesToFile(std::vector& byteBuffer, const std::string& savePath, uint32_t startAddr, uint32_t size) { - std::ofstream savedFile (savePath, std::ios::out|std::ios::binary|std::ios::app); - if (savedFile.is_open()) + std::ofstream savedFile (savePath, std::ios::in | std::ios::out | std::ios::binary); + if (!savedFile.is_open()) { - savedFile.seekp(startAddr, std::ios::beg); - savedFile.write(byteBuffer.data(), size); + savedFile.open(savePath, std::ios::out | std::ios::binary); + if (!savedFile.is_open()) { + return false; + } savedFile.close(); - return true; + savedFile.open(savePath, std::ios::in | std::ios::out | std::ios::binary); + if (!savedFile.is_open()) + return false; } - return false; + + savedFile.seekp(startAddr); + savedFile.write(byteBuffer.data(), size); + savedFile.close(); + return true; } @@ -134,7 +135,7 @@ int NDSFactory::getCardSizeInBytes(int cardType) bool NDSFactory::checkArm9FooterPresence(const std::string& sectionPath, uint32_t size) { - std::ifstream sectionFile (sectionPath, std::ios::in|std::ios::binary|std::ios::ate); + std::ifstream sectionFile (sectionPath, std::ios::binary | std::ios::ate); if (sectionFile.is_open()) { std::streamoff sectionRealSize = sectionFile.tellg(); @@ -307,9 +308,96 @@ bool NDSFactory::patchFat(const std::string& fatSectionPath, uint32_t shiftSize, pfatrange->endAddr += shiftSize; } + std::remove(savePath.c_str()); + return writeBytesToFile(fatBytes, savePath, 0, static_cast(sectionSize)); } +bool NDSFactory::buildFatData(const std::string& fatDataDirPath, const std::string& originalFatPath, uint32_t fatDataAddr, const std::string& savePath) +{ + std::vector fatDataBytes; + std::vector fat; + std::vector fntBytes; + + std::vector fileIDs; + std::ifstream fileIDsFile(fatDataDirPath + "/_file_IDs.txt", std::ios::in | std::ios::beg); + if (!fileIDsFile.is_open()) return false; + std::string fileIDsLine; + while (std::getline(fileIDsFile, fileIDsLine)) + { + FatFileID fatFileID; + fatFileID.id = static_cast(std::stoi(fileIDsLine.substr(0, fileIDsLine.find(":::")), nullptr, 16)); + fatFileID.path = fileIDsLine.substr(fileIDsLine.find(":::") + 3); + fileIDs.push_back(fatFileID); + } + fileIDsFile.close(); + + // This loop is needed to detect if roms has overlay files + // and if so, we need to extract their addresses from the original fat.bin + // it is a hacky but it works + for (FatFileID fatFileID : fileIDs) + { + std::string currentFile = fatDataDirPath + "/" + fatFileID.path; + if (std::filesystem::is_directory(std::filesystem::path(currentFile))) continue; + + int firstFileId = fatFileID.id; + if (firstFileId > 0) + { + std::ifstream originalFatFile(originalFatPath, std::ios::in | std::ios::binary | std::ios::beg); + if (!originalFatFile.is_open()) return false; + std::vector ovrBytes; + uint32_t requiredBytes = firstFileId * sizeof(FatRange); + ovrBytes.resize(requiredBytes); + originalFatFile.read(ovrBytes.data(), requiredBytes); + originalFatFile.close(); + + FatRange* pfatrange = reinterpret_cast(ovrBytes.data()); + for (size_t i = 0; i < ovrBytes.size(); i += sizeof(FatRange), pfatrange++) + fat.push_back({ pfatrange->startAddr, pfatrange->endAddr }); + } + break; + } + + for (FatFileID fatFileID : fileIDs) + { + std::string currentFile = fatDataDirPath + "/" + fatFileID.path; + + if (std::filesystem::is_directory(std::filesystem::path(currentFile))) continue; + + FatRange fatRange; + fatRange.startAddr = fatDataAddr + static_cast(fatDataBytes.size()); + + std::ifstream currentFatDataFile(currentFile, std::ios::in | std::ios::binary | std::ios::ate); + if (!currentFatDataFile.is_open()) return false; + std::streamsize size = currentFatDataFile.tellg(); + currentFatDataFile.seekg(0, std::ios::beg); + + std::vector buffer(size); + if (currentFatDataFile.read(buffer.data(), size)) + fatDataBytes.insert(fatDataBytes.end(), buffer.begin(), buffer.end()); + else + { + currentFatDataFile.close(); + return false; + } + currentFatDataFile.close(); + + fatRange.endAddr = fatDataAddr + static_cast(fatDataBytes.size()); + fat.push_back(fatRange); + } + + + const char* fat_ptr = reinterpret_cast(fat.data()); + std::vector fatBytes(fat_ptr, fat_ptr + fat.size() * sizeof(FatRange)); + std::remove((savePath + "/fat_data.bin").c_str()); + std::remove((savePath + "/fat.bin").c_str()); + bool res = true; + res &= writeBytesToFile(fatDataBytes, savePath + "/fat_data.bin", 0, static_cast(fatDataBytes.size())); + res &= writeBytesToFile(fatBytes, savePath + "/fat.bin", 0, static_cast(fatBytes.size())); + return res; + +} + uint16_t NDSFactory::calcHeaderCrc16(const std::vector& romHeader) { uint8_t loc; diff --git a/ndsfactory/ndsfactory.h b/ndsfactory/ndsfactory.h index 540ffd4..702e84f 100644 --- a/ndsfactory/ndsfactory.h +++ b/ndsfactory/ndsfactory.h @@ -24,6 +24,7 @@ public: bool extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath, bool logFileIDs); bool patchFat(const std::string& sectionPath, uint32_t shiftSize, const std::string& savePath); + bool buildFatData(const std::string& fatDataDirPath, const std::string& originalFatPath, uint32_t fatDataAddr, const std::string& savePath); uint16_t calcHeaderCrc16(const std::vector& romHeader); private: diff --git a/ui/dialogs/about/revision.h b/ui/dialogs/about/revision.h index 62dc2ff..e701c03 100644 --- a/ui/dialogs/about/revision.h +++ b/ui/dialogs/about/revision.h @@ -1,6 +1,6 @@ #ifndef REVISION_H #define REVISION_H -#define GIT_COMMIT_HASH "e2e53cb" +#define GIT_COMMIT_HASH "f46e527" #endif diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 736c96c..21b44f2 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -58,6 +58,9 @@ private slots: void on_fatExtractorLoadFatBtn_clicked(); void on_fatExtractorLoadFntBtn_clicked(); void on_fatExtractorExtractBtn_clicked(); + void on_fatBuilderOpenFatDataDirBtn_clicked(); + void on_fatBuilderLoadOriginalFatBtn_clicked(); + void on_fatBuilderBuildBtn_clicked(); private: Ui::MainWindow *ui; @@ -109,4 +112,5 @@ private: bool extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath, bool logFileIDs); bool patchFat(const std::string& loadPath, uint32_t shiftSize, const std::string& savePath); + bool buildFatData(const std::string& fatDataDirPath, const std::string& originalFatPath, uint32_t fatDataAddr, const std::string& savePath); }; diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 2c51789..fb0a4cc 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -702,7 +702,7 @@ - Save file IDs to _file_IDs.txt (required for games that access files by their ID) + Save file IDs to _file_IDs.txt (required for rebuilding FAT) @@ -736,6 +736,83 @@ + + + + + + FAT Builder + + + + + + + + Original fat.bin is required ONLY if the ROM has an overlay... + + + + + + + + + + + + + + Open FAT Data Directory + + + + + + + + + + + + + + Load Original fat.bin + + + + + + + + + + + Fat Files (fat_data.bin) Addr: + + + + + + + + + + + + + + Build! + + + + + + + + + + diff --git a/ui/tabs/fattools/fattoolstabfunctions.cpp b/ui/tabs/fattools/fattoolstabfunctions.cpp index 160065c..a57a492 100644 --- a/ui/tabs/fattools/fattoolstabfunctions.cpp +++ b/ui/tabs/fattools/fattoolstabfunctions.cpp @@ -1,3 +1,4 @@ +#include #include "./../../mainwindow.h" @@ -11,3 +12,9 @@ bool MainWindow::patchFat(const std::string& loadPath, uint32_t shiftSize, const { return ndsFactory.patchFat(loadPath, shiftSize, savePath); } + +bool MainWindow::buildFatData(const std::string& fatDataDirPath, const std::string& originalFatPath, uint32_t fatDataAddr, const std::string& savePath) +{ + if (!std::filesystem::exists(fatDataDirPath + "/_file_IDs.txt")) return false; + return ndsFactory.buildFatData(fatDataDirPath, originalFatPath, fatDataAddr, savePath); +} diff --git a/ui/tabs/fattools/fattoolstabsignals.cpp b/ui/tabs/fattools/fattoolstabsignals.cpp index aa42101..6ca5c78 100644 --- a/ui/tabs/fattools/fattoolstabsignals.cpp +++ b/ui/tabs/fattools/fattoolstabsignals.cpp @@ -106,3 +106,45 @@ void MainWindow::on_fatPatcherPatchFatBtn_clicked() ui->fatPatcherPatchFatBtn->setEnabled(true); } + +void MainWindow::on_fatBuilderOpenFatDataDirBtn_clicked() +{ + QString fatDirPat = QFileDialog::getExistingDirectory( + this, tr("Select Directory"), + "", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (!fatDirPat.isNull()) + ui->fatBuilderFatDirPathEdt->setText(fatDirPat.toUtf8()); +} + +void MainWindow::on_fatBuilderLoadOriginalFatBtn_clicked() +{ + QString fatPath = QFileDialog::getOpenFileName( + Q_NULLPTR, + "NDS Fat", + "", + "NDS Fat (*.bin)"); + + if (!fatPath.isNull()) + ui->fatBuilderOriginalFatPathEdt->setText(fatPath.toUtf8()); +} + +void MainWindow::on_fatBuilderBuildBtn_clicked() +{ + ui->fatBuilderOpenFatDataDirBtn->setEnabled(false); + + QString dirPath = QFileDialog::getExistingDirectory( + this, tr("Select Directory"), + "", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (!dirPath.isNull()) + buildFatData(ui->fatBuilderFatDirPathEdt->text().toStdString(), + ui->fatBuilderOriginalFatPathEdt->text().toStdString(), + ui->fatBuilderFatAddrEdt->text().toUInt(nullptr, 16), + dirPath.toStdString()) ? QMessageBox::information(this, tr("NDSFactory"), tr("fat_data.bin and fat.bin correctly built!")) + : QMessageBox::critical(this, tr("NDSFactory"), tr("Error building FAT!")); + + ui->fatBuilderOpenFatDataDirBtn->setEnabled(true); +} \ No newline at end of file diff --git a/ui/tabs/packer/packertabfunctions.cpp b/ui/tabs/packer/packertabfunctions.cpp index 9ff9202..e1f08e0 100644 --- a/ui/tabs/packer/packertabfunctions.cpp +++ b/ui/tabs/packer/packertabfunctions.cpp @@ -148,7 +148,10 @@ bool MainWindow::writeArm9BinPadding(char paddingType, const std::string& savePa size = extractPackerHeaderTableData(NDSHeaderNames::ARM7RomAddress).toUInt(nullptr, 16) - startAddr; if (isFooterPresent) + { + startAddr += Arm9FooterSize; size -= Arm9FooterSize; + } return ndsFactory.writePaddingToFile( paddingType,