diff --git a/source/PackTool/PackManagerSim.cpp b/source/PackTool/PackManagerSim.cpp new file mode 100644 index 0000000..18224f9 --- /dev/null +++ b/source/PackTool/PackManagerSim.cpp @@ -0,0 +1,141 @@ +// Reproduces the exact pack-registration flow the real client performs in +// PackInitialize() (UserInterface.cpp): parses the pack folder's "Index" +// manifest and registers every listed pack (plus root) into one real +// CEterPackManager, exactly like the running game does. This is the only +// reliable way to verify a pack, because single-pack tools (PackVerify) can't +// catch bugs in how a file's stored path lines up with what the manager's +// merged, folder-prefixed lookup actually queries for. +// +// Usage: +// PackManagerSim.exe +// Looks up one file through the full manager, e.g.: +// PackManagerSim.exe "Y:\Metin2Dev\Exec_Client\pack" "UIScript/PopupDialog.py" +// +// PackManagerSim.exe --verify-all +// Registers all packs, then checks that every entry of (read +// directly from its own .eix) resolves through the full manager, e.g.: +// PackManagerSim.exe "Y:\Metin2Dev\Exec_Client\pack" --verify-all uiscript + +#include +#include +#include +#include +#include + +#include "../EterPack/EterPackManager.h" +#include "../EterBase/lzo.h" + +int main(int argc, char** argv) +{ + CLZO lzoInstance; + + if (argc < 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, " or: %s --verify-all \n", argv[0]); + return 1; + } + + std::string packFolder = argv[1]; + std::string lookupName = argv[2]; + std::string verifyDbname = (argc >= 4) ? argv[3] : ""; + + std::string stFolder = packFolder + "/"; + std::string indexPath = stFolder + "Index"; + + std::ifstream indexFile(indexPath); + if (!indexFile) + { + fprintf(stderr, "Cannot open Index file: %s\n", indexPath.c_str()); + return 2; + } + + std::vector lines; + std::string line; + while (std::getline(indexFile, line)) + { + while (!line.empty() && (line.back() == '\r' || line.back() == '\n')) + line.pop_back(); + lines.push_back(line); + } + + if (lines.empty() || (lines[0] != "FILE" && lines[0] != "PACK")) + { + fprintf(stderr, "Invalid Index syntax\n"); + return 3; + } + + CEterPackManager manager; + manager.SetSearchMode(true); // _DISTRIBUTE build: pack-first + + int registered = 0, failed = 0; + + for (size_t i = 1; i + 1 < lines.size(); i += 2) + { + const std::string& rstFolder = lines[i]; + const std::string& rstName = lines[i + 1]; + + std::string packName = stFolder + rstName; + std::string texCacheName = packName + "_texcache"; + + if (manager.RegisterPack(packName.c_str(), rstFolder.c_str())) + ++registered; + else + ++failed; + + manager.RegisterPack(texCacheName.c_str(), rstFolder.c_str()); + } + + manager.RegisterRootPack((stFolder + "root").c_str()); + + printf("Registered %d packs (%d failed to open) from Index\n", registered, failed); + + if (lookupName == "--verify-all") + { + if (verifyDbname.empty()) + { + fprintf(stderr, "--verify-all requires a argument\n"); + return 12; + } + + std::string dbname = stFolder + verifyDbname; + CEterFileDict rawDict; + CEterPack rawPack; + if (!rawPack.Create(rawDict, dbname.c_str(), "", true)) + { + fprintf(stderr, "Cannot open raw pack '%s'\n", dbname.c_str()); + return 20; + } + + std::vector names; + rawPack.GetNames(&names); + + int ok = 0, bad = 0; + for (auto& n : names) + { + CMappedFile mf; + LPCVOID d = NULL; + if (manager.Get(mf, n.c_str(), &d)) + ++ok; + else + { + ++bad; + printf(" MISSING VIA MANAGER: %s\n", n.c_str()); + } + } + printf("verify-all '%s': %d/%zu OK via manager, %d missing\n", verifyDbname.c_str(), ok, names.size(), bad); + return bad > 0 ? 11 : 0; + } + + CMappedFile mappedFile; + LPCVOID data = NULL; + + bool ok = manager.Get(mappedFile, lookupName.c_str(), &data); + + printf("Get(\"%s\") => %s", lookupName.c_str(), ok ? "FOUND" : "NOT FOUND"); + if (ok) + printf(" (%lu bytes)", mappedFile.Size()); + printf("\n"); + + return ok ? 0 : 10; +}