Fix folder naming and repacking. Add support for extracting localization data.

This commit is contained in:
njohnson 2026-01-20 15:57:20 -05:00
parent 279c7aa666
commit 5c455e9a45
3 changed files with 83 additions and 14 deletions

View File

@ -231,8 +231,13 @@ bool CoalescedFile::packDirectory(const QString& dirPath, const QString& basePat
QString effectiveBasePath = basePath.isEmpty() ? dirPath : basePath;
// Find all .ini files recursively
QDirIterator it(dirPath, QStringList() << "*.ini", QDir::Files, QDirIterator::Subdirectories);
// Find all config and localization files recursively
// Supports: .ini, .int, .jpn, .deu, .esn, .fra, .ita, .kor, .pol, .rus, .cze, .hun, etc.
QStringList filters;
filters << "*.ini" << "*.int" << "*.jpn" << "*.deu" << "*.esn"
<< "*.fra" << "*.ita" << "*.kor" << "*.pol" << "*.rus"
<< "*.cze" << "*.hun" << "*.esm" << "*.ptb";
QDirIterator it(dirPath, filters, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString filePath = it.next();
@ -256,7 +261,7 @@ bool CoalescedFile::packDirectory(const QString& dirPath, const QString& basePat
}
if (m_entries.isEmpty()) {
setError("No .ini files found in directory");
setError("No config/localization files found in directory");
return false;
}
@ -264,6 +269,34 @@ bool CoalescedFile::packDirectory(const QString& dirPath, const QString& basePat
return true;
}
QString CoalescedFile::detectExtension() const
{
// Look at entries to detect the language/type
// Check paths for language folder indicators (e.g., \INT\, \JPN\, \DEU\)
// or file extensions
for (const IniEntry& entry : m_entries) {
QString path = entry.filename.toUpper();
// Check for language folder pattern like \INT\, \JPN\, etc.
QStringList langs = {"INT", "JPN", "DEU", "ESN", "FRA", "ITA", "KOR", "POL", "RUS", "CZE", "HUN", "ESM", "PTB"};
for (const QString& lang : langs) {
if (path.contains("\\" + lang + "\\")) {
return lang.toLower();
}
}
// Fall back to file extension
QFileInfo fi(entry.filename);
QString ext = fi.suffix().toLower();
if (!ext.isEmpty() && ext != "ini") {
return ext;
}
}
return "ini"; // Default
}
void CoalescedFile::setError(const QString& error)
{
m_lastError = error;

View File

@ -50,9 +50,12 @@ public:
// Static check if file is coalesced format without full parse
static bool isCoalescedFile(const QString& path);
// Pack a directory of INI files into this object
// Pack a directory of INI/localization files into this object
bool packDirectory(const QString& dirPath, const QString& basePath = QString());
// Detect language extension from entries (e.g., "int", "jpn", "ini")
QString detectExtension() const;
private:
QList<IniEntry> m_entries;
QString m_loadedPath;

View File

@ -17,8 +17,8 @@ void printError(const QString& message)
void showUsage()
{
cout << "UDK Config Extractor v1.0\n";
cout << "Extract and pack UDK/UE3 coalesced INI files\n\n";
cout << "UDK Config Extractor v1.1\n";
cout << "Extract and pack UDK/UE3 coalesced INI/INT files\n\n";
cout << "Usage:\n";
cout << " udk-config-extractor <command> [arguments]\n\n";
cout << "Commands:\n";
@ -31,8 +31,9 @@ void showUsage()
cout << " -h, --help Show this help message\n";
cout << " -v, --version Show version\n\n";
cout << "Drag & Drop:\n";
cout << " Drag a coalesced file onto exe Extracts to ./unpacked\n";
cout << " Drag a directory onto exe Packs to ./coalesced.ini\n";
cout << " Drag a coalesced file onto exe Extracts to ./<name>_<ext>/\n";
cout << " Drag a folder onto exe Packs to ./<name>.<ext>\n";
cout << " (folder coalesced_ini -> coalesced.ini)\n";
cout.flush();
}
@ -130,7 +131,14 @@ int cmdUnpack(const QStringList& args)
return 1;
}
QString outputDir = args.size() > 1 ? args[1] : ".";
QString outputDir;
if (args.size() > 1) {
outputDir = args[1];
} else {
// Default: use filename with _ instead of . (e.g., coalesced.ini -> coalesced_ini)
QFileInfo fi(args[0]);
outputDir = fi.completeBaseName() + "_" + fi.suffix();
}
QDir dir(outputDir);
if (!dir.exists() && !dir.mkpath(".")) {
@ -266,7 +274,7 @@ int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName("udk-config-extractor");
QCoreApplication::setApplicationVersion("1.0");
QCoreApplication::setApplicationVersion("1.1");
QStringList args = app.arguments();
args.removeFirst(); // Remove program name
@ -300,11 +308,36 @@ int main(int argc, char *argv[])
} else if (cmd == "extract") {
return cmdExtract(args);
} else if (QFileInfo(cmd).isDir()) {
// Directory dragged onto exe - pack to coalesced.ini
return cmdPack(QStringList() << cmd << "coalesced.ini");
// Directory dragged onto exe - derive output filename from folder name
// Folder "coalesced_ini" -> output "coalesced.ini"
CoalescedFile packer;
if (!packer.packDirectory(cmd)) {
printError(packer.lastError());
return 1;
}
// Get folder name and replace last _ with .
QString folderName = QFileInfo(cmd).fileName();
int lastUnderscore = folderName.lastIndexOf('_');
QString outputFile;
if (lastUnderscore > 0) {
outputFile = folderName.left(lastUnderscore) + "." + folderName.mid(lastUnderscore + 1);
} else {
// Fallback: use detected extension
outputFile = folderName + "." + packer.detectExtension();
}
if (!packer.save(outputFile)) {
printError(QString("Cannot write output file: %1").arg(outputFile));
return 1;
}
cout << QString("Packed %1 files (%2 bytes) to %3\n")
.arg(packer.count())
.arg(packer.totalSize())
.arg(outputFile);
cout.flush();
return 0;
} else if (QFile::exists(cmd)) {
// File dragged onto exe - unpack to ./unpacked
return cmdUnpack(QStringList() << cmd << "unpacked");
// File dragged onto exe - unpack to folder named after file
return cmdUnpack(QStringList() << cmd);
} else {
printError(QString("Unknown command: %1").arg(cmd));
showUsage();