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.
575 lines
14 KiB
575 lines
14 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 "HeadSpin.h" |
|
#include "hsExceptionStack.h" |
|
#include "hsHashTable.h" |
|
#include "hsStringTokenizer.h" |
|
#include "hsResMgr.h" |
|
#include <cmath> |
|
|
|
#include <max.h> |
|
#include <stdmat.h> |
|
#if MAX_VERSION_MAJOR >= 13 |
|
# include <INamedSelectionSetManager.h> |
|
#endif |
|
#pragma hdrstop |
|
|
|
#include "hsConverterUtils.h" |
|
#include "MaxMain/MaxCompat.h" |
|
|
|
#include "hsMaxLayerBase.h" |
|
#include "plInterp/plController.h" |
|
|
|
#include "MaxExport/plErrorMsg.h" |
|
#include "UserPropMgr.h" |
|
#include "plGImage/hsCodecManager.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "pnKeyedObject/hsKeyedObject.h" |
|
|
|
#include "MaxMain/MaxCompat.h" |
|
|
|
const char hsConverterUtils::fTagSeps[] = " ,\t\n=:;"; |
|
|
|
extern UserPropMgr gUserPropMgr; |
|
|
|
namespace { |
|
class ObjectInstancedEnumProc : public DependentEnumProc |
|
{ |
|
public: |
|
ObjectInstancedEnumProc() : fInstanceCount(0) { } |
|
|
|
int proc(ReferenceMaker *rmaker) |
|
{ |
|
hsGuardBegin("ObjectInstancedEnumProc::proc"); |
|
|
|
if (rmaker->SuperClassID()==BASENODE_CLASS_ID) |
|
{ |
|
fInstanceCount++; |
|
} |
|
|
|
return 0; |
|
hsGuardEnd; |
|
} |
|
|
|
int32_t GetInstanceCount() { return fInstanceCount; } |
|
|
|
private: |
|
int32_t fInstanceCount; |
|
}; |
|
} |
|
|
|
hsConverterUtils& hsConverterUtils::Instance() |
|
{ |
|
static hsConverterUtils the_instance; |
|
|
|
return the_instance; |
|
} |
|
|
|
hsConverterUtils::hsConverterUtils() : |
|
fInterface(GetCOREInterface()), |
|
fErrorMsg(nil), |
|
fSuppressMangling(false), |
|
fWarned(0) |
|
{ |
|
} |
|
|
|
void hsConverterUtils::Init(bool save, plErrorMsg *msg) |
|
{ |
|
hsGuardBegin("hsConverterUtils::Init"); |
|
|
|
fErrorMsg = msg; |
|
|
|
fSuppressMangling = false; |
|
fWarned = 0; |
|
fSave = save; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
TimeValue hsConverterUtils::GetTime(Interface *gi) |
|
{ |
|
hsGuardBegin("hsConverterUtils::GetTime"); |
|
|
|
return ((TimeValue)0); |
|
hsGuardEnd; |
|
// return gi->GetTime(); |
|
// return theSceneEnum->time; |
|
} |
|
|
|
bool hsConverterUtils::IsEnvironHolder(INode *node) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IsEnvironHolder"); |
|
|
|
return (gUserPropMgr.UserPropExists(node, "EnvironMap")); |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsConverterUtils::AutoStartDynamics(INode *node) |
|
{ |
|
hsGuardBegin("hsConverterUtils::AutoStartDynamics"); |
|
|
|
return (gUserPropMgr.UserPropExists(node,"AutoStart") || gUserPropMgr.UserPropExists(node,"aud")); |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsConverterUtils::RandomStartDynamics(INode *node) |
|
{ |
|
hsGuardBegin("hsConverterUtils::RandomStartDynamics"); |
|
|
|
return (gUserPropMgr.UserPropExists(node,"RandomStart")); |
|
hsGuardEnd; |
|
} |
|
|
|
void hsConverterUtils::StripOffTail(char* path) |
|
{ |
|
hsGuardBegin("hsConverterUtils::StripOffTail"); |
|
|
|
int i = strlen(path)-1; |
|
while(path[i] != '\\') |
|
i--; |
|
path[i+1] = 0; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
void hsConverterUtils::StripOffPath(char* fileName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::StripOffPath"); |
|
|
|
char tmp[256]; |
|
// Remove preceding path |
|
int i = strlen(fileName)-1; |
|
while(fileName[i] != '\\') |
|
i--; |
|
strcpy(tmp, fileName+i+1); |
|
strcpy(fileName, tmp); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// static |
|
// |
|
bool hsConverterUtils::IsReservedKeyword(const char* nodeName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IsReservedKeyword"); |
|
|
|
return (nodeName!=nil && |
|
( !_stricmp(nodeName, "theplayer") |
|
|| !_stricmp(nodeName, "the_player") |
|
|| !_stricmp(nodeName, "thecamera") |
|
|| !_stricmp(nodeName, "the_camera") |
|
|| !_stricmp(nodeName, "thedetector") |
|
|| !_stricmp(nodeName, "the_detector") |
|
|| !_stricmp(nodeName, "themonitor") |
|
|| !_stricmp(nodeName, "the_monitor") |
|
|| !_stricmp(nodeName, "thedetectorshape") |
|
|| !_stricmp(nodeName, "the_detector_shape")) ); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
char *hsConverterUtils::MangleReference(char *mangName, const char *nodeName, const char* defRoom) |
|
{ |
|
hsGuardBegin("hsConverterUtils::MangleReference"); |
|
|
|
//hsAssert(nodeName, "No node name in hsConverterUtils::MangleReference."); |
|
if(!nodeName) |
|
{ |
|
fErrorMsg->Set(true, "Mangle Reference Error", "No node name in hsConverterUtils::MangleReference.").Show(); |
|
fErrorMsg->Set(); |
|
return mangName; |
|
} |
|
if (!*nodeName) |
|
return hsStrcpy(mangName, nodeName); |
|
|
|
// doesn't want to be mangled |
|
if (('.' == nodeName[0])&&('.' == nodeName[1])) |
|
return hsStrcpy(mangName, nodeName + 2); |
|
|
|
// already mangled or reserved |
|
if (strstr(nodeName, "..") || IsReservedKeyword(nodeName)) |
|
return hsStrcpy(mangName, nodeName); |
|
|
|
INode *node = GetINodeByName(nodeName); |
|
|
|
if (!node) |
|
{ |
|
// no room so make global |
|
// Default is to make it global, but you can set another default (like same |
|
// room as referencer) with defRoom. |
|
|
|
plString tempName = plFormat("{}..{}", defRoom, nodeName); |
|
return hsStrcpy(mangName, tempName.c_str()); |
|
} |
|
|
|
return MangleReference(mangName, node); |
|
hsGuardEnd; |
|
} |
|
|
|
char *hsConverterUtils::MangleReference(char *mangName, INode *node, const char* defRoom) |
|
{ |
|
hsGuardBegin("hsConverterUtils::MangleReference"); |
|
|
|
if (!node) |
|
return nil; |
|
|
|
plString tempName; |
|
char *nodeName = node->GetName(); |
|
char *roomName = nil; |
|
TSTR sdata; |
|
hsStringTokenizer toker; |
|
if (gUserPropMgr.GetUserPropString(node, "Rooms", sdata)) |
|
{ |
|
toker.Reset(sdata, fTagSeps); |
|
roomName = toker.next(); |
|
} |
|
|
|
if (fSuppressMangling) |
|
{ |
|
return hsStrcpy(mangName, nodeName); |
|
} |
|
|
|
if (('.' == nodeName[0])&&('.' == nodeName[1])) |
|
tempName = (nodeName + 2); |
|
else if (!*nodeName |
|
|| strstr(nodeName, "..") |
|
|| IsReservedKeyword(nodeName) |
|
) |
|
tempName = nodeName; |
|
else if (roomName && *roomName) |
|
tempName = plFormat("{}..{}", roomName, nodeName); |
|
else |
|
tempName = plFormat("{}..{}", defRoom, nodeName); |
|
|
|
return hsStrcpy(mangName, tempName.c_str()); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
char *hsConverterUtils::MangleRefWithRoom(char *mangName, const char *nodeName, const char* roomName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::MangleRefWithRoom"); |
|
|
|
//hsAssert(nodeName && roomName, "No node or room name in hsConverterUtils::MangleRefWithRoom."); |
|
if(!(nodeName && roomName)) |
|
{ |
|
fErrorMsg->Set(true, "Mangle Room Reference Error", "No node or room name in hsConverterUtils::MangleRefWithRoom.").Show(); |
|
fErrorMsg->Set(); |
|
return mangName; |
|
} |
|
|
|
if (!*nodeName) |
|
return hsStrcpy(mangName,nodeName); |
|
|
|
// doesn't want to be mangled |
|
if (('.' == nodeName[0])&&('.' == nodeName[1])) |
|
return hsStrcpy(mangName, nodeName + 2); |
|
|
|
// already mangled or reserved |
|
if (strstr(nodeName, "..") || IsReservedKeyword(nodeName)) |
|
return hsStrcpy(mangName, nodeName); |
|
|
|
sprintf(mangName, "%s..%s", roomName, nodeName); |
|
return mangName; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
|
|
INode* hsConverterUtils::FindINodeFromMangledName(const char* mangName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::FindINodeFromMangledName"); |
|
|
|
if( !(mangName && *mangName) ) |
|
return nil; |
|
|
|
const char* nodeName = mangName; |
|
|
|
char* p; |
|
while( p = strstr((char*)nodeName, "..") ) |
|
{ |
|
nodeName = p + 2; |
|
} |
|
if( !(nodeName && *nodeName) ) |
|
nodeName = mangName; |
|
|
|
return GetINodeByName(nodeName); |
|
hsGuardEnd; |
|
} |
|
|
|
INode* hsConverterUtils::FindINodeFromKeyedObject(hsKeyedObject* obj) |
|
{ |
|
hsGuardBegin("hsConverterUtils::FindINodeFromKeyedObject"); |
|
|
|
INode* retVal = FindINodeFromMangledName(obj->GetKeyName().c_str()); |
|
if( retVal ) |
|
return (retVal); |
|
|
|
/* No more other Keys plasma 2.0 |
|
int i; |
|
for( i = 0; i < obj->GetNumOtherKeys(); i++ ) |
|
{ |
|
retVal = FindINodeFromMangledName(obj->GetOtherKey(i)->GetName()); |
|
if( retVal ) |
|
return retVal; |
|
} |
|
*/ |
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
// Uses MangleRef so all mangling happens in one place. Compares the name with a mangled version of it |
|
bool hsConverterUtils::IsMangled(const char *name) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IsMangled"); |
|
|
|
char mang[255]; |
|
return !strcmp(name,MangleReference(mang,name)); |
|
hsGuardEnd; |
|
} |
|
|
|
// Undoes the process of mangling. This includes taking a "name" back to "..name" |
|
char *hsConverterUtils::UnMangleReference(char *dest, const char *name) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IsMangled"); |
|
|
|
char *u = strstr((char*)name,".."); |
|
if (u) |
|
{ |
|
u+=2; |
|
strcpy(dest,u); |
|
} |
|
else if (!IsMangled(name)) |
|
{ |
|
strcpy(dest,".."); |
|
strcat(dest,name); |
|
} |
|
else |
|
{ |
|
strcpy(dest,name); |
|
} |
|
|
|
return dest; |
|
hsGuardEnd; |
|
} |
|
|
|
// Similar to UnMangle but doesn't take "name" back to "..name" |
|
char* hsConverterUtils::StripMangledReference(char* dest, const char* name) |
|
{ |
|
hsGuardBegin("hsConverterUtils::StripMangledReference"); |
|
|
|
char *u = strstr((char*)name,".."); |
|
if (u) |
|
{ |
|
u+=2; |
|
strcpy(dest,u); |
|
} |
|
else |
|
{ |
|
strcpy(dest,name); |
|
} |
|
|
|
return dest; |
|
hsGuardEnd; |
|
} |
|
|
|
int32_t hsConverterUtils::FindNamedSelSetFromName(const char *name) |
|
{ |
|
hsGuardBegin("hsConverterUtils::FindNamedSelSetFromName"); |
|
|
|
#if MAX_VERSION_MAJOR <= 12 |
|
for (int32_t i=0; i<fInterface->GetNumNamedSelSets(); i++) |
|
{ |
|
if (!_stricmp(name, fInterface->GetNamedSelSetName(i))) |
|
return (i); |
|
} |
|
#else |
|
INamedSelectionSetManager* selSetMgr = INamedSelectionSetManager::GetInstance(); |
|
for (int32_t i=0; i<selSetMgr->GetNumNamedSelSets(); i++) |
|
{ |
|
if (!_stricmp(name, selSetMgr->GetNamedSelSetName(i))) |
|
return (i); |
|
} |
|
#endif |
|
|
|
return (-1); |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsConverterUtils::IsInstanced(Object* maxObject) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IsInstanced"); |
|
|
|
if (!maxObject) |
|
{ |
|
return false; |
|
} |
|
|
|
ObjectInstancedEnumProc instProc; |
|
ENUMDEPENDENTS(maxObject, &instProc); |
|
|
|
return (instProc.GetInstanceCount() > 1); |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
INode* hsConverterUtils::IGetINodeByNameRecur(INode* node, const char* wantName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IGetINodeByNameRecur"); |
|
|
|
if (!node || !node->GetName()) |
|
return nil; |
|
|
|
char* nodeName=node->GetName(); |
|
if (!_stricmp(nodeName, wantName)) |
|
return node; |
|
|
|
// Process children |
|
int num = node->NumberOfChildren(); |
|
int i; |
|
for(i=0; i<num; i++) |
|
{ |
|
INode* ret; |
|
if ((ret=IGetINodeByNameRecur(node->GetChildNode(i), wantName))) |
|
return ret; |
|
} |
|
|
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// Matches name against node's name, case-insensitive, |
|
// |
|
INode* hsConverterUtils::GetINodeByName(const char* name, bool caseSensitive) |
|
{ |
|
hsGuardBegin("hsConverterUtils::GetINodeByName"); |
|
|
|
if (!name) |
|
{ |
|
return nil; |
|
} |
|
|
|
if (fNodeSearchCache) |
|
{ |
|
CacheNode cNode(name); |
|
cNode.SetCaseSensitive(caseSensitive); |
|
hsHashTableIterator<CacheNode> it = fNodeSearchCache->find(cNode); |
|
return it->GetNode(); |
|
} |
|
|
|
//hsAssert(fInterface, "nil fInterface in hsConverterUtils::GetINodeByName()"); |
|
if(!fInterface) |
|
{ |
|
fErrorMsg->Set(true, "Get INode by Name Error", "nil fInterface in hsConverterUtils::GetINodeByName()").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
|
|
if (caseSensitive) |
|
{ |
|
return fInterface->GetINodeByName(name); |
|
} |
|
|
|
return IGetINodeByNameRecur(fInterface->GetRootNode(), name); |
|
hsGuardEnd; |
|
} |
|
|
|
void hsConverterUtils::CreateNodeSearchCache() |
|
{ |
|
if (!fNodeSearchCache) |
|
{ |
|
fNodeSearchCache = new hsHashTable<CacheNode>(); |
|
} |
|
fNodeSearchCache->clear(); |
|
|
|
IBuildNodeSearchCacheRecur(fInterface->GetRootNode()); |
|
} |
|
|
|
void hsConverterUtils::DestroyNodeSearchCache() |
|
{ |
|
delete fNodeSearchCache; |
|
fNodeSearchCache = nil; |
|
} |
|
|
|
void hsConverterUtils::IBuildNodeSearchCacheRecur(INode* node) |
|
{ |
|
if (!node || !node->GetName()) |
|
return ; |
|
|
|
CacheNode cNode(node); |
|
fNodeSearchCache->insert(cNode); |
|
|
|
// Process children |
|
int num = node->NumberOfChildren(); |
|
int i; |
|
for(i=0; i<num; i++) |
|
{ |
|
IBuildNodeSearchCacheRecur(node->GetChildNode(i)); |
|
} |
|
} |
|
|
|
uint32_t hsConverterUtils::CacheNode::GetHash() const |
|
{ |
|
const char* k = GetName(); |
|
int len = k ? strlen(k) : 0; |
|
|
|
int h; |
|
for (h=len; len--;) |
|
{ |
|
h = ((h<<5)^(h>>27))^tolower(*k++); |
|
} |
|
return h; |
|
} |
|
|
|
bool hsConverterUtils::CacheNode::operator==(const CacheNode& other) const |
|
{ |
|
const char* k1 = GetName(); |
|
const char* k2 = other.GetName(); |
|
if (other.fCaseSensitive || fCaseSensitive) |
|
return !strcmp(k1,k2); |
|
else |
|
return !_stricmp(k1,k2); |
|
}
|
|
|