Add PackManagerSim: full-manager pack verification tool
PackVerify only checks a single pack's own round-trip, which missed a real bug: uiscript.eix entries were built without their required "uiscript/" path prefix, so the standalone tool "round-tripped" fine (source-relative paths matched its own wrongly-shifted reference) while the actual game's CEterPackManager - which merges all packs into one shared dict keyed by full path - failed to resolve any uiscript file. PackManagerSim replicates the real client's PackInitialize(): it parses the pack folder's Index manifest, registers every pack (~103) into one real CEterPackManager exactly like the game does, and can verify every entry of a given pack resolves through that full lookup path.
This commit is contained in:
@@ -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 <packFolder> <fileNameToLookup>
|
||||
// Looks up one file through the full manager, e.g.:
|
||||
// PackManagerSim.exe "Y:\Metin2Dev\Exec_Client\pack" "UIScript/PopupDialog.py"
|
||||
//
|
||||
// PackManagerSim.exe <packFolder> --verify-all <dbname>
|
||||
// Registers all packs, then checks that every entry of <dbname> (read
|
||||
// directly from its own .eix) resolves through the full manager, e.g.:
|
||||
// PackManagerSim.exe "Y:\Metin2Dev\Exec_Client\pack" --verify-all uiscript
|
||||
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#include "../EterPack/EterPackManager.h"
|
||||
#include "../EterBase/lzo.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
CLZO lzoInstance;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <packFolder> <fileNameToLookup>\n", argv[0]);
|
||||
fprintf(stderr, " or: %s <packFolder> --verify-all <dbname>\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<std::string> 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 <dbname> 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<std::string> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user