1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333 |
- /**
- Copyright 2013 BlackBerry Inc.
- Copyright (c) 2015-2017 Chukong Technologies
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- Original file from GamePlay3D: http://gameplay3d.org
- This file was modified to fit the cocos2d-x project
- */
- #include "base/CCProperties.h"
- #include <string.h>
- #include "platform/CCPlatformMacros.h"
- #include "platform/CCFileUtils.h"
- #include "math/Vec2.h"
- #include "math/Vec3.h"
- #include "math/Vec4.h"
- #include "math/Mat4.h"
- #include "math/Quaternion.h"
- #include "base/ccUTF8.h"
- #include "base/CCData.h"
- USING_NS_CC;
- // Utility functions (shared with SceneLoader).
- /** @script{ignore} */
- void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
- /** @script{ignore} */
- Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
- Properties::Properties()
- : _dataIdx(nullptr), _data(nullptr), _variables(nullptr), _dirPath(nullptr), _parent(nullptr)
- {
- _properties.reserve(32);
- }
- Properties::Properties(const Properties& copy)
- : _dataIdx(copy._dataIdx), _data(copy._data), _namespace(copy._namespace),
- _id(copy._id), _parentID(copy._parentID), _properties(copy._properties),
- _variables(nullptr), _dirPath(nullptr), _parent(copy._parent)
-
- {
- setDirectoryPath(copy._dirPath);
- for (const auto space: copy._namespaces)
- {
- _namespaces.push_back(new (std::nothrow) Properties(*space));
- }
- rewind();
- }
- Properties::Properties(Data* data, ssize_t* dataIdx)
- : _dataIdx(dataIdx), _data(data), _variables(NULL), _dirPath(NULL), _parent(NULL)
- {
- readProperties();
- rewind();
- }
- Properties::Properties(Data* data, ssize_t* dataIdx, const std::string& name, const char* id, const char* parentID, Properties* parent)
- : _dataIdx(dataIdx), _data(data), _namespace(name), _variables(NULL), _dirPath(NULL), _parent(parent)
- {
- if (id)
- {
- _id = id;
- }
- if (parentID)
- {
- _parentID = parentID;
- }
- readProperties();
- rewind();
- }
- Properties* Properties::createNonRefCounted(const std::string& url)
- {
- if (url.size() == 0)
- {
- CCLOGERROR("Attempting to create a Properties object from an empty URL!");
- return nullptr;
- }
- // Calculate the file and full namespace path from the specified url.
- std::string urlString = url;
- std::string fileString;
- std::vector<std::string> namespacePath;
- calculateNamespacePath(urlString, fileString, namespacePath);
- // data will be released automatically when 'data' goes out of scope
- // so we pass data as weak pointer
- auto data = FileUtils::getInstance()->getDataFromFile(fileString);
- ssize_t dataIdx = 0;
- Properties* properties = new (std::nothrow) Properties(&data, &dataIdx);
- properties->resolveInheritance();
- // Get the specified properties object.
- Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
- if (!p)
- {
- CCLOGWARN("Failed to load properties from url '%s'.", url.c_str());
- CC_SAFE_DELETE(properties);
- return nullptr;
- }
- // If the loaded properties object is not the root namespace,
- // then we have to clone it and delete the root namespace
- // so that we don't leak memory.
- if (p != properties)
- {
- p = p->clone();
- CC_SAFE_DELETE(properties);
- }
- // XXX
- // p->setDirectoryPath(FileSystem::getDirectoryName(fileString));
- p->setDirectoryPath("");
- return p;
- }
- static bool isVariable(const char* str, char* outName, size_t outSize)
- {
- size_t len = strlen(str);
- if (len > 3 && str[0] == '$' && str[1] == '{' && str[len - 1] == '}')
- {
- size_t size = len - 3;
- if (size > (outSize - 1))
- size = outSize - 1;
- strncpy(outName, str + 2, len - 3);
- outName[len - 3] = 0;
- return true;
- }
- return false;
- }
- void Properties::readProperties()
- {
- CCASSERT(_data->getSize() >0, "Invalid data");
- char line[2048];
- char variable[256];
- int c;
- char* name;
- char* value;
- char* parentID;
- char* rc;
- char* rcc;
- char* rccc;
- bool comment = false;
- while (true)
- {
- // Skip whitespace at the start of lines
- skipWhiteSpace();
- // Stop when we have reached the end of the file.
- if (eof())
- break;
- // Read the next line.
- rc = readLine(line, 2048);
- if (rc == NULL)
- {
- CCLOGERROR("Error reading line from file.");
- return;
- }
- // Ignore comments
- if (comment)
- {
- // Check for end of multi-line comment at either start or end of line
- if (strncmp(line, "*/", 2) == 0)
- comment = false;
- else
- {
- trimWhiteSpace(line);
- const auto len = strlen(line);
- if (len >= 2 && strncmp(line + (len - 2), "*/", 2) == 0)
- comment = false;
- }
- }
- else if (strncmp(line, "/*", 2) == 0)
- {
- // Start of multi-line comment (must be at start of line)
- comment = true;
- }
- else if (strncmp(line, "//", 2) != 0)
- {
- // If an '=' appears on this line, parse it as a name/value pair.
- // Note: strchr() has to be called before strtok(), or a backup of line has to be kept.
- rc = strchr(line, '=');
- if (rc != NULL)
- {
- // First token should be the property name.
- name = strtok(line, "=");
- if (name == NULL)
- {
- CCLOGERROR("Error parsing properties file: attribute without name.");
- return;
- }
- // Remove white-space from name.
- name = trimWhiteSpace(name);
- // Scan for next token, the property's value.
- value = strtok(NULL, "");
- if (value == NULL)
- {
- CCLOGERROR("Error parsing properties file: attribute with name ('%s') but no value.", name);
- return;
- }
- // Remove white-space from value.
- value = trimWhiteSpace(value);
- // Is this a variable assignment?
- if (isVariable(name, variable, 256))
- {
- setVariable(variable, value);
- }
- else
- {
- // Normal name/value pair
- _properties.push_back(Property(name, value));
- }
- }
- else
- {
- parentID = NULL;
- // Get the last character on the line (ignoring whitespace).
- const char* lineEnd = trimWhiteSpace(line) + (strlen(trimWhiteSpace(line)) - 1);
- // This line might begin or end a namespace,
- // or it might be a key/value pair without '='.
- // Check for '{' on same line.
- rc = strchr(line, '{');
- // Check for inheritance: ':'
- rcc = strchr(line, ':');
- // Check for '}' on same line.
- rccc = strchr(line, '}');
-
- // Get the name of the namespace.
- name = strtok(line, " \t\n{");
- name = trimWhiteSpace(name);
- if (name == NULL)
- {
- CCLOGERROR("Error parsing properties file: failed to determine a valid token for line '%s'.", line);
- return;
- }
- else if (name[0] == '}')
- {
- // End of namespace.
- return;
- }
- // Get its ID if it has one.
- value = strtok(NULL, ":{");
- value = trimWhiteSpace(value);
- // Get its parent ID if it has one.
- if (rcc != NULL)
- {
- parentID = strtok(NULL, "{");
- parentID = trimWhiteSpace(parentID);
- }
- if (value != NULL && value[0] == '{')
- {
- // If the namespace ends on this line, seek back to right before the '}' character.
- if (rccc && rccc == lineEnd)
- {
- if (seekFromCurrent(-1) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- while (readChar() != '}')
- {
- if (seekFromCurrent(-2) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- }
- if (seekFromCurrent(-1) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- }
- // New namespace without an ID.
- Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, NULL, parentID, this);
- _namespaces.push_back(space);
- // If the namespace ends on this line, seek to right after the '}' character.
- if (rccc && rccc == lineEnd)
- {
- if (seekFromCurrent(1) == false)
- {
- CCLOGERROR("Failed to seek to immediately after a '}' character in properties file.");
- return;
- }
- }
- }
- else
- {
- // If '{' appears on the same line.
- if (rc != NULL)
- {
- // If the namespace ends on this line, seek back to right before the '}' character.
- if (rccc && rccc == lineEnd)
- {
- if (seekFromCurrent(-1) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- while (readChar() != '}')
- {
- if (seekFromCurrent(-2) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- }
- if (seekFromCurrent(-1) == false)
- {
- CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
- return;
- }
- }
- // Create new namespace.
- Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this);
- _namespaces.push_back(space);
- // If the namespace ends on this line, seek to right after the '}' character.
- if (rccc && rccc == lineEnd)
- {
- if (seekFromCurrent(1) == false)
- {
- CCLOGERROR("Failed to seek to immediately after a '}' character in properties file.");
- return;
- }
- }
- }
- else
- {
- // Find out if the next line starts with "{"
- skipWhiteSpace();
- c = readChar();
- if (c == '{')
- {
- // Create new namespace.
- Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this);
- _namespaces.push_back(space);
- }
- else
- {
- // Back up from fgetc()
- if (seekFromCurrent(-1) == false)
- CCLOGERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
- // Store "name value" as a name/value pair, or even just "name".
- if (value != NULL)
- {
- _properties.push_back(Property(name, value));
- }
- else
- {
- _properties.push_back(Property(name, ""));
- }
- }
- }
- }
- }
- }
- }
- }
- Properties::~Properties()
- {
- CC_SAFE_DELETE(_dirPath);
- for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
- {
- CC_SAFE_DELETE(_namespaces[i]);
- }
- CC_SAFE_DELETE(_variables);
- }
- //
- // Stream simulation
- //
- signed char Properties::readChar()
- {
- if (eof())
- return EOF;
- return _data->_bytes[(*_dataIdx)++];
- }
- char* Properties::readLine(char* output, int num)
- {
- if (eof())
- return nullptr;
- // little optimization: avoid unneeded dereferences
- const ssize_t dataIdx = *_dataIdx;
- int i;
- for (i=0; i<num && dataIdx+i < _data->_size; i++)
- {
- auto c = _data->_bytes[dataIdx+i];
- if (c == '\n')
- break;
- output[i] = c;
- }
- output[i] = '\0';
- // restore value
- *_dataIdx = dataIdx+i;
- return output;
- }
- bool Properties::seekFromCurrent(int offset)
- {
- (*_dataIdx) += offset;
- return (!eof() && *_dataIdx >= 0);
- }
- bool Properties::eof()
- {
- return (*_dataIdx >= _data->_size);
- }
- void Properties::skipWhiteSpace()
- {
- signed char c;
- do
- {
- c = readChar();
- } while (isspace(c) && c != EOF);
- // If we are not at the end of the file, then since we found a
- // non-whitespace character, we put the cursor back in front of it.
- if (c != EOF)
- {
- if (seekFromCurrent(-1) == false)
- {
- CCLOGERROR("Failed to seek backwards one character after skipping whitespace.");
- }
- }
- }
- char* Properties::trimWhiteSpace(char *str)
- {
- if (str == NULL)
- {
- return str;
- }
- // Trim leading space.
- while (*str != '\0' && isspace(*str))
- str++;
- // All spaces?
- if (*str == 0)
- {
- return str;
- }
- // Trim trailing space.
- char *end = str + strlen(str) - 1;
- while (end > str && isspace(*end))
- end--;
- // Write new null terminator.
- *(end+1) = 0;
- return str;
- }
- void Properties::resolveInheritance(const char* id)
- {
- // Namespaces can be defined like so:
- // "name id : parentID { }"
- // This method merges data from the parent namespace into the child.
- // Get a top-level namespace.
- Properties* derived;
- if (id)
- {
- derived = getNamespace(id);
- }
- else
- {
- derived = getNextNamespace();
- }
- while (derived)
- {
- // If the namespace has a parent ID, find the parent.
- if (!derived->_parentID.empty())
- {
- Properties* parent = getNamespace(derived->_parentID.c_str());
- if (parent)
- {
- resolveInheritance(parent->getId());
- // Copy the child.
- Properties* overrides = new (std::nothrow) Properties(*derived);
- // Delete the child's data.
- for (size_t i = 0, count = derived->_namespaces.size(); i < count; i++)
- {
- CC_SAFE_DELETE(derived->_namespaces[i]);
- }
- // Copy data from the parent into the child.
- derived->_properties = parent->_properties;
- derived->_namespaces = std::vector<Properties*>();
- std::vector<Properties*>::const_iterator itt;
- for (const auto space: parent->_namespaces)
- {
- derived->_namespaces.push_back(new (std::nothrow) Properties(*space));
- }
- derived->rewind();
- // Take the original copy of the child and override the data copied from the parent.
- derived->mergeWith(overrides);
- // Delete the child copy.
- CC_SAFE_DELETE(overrides);
- }
- }
- // Resolve inheritance within this namespace.
- derived->resolveInheritance();
- // Get the next top-level namespace and check again.
- if (!id)
- {
- derived = getNextNamespace();
- }
- else
- {
- derived = NULL;
- }
- }
- }
- void Properties::mergeWith(Properties* overrides)
- {
- CCASSERT(overrides, "Invalid overrides");
- // Overwrite or add each property found in child.
- overrides->rewind();
- const char* name = overrides->getNextProperty();
- while (name)
- {
- this->setString(name, overrides->getString());
- name = overrides->getNextProperty();
- }
- this->_propertiesItr = this->_properties.end();
- // Merge all common nested namespaces, add new ones.
- Properties* overridesNamespace = overrides->getNextNamespace();
- while (overridesNamespace)
- {
- bool merged = false;
- rewind();
- Properties* derivedNamespace = getNextNamespace();
- while (derivedNamespace)
- {
- if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
- strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
- {
- derivedNamespace->mergeWith(overridesNamespace);
- merged = true;
- }
- derivedNamespace = getNextNamespace();
- }
- if (!merged)
- {
- // Add this new namespace.
- Properties* newNamespace = new (std::nothrow) Properties(*overridesNamespace);
- this->_namespaces.push_back(newNamespace);
- this->_namespacesItr = this->_namespaces.end();
- }
- overridesNamespace = overrides->getNextNamespace();
- }
- }
- const char* Properties::getNextProperty()
- {
- if (_propertiesItr == _properties.end())
- {
- // Restart from the beginning
- _propertiesItr = _properties.begin();
- }
- else
- {
- // Move to the next property
- ++_propertiesItr;
- }
- return _propertiesItr == _properties.end() ? NULL : _propertiesItr->name.c_str();
- }
- Properties* Properties::getNextNamespace()
- {
- if (_namespacesItr == _namespaces.end())
- {
- // Restart from the beginning
- _namespacesItr = _namespaces.begin();
- }
- else
- {
- ++_namespacesItr;
- }
- if (_namespacesItr != _namespaces.end())
- {
- Properties* ns = *_namespacesItr;
- return ns;
- }
- return nullptr;
- }
- void Properties::rewind()
- {
- _propertiesItr = _properties.end();
- _namespacesItr = _namespaces.end();
- }
- Properties* Properties::getNamespace(const char* id, bool searchNames, bool recurse) const
- {
- CCASSERT(id, "invalid id");
- for (const auto& it : _namespaces)
- {
- Properties* p = it;
- if (strcmp(searchNames ? p->_namespace.c_str() : p->_id.c_str(), id) == 0)
- return p;
-
- if (recurse)
- {
- // Search recursively.
- p = p->getNamespace(id, searchNames, true);
- if (p)
- return p;
- }
- }
- return nullptr;
- }
- const char* Properties::getNamespace() const
- {
- return _namespace.c_str();
- }
- const char* Properties::getId() const
- {
- return _id.c_str();
- }
- bool Properties::exists(const char* name) const
- {
- if (name == NULL)
- return false;
- for (const auto& itr : _properties)
- {
- if (itr.name == name)
- return true;
- }
- return false;
- }
- static bool isStringNumeric(const char* str)
- {
- CCASSERT(str, "invalid str");
- // The first character may be '-'
- if (*str == '-')
- str++;
- // The first character after the sign must be a digit
- if (!isdigit(*str))
- return false;
- str++;
- // All remaining characters must be digits, with a single decimal (.) permitted
- unsigned int decimalCount = 0;
- while (*str)
- {
- if (!isdigit(*str))
- {
- if (*str == '.' && decimalCount == 0)
- {
- // Max of 1 decimal allowed
- decimalCount++;
- }
- else
- {
- return false;
- }
- }
- str++;
- }
- return true;
- }
- Properties::Type Properties::getType(const char* name) const
- {
- const char* value = getString(name);
- if (!value)
- {
- return Properties::NONE;
- }
- // Parse the value to determine the format
- unsigned int commaCount = 0;
- char* valuePtr = const_cast<char*>(value);
- while ((valuePtr = strchr(valuePtr, ',')))
- {
- valuePtr++;
- commaCount++;
- }
- switch (commaCount)
- {
- case 0:
- return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING;
- case 1:
- return Properties::VECTOR2;
- case 2:
- return Properties::VECTOR3;
- case 3:
- return Properties::VECTOR4;
- case 15:
- return Properties::MATRIX;
- default:
- return Properties::STRING;
- }
- }
- const char* Properties::getString(const char* name, const char* defaultValue) const
- {
- char variable[256];
- const char* value = NULL;
- if (name)
- {
- // If 'name' is a variable, return the variable value
- if (isVariable(name, variable, 256))
- {
- return getVariable(variable, defaultValue);
- }
- for (const auto& itr : _properties)
- {
- if (itr.name == name)
- {
- value = itr.value.c_str();
- break;
- }
- }
- }
- else
- {
- // No name provided - get the value at the current iterator position
- if (_propertiesItr != _properties.end())
- {
- value = _propertiesItr->value.c_str();
- }
- }
- if (value)
- {
- // If the value references a variable, return the variable value
- if (isVariable(value, variable, 256))
- return getVariable(variable, defaultValue);
- return value;
- }
- return defaultValue;
- }
- bool Properties::setString(const char* name, const char* value)
- {
- if (name)
- {
- for (auto& itr : _properties)
- {
- if (itr.name == name)
- {
- // Update the first property that matches this name
- itr.value = value ? value : "";
- return true;
- }
- }
- // There is no property with this name, so add one
- _properties.push_back(Property(name, value ? value : ""));
- }
- else
- {
- // If there's a current property, set its value
- if (_propertiesItr == _properties.end())
- return false;
- _propertiesItr->value = value ? value : "";
- }
- return true;
- }
- bool Properties::getBool(const char* name, bool defaultValue) const
- {
- const char* valueString = getString(name);
- if (valueString)
- {
- return (strcmp(valueString, "true") == 0);
- }
- return defaultValue;
- }
- int Properties::getInt(const char* name) const
- {
- const char* valueString = getString(name);
- if (valueString)
- {
- int value;
- int scanned;
- scanned = sscanf(valueString, "%d", &value);
- if (scanned != 1)
- {
- CCLOGERROR("Error attempting to parse property '%s' as an integer.", name);
- return 0;
- }
- return value;
- }
- return 0;
- }
- float Properties::getFloat(const char* name) const
- {
- const char* valueString = getString(name);
- if (valueString)
- {
- float value;
- int scanned;
- scanned = sscanf(valueString, "%f", &value);
- if (scanned != 1)
- {
- CCLOGERROR("Error attempting to parse property '%s' as a float.", name);
- return 0.0f;
- }
- return value;
- }
- return 0.0f;
- }
- long Properties::getLong(const char* name) const
- {
- const char* valueString = getString(name);
- if (valueString)
- {
- long value;
- int scanned;
- scanned = sscanf(valueString, "%ld", &value);
- if (scanned != 1)
- {
- CCLOGERROR("Error attempting to parse property '%s' as a long integer.", name);
- return 0L;
- }
- return value;
- }
- return 0L;
- }
- bool Properties::getMat4(const char* name, Mat4* out) const
- {
- CCASSERT(out, "Invalid out");
- const char* valueString = getString(name);
- if (valueString)
- {
- float m[16];
- int scanned;
- scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
- &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7],
- &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]);
- if (scanned != 16)
- {
- CCLOGERROR("Error attempting to parse property '%s' as a matrix.", name);
- out->setIdentity();
- return false;
- }
- out->set(m);
- return true;
- }
- out->setIdentity();
- return false;
- }
- bool Properties::getVec2(const char* name, Vec2* out) const
- {
- return parseVec2(getString(name), out);
- }
- bool Properties::getVec3(const char* name, Vec3* out) const
- {
- return parseVec3(getString(name), out);
- }
- bool Properties::getVec4(const char* name, Vec4* out) const
- {
- return parseVec4(getString(name), out);
- }
- bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
- {
- return parseAxisAngle(getString(name), out);
- }
- bool Properties::getColor(const char* name, Vec3* out) const
- {
- return parseColor(getString(name), out);
- }
- bool Properties::getColor(const char* name, Vec4* out) const
- {
- return parseColor(getString(name), out);
- }
- bool Properties::getPath(const char* name, std::string* path) const
- {
- CCASSERT(name && path, "Invalid name or path");
- const char* valueString = getString(name);
- if (valueString)
- {
- if (FileUtils::getInstance()->isFileExist(valueString))
- {
- path->assign(valueString);
- return true;
- }
- else
- {
- const Properties* prop = this;
- while (prop != NULL)
- {
- // Search for the file path relative to the bundle file
- const std::string* dirPath = prop->_dirPath;
- if (dirPath != NULL && !dirPath->empty())
- {
- std::string relativePath = *dirPath;
- relativePath.append(valueString);
- if (FileUtils::getInstance()->isFileExist(relativePath))
- {
- path->assign(relativePath);
- return true;
- }
- }
- prop = prop->_parent;
- }
- }
- }
- return false;
- }
- const char* Properties::getVariable(const char* name, const char* defaultValue) const
- {
- if (name == NULL)
- return defaultValue;
- // Search for variable in this Properties object
- if (_variables)
- {
- for (size_t i = 0, count = _variables->size(); i < count; ++i)
- {
- Property& prop = (*_variables)[i];
- if (prop.name == name)
- return prop.value.c_str();
- }
- }
- // Search for variable in parent Properties
- return _parent ? _parent->getVariable(name, defaultValue) : defaultValue;
- }
- void Properties::setVariable(const char* name, const char* value)
- {
- CCASSERT(name, "Invalid name");
- Property* prop = NULL;
- // Search for variable in this Properties object and parents
- Properties* current = const_cast<Properties*>(this);
- while (current)
- {
- if (current->_variables)
- {
- for (size_t i = 0, count = current->_variables->size(); i < count; ++i)
- {
- Property* p = &(*current->_variables)[i];
- if (p->name == name)
- {
- prop = p;
- break;
- }
- }
- }
- current = current->_parent;
- }
- if (prop)
- {
- // Found an existing property, set it
- prop->value = value ? value : "";
- }
- else
- {
- // Add a new variable with this name
- if (!_variables)
- _variables = new (std::nothrow) std::vector<Property>();
- _variables->push_back(Property(name, value ? value : ""));
- }
- }
- Properties* Properties::clone()
- {
- Properties* p = new (std::nothrow) Properties();
-
- p->_namespace = _namespace;
- p->_id = _id;
- p->_parentID = _parentID;
- p->_properties = _properties;
- p->_propertiesItr = p->_properties.end();
- p->setDirectoryPath(_dirPath);
- for (size_t i = 0, count = _namespaces.size(); i < count; i++)
- {
- CCASSERT(_namespaces[i], "Invalid namespace");
- Properties* child = _namespaces[i]->clone();
- p->_namespaces.push_back(child);
- child->_parent = p;
- }
- p->_namespacesItr = p->_namespaces.end();
- return p;
- }
- void Properties::setDirectoryPath(const std::string* path)
- {
- if (path)
- {
- setDirectoryPath(*path);
- }
- else
- {
- CC_SAFE_DELETE(_dirPath);
- }
- }
- void Properties::setDirectoryPath(const std::string& path)
- {
- if (_dirPath == NULL)
- {
- _dirPath = new (std::nothrow) std::string(path);
- }
- else
- {
- _dirPath->assign(path);
- }
- }
- void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
- {
- // If the url references a specific namespace within the file,
- // calculate the full namespace path to the final namespace.
- size_t loc = urlString.rfind("#");
- if (loc != std::string::npos)
- {
- fileString = urlString.substr(0, loc);
- std::string namespacePathString = urlString.substr(loc + 1);
- while ((loc = namespacePathString.find("/")) != std::string::npos)
- {
- namespacePath.push_back(namespacePathString.substr(0, loc));
- namespacePathString = namespacePathString.substr(loc + 1);
- }
- namespacePath.push_back(namespacePathString);
- }
- else
- {
- fileString = urlString;
- }
- }
- Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
- {
- // If the url references a specific namespace within the file,
- // return the specified namespace or notify the user if it cannot be found.
- if (namespacePath.size() > 0)
- {
- size_t size = namespacePath.size();
- properties->rewind();
- Properties* iter = properties->getNextNamespace();
- for (size_t i = 0; i < size; )
- {
- while (true)
- {
- if (iter == NULL)
- {
- CCLOGWARN("Failed to load properties object from url.");
- return nullptr;
- }
- if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
- {
- if (i != size - 1)
- {
- properties = iter->getNextNamespace();
- iter = properties;
- }
- else
- properties = iter;
- i++;
- break;
- }
-
- iter = properties->getNextNamespace();
- }
- }
- return properties;
- }
- else
- return properties;
- }
- bool Properties::parseVec2(const char* str, Vec2* out)
- {
- if (str)
- {
- float x, y;
- if (sscanf(str, "%f,%f", &x, &y) == 2)
- {
- if (out)
- out->set(x, y);
- return true;
- }
- else
- {
- CCLOGWARN("Error attempting to parse property as a two-dimensional vector: %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f);
- return false;
- }
- bool Properties::parseVec3(const char* str, Vec3* out)
- {
- if (str)
- {
- float x, y, z;
- if (sscanf(str, "%f,%f,%f", &x, &y, &z) == 3)
- {
- if (out)
- out->set(x, y, z);
- return true;
- }
- else
- {
- CCLOGWARN("Error attempting to parse property as a three-dimensional vector: %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f, 0.0f);
- return false;
- }
- bool Properties::parseVec4(const char* str, Vec4* out)
- {
- if (str)
- {
- float x, y, z, w;
- if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
- {
- if (out)
- out->set(x, y, z, w);
- return true;
- }
- else
- {
- CCLOGWARN("Error attempting to parse property as a four-dimensional vector: %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f, 0.0f, 0.0f);
- return false;
- }
- bool Properties::parseAxisAngle(const char* str, Quaternion* out)
- {
- if (str)
- {
- float x, y, z, theta;
- if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &theta) == 4)
- {
- if (out)
- out->set(Vec3(x, y, z), MATH_DEG_TO_RAD(theta));
- return true;
- }
- else
- {
- CCLOGWARN("Error attempting to parse property as an axis-angle rotation: %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f, 0.0f, 1.0f);
- return false;
- }
- bool Properties::parseColor(const char* str, Vec3* out)
- {
- if (str)
- {
- if (strlen(str) == 7 && str[0] == '#')
- {
- // Read the string into an int as hex.
- unsigned int color;
- if (sscanf(str + 1, "%x", &color) == 1)
- {
- if (out)
- out->set(Vec3::fromColor(color));
- return true;
- }
- else
- {
- // Invalid format
- CCLOGWARN("Error attempting to parse property as an RGB color: %s", str);
- }
- }
- else
- {
- // Not a color string.
- CCLOGWARN("Error attempting to parse property as an RGB color (not specified as a color string): %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f, 0.0f);
- return false;
- }
- bool Properties::parseColor(const char* str, Vec4* out)
- {
- if (str)
- {
- if (strlen(str) == 9 && str[0] == '#')
- {
- // Read the string into an int as hex.
- unsigned int color;
- if (sscanf(str + 1, "%x", &color) == 1)
- {
- if (out)
- out->set(Vec4::fromColor(color));
- return true;
- }
- else
- {
- // Invalid format
- CCLOGWARN("Error attempting to parse property as an RGBA color: %s", str);
- }
- }
- else
- {
- // Not a color string.
- CCLOGWARN("Error attempting to parse property as an RGBA color (not specified as a color string): %s", str);
- }
- }
- if (out)
- out->set(0.0f, 0.0f, 0.0f, 0.0f);
- return false;
- }
|