/*==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 .
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 "plKeyFinder.h"
#include "hsTemplates.h"
#include "hsStlUtils.h"
#include "hsResMgr.h"
#include "plResManager.h"
#include "plRegistryHelpers.h"
#include "plRegistryNode.h"
#include "plRegistryKeyList.h"
#include "plPageInfo.h"
#include "pnFactory/plFactory.h"
#include "hsUtils.h"
#include "plCreatableIndex.h"
plResManager* IGetResMgr() { return (plResManager*)hsgResMgr::ResMgr(); }
plKeyFinder& plKeyFinder::Instance()
{
static plKeyFinder theInstance;
return theInstance;
}
const char* plKeyFinder::GetLastErrorString() // For Console display
{
// For Console display
static const char* KeyFinderErrors[] =
{
"Ok",
"Age not found",
"Page not found",
"Invalid class",
"None of those classes in this page",
"Object not found"
};
return KeyFinderErrors[fLastError];
}
//
// Does name string compare with potentially mangled (ie. [1 0 0]foo) names
//
hsBool NameMatches(const char* obName, const char* pKName, hsBool subString)
{
if (!obName || !pKName)
return false;
const char *o = obName;
const char *p = pKName;
// If names are mangled, unmangle
if (*o != '[' || *p != '[')
{
// skip past ']' in both names in case mangled
while (*o && *o != ']')
o++;
o = (*o==']') ? o+1 : obName;
while (*p && *p != ']')
p++;
p = (*p==']') ? p+1 : pKName;
}
if (!subString)
{
if (!stricmp(o, p))
return true; // FOUND IT!!!!!!!!!!!!!!!!!!!
}
else
{
char oCopy[256], pCopy[256];
strcpy(oCopy, o);
strcpy(pCopy, p);
hsStrLower(oCopy);
hsStrLower(pCopy);
if (strstr(pCopy, oCopy))
return true;
}
return false;
}
plKey plKeyFinder::StupidSearch(const char * age, const char * rm,
const char *className, const char *obName, hsBool subString)
{
UInt16 ty = plFactory::FindClassIndex(className);
return StupidSearch(age, rm, ty, obName, subString);
}
class plKeyFinderIter : public plRegistryKeyIterator, public plRegistryPageIterator
{
protected:
UInt16 fClassType;
const char *fObjName;
hsBool fSubstr;
plKey fFoundKey;
const char *fAgeName;
public:
plKey GetFoundKey( void ) const { return fFoundKey; }
plKeyFinderIter( UInt16 classType, const char *obName, hsBool substr )
: fFoundKey( nil ), fClassType( classType ), fObjName( obName ), fSubstr( substr ) { }
plKeyFinderIter( UInt16 classType, const char *obName, hsBool substr, const char *ageName )
: fFoundKey( nil ), fClassType( classType ), fObjName( obName ), fSubstr( substr ),
fAgeName( ageName ) {}
virtual hsBool EatKey( const plKey& key )
{
if( key->GetUoid().GetClassType() == fClassType &&
NameMatches( fObjName, key->GetUoid().GetObjectName(), fSubstr ) )
{
fFoundKey = key;
return false;
}
return true;
}
virtual hsBool EatPage( plRegistryPageNode *pageNode )
{
#ifndef _DEBUG
try
{
#endif
if( stricmp( pageNode->GetPageInfo().GetAge(), fAgeName ) == 0 )
{
// Try loading and searching thru this page
hsTArray keyRefs;
IGetResMgr()->LoadPageKeys( pageNode );
plKeyCollector coll( keyRefs );
pageNode->IterateKeys( &coll );
if( !pageNode->IterateKeys( this ) )
return false;
}
#ifndef _DEBUG
} catch (...)
{
}
#endif
return true;
}
};
plKey plKeyFinder::StupidSearch(const char * age, const char * rm,
UInt16 classType, const char *obName, hsBool subString)
{
if (!obName)
return nil;
plUoid newOid;
fLastError = kOk;
UInt16 maxClasses = plFactory::GetNumClasses();
UInt16 ty = classType;
if (ty == maxClasses) // error
{ fLastError = kInvalidClass;
return nil;
}
if( age != nil && rm != nil )
{
const plLocation &loc = IGetResMgr()->FindLocation( age, rm );
if( !loc.IsValid() )
{
fLastError = kPageNotFound;
return nil;
}
plKeyFinderIter keyFinder( classType, obName, subString );
if( !IGetResMgr()->IterateKeys( &keyFinder, loc ) )
// Return value of false means it stopped somewhere, i.e. found something
return keyFinder.GetFoundKey();
}
else if( age != nil )
{
plKeyFinderIter keyFinder(classType, obName, subString, age);
if( !IGetResMgr()->IterateAllPages( &keyFinder ) )
return keyFinder.GetFoundKey();
}
else
{
plKeyFinderIter keyFinder( classType, obName, subString );
if( !IGetResMgr()->IterateKeys( &keyFinder ) )
// Return value of false means it stopped somewhere, i.e. found something
return keyFinder.GetFoundKey();
}
fLastError = kObjectNotFound;
return nil;
}
void plKeyFinder::ReallyStupidResponderSearch(const char *name, std::vector& foundKeys, const plLocation &hintLocation )
{
ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plResponderModifier), foundKeys, hintLocation);
}
void plKeyFinder::ReallyStupidActivatorSearch(const char *name, std::vector& foundKeys, const plLocation &hintLocation)
{
// use the createable macro so we don't have to pull in all of Python
ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plLogicModifier), foundKeys, hintLocation);
ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plPythonFileMod), foundKeys, hintLocation);
ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plSittingModifier), foundKeys, hintLocation);
}
void plKeyFinder::IGetNames(std::vector& names, const char* searchName, int index)
{
// Not really searching for any particular key, just need all the logic mods
std::vector keys;
ReallyStupidSubstringSearch(searchName, index, keys);
for (int i = 0; i < keys.size(); i++)
{
// Only allow loaded ones to cut down on the crap
plKey key = keys[i];
if (key->ObjectIsLoaded())
names.push_back(key->GetName());
}
}
void plKeyFinder::GetResponderNames(std::vector& names)
{
IGetNames(names, "", CLASS_INDEX_SCOPED(plResponderModifier));
}
void plKeyFinder::GetActivatorNames(std::vector& names)
{
IGetNames(names, "", CLASS_INDEX_SCOPED(plLogicModifier));
}
class plKeyFinderIterator : public plRegistryKeyIterator, public plRegistryPageIterator
{
protected:
UInt16 fClassType;
const char *fObjName;
std::vector &fFoundKeys;
public:
plKeyFinderIterator( UInt16 classType, const char *obName, std::vector& foundKeys )
: fClassType( classType ), fObjName( obName ), fFoundKeys( foundKeys ) { }
virtual hsBool EatKey( const plKey& key )
{
if( key->GetUoid().IsValid() && key->GetUoid().GetClassType() == fClassType &&
strstr( key->GetUoid().GetObjectName(), fObjName ) )
{
fFoundKeys.push_back( key );
}
return true;
}
virtual hsBool EatPage( plRegistryPageNode *page )
{
hsBool ret = page->IterateKeys( this );
return ret;
}
};
void plKeyFinder::ReallyStupidSubstringSearch(const char *name, UInt16 objType, std::vector& foundKeys, const plLocation &hintLocation )
{
if (!name)
return;
plKeyFinderIterator collector( objType, name, foundKeys );
if( hintLocation.IsValid() )
{
plRegistryPageNode *hintPage = IGetResMgr()->FindPage( hintLocation );
if( hintPage != nil )
{
// Try all pages in the same age as that page
IGetResMgr()->IteratePages( &collector, hintPage->GetPageInfo().GetAge() );
}
if (foundKeys.size() > 0)
{
return;
}
}
//fpReg->IterateKeys( &collector );
IGetResMgr()->IterateAllPages( &collector );
}
//// Helper Class for FindSceneNodeKey ///////////////////////////////////////
class plPageFinder : public plRegistryPageIterator
{
protected:
plRegistryPageNode **fPagePtr;
const char *fFindString, *fAgeString;
public:
plPageFinder( plRegistryPageNode **page, const char *find ) : fPagePtr( page ), fFindString( find ), fAgeString( nil )
{ *fPagePtr = nil; }
plPageFinder( plRegistryPageNode **page, const char *ageS, const char *pageS ) : fPagePtr( page ), fFindString( pageS ), fAgeString( ageS )
{ *fPagePtr = nil; }
virtual hsBool EatPage( plRegistryPageNode *node )
{
static char str[ 512 ];
const plPageInfo &info = node->GetPageInfo();
// Are we searching by age/page?
if( fAgeString != nil )
{
if( stricmp( info.GetAge(), fAgeString ) == 0 &&
stricmp( info.GetPage(), fFindString ) == 0 )
{
*fPagePtr = node;
return false;
}
return true;
}
// Try for page only
if( stricmp( info.GetPage(), fFindString ) == 0 )
{
*fPagePtr = node;
return false;
}
// Try for full location
sprintf( str, "%s_%s_%s", info.GetAge(), info.GetPage() );
if( stricmp( str, fFindString ) == 0 )
{
*fPagePtr = node;
return false;
}
return true; // Keep searching
}
};
//// FindSceneNodeKey ////////////////////////////////////////////////////////
// Given a string for either a page name or a fully qualified location name,
// finds the page and then returns the key for the sceneNode in that page.
// Note: in our case, we want to force the page's keys to load if necessary,
// since the only time we call this function will be to actually load
// the darned thing.
plKey plKeyFinder::FindSceneNodeKey( const char *pageOrFullLocName ) const
{
plRegistryPageNode *pageNode;
plPageFinder pageFinder( &pageNode, pageOrFullLocName );
// Use our own page finder, since we want to do nifty things like partial
// matches, etc.
if( IGetResMgr()->IterateAllPages( &pageFinder ) || pageNode == nil )
return nil;
return IFindSceneNodeKey( pageNode );
}
//// FindSceneNodeKey ////////////////////////////////////////////////////////
// Age/page pair version
plKey plKeyFinder::FindSceneNodeKey( const char *ageName, const char *pageName ) const
{
plRegistryPageNode *pageNode;
plPageFinder pageFinder( &pageNode, ageName, pageName );
// Use our own page finder, since we want to do nifty things like partial
// matches, etc.
if (IGetResMgr()->IterateAllPages(&pageFinder) || pageNode == nil)
return nil;
return IFindSceneNodeKey( pageNode );
}
//// FindSceneNodeKey ////////////////////////////////////////////////////////
// plLocation version
plKey plKeyFinder::FindSceneNodeKey(const plLocation& location) const
{
plRegistryPageNode* pageNode = IGetResMgr()->FindPage(location);
if (pageNode == nil)
return nil;
return IFindSceneNodeKey(pageNode);
}
//// IFindSceneNodeKey ///////////////////////////////////////////////////////
plKey plKeyFinder::IFindSceneNodeKey(plRegistryPageNode* page) const
{
// Got the pageNode, try a find before loading
plRegistryKeyList* keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode));
if (keyList)
{
if (keyList->fStaticKeys.size() == 1)
{
return plKey::Make((plKeyData*)keyList->fStaticKeys[0]);
}
else if (keyList->fDynamicKeys.size() == 1) // happens during export
{
plRegistryKeyList::DynSet::const_iterator it = keyList->fDynamicKeys.begin();
plKeyImp* keyImp = *it;
return plKey::Make(keyImp);
}
}
// Try loading and see if that helps
if (page->IsFullyLoaded())
return nil;
IGetResMgr()->LoadPageKeys(page);
// Get the list of all sceneNodes
plKey retVal(nil);
keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode));
if (keyList && keyList->fStaticKeys.size() == 1)
{
retVal = plKey::Make((plKeyData*)keyList->fStaticKeys[0]);
}
// If we just loaded up all the keys for this page, then we
// may have a bunch of keys with a refcount of 0. For any of
// these keys that nothing else refs (yes, we have unused objects
// in the data), they don't get deleted because the refcount never
// rises above zero or falls back to zero. So we'll go ahead and
// ref and unref all of them. The ones in use stay put, the ones
// not being used go away. This is less than ideal.
IGetResMgr()->DumpUnusedKeys(page);
return retVal;
}
//// FindLocation ////////////////////////////////////////////////////////////
const plLocation &plKeyFinder::FindLocation( const char *age, const char *page ) const
{
if (age == nil)
{
static plLocation invalidLoc;
plRegistryPageNode *pageNode;
plPageFinder pageFinder( &pageNode, page );
if( IGetResMgr()->IterateAllPages( &pageFinder ) || pageNode == nil )
return invalidLoc;
return pageNode->GetPageInfo().GetLocation();
}
return IGetResMgr()->FindLocation( age, page );
}
//// GetLocationInfo /////////////////////////////////////////////////////////
const plPageInfo* plKeyFinder::GetLocationInfo( const plLocation &loc ) const
{
plRegistryPageNode *node = IGetResMgr()->FindPage( loc );
if (node)
return &node->GetPageInfo();
else
return nil;
}