2025-01-07 08:14:30 -08:00
|
|
|
/* Copyright (C) 2025 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2010-01-09 11:20:14 -08:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2010-01-09 11:20:14 -08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2010-01-09 11:20:14 -08:00
|
|
|
* 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
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2010-01-09 11:20:14 -08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
|
|
#include "ParamNode.h"
|
|
|
|
|
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "lib/debug.h"
|
|
|
|
|
#include "lib/path.h"
|
2011-02-17 12:08:20 -08:00
|
|
|
#include "lib/utf8.h"
|
2012-03-15 15:57:08 -07:00
|
|
|
#include "ps/CLogger.h"
|
2010-05-02 13:32:37 -07:00
|
|
|
#include "ps/CStr.h"
|
2021-05-18 04:09:54 -07:00
|
|
|
#include "ps/CStrIntern.h"
|
2010-10-23 12:59:40 -07:00
|
|
|
#include "ps/Filesystem.h"
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "ps/XMB/XMBData.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
#include "ps/XML/Xeromyces.h"
|
2025-01-07 08:14:30 -08:00
|
|
|
#include "scriptinterface/Object.h"
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "scriptinterface/ScriptInterface.h"
|
2021-05-03 09:07:26 -07:00
|
|
|
#include "scriptinterface/ScriptRequest.h"
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "simulation2/system/Component.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2025-08-03 10:37:17 -07:00
|
|
|
#include <algorithm>
|
2023-06-30 12:10:13 -07:00
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
2025-08-03 10:37:17 -07:00
|
|
|
#include <boost/algorithm/string/constants.hpp>
|
2023-06-30 12:10:13 -07:00
|
|
|
#include <boost/algorithm/string/join.hpp>
|
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
2025-08-03 10:37:17 -07:00
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <js/CharacterEncoding.h>
|
|
|
|
|
#include <js/PropertyAndElement.h>
|
|
|
|
|
#include <js/RootingAPI.h>
|
|
|
|
|
#include <js/String.h>
|
|
|
|
|
#include <js/Value.h>
|
|
|
|
|
#include <jsapi.h>
|
2010-01-09 11:20:14 -08:00
|
|
|
#include <sstream>
|
2022-12-04 11:56:12 -08:00
|
|
|
#include <string_view>
|
2025-08-03 10:37:17 -07:00
|
|
|
#include <utility>
|
|
|
|
|
#include <vector>
|
2010-07-03 03:48:44 -07:00
|
|
|
|
2010-02-02 15:01:17 -08:00
|
|
|
static CParamNode g_NullNode(false);
|
|
|
|
|
|
|
|
|
|
CParamNode::CParamNode(bool isOk) :
|
|
|
|
|
m_IsOk(isOk)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 06:02:34 -07:00
|
|
|
void CParamNode::LoadXML(CParamNode& ret, const XMBData& xmb, const wchar_t* sourceIdentifier /*= NULL*/)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2012-05-05 12:22:22 -07:00
|
|
|
ret.ApplyLayer(xmb, xmb.GetRoot(), sourceIdentifier);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2015-06-07 14:56:52 -07:00
|
|
|
void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName)
|
2010-10-23 12:59:40 -07:00
|
|
|
{
|
|
|
|
|
CXeromyces xero;
|
2015-06-07 14:56:52 -07:00
|
|
|
PSRETURN ok = xero.Load(g_VFS, path, validatorName);
|
2010-10-23 12:59:40 -07:00
|
|
|
if (ok != PSRETURN_OK)
|
|
|
|
|
return; // (Xeromyces already logged an error)
|
|
|
|
|
|
2012-05-05 12:22:22 -07:00
|
|
|
LoadXML(ret, xero, path.string().c_str());
|
2010-10-23 12:59:40 -07:00
|
|
|
}
|
|
|
|
|
|
2012-05-05 12:22:22 -07:00
|
|
|
PSRETURN CParamNode::LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier /*=NULL*/)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
|
|
|
|
CXeromyces xero;
|
|
|
|
|
PSRETURN ok = xero.LoadString(xml);
|
|
|
|
|
if (ok != PSRETURN_OK)
|
|
|
|
|
return ok;
|
|
|
|
|
|
2012-05-05 12:22:22 -07:00
|
|
|
ret.ApplyLayer(xero, xero.GetRoot(), sourceIdentifier);
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
|
|
|
return PSRETURN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 06:02:34 -07:00
|
|
|
void CParamNode::ApplyLayer(const XMBData& xmb, const XMBElement& element, const wchar_t* sourceIdentifier /*= NULL*/)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2010-07-21 09:04:17 -07:00
|
|
|
ResetScriptVal();
|
|
|
|
|
|
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 06:02:34 -07:00
|
|
|
std::string name = xmb.GetElementString(element.GetNodeName());
|
2021-04-11 02:23:10 -07:00
|
|
|
CStr value = element.GetText();
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2010-07-02 18:23:23 -07:00
|
|
|
bool hasSetValue = false;
|
|
|
|
|
|
2010-02-12 14:48:16 -08:00
|
|
|
// Look for special attributes
|
2010-04-09 11:43:50 -07:00
|
|
|
int at_disable = xmb.GetAttributeID("disable");
|
|
|
|
|
int at_replace = xmb.GetAttributeID("replace");
|
2017-03-16 12:56:12 -07:00
|
|
|
int at_filtered = xmb.GetAttributeID("filtered");
|
|
|
|
|
int at_merge = xmb.GetAttributeID("merge");
|
2015-12-05 09:02:25 -08:00
|
|
|
int at_op = xmb.GetAttributeID("op");
|
2010-07-02 18:23:23 -07:00
|
|
|
int at_datatype = xmb.GetAttributeID("datatype");
|
2015-12-05 09:02:25 -08:00
|
|
|
enum op {
|
|
|
|
|
INVALID,
|
|
|
|
|
ADD,
|
2019-01-02 06:46:17 -08:00
|
|
|
MUL,
|
|
|
|
|
MUL_ROUND
|
2015-12-05 09:02:25 -08:00
|
|
|
} op = INVALID;
|
2011-07-07 10:05:22 -07:00
|
|
|
bool replacing = false;
|
2017-03-16 12:56:12 -07:00
|
|
|
bool filtering = false;
|
|
|
|
|
bool merging = false;
|
2010-02-12 14:48:16 -08:00
|
|
|
{
|
|
|
|
|
XERO_ITER_ATTR(element, attr)
|
|
|
|
|
{
|
|
|
|
|
if (attr.Name == at_disable)
|
|
|
|
|
{
|
|
|
|
|
m_Childs.erase(name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-04-09 11:43:50 -07:00
|
|
|
else if (attr.Name == at_replace)
|
|
|
|
|
{
|
|
|
|
|
m_Childs.erase(name);
|
2011-07-07 10:05:22 -07:00
|
|
|
replacing = true;
|
2010-04-09 11:43:50 -07:00
|
|
|
}
|
2017-03-16 12:56:12 -07:00
|
|
|
else if (attr.Name == at_filtered)
|
|
|
|
|
{
|
|
|
|
|
filtering = true;
|
|
|
|
|
}
|
|
|
|
|
else if (attr.Name == at_merge)
|
|
|
|
|
{
|
|
|
|
|
if (m_Childs.find(name) == m_Childs.end())
|
|
|
|
|
return;
|
|
|
|
|
merging = true;
|
|
|
|
|
}
|
2015-12-05 09:02:25 -08:00
|
|
|
else if (attr.Name == at_op)
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
if (attr.Value == "add")
|
2016-11-23 05:02:58 -08:00
|
|
|
op = ADD;
|
2021-04-11 02:23:10 -07:00
|
|
|
else if (attr.Value == "mul")
|
2016-11-23 05:02:58 -08:00
|
|
|
op = MUL;
|
2021-04-11 02:23:10 -07:00
|
|
|
else if (attr.Value == "mul_round")
|
2019-01-02 06:46:17 -08:00
|
|
|
op = MUL_ROUND;
|
2016-11-23 05:02:58 -08:00
|
|
|
else
|
|
|
|
|
LOGWARNING("Invalid op '%ls'", attr.Value);
|
2015-12-05 09:02:25 -08:00
|
|
|
}
|
2011-07-07 10:05:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
XERO_ITER_ATTR(element, attr)
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
if (attr.Name == at_datatype && attr.Value == "tokens")
|
2010-07-02 18:23:23 -07:00
|
|
|
{
|
|
|
|
|
CParamNode& node = m_Childs[name];
|
2010-07-03 08:21:50 -07:00
|
|
|
|
|
|
|
|
// Split into tokens
|
2021-04-11 02:23:10 -07:00
|
|
|
std::vector<std::string> oldTokens;
|
|
|
|
|
std::vector<std::string> newTokens;
|
2014-09-12 16:11:03 -07:00
|
|
|
if (!replacing && !node.m_Value.empty()) // ignore the old tokens if replace="" was given
|
2014-09-12 14:45:30 -07:00
|
|
|
boost::algorithm::split(oldTokens, node.m_Value, boost::algorithm::is_space(), boost::algorithm::token_compress_on);
|
2014-09-12 16:11:03 -07:00
|
|
|
if (!value.empty())
|
|
|
|
|
boost::algorithm::split(newTokens, value, boost::algorithm::is_space(), boost::algorithm::token_compress_on);
|
2010-07-02 18:23:23 -07:00
|
|
|
|
2010-07-03 08:21:50 -07:00
|
|
|
// Merge the two lists
|
2021-04-11 02:23:10 -07:00
|
|
|
std::vector<std::string> tokens = oldTokens;
|
2022-12-04 11:56:12 -08:00
|
|
|
for (const std::string& newToken : newTokens)
|
2010-07-02 18:23:23 -07:00
|
|
|
{
|
2022-12-04 11:56:12 -08:00
|
|
|
if (newToken[0] == '-')
|
2012-03-15 15:57:08 -07:00
|
|
|
{
|
2022-12-04 11:56:12 -08:00
|
|
|
std::vector<std::string>::iterator tokenIt =
|
|
|
|
|
std::find(tokens.begin(), tokens.end(),
|
|
|
|
|
std::string_view{newToken}.substr(1));
|
2012-03-15 15:57:08 -07:00
|
|
|
if (tokenIt != tokens.end())
|
|
|
|
|
tokens.erase(tokenIt);
|
|
|
|
|
else
|
2022-12-04 11:56:12 -08:00
|
|
|
{
|
|
|
|
|
const std::string identifier{
|
|
|
|
|
sourceIdentifier ? (" in '" +
|
|
|
|
|
utf8_from_wstring(sourceIdentifier) + "'") : ""};
|
|
|
|
|
LOGWARNING("[ParamNode] Could not remove token "
|
|
|
|
|
"'%s' from node '%s'%s; not present in "
|
|
|
|
|
"list nor inherited (possible typo?)",
|
|
|
|
|
std::string_view{newToken}.substr(1), name,
|
|
|
|
|
identifier);
|
|
|
|
|
}
|
2012-03-15 15:57:08 -07:00
|
|
|
}
|
2010-07-02 18:23:23 -07:00
|
|
|
else
|
2012-03-15 15:57:08 -07:00
|
|
|
{
|
2022-12-04 11:56:12 -08:00
|
|
|
if (std::find(oldTokens.begin(), oldTokens.end(), newToken) == oldTokens.end())
|
|
|
|
|
tokens.push_back(newToken);
|
2012-03-15 15:57:08 -07:00
|
|
|
}
|
2010-07-02 18:23:23 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
node.m_Value = boost::algorithm::join(tokens, " ");
|
2010-07-02 18:23:23 -07:00
|
|
|
hasSetValue = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-02-12 14:48:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
// Add this element as a child node
|
|
|
|
|
CParamNode& node = m_Childs[name];
|
2016-11-23 05:02:58 -08:00
|
|
|
if (op != INVALID)
|
|
|
|
|
{
|
2015-12-05 09:02:25 -08:00
|
|
|
// TODO: Support parsing of data types other than fixed; log warnings in other cases
|
|
|
|
|
fixed oldval = node.ToFixed();
|
2021-04-11 02:23:10 -07:00
|
|
|
fixed mod = fixed::FromString(value);
|
2017-03-16 12:56:12 -07:00
|
|
|
|
2016-11-23 05:02:58 -08:00
|
|
|
switch (op)
|
|
|
|
|
{
|
|
|
|
|
case ADD:
|
2021-04-11 02:23:10 -07:00
|
|
|
node.m_Value = (oldval + mod).ToString();
|
2016-11-23 05:02:58 -08:00
|
|
|
break;
|
|
|
|
|
case MUL:
|
2021-04-11 02:23:10 -07:00
|
|
|
node.m_Value = oldval.Multiply(mod).ToString();
|
2019-01-02 06:46:17 -08:00
|
|
|
break;
|
|
|
|
|
case MUL_ROUND:
|
2021-04-11 02:23:10 -07:00
|
|
|
node.m_Value = fixed::FromInt(oldval.Multiply(mod).ToInt_RoundToNearest()).ToString();
|
2016-11-23 05:02:58 -08:00
|
|
|
break;
|
2020-11-26 14:28:50 -08:00
|
|
|
default:
|
|
|
|
|
break;
|
2016-11-23 05:02:58 -08:00
|
|
|
}
|
|
|
|
|
hasSetValue = true;
|
|
|
|
|
}
|
2017-03-16 12:56:12 -07:00
|
|
|
|
|
|
|
|
if (!hasSetValue && !merging)
|
2011-02-17 12:08:20 -08:00
|
|
|
node.m_Value = value;
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2016-11-15 06:01:15 -08:00
|
|
|
// We also need to reset node's script val, even if it has no children
|
|
|
|
|
// or if the attributes change.
|
|
|
|
|
node.ResetScriptVal();
|
|
|
|
|
|
2017-03-16 12:56:12 -07:00
|
|
|
// For the filtered case
|
|
|
|
|
ChildrenMap childs;
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
// Recurse through the element's children
|
|
|
|
|
XERO_ITER_EL(element, child)
|
|
|
|
|
{
|
2012-05-05 12:22:22 -07:00
|
|
|
node.ApplyLayer(xmb, child, sourceIdentifier);
|
2017-03-16 12:56:12 -07:00
|
|
|
if (filtering)
|
|
|
|
|
{
|
|
|
|
|
std::string childname = xmb.GetElementString(child.GetNodeName());
|
|
|
|
|
if (node.m_Childs.find(childname) != node.m_Childs.end())
|
|
|
|
|
childs[childname] = std::move(node.m_Childs[childname]);
|
|
|
|
|
}
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2017-03-16 12:56:12 -07:00
|
|
|
if (filtering)
|
|
|
|
|
node.m_Childs.swap(childs);
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
// Add the element's attributes, prefixing names with "@"
|
|
|
|
|
XERO_ITER_ATTR(element, attr)
|
|
|
|
|
{
|
2010-04-09 11:43:50 -07:00
|
|
|
// Skip special attributes
|
2017-03-16 12:56:12 -07:00
|
|
|
if (attr.Name == at_replace || attr.Name == at_op || attr.Name == at_merge || attr.Name == at_filtered)
|
2015-12-05 09:02:25 -08:00
|
|
|
continue;
|
2010-04-09 11:43:50 -07:00
|
|
|
// Add any others
|
XMB Improvements, parse JS into XMB, make strings more efficient.
XMB format is bumped to 4, invalidating all cached files. The
differences are:
- element/attribute names are stored after the elements themselves, and
not before. This allows writing XMB data in one pass instead of two.
- names themselves becomes offsets (instead of arbitrary integers),
making getting the string from the int name much more efficient.
XMBFile is renamed to XMBData to clarify that it does not, in fact,
refer to a file on disk.
XMBData::GetElementString is also changed to return a const char*, thus
not creating an std::string. A string_view version is added where
convenient.
The XML->XMB and JS->XMB conversion functions and the corresponding
storage are moved to `ps/XMB`, since that format doesn't particularly
relate to XML. CXeromyces becomes lighter and more focused as a result.
The XML->XMB conversion also benefits from the above streamlining.
Note that in a few cases, string_view gets printed to CLogger via
data(), which is generally not legal, but we know that the strings are
null-terminated here. Our libfmt (version 4) doesn't support
string_view, that would be v5.
Differential Revision: https://code.wildfiregames.com/D3909
This was SVN commit r25375.
2021-05-04 06:02:34 -07:00
|
|
|
const char* attrName(xmb.GetAttributeString(attr.Name));
|
|
|
|
|
node.m_Childs[CStr("@") + attrName].m_Value = attr.Value;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-29 23:34:23 -08:00
|
|
|
const CParamNode& CParamNode::GetOnlyChild() const
|
|
|
|
|
{
|
|
|
|
|
if (m_Childs.empty())
|
|
|
|
|
return g_NullNode;
|
|
|
|
|
|
|
|
|
|
ENSURE(m_Childs.size() == 1);
|
|
|
|
|
return m_Childs.begin()->second;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-02 15:01:17 -08:00
|
|
|
const CParamNode& CParamNode::GetChild(const char* name) const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
|
|
|
|
ChildrenMap::const_iterator it = m_Childs.find(name);
|
|
|
|
|
if (it == m_Childs.end())
|
2010-02-02 15:01:17 -08:00
|
|
|
return g_NullNode;
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CParamNode::IsOk() const
|
|
|
|
|
{
|
|
|
|
|
return m_IsOk;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
const std::wstring CParamNode::ToWString() const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return wstring_from_utf8(m_Value);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
const std::string& CParamNode::ToString() const
|
2010-05-27 16:31:03 -07:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return m_Value;
|
2010-05-27 16:31:03 -07:00
|
|
|
}
|
|
|
|
|
|
2012-04-21 21:04:02 -07:00
|
|
|
const CStrIntern CParamNode::ToUTF8Intern() const
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return CStrIntern(m_Value);
|
2012-04-21 21:04:02 -07:00
|
|
|
}
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
int CParamNode::ToInt() const
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return std::strtol(m_Value.c_str(), nullptr, 10);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2010-05-02 13:32:37 -07:00
|
|
|
fixed CParamNode::ToFixed() const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return fixed::FromString(m_Value);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-23 05:02:58 -08:00
|
|
|
float CParamNode::ToFloat() const
|
2012-04-21 21:04:02 -07:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
return std::strtof(m_Value.c_str(), nullptr);
|
2012-04-21 21:04:02 -07:00
|
|
|
}
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
bool CParamNode::ToBool() const
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
if (m_Value == "true")
|
2010-01-09 11:20:14 -08:00
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CParamNode::ChildrenMap& CParamNode::GetChildren() const
|
|
|
|
|
{
|
|
|
|
|
return m_Childs;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
std::string CParamNode::EscapeXMLString(const std::string& str)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
std::string ret;
|
2010-01-09 11:20:14 -08:00
|
|
|
ret.reserve(str.size());
|
2021-04-11 02:23:10 -07:00
|
|
|
// TODO: would be nice to check actual v1.0 XML codepoints,
|
|
|
|
|
// but our UTF8 validation routines are lacking.
|
2010-01-09 11:20:14 -08:00
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
char c = str[i];
|
2010-01-09 11:20:14 -08:00
|
|
|
switch (c)
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
case '<': ret += "<"; break;
|
|
|
|
|
case '>': ret += ">"; break;
|
|
|
|
|
case '&': ret += "&"; break;
|
|
|
|
|
case '"': ret += """; break;
|
|
|
|
|
case '\t': ret += "	"; break;
|
|
|
|
|
case '\n': ret += " "; break;
|
|
|
|
|
case '\r': ret += " "; break;
|
2010-01-09 11:20:14 -08:00
|
|
|
default:
|
2021-04-11 02:23:10 -07:00
|
|
|
ret += c;
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
std::string CParamNode::ToXMLString() const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
std::stringstream strm;
|
|
|
|
|
ToXMLString(strm);
|
2010-01-09 11:20:14 -08:00
|
|
|
return strm.str();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
void CParamNode::ToXMLString(std::ostream& strm) const
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
|
|
|
|
strm << m_Value;
|
|
|
|
|
|
|
|
|
|
ChildrenMap::const_iterator it = m_Childs.begin();
|
|
|
|
|
for (; it != m_Childs.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
// Skip attributes here (they were handled when the caller output the tag)
|
|
|
|
|
if (it->first.length() && it->first[0] == '@')
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
strm << "<" << it->first;
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
|
|
|
// Output the child's attributes first
|
|
|
|
|
ChildrenMap::const_iterator cit = it->second.m_Childs.begin();
|
|
|
|
|
for (; cit != it->second.m_Childs.end(); ++cit)
|
|
|
|
|
{
|
|
|
|
|
if (cit->first.length() && cit->first[0] == '@')
|
|
|
|
|
{
|
2021-04-11 02:23:10 -07:00
|
|
|
std::string attrname (cit->first.begin()+1, cit->first.end());
|
|
|
|
|
strm << " " << attrname << "=\"" << EscapeXMLString(cit->second.m_Value) << "\"";
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
strm << ">";
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
it->second.ToXMLString(strm);
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2021-04-11 02:23:10 -07:00
|
|
|
strm << "</" << it->first << ">";
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
2010-05-06 17:24:22 -07:00
|
|
|
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
void CParamNode::ToJSVal(const ScriptRequest& rq, bool cacheValue, JS::MutableHandleValue ret) const
|
2010-05-06 17:24:22 -07:00
|
|
|
{
|
2015-01-24 06:46:52 -08:00
|
|
|
if (cacheValue && m_ScriptVal != NULL)
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
2015-01-24 06:46:52 -08:00
|
|
|
ret.set(*m_ScriptVal);
|
2014-11-09 03:08:53 -08:00
|
|
|
return;
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
ConstructJSVal(rq, ret);
|
2011-01-12 04:29:00 -08:00
|
|
|
|
|
|
|
|
if (cacheValue)
|
2025-01-07 08:14:30 -08:00
|
|
|
{
|
|
|
|
|
if (ret.isObject())
|
|
|
|
|
Script::DeepFreezeObject(rq, ret);
|
|
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
m_ScriptVal.reset(new JS::PersistentRootedValue(rq.cx, ret));
|
2025-01-07 08:14:30 -08:00
|
|
|
}
|
2010-05-06 17:24:22 -07:00
|
|
|
}
|
|
|
|
|
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
void CParamNode::ConstructJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const
|
2010-05-06 17:24:22 -07:00
|
|
|
{
|
2011-01-12 04:29:00 -08:00
|
|
|
if (m_Childs.empty())
|
|
|
|
|
{
|
|
|
|
|
// Empty node - map to undefined
|
|
|
|
|
if (m_Value.empty())
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setUndefined();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
|
|
|
|
|
// Just a string
|
2021-04-11 02:23:10 -07:00
|
|
|
JS::RootedString str(rq.cx, JS_NewStringCopyUTF8Z(rq.cx, JS::ConstUTF8CharsZ(m_Value.data(), m_Value.size())));
|
2011-01-12 04:29:00 -08:00
|
|
|
if (str)
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setString(str);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
// TODO: report error
|
2014-11-09 03:08:53 -08:00
|
|
|
ret.setUndefined();
|
|
|
|
|
return;
|
2011-01-12 04:29:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Got child nodes - convert this node into a hash-table-style object:
|
|
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx));
|
2011-01-12 04:29:00 -08:00
|
|
|
if (!obj)
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setUndefined();
|
|
|
|
|
return; // TODO: report error
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue childVal(rq.cx);
|
2011-01-12 04:29:00 -08:00
|
|
|
for (std::map<std::string, CParamNode>::const_iterator it = m_Childs.begin(); it != m_Childs.end(); ++it)
|
|
|
|
|
{
|
2020-11-13 05:18:22 -08:00
|
|
|
it->second.ConstructJSVal(rq, &childVal);
|
|
|
|
|
if (!JS_SetProperty(rq.cx, obj, it->first.c_str(), childVal))
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setUndefined();
|
|
|
|
|
return; // TODO: report error
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the node has a string too, add that as an extra property
|
|
|
|
|
if (!m_Value.empty())
|
|
|
|
|
{
|
2024-10-27 01:42:45 -07:00
|
|
|
JS::RootedString str(rq.cx, JS_NewStringCopyUTF8Z(rq.cx, JS::ConstUTF8CharsZ(m_Value.data(), m_Value.size())));
|
2011-01-12 04:29:00 -08:00
|
|
|
if (!str)
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setUndefined();
|
|
|
|
|
return; // TODO: report error
|
|
|
|
|
}
|
2015-01-24 06:46:52 -08:00
|
|
|
|
2020-11-26 14:28:50 -08:00
|
|
|
JS::RootedValue subChildVal(rq.cx, JS::StringValue(str));
|
|
|
|
|
if (!JS_SetProperty(rq.cx, obj, "_string", subChildVal))
|
2014-11-09 03:08:53 -08:00
|
|
|
{
|
|
|
|
|
ret.setUndefined();
|
|
|
|
|
return; // TODO: report error
|
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
}
|
|
|
|
|
|
2014-11-09 03:08:53 -08:00
|
|
|
ret.setObject(*obj);
|
2010-05-06 17:24:22 -07:00
|
|
|
}
|
2010-07-21 09:04:17 -07:00
|
|
|
|
|
|
|
|
void CParamNode::ResetScriptVal()
|
|
|
|
|
{
|
2015-01-24 06:46:52 -08:00
|
|
|
m_ScriptVal = NULL;
|
2010-07-21 09:04:17 -07:00
|
|
|
}
|