persistenceManager.cpp 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "persistenceManager.h"
  23. #include "console/simSet.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "core/stream/fileStream.h"
  27. #include "gui/core/guiTypes.h"
  28. #include "materials/customMaterialDefinition.h"
  29. #include "ts/tsShapeConstruct.h"
  30. #include "sim/netStringTable.h"
  31. IMPLEMENT_CONOBJECT(PersistenceManager);
  32. ConsoleDocClass( PersistenceManager,
  33. "@brief this class manages updating SimObjects in the file they were "
  34. "created in non-destructively (mostly aimed at datablocks and materials).\n\n"
  35. "Basic scripting interface:\n\n"
  36. " - Creation: new PersistenceManager(FooManager);\n"
  37. " - Flag objects as dirty: FooManager.setDirty(<object name or id>);\n"
  38. " - Remove objects from dirty list: FooManager.removeDirty(<object name or id>);\n"
  39. " - List all currently dirty objects: FooManager.listDirty();\n"
  40. " - Check to see if an object is dirty: FooManager.isDirty(<object name or id>);\n"
  41. " - Save dirty objects to their files: FooManager.saveDirty();\n\n"
  42. "@note Dirty objects don't update their files until saveDirty() is "
  43. "called so you can change their properties after you flag them as dirty\n\n"
  44. "@note Currently only used by editors, not intended for actual game development\n\n"
  45. "@ingroup Console\n"
  46. "@ingroup Editors\n"
  47. "@internal");
  48. PersistenceManager::PersistenceManager()
  49. {
  50. mCurrentObject = NULL;
  51. mCurrentFile = NULL;
  52. VECTOR_SET_ASSOCIATION(mLineBuffer);
  53. mLineBuffer.reserve(2048);
  54. }
  55. PersistenceManager::~PersistenceManager()
  56. {
  57. mDirtyObjects.clear();
  58. }
  59. bool PersistenceManager::onAdd()
  60. {
  61. if (!Parent::onAdd())
  62. return false;
  63. return true;
  64. }
  65. void PersistenceManager::onRemove()
  66. {
  67. Parent::onRemove();
  68. }
  69. void PersistenceManager::clearLineBuffer()
  70. {
  71. for (U32 i = 0; i < mLineBuffer.size(); i++)
  72. {
  73. dFree( mLineBuffer[ i ] );
  74. mLineBuffer[ i ] = NULL;
  75. }
  76. mLineBuffer.clear();
  77. }
  78. void PersistenceManager::deleteObject(ParsedObject* object)
  79. {
  80. if (object)
  81. {
  82. // Clear up used property memory
  83. for (U32 j = 0; j < object->properties.size(); j++)
  84. {
  85. ParsedProperty& prop = object->properties[j];
  86. if (prop.value)
  87. {
  88. dFree( prop.value );
  89. prop.value = NULL;
  90. }
  91. }
  92. object->properties.clear();
  93. // Delete the parsed object
  94. SAFE_DELETE(object);
  95. }
  96. }
  97. void PersistenceManager::clearObjects()
  98. {
  99. // Clean up the object buffer
  100. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  101. deleteObject(mObjectBuffer[i]);
  102. mObjectBuffer.clear();
  103. // We shouldn't have anything in the object stack
  104. // but let's clean it up just in case
  105. // Clean up the object buffer
  106. for (U32 i = 0; i < mObjectStack.size(); i++)
  107. deleteObject(mObjectStack[i]);
  108. mObjectStack.clear();
  109. // Finally make sure there isn't a current object
  110. deleteObject(mCurrentObject);
  111. }
  112. void PersistenceManager::clearFileData()
  113. {
  114. // Clear the active file name
  115. if (mCurrentFile)
  116. {
  117. dFree( mCurrentFile );
  118. mCurrentFile = NULL;
  119. }
  120. // Clear the file objects
  121. clearObjects();
  122. // Clear the line buffer
  123. clearLineBuffer();
  124. // Clear the tokenizer data
  125. mParser.clear();
  126. }
  127. void PersistenceManager::clearAll()
  128. {
  129. // Clear the file data in case it hasn't cleared yet
  130. clearFileData();
  131. // Clear the dirty object list
  132. mDirtyObjects.clear();
  133. // Clear the remove field list
  134. mRemoveFields.clear();
  135. }
  136. bool PersistenceManager::readFile(const char* fileName)
  137. {
  138. // Clear our previous file buffers just in
  139. // case saveDirtyFile() didn't catch it
  140. clearFileData();
  141. // Handle an object writing out to a new file
  142. if ( !Torque::FS::IsFile( fileName ) )
  143. {
  144. // Set our current file
  145. mCurrentFile = dStrdup(fileName);
  146. return true;
  147. }
  148. // Try to open the file
  149. FileStream stream;
  150. stream.open( fileName, Torque::FS::File::Read );
  151. if ( stream.getStatus() != Stream::Ok )
  152. {
  153. Con::errorf("PersistenceManager::readFile() - Failed to open %s", fileName);
  154. return false;
  155. }
  156. // The file is good so read it in
  157. mCurrentFile = dStrdup(fileName);
  158. while(stream.getStatus() != Stream::EOS)
  159. {
  160. U8* buffer = ( U8* ) dMalloc( 2048 );
  161. dMemset(buffer, 0, 2048);
  162. stream.readLine(buffer, 2048);
  163. mLineBuffer.push_back((const char*)buffer);
  164. }
  165. // Because of the way that writeLine() works we need to
  166. // make sure we don't have an empty last line or else
  167. // we will get an extra line break
  168. if (mLineBuffer.size() > 0)
  169. {
  170. if (mLineBuffer.last() && mLineBuffer.last()[0] == 0)
  171. {
  172. dFree(mLineBuffer.last());
  173. mLineBuffer.pop_back();
  174. }
  175. }
  176. stream.close();
  177. //Con::printf("Successfully opened and read %s", mCurrentFile);
  178. return true;
  179. }
  180. void PersistenceManager::killObject()
  181. {
  182. // Don't save this object
  183. SAFE_DELETE(mCurrentObject);
  184. // If there is an object in the stack restore it
  185. if (mObjectStack.size() > 0)
  186. {
  187. mCurrentObject = mObjectStack.last();
  188. mObjectStack.pop_back();
  189. }
  190. }
  191. void PersistenceManager::saveObject()
  192. {
  193. // Now that we have all of the data attempt to
  194. // find the corresponding SimObject
  195. mCurrentObject->simObject = Sim::findObject(mCurrentFile, mCurrentObject->endLine + 1);
  196. // Save this object
  197. mObjectBuffer.push_back(mCurrentObject);
  198. mCurrentObject = NULL;
  199. // If there is an object in the stack restore it
  200. if (mObjectStack.size() > 0)
  201. {
  202. mCurrentObject = mObjectStack.last();
  203. mObjectStack.pop_back();
  204. }
  205. }
  206. void PersistenceManager::parseObject()
  207. {
  208. // We *should* already be in position but just in case...
  209. if (!mParser.tokenICmp("new") &&
  210. !mParser.tokenICmp("singleton") &&
  211. !mParser.tokenICmp("datablock"))
  212. {
  213. Con::errorf("PersistenceManager::parseObject() - handed a position that doesn't point to an object \
  214. creation keyword (new, singleton, datablock)");
  215. return;
  216. }
  217. // If there is an object already being parsed then
  218. // push it into the stack to finish later
  219. if (mCurrentObject)
  220. mObjectStack.push_back(mCurrentObject);
  221. mCurrentObject = new ParsedObject;
  222. //// If this object declaration is being assigned to a variable then
  223. //// consider that the "start" of the declaration (otherwise we could
  224. //// get a script compile error if we delete the object declaration)
  225. mParser.regressToken(true);
  226. if (mParser.tokenICmp("="))
  227. {
  228. // Ok, we are at an '='...back up to the beginning of that variable
  229. mParser.regressToken(true);
  230. // Get the startLine and startPosition
  231. mCurrentObject->startLine = mParser.getCurrentLine();
  232. mCurrentObject->startPosition = mParser.getTokenLineOffset();
  233. // Advance back to the object declaration
  234. mParser.advanceToken(true);
  235. mParser.advanceToken(true);
  236. }
  237. else
  238. {
  239. // Advance back to the object declaration
  240. mParser.advanceToken(true);
  241. // Get the startLine and startPosition
  242. mCurrentObject->startLine = mParser.getCurrentLine();
  243. mCurrentObject->startPosition = mParser.getTokenLineOffset();
  244. }
  245. if (mObjectStack.size() > 0)
  246. mCurrentObject->parentObject = mObjectStack.last();
  247. // The next token should be the className
  248. mCurrentObject->className = StringTable->insert(mParser.getNextToken());
  249. // Advance to '('
  250. mParser.advanceToken(true);
  251. if (!mParser.tokenICmp("("))
  252. {
  253. Con::errorf("PersistenceManager::parseObject() - badly formed object \
  254. declaration on line %d - was expecting a '(' character", mParser.getCurrentLine()+1);
  255. // Remove this object without saving it
  256. killObject();
  257. return;
  258. }
  259. // The next token should either be the object name or ')'
  260. mParser.advanceToken(true);
  261. if (mParser.tokenICmp(")"))
  262. {
  263. mCurrentObject->name = StringTable->EmptyString();
  264. mCurrentObject->nameLine = mParser.getCurrentLine();
  265. mCurrentObject->namePosition = mParser.getTokenLineOffset();
  266. }
  267. else
  268. {
  269. mCurrentObject->name = StringTable->insert(mParser.getToken());
  270. mCurrentObject->nameLine = mParser.getCurrentLine();
  271. mCurrentObject->namePosition = mParser.getTokenLineOffset();
  272. // Advance to either ')' or ':'
  273. mParser.advanceToken(true);
  274. if (mParser.tokenICmp(":"))
  275. {
  276. // Advance past the object we are copying from
  277. mParser.advanceToken(true);
  278. // Advance to ')'
  279. mParser.advanceToken(true);
  280. }
  281. if (!mParser.tokenICmp(")"))
  282. {
  283. Con::errorf("PersistenceManager::parseObject() - badly formed object \
  284. declaration on line %d - was expecting a ')' character", mParser.getCurrentLine()+1);
  285. // Remove this object without saving it
  286. killObject();
  287. return;
  288. }
  289. }
  290. // The next token should either be a ';' or a '{'
  291. mParser.advanceToken(true);
  292. if (mParser.tokenICmp(";"))
  293. {
  294. // Save the end line number
  295. mCurrentObject->endLine = mParser.getCurrentLine();
  296. // Save the end position
  297. mCurrentObject->endPosition = mParser.getTokenLineOffset();
  298. // Flag this object as not having braces
  299. mCurrentObject->hasBraces = false;
  300. saveObject(); // Object has no fields
  301. return;
  302. }
  303. else if (!mParser.tokenICmp("{"))
  304. {
  305. Con::errorf("PersistenceManager::parseObject() - badly formed object \
  306. declaration on line %d - was expecting a '{' character", mParser.getCurrentLine()+1);
  307. // Remove this object without saving it
  308. killObject();
  309. return;
  310. }
  311. while (mParser.advanceToken(true))
  312. {
  313. // Check for a subobject
  314. if (mParser.tokenICmp("new") ||
  315. mParser.tokenICmp("singleton") ||
  316. mParser.tokenICmp("datablock"))
  317. {
  318. parseObject();
  319. }
  320. // Check to see if we have a property
  321. if (mParser.tokenICmp("="))
  322. {
  323. // Ok, we are at an '='...back up to find out
  324. // what variable is getting assigned
  325. mParser.regressToken(true);
  326. const char* variable = mParser.getToken();
  327. if (variable && dStrlen(variable) > 0)
  328. {
  329. // See if it is a global or a local variable
  330. if (variable[0] == '%' || variable[0] == '$')
  331. {
  332. // We ignore this variable and go
  333. // back to our previous place
  334. mParser.advanceToken(true);
  335. }
  336. // Could also potentially be a <object>.<variable>
  337. // assignment which we don't care about either
  338. else if (dStrchr(variable, '.'))
  339. {
  340. // We ignore this variable and go
  341. // back to our previous place
  342. mParser.advanceToken(true);
  343. }
  344. // If we made it to here assume it is a variable
  345. // for the current object
  346. else
  347. {
  348. // Create our new property
  349. mCurrentObject->properties.increment();
  350. ParsedProperty& prop = mCurrentObject->properties.last();
  351. // Check to see if this is an array variable
  352. if (dStrlen(variable) > 3 && variable[dStrlen(variable) - 1] == ']')
  353. {
  354. // The last character is a ']' which *should* mean
  355. // there is also a corresponding '['
  356. const char* arrayPosStart = dStrrchr(variable, '[');
  357. if (!arrayPosStart)
  358. {
  359. Con::errorf("PersistenceManager::parseObject() - error parsing array position - \
  360. was expecting a '[' character");
  361. }
  362. else
  363. {
  364. // Parse the array position for the variable name
  365. S32 arrayPos = -1;
  366. dSscanf(arrayPosStart, "[%d]", &arrayPos);
  367. // If we got a valid array position then set it
  368. if (arrayPos > -1)
  369. prop.arrayPos = arrayPos;
  370. // Trim off the [<pos>] from the variable name
  371. char* variableName = dStrdup(variable);
  372. variableName[arrayPosStart - variable] = 0;
  373. // Set the variable name to our new shortened name
  374. variable = StringTable->insert(variableName, true);
  375. // Cleanup our variableName buffer
  376. dFree( variableName );
  377. }
  378. }
  379. // Set back the variable name
  380. prop.name = StringTable->insert(variable, true);
  381. // Store the start position for this variable
  382. prop.startLine = mParser.getCurrentLine();
  383. prop.startPosition = mParser.getTokenLineOffset();
  384. // Advance back to the '='
  385. mParser.advanceToken(true);
  386. // Sanity check
  387. if (!mParser.tokenICmp("="))
  388. Con::errorf("PersistenceManager::parseObject() - somehow we aren't \
  389. pointing at the expected '=' character");
  390. else
  391. {
  392. // The next token should be the value
  393. // being assigned to the variable
  394. mParser.advanceToken(true);
  395. // Store the line number for this value
  396. prop.valueLine = mParser.getCurrentLine();
  397. // Store the values beginning position
  398. prop.valuePosition = mParser.getTokenLineOffset();
  399. // Read tokens up to the semicolon.
  400. // Quoted tokens skip the leading and trailing quote characters. eg.
  401. // "this" becomes: this
  402. // "this" TAB "that" becomes: this" TAB "that
  403. // "this" TAB "that" TAB "other" becomes: this" TAB "that" TAB "other
  404. String value;
  405. bool wasQuoted = false;
  406. while (!mParser.endOfFile() && !mParser.tokenICmp(";"))
  407. {
  408. // Join tokens together (skipped first time through when string is empty)
  409. if (value.length() > 0)
  410. {
  411. if (wasQuoted)
  412. value += "\" "; // quoted followed by non-quoted
  413. else if (mParser.tokenIsQuoted())
  414. value += " \""; // non-quoted followed by quoted
  415. else
  416. value += " "; // non-quoted followed by non-quoted
  417. }
  418. value += mParser.getToken();
  419. wasQuoted = mParser.tokenIsQuoted();
  420. mParser.advanceToken(true);
  421. }
  422. // TODO: make sure this doesn't leak
  423. prop.value = dStrdup(value.c_str());
  424. if (!mParser.tokenICmp(";"))
  425. Con::errorf("PersistenceManager::parseObject() - badly formed variable "
  426. "assignment on line %d - was expecting a ';' character", mParser.getCurrentLine()+1);
  427. // Store the end position for this variable
  428. prop.endLine = mParser.getCurrentLine();
  429. prop.endPosition = mParser.getTokenLineOffset();
  430. if (wasQuoted)
  431. prop.endPosition -= 1;
  432. }
  433. }
  434. }
  435. }
  436. // Check for the end of the object declaration
  437. if (mParser.tokenICmp("}"))
  438. {
  439. // See if the next token is a ';'
  440. mParser.advanceToken(true);
  441. if (mParser.tokenICmp(";"))
  442. {
  443. // Save the end line number
  444. mCurrentObject->endLine = mParser.getCurrentLine();
  445. // Save the end position
  446. mCurrentObject->endPosition = mParser.getTokenLineOffset();
  447. saveObject();
  448. break;
  449. }
  450. }
  451. }
  452. }
  453. bool PersistenceManager::parseFile(const char* fileName)
  454. {
  455. // Read the file into the line buffer
  456. if (!readFile(fileName))
  457. return false;
  458. // Load it into our Tokenizer parser
  459. if (!mParser.openFile(fileName))
  460. {
  461. // Handle an object writing out to a new file
  462. if ( !Torque::FS::IsFile( fileName ) )
  463. return true;
  464. return false;
  465. }
  466. // Set our reserved "single" tokens
  467. mParser.setSingleTokens("(){};=:");
  468. // Search object declarations
  469. while (mParser.advanceToken(true))
  470. {
  471. if (mParser.tokenICmp("new") ||
  472. mParser.tokenICmp("singleton") ||
  473. mParser.tokenICmp("datablock"))
  474. {
  475. parseObject();
  476. }
  477. }
  478. // If we had an object that didn't end properly
  479. // then we could have objects on the stack
  480. while (mCurrentObject)
  481. saveObject();
  482. //Con::errorf("Parsed Results:");
  483. //for (U32 i = 0; i < mObjectBuffer.size(); i++)
  484. //{
  485. // ParsedObject* parsedObject = mObjectBuffer[i];
  486. // Con::warnf(" mObjectBuffer[%d]:", i);
  487. // Con::warnf(" name = %s", parsedObject->name);
  488. // Con::warnf(" className = %s", parsedObject->className);
  489. // Con::warnf(" startLine = %d", parsedObject->startLine + 1);
  490. // Con::warnf(" endLine = %d", parsedObject->endLine + 1);
  491. // //if (mObjectBuffer[i]->properties.size() > 0)
  492. // //{
  493. // // Con::warnf(" properties:");
  494. // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++)
  495. // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name,
  496. // // mObjectBuffer[i]->properties[j].value);
  497. // //}
  498. // if (!parsedObject->simObject.isNull())
  499. // {
  500. // SimObject* simObject = parsedObject->simObject;
  501. // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId());
  502. // Con::warnf(" declaration line = %d", simObject->getDeclarationLine());
  503. // }
  504. //}
  505. return true;
  506. }
  507. S32 PersistenceManager::getPropertyIndex(ParsedObject* parsedObject, const char* fieldName, U32 arrayPos)
  508. {
  509. S32 propertyIndex = -1;
  510. if (!parsedObject)
  511. return propertyIndex;
  512. for (U32 i = 0; i < parsedObject->properties.size(); i++)
  513. {
  514. if (dStricmp(fieldName, parsedObject->properties[i].name) == 0 &&
  515. parsedObject->properties[i].arrayPos == arrayPos)
  516. {
  517. propertyIndex = i;
  518. break;
  519. }
  520. }
  521. return propertyIndex;
  522. }
  523. S32 PersistenceManager::getSpecialPropertyAtOffset(ParsedObject* parsedObject, const char* fieldName, U32 offsetPos)
  524. {
  525. S32 propertyIndex = -1;
  526. if (!parsedObject)
  527. return propertyIndex;
  528. U32 hitCount = -1;
  529. for (U32 i = 0; i < parsedObject->properties.size(); i++)
  530. {
  531. if (dStricmp(fieldName, parsedObject->properties[i].name) == 0)
  532. {
  533. hitCount++;
  534. if (hitCount == offsetPos)
  535. {
  536. propertyIndex = i;
  537. break;
  538. }
  539. }
  540. }
  541. return propertyIndex;
  542. }
  543. char* PersistenceManager::getObjectIndent(ParsedObject* object)
  544. {
  545. char* indent = Con::getReturnBuffer(2048);
  546. indent[0] = 0;
  547. if (!object)
  548. return indent;
  549. if (object->startLine < 0 || object->startLine >= mLineBuffer.size())
  550. return indent;
  551. const char* line = mLineBuffer[object->startLine];
  552. if (line)
  553. {
  554. const char* nonSpace = line;
  555. U32 strLen = dStrlen(line);
  556. for (U32 i = 0; i < strLen; i++)
  557. {
  558. if (*nonSpace != ' ')
  559. break;
  560. nonSpace++;
  561. }
  562. dStrncpy(indent, line, nonSpace - line);
  563. indent[nonSpace - line] = 0;
  564. }
  565. return indent;
  566. }
  567. void PersistenceManager::updatePositions(U32 lineNumber, U32 startPos, S32 diff)
  568. {
  569. if (diff == 0)
  570. return;
  571. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  572. {
  573. ParsedObject* object = mObjectBuffer[i];
  574. if (object->nameLine == lineNumber && object->namePosition > startPos)
  575. object->namePosition += diff;
  576. if (object->endLine == lineNumber && object->endPosition > startPos)
  577. object->endPosition += diff;
  578. if (lineNumber >= object->startLine && lineNumber <= object->endLine)
  579. {
  580. for (U32 j = 0; j < object->properties.size(); j++)
  581. {
  582. ParsedProperty& prop = object->properties[j];
  583. S32 propStartPos = prop.startPosition;
  584. S32 endPos = prop.endPosition;
  585. S32 valuePos = prop.valuePosition;
  586. if (lineNumber == prop.startLine && propStartPos > startPos)
  587. {
  588. propStartPos += diff;
  589. if (propStartPos < 0)
  590. propStartPos = 0;
  591. prop.startPosition = valuePos;
  592. }
  593. if (lineNumber == prop.endLine && endPos > startPos)
  594. {
  595. endPos += diff;
  596. if (endPos < 0)
  597. endPos = 0;
  598. prop.endPosition = endPos;
  599. }
  600. if (lineNumber == prop.valueLine && valuePos > startPos)
  601. {
  602. valuePos += diff;
  603. if (valuePos < 0)
  604. valuePos = 0;
  605. prop.valuePosition = valuePos;
  606. }
  607. }
  608. }
  609. }
  610. }
  611. void PersistenceManager::updateLineOffsets(U32 startLine, S32 diff, ParsedObject* skipObject)
  612. {
  613. if (diff == 0)
  614. return;
  615. if (startLine >= mLineBuffer.size())
  616. return;
  617. if (startLine + diff >= mLineBuffer.size())
  618. return;
  619. // Make sure we don't double offset a SimObject's
  620. // declaration line
  621. SimObjectList updated;
  622. if (skipObject && !skipObject->simObject.isNull())
  623. updated.push_back_unique(skipObject->simObject);
  624. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  625. {
  626. ParsedObject* object = mObjectBuffer[i];
  627. // See if this is the skipObject
  628. if (skipObject && skipObject == object)
  629. continue;
  630. // We can safely ignore objects that
  631. // came earlier in the file
  632. if (object->endLine < startLine)
  633. continue;
  634. if (object->startLine >= startLine)
  635. object->startLine += diff;
  636. if (object->nameLine >= startLine)
  637. object->nameLine += diff;
  638. for (U32 j = 0; j < object->properties.size(); j++)
  639. {
  640. if (object->properties[j].startLine >= startLine)
  641. object->properties[j].startLine += diff;
  642. if (object->properties[j].endLine >= startLine)
  643. object->properties[j].endLine += diff;
  644. if (object->properties[j].valueLine >= startLine)
  645. object->properties[j].valueLine += diff;
  646. }
  647. if (object->endLine >= startLine)
  648. object->endLine += diff;
  649. if (!object->simObject.isNull() &&
  650. object->simObject->getDeclarationLine() > startLine)
  651. {
  652. // Check for already updated SimObject's
  653. U32 currSize = updated.size();
  654. updated.push_back_unique(object->simObject);
  655. if (updated.size() == currSize)
  656. continue;
  657. S32 newDeclLine = object->simObject->getDeclarationLine() + diff;
  658. if (newDeclLine < 0)
  659. newDeclLine = 0;
  660. object->simObject->setDeclarationLine(newDeclLine);
  661. }
  662. }
  663. }
  664. PersistenceManager::ParsedObject* PersistenceManager::findParentObject(SimObject* object, ParsedObject* parentObject)
  665. {
  666. ParsedObject* ret = NULL;
  667. if (!object)
  668. return ret;
  669. // First test for the SimGroup it belongs to
  670. ret = findParsedObject(object->getGroup(), parentObject);
  671. if (ret)
  672. return ret;
  673. // TODO: Test all of the SimSet's that this object belongs to
  674. return ret;
  675. }
  676. PersistenceManager::ParsedObject* PersistenceManager::findParsedObject(SimObject* object, ParsedObject* parentObject)
  677. {
  678. if (!object)
  679. return NULL;
  680. // See if our object belongs to a parent
  681. if (!parentObject)
  682. parentObject = findParentObject(object, parentObject);
  683. // First let's compare the object to the SimObject's that
  684. // we matched to our ParsedObject's when we loaded them
  685. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  686. {
  687. ParsedObject* testObj = mObjectBuffer[i];
  688. if (testObj->simObject == object)
  689. {
  690. // Deal with children objects
  691. if (testObj->parentObject != parentObject)
  692. continue;
  693. return testObj;
  694. }
  695. }
  696. // Didn't find it in our ParsedObject's SimObject's
  697. // so see if we can find one that corresponds to the
  698. // same name and className
  699. const char *originalName = object->getOriginalName();
  700. // Find the corresponding ParsedObject
  701. if (originalName && originalName[0])
  702. {
  703. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  704. {
  705. ParsedObject* testObj = mObjectBuffer[i];
  706. if (testObj->name == originalName)
  707. {
  708. // Deal with children objects
  709. if (testObj->parentObject != parentObject)
  710. continue;
  711. return testObj;
  712. }
  713. }
  714. }
  715. //Check internal names
  716. if (object->getInternalName())
  717. {
  718. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  719. {
  720. ParsedObject* testObj = mObjectBuffer[i];
  721. for (U32 j = 0; j < testObj->properties.size(); j++)
  722. {
  723. const ParsedProperty &prop = testObj->properties[j];
  724. if ( String::compare( prop.name, "internalName" ) == 0 &&
  725. String::compare( prop.value, object->getInternalName() ) == 0 )
  726. return testObj;
  727. else if ( String::compare(prop.name, "internalName") == 0)
  728. break;
  729. }
  730. }
  731. }
  732. return NULL;
  733. }
  734. void PersistenceManager::updateToken( const U32 lineNumber, const U32 linePosition, const U32 oldValueLen, const char* newValue, bool addQuotes )
  735. {
  736. // Make sure we have a valid lineNumber
  737. if (lineNumber < 0 || linePosition < 0 ||
  738. lineNumber >= mLineBuffer.size())
  739. return;
  740. // Grab the line that the value is on
  741. const char* line = mLineBuffer[lineNumber];
  742. U32 newValueLen = ( newValue ) ? dStrlen(newValue) : 0;
  743. // Make sure we have a valid linePosition
  744. if (linePosition >= dStrlen(line) ||
  745. linePosition + oldValueLen > dStrlen(line))
  746. return;
  747. // Get all of the characters up to the value position
  748. U32 preStringLen = linePosition;
  749. bool needQuotes = false;
  750. if( addQuotes && line[ linePosition - 1 ] != '"' )
  751. {
  752. preStringLen ++;
  753. needQuotes = true;
  754. }
  755. char* preString = ( char* ) dMalloc( preStringLen + 1 );
  756. dMemcpy( preString, line, linePosition );
  757. if( needQuotes )
  758. {
  759. preString[ linePosition ] = '"';
  760. preString[ linePosition + 1 ] = 0;
  761. }
  762. else
  763. preString[ linePosition ] = 0;
  764. // Get all of the characters that occur after the value
  765. const char* postStringSrc = line + linePosition + oldValueLen;
  766. U32 postStringLen = dStrlen( postStringSrc );
  767. if( needQuotes )
  768. postStringLen ++;
  769. char* postString = ( char* ) dMalloc( postStringLen + 1 );
  770. if( needQuotes )
  771. postString[ 0 ] = '"';
  772. dStrcpy( &postString[ needQuotes ? 1 : 0 ], postStringSrc, postStringLen + (needQuotes ? 0 : 1) );
  773. postString[ postStringLen ] = 0;
  774. // Calculate the length of our new line
  775. U32 newLineLen = 0;
  776. newLineLen += preStringLen;
  777. newLineLen += newValueLen;
  778. newLineLen += postStringLen;
  779. // Create a buffer for our new line and
  780. // null terminate it
  781. char* newLine = ( char* ) dMalloc( newLineLen + 1 );
  782. newLine[0] = 0;
  783. // Build the new line with the
  784. // preString + newValue + postString
  785. dStrcat(newLine, preString, newLineLen + 1);
  786. if ( newValue )
  787. dStrcat(newLine, newValue, newLineLen + 1);
  788. dStrcat(newLine, postString, newLineLen + 1);
  789. // Clear our existing line
  790. if (mLineBuffer[lineNumber])
  791. {
  792. dFree( mLineBuffer[ lineNumber ] );
  793. mLineBuffer[ lineNumber ] = NULL;
  794. }
  795. // Set the new line
  796. mLineBuffer[lineNumber] = newLine;
  797. // Figure out the size difference of the old value
  798. // and new value in case we need to update any else
  799. // on the line after it
  800. S32 diff = newValueLen - oldValueLen;
  801. // Update anything that is on the line after this that needs
  802. // to change its offsets to reflect the new line
  803. updatePositions(lineNumber, linePosition, diff);
  804. // Clean up our buffers
  805. dFree( preString );
  806. dFree( postString );
  807. }
  808. const char* PersistenceManager::getFieldValue(SimObject* object, const char* fieldName, U32 arrayPos)
  809. {
  810. // Our return string
  811. char* ret = NULL;
  812. // Buffer to hold the string equivalent of the arrayPos
  813. char arrayPosStr[8];
  814. dSprintf(arrayPosStr, 8, "%d", arrayPos);
  815. // Get the object's value
  816. const char *value = object->getDataField(fieldName, arrayPosStr );
  817. if (value)
  818. ret = dStrdup(value);
  819. return ret;
  820. }
  821. const char* PersistenceManager::createNewProperty(const char* name, const char* value, bool isArray, U32 arrayPos)
  822. {
  823. if (!name || !value)
  824. return NULL;
  825. AssertFatal( value[0] != StringTagPrefixByte, "Got tagged string!" );
  826. char* newProp = ( char* ) dMalloc( 2048 );
  827. dMemset(newProp, 0, 2048);
  828. if (value)
  829. {
  830. if (isArray)
  831. dSprintf(newProp, 2048, "%s[%d] = \"%s\";", name, arrayPos, value);
  832. else
  833. dSprintf(newProp, 2048, "%s = \"%s\";", name, value);
  834. }
  835. else
  836. {
  837. if (isArray)
  838. dSprintf(newProp, 2048, "%s[%d] = \"\";", name, arrayPos);
  839. else
  840. dSprintf(newProp, 2048, "%s = \"\";", name);
  841. }
  842. return newProp;
  843. }
  844. bool PersistenceManager::isEmptyLine(const char* line)
  845. {
  846. // Simple test first
  847. if (!line || dStrlen(line) == 0)
  848. return true;
  849. U32 len = dStrlen(line);
  850. for (U32 i = 0; i < len; i++)
  851. {
  852. const char& c = line[i];
  853. // Skip "empty" characters
  854. if (c == ' ' ||
  855. c == '\t' ||
  856. c == '\r' ||
  857. c == '\n')
  858. {
  859. continue;
  860. }
  861. // If we have made it to the an end of the line
  862. // comment then consider this an empty line
  863. if (c == '/')
  864. {
  865. if (i < len - 1)
  866. {
  867. if (line[i + 1] == '/')
  868. return true;
  869. }
  870. }
  871. // If it isn't an "empty" character or a comment then
  872. // we have a valid character on the line and it isn't empty
  873. return false;
  874. }
  875. return true;
  876. }
  877. void PersistenceManager::removeLine(U32 lineNumber)
  878. {
  879. if (lineNumber >= mLineBuffer.size())
  880. return;
  881. if (mLineBuffer[lineNumber])
  882. {
  883. dFree( mLineBuffer[ lineNumber ] );
  884. mLineBuffer[ lineNumber ] = NULL;
  885. }
  886. mLineBuffer.erase(lineNumber);
  887. updateLineOffsets(lineNumber, -1);
  888. }
  889. void PersistenceManager::removeTextBlock(U32 startLine, U32 endLine, U32 startPos, U32 endPos, bool removeEmptyLines)
  890. {
  891. // Make sure we have valid lines
  892. if (startLine >= mLineBuffer.size() || endLine >= mLineBuffer.size())
  893. return;
  894. // We assume that the startLine is before the endLine
  895. if (startLine > endLine)
  896. return;
  897. // Grab the lines (they may be the same)
  898. const char* startLineText = mLineBuffer[startLine];
  899. const char* endLineText = mLineBuffer[endLine];
  900. // Make sure we have a valid startPos
  901. if (startPos >= dStrlen(startLineText))
  902. return;
  903. // Make sure we have a valid endPos
  904. if (endPos > dStrlen(endLineText))
  905. return;
  906. if (startLine == endLine)
  907. {
  908. // Now let updateToken do the heavy lifting on removing it
  909. updateToken(startLine, startPos, endPos - startPos, "");
  910. // Handle removing an empty lines if desired
  911. if (removeEmptyLines)
  912. {
  913. const char* line = mLineBuffer[startLine];
  914. if (isEmptyLine(line))
  915. removeLine(startLine);
  916. }
  917. }
  918. else
  919. {
  920. // Start with clearing the startLine from startPos to the end
  921. updateToken(startLine, startPos, dStrlen(startLineText + startPos), "");
  922. // Then clear the endLine from beginning to endPos
  923. updateToken(endLine, 0, endPos, "");
  924. // Handle removing an empty endLine if desired
  925. if (removeEmptyLines)
  926. {
  927. const char* line = mLineBuffer[endLine];
  928. if (isEmptyLine(line))
  929. removeLine(endLine);
  930. }
  931. // Handle removing any lines between the startLine and endLine
  932. for (U32 i = startLine + 1; i < endLine; i++)
  933. removeLine(startLine + 1);
  934. // Handle removing an empty startLine if desired
  935. if (removeEmptyLines)
  936. {
  937. const char* line = mLineBuffer[startLine];
  938. if (isEmptyLine(line))
  939. removeLine(startLine);
  940. }
  941. }
  942. }
  943. void PersistenceManager::removeParsedObject(ParsedObject* parsedObject)
  944. {
  945. if (!parsedObject)
  946. return;
  947. if (parsedObject->startLine < 0 || parsedObject->startLine >= mLineBuffer.size())
  948. return;
  949. if (parsedObject->endLine < 0 || parsedObject->startLine >= mLineBuffer.size())
  950. return;
  951. removeTextBlock(parsedObject->startLine, parsedObject->endLine,
  952. parsedObject->startPosition, parsedObject->endPosition+1, true); // +1 to remove trailing semicolon as well
  953. parsedObject->parentObject = NULL;
  954. parsedObject->simObject = NULL;
  955. }
  956. void PersistenceManager::removeField(const ParsedProperty& prop)
  957. {
  958. if (prop.startLine < 0 || prop.startLine >= mLineBuffer.size())
  959. return;
  960. if (prop.endLine < 0 || prop.endLine >= mLineBuffer.size())
  961. return;
  962. S32 endPosition = prop.endPosition+1; // +1 to remove trailing semicolon as well
  963. if ((endPosition < dStrlen(mLineBuffer[prop.endLine])) &&
  964. (mLineBuffer[prop.endLine][endPosition] == ';')) // adjust end position for quoted values (otherwise a trailing semicolon will remain)
  965. endPosition++;
  966. removeTextBlock(prop.startLine, prop.endLine, prop.startPosition, endPosition, true);
  967. }
  968. U32 PersistenceManager::writeProperties(const Vector<const char*>& properties, const U32 insertLine, const char* objectIndent)
  969. {
  970. U32 currInsertLine = insertLine;
  971. for (U32 i = 0; i < properties.size(); i++)
  972. {
  973. const char* prop = properties[i];
  974. if (!prop || dStrlen(prop) == 0)
  975. continue;
  976. U32 len = dStrlen(objectIndent) + dStrlen(prop) + 4;
  977. char* newLine = ( char* ) dMalloc( len );
  978. dSprintf(newLine, len, "%s %s", objectIndent, prop);
  979. mLineBuffer.insert(currInsertLine, newLine);
  980. currInsertLine++;
  981. }
  982. return currInsertLine - insertLine;
  983. }
  984. PersistenceManager::ParsedObject* PersistenceManager::writeNewObject(SimObject* object, const Vector<const char*>& properties, const U32 insertLine, ParsedObject* parentObject)
  985. {
  986. ParsedObject* parsedObject = new ParsedObject;
  987. parsedObject->name = object->getName();
  988. parsedObject->className = object->getClassName();
  989. parsedObject->simObject = object;
  990. U32 currInsertLine = insertLine;
  991. // If the parentObject isn't set see if
  992. // we can find it in the file
  993. if (!parentObject)
  994. parentObject = findParentObject(object);
  995. parsedObject->parentObject = parentObject;
  996. char* indent = getObjectIndent(parentObject);
  997. if (parentObject)
  998. dStrcat(indent, " \0", 2048);
  999. // Write out the beginning of the object declaration
  1000. const char* dclToken = "new";
  1001. if (dynamic_cast<Material*>(object) ||
  1002. dynamic_cast<CustomMaterial*>(object) ||
  1003. dynamic_cast<GuiControlProfile*>(object) ||
  1004. dynamic_cast<TSShapeConstructor*>(object))
  1005. dclToken = "singleton";
  1006. else if( dynamic_cast< SimDataBlock* >( object ) )
  1007. dclToken = "datablock";
  1008. char newLine[ 4096 ];
  1009. dMemset(newLine, 0, sizeof( newLine));
  1010. // New line before an object declaration
  1011. dSprintf(newLine, sizeof( newLine ), "");
  1012. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1013. currInsertLine++;
  1014. dMemset(newLine, 0, sizeof( newLine ));
  1015. parsedObject->startLine = currInsertLine;
  1016. parsedObject->nameLine = currInsertLine;
  1017. parsedObject->namePosition = dStrlen(indent) + dStrlen(dclToken) + dStrlen(object->getClassName()) + 2;
  1018. // Objects that had no name were getting saved out as: Object((null))
  1019. if ( object->getName() != NULL )
  1020. {
  1021. if( object->getCopySource() )
  1022. dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s : %s)", indent, dclToken, object->getClassName(), object->getName(),
  1023. object->getCopySource() ? object->getCopySource()->getName() : "" );
  1024. else
  1025. dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s)", indent, dclToken, object->getClassName(), object->getName());
  1026. }
  1027. else
  1028. dSprintf(newLine, sizeof( newLine ), "%s%s %s()", indent, dclToken, object->getClassName() );
  1029. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1030. currInsertLine++;
  1031. dMemset(newLine, 0, sizeof( newLine ));
  1032. dSprintf(newLine, sizeof( newLine ), "%s{", indent);
  1033. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1034. currInsertLine++;
  1035. dMemset(newLine, 0, sizeof( newLine ));
  1036. currInsertLine += writeProperties(properties, currInsertLine, indent);
  1037. parsedObject->endLine = currInsertLine;
  1038. parsedObject->updated = true;
  1039. dSprintf(newLine, sizeof( newLine ), "%s};", indent);
  1040. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1041. currInsertLine++;
  1042. updateLineOffsets(insertLine, currInsertLine - insertLine, parsedObject);
  1043. mObjectBuffer.push_back(parsedObject);
  1044. // Update the SimObject to reflect its saved name and declaration line.
  1045. // These values should always reflect what is in the file, even if the object
  1046. // has not actually been re-created from an execution of that file yet.
  1047. object->setOriginalName( object->getName() );
  1048. object->setDeclarationLine( currInsertLine );
  1049. if (mCurrentFile)
  1050. object->setFilename(mCurrentFile);
  1051. return parsedObject;
  1052. }
  1053. void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObject)
  1054. {
  1055. // Create a default object of the same type
  1056. ConsoleObject *defaultConObject = ConsoleObject::create(object->getClassName());
  1057. SimObject* defaultObject = dynamic_cast<SimObject*>(defaultConObject);
  1058. // ***Really*** shouldn't happen
  1059. if (!defaultObject)
  1060. return;
  1061. Vector<const char*> newLines;
  1062. ParsedObject* parsedObject = findParsedObject(object, parentObject);
  1063. // If we don't already have an association between the ParsedObject
  1064. // and the SimObject then go ahead and create it
  1065. if (parsedObject && parsedObject->simObject.isNull())
  1066. parsedObject->simObject = object;
  1067. // Kill all fields on the remove list.
  1068. for( U32 i = 0; i < mRemoveFields.size(); ++ i )
  1069. {
  1070. RemoveField& field = mRemoveFields[ i ];
  1071. if( field.object != object )
  1072. continue;
  1073. S32 propertyIndex = getPropertyIndex( parsedObject, field.fieldName, field.arrayPos );
  1074. if( propertyIndex != -1 )
  1075. removeField( parsedObject->properties[ propertyIndex ] );
  1076. }
  1077. // Get our field list
  1078. const AbstractClassRep::FieldList &list = object->getFieldList();
  1079. for(U32 i = 0; i < list.size(); i++)
  1080. {
  1081. const AbstractClassRep::Field* f = &list[i];
  1082. // Skip the special field types.
  1083. if ( f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors))
  1084. continue;
  1085. if (f->flag.test(AbstractClassRep::FIELD_SpecialtyArrayField))
  1086. {
  1087. U32 fieldArraySize = object->getSpecialFieldSize(f->pFieldname);
  1088. for(U32 j = 0; j < fieldArraySize; j++)
  1089. {
  1090. const char* value = object->getSpecialFieldOut(f->pFieldname, j);
  1091. // Make sure we got a value
  1092. if (!value)
  1093. continue;
  1094. // Let's see if this field is already in the file
  1095. S32 propertyIndex = getSpecialPropertyAtOffset(parsedObject, f->pFieldname, j);
  1096. if (propertyIndex > -1)
  1097. {
  1098. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1099. // If this field is on the remove list then remove it and continue
  1100. if (findRemoveField(object, f->pFieldname, j))
  1101. {
  1102. removeField(parsedObject->properties[propertyIndex]);
  1103. dFree(value);
  1104. continue;
  1105. }
  1106. // Run the parsed value through the console system conditioners so
  1107. // that it will better match the data we got back from the object.
  1108. const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
  1109. // If our data doesn't match then we get to update it.
  1110. //
  1111. // As for copy-sources, we just assume here that if a property setting
  1112. // is there in the file, the user does not want it inherited from the copy-source
  1113. // even in the case the actual values are identical.
  1114. if (dStricmp(value, evalue) != 0)
  1115. {
  1116. if (value[0] == '\0' &&
  1117. dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value) == 0 &&
  1118. (!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value) == 0))
  1119. {
  1120. removeField(prop);
  1121. }
  1122. else
  1123. {
  1124. // TODO: This should be wrapped in a helper method... probably.
  1125. // Detect and collapse relative path information
  1126. if (f->type == TypeFilename ||
  1127. f->type == TypeStringFilename ||
  1128. f->type == TypeImageFilename ||
  1129. f->type == TypePrefabFilename ||
  1130. f->type == TypeShapeFilename ||
  1131. f->type == TypeSoundFilename)
  1132. {
  1133. char fnBuf[1024];
  1134. Con::collapseScriptFilename(fnBuf, 1024, value);
  1135. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
  1136. }
  1137. else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
  1138. {
  1139. char cmdBuf[1024];
  1140. expandEscape(cmdBuf, value);
  1141. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
  1142. }
  1143. else
  1144. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
  1145. }
  1146. }
  1147. }
  1148. else
  1149. {
  1150. // No need to process a removed field that doesn't exist in the file
  1151. if (findRemoveField(object, f->pFieldname, j))
  1152. {
  1153. dFree(value);
  1154. continue;
  1155. }
  1156. bool mustUpdate = false;
  1157. // If we didn't find the property in the ParsedObject
  1158. // then we need to compare against the default value
  1159. // for this property and save it out if it is different
  1160. const char* defaultValue = defaultObject->getSpecialFieldOut(f->pFieldname, j);
  1161. if (!defaultValue || dStricmp(value, defaultValue) != 0)
  1162. {
  1163. // Value differs. Check whether it also differs from the
  1164. // value in the copy source if there is one.
  1165. if (object->getCopySource())
  1166. {
  1167. const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1168. if (!copySourceValue || dStricmp(copySourceValue, value) != 0)
  1169. mustUpdate = true;
  1170. if (copySourceValue)
  1171. dFree(copySourceValue);
  1172. }
  1173. else
  1174. mustUpdate = true;
  1175. }
  1176. else
  1177. {
  1178. // Value does not differ. If it differs from the copy source's value,
  1179. // though, we still want to write it out as otherwise we'll see the
  1180. // copy source's value override us.
  1181. if (object->getCopySource())
  1182. {
  1183. const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1184. if (copySourceValue && dStricmp(copySourceValue, value) != 0)
  1185. mustUpdate = true;
  1186. if (copySourceValue)
  1187. dFree(copySourceValue);
  1188. }
  1189. }
  1190. // The default value for most string type fields is
  1191. // NULL so we can't just continue here or we'd never ever
  1192. // write them out...
  1193. //
  1194. //if (!defaultValue)
  1195. // continue;
  1196. // If the object's value is different from the default
  1197. // value then add it to the ParsedObject's newLines
  1198. if (mustUpdate)
  1199. {
  1200. // TODO: This should be wrapped in a helper method... probably.
  1201. // Detect and collapse relative path information
  1202. if (f->type == TypeFilename ||
  1203. f->type == TypeStringFilename ||
  1204. f->type == TypeImageFilename ||
  1205. f->type == TypePrefabFilename ||
  1206. f->type == TypeShapeFilename ||
  1207. f->type == TypeSoundFilename)
  1208. {
  1209. char fnBuf[1024];
  1210. Con::collapseScriptFilename(fnBuf, 1024, value);
  1211. newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
  1212. }
  1213. else if (f->type == TypeCommand)
  1214. {
  1215. char cmdBuf[1024];
  1216. expandEscape(cmdBuf, value);
  1217. newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
  1218. }
  1219. else
  1220. newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
  1221. }
  1222. if (defaultValue)
  1223. dFree(defaultValue);
  1224. }
  1225. //dFree(value);
  1226. }
  1227. }
  1228. else
  1229. {
  1230. for (U32 j = 0; S32(j) < f->elementCount; j++)
  1231. {
  1232. const char* value = getFieldValue(object, f->pFieldname, j);
  1233. // Make sure we got a value
  1234. if (!value)
  1235. continue;
  1236. // Let's see if this field is already in the file
  1237. S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j);
  1238. if (propertyIndex > -1)
  1239. {
  1240. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1241. // If this field is on the remove list then remove it and continue
  1242. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
  1243. {
  1244. removeField(parsedObject->properties[propertyIndex]);
  1245. dFree(value);
  1246. continue;
  1247. }
  1248. // Run the parsed value through the console system conditioners so
  1249. // that it will better match the data we got back from the object.
  1250. const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
  1251. // If our data doesn't match then we get to update it.
  1252. //
  1253. // As for copy-sources, we just assume here that if a property setting
  1254. // is there in the file, the user does not want it inherited from the copy-source
  1255. // even in the case the actual values are identical.
  1256. if (dStricmp(value, evalue) != 0)
  1257. {
  1258. if (value[0] == '\0' &&
  1259. dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value) == 0 &&
  1260. (!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value) == 0))
  1261. {
  1262. removeField(prop);
  1263. }
  1264. else
  1265. {
  1266. // TODO: This should be wrapped in a helper method... probably.
  1267. // Detect and collapse relative path information
  1268. if (f->type == TypeFilename ||
  1269. f->type == TypeStringFilename ||
  1270. f->type == TypeImageFilename ||
  1271. f->type == TypePrefabFilename ||
  1272. f->type == TypeShapeFilename ||
  1273. f->type == TypeSoundFilename)
  1274. {
  1275. char fnBuf[1024];
  1276. Con::collapseScriptFilename(fnBuf, 1024, value);
  1277. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
  1278. }
  1279. else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
  1280. {
  1281. char cmdBuf[1024];
  1282. expandEscape(cmdBuf, value);
  1283. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
  1284. }
  1285. else
  1286. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
  1287. }
  1288. }
  1289. }
  1290. else
  1291. {
  1292. // No need to process a removed field that doesn't exist in the file
  1293. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
  1294. {
  1295. dFree(value);
  1296. continue;
  1297. }
  1298. bool mustUpdate = false;
  1299. // If we didn't find the property in the ParsedObject
  1300. // then we need to compare against the default value
  1301. // for this property and save it out if it is different
  1302. const char* defaultValue = getFieldValue(defaultObject, f->pFieldname, j);
  1303. if (!defaultValue || dStricmp(value, defaultValue) != 0)
  1304. {
  1305. // Value differs. Check whether it also differs from the
  1306. // value in the copy source if there is one.
  1307. if (object->getCopySource())
  1308. {
  1309. const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1310. if (!copySourceValue || dStricmp(copySourceValue, value) != 0)
  1311. mustUpdate = true;
  1312. if (copySourceValue)
  1313. dFree(copySourceValue);
  1314. }
  1315. else
  1316. mustUpdate = true;
  1317. }
  1318. else
  1319. {
  1320. // Value does not differ. If it differs from the copy source's value,
  1321. // though, we still want to write it out as otherwise we'll see the
  1322. // copy source's value override us.
  1323. if (object->getCopySource())
  1324. {
  1325. const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1326. if (copySourceValue && dStricmp(copySourceValue, value) != 0)
  1327. mustUpdate = true;
  1328. if (copySourceValue)
  1329. dFree(copySourceValue);
  1330. }
  1331. }
  1332. // The default value for most string type fields is
  1333. // NULL so we can't just continue here or we'd never ever
  1334. // write them out...
  1335. //
  1336. //if (!defaultValue)
  1337. // continue;
  1338. // If the object's value is different from the default
  1339. // value then add it to the ParsedObject's newLines
  1340. if (mustUpdate)
  1341. {
  1342. // TODO: This should be wrapped in a helper method... probably.
  1343. // Detect and collapse relative path information
  1344. if (f->type == TypeFilename ||
  1345. f->type == TypeStringFilename ||
  1346. f->type == TypeImageFilename ||
  1347. f->type == TypePrefabFilename ||
  1348. f->type == TypeShapeFilename ||
  1349. f->type == TypeSoundFilename)
  1350. {
  1351. char fnBuf[1024];
  1352. Con::collapseScriptFilename(fnBuf, 1024, value);
  1353. newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
  1354. }
  1355. else if (f->type == TypeCommand)
  1356. {
  1357. char cmdBuf[1024];
  1358. expandEscape(cmdBuf, value);
  1359. newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
  1360. }
  1361. else
  1362. newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
  1363. }
  1364. if (defaultValue)
  1365. dFree(defaultValue);
  1366. }
  1367. dFree(value);
  1368. }
  1369. }
  1370. }
  1371. // Handle dynamic fields
  1372. SimFieldDictionary* fieldDict = object->getFieldDictionary();
  1373. for(SimFieldDictionaryIterator itr(fieldDict); *itr; ++itr)
  1374. {
  1375. SimFieldDictionary::Entry * entry = (*itr);
  1376. if( !entry->value )
  1377. continue;
  1378. // Let's see if this field is already in the file
  1379. S32 propertyIndex = getPropertyIndex(parsedObject, entry->slotName);
  1380. if (propertyIndex > -1)
  1381. {
  1382. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1383. // If this field is on the remove list then remove it and continue
  1384. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1385. {
  1386. removeField( parsedObject->properties[ propertyIndex ] );
  1387. continue;
  1388. }
  1389. if( object->getCopySource() )
  1390. {
  1391. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1392. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1393. {
  1394. removeField( prop );
  1395. continue;
  1396. }
  1397. }
  1398. const char* evalue = prop.value;
  1399. const char *entryVal = entry->value;
  1400. if ( entryVal[0] == StringTagPrefixByte )
  1401. entryVal = gNetStringTable->lookupString( dAtoi( entryVal+1 ) );
  1402. else
  1403. {
  1404. // Run the parsed value through the console system conditioners so
  1405. // that it will better match the data we got back from the object.
  1406. evalue = Con::getFormattedData(TypeString, evalue);
  1407. }
  1408. // If our data doesn't match then we get to update it
  1409. if (dStricmp(entryVal, evalue) != 0)
  1410. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, entryVal);
  1411. }
  1412. else
  1413. {
  1414. // No need to process a removed field that doesn't exist in the file
  1415. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1416. continue;
  1417. if( object->getCopySource() )
  1418. {
  1419. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1420. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1421. continue;
  1422. }
  1423. newLines.push_back(createNewProperty(entry->slotName, entry->value));
  1424. }
  1425. }
  1426. // If we have a parsedObject and the name changed
  1427. // then update the parsedObject to the new name.
  1428. // NOTE: an object 'can' have a NULL name which crashes in dStricmp.
  1429. if (parsedObject && parsedObject->name != StringTable->insert(object->getName(), true) )
  1430. {
  1431. StringTableEntry objectName = StringTable->insert(object->getName(), true);
  1432. if (parsedObject->name != objectName)
  1433. {
  1434. // Update the name in the file
  1435. updateToken(parsedObject->nameLine, parsedObject->namePosition, dStrlen(parsedObject->name), object->getName());
  1436. // Updated the parsedObject's name
  1437. parsedObject->name = objectName;
  1438. // Updated the object's "original" name to the one that is now in the file
  1439. object->setOriginalName(objectName);
  1440. }
  1441. }
  1442. if (parsedObject && newLines.size() > 0)
  1443. {
  1444. U32 lastPropLine = parsedObject->endLine;
  1445. if (parsedObject->properties.size() > 0)
  1446. lastPropLine = parsedObject->properties.last().valueLine + 1;
  1447. U32 currInsertLine = lastPropLine;
  1448. const char* indent = getObjectIndent(parsedObject);
  1449. // This should handle adding the opening { to an object
  1450. // that formerly did not have {};
  1451. if (!parsedObject->hasBraces)
  1452. {
  1453. updateToken(parsedObject->endLine, parsedObject->endPosition, 1, "\r\n{");
  1454. currInsertLine++;
  1455. }
  1456. currInsertLine += writeProperties(newLines, currInsertLine, indent);
  1457. // This should handle adding the opening } to an object
  1458. // that formerly did not have {};
  1459. if (!parsedObject->hasBraces)
  1460. {
  1461. U32 len = dStrlen(indent) + 3;
  1462. char* newLine = ( char* ) dMalloc( len );
  1463. dSprintf(newLine, len, "%s};", indent);
  1464. mLineBuffer.insert(currInsertLine, newLine);
  1465. currInsertLine++;
  1466. }
  1467. // Update the line offsets to account for the new lines
  1468. updateLineOffsets(lastPropLine, currInsertLine - lastPropLine);
  1469. }
  1470. else if (!parsedObject)
  1471. {
  1472. U32 insertLine = mLineBuffer.size();
  1473. if (!parentObject)
  1474. parentObject = findParentObject(object, parentObject);
  1475. if (parentObject && parentObject->endLine > -1)
  1476. insertLine = parentObject->endLine;
  1477. parsedObject = writeNewObject(object, newLines, insertLine, parentObject);
  1478. }
  1479. // Clean up the newLines memory
  1480. for (U32 i = 0; i < newLines.size(); i++)
  1481. {
  1482. if (newLines[i])
  1483. {
  1484. dFree(newLines[i]);
  1485. newLines[ i ] = NULL;
  1486. }
  1487. }
  1488. newLines.clear();
  1489. SimSet* set = dynamic_cast<SimSet*>(object);
  1490. if (set)
  1491. {
  1492. for(SimSet::iterator i = set->begin(); i != set->end(); i++)
  1493. {
  1494. SimObject* subObject = (SimObject*)(*i);
  1495. updateObject(subObject, parsedObject);
  1496. }
  1497. }
  1498. // Loop through the children of this parsedObject
  1499. // If they haven't been updated then assume that they
  1500. // don't exist in the file anymore
  1501. if (parsedObject)
  1502. {
  1503. for (S32 i = 0; i < mObjectBuffer.size(); i++)
  1504. {
  1505. ParsedObject* removeObj = mObjectBuffer[i];
  1506. if (removeObj->parentObject == parsedObject && !removeObj->updated)
  1507. {
  1508. removeParsedObject(removeObj);
  1509. mObjectBuffer.erase(i);
  1510. i--;
  1511. deleteObject(removeObj);
  1512. }
  1513. }
  1514. }
  1515. // Flag this as an updated object
  1516. if (parsedObject)
  1517. parsedObject->updated = true;
  1518. // Cleanup our created default object
  1519. delete defaultConObject;
  1520. }
  1521. bool PersistenceManager::saveDirtyFile()
  1522. {
  1523. FileStream stream;
  1524. stream.open( mCurrentFile, Torque::FS::File::Write );
  1525. if ( stream.getStatus() != Stream::Ok )
  1526. {
  1527. clearFileData();
  1528. return false;
  1529. }
  1530. for (U32 i = 0; i < mLineBuffer.size(); i++)
  1531. stream.writeLine((const U8*)mLineBuffer[i]);
  1532. stream.close();
  1533. //Con::printf("Successfully opened and wrote %s", mCurrentFile);
  1534. //Con::errorf("Updated Results:");
  1535. //for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1536. //{
  1537. // ParsedObject* parsedObject = mObjectBuffer[i];
  1538. // Con::warnf(" mObjectBuffer[%d]:", i);
  1539. // Con::warnf(" name = %s", parsedObject->name);
  1540. // Con::warnf(" className = %s", parsedObject->className);
  1541. // Con::warnf(" startLine = %d", parsedObject->startLine + 1);
  1542. // Con::warnf(" endLine = %d", parsedObject->endLine + 1);
  1543. // //if (mObjectBuffer[i]->properties.size() > 0)
  1544. // //{
  1545. // // Con::warnf(" properties:");
  1546. // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++)
  1547. // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name,
  1548. // // mObjectBuffer[i]->properties[j].value);
  1549. // //}
  1550. // if (!parsedObject->simObject.isNull())
  1551. // {
  1552. // SimObject* simObject = parsedObject->simObject;
  1553. // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId());
  1554. // Con::warnf(" declaration line = %d", simObject->getDeclarationLine());
  1555. // }
  1556. //}
  1557. // Clear our file data
  1558. clearFileData();
  1559. return true;
  1560. }
  1561. S32 QSORT_CALLBACK PersistenceManager::compareFiles(const void* a,const void* b)
  1562. {
  1563. DirtyObject* objectA = (DirtyObject*)(a);
  1564. DirtyObject* objectB = (DirtyObject*)(b);
  1565. if (objectA->isNull())
  1566. return -1;
  1567. else if (objectB->isNull())
  1568. return 1;
  1569. if (objectA->fileName == objectB->fileName)
  1570. return objectA->getObject()->getDeclarationLine() - objectB->getObject()->getDeclarationLine();
  1571. return dStricmp(objectA->fileName, objectB->fileName);
  1572. }
  1573. bool PersistenceManager::setDirty(SimObject* inObject, const char* inFileName)
  1574. {
  1575. // Check if the object is already in the dirty list...
  1576. DirtyObject *pDirty = findDirtyObject( inObject );
  1577. // The filename we will save this object to (later)..
  1578. String saveFile;
  1579. // Expand the script filename if we were passed one.
  1580. if ( inFileName )
  1581. {
  1582. char buffer[4096];
  1583. Con::expandScriptFilename( buffer, 4096, inFileName );
  1584. saveFile = buffer;
  1585. }
  1586. // If no filename was passed in, and the object was already dirty,
  1587. // we have nothing to do.
  1588. if ( saveFile.isEmpty() && pDirty )
  1589. return true;
  1590. // Otherwise default to the simObject's filename.
  1591. if ( saveFile.isEmpty() )
  1592. saveFile = inObject->getFilename();
  1593. // Error if still no filename.
  1594. if ( saveFile.isEmpty() )
  1595. {
  1596. if (inObject->getName())
  1597. Con::errorf("PersistenceManager::setDirty() - SimObject %s has no file name associated with it - can not save", inObject->getName());
  1598. else
  1599. Con::errorf("PersistenceManager::setDirty() - SimObject %d has no file name associated with it - can not save", inObject->getId());
  1600. return false;
  1601. }
  1602. // Update the DirtyObject's fileName if we have it
  1603. // else create a new one.
  1604. if ( pDirty )
  1605. pDirty->fileName = StringTable->insert( saveFile );
  1606. else
  1607. {
  1608. // Add the newly dirty object.
  1609. mDirtyObjects.increment();
  1610. mDirtyObjects.last().setObject( inObject );
  1611. mDirtyObjects.last().fileName = StringTable->insert( saveFile );
  1612. }
  1613. return true;
  1614. }
  1615. void PersistenceManager::removeDirty(SimObject* object)
  1616. {
  1617. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1618. {
  1619. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1620. if (dirtyObject.isNull())
  1621. continue;
  1622. if (dirtyObject.getObject() == object)
  1623. {
  1624. mDirtyObjects.erase(i);
  1625. break;
  1626. }
  1627. }
  1628. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1629. {
  1630. const RemoveField& field = mRemoveFields[i];
  1631. if (field.object != object)
  1632. continue;
  1633. mRemoveFields.erase(i);
  1634. if (i > 0)
  1635. i--;
  1636. }
  1637. }
  1638. void PersistenceManager::addRemoveField(SimObject* object, const char* fieldName)
  1639. {
  1640. // Check to see if this is an array variable
  1641. U32 arrayPos = 0;
  1642. const char* name = fieldName;
  1643. if (dStrlen(fieldName) > 3 && fieldName[dStrlen(fieldName) - 1] == ']')
  1644. {
  1645. // The last character is a ']' which *should* mean
  1646. // there is also a corresponding '['
  1647. const char* arrayPosStart = dStrrchr(fieldName, '[');
  1648. if (!arrayPosStart)
  1649. {
  1650. Con::errorf("PersistenceManager::addRemoveField() - error parsing array position - \
  1651. was expecting a '[' character");
  1652. }
  1653. else
  1654. {
  1655. // Parse the array position for the variable name
  1656. dSscanf(arrayPosStart, "[%d]", &arrayPos);
  1657. // Trim off the [<pos>] from the variable name
  1658. char* variableName = dStrdup(fieldName);
  1659. variableName[arrayPosStart - fieldName] = 0;
  1660. // Set the variable name to our new shortened name
  1661. name = StringTable->insert(variableName, true);
  1662. // Cleanup our variableName buffer
  1663. dFree( variableName );
  1664. }
  1665. }
  1666. // Make sure this field isn't already on the list
  1667. if (!findRemoveField(object, name, arrayPos))
  1668. {
  1669. mRemoveFields.increment();
  1670. RemoveField& field = mRemoveFields.last();
  1671. field.object = object;
  1672. field.fieldName = StringTable->insert(name);
  1673. field.arrayPos = arrayPos;
  1674. }
  1675. }
  1676. bool PersistenceManager::isDirty(SimObject* object)
  1677. {
  1678. return ( findDirtyObject( object ) != NULL );
  1679. }
  1680. PersistenceManager::DirtyObject* PersistenceManager::findDirtyObject(SimObject* object)
  1681. {
  1682. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1683. {
  1684. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1685. if (dirtyObject.isNull())
  1686. continue;
  1687. if (dirtyObject.getObject() == object)
  1688. return &mDirtyObjects[i];
  1689. }
  1690. return NULL;
  1691. }
  1692. bool PersistenceManager::findRemoveField(SimObject* object, const char* fieldName, U32 arrayPos)
  1693. {
  1694. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1695. {
  1696. if (mRemoveFields[i].object == object &&
  1697. mRemoveFields[i].arrayPos == arrayPos &&
  1698. dStricmp(mRemoveFields[i].fieldName, fieldName) == 0)
  1699. {
  1700. return true;
  1701. }
  1702. }
  1703. return false;
  1704. }
  1705. bool PersistenceManager::saveDirty()
  1706. {
  1707. // Remove any null SimObject's first
  1708. for (S32 i = 0; i < mDirtyObjects.size(); i++)
  1709. {
  1710. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1711. if (dirtyObject.isNull())
  1712. {
  1713. mDirtyObjects.erase(i);
  1714. i--;
  1715. }
  1716. }
  1717. // Sort by filename and declaration lines
  1718. dQsort(mDirtyObjects.address(), mDirtyObjects.size(), sizeof(DirtyList::value_type), compareFiles);
  1719. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1720. {
  1721. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1722. if (dirtyObject.isNull())
  1723. continue;
  1724. SimObject* object = dirtyObject.getObject();
  1725. if (!mCurrentFile || dStricmp(mCurrentFile, dirtyObject.fileName) != 0)
  1726. {
  1727. // If mCurrentFile is set then that means we
  1728. // changed file names so save our previous one
  1729. if (mCurrentFile)
  1730. saveDirtyFile();
  1731. // Open our new file and parse it
  1732. bool success = parseFile(dirtyObject.fileName);
  1733. if (!success)
  1734. {
  1735. const char *name = object->getName();
  1736. if (name)
  1737. {
  1738. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s %s (%d)",
  1739. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1740. }
  1741. else
  1742. {
  1743. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s (%d)",
  1744. dirtyObject.fileName, object->getClassName(), object->getId());
  1745. }
  1746. continue;
  1747. }
  1748. }
  1749. // Update this object's properties
  1750. //
  1751. // An empty script file (with 1 line) gets here with zero
  1752. // elements in the linebuffer, so this would prevent us from
  1753. // ever writing to it... Or is this test preventing bad things from
  1754. // happening if the file didn't exist at all?
  1755. //
  1756. if (mCurrentFile /*&& mLineBuffer.size() > 0*/)
  1757. updateObject(object);
  1758. }
  1759. // Save out our last file
  1760. if (mCurrentFile)
  1761. saveDirtyFile();
  1762. // Done writing out our dirty objects so reset everything
  1763. clearAll();
  1764. return true;
  1765. }
  1766. bool PersistenceManager::saveDirtyObject(SimObject* object)
  1767. {
  1768. // find our object passed in
  1769. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1770. {
  1771. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1772. if (dirtyObject.isNull())
  1773. continue;
  1774. if (dirtyObject.getObject() == object)
  1775. {
  1776. // Open our new file and parse it
  1777. bool success = parseFile(dirtyObject.fileName);
  1778. if (!success)
  1779. {
  1780. const char *name = object->getName();
  1781. if (name)
  1782. {
  1783. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s %s (%d)",
  1784. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1785. }
  1786. else
  1787. {
  1788. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s (%d)",
  1789. dirtyObject.fileName, object->getClassName(), object->getId());
  1790. }
  1791. return false;
  1792. }
  1793. // if the file exists then lets update and save
  1794. if(mCurrentFile)
  1795. {
  1796. updateObject(object);
  1797. saveDirtyFile();
  1798. }
  1799. break;
  1800. }
  1801. }
  1802. // remove this object from the dirty list
  1803. removeDirty(object);
  1804. return true;
  1805. }
  1806. void PersistenceManager::removeObjectFromFile(SimObject* object, const char* fileName)
  1807. {
  1808. if (mCurrentFile)
  1809. {
  1810. Con::errorf("PersistenceManager::removeObjectFromFile(): Can't remove an object from a \
  1811. file while another is currently opened");
  1812. return;
  1813. }
  1814. const char* file = object->getFilename();
  1815. if (fileName)
  1816. {
  1817. char buffer[1024];
  1818. Con::expandScriptFilename( buffer, 1024, fileName );
  1819. file = StringTable->insert(buffer);
  1820. }
  1821. bool success = false;
  1822. if ( file && file[ 0 ] )
  1823. success = parseFile(file);
  1824. if (!success)
  1825. {
  1826. const char *name = object->getName();
  1827. String errorNameStr;
  1828. if ( name )
  1829. errorNameStr = String::ToString( "%s %s (%d)", object->getClassName(), name, object->getId() );
  1830. else
  1831. errorNameStr = String::ToString( "%s (%d)", object->getClassName(), object->getId() );
  1832. if ( !file )
  1833. Con::errorf("PersistenceManager::removeObjectFromFile(): File was null trying to save %s", errorNameStr.c_str() );
  1834. else
  1835. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to open %s to save %s", file, errorNameStr.c_str() );
  1836. // Reset everything
  1837. clearAll();
  1838. return;
  1839. }
  1840. ParsedObject* parsedObject = findParsedObject(object);
  1841. if (!parsedObject)
  1842. {
  1843. const char *name = object->getName();
  1844. if (name)
  1845. {
  1846. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s %s (%d) in %s",
  1847. object->getClassName(), name, object->getId(), file);
  1848. }
  1849. else
  1850. {
  1851. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s (%d) in %s",
  1852. object->getClassName(), object->getId(), file);
  1853. }
  1854. // Reset everything
  1855. clearAll();
  1856. return;
  1857. }
  1858. removeParsedObject(parsedObject);
  1859. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1860. {
  1861. ParsedObject* removeObj = mObjectBuffer[i];
  1862. if (removeObj == parsedObject)
  1863. {
  1864. mObjectBuffer.erase(i);
  1865. break;
  1866. }
  1867. }
  1868. deleteObject(parsedObject);
  1869. // Save out the file
  1870. if (mCurrentFile)
  1871. saveDirtyFile();
  1872. // Reset everything
  1873. clearAll();
  1874. }
  1875. void PersistenceManager::deleteObjectsFromFile(const char* fileName)
  1876. {
  1877. if ( mCurrentFile )
  1878. {
  1879. Con::errorf( "PersistenceManager::deleteObjectsFromFile(): Cannot process while file while another is currently open." );
  1880. return;
  1881. }
  1882. // Expand Script File.
  1883. char buffer[1024];
  1884. Con::expandScriptFilename( buffer, 1024, fileName );
  1885. // Parse File.
  1886. if ( !parseFile( StringTable->insert(buffer) ) )
  1887. {
  1888. // Invalid.
  1889. return;
  1890. }
  1891. // Iterate over the objects.
  1892. for ( Vector<ParsedObject*>::iterator itr = mObjectBuffer.begin(); itr != mObjectBuffer.end(); itr++ )
  1893. {
  1894. SimObject *object;
  1895. if ( Sim::findObject( ( *itr )->name, object ) )
  1896. {
  1897. // Delete the Object.
  1898. object->deleteObject();
  1899. }
  1900. }
  1901. // Clear.
  1902. clearAll();
  1903. }
  1904. DefineEngineMethod( PersistenceManager, deleteObjectsFromFile, void, ( const char * fileName ), , "( fileName )"
  1905. "Delete all of the objects that are created from the given file." )
  1906. {
  1907. // Delete Objects.
  1908. object->deleteObjectsFromFile( fileName );
  1909. }
  1910. DefineEngineMethod( PersistenceManager, setDirty, void, ( const char * objName, const char * fileName ), (""), "(SimObject object, [filename])"
  1911. "Mark an existing SimObject as dirty (will be written out when saveDirty() is called).")
  1912. {
  1913. SimObject *dirtyObject = NULL;
  1914. if (String::compare(objName,"") != 0)
  1915. {
  1916. if (!Sim::findObject(objName, dirtyObject))
  1917. {
  1918. Con::printf("PersistenceManager::setDirty(): Invalid SimObject: %s", objName);
  1919. return;
  1920. }
  1921. }
  1922. // Prevent ourselves from shooting us in the foot.
  1923. if( dirtyObject == Sim::getRootGroup() )
  1924. {
  1925. Con::errorf( "PersistenceManager::setDirty(): Cannot save RootGroup" );
  1926. return;
  1927. }
  1928. if (dirtyObject)
  1929. {
  1930. if (String::compare( fileName,"")!=0)
  1931. object->setDirty(dirtyObject, fileName);
  1932. else
  1933. object->setDirty(dirtyObject);
  1934. }
  1935. }
  1936. DefineEngineMethod( PersistenceManager, removeDirty, void, ( const char * objName ), , "(SimObject object)"
  1937. "Remove a SimObject from the dirty list.")
  1938. {
  1939. SimObject *dirtyObject = NULL;
  1940. if (String::compare( objName,"")!=0)
  1941. {
  1942. if (!Sim::findObject(objName, dirtyObject))
  1943. {
  1944. Con::printf("PersistenceManager::removeDirty(): Invalid SimObject: %s", objName);
  1945. return;
  1946. }
  1947. }
  1948. if (dirtyObject)
  1949. object->removeDirty(dirtyObject);
  1950. }
  1951. DefineEngineMethod( PersistenceManager, isDirty, bool, ( const char * objName ), , "(SimObject object)"
  1952. "Returns true if the SimObject is on the dirty list.")
  1953. {
  1954. SimObject *dirtyObject = NULL;
  1955. if (String::compare ( objName,"")!=0)
  1956. {
  1957. if (!Sim::findObject(objName, dirtyObject))
  1958. {
  1959. Con::printf("PersistenceManager::isDirty(): Invalid SimObject: %s", objName);
  1960. return false;
  1961. }
  1962. }
  1963. if (dirtyObject)
  1964. return object->isDirty(dirtyObject);
  1965. return false;
  1966. }
  1967. DefineEngineMethod( PersistenceManager, hasDirty, bool, (), , "()"
  1968. "Returns true if the manager has dirty objects to save." )
  1969. {
  1970. return object->hasDirty();
  1971. }
  1972. DefineEngineMethod( PersistenceManager, getDirtyObjectCount, S32, (), , "()"
  1973. "Returns the number of dirty objects." )
  1974. {
  1975. return object->getDirtyList().size();
  1976. }
  1977. DefineEngineMethod( PersistenceManager, getDirtyObject, S32, (S32 index), , "( index )"
  1978. "Returns the ith dirty object." )
  1979. {
  1980. if ( index < 0 || index >= object->getDirtyList().size() )
  1981. {
  1982. Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", index );
  1983. return 0;
  1984. }
  1985. // Fetch Object.
  1986. const PersistenceManager::DirtyObject& dirtyObject = object->getDirtyList()[index];
  1987. // Return Id.
  1988. return ( dirtyObject.getObject() ) ? dirtyObject.getObject()->getId() : 0;
  1989. }
  1990. DefineEngineMethod( PersistenceManager, listDirty, void, (), , "()"
  1991. "Prints the dirty list to the console.")
  1992. {
  1993. const PersistenceManager::DirtyList dirtyList = object->getDirtyList();
  1994. for(U32 i = 0; i < dirtyList.size(); i++)
  1995. {
  1996. const PersistenceManager::DirtyObject& dirtyObject = dirtyList[i];
  1997. if (dirtyObject.isNull())
  1998. continue;
  1999. SimObject *obj = dirtyObject.getObject();
  2000. bool isSet = dynamic_cast<SimSet *>(obj) != 0;
  2001. const char *name = obj->getName();
  2002. if (name)
  2003. {
  2004. Con::printf(" %d,\"%s\": %s %s %s", obj->getId(), name,
  2005. obj->getClassName(), dirtyObject.fileName, isSet ? "(g)":"");
  2006. }
  2007. else
  2008. {
  2009. Con::printf(" %d: %s %s, %s", obj->getId(), obj->getClassName(),
  2010. dirtyObject.fileName, isSet ? "(g)" : "");
  2011. }
  2012. }
  2013. }
  2014. DefineEngineMethod( PersistenceManager, saveDirty, bool, (), , "()"
  2015. "Saves all of the SimObject's on the dirty list to their respective files.")
  2016. {
  2017. return object->saveDirty();
  2018. }
  2019. DefineEngineMethod( PersistenceManager, saveDirtyObject, bool, (const char * objName), , "(SimObject object)"
  2020. "Save a dirty SimObject to it's file.")
  2021. {
  2022. SimObject *dirtyObject = NULL;
  2023. if (String::compare ( objName, "")!=0)
  2024. {
  2025. if (!Sim::findObject(objName, dirtyObject))
  2026. {
  2027. Con::printf("%s(): Invalid SimObject: %s", object->getName(), objName);
  2028. return false;
  2029. }
  2030. }
  2031. if (dirtyObject)
  2032. return object->saveDirtyObject(dirtyObject);
  2033. return false;
  2034. }
  2035. DefineEngineMethod( PersistenceManager, clearAll, void, (), , "()"
  2036. "Clears all the tracked objects without saving them." )
  2037. {
  2038. object->clearAll();
  2039. }
  2040. DefineEngineMethod( PersistenceManager, removeObjectFromFile, void, (const char * objName, const char * filename),("") , "(SimObject object, [filename])"
  2041. "Remove an existing SimObject from a file (can optionally specify a different file than \
  2042. the one it was created in.")
  2043. {
  2044. SimObject *dirtyObject = NULL;
  2045. if (String::compare ( objName , "")!=0)
  2046. {
  2047. if (!Sim::findObject(objName, dirtyObject))
  2048. {
  2049. Con::printf("PersistenceManager::removeObjectFromFile(): Invalid SimObject: %s", objName);
  2050. return;
  2051. }
  2052. }
  2053. if (dirtyObject)
  2054. {
  2055. if (String::compare( filename,"")!=0)
  2056. object->removeObjectFromFile(dirtyObject, filename);
  2057. else
  2058. object->removeObjectFromFile(dirtyObject);
  2059. }
  2060. }
  2061. DefineEngineMethod( PersistenceManager, removeField, void, (const char * objName, const char * fieldName), , "(SimObject object, string fieldName)"
  2062. "Remove a specific field from an object declaration.")
  2063. {
  2064. SimObject *dirtyObject = NULL;
  2065. if (String::compare(objName,"")!=0)
  2066. {
  2067. if (!Sim::findObject(objName, dirtyObject))
  2068. {
  2069. Con::printf("PersistenceManager::removeField(): Invalid SimObject: %s", objName);
  2070. return;
  2071. }
  2072. }
  2073. if (dirtyObject)
  2074. {
  2075. if (String::compare(fieldName,"") != 0)
  2076. object->addRemoveField(dirtyObject, fieldName);
  2077. }
  2078. }