/Rainman2/luattrib.cpp

http://modstudio2.googlecode.com/ · C++ · 1310 lines · 1129 code · 125 blank · 56 comment · 225 complexity · 0f16f12841bf24b254b013450a0bd0f6 MD5 · raw file

  1. /*
  2. Copyright (c) 2008 Peter "Corsix" Cawley
  3. Permission is hereby granted, free of charge, to any person
  4. obtaining a copy of this software and associated documentation
  5. files (the "Software"), to deal in the Software without
  6. restriction, including without limitation the rights to use,
  7. copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the
  9. Software is furnished to do so, subject to the following
  10. conditions:
  11. The above copyright notice and this permission notice shall be
  12. included in all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  15. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  17. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  18. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20. OTHER DEALINGS IN THE SOFTWARE.
  21. */
  22. #include "luattrib.h"
  23. #ifdef RAINMAN2_USE_LUA
  24. extern "C" {
  25. #include <lua.h>
  26. #include <lauxlib.h>
  27. #include <lobject.h>
  28. #include <lopcodes.h>
  29. #include <lstate.h>
  30. }
  31. #include <math.h>
  32. #include <new>
  33. #include <algorithm>
  34. #include <locale>
  35. #include <limits>
  36. #include "hash.h"
  37. #include "exception.h"
  38. #include "rgd_dict.h"
  39. #include "binaryattrib.h"
  40. #pragma warning(disable: 4996)
  41. class LuaAttribTableAdapter : public IAttributeTable
  42. {
  43. public:
  44. LuaAttribTableAdapter(LuaAttrib::_table_t *pTable, LuaAttrib *pFile);
  45. ~LuaAttribTableAdapter();
  46. unsigned long getChildCount() throw();
  47. IAttributeValue* getChild(unsigned long iIndex) throw(...);
  48. void addChild(unsigned long iName) throw(...);
  49. void deleteChild(unsigned long iIndex, bool bRevertIsSufficient) throw(...);
  50. unsigned long findChildIndex(unsigned long iName) throw();
  51. protected:
  52. void _sortValues();
  53. std::vector<unsigned long> m_vValues;
  54. LuaAttrib::_table_t *m_pTable;
  55. LuaAttrib *m_pFile;
  56. };
  57. class LuaAttribValueAdapter : public IAttributeValue
  58. {
  59. public:
  60. LuaAttribValueAdapter(LuaAttrib::_value_t *pValue, LuaAttrib::_table_t *pParent, unsigned long iName, LuaAttrib *pFile)
  61. : m_pValue(pValue), m_pParent(pParent), m_iName(iName), m_pFile(pFile)
  62. {
  63. m_bConst = !pParent || (pParent->mapContents.count(iName) == 0);
  64. }
  65. ~LuaAttribValueAdapter()
  66. {
  67. }
  68. unsigned long getName() const throw()
  69. {
  70. return m_iName;
  71. }
  72. eAttributeValueTypes getType() const throw()
  73. {
  74. switch(m_pValue->eType)
  75. {
  76. case LuaAttrib::_value_t::T_String:
  77. return VT_String;
  78. case LuaAttrib::_value_t::T_Boolean:
  79. return VT_Boolean;
  80. case LuaAttrib::_value_t::T_Float:
  81. return VT_Float;
  82. case LuaAttrib::_value_t::T_Table:
  83. return VT_Table;
  84. default:
  85. return VT_Unknown;
  86. }
  87. }
  88. RainString getFileSetIn() const throw()
  89. {
  90. if(m_pFile)
  91. return m_pFile->getName();
  92. return L"";
  93. }
  94. eAttributeValueIcons getIcon() const throw()
  95. {
  96. if(isTable())
  97. {
  98. if(m_pValue->pValue->bTotallyUnchaged)
  99. return VI_SameAsParent;
  100. else
  101. return VI_Table;
  102. }
  103. if(m_pParent->mapContents.count(m_iName) == 0)
  104. return VI_SameAsParent;
  105. const LuaAttrib::_table_t *pGrandparent = _findGrandparentWithKey();
  106. if(!pGrandparent)
  107. return VI_NewSinceParent;
  108. const LuaAttrib::_value_t &oValue = ((LuaAttrib::_table_t *)pGrandparent)->mapContents[m_iName];
  109. if(m_pValue->eType != oValue.eType)
  110. return VI_DifferentToParent;
  111. switch(getType())
  112. {
  113. case VT_String:
  114. return (m_pValue->sValue == oValue.sValue || (m_pValue->iLength == oValue.iLength && strcmp(oValue.sValue, m_pValue->sValue) == 0)) ? VI_SameAsParent : VI_DifferentToParent;
  115. case VT_Float:
  116. return (m_pValue->fValue == oValue.fValue) ? VI_SameAsParent : VI_DifferentToParent;
  117. case VT_Boolean:
  118. return (m_pValue->bValue == oValue.bValue) ? VI_SameAsParent : VI_DifferentToParent;
  119. default:
  120. return VI_DifferentToParent;
  121. };
  122. }
  123. const IAttributeValue* getParent() const throw(...)
  124. {
  125. LuaAttrib::_table_t *pGrandparent = (LuaAttrib::_table_t*)_findGrandparentWithKey();
  126. if(!pGrandparent)
  127. THROW_SIMPLE(L"Value is not present in any parent");
  128. return CHECK_ALLOCATION(new (std::nothrow) LuaAttribValueAdapter(&pGrandparent->mapContents[m_iName], pGrandparent, m_iName, pGrandparent->pSourceFile));
  129. }
  130. const IAttributeValue* getParentNoThrow() const throw()
  131. {
  132. LuaAttrib::_table_t *pGrandparent = (LuaAttrib::_table_t*)_findGrandparentWithKey();
  133. if(!pGrandparent)
  134. return 0;
  135. return (new (std::nothrow) LuaAttribValueAdapter(&pGrandparent->mapContents[m_iName], pGrandparent, m_iName, pGrandparent->pSourceFile));
  136. }
  137. RainString getValueString () const throw(...)
  138. {
  139. CHECK_ASSERT(m_pValue->eType == LuaAttrib::_value_t::T_String);
  140. return RainString(m_pValue->sValue, m_pValue->iLength);
  141. }
  142. bool getValueBoolean() const throw(...)
  143. {
  144. CHECK_ASSERT(m_pValue->eType == LuaAttrib::_value_t::T_Boolean);
  145. return m_pValue->bValue;
  146. }
  147. IAttributeTable* getValueTable () const throw(...)
  148. {
  149. CHECK_ASSERT(m_pValue->eType == LuaAttrib::_value_t::T_Table);
  150. return new LuaAttribTableAdapter(m_pValue->pValue, m_pFile);
  151. }
  152. float getValueFloat () const throw(...)
  153. {
  154. CHECK_ASSERT(m_pValue->eType == LuaAttrib::_value_t::T_Float);
  155. return m_pValue->fValue;
  156. }
  157. long getValueInteger () const throw(...)
  158. {
  159. THROW_SIMPLE(L"Value is not integer");
  160. }
  161. virtual void setName(unsigned long iName ) throw(...)
  162. {
  163. if(!m_pParent)
  164. THROW_SIMPLE(L"Cannot change the name of this item");
  165. m_pParent->mapContents[iName] = *m_pValue;
  166. m_pParent->mapContents.erase(m_iName);
  167. m_iName = iName;
  168. }
  169. virtual void setType(eAttributeValueTypes eNewType) throw(...)
  170. {
  171. if(getType() != eNewType)
  172. {
  173. _checkConst();
  174. m_pValue->free();
  175. switch(eNewType)
  176. {
  177. case VT_String:
  178. m_pValue->sValue = "";
  179. break;
  180. case VT_Boolean:
  181. m_pValue->bValue = false;
  182. break;
  183. case VT_Table:
  184. CHECK_ALLOCATION(m_pValue->pValue = new (std::nothrow) LuaAttrib::_table_t(m_pFile));
  185. break;
  186. case VT_Float:
  187. m_pValue->fValue = 0.0f;
  188. break;
  189. };
  190. }
  191. }
  192. virtual void setValueString (const RainString& sValue) throw(...)
  193. {
  194. setType(VT_String);
  195. _checkConst();
  196. lua_State *L = m_pFile->getCache()->getLuaState();
  197. lua_checkstack(L, 8);
  198. lua_pushlightuserdata(L, reinterpret_cast<void*>(m_pFile));
  199. lua_gettable(L, LUA_REGISTRYINDEX);
  200. if(lua_type(L, -1) == LUA_TNIL)
  201. {
  202. lua_pop(L, 1);
  203. lua_newtable(L);
  204. lua_pushlightuserdata(L, reinterpret_cast<void*>(m_pFile));
  205. lua_pushvalue(L, -2);
  206. lua_settable(L, LUA_REGISTRYINDEX);
  207. }
  208. luaL_Buffer b;
  209. luaL_buffinit(L, &b);
  210. for(size_t i = 0; i < sValue.length(); ++i)
  211. luaL_addchar(&b, sValue.getCharacters()[i]);
  212. luaL_pushresult(&b);
  213. m_pValue->sValue = lua_tostring(L, -1);
  214. m_pValue->iLength = static_cast<long>(sValue.length());
  215. lua_rawseti(L, -2, static_cast<int>(lua_objlen(L, -2) + 1));
  216. lua_pop(L, 1);
  217. }
  218. virtual void setValueBoolean(bool bValue ) throw(...)
  219. {
  220. setType(VT_Boolean);
  221. _checkConst();
  222. m_pValue->bValue = bValue;
  223. }
  224. virtual void setValueFloat (float fValue ) throw(...)
  225. {
  226. setType(VT_Float);
  227. _checkConst();
  228. m_pValue->fValue = fValue;
  229. }
  230. void _checkConst()
  231. {
  232. if(m_bConst)
  233. {
  234. if(!m_pParent)
  235. THROW_SIMPLE(L"Value is immutable");
  236. m_pParent->mapContents[m_iName] = *m_pValue;
  237. m_pValue = &m_pParent->mapContents[m_iName];
  238. m_pFile = m_pParent->pSourceFile;
  239. m_bConst = false;
  240. }
  241. }
  242. const LuaAttrib::_table_t* _findGrandparentWithKey() const
  243. {
  244. const LuaAttrib::_table_t *pTable = m_pParent;
  245. for(; pTable; pTable = pTable->pInheritFrom)
  246. {
  247. if(pTable->mapContents.count(m_iName))
  248. {
  249. for(pTable = pTable->pInheritFrom; pTable; pTable = pTable->pInheritFrom)
  250. {
  251. if(pTable->mapContents.count(m_iName))
  252. return pTable;
  253. }
  254. return 0;
  255. }
  256. }
  257. return 0;
  258. }
  259. protected:
  260. LuaAttrib::_value_t *m_pValue;
  261. LuaAttrib::_table_t *m_pParent;
  262. unsigned long m_iName;
  263. bool m_bConst;
  264. LuaAttrib *m_pFile;
  265. };
  266. LuaAttribTableAdapter::LuaAttribTableAdapter(LuaAttrib::_table_t *_pTable, LuaAttrib *pFile)
  267. : m_pTable(_pTable), m_pFile(pFile)
  268. {
  269. std::map<unsigned long, bool> mapKeys;
  270. for(const LuaAttrib::_table_t* pTable = _pTable; pTable; pTable = pTable->pInheritFrom)
  271. {
  272. for(std::map<unsigned long, LuaAttrib::_value_t>::const_iterator itr = pTable->mapContents.begin(); itr != pTable->mapContents.end(); ++itr)
  273. {
  274. if(mapKeys.count(itr->first) == 0)
  275. {
  276. mapKeys[itr->first] = true;
  277. m_vValues.push_back(itr->first);
  278. }
  279. }
  280. }
  281. _sortValues();
  282. }
  283. LuaAttribTableAdapter::~LuaAttribTableAdapter()
  284. {
  285. }
  286. unsigned long LuaAttribTableAdapter::getChildCount() throw()
  287. {
  288. return static_cast<unsigned long>(m_vValues.size());
  289. }
  290. IAttributeValue* LuaAttribTableAdapter::getChild(unsigned long iIndex) throw(...)
  291. {
  292. CHECK_RANGE_LTMAX(0, iIndex, getChildCount());
  293. unsigned long iKey = m_vValues[iIndex];
  294. for(LuaAttrib::_table_t* pTable = m_pTable; pTable; pTable = const_cast<LuaAttrib::_table_t*>(pTable->pInheritFrom))
  295. {
  296. if(pTable->mapContents.count(iKey))
  297. {
  298. LuaAttrib::_value_t *pValue = &pTable->mapContents[iKey];
  299. if(pValue->eType == LuaAttrib::_value_t::T_Table && pTable != m_pTable)
  300. {
  301. LuaAttrib::_value_t oValue = *pValue;
  302. m_pFile->_wraptable(&oValue);
  303. oValue.pValue->bTotallyUnchaged = true;
  304. m_pTable->mapContents[iKey] = oValue;
  305. pTable = m_pTable;
  306. pValue = &pTable->mapContents[iKey];
  307. }
  308. return CHECK_ALLOCATION(new (std::nothrow) LuaAttribValueAdapter(pValue, m_pTable, iKey, pTable->pSourceFile));
  309. }
  310. }
  311. THROW_SIMPLE(L"Child not found - should never happen");
  312. }
  313. void LuaAttribTableAdapter::addChild(unsigned long iName) throw(...)
  314. {
  315. if(m_pTable->mapContents.count(iName))
  316. THROW_SIMPLE(L"Cannot add a child which already exists");
  317. m_pTable->mapContents[iName] = LuaAttrib::_value_t();
  318. m_vValues.push_back(iName);
  319. _sortValues();
  320. }
  321. void LuaAttribTableAdapter::deleteChild(unsigned long iIndex, bool bRevertIsSufficient) throw(...)
  322. {
  323. CHECK_RANGE_LTMAX(0, iIndex, getChildCount());
  324. unsigned long iKey = m_vValues[iIndex];
  325. bool bInTopLevel, bInFurtherLevels = false;
  326. bInTopLevel = m_pTable->mapContents.count(iKey) == 1;
  327. for(const LuaAttrib::_table_t* pTable = m_pTable->pInheritFrom; pTable; pTable = pTable->pInheritFrom)
  328. {
  329. bInFurtherLevels = bInFurtherLevels || (pTable->mapContents.count(iKey) == 1);
  330. }
  331. if(bInFurtherLevels)
  332. {
  333. if(!bInTopLevel || !bRevertIsSufficient)
  334. THROW_SIMPLE(L"Cannot delete child as it is set in inherited files");
  335. m_pTable->mapContents.erase(iKey);
  336. }
  337. else
  338. {
  339. m_pTable->mapContents.erase(iKey);
  340. m_vValues.erase(m_vValues.begin() + iIndex);
  341. }
  342. }
  343. unsigned long LuaAttribTableAdapter::findChildIndex(unsigned long iName) throw()
  344. {
  345. unsigned long iIndex = 0;
  346. for(std::vector<unsigned long>::iterator itr = m_vValues.begin(); itr != m_vValues.end(); ++itr, ++iIndex)
  347. {
  348. if(*itr == iName)
  349. return iIndex;
  350. }
  351. return NO_INDEX;
  352. }
  353. //! Alphabetical comparison function for RGD hashes
  354. /*!
  355. Sorting with this function as the predicate causes hashes with
  356. no known text equivalent to be placed first, in ascending order,
  357. followed by hashes with known text equivalents, in alphabetical
  358. order.
  359. */
  360. static bool HashSortLessThen(unsigned long i1, unsigned long i2)
  361. {
  362. RgdDictionary *pDictionary = RgdDictionary::getSingleton();
  363. const char* s1 = pDictionary->hashToAsciiNoThrow(i1);
  364. const char* s2 = pDictionary->hashToAsciiNoThrow(i2);
  365. // Two strings => lexicographical less-than
  366. if(s1 && s2)
  367. return stricmp(s1, s2) < 0;
  368. else
  369. {
  370. // One hash and one string => hash comes first
  371. if(s1)
  372. return false;
  373. else if(s2)
  374. return true;
  375. // Two hashes => smaller one first
  376. return i1 < i2;
  377. }
  378. }
  379. //! HashSortLessThen as a function object
  380. struct HashSortLessThen_t
  381. {
  382. bool operator()(unsigned long a, unsigned long b) const
  383. {
  384. return HashSortLessThen(a, b);
  385. }
  386. };
  387. void LuaAttribTableAdapter::_sortValues()
  388. {
  389. std::sort(m_vValues.begin(), m_vValues.end(), HashSortLessThen);
  390. }
  391. LuaAttribCache::LuaAttribCache()
  392. {
  393. m_L = 0;
  394. m_pDirectory = 0;
  395. m_oEmptyTable.eType = LuaAttrib::_value_t::T_Table;
  396. m_oEmptyTable.pValue = new LuaAttrib::_table_t(0);
  397. m_pEmptyFile = new LuaAttrib;
  398. m_pEmptyFile->setName(L"");
  399. m_oEmptyTable.pValue->pSourceFile = m_pEmptyFile;
  400. }
  401. LuaAttribCache::~LuaAttribCache()
  402. {
  403. delete m_pEmptyFile;
  404. for(std::map<unsigned long, LuaAttrib*>::iterator itr = m_mapFiles.begin(); itr != m_mapFiles.end(); ++itr)
  405. delete itr->second;
  406. if(m_L)
  407. lua_close(m_L);
  408. }
  409. lua_State* LuaAttribCache::getLuaState()
  410. {
  411. if(!m_L)
  412. m_L = luaL_newstate();
  413. return m_L;
  414. }
  415. void LuaAttribCache::setBaseFolder(IDirectory *pFolder)
  416. {
  417. m_pDirectory = pFolder;
  418. }
  419. LuaAttrib* LuaAttribCache::_performGet(const char* sFilename)
  420. {
  421. if(sFilename[0] == 0)
  422. return 0;
  423. unsigned long iHash = CRCCaselessHashSimple((const void*)sFilename, strlen(sFilename));
  424. LuaAttrib* pAttribFile = m_mapFiles[iHash];
  425. if(!pAttribFile)
  426. {
  427. IFile* pFile = 0;
  428. try
  429. {
  430. pFile = m_pDirectory->getStore()->openFile(m_pDirectory->getPath() + RainString(sFilename), FM_Read);
  431. }
  432. CATCH_THROW_SIMPLE_({}, L"Cannot open attrib file \'%S\'", sFilename);
  433. lua_State *L = getLuaState();
  434. int iLuaStackTop = lua_gettop(L);
  435. try
  436. {
  437. CHECK_ALLOCATION(pAttribFile = new LuaAttrib);
  438. pAttribFile->setName(sFilename);
  439. pAttribFile->setBaseFolder(m_pDirectory);
  440. pAttribFile->setCache(this, false);
  441. pAttribFile->loadFromFile(pFile);
  442. delete pFile;
  443. pFile = 0;
  444. }
  445. CATCH_THROW_SIMPLE_({delete pAttribFile; lua_settop(L, iLuaStackTop); delete pFile;}, L"Cannot load attrib file \'%S\'", sFilename);
  446. int iNewStackTop = lua_gettop(L);
  447. if(iNewStackTop > iLuaStackTop)
  448. {
  449. lua_checkstack(L, 5);
  450. lua_createtable(L, iNewStackTop - iLuaStackTop, 0);
  451. lua_pushlightuserdata(L, (void*)pAttribFile);
  452. lua_pushvalue(L, -2);
  453. lua_settable(L, LUA_REGISTRYINDEX);
  454. for(int iIndex = (iLuaStackTop + 1); iIndex <= iNewStackTop; ++iIndex)
  455. {
  456. lua_pushvalue(L, iIndex);
  457. lua_rawseti(L, -2, iIndex - iLuaStackTop);
  458. }
  459. lua_settop(L, iLuaStackTop);
  460. }
  461. else if(iNewStackTop < iLuaStackTop)
  462. {
  463. delete pAttribFile;
  464. THROW_SIMPLE_(L"LuaAttrib loading \'%S\' popped items off the stack", sFilename);
  465. }
  466. m_mapFiles[iHash] = pAttribFile;
  467. }
  468. return pAttribFile;
  469. }
  470. LuaAttrib::_value_t LuaAttribCache::getMetaData(const char* sFilename)
  471. {
  472. LuaAttrib *pFile = _performGet(sFilename);
  473. return pFile ? pFile->m_oMetaData : m_oEmptyTable;
  474. }
  475. LuaAttrib::_value_t LuaAttribCache::getGameData(const char* sFilename)
  476. {
  477. LuaAttrib *pFile = _performGet(sFilename);
  478. return pFile ? pFile->m_oGameData : m_oEmptyTable;
  479. }
  480. size_t LuaAttribCache::writeTableToBinaryFile(const LuaAttrib::_table_t* pTable, IFile* pFile, bool bCacheResult)
  481. {
  482. while(pTable->bTotallyUnchaged && pTable->pInheritFrom)
  483. {
  484. pTable = pTable->pInheritFrom;
  485. bCacheResult = true;
  486. }
  487. try
  488. {
  489. if(m_mapTables.count(pTable) != 0)
  490. {
  491. MemoryWriteFile *pCached = m_mapTables[pTable];
  492. pFile->write(pCached->getBuffer(), 1, pCached->tell());
  493. return pCached->tell();
  494. }
  495. else
  496. {
  497. if(bCacheResult)
  498. {
  499. MemoryWriteFile *pCached = new MemoryWriteFile;
  500. m_mapTables[pTable] = pCached;
  501. pTable->writeToBinary(pCached);
  502. pFile->write(pCached->getBuffer(), 1, pCached->tell());
  503. return pCached->tell();
  504. }
  505. else
  506. {
  507. return pTable->writeToBinary(pFile);
  508. }
  509. }
  510. }
  511. catch(RainException *pE)
  512. {
  513. RETHROW_SIMPLE(pE, L"Error writing Lua table to binary file");
  514. }
  515. }
  516. LuaAttrib::LuaAttrib() throw()
  517. {
  518. m_pDirectory = 0;
  519. m_pCache = 0;
  520. m_bOwnCache = false;
  521. }
  522. LuaAttrib::~LuaAttrib() throw()
  523. {
  524. if(m_bOwnCache)
  525. delete m_pCache;
  526. }
  527. IAttributeValue* LuaAttrib::getGameData() throw(...)
  528. {
  529. return new LuaAttribValueAdapter(&m_oGameData, 0, RgdDictionary::getSingleton()->asciiToHash("GameData"), this);
  530. }
  531. IAttributeValue* LuaAttrib::getMetaData() throw(...)
  532. {
  533. return new LuaAttribValueAdapter(&m_oMetaData, 0, RgdDictionary::getSingleton()->asciiToHash("MetaData"), this);
  534. }
  535. void LuaAttrib::setName(const RainString& sName) throw()
  536. {
  537. m_sName = sName;
  538. }
  539. RainString& LuaAttrib::getName() throw()
  540. {
  541. return m_sName;
  542. }
  543. LuaAttribCache* LuaAttrib::getCache() throw(...)
  544. {
  545. if(!m_pCache)
  546. {
  547. CHECK_ALLOCATION(m_pCache = new (std::nothrow) LuaAttribCache);
  548. m_pCache->setBaseFolder(m_pDirectory);
  549. m_bOwnCache = true;
  550. }
  551. return m_pCache;
  552. }
  553. void LuaAttrib::setCache(LuaAttribCache* pCache, bool bOwnCache) throw(...)
  554. {
  555. CHECK_ASSERT(m_pCache == 0 && pCache != 0);
  556. m_pCache = pCache;
  557. m_bOwnCache = bOwnCache;
  558. }
  559. void LuaAttrib::setBaseFolder(IDirectory *pFolder) throw()
  560. {
  561. m_pDirectory = pFolder;
  562. }
  563. template <class TIn, class TOut>
  564. static TOut static_cast_tolower(TIn v)
  565. {
  566. return std::tolower(static_cast<TOut>(v), std::locale::classic());
  567. }
  568. //! Calculates the length of a string literal at compile-time, rather than leaving it to runtime
  569. /*!
  570. \param s A string literal
  571. Returns two values; the literal itself, and the length of the literal, *not* including the
  572. NULL terminator (hence the -1).
  573. */
  574. #define LITERAL(s) (s), ((sizeof(s)/sizeof(*(s)))-1)
  575. void LuaAttrib::saveToTextFile(IFile *pFile) throw(...)
  576. {
  577. CHECK_ASSERT(m_oGameData.eType == _value_t::T_Table && m_oMetaData.eType == _value_t::T_Table);
  578. try
  579. {
  580. BufferingOutputStream<char> oOutput(pFile);
  581. oOutput.write(LITERAL("----------------------------------------\r\n"));
  582. oOutput.write(LITERAL("-- File: \'"));
  583. oOutput.writeConverting(m_sName.getCharacters(), m_sName.length(), static_cast_tolower<RainChar, char>);
  584. oOutput.write(LITERAL("\'\r\n"));
  585. oOutput.write(LITERAL("-- Created by: Corsix\'s LuaEdit\r\n"));
  586. oOutput.write(LITERAL("-- Note: Feel free to edit by hand!\r\n"));
  587. oOutput.write(LITERAL("-- Partially or fully (c) Relic Entertainment Inc.\r\n\r\n"));
  588. _writeInheritLine(oOutput, m_oGameData, false);
  589. _writeInheritLine(oOutput, m_oMetaData, true);
  590. m_oGameData.pValue->writeToText(oOutput, LITERAL("GameData"));
  591. oOutput.write(LITERAL("\r\n"));
  592. _writeMetaData(oOutput);
  593. }
  594. CATCH_THROW_SIMPLE({}, L"Error encountered while writing Lua data");
  595. }
  596. void LuaAttrib::_writeInheritLine(BufferingOutputStream<char>& oOutput, LuaAttrib::_value_t& oTable, bool bIsMetaData)
  597. {
  598. if(bIsMetaData)
  599. oOutput.write(LITERAL("MetaData = InheritMeta([["));
  600. else
  601. oOutput.write(LITERAL("GameData = Inherit([["));
  602. if(oTable.pValue->pInheritFrom)
  603. {
  604. RainString &sInheritFrom = oTable.pValue->pInheritFrom->pSourceFile->m_sName;
  605. oOutput.writeConverting(sInheritFrom.getCharacters(), sInheritFrom.length(), static_cast_tolower<RainChar, char>);
  606. }
  607. oOutput.write("]])\r\n\r\n", bIsMetaData ? 7 : 5);
  608. }
  609. void LuaAttrib::_writeMetaData(BufferingOutputStream<char>& oOutput)
  610. {
  611. // Re-order the keys alphabetically, stripping out $REF
  612. std::map<unsigned long, _value_t*, HashSortLessThen_t> mapKeys;
  613. for(std::map<unsigned long, _value_t>::iterator itr = m_oMetaData.pValue->mapContents.begin(); itr != m_oMetaData.pValue->mapContents.end(); ++itr)
  614. {
  615. if(itr->first != RgdDictionary::_REF)
  616. mapKeys[itr->first] = &itr->second;
  617. }
  618. // Print keys
  619. for(std::map<unsigned long, _value_t*, HashSortLessThen_t>::iterator itr = mapKeys.begin(); itr != mapKeys.end(); ++itr)
  620. {
  621. if(itr->first == RgdDictionary::_REF)
  622. continue;
  623. oOutput.write(LITERAL("MetaData[\""));
  624. oOutput.write(RgdDictionary::getSingleton()->hashToAscii(itr->first));
  625. oOutput.write(LITERAL("\"] = "));
  626. CHECK_ASSERT(itr->second->eType == _value_t::T_Table && "MetaData children should all be tables");
  627. itr->second->pValue->writeToTextAsMetaDataTable(oOutput);
  628. }
  629. }
  630. static bool StringHasSpecialChars(const char* s)
  631. {
  632. for(; *s; ++s)
  633. {
  634. if(*s == '\\' || *s == '\r' || *s == '\n' || *s == '\"')
  635. return true;
  636. }
  637. return false;
  638. }
  639. void LuaAttrib::_table_t::writeToTextAsMetaDataTable(BufferingOutputStream<char>& oOutput)
  640. {
  641. oOutput.write(LITERAL("{"));
  642. for(std::map<unsigned long, _value_t>::iterator itr = mapContents.begin(); itr != mapContents.end(); ++itr)
  643. {
  644. oOutput.write(RgdDictionary::getSingleton()->hashToAscii(itr->first));
  645. oOutput.write(LITERAL(" = "));
  646. switch(itr->second.eType)
  647. {
  648. case LuaAttrib::_value_t::T_Float: {
  649. float fValue = itr->second.fValue;
  650. char sBuffer[64];
  651. if(fValue == floor(fValue) && fValue <= static_cast<float>(std::numeric_limits<long>::max()) && fValue >= static_cast<float>(std::numeric_limits<long>::min()))
  652. {
  653. sprintf(sBuffer, "%li", static_cast<long>(fValue));
  654. }
  655. else
  656. {
  657. sprintf(sBuffer, "%.3f", fValue);
  658. }
  659. oOutput.write(sBuffer);
  660. break; }
  661. case LuaAttrib::_value_t::T_String:
  662. oOutput.write(LITERAL("[["));
  663. oOutput.write(itr->second.sValue, itr->second.iLength);
  664. oOutput.write(LITERAL("]]"));
  665. break;
  666. case LuaAttrib::_value_t::T_Boolean:
  667. if(itr->second.bValue)
  668. oOutput.write(LITERAL("true"));
  669. else
  670. oOutput.write(LITERAL("false"));
  671. break;
  672. case LuaAttrib::_value_t::T_Integer: {
  673. char sBuffer[64];
  674. sprintf(sBuffer, "%li", itr->second.iValue);
  675. break; }
  676. case LuaAttrib::_value_t::T_Table:
  677. itr->second.pValue->writeToTextAsMetaDataTable(oOutput);
  678. break;
  679. default:
  680. THROW_SIMPLE_(L"Cannot write meta data of type %i to text file", static_cast<int>(itr->second.eType));
  681. }
  682. oOutput.write(LITERAL(", "));
  683. }
  684. oOutput.write(LITERAL("}\r\n"));
  685. }
  686. void LuaAttrib::_table_t::writeToText(BufferingOutputStream<char>& oOutput, const char* sPrefix, size_t iPrefixLength)
  687. {
  688. // Re-order the keys alphabetically, stripping out $REF
  689. std::map<unsigned long, _value_t*, HashSortLessThen_t> mapKeys;
  690. for(std::map<unsigned long, _value_t>::iterator itr = mapContents.begin(); itr != mapContents.end(); ++itr)
  691. {
  692. if(itr->first != RgdDictionary::_REF)
  693. mapKeys[itr->first] = &itr->second;
  694. }
  695. // Print keys
  696. for(std::map<unsigned long, _value_t*, HashSortLessThen_t>::iterator itr = mapKeys.begin(); itr != mapKeys.end(); ++itr)
  697. {
  698. if(itr->second->eType != _value_t::T_Table || itr->second->pValue->mapContents.count(RgdDictionary::_REF) == 1)
  699. {
  700. oOutput.write(sPrefix, iPrefixLength);
  701. oOutput.write(LITERAL("[\""));
  702. oOutput.write(RgdDictionary::getSingleton()->hashToAscii(itr->first));
  703. oOutput.write(LITERAL("\"] = "));
  704. switch(itr->second->eType)
  705. {
  706. case _value_t::T_Float: {
  707. char sBuffer[64];
  708. sprintf(sBuffer, "%.5f", itr->second->fValue);
  709. oOutput.write(sBuffer);
  710. break; }
  711. case _value_t::T_String:
  712. if(StringHasSpecialChars(itr->second->sValue))
  713. {
  714. oOutput.write(LITERAL("[["));
  715. oOutput.write(itr->second->sValue, itr->second->iLength);
  716. oOutput.write(LITERAL("]]"));
  717. }
  718. else
  719. {
  720. oOutput.write(LITERAL("\""));
  721. oOutput.write(itr->second->sValue, itr->second->iLength);
  722. oOutput.write(LITERAL("\""));
  723. }
  724. break;
  725. case _value_t::T_Boolean:
  726. if(itr->second->bValue)
  727. oOutput.write(LITERAL("true"));
  728. else
  729. oOutput.write(LITERAL("false"));
  730. break;
  731. case _value_t::T_Integer: {
  732. char sBuffer[64];
  733. sprintf(sBuffer, "%li", itr->second->iValue);
  734. break; }
  735. case _value_t::T_Table: {
  736. oOutput.write(LITERAL("Reference([["));
  737. RainString sReferenceFrom = itr->second->pValue->pInheritFrom->pSourceFile->m_sName;
  738. oOutput.writeConverting(sReferenceFrom.getCharacters(), sReferenceFrom.length(), static_cast_tolower<RainChar, char>);
  739. oOutput.write(LITERAL("]])"));
  740. break; }
  741. default:
  742. THROW_SIMPLE_(L"Cannot write data of type %i to text file", static_cast<int>(itr->second->eType));
  743. }
  744. oOutput.write(LITERAL("\r\n"));
  745. }
  746. if(itr->second->eType == _value_t::T_Table)
  747. {
  748. _table_t *pChildTable = itr->second->pValue;
  749. if(!pChildTable->bTotallyUnchaged)
  750. {
  751. const char *sName = RgdDictionary::getSingleton()->hashToAscii(itr->first);
  752. size_t iNameLength = strlen(sName);
  753. char *sNewPrefix = CHECK_ALLOCATION(new (std::nothrow) char[iPrefixLength + 5 + iNameLength]);
  754. sprintf(sNewPrefix, "%s[\"%s\"]", sPrefix, sName);
  755. try
  756. {
  757. pChildTable->writeToText(oOutput, sNewPrefix, iPrefixLength + 4 + iNameLength);
  758. }
  759. CATCH_THROW_SIMPLE_(delete[] sNewPrefix, L"Cannot write child table \'%S\'", sName);
  760. delete[] sNewPrefix;
  761. }
  762. }
  763. }
  764. }
  765. #undef LITERAL
  766. void LuaAttrib::loadFromFile(IFile *pFile) throw(...)
  767. {
  768. m_oGameData.free();
  769. lua_State *L = getCache()->getLuaState();
  770. lua_checkstack(L, 1);
  771. if(pFile->lua_load(L, "attrib file") != 0)
  772. {
  773. // Try and parse the Lua error into a nice exception
  774. // Lua gives an error as "file:line:message", which can be reparsed into an exception
  775. size_t iLength;
  776. const char* sErr = lua_tolstring(L, -1, &iLength);
  777. if(sErr)
  778. {
  779. const char* sLineBegin = strchr(sErr, ':');
  780. if(sLineBegin)
  781. {
  782. ++sLineBegin;
  783. const char* sMessageBegin = strchr(sLineBegin, ':');
  784. if(sMessageBegin)
  785. {
  786. ++sMessageBegin;
  787. throw new RainException(__FILE__, __LINE__, RainString(L"Cannot parse attrib file"), new
  788. RainException(RainString(sErr, sLineBegin - sErr - 1), atoi(sLineBegin), RainString(sMessageBegin, iLength - (sMessageBegin - sErr))));
  789. }
  790. }
  791. }
  792. // ... or just throw a simple exception
  793. throw new RainException(__FILE__, __LINE__, RainString(L"Cannot parse attrib file: ") + RainString(L, -1));
  794. }
  795. Proto *pPrototype = reinterpret_cast<Closure*>(const_cast<void*>(lua_topointer(L, -1)))->l.p;
  796. _execute(pPrototype);
  797. }
  798. void LuaAttrib::_loadk(_value_t *pDestination, Proto* pFunction, int iK)
  799. {
  800. pDestination->free();
  801. TValue *pValue = pFunction->k + iK;
  802. switch(pValue->tt)
  803. {
  804. case LUA_TNIL:
  805. break;
  806. case LUA_TBOOLEAN:
  807. pDestination->eType = _value_t::T_Boolean;
  808. pDestination->bValue = pValue->value.b != 0;
  809. break;
  810. case LUA_TNUMBER:
  811. pDestination->eType = _value_t::T_Float;
  812. pDestination->fValue = static_cast<float>(pValue->value.n);
  813. break;
  814. case LUA_TSTRING:
  815. pDestination->eType = _value_t::T_String;
  816. pDestination->sValue = svalue(pValue);
  817. pDestination->iLength = static_cast<long>(tsvalue(pValue)->len);
  818. break;
  819. default:
  820. THROW_SIMPLE_(L"Unsupported constant type: %i", static_cast<int>(pValue->tt));
  821. };
  822. }
  823. unsigned long LuaAttrib::_hashvalue(_value_t* pValue)
  824. {
  825. switch(pValue->eType)
  826. {
  827. case _value_t::T_String:
  828. return RgdDictionary::getSingleton()->asciiToHash(pValue->sValue, pValue->iLength);
  829. case _value_t::T_Boolean:
  830. return RgdDictionary::getSingleton()->asciiToHash(pValue->bValue ? "true" : "false");
  831. case _value_t::T_Float: {
  832. char sBuffer[32];
  833. if(floor(pValue->fValue) == pValue->fValue)
  834. sprintf(sBuffer, "%.0f", pValue->fValue);
  835. else
  836. sprintf(sBuffer, "%.7f", pValue->fValue);
  837. return RgdDictionary::getSingleton()->asciiToHash(sBuffer, strlen(sBuffer)); }
  838. default:
  839. THROW_SIMPLE_("Cannot hash value of type %i", static_cast<int>(pValue->eType));
  840. }
  841. }
  842. void LuaAttrib::_wraptable(_value_t* pValue) throw(...)
  843. {
  844. _table_t* pTable = pValue->pValue;
  845. pValue->pValue = CHECK_ALLOCATION(new (std::nothrow) _table_t(this));
  846. pValue->pValue->pInheritFrom = pTable;
  847. }
  848. template <class T>
  849. struct AutoDeleteArray
  850. {
  851. AutoDeleteArray(T* p) : m_p(p) {}
  852. ~AutoDeleteArray() {delete[] m_p;}
  853. T* m_p;
  854. };
  855. void LuaAttrib::_execute(Proto* pFunction)
  856. {
  857. _value_t oTempK1, oTempK2;
  858. _value_t* pStack = CHECK_ALLOCATION(new (std::nothrow) _value_t[pFunction->maxstacksize]);
  859. Instruction *I;
  860. AutoDeleteArray<_value_t> DeleteStack(pStack);
  861. bool bContinue = true;
  862. try
  863. {
  864. for(I = pFunction->code; bContinue; ++I)
  865. {
  866. switch(GET_OPCODE(*I))
  867. {
  868. case OP_MOVE: // A B R(A) := R(B)
  869. pStack[GETARG_A(*I)] = pStack[GETARG_B(*I)];
  870. break;
  871. case OP_LOADK: // A Bx R(A) := Kst(Bx)
  872. _loadk(pStack + GETARG_A(*I), pFunction, GETARG_Bx(*I));
  873. break;
  874. case OP_LOADBOOL: // A B C R(A) := (Bool)B; if (C) pc++
  875. pStack[GETARG_A(*I)].free();
  876. pStack[GETARG_A(*I)].eType = _value_t::T_Boolean;
  877. pStack[GETARG_A(*I)].bValue = GETARG_B(*I) != 0;
  878. if(GETARG_C(*I))
  879. ++I;
  880. break;
  881. case OP_LOADNIL: // A B R(A) := ... := R(B) := nil
  882. for(int i = GETARG_A(*I); i <= GETARG_B(*I); ++i)
  883. pStack[i].free();
  884. break;
  885. case OP_GETGLOBAL:{// A Bx R(A) := Gbl[Kst(Bx)]
  886. unsigned int iHash = tsvalue(pFunction->k + GETARG_Bx(*I))->hash;
  887. const char* s = svalue(pFunction->k + GETARG_Bx(*I));
  888. switch(iHash)
  889. {
  890. case LuaCompileTimeHashes::GameData:
  891. pStack[GETARG_A(*I)] = m_oGameData;
  892. break;
  893. case LuaCompileTimeHashes::MetaData:
  894. pStack[GETARG_A(*I)] = m_oMetaData;
  895. break;
  896. case LuaCompileTimeHashes::Inherit:
  897. case LuaCompileTimeHashes::Reference:
  898. pStack[GETARG_A(*I)].free();
  899. pStack[GETARG_A(*I)].eType = _value_t::T_ReferenceFn;
  900. break;
  901. case LuaCompileTimeHashes::InheritMeta:
  902. pStack[GETARG_A(*I)].free();
  903. pStack[GETARG_A(*I)].eType = _value_t::T_InheritMetaFn;
  904. break;
  905. default:
  906. THROW_SIMPLE_(L"Unsupported global: %S", s);
  907. }
  908. break; }
  909. case OP_GETTABLE: {// A B C R(A) := R(B)[RK(C)]
  910. _value_t* pTable = pStack + GETARG_B(*I);
  911. CHECK_ASSERT(pTable->eType == _value_t::T_Table && pTable->pValue && "Using a non-table value as a table");
  912. _value_t* pKey = ISK(GETARG_C(*I)) ? (_loadk(&oTempK1, pFunction, INDEXK(GETARG_C(*I))), &oTempK1) : pStack + GETARG_C(*I);
  913. unsigned long iHash = _hashvalue(pKey);
  914. _value_t oValue;
  915. _table_t* pLookin = pTable->pValue;
  916. while(pLookin && pLookin->mapContents.count(iHash) == 0)
  917. pLookin = const_cast<_table_t*>(pLookin->pInheritFrom);
  918. if(pLookin)
  919. {
  920. oValue = pLookin->mapContents[iHash];
  921. if(oValue.eType == _value_t::T_Table && pLookin != pTable->pValue)
  922. {
  923. _wraptable(&oValue);
  924. pTable->pValue->mapContents[iHash] = oValue;
  925. }
  926. }
  927. pStack[GETARG_A(*I)] = oValue;
  928. break; }
  929. case OP_SETGLOBAL:{// A Bx Gbl[Kst(Bx)] := R(A)
  930. unsigned int iHash = tsvalue(pFunction->k + GETARG_Bx(*I))->hash;
  931. if(iHash == LuaCompileTimeHashes::GameData)
  932. m_oGameData = pStack[GETARG_A(*I)];
  933. else if(iHash == LuaCompileTimeHashes::MetaData)
  934. m_oMetaData = pStack[GETARG_A(*I)];
  935. else
  936. THROW_SIMPLE_(L"Unsupported global for write access: %S", svalue(pFunction->k + GETARG_Bx(*I)));
  937. break; }
  938. case OP_SETTABLE: {// A B C R(A)[RK(B)] := RK(C)
  939. _value_t* pTable = pStack + GETARG_A(*I);
  940. _value_t* pKey = ISK(GETARG_B(*I)) ? (_loadk(&oTempK2, pFunction, INDEXK(GETARG_B(*I))), &oTempK2) : pStack + GETARG_B(*I);
  941. _value_t* pValue = ISK(GETARG_C(*I)) ? (_loadk(&oTempK1, pFunction, INDEXK(GETARG_C(*I))), &oTempK1) : pStack + GETARG_C(*I);
  942. if(pTable->eType != _value_t::T_Table || !pTable->pValue)
  943. {
  944. THROW_SIMPLE_(L"Using a non-table value as a table: table['%S'] = '%S' on line %i",
  945. pKey->eType == _value_t::T_String ? pKey->sValue : "(non string)",
  946. pValue->eType == _value_t::T_String ? pValue->sValue : "(non string)",
  947. static_cast<int>(pFunction->sizelineinfo == pFunction->sizecode ? pFunction->lineinfo[I - pFunction->code] : -1));
  948. }
  949. unsigned long iHash = _hashvalue(pKey);
  950. pTable->pValue->mapContents[iHash] = *pValue;
  951. break; }
  952. case OP_NEWTABLE: // A B C R(A) := {} (size = B,C)
  953. pStack[GETARG_A(*I)].free();
  954. pStack[GETARG_A(*I)].eType = _value_t::T_Table;
  955. CHECK_ALLOCATION(pStack[GETARG_A(*I)].pValue = new (std::nothrow) _table_t(this));
  956. break;
  957. case OP_NOT: { // A B R(A) := not R(B)
  958. bool bValue = false;
  959. _value_t* pValue = pStack + GETARG_B(*I);
  960. if(pValue->eType == _value_t::T_Nil)
  961. bValue = true;
  962. if(pValue->eType == _value_t::T_Boolean)
  963. bValue = !pValue->bValue;
  964. pValue = pStack + GETARG_A(*I);
  965. pValue->free();
  966. pValue->eType = _value_t::T_Boolean;
  967. pValue->bValue = bValue;
  968. break; }
  969. case OP_JMP: // sBx pc+=sBx
  970. I += GETARG_sBx(*I);
  971. break;
  972. case OP_CALL: { // A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
  973. _value_t* pFunction = pStack + GETARG_A(*I);
  974. CHECK_ASSERT((pFunction->eType == _value_t::T_InheritMetaFn || pFunction->eType == _value_t::T_ReferenceFn) && "Attempt to call a non-function");
  975. CHECK_ASSERT(GETARG_B(*I) != 1 && "Function call requires at least one argument");
  976. CHECK_ASSERT(GETARG_C(*I) == 2 && "Function call must have exactly one result");
  977. _value_t* pFilename = pStack + GETARG_A(*I) + 1;
  978. CHECK_ASSERT(pFilename->eType == _value_t::T_String && "Can only inherit/reference a string filename");
  979. try
  980. {
  981. if(pFunction->eType == _value_t::T_InheritMetaFn)
  982. pStack[GETARG_A(*I)] = getCache()->getMetaData(pFilename->sValue);
  983. else
  984. pStack[GETARG_A(*I)] = getCache()->getGameData(pFilename->sValue);
  985. if(pStack[GETARG_A(*I)].eType != _value_t::T_Table)
  986. THROW_SIMPLE(L"Inheritance function did not return a table");
  987. }
  988. CATCH_THROW_SIMPLE_({}, L"Cannot load \'%S\'", pFilename->sValue);
  989. if(pStack[GETARG_A(*I)].eType == _value_t::T_Table)
  990. {
  991. _wraptable(pStack + GETARG_A(*I));
  992. pStack[GETARG_A(*I)].pValue->mapContents[RgdDictionary::_REF] = *pFilename;
  993. pStack[GETARG_A(*I)].pValue->pSourceFile = this;
  994. }
  995. break; }
  996. case OP_RETURN: // A B return R(A), ... ,R(A+B-2) (see note)
  997. bContinue = false;
  998. break;
  999. default:
  1000. THROW_SIMPLE_(L"Unsupported Lua Opcode: %i", static_cast<int>(GET_OPCODE(*I)));
  1001. }
  1002. }
  1003. }
  1004. catch(RainException *pE)
  1005. {
  1006. RainString sSource(L"Lua function");
  1007. if(pFunction->source && getstr(pFunction->source))
  1008. sSource = RainString(getstr(pFunction->source), pFunction->source->tsv.len);
  1009. unsigned long iLine = 0;
  1010. if(pFunction->lineinfo)
  1011. iLine = pFunction->lineinfo[I - pFunction->code];
  1012. throw new RainException(sSource, iLine, L"Error while executing Lua in custom VM", pE);
  1013. }
  1014. }
  1015. LuaAttrib::_value_t::_value_t()
  1016. {
  1017. eType = T_Nil;
  1018. }
  1019. LuaAttrib::_value_t::_value_t(const _value_t& oOther)
  1020. {
  1021. eType = T_Nil;
  1022. *this = oOther;
  1023. }
  1024. LuaAttrib::_value_t::~_value_t()
  1025. {
  1026. free();
  1027. }
  1028. LuaAttrib::_value_t& LuaAttrib::_value_t::operator =(const _value_t& oOther)
  1029. {
  1030. if(oOther.eType == T_Table)
  1031. ++oOther.pValue->iReferenceCount;
  1032. free();
  1033. memcpy(this, &oOther, sizeof(_value_t));
  1034. return *this;
  1035. }
  1036. void LuaAttrib::_value_t::free()
  1037. {
  1038. if(eType == T_Table)
  1039. {
  1040. if(--pValue->iReferenceCount == 0)
  1041. delete pValue;
  1042. }
  1043. eType = T_Nil;
  1044. }
  1045. LuaAttrib::_table_t::_table_t(LuaAttrib* pFile)
  1046. {
  1047. pSourceFile = pFile;
  1048. iReferenceCount = 1;
  1049. pInheritFrom = 0;
  1050. bTotallyUnchaged = false;
  1051. }
  1052. LuaAttrib::_table_t::~_table_t()
  1053. {
  1054. if(pInheritFrom && --((_table_t*)pInheritFrom)->iReferenceCount == 0)
  1055. delete pInheritFrom;
  1056. }
  1057. size_t LuaAttrib::_table_t::writeToBinary(IFile* pFile) const
  1058. {
  1059. // Generate a list of keys to write
  1060. std::map<unsigned long, const _table_t*> mapKeys;
  1061. for(const _table_t* pTable = this; pTable; pTable = pTable->pInheritFrom)
  1062. {
  1063. for(std::map<unsigned long, _value_t>::const_iterator itr = pTable->mapContents.begin(); itr != pTable->mapContents.end(); ++itr)
  1064. {
  1065. if(mapKeys.count(itr->first) == 0)
  1066. mapKeys[itr->first] = pTable;
  1067. }
  1068. }
  1069. // Prepare a memory buffer for the header and leave space in the output to write it when done
  1070. size_t iHeaderLength = mapKeys.size() * sizeof(long) * 3;
  1071. MemoryWriteFile fTableHeader(iHeaderLength);
  1072. pFile->writeArray(fTableHeader.getBuffer(), iHeaderLength);
  1073. // Write keys
  1074. size_t iDataLength = 0;
  1075. for(std::map<unsigned long, const _table_t*>::iterator itr = mapKeys.begin(); itr != mapKeys.end(); ++itr)
  1076. {
  1077. const _value_t& oValue = itr->second->mapContents.find(itr->first)->second;
  1078. // Set the data type code and alignment length for the type being written
  1079. bool bConvertStringToWide = false;
  1080. long iDataTypeCode;
  1081. size_t iAlignLength;
  1082. switch(oValue.eType)
  1083. {
  1084. case LuaAttrib::_value_t::T_Float:
  1085. iDataTypeCode = BinaryAttribDataTypeCode<float>::code;
  1086. iAlignLength = sizeof(float);
  1087. break;
  1088. case LuaAttrib::_value_t::T_String:
  1089. iDataTypeCode = BinaryAttribDataTypeCode<char*>::code;
  1090. // The the opportunity to check if the string needs to be converted to a unicode (wide) string
  1091. // This is done for all strings of form "$[0-9]+"
  1092. if(oValue.sValue[0] == '$' && oValue.iLength >= 2)
  1093. {
  1094. bConvertStringToWide = true;
  1095. for(long i = 1; i < oValue.iLength; ++i)
  1096. {
  1097. if(oValue.sValue[i] < '0' || oValue.sValue[i] > '9')
  1098. {
  1099. bConvertStringToWide = false;
  1100. break;
  1101. }
  1102. }
  1103. if(bConvertStringToWide)
  1104. {
  1105. iDataTypeCode = BinaryAttribDataTypeCode<wchar_t*>::code;
  1106. iAlignLength = sizeof(wchar_t);
  1107. }
  1108. }
  1109. break;
  1110. case LuaAttrib::_value_t::T_Boolean:
  1111. iDataTypeCode = BinaryAttribDataTypeCode<bool>::code;
  1112. iAlignLength = sizeof(char);
  1113. break;
  1114. case LuaAttrib::_value_t::T_Integer:
  1115. iDataTypeCode = BinaryAttribDataTypeCode<long>::code;
  1116. iAlignLength = sizeof(long);
  1117. break;
  1118. case LuaAttrib::_value_t::T_Table:
  1119. iDataTypeCode = BinaryAttribDataTypeCode<IAttributeTable>::code;
  1120. iAlignLength = sizeof(long);
  1121. break;
  1122. default:
  1123. THROW_SIMPLE_(L"Cannot write type %li to binary file", static_cast<long>(oValue.eType));
  1124. }
  1125. // Write any alignment bytes
  1126. if(iDataLength % iAlignLength)
  1127. {
  1128. #define max2(a, b) ((a) > (b) ? (a) : (b))
  1129. #define max4(a, b, c, d) max2( max2((a), (b)), max2((c), (d)) )
  1130. unsigned char cNullBytes[max4(sizeof(long), sizeof(float), sizeof(char), sizeof(wchar_t))] = {0};
  1131. #undef max4
  1132. #undef max2
  1133. iAlignLength = iAlignLength - (iDataLength % iAlignLength);
  1134. iDataLength += iAlignLength;
  1135. pFile->writeArray(cNullBytes, iAlignLength);
  1136. }
  1137. fTableHeader.writeOne(itr->first);
  1138. fTableHeader.writeOne(iDataTypeCode);
  1139. fTableHeader.writeOne(static_cast<unsigned long>(iDataLength));
  1140. switch(oValue.eType)
  1141. {
  1142. case LuaAttrib::_value_t::T_Float:
  1143. pFile->writeOne(oValue.fValue);
  1144. iDataLength += sizeof(float);
  1145. break;
  1146. case LuaAttrib::_value_t::T_String:
  1147. if(bConvertStringToWide)
  1148. {
  1149. wchar_t c;
  1150. for(long i = 0; i <= oValue.iLength; ++i)
  1151. {
  1152. c = oValue.sValue[i];
  1153. pFile->writeOne(c);
  1154. }
  1155. iDataLength += oValue.iLength * sizeof(wchar_t);
  1156. }
  1157. else
  1158. {
  1159. pFile->writeArray(oValue.sValue, oValue.iLength + 1);
  1160. iDataLength += oValue.iLength + 1;
  1161. }
  1162. break;
  1163. case LuaAttrib::_value_t::T_Boolean: {
  1164. pFile->writeOne(oValue.bValue ? BinaryAttribBooleanEncoding<true>::value : BinaryAttribBooleanEncoding<false>::value);
  1165. iDataLength += sizeof BinaryAttribBooleanEncoding<true>::value;
  1166. break; }
  1167. case LuaAttrib::_value_t::T_Integer:
  1168. pFile->writeOne(oValue.iValue);
  1169. iDataLength += sizeof(long);
  1170. break;
  1171. case LuaAttrib::_value_t::T_Table:
  1172. iDataLength += pSourceFile->getCache()->writeTableToBinaryFile(oValue.pValue, pFile, itr->second != this);
  1173. break;
  1174. };
  1175. }
  1176. // Write table header
  1177. pFile->seek(-static_cast<seek_offset_t>(iDataLength + iHeaderLength), SR_Current);
  1178. pFile->writeArray(fTableHeader.getBuffer(), iHeaderLength);
  1179. pFile->seek(-static_cast<seek_offset_t>(iDataLength), SR_Current);
  1180. return iDataLength + iHeaderLength;
  1181. }
  1182. #endif