mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
131 lines
3.9 KiB
C++
131 lines
3.9 KiB
C++
#include "game/completemanager.h"
|
|
#include "mpshared/misc.h"
|
|
#include "base/format.h"
|
|
#include "game/httppackmanager.h"
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
|
|
namespace wi {
|
|
|
|
void CompleteManager::Init() {
|
|
map_.clear();
|
|
DIR *pdir = opendir(completedir_.c_str());
|
|
dirent *pdent;
|
|
while ((pdent = readdir(pdir)) != NULL) {
|
|
dword key;
|
|
MissionHash mh;
|
|
if (!ParseFilename(pdent->d_name, &key, mh.hash)) {
|
|
continue;
|
|
}
|
|
map_.insert(CompleteMap::value_type(key, mh));
|
|
}
|
|
closedir(pdir);
|
|
}
|
|
|
|
bool CompleteManager::ParseFilename(const char *filename, dword *pkey,
|
|
byte *hash) {
|
|
|
|
LOG() << filename;
|
|
|
|
// key -hash
|
|
// kkkkkkkk-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
|
|
if (strlen(filename) != 8 + 1 + 32) {
|
|
return false;
|
|
}
|
|
char szId[9];
|
|
strncpyz(szId, filename, 9);
|
|
if (!base::Format::ToDword(szId, 16, pkey)) {
|
|
return false;
|
|
}
|
|
return base::Format::FromHex(filename + 8 + 1, hash, 16);
|
|
}
|
|
|
|
const char *CompleteManager::GetFilename(dword key, MissionHash *pmh) {
|
|
char szHash[33];
|
|
strncpyz(szHash, base::Format::ToHex(pmh->hash, 16), sizeof(szHash));
|
|
return base::Format::ToString("%08x-%s", key, szHash);
|
|
}
|
|
|
|
bool CompleteManager::IsComplete(const MissionIdentifier *pmiid) {
|
|
// Quickly fail
|
|
dword key = GetKey(pmiid);
|
|
if (map_.count(key) == 0) {
|
|
return false;
|
|
}
|
|
|
|
// Slow verification
|
|
MissionHash mh;
|
|
if (!GetMissionHash(pmiid, &mh)) {
|
|
return false;
|
|
}
|
|
|
|
// See if this hash is known.
|
|
// Using a multimap for backwards compat reasons. See BadHashString.
|
|
std::pair<CompleteMap::iterator, CompleteMap::iterator> range;
|
|
range = map_.equal_range(key);
|
|
for (CompleteMap::iterator it = range.first; it != range.second; it++) {
|
|
if (memcmp(mh.hash, it->second.hash, sizeof(mh.hash)) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CompleteManager::GetMissionHash(const MissionIdentifier *pmiid,
|
|
MissionHash *pmh) {
|
|
gppackm->Mount(gpakr, &pmiid->packid);
|
|
if (!gpakr.HashFile(pmiid->szLvlFilename, pmh->hash)) {
|
|
gppackm->Unmount(gpakr, &pmiid->packid);
|
|
return false;
|
|
}
|
|
gppackm->Unmount(gpakr, &pmiid->packid);
|
|
return true;
|
|
}
|
|
|
|
dword CompleteManager::GetKey(const MissionIdentifier *pmiid) {
|
|
const char *s = base::Format::ToString("%08x", pmiid->packid.id);
|
|
return BadHashString(s) ^ BadHashString(pmiid->szLvlFilename);
|
|
}
|
|
|
|
void CompleteManager::MarkComplete(const MissionIdentifier *pmiid) {
|
|
MissionHash mh;
|
|
if (!GetMissionHash(pmiid, &mh)) {
|
|
return;
|
|
}
|
|
dword key = GetKey(pmiid);
|
|
const char *filename = GetFilename(key, &mh);
|
|
char szFilename[8 + 1 + 32 + 1];
|
|
strncpyz(szFilename, filename, sizeof(szFilename));
|
|
const char *filepath = base::Format::ToString("%s/%s",
|
|
completedir_.c_str(), szFilename);
|
|
|
|
// This will create a zero length file. Perhaps in the future stats can
|
|
// be stored in this file, and this will become StatManager instead.
|
|
|
|
FILE *pf = fopen(filepath, "w");
|
|
if (pf != NULL) {
|
|
fclose(pf);
|
|
map_.insert(CompleteMap::value_type(key, mh));
|
|
}
|
|
}
|
|
|
|
dword CompleteManager::BadHashString(const char *s) {
|
|
// This string hash is here for a few reasons:
|
|
// 1. WI v1.5 CompleteManager uses this hash algorithm, and has
|
|
// committed its values to storage (oops)
|
|
// 2. This used to be the default string hash algorithm. It is no longer,
|
|
// because it is broken. For example, s_05.lvl hashes to the same value
|
|
// as s_10.lvl (as well as 6 to 11, 7 to 12, 8 to 13, and 9 to 14).
|
|
// 3. The easiest thing to do is to continue using it, and use a multimap
|
|
// for lookup, instead of a map (which is now the case).
|
|
dword h = 0;
|
|
for (; *s != 0; s++) {
|
|
h = 5 * h + *s;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
} // namespace wi
|