// Write-mode pack builder: recursively packs every file under a source folder // into .eix/.epk in an output folder, using the same CEterPack // class the game client itself uses to read packs (so the result is guaranteed // compatible). Always rebuilds the pack from scratch (deletes any existing // .eix/.epk in the output folder first) so removed source files don't // linger as stale entries. // // Usage: PackMake.exe // e.g. PackMake.exe "Y:\Metin2Dev\Exec_Client\Eternexus\root" "Y:\scratch\pack" root #include #include #include #include #include "../EterPack/EterPack.h" #include "../EterBase/lzo.h" namespace fs = std::filesystem; int main(int argc, char** argv) { // CLZO is a CSingleton that registers itself via ctor side effect (no // lazy init) - EncryptIndexFile() calls CLZO::Instance() internally, so an // instance must exist before that or Instance() dereferences a null pointer. CLZO lzoInstance; if (argc < 4) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } const std::string sourceFolder = argv[1]; const std::string outputFolder = argv[2]; const char* dbname = argv[3]; if (!fs::is_directory(sourceFolder)) { fprintf(stderr, "Source folder does not exist: %s\n", sourceFolder.c_str()); return 2; } if (!fs::is_directory(outputFolder)) { fprintf(stderr, "Output pack folder does not exist: %s\n", outputFolder.c_str()); return 2; } if (!SetCurrentDirectoryA(outputFolder.c_str())) { fprintf(stderr, "FAILED to chdir to '%s'\n", outputFolder.c_str()); return 4; } std::string eixPath = std::string(dbname) + ".eix"; std::string epkPath = std::string(dbname) + ".epk"; if (fs::exists(eixPath) || fs::exists(epkPath)) { printf("Removing existing %s / %s in output folder for a clean rebuild\n", eixPath.c_str(), epkPath.c_str()); fs::remove(eixPath); fs::remove(epkPath); } CEterFileDict dict; CEterPack pack; if (!pack.Create(dict, dbname, "", /*bReadOnly=*/false)) { fprintf(stderr, "FAILED to create pack '%s' in '%s'\n", dbname, outputFolder.c_str()); return 3; } long fileCount = 0; long failCount = 0; for (auto it = fs::recursive_directory_iterator(sourceFolder); it != fs::recursive_directory_iterator(); ++it) { if (!it->is_regular_file()) continue; std::string relPath = fs::relative(it->path(), sourceFolder).generic_string(); std::string srcPath = it->path().string(); if (!pack.Put(relPath.c_str(), srcPath.c_str(), COMPRESSED_TYPE_NONE, "")) { fprintf(stderr, "FAILED to put '%s' (source: %s)\n", relPath.c_str(), srcPath.c_str()); ++failCount; continue; } ++fileCount; } if (!pack.EncryptIndexFile()) { fprintf(stderr, "FAILED to encrypt index for pack '%s'\n", dbname); return 5; } printf("Packed %ld files (%ld failures) into %s / %s\n", fileCount, failCount, eixPath.c_str(), epkPath.c_str()); return failCount > 0 ? 6 : 0; }