You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

420 lines
12 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plRegistryNode.h"
#include "plRegistryKeyList.h"
#include "plRegistryHelpers.h"
#include "pnKeyedObject/plKeyImp.h"
#include "plStatusLog/plStatusLog.h"
#include "pnFactory/plFactory.h"
#include "plFile/plFileUtils.h"
#include "hsStlUtils.h"
#include "plVersion.h"
plRegistryPageNode::plRegistryPageNode(const char* path)
: fValid(kPageCorrupt)
, fPath(nil)
, fDynLoadedTypes(0)
, fStaticLoadedTypes(0)
, fOpenRequests(0)
, fIsNewPage(false)
{
fPath = hsStrcpy(path);
hsStream* stream = OpenStream();
if (stream)
{
fPageInfo.Read(&fStream);
fValid = IVerify();
CloseStream();
}
}
plRegistryPageNode::plRegistryPageNode(const plLocation& location, const char* age, const char* page, const char* dataPath)
: fValid(kPageOk)
, fPath(nil)
, fPageInfo(location)
, fDynLoadedTypes(0)
, fStaticLoadedTypes(0)
, fOpenRequests(0)
, fIsNewPage(true)
{
fPageInfo.SetStrings(age, page);
char filePath[512];
// Copy the path over
strncpy(filePath, dataPath, sizeof(filePath));
plFileUtils::AddSlash(filePath);
// Time to construct our actual file name. For now, we'll use the same old format
// of age_page.extension
strncat(filePath, fPageInfo.GetAge(), sizeof(filePath));
strncat(filePath, "_District_", sizeof(filePath));
strncat(filePath, fPageInfo.GetPage(), sizeof(filePath));
strncat(filePath, ".prp", sizeof(filePath));
fPath = hsStrcpy(filePath);
}
plRegistryPageNode::~plRegistryPageNode()
{
delete [] fPath;
UnloadKeys();
}
PageCond plRegistryPageNode::IVerify()
{
// Check the checksum values first, to make sure the files aren't corrupt
uint32_t ourChecksum = 0;
hsStream* stream = OpenStream();
if (stream)
{
ourChecksum = stream->GetEOF() - fPageInfo.GetDataStart();
CloseStream();
}
if (ourChecksum != fPageInfo.GetChecksum())
return kPageCorrupt;
// If major version out-of-date, entire location is screwed
if (fPageInfo.GetMajorVersion() > plVersion::GetMajorVersion())
return kPageTooNew;
else if (fPageInfo.GetMajorVersion() < plVersion::GetMajorVersion())
return kPageOutOfDate;
// Check the minor versions
const plPageInfo::ClassVerVec& classVersions = fPageInfo.GetClassVersions();
for (int i = 0; i < classVersions.size(); i++)
{
const plPageInfo::ClassVersion& cv = classVersions[i];
uint16_t curVersion = plVersion::GetCreatableVersion(cv.Class);
if (curVersion > cv.Version)
return kPageOutOfDate;
else if (curVersion < cv.Version)
return kPageTooNew;
}
return kPageOk;
}
hsStream* plRegistryPageNode::OpenStream()
{
if (fOpenRequests == 0)
{
if (!fStream.Open(fPath, "rb"))
return nil;
}
fOpenRequests++;
return &fStream;
}
void plRegistryPageNode::CloseStream()
{
if (fOpenRequests > 0)
fOpenRequests--;
if (fOpenRequests == 0)
fStream.Close();
}
void plRegistryPageNode::LoadKeys()
{
hsAssert(IsValid(), "Trying to load keys for invalid page");
hsAssert(!fIsNewPage, "Trying to read a new page");
if (IsFullyLoaded())
return;
hsStream* stream = OpenStream();
if (!stream)
{
hsAssert(0, xtl::format("plRegistryPageNode::LoadKeysFromSource - bad stream %s,%s",
GetPageInfo().GetAge(), GetPageInfo().GetPage()).c_str());
return;
}
// If we're loading keys in the middle of a read because FindKey() failed, we'd better
// make note of our stream position and restore it when we're done.
uint32_t oldPos = stream->GetPosition();
stream->SetPosition(GetPageInfo().GetIndexStart());
// Read in the number of key types
uint32_t numTypes = stream->ReadLE32();
for (uint32_t i = 0; i < numTypes; i++)
{
uint16_t classType = stream->ReadLE16();
plRegistryKeyList* keyList = IGetKeyList(classType);
if (!keyList)
{
keyList = new plRegistryKeyList(classType);
fKeyLists[classType] = keyList;
}
keyList->Read(stream);
}
stream->SetPosition(oldPos);
CloseStream();
fStaticLoadedTypes = fKeyLists.size();
}
void plRegistryPageNode::UnloadKeys()
{
KeyMap::iterator it = fKeyLists.begin();
for (; it != fKeyLists.end(); it++)
{
plRegistryKeyList* keyList = it->second;
delete keyList;
}
fKeyLists.clear();
fDynLoadedTypes = 0;
fStaticLoadedTypes = 0;
}
//// plWriteIterator /////////////////////////////////////////////////////////
// Key iterator for writing objects
class plWriteIterator : public plRegistryKeyIterator
{
protected:
hsStream* fStream;
public:
plWriteIterator(hsStream* s) : fStream(s) {}
virtual hsBool EatKey(const plKey& key)
{
plKeyImp* imp = (plKeyImp*)key;
imp->WriteObject(fStream);
return true;
}
};
void plRegistryPageNode::Write()
{
hsAssert(fOpenRequests == 0, "Trying to write while the page is open for reading");
if (!fStream.Open(fPath, "wb"))
{
hsAssert(0, "Couldn't open file for writing");
return;
}
// Some prep stuff. Assign object IDs for every key in this page, and put the
// versions of all our creatable types in the pageinfo.
fPageInfo.ClearClassVersions();
KeyMap::const_iterator it;
for (it = fKeyLists.begin(); it != fKeyLists.end(); it++)
{
plRegistryKeyList* keyList = it->second;
keyList->PrepForWrite();
int ver = plVersion::GetCreatableVersion(keyList->GetClassType());
fPageInfo.AddClassVersion(keyList->GetClassType(), ver);
}
// First thing we write is the pageinfo. Later we'll rewind and overwrite this with the final values
fPageInfo.Write(&fStream);
fPageInfo.SetDataStart(fStream.GetPosition());
// Write all our objects
plWriteIterator writer(&fStream);
IterateKeys(&writer);
fPageInfo.SetIndexStart(fStream.GetPosition());
// Write our keys
fStream.WriteLE32(fKeyLists.size());
for (it = fKeyLists.begin(); it != fKeyLists.end(); it++)
{
plRegistryKeyList* keyList = it->second;
fStream.WriteLE16(keyList->GetClassType());
keyList->Write(&fStream);
}
// Rewind and write the pageinfo with the correct data and index offsets
fStream.Rewind();
fPageInfo.SetChecksum(fStream.GetEOF() - fPageInfo.GetDataStart());
fPageInfo.Write(&fStream);
fStream.Close();
}
//// IterateKeys /////////////////////////////////////////////////////////////
hsBool plRegistryPageNode::IterateKeys(plRegistryKeyIterator* iterator) const
{
KeyMap::const_iterator it = fKeyLists.begin();
for (; it != fKeyLists.end(); it++)
{
plRegistryKeyList* keyList = it->second;
if (!keyList->IterateKeys(iterator))
return false;
}
return true;
}
//// IterateKeys /////////////////////////////////////////////////////////////
// Restricted version that only iterates through the keys of a given class
// type.
hsBool plRegistryPageNode::IterateKeys(plRegistryKeyIterator* iterator, uint16_t classToRestrictTo) const
{
plRegistryKeyList* keyList = IGetKeyList(classToRestrictTo);
if (keyList != nil)
return keyList->IterateKeys(iterator);
return true;
}
plKeyImp* plRegistryPageNode::FindKey(uint16_t classType, const char* name) const
{
plRegistryKeyList* keys = IGetKeyList(classType);
if (keys == nil)
return nil;
return keys->FindKey(name);
}
plKeyImp* plRegistryPageNode::FindKey(const plUoid& uoid) const
{
plRegistryKeyList* keys = IGetKeyList(uoid.GetClassType());
if (keys == nil)
return nil;
return keys->FindKey(uoid);
}
void plRegistryPageNode::AddKey(plKeyImp* key)
{
uint16_t classType = key->GetUoid().GetClassType();
plRegistryKeyList* keys = fKeyLists[classType];
if (keys == nil)
{
keys = new plRegistryKeyList(classType);
fKeyLists[classType] = keys;
}
// Error check
if (keys->FindKey(key->GetUoid().GetObjectName()) != nil)
{
//char str[512], tempStr[128];
//sprintf(str, "Attempting to add a key with a duplicate name. Not allowed."
// "\n\n(Key name: %s, Class: %s, Loc: %s)", key->GetUoid().GetObjectName(),
// plFactory::GetNameOfClass(classType), key->GetUoid().GetLocation().StringIze(tempStr));
//hsStatusMessage(str);
hsBool recovered = false;
// Attempt recovery
for (int i = 0; i < 500; i++)
{
char tempName[512];
sprintf(tempName, "%s%d", key->GetUoid().GetObjectName(), i);
if (keys->FindKey(tempName) == nil)
{
plUoid uoid(key->GetUoid().GetLocation(), key->GetUoid().GetClassType(), tempName, key->GetUoid().GetLoadMask());
key->SetUoid(uoid);
recovered = true;
break;
}
}
if (!recovered)
{
hsAssert(0, "Couldn't allocate a unique key");
return;
}
}
plRegistryKeyList::LoadStatus loadStatusChange;
keys->AddKey(key, loadStatusChange);
if (loadStatusChange == plRegistryKeyList::kDynLoaded)
fDynLoadedTypes++;
}
void plRegistryPageNode::SetKeyUsed(plKeyImp* key)
{
plRegistryKeyList* keys = IGetKeyList(key->GetUoid().GetClassType());
if (keys == nil)
return;
keys->SetKeyUsed(key);
}
hsBool plRegistryPageNode::SetKeyUnused(plKeyImp* key)
{
plRegistryKeyList* keys = IGetKeyList(key->GetUoid().GetClassType());
if (keys == nil)
return false;
plRegistryKeyList::LoadStatus loadStatusChange;
hsBool removed = keys->SetKeyUnused(key, loadStatusChange);
// If the key type just changed load status, update our load counts
if (loadStatusChange == plRegistryKeyList::kDynUnloaded)
fDynLoadedTypes--;
else if (loadStatusChange == plRegistryKeyList::kStaticUnloaded)
fStaticLoadedTypes--;
return removed;
}
plRegistryKeyList* plRegistryPageNode::IGetKeyList(uint16_t classType) const
{
KeyMap::const_iterator it = fKeyLists.find(classType);
if (it != fKeyLists.end())
return it->second;
return nil;
}
void plRegistryPageNode::DeleteSource()
{
hsAssert(fOpenRequests == 0, "Deleting a stream that's open for reading");
plFileUtils::RemoveFile(fPath);
}