From 921eef86e2d37b8b70d4300363a74fef9e64e44d Mon Sep 17 00:00:00 2001 From: Luca D'Amico Date: Sat, 10 Aug 2024 23:20:12 +0200 Subject: [PATCH] FAT Data Extractor moved to FAT Tools Tab --- README.md | 6 +- ndsfactory/ndsfactory.cpp | 146 +++++++++++++++++++- ndsfactory/ndsfactory.h | 3 +- ui/dialogs/about/revision.h | 2 +- ui/mainwindow.h | 15 +- ui/mainwindow.ui | 136 ++++++++++++++++-- ui/tabs/fattools/fattoolstabfunctions.cpp | 6 + ui/tabs/fattools/fattoolstabsignals.cpp | 58 ++++++++ ui/tabs/unpacker/unpackertabfunctions.cpp | 159 ---------------------- ui/tabs/unpacker/unpackertabsignals.cpp | 11 -- 10 files changed, 346 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index 464c632..72cedb9 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ This tool is particularly useful for modding games or writing trainers. # How to use ## Unpacker Tab -In the Unpacker Tab, you can load your Nintendo DS software (.nds) and extract the ROM sections, including individual FAT files. Take note of the original address of the FAT files, as you will need this value if you alter the addresses and sizes of the sections. +In the Unpacker Tab, you can load your Nintendo DS software (.nds) and extract the ROM sections. Take note of the original address of the FAT files, as you will need this value if you alter the addresses and sizes of the sections or if you want to extract its contents. **You can then do what you want with these sections (inject code, apply patches etc.)** @@ -42,7 +42,9 @@ In the Unpacker Tab, you can load your Nintendo DS software (.nds) and extract t In the Packer Tab, you can recreate a .nds file using your edited sections. If your sections are larger than the originals, you must update their addresses and sizes in the header. Ensure that the addresses do not overlap, or the final ROM will be broken. If you repack edited sections and the FAT files' address is different from the original, you must patch the FAT (fat.bin). The FAT contains absolute addresses representing each file's start and end addresses, so you need to update them accordingly (use the FAT Patching Tab for this). ## Fat Tools Tab -In this tab, you can patch the FAT section (fat.bin). This is only necessary if the FAT files' final address (fat_data.bin) differs from the original. Patching the FAT is straightforward: load your fat.bin, and fill in the original and new addresses of fat_data.bin. This will produce a patched fat.bin for use in the packing process. +In this tab, you can: +* extract the FAT files from fat_data.bin. +* patch the FAT section (fat.bin): this is only necessary if the FAT files' final address (fat_data.bin) differs from the original. Patching the FAT is straightforward: load your fat.bin, and fill in the original and new addresses of fat_data.bin. This will produce a patched fat.bin for use in the packing process. # Known Limitations/Possible Future Features/Bugs diff --git a/ndsfactory/ndsfactory.cpp b/ndsfactory/ndsfactory.cpp index 4ed9c86..078e4d4 100644 --- a/ndsfactory/ndsfactory.cpp +++ b/ndsfactory/ndsfactory.cpp @@ -2,11 +2,25 @@ #include #include #include +#include #include "ndsfactory.h" #include "fatstruct.h" #include "crctable.h" +// Byte offsets for interpreting memory +#define SECOND_BYTE_SHIFT 8 +#define THIRD_BYTE_SHIFT 16 +#define FOURTH_BYTE_SHIFT 24 + +// Magic values for FAT extraction +#define CONTROL_BYTE_LENGTH_MASK 0x7F +#define CONTROL_BYTE_DIR_MASK 0x80 +#define DUMMY_CONTROL_VALUE 0xFF +#define FNT_HEADER_OFFSET_MASK 0XFFF +#define ROOT_DIRECTORY_ADDRESS 0xF000 + + NDSFactory::NDSFactory() { @@ -80,12 +94,6 @@ bool NDSFactory::writeSectionToFile(const std::string& sectionPath, const std::s return false; } -bool NDSFactory::writeFatSectionToFile(const std::string& romPath, FatRange* pfatrange, const std::string& savePath){ - uint32_t size=pfatrange->endAddr-pfatrange->startAddr; - if(!dumpDataFromFile(romPath, savePath, pfatrange->startAddr, size)) return false; - return true; -} - 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); @@ -126,6 +134,131 @@ bool NDSFactory::checkArm9FooterPresence(const std::string& sectionPath, uint32_ return false; } +bool NDSFactory::extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, + const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath) +{ + std::vector fatDataBytes; + std::vector fatBytes; + std::vector fntBytes; + + std::ifstream fatDataSectionFile(fatDataSectionPath, std::ios::in | std::ios::binary | std::ios::ate); + if (!fatDataSectionFile.is_open()) return false; + std::streamoff fatDataSectionSize = fatDataSectionFile.tellg(); + fatDataBytes.resize(fatDataSectionSize); + fatDataSectionFile.seekg(0, std::ios::beg); + fatDataSectionFile.read(fatDataBytes.data(), fatDataSectionSize); + fatDataSectionFile.close(); + + std::ifstream fatSectionFile(fatSectionPath, std::ios::in | std::ios::binary | std::ios::ate); + if (!fatSectionFile.is_open()) return false; + std::streamoff fatSectionSize = fatSectionFile.tellg(); + fatBytes.resize(fatSectionSize); + fatSectionFile.seekg(0, std::ios::beg); + fatSectionFile.read(fatBytes.data(), fatSectionSize); + fatSectionFile.close(); + + std::ifstream fntSectionFile(fntSectionPath, std::ios::in | std::ios::binary | std::ios::ate); + if (!fntSectionFile.is_open()) return false; + std::streamoff fntSectionSize = fntSectionFile.tellg(); + fntBytes.resize(fntSectionSize); + fntSectionFile.seekg(0, std::ios::beg); + fntSectionFile.read(fntBytes.data(), fntSectionSize); + fntSectionFile.close(); + + FatRange* pfatrange = reinterpret_cast(fatBytes.data()); + + // This lambda function was written by NyuBlara, all credits to him. + // I edited it a bit to make it work with the rest of the updated code. + auto parseFolder = [this, fntBytes, pfatrange, fatDataSectionPath, originalFatDataAddr](uint32_t folderId, std::string curPath, auto& parseFolder) { + + uint32_t currentOffset = 8 * (folderId & FNT_HEADER_OFFSET_MASK); // offset for the current directory's info in the FNT header + // Only the lower 12 bit of the given offset are relevant + + // --------------------------------------------------------------------- + // About how the FAT and FNT work : + // The FNT has two sections : + // a "header" where every entry contains : + //// - a 4-byte address where the corresponding directory's data starts in the body + //// - a 2-byte offset that is the index of the first file of the directory in the FAT + //// (e.g. : if the offset is 42, the first file in the directory is situated at the ROM addresses stored in the 42nd FAT entry) + //// (and its second will be 43, etc.) + // a "body" where every entry contains : + //// - a length+status/control byte : lower 7 bits (control byte & 0x7F) are a length, highest bit (control byte & 0x80) is set if entry is a directory, and not set if it's a file + //// - a name which length is the length portion of the previous control byte (e.g. : if the control byte was 0x83, the name is three bytes long) + //// - if the entry is a directory, a 2-byte address (where only the lower 12 bit are relevant for some reason) at which this directory's info is located in the FNT header + // Thus, the FNT reading operation will consist in bouncing back and forth between body and header every time we must process a subdirectory + // Thank Heavens for random-access containers ! + // --------------------------------------------------------------------- + + // Get the 4-byte address for the folder data + + uint32_t fntBodyOffset = + (uint32_t)((unsigned char)fntBytes[currentOffset + 3] << (uint32_t)FOURTH_BYTE_SHIFT | + (unsigned char)fntBytes[currentOffset + 2] << (uint32_t)THIRD_BYTE_SHIFT | + (unsigned char)fntBytes[currentOffset + 1] << (uint32_t)SECOND_BYTE_SHIFT | + (unsigned char)fntBytes[currentOffset]); + currentOffset += 4; + + // Get the 2-byte offset for the folder's first file in the FAT + uint16_t fatOffset = + (uint16_t)((unsigned char)fntBytes[currentOffset + 1] << SECOND_BYTE_SHIFT | + (unsigned char)fntBytes[currentOffset]); + + // Jump to FNT body a specified address + currentOffset = fntBodyOffset; + + uint8_t controlByte = DUMMY_CONTROL_VALUE; + + while (true) + { + controlByte = fntBytes[currentOffset]; // Entry's control byte + if (controlByte == 0) break; // A control byte of 0 terminates the directory's contents + currentOffset++; + + uint8_t nameLength = controlByte & CONTROL_BYTE_LENGTH_MASK; // length of entry name + bool isDir = controlByte & CONTROL_BYTE_DIR_MASK; // set if entry is a directory + + // Reconstitute name from bytes + // Btw I wish I could use the actual byte type but I have to comply with the software's choice of using char + std::vector nameString; + for (size_t i = 0; i < nameLength; i++) nameString.push_back(fntBytes[currentOffset++]); + std::string name(&nameString[0], (size_t)nameLength); + + std::string newPath = curPath + '/' + name; + + if (isDir) + { + // Get the 2-byte address for this folder's info in the FNT header + uint16_t subFolderId = ((unsigned char)fntBytes[currentOffset + 1] << SECOND_BYTE_SHIFT | + (unsigned char)fntBytes[currentOffset]); + currentOffset += 2; + + if (!std::filesystem::exists(newPath)) + if (!std::filesystem::create_directory(newPath)) return false; + + // Jump back to the FNT header and repeat the process for subdirectory ! + if (!parseFolder(subFolderId, newPath, parseFolder)) return false; + } + else + { + // Remember we have the offset for the directory's first file in the FAT. + // From then, every file is just the next entry. + // So we just have to use that offset and increment it every time. + + uint32_t fileStartAddr = (pfatrange + fatOffset)->startAddr - originalFatDataAddr; + uint32_t fileSize = (pfatrange + fatOffset)->endAddr - (pfatrange + fatOffset)->startAddr; + if (!dumpDataFromFile(fatDataSectionPath, newPath, fileStartAddr, fileSize)) return false; + + fatOffset++; + } + } + + return true; + }; + + return parseFolder(ROOT_DIRECTORY_ADDRESS, savePath, parseFolder); +} + bool NDSFactory::patchFat(const std::string& fatSectionPath, uint32_t shiftSize, const std::string& savePath) { std::vector fatBytes; @@ -142,7 +275,6 @@ bool NDSFactory::patchFat(const std::string& fatSectionPath, uint32_t shiftSize, sectionFile.read (fatBytes.data(), sectionSize); sectionFile.close(); - FatRange* pfatrange = reinterpret_cast(fatBytes.data()); for(size_t i = 0; i < fatBytes.size(); i += sizeof(FatRange), pfatrange++) { diff --git a/ndsfactory/ndsfactory.h b/ndsfactory/ndsfactory.h index d3810a9..f388951 100644 --- a/ndsfactory/ndsfactory.h +++ b/ndsfactory/ndsfactory.h @@ -16,11 +16,12 @@ public: bool dumpDataFromFile(const std::string& romPath, const std::string& savePath, uint32_t startAddr, uint32_t size); bool readBytesFromFile(std::vector& byteBuffer, const std::string& romPath, uint32_t startAddr, uint32_t size); bool writeSectionToFile(const std::string& sectionPath, const std::string& savePath, uint32_t startAddr, uint32_t size); - bool writeFatSectionToFile(const std::string& romPath, FatRange* pfatrange, const std::string& savePath); bool writeBytesToFile(std::vector& byteBuffer, const std::string& savePath, uint32_t startAddr, uint32_t size); bool writePaddingToFile(char paddingChar, const std::string& savePath, uint32_t startAddr, uint32_t size); int getCardSizeInBytes(int cardType); bool checkArm9FooterPresence(const std::string& sectionPath, uint32_t size); + bool extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, + const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath); bool patchFat(const std::string& sectionPath, uint32_t shiftSize, const std::string& savePath); uint16_t calcHeaderCrc16(const std::vector& romHeader); diff --git a/ui/dialogs/about/revision.h b/ui/dialogs/about/revision.h index 5172a59..4888de2 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 "aa2ed50" +#define GIT_COMMIT_HASH "d69e4e1" #endif diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 8eb5fe1..6bf915e 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -31,8 +31,6 @@ private slots: void on_unpackerDumpArm9OverlayFilesBtn_clicked(); void on_unpackerDumpArm7OverlayFilesBtn_clicked(); void on_unpackerDumpEverythingBtn_clicked(); - void on_unpackerDecodeFatFilesBtn_clicked(); - void notifyExtractionResult(bool result); void on_actionDark_triggered(); void on_actionLight_triggered(); @@ -51,13 +49,15 @@ private slots: void on_packerLoadArm7OverlayFilesBtn_clicked(); void on_packerLoadIconTitleBtn_clicked(); void on_packerLoadFatFilesBtn_clicked(); + void on_packerCalcHeaderCrcBtn_clicked(); void on_packerBuildNDSRomBtn_clicked(); void on_fatPatcherLoadFatBtn_clicked(); - void on_fatPatcherPatchFatBtn_clicked(); - - void on_packerCalcHeaderCrcBtn_clicked(); + void on_fatExtractorLoadFatDataBtn_clicked(); + void on_fatExtractorLoadFatBtn_clicked(); + void on_fatExtractorLoadFntBtn_clicked(); + void on_fatExtractorExtractBtn_clicked(); private: Ui::MainWindow *ui; @@ -78,6 +78,7 @@ private: bool dumpIconTitle(const std::string& dirPath); bool dumpFatFiles(const std::string& dirPath); bool dumpEverything(QString dirPath); + void notifyExtractionResult(bool result); void populatePackerSectionHeader(NDSHeader *ndsHeader); void enableCalcCrcButton(); @@ -111,7 +112,7 @@ private: //QString extractUnpackerHeaderTableData(int index); QString extractPackerHeaderTableData(int index); - bool decodeFatFiles(QString dirPath); - + bool extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, + const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath); bool patchFat(const std::string& loadPath, uint32_t shiftSize, const std::string& savePath); }; diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index e3fab5a..42bcf2e 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -274,13 +274,6 @@ - - - - Decode FAT Contents - - - @@ -601,11 +594,135 @@ - 4 + 6 QLayout::SizeConstraint::SetDefaultConstraint + + + + FAT Data Extractor + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Load FAT Data(fat_data.bin) + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Load FNT(fnt.bin) + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Load FAT(fat.bin) + + + + + + + + + + + Original Fat Files (fat_data.bin) Addr: + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Extract FAT Data! + + + + + + + + @@ -734,6 +851,9 @@ Qt::Orientation::Vertical + + QSizePolicy::Policy::Expanding + 20 diff --git a/ui/tabs/fattools/fattoolstabfunctions.cpp b/ui/tabs/fattools/fattoolstabfunctions.cpp index cbb4383..61570ce 100644 --- a/ui/tabs/fattools/fattoolstabfunctions.cpp +++ b/ui/tabs/fattools/fattoolstabfunctions.cpp @@ -1,6 +1,12 @@ #include "./../../mainwindow.h" +bool MainWindow::extractFatData(const std::string& fatDataSectionPath, const std::string& fatSectionPath, + const std::string& fntSectionPath, uint32_t originalFatDataAddr, const std::string& savePath) +{ + return ndsFactory.extractFatData(fatDataSectionPath, fatSectionPath, fntSectionPath, originalFatDataAddr, savePath); +} + bool MainWindow::patchFat(const std::string& loadPath, uint32_t shiftSize, const std::string& savePath) { return ndsFactory.patchFat(loadPath, shiftSize, savePath); diff --git a/ui/tabs/fattools/fattoolstabsignals.cpp b/ui/tabs/fattools/fattoolstabsignals.cpp index 9fd706d..6caff20 100644 --- a/ui/tabs/fattools/fattoolstabsignals.cpp +++ b/ui/tabs/fattools/fattoolstabsignals.cpp @@ -3,6 +3,64 @@ #include "./../../mainwindow.h" #include "./../../ui_mainwindow.h" +void MainWindow::on_fatExtractorLoadFatDataBtn_clicked() +{ + QString FatDataPath = QFileDialog::getOpenFileName( + Q_NULLPTR, + "NDS Fat Data", + QDir::currentPath(), + "NDS FAT_DATA (*.bin)"); + + if (!FatDataPath.isNull()) + { + ui->fatExtractorFatDataPathEdt->setText(FatDataPath.toUtf8()); + } +} + +void MainWindow::on_fatExtractorLoadFatBtn_clicked() +{ + QString FatPath = QFileDialog::getOpenFileName( + Q_NULLPTR, + "NDS Fat", + QDir::currentPath(), + "NDS FAT (*.bin)"); + + if (!FatPath.isNull()) + { + ui->fatExtractorFatPathEdt->setText(FatPath.toUtf8()); + } +} + +void MainWindow::on_fatExtractorLoadFntBtn_clicked() +{ + QString FntPath = QFileDialog::getOpenFileName( + Q_NULLPTR, + "NDS Fnt", + QDir::currentPath(), + "NDS FNT (*.bin)"); + + if (!FntPath.isNull()) + { + ui->fatExtractorFntPathEdt->setText(FntPath.toUtf8()); + } +} + +void MainWindow::on_fatExtractorExtractBtn_clicked() +{ + QString dirPath = QFileDialog::getExistingDirectory( + this, tr("Select Directory"), + "", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (!dirPath.isNull()) + extractFatData(ui->fatExtractorFatDataPathEdt->text().toStdString(), + ui->fatExtractorFatPathEdt->text().toStdString(), + ui->fatExtractorFntPathEdt->text().toStdString(), + ui->fatExtractorOriginalFatFilesAddrEdt->text().toUInt(nullptr, 16), + dirPath.toStdString()) ? QMessageBox::information(this, tr("NDS Factory"), tr("FAT files extraction completed!")) + : QMessageBox::critical(this, tr("NDS Factory"), tr("Error extracting FAT files!")); + +} void MainWindow::on_fatPatcherLoadFatBtn_clicked() { diff --git a/ui/tabs/unpacker/unpackertabfunctions.cpp b/ui/tabs/unpacker/unpackertabfunctions.cpp index cacef12..b16c85e 100644 --- a/ui/tabs/unpacker/unpackertabfunctions.cpp +++ b/ui/tabs/unpacker/unpackertabfunctions.cpp @@ -8,18 +8,6 @@ #include "../../models/ndsheadermodel.h" #include "../../../ndsfactory/fatstruct.h" -// Byte offsets for interpreting memory -#define SECOND_BYTE_SHIFT 8 -#define THIRD_BYTE_SHIFT 16 -#define FOURTH_BYTE_SHIFT 24 - -// Magic values for FAT extraction -#define CONTROL_BYTE_LENGTH_MASK 0x7F -#define CONTROL_BYTE_DIR_MASK 0x80 -#define DUMMY_CONTROL_VALUE 0xFF -#define FNT_HEADER_OFFSET_MASK 0XFFF -#define ROOT_DIRECTORY_ADDRESS 0xF000 - // Size constants #define ICON_TITLE_SIZE 0xA00 @@ -169,150 +157,3 @@ bool MainWindow::dumpEverything(QString dirPath) result &= dumpFatFiles(QDir::toNativeSeparators(dirPath+"/fat_data.bin").toStdString()); return result; } - -bool MainWindow::decodeFatFiles(QString dirPath) -{ - // Prepare necessary info from ROM - - std::string romPath = ui->loadedRomPath->text().toStdString(); // ROM itself - - // Addresses of the file allocation table and file name table - - uint32_t fatAddr = ui->unpackerHeaderDataTable->model()->index(NDSHeaderNames::FATAddress, 1).data().toString().toUInt(nullptr,16); - uint32_t fatSize = ui->unpackerHeaderDataTable->model()->index(NDSHeaderNames::FATSize, 1).data().toString().toUInt(nullptr,16); - - // Sizes of these tables - - uint32_t fntAddr = ui->unpackerHeaderDataTable->model()->index(NDSHeaderNames::FilenameTableAddress, 1).data().toString().toUInt(nullptr,16); - uint32_t fntSize = ui->unpackerHeaderDataTable->model()->index(NDSHeaderNames::FilenameTableSize, 1).data().toString().toUInt(nullptr,16); - - // Buffers to receive the contents of the FAT and FNT - - std::vector fatBytes(static_cast(fatSize)); - std::vector fntBytes(static_cast(fntSize)); - - // Fill them - - if(!ndsFactory.readBytesFromFile(fatBytes, romPath, fatAddr, fatSize)) return false; - if(!ndsFactory.readBytesFromFile(fntBytes, romPath, fntAddr, fntSize)) return false; - - // Use the available FAT range struct and read the FAT bytes as such - - FatRange* pfatrange = reinterpret_cast(fatBytes.data()); - - // Recursive function that looks up FNT info to find file names and directory structures, - // And writes the ROM data in the ranges indicated by the FAT simultaneously. - - auto parseFolder = [this, fntBytes, pfatrange, romPath](uint32_t folderId, std::string curPath, auto& parseFolder){ - - if(false) return false; // this is stupid, but it's C++ - // If we take it out, the compiler will complain because it doesn't known the return type of the lambda - // But we can't make the lambda bool either...so this is the best option... - - QDir curDir(QString::fromStdString(curPath)); // useful a bit later - - uint32_t currentOffset = 8 * (folderId & FNT_HEADER_OFFSET_MASK); // offset for the current directory's info in the FNT header - // Only the lower 12 bit of the given offset are relevant - - // --------------------------------------------------------------------- - // About how the FAT and FNT work : - - // The FNT has two sections : - // a "header" where every entry contains : - // - a 4-byte address where the corresponding directory's data starts in the body - // - a 2-byte offset that is the index of the first file of the directory in the FAT - // (e.g. : if the offset is 42, the first file in the directory is situated at the ROM addresses stored in the 42nd FAT entry) - // (and its second will be 43, etc.) - // a "body" where every entry contains : - // - a length+status/control byte : lower 7 bits (control byte & 0x7F) are a length, highest bit (control byte & 0x80) is set if entry is a directory, and not set if it's a file - // - a name which length is the length portion of the previous control byte (e.g. : if the control byte was 0x83, the name is three bytes long) - // - if the entry is a directory, a 2-byte address (where only the lower 12 bit are relevant for some reason) at which this directory's info is located in the FNT header - - // Thus, the FNT reading operation will consist in bouncing back and forth between body and header every time we must process a subdirectory - // Thank Heavens for random-access containers ! - - // --------------------------------------------------------------------- - - // Get the 4-byte address for the folder data - - uint32_t fntBodyOffset = - (uint32_t)((unsigned char) fntBytes[currentOffset+3] << (uint32_t) FOURTH_BYTE_SHIFT | - (unsigned char) fntBytes[currentOffset+2] << (uint32_t) THIRD_BYTE_SHIFT | - (unsigned char) fntBytes[currentOffset + 1] << (uint32_t) SECOND_BYTE_SHIFT | - (unsigned char) fntBytes[currentOffset]); - currentOffset+=4; - - // Get the 2-byte offset for the folder's first file in the FAT - - uint16_t fatOffset = - (uint16_t)((unsigned char) fntBytes[currentOffset+1] << SECOND_BYTE_SHIFT | - (unsigned char) fntBytes[currentOffset]); - - // Jump to FNT body a specified address - - currentOffset = fntBodyOffset; - - uint8_t controlByte = DUMMY_CONTROL_VALUE; - - while(true){ - - controlByte = fntBytes[currentOffset]; // Entry's control byte - if(controlByte==0) break; // A control byte of 0 terminates the directory's contents - currentOffset++; - - uint8_t nameLength = controlByte & CONTROL_BYTE_LENGTH_MASK; // length of entry name - bool isDir = controlByte & CONTROL_BYTE_DIR_MASK; // set if entry is a directory - - // Reconstitute name from bytes - // Btw I wish I could use the actual byte type but I have to comply with the software's choice of using char - - std::vector nameString; - for(size_t i = 0 ; i