FAT Data Extractor moved to FAT Tools Tab

This commit is contained in:
Luca D'Amico
2024-08-10 23:20:12 +02:00
parent d69e4e13fa
commit 921eef86e2
10 changed files with 346 additions and 196 deletions

View File

@@ -1,6 +1,6 @@
#ifndef REVISION_H
#define REVISION_H
#define GIT_COMMIT_HASH "aa2ed50"
#define GIT_COMMIT_HASH "d69e4e1"
#endif

View File

@@ -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);
};

View File

@@ -274,13 +274,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="unpackerDecodeFatFilesBtn">
<property name="text">
<string>Decode FAT Contents</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -601,11 +594,135 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout_14">
<property name="spacing">
<number>4</number>
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>FAT Data Extractor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_38">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="fatExtractorFatDataPathEdt"/>
</item>
<item>
<widget class="QPushButton" name="fatExtractorLoadFatDataBtn">
<property name="text">
<string>Load FAT Data(fat_data.bin)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_40">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="fatExtractorFntPathEdt"/>
</item>
<item>
<widget class="QPushButton" name="fatExtractorLoadFntBtn">
<property name="text">
<string>Load FNT(fnt.bin)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_39" stretch="0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="fatExtractorFatPathEdt"/>
</item>
<item>
<widget class="QPushButton" name="fatExtractorLoadFatBtn">
<property name="text">
<string>Load FAT(fat.bin)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Original Fat Files (fat_data.bin) Addr:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="fatExtractorOriginalFatFilesAddrEdt"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_41">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="fatExtractorExtractBtn">
<property name="text">
<string>Extract FAT Data!</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
@@ -734,6 +851,9 @@
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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<char> fatBytes(static_cast<unsigned long>(fatSize));
std::vector<char> fntBytes(static_cast<unsigned long>(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<FatRange*>(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<char> nameString;
for(size_t i = 0 ; i<nameLength ; i++) nameString.push_back(fntBytes[currentOffset++]);
std::string name(&nameString[0], (size_t)nameLength);
// We'll need this either way
QString newPath(QDir::toNativeSeparators(QString::fromStdString(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;
// Now the QDir we created earlier comes into play :
// C++ doesn't automatically create directories (!!!) so we have to rely on QT to do that manually.
// Otherwise, the ofstream will not open,
// And even if we force it open, it will just write into nothingness !!!
if(!curDir.exists(newPath)) curDir.mkdir(newPath); // I don't think the check is even necessary, actually
// Jump back to the FNT header and repeat the process for subdirectory !
if(!parseFolder(subFolderId,newPath.toStdString(),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.
if(!ndsFactory.writeFatSectionToFile(
romPath,
pfatrange+fatOffset,
newPath.toStdString()))
return false;
fatOffset++;
}
}
return true;
};
// The root folder's ID is, obviously, 0 (only the lower 12-bit count!)
return parseFolder(ROOT_DIRECTORY_ADDRESS,dirPath.toStdString(),parseFolder);
}

View File

@@ -169,17 +169,6 @@ void MainWindow::on_unpackerDumpEverythingBtn_clicked()
notifyExtractionResult(dumpEverything(dirPath));
}
void MainWindow::on_unpackerDecodeFatFilesBtn_clicked()
{
QString dirPath = QFileDialog::getExistingDirectory(
this, tr("Select Directory"),
"",
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!dirPath.isNull())
notifyExtractionResult(decodeFatFiles(dirPath));
}
void MainWindow::notifyExtractionResult(bool result)
{
if(result)