Properties.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /*
  2. * Properties.cpp
  3. */
  4. #include "Properties.h"
  5. #include "FileSystem.h"
  6. #include <xtree>
  7. #include <sstream>
  8. namespace gameplay
  9. {
  10. Properties::Properties(FILE* file)
  11. {
  12. readProperties(file);
  13. _propertiesItr = _properties.end();
  14. _namespacesItr = _namespaces.end();
  15. }
  16. Properties::Properties(FILE* file, const char* name, const char* id) : _namespace(name)
  17. {
  18. if (id)
  19. {
  20. _id = id;
  21. }
  22. readProperties(file);
  23. _propertiesItr = _properties.end();
  24. _namespacesItr = _namespaces.end();
  25. }
  26. Properties* Properties::create(const char* filePath)
  27. {
  28. assert(filePath);
  29. FILE* file = FileSystem::openFile(filePath, "rb");
  30. if (!file)
  31. {
  32. return NULL;
  33. }
  34. Properties* properties = new Properties(file);
  35. fclose(file);
  36. return properties;
  37. }
  38. void Properties::readProperties(FILE* file)
  39. {
  40. char line[2048];
  41. int c;
  42. char* name;
  43. char* value;
  44. char* rc;
  45. while (!feof(file))
  46. {
  47. skipWhiteSpace(file);
  48. // Read the next line.
  49. rc = fgets(line, 2048, file);
  50. if (rc == NULL)
  51. {
  52. return;
  53. }
  54. // Ignore comment, skip line.
  55. if (strncmp(line, "//", 2) != 0)
  56. {
  57. // If an '=' appears on this line, parse it as a name/value pair.
  58. // Note: strchr() has to be called before strtok(), or a backup of line has to be kept.
  59. rc = strchr(line, '=');
  60. if (rc != NULL)
  61. {
  62. // There could be a '}' at the end of the line, ending a namespace.
  63. rc = strchr(line, '}');
  64. // First token should be the property name.
  65. name = strtok(line, " =\t");
  66. if (name == NULL)
  67. {
  68. LOG_ERROR("Error parsing properties file: value without name.");
  69. return;
  70. }
  71. // Scan for next token, the property's value.
  72. value = strtok(NULL, "=");
  73. if (value == NULL)
  74. {
  75. LOG_ERROR("Error parsing properties file: name without value.");
  76. }
  77. // Remove white-space from value.
  78. value = trimWhiteSpace(value);
  79. // Store name/value pair.
  80. _properties[name] = value;
  81. if (rc != NULL)
  82. {
  83. // End of namespace.
  84. return;
  85. }
  86. }
  87. else
  88. {
  89. // This line might begin or end a namespace,
  90. // or it might be a key/value pair without '='.
  91. // Check for '{' on same line.
  92. rc = strchr(line, '{');
  93. // Get the name of the namespace.
  94. name = strtok(line, " \t\n{");
  95. name = trimWhiteSpace(name);
  96. if (name == NULL)
  97. {
  98. LOG_ERROR("Error parsing properties file: unknown error.");
  99. }
  100. else if (name[0] == '}')
  101. {
  102. // End of namespace.
  103. return;
  104. }
  105. // Get its ID if it has one.
  106. value = strtok(NULL, "{");
  107. value = trimWhiteSpace(value);
  108. if (value != NULL && value[0] == '{')
  109. {
  110. // New namespace without an ID.
  111. Properties* space = new Properties(file, name, NULL);
  112. _namespaces.push_back(space);
  113. }
  114. else
  115. {
  116. // If '{' appears on the same line.
  117. if (rc != NULL)
  118. {
  119. // Create new namespace.
  120. Properties* space = new Properties(file, name, value);
  121. _namespaces.push_back(space);
  122. }
  123. else
  124. {
  125. // Find out if the next line starts with "{"
  126. skipWhiteSpace(file);
  127. c = fgetc(file);
  128. if (c == '{')
  129. {
  130. // Create new namespace.
  131. Properties* space = new Properties(file, name, value);
  132. _namespaces.push_back(space);
  133. }
  134. else
  135. {
  136. // Back up from fgetc()
  137. fseek(file, -1, SEEK_CUR);
  138. // Store "name value" as a name/value pair, or even just "name".
  139. if (value != NULL)
  140. {
  141. _properties[name] = value;
  142. }
  143. else
  144. {
  145. _properties[name] = std::string();
  146. }
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. }
  154. Properties::~Properties()
  155. {
  156. unsigned int count = _namespaces.size();
  157. for (unsigned int i = 0; i < count; i++)
  158. {
  159. SAFE_DELETE(_namespaces[i]);
  160. }
  161. }
  162. void Properties::skipWhiteSpace(FILE* file)
  163. {
  164. int c;
  165. do
  166. {
  167. c = fgetc(file);
  168. } while (isspace(c));
  169. // We found a non-whitespace character; put the cursor back in front of it.
  170. fseek(file, -1, SEEK_CUR);
  171. }
  172. char* Properties::trimWhiteSpace(char *str)
  173. {
  174. if (str == NULL)
  175. {
  176. return str;
  177. }
  178. char *end;
  179. // Trim leading space.
  180. while (isspace(*str))
  181. str++;
  182. // All spaces?
  183. if (*str == 0)
  184. {
  185. return str;
  186. }
  187. // Trim trailing space.
  188. end = str + strlen(str) - 1;
  189. while (end > str && isspace(*end))
  190. end--;
  191. // Write new null terminator.
  192. *(end+1) = 0;
  193. return str;
  194. }
  195. const char* Properties::getNextProperty(const char** value)
  196. {
  197. if (_propertiesItr == _properties.end())
  198. {
  199. // Restart from the beginning
  200. _propertiesItr = _properties.begin();
  201. }
  202. else
  203. {
  204. // Move to the next property
  205. _propertiesItr++;
  206. }
  207. if (_propertiesItr != _properties.end())
  208. {
  209. const std::string& name = _propertiesItr->first;
  210. if (!name.empty())
  211. {
  212. if (value)
  213. {
  214. *value = _propertiesItr->second.c_str();
  215. }
  216. return name.c_str();
  217. }
  218. }
  219. return NULL;
  220. }
  221. Properties* Properties::getNextNamespace()
  222. {
  223. if (_namespacesItr == _namespaces.end())
  224. {
  225. // Restart from the beginning
  226. _namespacesItr = _namespaces.begin();
  227. }
  228. else
  229. {
  230. _namespacesItr++;
  231. }
  232. if (_namespacesItr != _namespaces.end())
  233. {
  234. Properties* ns = *_namespacesItr;
  235. return ns;
  236. }
  237. return NULL;
  238. }
  239. void Properties::rewind()
  240. {
  241. _propertiesItr = _properties.end();
  242. _namespacesItr = _namespaces.end();
  243. }
  244. Properties* Properties::getNamespace(const char* id) const
  245. {
  246. Properties* ret = NULL;
  247. std::vector<Properties*>::const_iterator it;
  248. for (it = _namespaces.begin(); it < _namespaces.end(); it++)
  249. {
  250. ret = *it;
  251. if (strcmp(ret->_id.c_str(), id) == 0)
  252. {
  253. return ret;
  254. }
  255. // Search recursively.
  256. ret = ret->getNamespace(id);
  257. if (ret != NULL)
  258. {
  259. return ret;
  260. }
  261. }
  262. return ret;
  263. }
  264. const char* Properties::getNamespace() const
  265. {
  266. return _namespace.c_str();
  267. }
  268. const char* Properties::getId() const
  269. {
  270. return _id.c_str();
  271. }
  272. bool Properties::exists(const char* name) const
  273. {
  274. assert(name);
  275. return _properties.find(name) != _properties.end();
  276. }
  277. bool isStringNumeric(const char* str)
  278. {
  279. char* ptr = const_cast<char*>(str);
  280. // First character must be a digit
  281. if (!isdigit(*ptr))
  282. return false;
  283. ptr++;
  284. // All remaining characters must be digits, with a single decimal (.) permitted
  285. unsigned int decimalCount = 0;
  286. while (*ptr)
  287. {
  288. if (!isdigit(*ptr))
  289. {
  290. if (*ptr == '.' && decimalCount == 0)
  291. {
  292. // Max of 1 decimal allowed
  293. decimalCount++;
  294. }
  295. else
  296. {
  297. // Not a number
  298. }
  299. }
  300. ptr++;
  301. }
  302. return true;
  303. }
  304. Properties::Type Properties::getType(const char* name) const
  305. {
  306. const char* value = getString(name);
  307. if (!value)
  308. {
  309. return Properties::NONE;
  310. }
  311. // Parse the value to determine the format
  312. unsigned int commaCount = 0;
  313. unsigned int length = strlen(value);
  314. char* valuePtr = const_cast<char*>(value);
  315. while (valuePtr = strchr(valuePtr, ','))
  316. {
  317. valuePtr++;
  318. commaCount++;
  319. }
  320. switch (commaCount)
  321. {
  322. case 0:
  323. return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING;
  324. case 1:
  325. return Properties::VECTOR2;
  326. case 2:
  327. return Properties::VECTOR3;
  328. case 3:
  329. return Properties::VECTOR4;
  330. case 15:
  331. return Properties::MATRIX;
  332. default:
  333. return Properties::STRING;
  334. }
  335. }
  336. const char* Properties::getString(const char* name) const
  337. {
  338. if (name)
  339. {
  340. std::map<std::string, std::string>::const_iterator itr = _properties.find(name);
  341. if (itr != _properties.end())
  342. {
  343. return itr->second.c_str();
  344. }
  345. }
  346. else
  347. {
  348. if (_propertiesItr != _properties.end())
  349. {
  350. return _propertiesItr->second.c_str();
  351. }
  352. }
  353. return NULL;
  354. }
  355. bool Properties::getBool(const char* name) const
  356. {
  357. if (exists(name))
  358. {
  359. if (_properties.find(name)->second == "true")
  360. {
  361. return true;
  362. }
  363. }
  364. return false;
  365. }
  366. int Properties::getInt(const char* name) const
  367. {
  368. const char* valueString = getString(name);
  369. if (valueString)
  370. {
  371. int value;
  372. int scanned;
  373. scanned = sscanf(valueString, "%d", &value);
  374. if (scanned != 1)
  375. {
  376. LOG_ERROR_VARG("Error parsing property: %s", name);
  377. return 0;
  378. }
  379. return value;
  380. }
  381. return 0;
  382. }
  383. float Properties::getFloat(const char* name) const
  384. {
  385. const char* valueString = getString(name);
  386. if (valueString)
  387. {
  388. float value;
  389. int scanned;
  390. scanned = sscanf(valueString, "%f", &value);
  391. if (scanned != 1)
  392. {
  393. LOG_ERROR_VARG("Error parsing property: %s", name);
  394. return 0.0f;
  395. }
  396. return value;
  397. }
  398. return 0.0f;
  399. }
  400. long Properties::getLong(const char* name) const
  401. {
  402. const char* valueString = getString(name);
  403. if (valueString)
  404. {
  405. long value;
  406. int scanned;
  407. scanned = sscanf(valueString, "%ld", &value);
  408. if (scanned != 1)
  409. {
  410. LOG_ERROR_VARG("Error parsing property: %s", name);
  411. return 0L;
  412. }
  413. return value;
  414. }
  415. return 0L;
  416. }
  417. bool Properties::getMatrix(const char* name, Matrix* out) const
  418. {
  419. assert(out);
  420. const char* valueString = getString(name);
  421. if (valueString)
  422. {
  423. float m[16];
  424. int scanned;
  425. scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
  426. &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7],
  427. &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]);
  428. if (scanned != 16)
  429. {
  430. LOG_ERROR_VARG("Error parsing property: %s", name);
  431. out->setIdentity();
  432. return false;
  433. }
  434. out->set(m);
  435. return true;
  436. }
  437. out->setIdentity();
  438. return false;
  439. }
  440. bool Properties::getVector2(const char* name, Vector2* out) const
  441. {
  442. assert(out);
  443. const char* valueString = getString(name);
  444. if (valueString)
  445. {
  446. float x, y;
  447. int scanned;
  448. scanned = sscanf(valueString, "%f,%f", &x, &y);
  449. if (scanned != 2)
  450. {
  451. LOG_ERROR_VARG("Error parsing property: %s", name);
  452. out->set(0.0f, 0.0f);
  453. return false;
  454. }
  455. out->set(x, y);
  456. return true;
  457. }
  458. out->set(0.0f, 0.0f);
  459. return false;
  460. }
  461. bool Properties::getVector3(const char* name, Vector3* out) const
  462. {
  463. assert(out);
  464. const char* valueString = getString(name);
  465. if (valueString)
  466. {
  467. float x, y, z;
  468. int scanned;
  469. scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
  470. if (scanned != 3)
  471. {
  472. LOG_ERROR_VARG("Error parsing property: %s", name);
  473. out->set(0.0f, 0.0f, 0.0f);
  474. return false;
  475. }
  476. out->set(x, y, z);
  477. return true;
  478. }
  479. out->set(0.0f, 0.0f, 0.0f);
  480. return false;
  481. }
  482. bool Properties::getVector4(const char* name, Vector4* out) const
  483. {
  484. assert(out);
  485. const char* valueString = getString(name);
  486. if (valueString)
  487. {
  488. float x, y, z, w;
  489. int scanned;
  490. scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
  491. if (scanned != 4)
  492. {
  493. LOG_ERROR_VARG("Error parsing property: %s", name);
  494. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  495. return false;
  496. }
  497. out->set(x, y, z, w);
  498. return true;
  499. }
  500. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  501. return false;
  502. }
  503. bool Properties::getColor(const char* name, Vector3* out) const
  504. {
  505. assert(out);
  506. const char* valueString = getString(name);
  507. if (valueString)
  508. {
  509. if (strlen(valueString) != 7 ||
  510. valueString[0] != '#')
  511. {
  512. // Not a color string.
  513. LOG_ERROR_VARG("Error parsing property: %s", name);
  514. out->set(0.0f, 0.0f, 0.0f);
  515. return false;
  516. }
  517. // Read the string into an int as hex.
  518. unsigned int color;
  519. sscanf(valueString+1, "%x", &color);
  520. out->set(Vector3::fromColor(color));
  521. return true;
  522. }
  523. out->set(0.0f, 0.0f, 0.0f);
  524. return false;
  525. }
  526. bool Properties::getColor(const char* name, Vector4* out) const
  527. {
  528. assert(out);
  529. const char* valueString = getString(name);
  530. if (valueString)
  531. {
  532. if (strlen(valueString) != 9 ||
  533. valueString[0] != '#')
  534. {
  535. // Not a color string.
  536. LOG_ERROR_VARG("Error parsing property: %s", name);
  537. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  538. return false;
  539. }
  540. // Read the string into an int as hex.
  541. unsigned int color;
  542. sscanf(valueString+1, "%x", &color);
  543. out->set(Vector4::fromColor(color));
  544. return true;
  545. }
  546. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  547. return false;
  548. }
  549. }