// 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; }