1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "nel/ligo/primitive_class.h"
22 #include "nel/ligo/primitive.h"
23 #include "nel/ligo/ligo_config.h"
24 #include "nel/misc/i_xml.h"
25 #include "nel/misc/path.h"
28 using namespace NLMISC
;
29 using namespace NLLIGO
;
31 // ***************************************************************************
33 CPrimitiveClass::CPrimitiveClass()
37 // ***************************************************************************
39 bool ReadFloat (const char *propName
, float &result
, xmlNodePtr xmlNode
)
42 if (CIXml::getPropertyString (value
, xmlNode
, propName
))
44 NLMISC::fromString(value
, result
);
50 // ***************************************************************************
52 bool ReadInt (const char *propName
, int &result
, xmlNodePtr xmlNode
)
55 if (CIXml::getPropertyString (value
, xmlNode
, propName
))
57 result
= atoi (value
.c_str ());
63 // ***************************************************************************
65 bool ReadBool (const char *propName
, bool &result
, xmlNodePtr xmlNode
, const char *filename
, CLigoConfig
&config
)
68 if (CIXml::getPropertyString (str
, xmlNode
, propName
))
72 else if (str
== "false")
76 config
.syntaxError (filename
, xmlNode
, "Unknown (%s) parameter (%s), should be false or true", propName
, str
.c_str ());
84 // ***************************************************************************
86 bool ReadColor (CRGBA
&color
, xmlNodePtr node
)
89 float r
= DEFAULT_PRIMITIVE_COLOR
.R
;
90 float g
= DEFAULT_PRIMITIVE_COLOR
.G
;
91 float b
= DEFAULT_PRIMITIVE_COLOR
.B
;
92 float a
= DEFAULT_PRIMITIVE_COLOR
.A
;
95 if (!ReadFloat ("R", r
, node
))
97 if (!ReadFloat ("G", g
, node
))
99 if (!ReadFloat ("B", b
, node
))
101 if (!ReadFloat ("A", a
, node
))
105 clamp (r
, 0.f
, 255.f
);
106 clamp (g
, 0.f
, 255.f
);
107 clamp (b
, 0.f
, 255.f
);
108 clamp (a
, 0.f
, 255.f
);
111 color
.set((uint8
)r
, (uint8
)g
, (uint8
)b
, (uint8
)a
);
115 // ***************************************************************************
117 bool ReadChild (CPrimitiveClass::CChild
&child
, xmlNodePtr childNode
, const char *filename
, bool _static
, CLigoConfig
&config
)
119 // Read the class name
120 if (!config
.getPropertyString (child
.ClassName
, filename
, childNode
, "CLASS_NAME"))
124 if (!_static
|| config
.getPropertyString (child
.Name
, filename
, childNode
, "NAME"))
126 // Read the parameters
127 child
.Parameters
.reserve (CIXml::countChildren (childNode
, "PARAMETER"));
128 for ( xmlNodePtr childParamNode
= CIXml::getFirstChildNode (childNode
, "PARAMETER");
129 childParamNode
!= NULL
;
130 childParamNode
= CIXml::getNextChildNode (childParamNode
, "PARAMETER"))
132 // Add a static child
133 child
.Parameters
.push_back (CPrimitiveClass::CInitParameters ());
136 CPrimitiveClass::CInitParameters
&childParam
= child
.Parameters
.back ();
138 // Read the class name
139 if (!config
.getPropertyString (childParam
.Name
, filename
, childParamNode
, "NAME"))
142 // Read the parameters
144 childParam
.DefaultValue
.resize (CIXml::countChildren (childParamNode
, "DEFAULT_VALUE"));
145 for ( xmlNodePtr childParamValueNode
= CIXml::getFirstChildNode (childParamNode
, "DEFAULT_VALUE");
146 childParamValueNode
!= NULL
;
147 childParamValueNode
= CIXml::getNextChildNode (childParamValueNode
, "DEFAULT_VALUE"))
150 childParam
.DefaultValue
[defaultId
].GenID
= false;
152 // Read the gen id flag
154 if (CIXml::getPropertyString (value
, childParamValueNode
, "GEN_ID") && (value
!= "false"))
156 childParam
.DefaultValue
[defaultId
].GenID
= true;
160 if (!config
.getPropertyString (value
, filename
, childParamValueNode
, "VALUE"))
163 childParam
.DefaultValue
[defaultId
].Name
= value
;
176 // ***************************************************************************
178 bool CPrimitiveClass::read (xmlNodePtr primitiveNode
,
179 const char *filename
,
180 const char *className
,
181 std::set
<std::string
> &contextStrings
,
182 std::map
<std::string
, std::string
> &contextFilesLookup
,
184 bool parsePrimitiveComboContent
)
186 // init default parameters
189 FileExtension
.clear();
192 LinkBrothers
= false;
197 // read parent class properties
199 if (CIXml::getPropertyString (parentClass
, primitiveNode
, "PARENT_CLASS"))
201 const CPrimitiveClass
*parent
= config
.getPrimitiveClass(parentClass
.c_str());
205 config
.syntaxError (filename
, primitiveNode
, "Can't find parent class (%s) for class (%s)", parentClass
.c_str (), className
);
209 // copy all the properties
218 if (!config
.getPropertyString (type
, filename
, primitiveNode
, "TYPE"))
224 else if (type
== "point")
226 else if (type
== "path")
228 else if (type
== "zone")
230 else if (type
== "bitmap")
232 else if (type
== "alias")
236 config
.syntaxError (filename
, primitiveNode
, "Unknown primitive type (%s)", type
.c_str ());
241 ReadColor (Color
, primitiveNode
);
244 ReadBool ("AUTO_INIT", AutoInit
, primitiveNode
, filename
, config
);
247 ReadBool ("DELETABLE", Deletable
, primitiveNode
, filename
, config
);
250 CIXml::getPropertyString (FileExtension
, primitiveNode
, "FILE_EXTENSION");
253 CIXml::getPropertyString (FileType
, primitiveNode
, "FILE_TYPE");
256 ReadBool ("COLLISION", Collision
, primitiveNode
, filename
, config
);
259 ReadBool ("LINK_BROTHERS", LinkBrothers
, primitiveNode
, filename
, config
);
262 ReadBool ("SHOW_ARROW", ShowArrow
, primitiveNode
, filename
, config
);
264 // Numberize when copy the primitive
265 ReadBool ("NUMBERIZE", Numberize
, primitiveNode
, filename
, config
);
268 ReadBool ("VISIBLE", Visible
, primitiveNode
, filename
, config
);
270 // Read the parameters
271 for ( xmlNodePtr paramNode
= CIXml::getFirstChildNode (primitiveNode
, "PARAMETER");
273 paramNode
= CIXml::getNextChildNode (paramNode
, "PARAMETER"))
275 // Read the property name
276 if (!config
.getPropertyString (type
, filename
, paramNode
, "NAME"))
279 // look if the parameter is not already defined by the parent class
281 while (i
<Parameters
.size())
283 if (Parameters
[i
].Name
== type
)
285 // the param already exist, remove parent param
286 Parameters
.erase(Parameters
.begin() + i
);
293 Parameters
.push_back (CParameter ());
296 CParameter
¶meter
= Parameters
.back ();
299 parameter
.Name
= type
;
302 if (!config
.getPropertyString (type
, filename
, paramNode
, "TYPE"))
306 if (type
== "boolean")
307 parameter
.Type
= CParameter::Boolean
;
308 else if (type
== "const_string")
309 parameter
.Type
= CParameter::ConstString
;
310 else if (type
== "string")
311 parameter
.Type
= CParameter::String
;
312 else if (type
== "string_array")
313 parameter
.Type
= CParameter::StringArray
;
314 else if (type
== "const_string_array")
315 parameter
.Type
= CParameter::ConstStringArray
;
318 config
.syntaxError (filename
, paramNode
, "Unknown primitive parameter type (%s)", type
.c_str ());
323 parameter
.Visible
= true;
324 ReadBool ("VISIBLE", parameter
.Visible
, paramNode
, filename
, config
);
327 parameter
.Filename
= false;
328 ReadBool ("FILENAME", parameter
.Filename
, paramNode
, filename
, config
);
331 parameter
.Lookup
= false;
332 ReadBool ("LOOKUP", parameter
.Lookup
, paramNode
, filename
, config
);
334 // Read only primitive
335 parameter
.ReadOnly
= false;
336 ReadBool ("READ_ONLY", parameter
.ReadOnly
, paramNode
, filename
, config
);
339 parameter
.Editable
= false;
340 ReadBool ("EDITABLE", parameter
.Editable
, paramNode
, filename
, config
);
342 // sort combo box entries
343 parameter
.SortEntries
= false;
344 ReadBool ("SORT_ENTRIES", parameter
.SortEntries
, paramNode
, filename
, config
);
346 // Display horizontal scroller in multi-line edit box
347 parameter
.DisplayHS
= false;
348 ReadBool ("SHOW_HS", parameter
.DisplayHS
, paramNode
, filename
, config
);
351 parameter
.WidgetHeight
= 100;
353 if (ReadInt ("WIDGET_HEIGHT", temp
, paramNode
))
354 parameter
.WidgetHeight
= (uint
)temp
;
356 // Read the file extension
357 parameter
.FileExtension
.clear();
358 CIXml::getPropertyString (parameter
.FileExtension
, paramNode
, "FILE_EXTENSION");
359 parameter
.FileExtension
= toLowerAscii(parameter
.FileExtension
);
361 // Autonaming preference
362 parameter
.Autoname
.clear();
363 CIXml::getPropertyString (parameter
.Autoname
, paramNode
, "AUTONAME");
365 // Read the file extension
366 parameter
.Folder
.clear();
367 CIXml::getPropertyString (parameter
.Folder
, paramNode
, "FOLDER");
368 parameter
.Folder
= toLowerAscii(parameter
.Folder
);
370 // Read the combo values
371 for ( xmlNodePtr comboValueNode
= CIXml::getFirstChildNode (paramNode
, "COMBO_VALUES");
372 comboValueNode
!= NULL
;
373 comboValueNode
= CIXml::getNextChildNode (comboValueNode
, "COMBO_VALUES"))
376 if (!config
.getPropertyString (type
, filename
, comboValueNode
, "CONTEXT_NAME"))
380 contextStrings
.insert (type
);
383 pair
<std::map
<std::string
, CParameter::CConstStringValue
>::iterator
, bool> insertResult
=
384 parameter
.ComboValues
.insert (std::map
<std::string
, CParameter::CConstStringValue
>::value_type (type
, CParameter::CConstStringValue ()));
386 // The combo value ref
387 CParameter::CConstStringValue
&comboValue
= insertResult
.first
->second
;
390 for ( xmlNodePtr comboValueValueNode
= CIXml::getFirstChildNode (comboValueNode
, "CONTEXT_VALUE");
391 comboValueValueNode
!= NULL
;
392 comboValueValueNode
= CIXml::getNextChildNode (comboValueValueNode
, "CONTEXT_VALUE"))
395 if (!config
.getPropertyString (type
, filename
, comboValueValueNode
, "VALUE"))
398 comboValue
.Values
.push_back (type
);
402 // Read the combo files
403 for ( xmlNodePtr comboValueNode
= CIXml::getFirstChildNode (paramNode
, "COMBO_FILES");
404 comboValueNode
!= NULL
;
405 comboValueNode
= CIXml::getNextChildNode (comboValueNode
, "COMBO_FILES"))
408 if (!config
.getPropertyString (type
, filename
, comboValueNode
, "CONTEXT_NAME"))
411 // Read the path to search
413 if (CIXml::getPropertyString (path
, comboValueNode
, "PATH"))
415 if (!parsePrimitiveComboContent
)
418 // Look for files in the path
419 std::vector
<std::string
> files
;
420 CPath::getPathContent (path
, true, false, true, files
);
427 contextStrings
.insert (type
);
430 for (uint i
=0; i
<files
.size (); i
++)
433 if (toLowerAscii(NLMISC::CFile::getExtension (files
[i
])) != parameter
.FileExtension
)
437 pair
<std::map
<std::string
, CParameter::CConstStringValue
>::iterator
, bool> insertResult
=
438 parameter
.ComboValues
.insert (std::map
<std::string
, CParameter::CConstStringValue
>::value_type (type
, CParameter::CConstStringValue ()));
440 // The combo value ref
441 CParameter::CConstStringValue
&comboValue
= insertResult
.first
->second
;
443 // Get the filename without extension
444 string nameWithoutExt
= toLowerAscii(NLMISC::CFile::getFilenameWithoutExtension (files
[i
]));
447 comboValue
.Values
.push_back (nameWithoutExt
);
449 // Add the value for lookup
450 contextFilesLookup
.insert (map
<string
, string
>::value_type (nameWithoutExt
, files
[i
]));
456 if (!config
.getPropertyString (primpath
, filename
, comboValueNode
, "PRIM_PATH"))
460 contextStrings
.insert (type
);
463 pair
<std::map
<std::string
, CParameter::CConstStringValue
>::iterator
, bool> insertResult
=
464 parameter
.ComboValues
.insert (std::map
<std::string
, CParameter::CConstStringValue
>::value_type (type
, CParameter::CConstStringValue ()));
466 // The combo value ref
467 CParameter::CConstStringValue
&comboValue
= insertResult
.first
->second
;
469 comboValue
.PrimitivePath
.push_back(primpath
);
473 // Read parameters default values
475 parameter
.DefaultValue
.resize (CIXml::countChildren (paramNode
, "DEFAULT_VALUE"));
476 for ( xmlNodePtr defaultValueNode
= CIXml::getFirstChildNode (paramNode
, "DEFAULT_VALUE");
477 defaultValueNode
!= NULL
;
478 defaultValueNode
= CIXml::getNextChildNode (defaultValueNode
, "DEFAULT_VALUE"))
481 parameter
.DefaultValue
[defaultId
].GenID
= false;
483 // Read the gen id flag
485 if (CIXml::getPropertyString (value
, defaultValueNode
, "GEN_ID") && (value
!= "false"))
487 parameter
.DefaultValue
[defaultId
].GenID
= true;
491 if (!config
.getPropertyString (value
, filename
, defaultValueNode
, "VALUE"))
493 parameter
.DefaultValue
[defaultId
].Name
= value
;
499 // Read static children
500 StaticChildren
.reserve (StaticChildren
.size() + CIXml::countChildren (primitiveNode
, "STATIC_CHILD"));
501 for ( xmlNodePtr childrenNode
= CIXml::getFirstChildNode (primitiveNode
, "STATIC_CHILD");
502 childrenNode
!= NULL
;
503 childrenNode
= CIXml::getNextChildNode (childrenNode
, "STATIC_CHILD"))
505 // Add a static child
506 StaticChildren
.push_back (CChild ());
509 CChild
&child
= StaticChildren
.back ();
512 if (!ReadChild (child
, childrenNode
, filename
, true, config
))
516 // Read dynamic children
517 DynamicChildren
.reserve (DynamicChildren
.size() + CIXml::countChildren (primitiveNode
, "DYNAMIC_CHILD"));
518 for ( xmlNodePtr childrenNode
= CIXml::getFirstChildNode (primitiveNode
, "DYNAMIC_CHILD");
519 childrenNode
!= NULL
;
520 childrenNode
= CIXml::getNextChildNode (childrenNode
, "DYNAMIC_CHILD"))
522 // Add a static child
523 DynamicChildren
.push_back (CChild ());
526 CChild
&child
= DynamicChildren
.back ();
529 if (!ReadChild (child
, childrenNode
, filename
, false, config
))
533 // Read generated children
534 GeneratedChildren
.reserve (GeneratedChildren
.size() + CIXml::countChildren (primitiveNode
, "GENERATED_CHILD"));
535 for ( xmlNodePtr childrenNode
= CIXml::getFirstChildNode (primitiveNode
, "GENERATED_CHILD");
536 childrenNode
!= NULL
;
537 childrenNode
= CIXml::getNextChildNode (childrenNode
, "GENERATED_CHILD"))
539 // Add a static child
540 GeneratedChildren
.push_back (CChild ());
543 CChild
&child
= GeneratedChildren
.back ();
546 if (!ReadChild (child
, childrenNode
, filename
, false, config
))
555 // ***************************************************************************
557 CPrimitiveClass::CParameter::CParameter (const NLLIGO::IProperty
&property
, const char *propertyName
)
562 Type
= (typeid (property
) == typeid (CPropertyString
)) ? CPrimitiveClass::CParameter::String
: CPrimitiveClass::CParameter::StringArray
;
565 // ***************************************************************************
566 // CPrimitiveClass::CParameter
567 // ***************************************************************************
569 bool CPrimitiveClass::CParameter::operator== (const CParameter
&other
) const
571 return (Type
== other
.Type
) &&
572 (Name
== other
.Name
) &&
573 (Visible
== other
.Visible
) &&
574 (Filename
== other
.Filename
) &&
575 (ComboValues
== other
.ComboValues
) &&
576 (DefaultValue
== other
.DefaultValue
);
579 // ***************************************************************************
581 bool CPrimitiveClass::CParameter::operator< (const CParameter
&other
) const
583 return (Name
< other
.Name
) ? true : (Name
> other
.Name
) ? false :
584 (Type
< other
.Type
) ? true : (Type
> other
.Type
) ? false :
585 (Visible
< other
.Visible
) ? true : (Visible
> other
.Visible
) ? false :
586 (Filename
< other
.Filename
) ? true : (Filename
> other
.Filename
) ? false :
587 (ComboValues
< other
.ComboValues
) ? true : (ComboValues
> other
.ComboValues
) ? false :
588 (DefaultValue
< other
.DefaultValue
) ? true : (DefaultValue
> other
.DefaultValue
) ? false :
592 // ***************************************************************************
593 // CPrimitiveClass::CParameter::CConstStringValue
594 // ***************************************************************************
596 bool CPrimitiveClass::CParameter::CConstStringValue::operator== (const CConstStringValue
&other
) const
598 return Values
== other
.Values
;
601 // ***************************************************************************
603 bool CPrimitiveClass::CParameter::CConstStringValue::operator< (const CConstStringValue
&other
) const
605 return Values
< other
.Values
;
609 void CPrimitiveClass::CParameter::CConstStringValue::appendFilePath(std::vector
<std::string
> &pathList
) const
611 pathList
.insert(pathList
.end(), Values
.begin(), Values
.end());
614 void CPrimitiveClass::CParameter::CConstStringValue::appendPrimPath(std::vector
<std::string
> &pathList
, const std::vector
<const IPrimitive
*> &relativePrimPaths
) const
616 std::set
<std::string
> relativePrimPathString
;
617 for (std::vector
<const IPrimitive
*>::const_iterator it
=relativePrimPaths
.begin(), itEnd
=relativePrimPaths
.end(); it
!=itEnd
;++it
)
619 const uint nbChilds
=(*it
)->getNumChildren();
620 for (uint childIndex
=0;childIndex
<nbChilds
;childIndex
++)
622 const IPrimitive
*child
=NULL
;
623 if ( !(*it
)->getChild(child
,childIndex
)
627 if (child
->getPropertyByName("name", str
))
628 relativePrimPathString
.insert(str
);
632 pathList
.insert(pathList
.end(), relativePrimPathString
.begin(), relativePrimPathString
.end());
635 void CPrimitiveClass::CParameter::CConstStringValue::getPrimitivesForPrimPath (std::vector
<const IPrimitive
*> &relativePrimPaths
, const std::vector
<const IPrimitive
*> &startPrimPath
) const
637 for (uint i
=0; i
<PrimitivePath
.size (); i
++)
639 set
<const IPrimitive
*> relativePrimPath
;
640 for (uint locIndex
=0;locIndex
<startPrimPath
.size();locIndex
++)
642 const IPrimitive
*const cursor
=startPrimPath
[locIndex
]->getPrimitive(PrimitivePath
[i
]);
644 relativePrimPath
.insert(cursor
);
646 if (relativePrimPath
.size()==1)
647 relativePrimPaths
.push_back(*relativePrimPath
.begin());
652 // ***************************************************************************
654 bool CPrimitiveClass::CParameter::translateAutoname (std::string
&result
, const IPrimitive
&primitive
, const CPrimitiveClass
&primitiveClass
) const
657 string::size_type strBegin
= 0;
658 string::size_type strEnd
= 0;
659 while (strBegin
!= Autoname
.size())
661 strEnd
= Autoname
.find ('$', strBegin
);
662 if (strEnd
== string::npos
)
664 strEnd
= Autoname
.size();
665 result
+= Autoname
.substr (strBegin
, strEnd
-strBegin
);
669 // Copy the remaining string
670 result
+= Autoname
.substr (strBegin
, strEnd
-strBegin
);
671 if (strEnd
!= Autoname
.size())
674 strEnd
= Autoname
.find ('$', strBegin
);
675 if (strEnd
== string::npos
)
676 strEnd
= Autoname
.size();
679 string keyWord
= Autoname
.substr (strBegin
, strEnd
-strBegin
);
681 // Loop for the parameter
683 for (i
=0; i
<primitiveClass
.Parameters
.size (); i
++)
685 if (primitiveClass
.Parameters
[i
].Name
== keyWord
)
687 // Get its string value
689 const IProperty
*prop
;
690 if (primitive
.getPropertyByName (keyWord
.c_str(), prop
))
692 // The property has been found ?
696 const CPropertyString
*_string
= dynamic_cast<const CPropertyString
*>(prop
);
701 if (!(_string
->String
.empty()))
703 result
+= _string
->String
;
710 const CPropertyStringArray
*array
= dynamic_cast<const CPropertyStringArray
*>(prop
);
715 if (!(array
->StringArray
.empty()))
718 for (i
=0; i
<array
->StringArray
.size()-1; i
++)
719 result
+= array
->StringArray
[i
] + "\n";
720 result
+= array
->StringArray
[i
];
728 // Get its default value
730 if (primitiveClass
.Parameters
[i
].getDefaultValue (result2
, primitive
, primitiveClass
))
747 // ***************************************************************************
749 bool CPrimitiveClass::CParameter::getDefaultValue (std::string
&result
, const IPrimitive
&primitive
, const CPrimitiveClass
&primitiveClass
, std::string
*fromWhere
) const
752 if (!Autoname
.empty())
755 *fromWhere
= "Autoname value : "+Autoname
;
756 return translateAutoname (result
, primitive
, primitiveClass
);
761 *fromWhere
= "Default value";
762 if (!DefaultValue
.empty())
763 result
= DefaultValue
[0].Name
;
768 // ***************************************************************************
770 bool CPrimitiveClass::CParameter::getDefaultValue (std::vector
<std::string
> &result
, const IPrimitive
&primitive
, const CPrimitiveClass
&primitiveClass
, std::string
* /* fromWhere */) const
772 if (!Autoname
.empty())
775 if (translateAutoname (temp
, primitive
, primitiveClass
))
782 for (i
=0; i
<temp
.size(); i
++)
786 result
.push_back (tmp
);
791 tmp
.push_back(temp
[i
]);
795 result
.push_back (tmp
);
805 result
.resize (DefaultValue
.size());
806 for (i
=0; i
<DefaultValue
.size(); i
++)
807 result
[i
] = DefaultValue
[i
].Name
;
812 // ***************************************************************************