/*==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 .
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 "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 "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
{
if (_TEMP_CONVERT_FROM_LITERAL(p).Find(o, plString::kCaseInsensitive) >= 0)
return true;
}
return false;
}
plKey plKeyFinder::StupidSearch(const char * age, const char * rm,
const char *className, const plString &obName, hsBool subString)
{
uint16_t ty = plFactory::FindClassIndex(className);
return StupidSearch(age, rm, ty, obName, subString);
}
class plKeyFinderIter : public plRegistryKeyIterator, public plRegistryPageIterator
{
protected:
uint16_t fClassType;
plString fObjName;
hsBool fSubstr;
plKey fFoundKey;
const char *fAgeName;
public:
plKey GetFoundKey( void ) const { return fFoundKey; }
plKeyFinderIter( uint16_t classType, const plString &obName, hsBool substr )
: fFoundKey( nil ), fClassType( classType ), fObjName( obName ), fSubstr( substr ) { }
plKeyFinderIter( uint16_t classType, const plString &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.c_str(), key->GetUoid().GetObjectName().c_str(), 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_t classType, const plString &obName, hsBool subString)
{
if (obName.IsNull())
return nil;
plUoid newOid;
fLastError = kOk;
uint16_t maxClasses = plFactory::GetNumClasses();
uint16_t 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 plString &name, std::vector& foundKeys, const plLocation &hintLocation )
{
ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plResponderModifier), foundKeys, hintLocation);
}
void plKeyFinder::ReallyStupidActivatorSearch(const plString &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 plString& 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, _TEMP_CONVERT_FROM_LITERAL(""), CLASS_INDEX_SCOPED(plResponderModifier));
}
void plKeyFinder::GetActivatorNames(std::vector& names)
{
IGetNames(names, _TEMP_CONVERT_FROM_LITERAL(""), CLASS_INDEX_SCOPED(plLogicModifier));
}
class plKeyFinderIterator : public plRegistryKeyIterator, public plRegistryPageIterator
{
protected:
uint16_t fClassType;
plString fObjName;
std::vector &fFoundKeys;
public:
plKeyFinderIterator( uint16_t classType, const plString &obName, std::vector& foundKeys )
: fClassType( classType ), fObjName( obName ), fFoundKeys( foundKeys ) { }
virtual hsBool EatKey( const plKey& key )
{
if( key->GetUoid().IsValid() && key->GetUoid().GetClassType() == fClassType &&
key->GetUoid().GetObjectName().Find( fObjName ) >= 0 )
{
fFoundKeys.push_back( key );
}
return true;
}
virtual hsBool EatPage( plRegistryPageNode *page )
{
hsBool ret = page->IterateKeys( this );
return ret;
}
};
void plKeyFinder::ReallyStupidSubstringSearch(const plString &name, uint16_t objType, std::vector& foundKeys, const plLocation &hintLocation )
{
if (name.IsNull())
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", 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;
}