/*==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; }