// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "nel/ligo/primitive_class.h"
#include "nel/ligo/primitive.h"
#include "nel/ligo/ligo_config.h"
#include "nel/misc/i_xml.h"
#include "nel/misc/path.h"
using namespace std;
using namespace NLMISC;
using namespace NLLIGO;
// ***************************************************************************
CPrimitiveClass::CPrimitiveClass()
{
}
// ***************************************************************************
bool ReadFloat (const char *propName, float &result, xmlNodePtr xmlNode)
{
string value;
if (CIXml::getPropertyString (value, xmlNode, propName))
{
result = (float)atof (value.c_str ());
return true;
}
return false;
}
// ***************************************************************************
bool ReadInt (const char *propName, int &result, xmlNodePtr xmlNode)
{
string value;
if (CIXml::getPropertyString (value, xmlNode, propName))
{
result = atoi (value.c_str ());
return true;
}
return false;
}
// ***************************************************************************
bool ReadBool (const char *propName, bool &result, xmlNodePtr xmlNode, const char *filename, CLigoConfig &config)
{
string str;
if (CIXml::getPropertyString (str, xmlNode, propName))
{
if (str == "true")
result = true;
else if (str == "false")
result = false;
else
{
config.syntaxError (filename, xmlNode, "Unknown (%s) parameter (%s), should be false or true", propName, str.c_str ());
return false;
}
return true;
}
return false;
}
// ***************************************************************************
bool ReadColor (CRGBA &color, xmlNodePtr node)
{
// Read the color
float r = DEFAULT_PRIMITIVE_COLOR.R;
float g = DEFAULT_PRIMITIVE_COLOR.G;
float b = DEFAULT_PRIMITIVE_COLOR.B;
float a = DEFAULT_PRIMITIVE_COLOR.A;
// Read the value
if (!ReadFloat ("R", r, node))
return false;
if (!ReadFloat ("G", g, node))
return false;
if (!ReadFloat ("B", b, node))
return false;
if (!ReadFloat ("A", a, node))
a = 255;
// Clamp
clamp (r, 0.f, 255.f);
clamp (g, 0.f, 255.f);
clamp (b, 0.f, 255.f);
clamp (a, 0.f, 255.f);
// Set
color.set((uint8)r, (uint8)g, (uint8)b, (uint8)a);
return true;
}
// ***************************************************************************
bool ReadChild (CPrimitiveClass::CChild &child, xmlNodePtr childNode, const char *filename, bool _static, CLigoConfig &config)
{
// Read the class name
if (!config.getPropertyString (child.ClassName, filename, childNode, "CLASS_NAME"))
goto failed;
// Read the name
if (!_static || config.getPropertyString (child.Name, filename, childNode, "NAME"))
{
// Read the parameters
child.Parameters.reserve (CIXml::countChildren (childNode, "PARAMETER"));
for ( xmlNodePtr childParamNode = CIXml::getFirstChildNode (childNode, "PARAMETER");
childParamNode != NULL;
childParamNode = CIXml::getNextChildNode (childParamNode, "PARAMETER"))
{
// Add a static child
child.Parameters.push_back (CPrimitiveClass::CInitParameters ());
// Child ref
CPrimitiveClass::CInitParameters &childParam = child.Parameters.back ();
// Read the class name
if (!config.getPropertyString (childParam.Name, filename, childParamNode, "NAME"))
goto failed;
// Read the parameters
uint defaultId = 0;
childParam.DefaultValue.resize (CIXml::countChildren (childParamNode, "DEFAULT_VALUE"));
for ( xmlNodePtr childParamValueNode = CIXml::getFirstChildNode (childParamNode, "DEFAULT_VALUE");
childParamValueNode != NULL;
childParamValueNode = CIXml::getNextChildNode (childParamValueNode, "DEFAULT_VALUE"))
{
// Gen id flag
childParam.DefaultValue[defaultId].GenID = false;
// Read the gen id flag
string value;
if (CIXml::getPropertyString (value, childParamValueNode, "GEN_ID") && (value != "false"))
{
childParam.DefaultValue[defaultId].GenID = true;
}
else
{
if (!config.getPropertyString (value, filename, childParamValueNode, "VALUE"))
goto failed;
childParam.DefaultValue[defaultId].Name = value;
}
defaultId++;
}
}
// Ok
return true;
}
failed:
return false;
}
// ***************************************************************************
bool CPrimitiveClass::read (xmlNodePtr primitiveNode,
const char *filename,
const char *className,
std::set &contextStrings,
std::map &contextFilesLookup,
CLigoConfig &config,
bool parsePrimitiveComboContent)
{
// init default parameters
AutoInit = false;
Deletable = true;
FileExtension = "";
FileType = "";
Collision = false;
LinkBrothers = false;
ShowArrow = true;
Numberize = true;
Visible = true;
// read parent class properties
string parentClass;
if (CIXml::getPropertyString (parentClass, primitiveNode, "PARENT_CLASS"))
{
const CPrimitiveClass *parent = config.getPrimitiveClass(parentClass.c_str());
if (parent == NULL)
{
config.syntaxError (filename, primitiveNode, "Can't find parent class (%s) for class (%s)", parentClass.c_str (), className);
return false;
}
// copy all the properties
*this = *parent;
}
// The name
Name = className;
// Read the type
std::string type;
if (!config.getPropertyString (type, filename, primitiveNode, "TYPE"))
goto failed;
// Good type ?
if (type == "node")
Type = Node;
else if (type == "point")
Type = Point;
else if (type == "path")
Type = Path;
else if (type == "zone")
Type = Zone;
else if (type == "bitmap")
Type = Bitmap;
else if (type == "alias")
Type = Alias;
else
{
config.syntaxError (filename, primitiveNode, "Unknown primitive type (%s)", type.c_str ());
goto failed;
}
// Read the color
ReadColor (Color, primitiveNode);
// Autoinit
ReadBool ("AUTO_INIT", AutoInit, primitiveNode, filename, config);
// Deletable
ReadBool ("DELETABLE", Deletable, primitiveNode, filename, config);
// File extension
CIXml::getPropertyString (FileExtension, primitiveNode, "FILE_EXTENSION");
// File type
CIXml::getPropertyString (FileType, primitiveNode, "FILE_TYPE");
// Collision
ReadBool ("COLLISION", Collision, primitiveNode, filename, config);
// LinkBrothers
ReadBool ("LINK_BROTHERS", LinkBrothers, primitiveNode, filename, config);
// ShowArrow
ReadBool ("SHOW_ARROW", ShowArrow, primitiveNode, filename, config);
// Numberize when copy the primitive
ReadBool ("NUMBERIZE", Numberize, primitiveNode, filename, config);
// Visible ?
ReadBool ("VISIBLE", Visible, primitiveNode, filename, config);
// Read the parameters
for ( xmlNodePtr paramNode = CIXml::getFirstChildNode (primitiveNode, "PARAMETER");
paramNode != NULL;
paramNode = CIXml::getNextChildNode (paramNode, "PARAMETER"))
{
// Read the property name
if (!config.getPropertyString (type, filename, paramNode, "NAME"))
goto failed;
// look if the parameter is not already defined by the parent class
uint i=0;
while (i::iterator, bool> insertResult =
parameter.ComboValues.insert (std::map::value_type (type, CParameter::CConstStringValue ()));
// The combo value ref
CParameter::CConstStringValue &comboValue = insertResult.first->second;
// Read the values
for ( xmlNodePtr comboValueValueNode = CIXml::getFirstChildNode (comboValueNode, "CONTEXT_VALUE");
comboValueValueNode != NULL;
comboValueValueNode = CIXml::getNextChildNode (comboValueValueNode, "CONTEXT_VALUE"))
{
// Read the value
if (!config.getPropertyString (type, filename, comboValueValueNode, "VALUE"))
goto failed;
comboValue.Values.push_back (type);
}
}
// Read the combo files
for ( xmlNodePtr comboValueNode = CIXml::getFirstChildNode (paramNode, "COMBO_FILES");
comboValueNode != NULL;
comboValueNode = CIXml::getNextChildNode (comboValueNode, "COMBO_FILES"))
{
// Read the context
if (!config.getPropertyString (type, filename, comboValueNode, "CONTEXT_NAME"))
goto failed;
// Read the path to search
string path;
if (CIXml::getPropertyString (path, comboValueNode, "PATH"))
{
if (!parsePrimitiveComboContent)
continue;
// Look for files in the path
std::vector files;
CPath::getPathContent (path, true, false, true, files);
// Not empty ?
if (files.empty ())
continue;
// Add this context
contextStrings.insert (type);
// For each file
for (uint i=0; i::iterator, bool> insertResult =
parameter.ComboValues.insert (std::map::value_type (type, CParameter::CConstStringValue ()));
// The combo value ref
CParameter::CConstStringValue &comboValue = insertResult.first->second;
// Get the filename without extension
string nameWithoutExt = toLower(NLMISC::CFile::getFilenameWithoutExtension (files[i]));
// Add the values
comboValue.Values.push_back (nameWithoutExt);
// Add the value for lookup
contextFilesLookup.insert (map::value_type (nameWithoutExt, files[i]));
}
}
else
{
string primpath;
if (!config.getPropertyString (primpath, filename, comboValueNode, "PRIM_PATH"))
goto failed;
// Add this context
contextStrings.insert (type);
// Add a combo value
pair::iterator, bool> insertResult =
parameter.ComboValues.insert (std::map::value_type (type, CParameter::CConstStringValue ()));
// The combo value ref
CParameter::CConstStringValue &comboValue = insertResult.first->second;
comboValue.PrimitivePath.push_back(primpath);
}
}
// Read parameters default values
uint defaultId = 0;
parameter.DefaultValue.resize (CIXml::countChildren (paramNode, "DEFAULT_VALUE"));
for ( xmlNodePtr defaultValueNode = CIXml::getFirstChildNode (paramNode, "DEFAULT_VALUE");
defaultValueNode != NULL;
defaultValueNode = CIXml::getNextChildNode (defaultValueNode, "DEFAULT_VALUE"))
{
// Gen id flag
parameter.DefaultValue[defaultId].GenID = false;
// Read the gen id flag
string value;
if (CIXml::getPropertyString (value, defaultValueNode, "GEN_ID") && (value != "false"))
{
parameter.DefaultValue[defaultId].GenID = true;
}
else
{
if (!config.getPropertyString (value, filename, defaultValueNode, "VALUE"))
goto failed;
parameter.DefaultValue[defaultId].Name = value;
}
defaultId++;
}
}
// Read static children
StaticChildren.reserve (StaticChildren.size() + CIXml::countChildren (primitiveNode, "STATIC_CHILD"));
for ( xmlNodePtr childrenNode = CIXml::getFirstChildNode (primitiveNode, "STATIC_CHILD");
childrenNode != NULL;
childrenNode = CIXml::getNextChildNode (childrenNode, "STATIC_CHILD"))
{
// Add a static child
StaticChildren.push_back (CChild ());
// Child ref
CChild &child = StaticChildren.back ();
// Read the child
if (!ReadChild (child, childrenNode, filename, true, config))
goto failed;
}
// Read dynamic children
DynamicChildren.reserve (DynamicChildren.size() + CIXml::countChildren (primitiveNode, "DYNAMIC_CHILD"));
for ( xmlNodePtr childrenNode = CIXml::getFirstChildNode (primitiveNode, "DYNAMIC_CHILD");
childrenNode != NULL;
childrenNode = CIXml::getNextChildNode (childrenNode, "DYNAMIC_CHILD"))
{
// Add a static child
DynamicChildren.push_back (CChild ());
// Child ref
CChild &child = DynamicChildren.back ();
// Read the child
if (!ReadChild (child, childrenNode, filename, false, config))
goto failed;
}
// Read generated children
GeneratedChildren.reserve (GeneratedChildren.size() + CIXml::countChildren (primitiveNode, "GENERATED_CHILD"));
for ( xmlNodePtr childrenNode = CIXml::getFirstChildNode (primitiveNode, "GENERATED_CHILD");
childrenNode != NULL;
childrenNode = CIXml::getNextChildNode (childrenNode, "GENERATED_CHILD"))
{
// Add a static child
GeneratedChildren.push_back (CChild ());
// Child ref
CChild &child = GeneratedChildren.back ();
// Read the child
if (!ReadChild (child, childrenNode, filename, false, config))
goto failed;
}
return true;
failed:
return false;
}
// ***************************************************************************
CPrimitiveClass::CParameter::CParameter (const NLLIGO::IProperty &property, const char *propertyName)
{
Name = propertyName;
Filename = false;
Visible = true;
Type = (typeid (property) == typeid (CPropertyString)) ? CPrimitiveClass::CParameter::String : CPrimitiveClass::CParameter::StringArray;
}
// ***************************************************************************
// CPrimitiveClass::CParameter
// ***************************************************************************
bool CPrimitiveClass::CParameter::operator== (const CParameter &other) const
{
return (Type == other.Type) &&
(Name == other.Name) &&
(Visible == other.Visible) &&
(Filename == other.Filename) &&
(ComboValues == other.ComboValues) &&
(DefaultValue == other.DefaultValue);
}
// ***************************************************************************
bool CPrimitiveClass::CParameter::operator< (const CParameter &other) const
{
return (Name < other.Name) ? true : (Name > other.Name) ? false :
(Type < other.Type) ? true : (Type > other.Type) ? false :
(Visible < other.Visible) ? true : (Visible > other.Visible) ? false :
(Filename < other.Filename) ? true : (Filename > other.Filename) ? false :
(ComboValues < other.ComboValues) ? true : (ComboValues > other.ComboValues) ? false :
(DefaultValue < other.DefaultValue) ? true : (DefaultValue > other.DefaultValue) ? false :
false;
}
// ***************************************************************************
// CPrimitiveClass::CParameter::CConstStringValue
// ***************************************************************************
bool CPrimitiveClass::CParameter::CConstStringValue::operator== (const CConstStringValue &other) const
{
return Values == other.Values;
}
// ***************************************************************************
bool CPrimitiveClass::CParameter::CConstStringValue::operator< (const CConstStringValue &other) const
{
return Values < other.Values;
}
void CPrimitiveClass::CParameter::CConstStringValue::appendFilePath(std::vector &pathList) const
{
pathList.insert(pathList.end(), Values.begin(), Values.end());
}
void CPrimitiveClass::CParameter::CConstStringValue::appendPrimPath(std::vector &pathList, const std::vector &relativePrimPaths) const
{
std::set relativePrimPathString;
for (std::vector::const_iterator it=relativePrimPaths.begin(), itEnd=relativePrimPaths.end(); it!=itEnd;++it)
{
const uint nbChilds=(*it)->getNumChildren();
for (uint childIndex=0;childIndexgetChild(child,childIndex)
|| !child )
continue;
std::string str;
if (child->getPropertyByName("name", str))
relativePrimPathString.insert(str);
}
}
pathList.insert(pathList.end(), relativePrimPathString.begin(), relativePrimPathString.end());
}
void CPrimitiveClass::CParameter::CConstStringValue::getPrimitivesForPrimPath (std::vector &relativePrimPaths, const std::vector &startPrimPath) const
{
for (uint i=0; i relativePrimPath;
for (uint locIndex=0;locIndexgetPrimitive(PrimitivePath[i]);
if (cursor)
relativePrimPath.insert(cursor);
}
if (relativePrimPath.size()==1)
relativePrimPaths.push_back(*relativePrimPath.begin());
}
}
// ***************************************************************************
bool CPrimitiveClass::CParameter::translateAutoname (std::string &result, const IPrimitive &primitive, const CPrimitiveClass &primitiveClass) const
{
result = "";
string::size_type strBegin = 0;
string::size_type strEnd = 0;
while (strBegin != Autoname.size())
{
strEnd = Autoname.find ('$', strBegin);
if (strEnd == string::npos)
{
strEnd = Autoname.size();
result += Autoname.substr (strBegin, strEnd-strBegin);
}
else
{
// Copy the remaining string
result += Autoname.substr (strBegin, strEnd-strBegin);
if (strEnd != Autoname.size())
{
strBegin = strEnd+1;
strEnd = Autoname.find ('$', strBegin);
if (strEnd == string::npos)
strEnd = Autoname.size();
else
{
string keyWord = Autoname.substr (strBegin, strEnd-strBegin);
// Loop for the parameter
uint i;
for (i=0; i(prop);
// Is a string ?
if (_string)
{
if (!(_string->String.empty()))
{
result += _string->String;
break;
}
}
else
{
// Try an array
const CPropertyStringArray *array = dynamic_cast(prop);
// Is an array ?
if (array)
{
if (!(array->StringArray.empty()))
{
uint i;
for (i=0; iStringArray.size()-1; i++)
result += array->StringArray[i] + "\n";
result += array->StringArray[i];
break;
}
}
}
}
}
// Get its default value
std::string result2;
if (primitiveClass.Parameters[i].getDefaultValue (result2, primitive, primitiveClass))
{
result += result2;
break;
}
}
}
strEnd++;
}
}
}
strBegin = strEnd;
}
return true;
}
// ***************************************************************************
bool CPrimitiveClass::CParameter::getDefaultValue (std::string &result, const IPrimitive &primitive, const CPrimitiveClass &primitiveClass, std::string *fromWhere) const
{
result = "";
if (!Autoname.empty())
{
if (fromWhere)
*fromWhere = "Autoname value : "+Autoname;
return translateAutoname (result, primitive, primitiveClass);
}
else
{
if (fromWhere)
*fromWhere = "Default value";
if (!DefaultValue.empty())
result = DefaultValue[0].Name;
}
return true;
}
// ***************************************************************************
bool CPrimitiveClass::CParameter::getDefaultValue (std::vector &result, const IPrimitive &primitive, const CPrimitiveClass &primitiveClass, std::string * /* fromWhere */) const
{
if (!Autoname.empty())
{
string temp;
if (translateAutoname (temp, primitive, primitiveClass))
{
result.clear ();
if (!temp.empty())
{
string tmp;
uint i;
for (i=0; i