persistenceManager.cpp 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398
  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. char* PersistenceManager::getObjectIndent(ParsedObject* object)
  524. {
  525. char* indent = Con::getReturnBuffer(2048);
  526. indent[0] = 0;
  527. if (!object)
  528. return indent;
  529. if (object->startLine < 0 || object->startLine >= mLineBuffer.size())
  530. return indent;
  531. const char* line = mLineBuffer[object->startLine];
  532. if (line)
  533. {
  534. const char* nonSpace = line;
  535. U32 strLen = dStrlen(line);
  536. for (U32 i = 0; i < strLen; i++)
  537. {
  538. if (*nonSpace != ' ')
  539. break;
  540. nonSpace++;
  541. }
  542. dStrncpy(indent, line, nonSpace - line);
  543. indent[nonSpace - line] = 0;
  544. }
  545. return indent;
  546. }
  547. void PersistenceManager::updatePositions(U32 lineNumber, U32 startPos, S32 diff)
  548. {
  549. if (diff == 0)
  550. return;
  551. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  552. {
  553. ParsedObject* object = mObjectBuffer[i];
  554. if (object->nameLine == lineNumber && object->namePosition > startPos)
  555. object->namePosition += diff;
  556. if (object->endLine == lineNumber && object->endPosition > startPos)
  557. object->endPosition += diff;
  558. if (lineNumber >= object->startLine && lineNumber <= object->endLine)
  559. {
  560. for (U32 j = 0; j < object->properties.size(); j++)
  561. {
  562. ParsedProperty& prop = object->properties[j];
  563. S32 propStartPos = prop.startPosition;
  564. S32 endPos = prop.endPosition;
  565. S32 valuePos = prop.valuePosition;
  566. if (lineNumber == prop.startLine && propStartPos > startPos)
  567. {
  568. propStartPos += diff;
  569. if (propStartPos < 0)
  570. propStartPos = 0;
  571. prop.startPosition = valuePos;
  572. }
  573. if (lineNumber == prop.endLine && endPos > startPos)
  574. {
  575. endPos += diff;
  576. if (endPos < 0)
  577. endPos = 0;
  578. prop.endPosition = endPos;
  579. }
  580. if (lineNumber == prop.valueLine && valuePos > startPos)
  581. {
  582. valuePos += diff;
  583. if (valuePos < 0)
  584. valuePos = 0;
  585. prop.valuePosition = valuePos;
  586. }
  587. }
  588. }
  589. }
  590. }
  591. void PersistenceManager::updateLineOffsets(U32 startLine, S32 diff, ParsedObject* skipObject)
  592. {
  593. if (diff == 0)
  594. return;
  595. if (startLine >= mLineBuffer.size())
  596. return;
  597. if (startLine + diff >= mLineBuffer.size())
  598. return;
  599. // Make sure we don't double offset a SimObject's
  600. // declaration line
  601. SimObjectList updated;
  602. if (skipObject && !skipObject->simObject.isNull())
  603. updated.push_back_unique(skipObject->simObject);
  604. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  605. {
  606. ParsedObject* object = mObjectBuffer[i];
  607. // See if this is the skipObject
  608. if (skipObject && skipObject == object)
  609. continue;
  610. // We can safely ignore objects that
  611. // came earlier in the file
  612. if (object->endLine < startLine)
  613. continue;
  614. if (object->startLine >= startLine)
  615. object->startLine += diff;
  616. if (object->nameLine >= startLine)
  617. object->nameLine += diff;
  618. for (U32 j = 0; j < object->properties.size(); j++)
  619. {
  620. if (object->properties[j].startLine >= startLine)
  621. object->properties[j].startLine += diff;
  622. if (object->properties[j].endLine >= startLine)
  623. object->properties[j].endLine += diff;
  624. if (object->properties[j].valueLine >= startLine)
  625. object->properties[j].valueLine += diff;
  626. }
  627. if (object->endLine >= startLine)
  628. object->endLine += diff;
  629. if (!object->simObject.isNull() &&
  630. object->simObject->getDeclarationLine() > startLine)
  631. {
  632. // Check for already updated SimObject's
  633. U32 currSize = updated.size();
  634. updated.push_back_unique(object->simObject);
  635. if (updated.size() == currSize)
  636. continue;
  637. S32 newDeclLine = object->simObject->getDeclarationLine() + diff;
  638. if (newDeclLine < 0)
  639. newDeclLine = 0;
  640. object->simObject->setDeclarationLine(newDeclLine);
  641. }
  642. }
  643. }
  644. PersistenceManager::ParsedObject* PersistenceManager::findParentObject(SimObject* object, ParsedObject* parentObject)
  645. {
  646. ParsedObject* ret = NULL;
  647. if (!object)
  648. return ret;
  649. // First test for the SimGroup it belongs to
  650. ret = findParsedObject(object->getGroup(), parentObject);
  651. if (ret)
  652. return ret;
  653. // TODO: Test all of the SimSet's that this object belongs to
  654. return ret;
  655. }
  656. PersistenceManager::ParsedObject* PersistenceManager::findParsedObject(SimObject* object, ParsedObject* parentObject)
  657. {
  658. if (!object)
  659. return NULL;
  660. // See if our object belongs to a parent
  661. if (!parentObject)
  662. parentObject = findParentObject(object, parentObject);
  663. // First let's compare the object to the SimObject's that
  664. // we matched to our ParsedObject's when we loaded them
  665. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  666. {
  667. ParsedObject* testObj = mObjectBuffer[i];
  668. if (testObj->simObject == object)
  669. {
  670. // Deal with children objects
  671. if (testObj->parentObject != parentObject)
  672. continue;
  673. return testObj;
  674. }
  675. }
  676. // Didn't find it in our ParsedObject's SimObject's
  677. // so see if we can find one that corresponds to the
  678. // same name and className
  679. const char *originalName = object->getOriginalName();
  680. // Find the corresponding ParsedObject
  681. if (originalName && originalName[0])
  682. {
  683. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  684. {
  685. ParsedObject* testObj = mObjectBuffer[i];
  686. if (testObj->name == originalName)
  687. {
  688. // Deal with children objects
  689. if (testObj->parentObject != parentObject)
  690. continue;
  691. return testObj;
  692. }
  693. }
  694. }
  695. //Check internal names
  696. if (object->getInternalName())
  697. {
  698. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  699. {
  700. ParsedObject* testObj = mObjectBuffer[i];
  701. for (U32 j = 0; j < testObj->properties.size(); j++)
  702. {
  703. const ParsedProperty &prop = testObj->properties[j];
  704. if ( String::compare( prop.name, "internalName" ) == 0 &&
  705. String::compare( prop.value, object->getInternalName() ) == 0 )
  706. return testObj;
  707. else if ( String::compare(prop.name, "internalName") == 0)
  708. break;
  709. }
  710. }
  711. }
  712. return NULL;
  713. }
  714. void PersistenceManager::updateToken( const U32 lineNumber, const U32 linePosition, const U32 oldValueLen, const char* newValue, bool addQuotes )
  715. {
  716. // Make sure we have a valid lineNumber
  717. if (lineNumber < 0 || linePosition < 0 ||
  718. lineNumber >= mLineBuffer.size())
  719. return;
  720. // Grab the line that the value is on
  721. const char* line = mLineBuffer[lineNumber];
  722. U32 newValueLen = ( newValue ) ? dStrlen(newValue) : 0;
  723. // Make sure we have a valid linePosition
  724. if (linePosition >= dStrlen(line) ||
  725. linePosition + oldValueLen > dStrlen(line))
  726. return;
  727. // Get all of the characters up to the value position
  728. U32 preStringLen = linePosition;
  729. bool needQuotes = false;
  730. if( addQuotes && line[ linePosition - 1 ] != '"' )
  731. {
  732. preStringLen ++;
  733. needQuotes = true;
  734. }
  735. char* preString = ( char* ) dMalloc( preStringLen + 1 );
  736. dMemcpy( preString, line, linePosition );
  737. if( needQuotes )
  738. {
  739. preString[ linePosition ] = '"';
  740. preString[ linePosition + 1 ] = 0;
  741. }
  742. else
  743. preString[ linePosition ] = 0;
  744. // Get all of the characters that occur after the value
  745. const char* postStringSrc = line + linePosition + oldValueLen;
  746. U32 postStringLen = dStrlen( postStringSrc );
  747. if( needQuotes )
  748. postStringLen ++;
  749. char* postString = ( char* ) dMalloc( postStringLen + 1 );
  750. if( needQuotes )
  751. postString[ 0 ] = '"';
  752. dStrcpy( &postString[ needQuotes ? 1 : 0 ], postStringSrc, postStringLen + (needQuotes ? 0 : 1) );
  753. postString[ postStringLen ] = 0;
  754. // Calculate the length of our new line
  755. U32 newLineLen = 0;
  756. newLineLen += preStringLen;
  757. newLineLen += newValueLen;
  758. newLineLen += postStringLen;
  759. // Create a buffer for our new line and
  760. // null terminate it
  761. char* newLine = ( char* ) dMalloc( newLineLen + 1 );
  762. newLine[0] = 0;
  763. // Build the new line with the
  764. // preString + newValue + postString
  765. dStrcat(newLine, preString, newLineLen + 1);
  766. if ( newValue )
  767. dStrcat(newLine, newValue, newLineLen + 1);
  768. dStrcat(newLine, postString, newLineLen + 1);
  769. // Clear our existing line
  770. if (mLineBuffer[lineNumber])
  771. {
  772. dFree( mLineBuffer[ lineNumber ] );
  773. mLineBuffer[ lineNumber ] = NULL;
  774. }
  775. // Set the new line
  776. mLineBuffer[lineNumber] = newLine;
  777. // Figure out the size difference of the old value
  778. // and new value in case we need to update any else
  779. // on the line after it
  780. S32 diff = newValueLen - oldValueLen;
  781. // Update anything that is on the line after this that needs
  782. // to change its offsets to reflect the new line
  783. updatePositions(lineNumber, linePosition, diff);
  784. // Clean up our buffers
  785. dFree( preString );
  786. dFree( postString );
  787. }
  788. const char* PersistenceManager::getFieldValue(SimObject* object, const char* fieldName, U32 arrayPos)
  789. {
  790. // Our return string
  791. char* ret = NULL;
  792. // Buffer to hold the string equivalent of the arrayPos
  793. char arrayPosStr[8];
  794. dSprintf(arrayPosStr, 8, "%d", arrayPos);
  795. // Get the object's value
  796. const char *value = object->getDataField(fieldName, arrayPosStr );
  797. if (value)
  798. ret = dStrdup(value);
  799. return ret;
  800. }
  801. const char* PersistenceManager::createNewProperty(const char* name, const char* value, bool isArray, U32 arrayPos)
  802. {
  803. if (!name || !value)
  804. return NULL;
  805. AssertFatal( value[0] != StringTagPrefixByte, "Got tagged string!" );
  806. char* newProp = ( char* ) dMalloc( 2048 );
  807. dMemset(newProp, 0, 2048);
  808. if (value)
  809. {
  810. if (isArray)
  811. dSprintf(newProp, 2048, "%s[%d] = \"%s\";", name, arrayPos, value);
  812. else
  813. dSprintf(newProp, 2048, "%s = \"%s\";", name, value);
  814. }
  815. else
  816. {
  817. if (isArray)
  818. dSprintf(newProp, 2048, "%s[%d] = \"\";", name, arrayPos);
  819. else
  820. dSprintf(newProp, 2048, "%s = \"\";", name);
  821. }
  822. return newProp;
  823. }
  824. bool PersistenceManager::isEmptyLine(const char* line)
  825. {
  826. // Simple test first
  827. if (!line || dStrlen(line) == 0)
  828. return true;
  829. U32 len = dStrlen(line);
  830. for (U32 i = 0; i < len; i++)
  831. {
  832. const char& c = line[i];
  833. // Skip "empty" characters
  834. if (c == ' ' ||
  835. c == '\t' ||
  836. c == '\r' ||
  837. c == '\n')
  838. {
  839. continue;
  840. }
  841. // If we have made it to the an end of the line
  842. // comment then consider this an empty line
  843. if (c == '/')
  844. {
  845. if (i < len - 1)
  846. {
  847. if (line[i + 1] == '/')
  848. return true;
  849. }
  850. }
  851. // If it isn't an "empty" character or a comment then
  852. // we have a valid character on the line and it isn't empty
  853. return false;
  854. }
  855. return true;
  856. }
  857. void PersistenceManager::removeLine(U32 lineNumber)
  858. {
  859. if (lineNumber >= mLineBuffer.size())
  860. return;
  861. if (mLineBuffer[lineNumber])
  862. {
  863. dFree( mLineBuffer[ lineNumber ] );
  864. mLineBuffer[ lineNumber ] = NULL;
  865. }
  866. mLineBuffer.erase(lineNumber);
  867. updateLineOffsets(lineNumber, -1);
  868. }
  869. void PersistenceManager::removeTextBlock(U32 startLine, U32 endLine, U32 startPos, U32 endPos, bool removeEmptyLines)
  870. {
  871. // Make sure we have valid lines
  872. if (startLine >= mLineBuffer.size() || endLine >= mLineBuffer.size())
  873. return;
  874. // We assume that the startLine is before the endLine
  875. if (startLine > endLine)
  876. return;
  877. // Grab the lines (they may be the same)
  878. const char* startLineText = mLineBuffer[startLine];
  879. const char* endLineText = mLineBuffer[endLine];
  880. // Make sure we have a valid startPos
  881. if (startPos >= dStrlen(startLineText))
  882. return;
  883. // Make sure we have a valid endPos
  884. if (endPos > dStrlen(endLineText))
  885. return;
  886. if (startLine == endLine)
  887. {
  888. // Now let updateToken do the heavy lifting on removing it
  889. updateToken(startLine, startPos, endPos - startPos, "");
  890. // Handle removing an empty lines if desired
  891. if (removeEmptyLines)
  892. {
  893. const char* line = mLineBuffer[startLine];
  894. if (isEmptyLine(line))
  895. removeLine(startLine);
  896. }
  897. }
  898. else
  899. {
  900. // Start with clearing the startLine from startPos to the end
  901. updateToken(startLine, startPos, dStrlen(startLineText + startPos), "");
  902. // Then clear the endLine from beginning to endPos
  903. updateToken(endLine, 0, endPos, "");
  904. // Handle removing an empty endLine if desired
  905. if (removeEmptyLines)
  906. {
  907. const char* line = mLineBuffer[endLine];
  908. if (isEmptyLine(line))
  909. removeLine(endLine);
  910. }
  911. // Handle removing any lines between the startLine and endLine
  912. for (U32 i = startLine + 1; i < endLine; i++)
  913. removeLine(startLine + 1);
  914. // Handle removing an empty startLine if desired
  915. if (removeEmptyLines)
  916. {
  917. const char* line = mLineBuffer[startLine];
  918. if (isEmptyLine(line))
  919. removeLine(startLine);
  920. }
  921. }
  922. }
  923. void PersistenceManager::removeParsedObject(ParsedObject* parsedObject)
  924. {
  925. if (!parsedObject)
  926. return;
  927. if (parsedObject->startLine < 0 || parsedObject->startLine >= mLineBuffer.size())
  928. return;
  929. if (parsedObject->endLine < 0 || parsedObject->startLine >= mLineBuffer.size())
  930. return;
  931. removeTextBlock(parsedObject->startLine, parsedObject->endLine,
  932. parsedObject->startPosition, parsedObject->endPosition+1, true); // +1 to remove trailing semicolon as well
  933. parsedObject->parentObject = NULL;
  934. parsedObject->simObject = NULL;
  935. }
  936. void PersistenceManager::removeField(const ParsedProperty& prop)
  937. {
  938. if (prop.startLine < 0 || prop.startLine >= mLineBuffer.size())
  939. return;
  940. if (prop.endLine < 0 || prop.endLine >= mLineBuffer.size())
  941. return;
  942. S32 endPosition = prop.endPosition+1; // +1 to remove trailing semicolon as well
  943. if ((endPosition < dStrlen(mLineBuffer[prop.endLine])) &&
  944. (mLineBuffer[prop.endLine][endPosition] == ';')) // adjust end position for quoted values (otherwise a trailing semicolon will remain)
  945. endPosition++;
  946. removeTextBlock(prop.startLine, prop.endLine, prop.startPosition, endPosition, true);
  947. }
  948. U32 PersistenceManager::writeProperties(const Vector<const char*>& properties, const U32 insertLine, const char* objectIndent)
  949. {
  950. U32 currInsertLine = insertLine;
  951. for (U32 i = 0; i < properties.size(); i++)
  952. {
  953. const char* prop = properties[i];
  954. if (!prop || dStrlen(prop) == 0)
  955. continue;
  956. U32 len = dStrlen(objectIndent) + dStrlen(prop) + 4;
  957. char* newLine = ( char* ) dMalloc( len );
  958. dSprintf(newLine, len, "%s %s", objectIndent, prop);
  959. mLineBuffer.insert(currInsertLine, newLine);
  960. currInsertLine++;
  961. }
  962. return currInsertLine - insertLine;
  963. }
  964. PersistenceManager::ParsedObject* PersistenceManager::writeNewObject(SimObject* object, const Vector<const char*>& properties, const U32 insertLine, ParsedObject* parentObject)
  965. {
  966. ParsedObject* parsedObject = new ParsedObject;
  967. parsedObject->name = object->getName();
  968. parsedObject->className = object->getClassName();
  969. parsedObject->simObject = object;
  970. U32 currInsertLine = insertLine;
  971. // If the parentObject isn't set see if
  972. // we can find it in the file
  973. if (!parentObject)
  974. parentObject = findParentObject(object);
  975. parsedObject->parentObject = parentObject;
  976. char* indent = getObjectIndent(parentObject);
  977. if (parentObject)
  978. dStrcat(indent, " \0", 2048);
  979. // Write out the beginning of the object declaration
  980. const char* dclToken = "new";
  981. if (dynamic_cast<Material*>(object) ||
  982. dynamic_cast<CustomMaterial*>(object) ||
  983. dynamic_cast<GuiControlProfile*>(object) ||
  984. dynamic_cast<TSShapeConstructor*>(object))
  985. dclToken = "singleton";
  986. else if( dynamic_cast< SimDataBlock* >( object ) )
  987. dclToken = "datablock";
  988. char newLine[ 4096 ];
  989. dMemset(newLine, 0, sizeof( newLine));
  990. // New line before an object declaration
  991. dSprintf(newLine, sizeof( newLine ), "");
  992. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  993. currInsertLine++;
  994. dMemset(newLine, 0, sizeof( newLine ));
  995. parsedObject->startLine = currInsertLine;
  996. parsedObject->nameLine = currInsertLine;
  997. parsedObject->namePosition = dStrlen(indent) + dStrlen(dclToken) + dStrlen(object->getClassName()) + 2;
  998. // Objects that had no name were getting saved out as: Object((null))
  999. if ( object->getName() != NULL )
  1000. {
  1001. if( object->getCopySource() )
  1002. dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s : %s)", indent, dclToken, object->getClassName(), object->getName(),
  1003. object->getCopySource() ? object->getCopySource()->getName() : "" );
  1004. else
  1005. dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s)", indent, dclToken, object->getClassName(), object->getName());
  1006. }
  1007. else
  1008. dSprintf(newLine, sizeof( newLine ), "%s%s %s()", indent, dclToken, object->getClassName() );
  1009. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1010. currInsertLine++;
  1011. dMemset(newLine, 0, sizeof( newLine ));
  1012. dSprintf(newLine, sizeof( newLine ), "%s{", indent);
  1013. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1014. currInsertLine++;
  1015. dMemset(newLine, 0, sizeof( newLine ));
  1016. currInsertLine += writeProperties(properties, currInsertLine, indent);
  1017. parsedObject->endLine = currInsertLine;
  1018. parsedObject->updated = true;
  1019. dSprintf(newLine, sizeof( newLine ), "%s};", indent);
  1020. mLineBuffer.insert(currInsertLine, dStrdup(newLine));
  1021. currInsertLine++;
  1022. updateLineOffsets(insertLine, currInsertLine - insertLine, parsedObject);
  1023. mObjectBuffer.push_back(parsedObject);
  1024. // Update the SimObject to reflect its saved name and declaration line.
  1025. // These values should always reflect what is in the file, even if the object
  1026. // has not actually been re-created from an execution of that file yet.
  1027. object->setOriginalName( object->getName() );
  1028. object->setDeclarationLine( currInsertLine );
  1029. if (mCurrentFile)
  1030. object->setFilename(mCurrentFile);
  1031. return parsedObject;
  1032. }
  1033. void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObject)
  1034. {
  1035. // Create a default object of the same type
  1036. ConsoleObject *defaultConObject = ConsoleObject::create(object->getClassName());
  1037. SimObject* defaultObject = dynamic_cast<SimObject*>(defaultConObject);
  1038. // ***Really*** shouldn't happen
  1039. if (!defaultObject)
  1040. return;
  1041. Vector<const char*> newLines;
  1042. ParsedObject* parsedObject = findParsedObject(object, parentObject);
  1043. // If we don't already have an association between the ParsedObject
  1044. // and the SimObject then go ahead and create it
  1045. if (parsedObject && parsedObject->simObject.isNull())
  1046. parsedObject->simObject = object;
  1047. // Kill all fields on the remove list.
  1048. for( U32 i = 0; i < mRemoveFields.size(); ++ i )
  1049. {
  1050. RemoveField& field = mRemoveFields[ i ];
  1051. if( field.object != object )
  1052. continue;
  1053. S32 propertyIndex = getPropertyIndex( parsedObject, field.fieldName, field.arrayPos );
  1054. if( propertyIndex != -1 )
  1055. removeField( parsedObject->properties[ propertyIndex ] );
  1056. }
  1057. // Get our field list
  1058. const AbstractClassRep::FieldList &list = object->getFieldList();
  1059. for(U32 i = 0; i < list.size(); i++)
  1060. {
  1061. const AbstractClassRep::Field* f = &list[i];
  1062. // Skip the special field types.
  1063. if ( f->type >= AbstractClassRep::ARCFirstCustomField )
  1064. continue;
  1065. for(U32 j = 0; S32(j) < f->elementCount; j++)
  1066. {
  1067. const char* value = getFieldValue(object, f->pFieldname, j);
  1068. // Make sure we got a value
  1069. if (!value)
  1070. continue;
  1071. // Let's see if this field is already in the file
  1072. S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j);
  1073. if (propertyIndex > -1)
  1074. {
  1075. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1076. // If this field is on the remove list then remove it and continue
  1077. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
  1078. {
  1079. removeField( parsedObject->properties[ propertyIndex ] );
  1080. dFree( value );
  1081. continue;
  1082. }
  1083. // Run the parsed value through the console system conditioners so
  1084. // that it will better match the data we got back from the object.
  1085. const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
  1086. // If our data doesn't match then we get to update it.
  1087. //
  1088. // As for copy-sources, we just assume here that if a property setting
  1089. // is there in the file, the user does not want it inherited from the copy-source
  1090. // even in the case the actual values are identical.
  1091. if( dStricmp(value, evalue) != 0 )
  1092. {
  1093. if( value[ 0 ] == '\0' &&
  1094. dStricmp( getFieldValue( defaultObject, f->pFieldname, j ), value ) == 0 &&
  1095. ( !object->getCopySource() || dStricmp( getFieldValue( object->getCopySource(), f->pFieldname, j ), value ) == 0 ) )
  1096. {
  1097. removeField( prop );
  1098. }
  1099. else
  1100. {
  1101. // TODO: This should be wrapped in a helper method... probably.
  1102. // Detect and collapse relative path information
  1103. if (f->type == TypeFilename ||
  1104. f->type == TypeStringFilename ||
  1105. f->type == TypeImageFilename ||
  1106. f->type == TypePrefabFilename ||
  1107. f->type == TypeShapeFilename ||
  1108. f->type == TypeSoundFilename )
  1109. {
  1110. char fnBuf[1024];
  1111. Con::collapseScriptFilename(fnBuf, 1024, value);
  1112. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
  1113. }
  1114. else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
  1115. {
  1116. char cmdBuf[1024];
  1117. expandEscape(cmdBuf, value);
  1118. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
  1119. }
  1120. else
  1121. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
  1122. }
  1123. }
  1124. }
  1125. else
  1126. {
  1127. // No need to process a removed field that doesn't exist in the file
  1128. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
  1129. {
  1130. dFree( value );
  1131. continue;
  1132. }
  1133. bool mustUpdate = false;
  1134. // If we didn't find the property in the ParsedObject
  1135. // then we need to compare against the default value
  1136. // for this property and save it out if it is different
  1137. const char* defaultValue = getFieldValue(defaultObject, f->pFieldname, j);
  1138. if( !defaultValue || dStricmp( value, defaultValue ) != 0 )
  1139. {
  1140. // Value differs. Check whether it also differs from the
  1141. // value in the copy source if there is one.
  1142. if( object->getCopySource() )
  1143. {
  1144. const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j );
  1145. if( !copySourceValue || dStricmp( copySourceValue, value ) != 0 )
  1146. mustUpdate = true;
  1147. if( copySourceValue )
  1148. dFree( copySourceValue );
  1149. }
  1150. else
  1151. mustUpdate = true;
  1152. }
  1153. else
  1154. {
  1155. // Value does not differ. If it differs from the copy source's value,
  1156. // though, we still want to write it out as otherwise we'll see the
  1157. // copy source's value override us.
  1158. if( object->getCopySource() )
  1159. {
  1160. const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j );
  1161. if( copySourceValue && dStricmp( copySourceValue, value ) != 0 )
  1162. mustUpdate = true;
  1163. if( copySourceValue )
  1164. dFree( copySourceValue );
  1165. }
  1166. }
  1167. // The default value for most string type fields is
  1168. // NULL so we can't just continue here or we'd never ever
  1169. // write them out...
  1170. //
  1171. //if (!defaultValue)
  1172. // continue;
  1173. // If the object's value is different from the default
  1174. // value then add it to the ParsedObject's newLines
  1175. if ( mustUpdate )
  1176. {
  1177. // TODO: This should be wrapped in a helper method... probably.
  1178. // Detect and collapse relative path information
  1179. if (f->type == TypeFilename ||
  1180. f->type == TypeStringFilename ||
  1181. f->type == TypeImageFilename ||
  1182. f->type == TypePrefabFilename ||
  1183. f->type == TypeShapeFilename ||
  1184. f->type == TypeSoundFilename )
  1185. {
  1186. char fnBuf[1024];
  1187. Con::collapseScriptFilename(fnBuf, 1024, value);
  1188. newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
  1189. }
  1190. else if (f->type == TypeCommand)
  1191. {
  1192. char cmdBuf[1024];
  1193. expandEscape(cmdBuf, value);
  1194. newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
  1195. }
  1196. else
  1197. newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
  1198. }
  1199. if (defaultValue)
  1200. dFree( defaultValue );
  1201. }
  1202. dFree( value );
  1203. }
  1204. }
  1205. // Handle dynamic fields
  1206. SimFieldDictionary* fieldDict = object->getFieldDictionary();
  1207. for(SimFieldDictionaryIterator itr(fieldDict); *itr; ++itr)
  1208. {
  1209. SimFieldDictionary::Entry * entry = (*itr);
  1210. if( !entry->value )
  1211. continue;
  1212. // Let's see if this field is already in the file
  1213. S32 propertyIndex = getPropertyIndex(parsedObject, entry->slotName);
  1214. if (propertyIndex > -1)
  1215. {
  1216. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1217. // If this field is on the remove list then remove it and continue
  1218. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1219. {
  1220. removeField( parsedObject->properties[ propertyIndex ] );
  1221. continue;
  1222. }
  1223. if( object->getCopySource() )
  1224. {
  1225. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1226. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1227. {
  1228. removeField( prop );
  1229. continue;
  1230. }
  1231. }
  1232. const char* evalue = prop.value;
  1233. const char *entryVal = entry->value;
  1234. if ( entryVal[0] == StringTagPrefixByte )
  1235. entryVal = gNetStringTable->lookupString( dAtoi( entryVal+1 ) );
  1236. else
  1237. {
  1238. // Run the parsed value through the console system conditioners so
  1239. // that it will better match the data we got back from the object.
  1240. evalue = Con::getFormattedData(TypeString, evalue);
  1241. }
  1242. // If our data doesn't match then we get to update it
  1243. if (dStricmp(entryVal, evalue) != 0)
  1244. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, entryVal);
  1245. }
  1246. else
  1247. {
  1248. // No need to process a removed field that doesn't exist in the file
  1249. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1250. continue;
  1251. if( object->getCopySource() )
  1252. {
  1253. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1254. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1255. continue;
  1256. }
  1257. newLines.push_back(createNewProperty(entry->slotName, entry->value));
  1258. }
  1259. }
  1260. // If we have a parsedObject and the name changed
  1261. // then update the parsedObject to the new name.
  1262. // NOTE: an object 'can' have a NULL name which crashes in dStricmp.
  1263. if (parsedObject && parsedObject->name != StringTable->insert(object->getName(), true) )
  1264. {
  1265. StringTableEntry objectName = StringTable->insert(object->getName(), true);
  1266. if (parsedObject->name != objectName)
  1267. {
  1268. // Update the name in the file
  1269. updateToken(parsedObject->nameLine, parsedObject->namePosition, dStrlen(parsedObject->name), object->getName());
  1270. // Updated the parsedObject's name
  1271. parsedObject->name = objectName;
  1272. // Updated the object's "original" name to the one that is now in the file
  1273. object->setOriginalName(objectName);
  1274. }
  1275. }
  1276. if (parsedObject && newLines.size() > 0)
  1277. {
  1278. U32 lastPropLine = parsedObject->endLine;
  1279. if (parsedObject->properties.size() > 0)
  1280. lastPropLine = parsedObject->properties.last().valueLine + 1;
  1281. U32 currInsertLine = lastPropLine;
  1282. const char* indent = getObjectIndent(parsedObject);
  1283. // This should handle adding the opening { to an object
  1284. // that formerly did not have {};
  1285. if (!parsedObject->hasBraces)
  1286. {
  1287. updateToken(parsedObject->endLine, parsedObject->endPosition, 1, "\r\n{");
  1288. currInsertLine++;
  1289. }
  1290. currInsertLine += writeProperties(newLines, currInsertLine, indent);
  1291. // This should handle adding the opening } to an object
  1292. // that formerly did not have {};
  1293. if (!parsedObject->hasBraces)
  1294. {
  1295. U32 len = dStrlen(indent) + 3;
  1296. char* newLine = ( char* ) dMalloc( len );
  1297. dSprintf(newLine, len, "%s};", indent);
  1298. mLineBuffer.insert(currInsertLine, newLine);
  1299. currInsertLine++;
  1300. }
  1301. // Update the line offsets to account for the new lines
  1302. updateLineOffsets(lastPropLine, currInsertLine - lastPropLine);
  1303. }
  1304. else if (!parsedObject)
  1305. {
  1306. U32 insertLine = mLineBuffer.size();
  1307. if (!parentObject)
  1308. parentObject = findParentObject(object, parentObject);
  1309. if (parentObject && parentObject->endLine > -1)
  1310. insertLine = parentObject->endLine;
  1311. parsedObject = writeNewObject(object, newLines, insertLine, parentObject);
  1312. }
  1313. // Clean up the newLines memory
  1314. for (U32 i = 0; i < newLines.size(); i++)
  1315. {
  1316. if (newLines[i])
  1317. {
  1318. dFree(newLines[i]);
  1319. newLines[ i ] = NULL;
  1320. }
  1321. }
  1322. newLines.clear();
  1323. SimSet* set = dynamic_cast<SimSet*>(object);
  1324. if (set)
  1325. {
  1326. for(SimSet::iterator i = set->begin(); i != set->end(); i++)
  1327. {
  1328. SimObject* subObject = (SimObject*)(*i);
  1329. updateObject(subObject, parsedObject);
  1330. }
  1331. }
  1332. // Loop through the children of this parsedObject
  1333. // If they haven't been updated then assume that they
  1334. // don't exist in the file anymore
  1335. if (parsedObject)
  1336. {
  1337. for (S32 i = 0; i < mObjectBuffer.size(); i++)
  1338. {
  1339. ParsedObject* removeObj = mObjectBuffer[i];
  1340. if (removeObj->parentObject == parsedObject && !removeObj->updated)
  1341. {
  1342. removeParsedObject(removeObj);
  1343. mObjectBuffer.erase(i);
  1344. i--;
  1345. deleteObject(removeObj);
  1346. }
  1347. }
  1348. }
  1349. // Flag this as an updated object
  1350. if (parsedObject)
  1351. parsedObject->updated = true;
  1352. // Cleanup our created default object
  1353. delete defaultConObject;
  1354. }
  1355. bool PersistenceManager::saveDirtyFile()
  1356. {
  1357. FileStream stream;
  1358. stream.open( mCurrentFile, Torque::FS::File::Write );
  1359. if ( stream.getStatus() != Stream::Ok )
  1360. {
  1361. clearFileData();
  1362. return false;
  1363. }
  1364. for (U32 i = 0; i < mLineBuffer.size(); i++)
  1365. stream.writeLine((const U8*)mLineBuffer[i]);
  1366. stream.close();
  1367. //Con::printf("Successfully opened and wrote %s", mCurrentFile);
  1368. //Con::errorf("Updated Results:");
  1369. //for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1370. //{
  1371. // ParsedObject* parsedObject = mObjectBuffer[i];
  1372. // Con::warnf(" mObjectBuffer[%d]:", i);
  1373. // Con::warnf(" name = %s", parsedObject->name);
  1374. // Con::warnf(" className = %s", parsedObject->className);
  1375. // Con::warnf(" startLine = %d", parsedObject->startLine + 1);
  1376. // Con::warnf(" endLine = %d", parsedObject->endLine + 1);
  1377. // //if (mObjectBuffer[i]->properties.size() > 0)
  1378. // //{
  1379. // // Con::warnf(" properties:");
  1380. // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++)
  1381. // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name,
  1382. // // mObjectBuffer[i]->properties[j].value);
  1383. // //}
  1384. // if (!parsedObject->simObject.isNull())
  1385. // {
  1386. // SimObject* simObject = parsedObject->simObject;
  1387. // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId());
  1388. // Con::warnf(" declaration line = %d", simObject->getDeclarationLine());
  1389. // }
  1390. //}
  1391. // Clear our file data
  1392. clearFileData();
  1393. return true;
  1394. }
  1395. S32 QSORT_CALLBACK PersistenceManager::compareFiles(const void* a,const void* b)
  1396. {
  1397. DirtyObject* objectA = (DirtyObject*)(a);
  1398. DirtyObject* objectB = (DirtyObject*)(b);
  1399. if (objectA->isNull())
  1400. return -1;
  1401. else if (objectB->isNull())
  1402. return 1;
  1403. if (objectA->fileName == objectB->fileName)
  1404. return objectA->getObject()->getDeclarationLine() - objectB->getObject()->getDeclarationLine();
  1405. return dStricmp(objectA->fileName, objectB->fileName);
  1406. }
  1407. bool PersistenceManager::setDirty(SimObject* inObject, const char* inFileName)
  1408. {
  1409. // Check if the object is already in the dirty list...
  1410. DirtyObject *pDirty = findDirtyObject( inObject );
  1411. // The filename we will save this object to (later)..
  1412. String saveFile;
  1413. // Expand the script filename if we were passed one.
  1414. if ( inFileName )
  1415. {
  1416. char buffer[4096];
  1417. Con::expandScriptFilename( buffer, 4096, inFileName );
  1418. saveFile = buffer;
  1419. }
  1420. // If no filename was passed in, and the object was already dirty,
  1421. // we have nothing to do.
  1422. if ( saveFile.isEmpty() && pDirty )
  1423. return true;
  1424. // Otherwise default to the simObject's filename.
  1425. if ( saveFile.isEmpty() )
  1426. saveFile = inObject->getFilename();
  1427. // Error if still no filename.
  1428. if ( saveFile.isEmpty() )
  1429. {
  1430. if (inObject->getName())
  1431. Con::errorf("PersistenceManager::setDirty() - SimObject %s has no file name associated with it - can not save", inObject->getName());
  1432. else
  1433. Con::errorf("PersistenceManager::setDirty() - SimObject %d has no file name associated with it - can not save", inObject->getId());
  1434. return false;
  1435. }
  1436. // Update the DirtyObject's fileName if we have it
  1437. // else create a new one.
  1438. if ( pDirty )
  1439. pDirty->fileName = StringTable->insert( saveFile );
  1440. else
  1441. {
  1442. // Add the newly dirty object.
  1443. mDirtyObjects.increment();
  1444. mDirtyObjects.last().setObject( inObject );
  1445. mDirtyObjects.last().fileName = StringTable->insert( saveFile );
  1446. }
  1447. return true;
  1448. }
  1449. void PersistenceManager::removeDirty(SimObject* object)
  1450. {
  1451. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1452. {
  1453. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1454. if (dirtyObject.isNull())
  1455. continue;
  1456. if (dirtyObject.getObject() == object)
  1457. {
  1458. mDirtyObjects.erase(i);
  1459. break;
  1460. }
  1461. }
  1462. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1463. {
  1464. const RemoveField& field = mRemoveFields[i];
  1465. if (field.object != object)
  1466. continue;
  1467. mRemoveFields.erase(i);
  1468. if (i > 0)
  1469. i--;
  1470. }
  1471. }
  1472. void PersistenceManager::addRemoveField(SimObject* object, const char* fieldName)
  1473. {
  1474. // Check to see if this is an array variable
  1475. U32 arrayPos = 0;
  1476. const char* name = fieldName;
  1477. if (dStrlen(fieldName) > 3 && fieldName[dStrlen(fieldName) - 1] == ']')
  1478. {
  1479. // The last character is a ']' which *should* mean
  1480. // there is also a corresponding '['
  1481. const char* arrayPosStart = dStrrchr(fieldName, '[');
  1482. if (!arrayPosStart)
  1483. {
  1484. Con::errorf("PersistenceManager::addRemoveField() - error parsing array position - \
  1485. was expecting a '[' character");
  1486. }
  1487. else
  1488. {
  1489. // Parse the array position for the variable name
  1490. dSscanf(arrayPosStart, "[%d]", &arrayPos);
  1491. // Trim off the [<pos>] from the variable name
  1492. char* variableName = dStrdup(fieldName);
  1493. variableName[arrayPosStart - fieldName] = 0;
  1494. // Set the variable name to our new shortened name
  1495. name = StringTable->insert(variableName, true);
  1496. // Cleanup our variableName buffer
  1497. dFree( variableName );
  1498. }
  1499. }
  1500. // Make sure this field isn't already on the list
  1501. if (!findRemoveField(object, name, arrayPos))
  1502. {
  1503. mRemoveFields.increment();
  1504. RemoveField& field = mRemoveFields.last();
  1505. field.object = object;
  1506. field.fieldName = StringTable->insert(name);
  1507. field.arrayPos = arrayPos;
  1508. }
  1509. }
  1510. bool PersistenceManager::isDirty(SimObject* object)
  1511. {
  1512. return ( findDirtyObject( object ) != NULL );
  1513. }
  1514. PersistenceManager::DirtyObject* PersistenceManager::findDirtyObject(SimObject* object)
  1515. {
  1516. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1517. {
  1518. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1519. if (dirtyObject.isNull())
  1520. continue;
  1521. if (dirtyObject.getObject() == object)
  1522. return &mDirtyObjects[i];
  1523. }
  1524. return NULL;
  1525. }
  1526. bool PersistenceManager::findRemoveField(SimObject* object, const char* fieldName, U32 arrayPos)
  1527. {
  1528. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1529. {
  1530. if (mRemoveFields[i].object == object &&
  1531. mRemoveFields[i].arrayPos == arrayPos &&
  1532. dStricmp(mRemoveFields[i].fieldName, fieldName) == 0)
  1533. {
  1534. return true;
  1535. }
  1536. }
  1537. return false;
  1538. }
  1539. bool PersistenceManager::saveDirty()
  1540. {
  1541. // Remove any null SimObject's first
  1542. for (S32 i = 0; i < mDirtyObjects.size(); i++)
  1543. {
  1544. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1545. if (dirtyObject.isNull())
  1546. {
  1547. mDirtyObjects.erase(i);
  1548. i--;
  1549. }
  1550. }
  1551. // Sort by filename and declaration lines
  1552. dQsort(mDirtyObjects.address(), mDirtyObjects.size(), sizeof(DirtyList::value_type), compareFiles);
  1553. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1554. {
  1555. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1556. if (dirtyObject.isNull())
  1557. continue;
  1558. SimObject* object = dirtyObject.getObject();
  1559. if (!mCurrentFile || dStricmp(mCurrentFile, dirtyObject.fileName) != 0)
  1560. {
  1561. // If mCurrentFile is set then that means we
  1562. // changed file names so save our previous one
  1563. if (mCurrentFile)
  1564. saveDirtyFile();
  1565. // Open our new file and parse it
  1566. bool success = parseFile(dirtyObject.fileName);
  1567. if (!success)
  1568. {
  1569. const char *name = object->getName();
  1570. if (name)
  1571. {
  1572. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s %s (%d)",
  1573. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1574. }
  1575. else
  1576. {
  1577. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s (%d)",
  1578. dirtyObject.fileName, object->getClassName(), object->getId());
  1579. }
  1580. continue;
  1581. }
  1582. }
  1583. // Update this object's properties
  1584. //
  1585. // An empty script file (with 1 line) gets here with zero
  1586. // elements in the linebuffer, so this would prevent us from
  1587. // ever writing to it... Or is this test preventing bad things from
  1588. // happening if the file didn't exist at all?
  1589. //
  1590. if (mCurrentFile /*&& mLineBuffer.size() > 0*/)
  1591. updateObject(object);
  1592. }
  1593. // Save out our last file
  1594. if (mCurrentFile)
  1595. saveDirtyFile();
  1596. // Done writing out our dirty objects so reset everything
  1597. clearAll();
  1598. return true;
  1599. }
  1600. bool PersistenceManager::saveDirtyObject(SimObject* object)
  1601. {
  1602. // find our object passed in
  1603. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1604. {
  1605. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1606. if (dirtyObject.isNull())
  1607. continue;
  1608. if (dirtyObject.getObject() == object)
  1609. {
  1610. // Open our new file and parse it
  1611. bool success = parseFile(dirtyObject.fileName);
  1612. if (!success)
  1613. {
  1614. const char *name = object->getName();
  1615. if (name)
  1616. {
  1617. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s %s (%d)",
  1618. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1619. }
  1620. else
  1621. {
  1622. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s (%d)",
  1623. dirtyObject.fileName, object->getClassName(), object->getId());
  1624. }
  1625. return false;
  1626. }
  1627. // if the file exists then lets update and save
  1628. if(mCurrentFile)
  1629. {
  1630. updateObject(object);
  1631. saveDirtyFile();
  1632. }
  1633. break;
  1634. }
  1635. }
  1636. // remove this object from the dirty list
  1637. removeDirty(object);
  1638. return true;
  1639. }
  1640. void PersistenceManager::removeObjectFromFile(SimObject* object, const char* fileName)
  1641. {
  1642. if (mCurrentFile)
  1643. {
  1644. Con::errorf("PersistenceManager::removeObjectFromFile(): Can't remove an object from a \
  1645. file while another is currently opened");
  1646. return;
  1647. }
  1648. const char* file = object->getFilename();
  1649. if (fileName)
  1650. {
  1651. char buffer[1024];
  1652. Con::expandScriptFilename( buffer, 1024, fileName );
  1653. file = StringTable->insert(buffer);
  1654. }
  1655. bool success = false;
  1656. if ( file && file[ 0 ] )
  1657. success = parseFile(file);
  1658. if (!success)
  1659. {
  1660. const char *name = object->getName();
  1661. String errorNameStr;
  1662. if ( name )
  1663. errorNameStr = String::ToString( "%s %s (%d)", object->getClassName(), name, object->getId() );
  1664. else
  1665. errorNameStr = String::ToString( "%s (%d)", object->getClassName(), object->getId() );
  1666. if ( !file )
  1667. Con::errorf("PersistenceManager::removeObjectFromFile(): File was null trying to save %s", errorNameStr.c_str() );
  1668. else
  1669. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to open %s to save %s", file, errorNameStr.c_str() );
  1670. // Reset everything
  1671. clearAll();
  1672. return;
  1673. }
  1674. ParsedObject* parsedObject = findParsedObject(object);
  1675. if (!parsedObject)
  1676. {
  1677. const char *name = object->getName();
  1678. if (name)
  1679. {
  1680. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s %s (%d) in %s",
  1681. object->getClassName(), name, object->getId(), file);
  1682. }
  1683. else
  1684. {
  1685. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s (%d) in %s",
  1686. object->getClassName(), object->getId(), file);
  1687. }
  1688. // Reset everything
  1689. clearAll();
  1690. return;
  1691. }
  1692. removeParsedObject(parsedObject);
  1693. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1694. {
  1695. ParsedObject* removeObj = mObjectBuffer[i];
  1696. if (removeObj == parsedObject)
  1697. {
  1698. mObjectBuffer.erase(i);
  1699. break;
  1700. }
  1701. }
  1702. deleteObject(parsedObject);
  1703. // Save out the file
  1704. if (mCurrentFile)
  1705. saveDirtyFile();
  1706. // Reset everything
  1707. clearAll();
  1708. }
  1709. void PersistenceManager::deleteObjectsFromFile(const char* fileName)
  1710. {
  1711. if ( mCurrentFile )
  1712. {
  1713. Con::errorf( "PersistenceManager::deleteObjectsFromFile(): Cannot process while file while another is currently open." );
  1714. return;
  1715. }
  1716. // Expand Script File.
  1717. char buffer[1024];
  1718. Con::expandScriptFilename( buffer, 1024, fileName );
  1719. // Parse File.
  1720. if ( !parseFile( StringTable->insert(buffer) ) )
  1721. {
  1722. // Invalid.
  1723. return;
  1724. }
  1725. // Iterate over the objects.
  1726. for ( Vector<ParsedObject*>::iterator itr = mObjectBuffer.begin(); itr != mObjectBuffer.end(); itr++ )
  1727. {
  1728. SimObject *object;
  1729. if ( Sim::findObject( ( *itr )->name, object ) )
  1730. {
  1731. // Delete the Object.
  1732. object->deleteObject();
  1733. }
  1734. }
  1735. // Clear.
  1736. clearAll();
  1737. }
  1738. DefineEngineMethod( PersistenceManager, deleteObjectsFromFile, void, ( const char * fileName ), , "( fileName )"
  1739. "Delete all of the objects that are created from the given file." )
  1740. {
  1741. // Delete Objects.
  1742. object->deleteObjectsFromFile( fileName );
  1743. }
  1744. DefineEngineMethod( PersistenceManager, setDirty, void, ( const char * objName, const char * fileName ), (""), "(SimObject object, [filename])"
  1745. "Mark an existing SimObject as dirty (will be written out when saveDirty() is called).")
  1746. {
  1747. SimObject *dirtyObject = NULL;
  1748. if (String::compare(objName,"") != 0)
  1749. {
  1750. if (!Sim::findObject(objName, dirtyObject))
  1751. {
  1752. Con::printf("PersistenceManager::setDirty(): Invalid SimObject: %s", objName);
  1753. return;
  1754. }
  1755. }
  1756. // Prevent ourselves from shooting us in the foot.
  1757. if( dirtyObject == Sim::getRootGroup() )
  1758. {
  1759. Con::errorf( "PersistenceManager::setDirty(): Cannot save RootGroup" );
  1760. return;
  1761. }
  1762. if (dirtyObject)
  1763. {
  1764. if (String::compare( fileName,"")!=0)
  1765. object->setDirty(dirtyObject, fileName);
  1766. else
  1767. object->setDirty(dirtyObject);
  1768. }
  1769. }
  1770. DefineEngineMethod( PersistenceManager, removeDirty, void, ( const char * objName ), , "(SimObject object)"
  1771. "Remove a SimObject from the dirty list.")
  1772. {
  1773. SimObject *dirtyObject = NULL;
  1774. if (String::compare( objName,"")!=0)
  1775. {
  1776. if (!Sim::findObject(objName, dirtyObject))
  1777. {
  1778. Con::printf("PersistenceManager::removeDirty(): Invalid SimObject: %s", objName);
  1779. return;
  1780. }
  1781. }
  1782. if (dirtyObject)
  1783. object->removeDirty(dirtyObject);
  1784. }
  1785. DefineEngineMethod( PersistenceManager, isDirty, bool, ( const char * objName ), , "(SimObject object)"
  1786. "Returns true if the SimObject is on the dirty list.")
  1787. {
  1788. SimObject *dirtyObject = NULL;
  1789. if (String::compare ( objName,"")!=0)
  1790. {
  1791. if (!Sim::findObject(objName, dirtyObject))
  1792. {
  1793. Con::printf("PersistenceManager::isDirty(): Invalid SimObject: %s", objName);
  1794. return false;
  1795. }
  1796. }
  1797. if (dirtyObject)
  1798. return object->isDirty(dirtyObject);
  1799. return false;
  1800. }
  1801. DefineEngineMethod( PersistenceManager, hasDirty, bool, (), , "()"
  1802. "Returns true if the manager has dirty objects to save." )
  1803. {
  1804. return object->hasDirty();
  1805. }
  1806. DefineEngineMethod( PersistenceManager, getDirtyObjectCount, S32, (), , "()"
  1807. "Returns the number of dirty objects." )
  1808. {
  1809. return object->getDirtyList().size();
  1810. }
  1811. DefineEngineMethod( PersistenceManager, getDirtyObject, S32, (S32 index), , "( index )"
  1812. "Returns the ith dirty object." )
  1813. {
  1814. if ( index < 0 || index >= object->getDirtyList().size() )
  1815. {
  1816. Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", index );
  1817. return 0;
  1818. }
  1819. // Fetch Object.
  1820. const PersistenceManager::DirtyObject& dirtyObject = object->getDirtyList()[index];
  1821. // Return Id.
  1822. return ( dirtyObject.getObject() ) ? dirtyObject.getObject()->getId() : 0;
  1823. }
  1824. DefineEngineMethod( PersistenceManager, listDirty, void, (), , "()"
  1825. "Prints the dirty list to the console.")
  1826. {
  1827. const PersistenceManager::DirtyList dirtyList = object->getDirtyList();
  1828. for(U32 i = 0; i < dirtyList.size(); i++)
  1829. {
  1830. const PersistenceManager::DirtyObject& dirtyObject = dirtyList[i];
  1831. if (dirtyObject.isNull())
  1832. continue;
  1833. SimObject *obj = dirtyObject.getObject();
  1834. bool isSet = dynamic_cast<SimSet *>(obj) != 0;
  1835. const char *name = obj->getName();
  1836. if (name)
  1837. {
  1838. Con::printf(" %d,\"%s\": %s %s %s", obj->getId(), name,
  1839. obj->getClassName(), dirtyObject.fileName, isSet ? "(g)":"");
  1840. }
  1841. else
  1842. {
  1843. Con::printf(" %d: %s %s, %s", obj->getId(), obj->getClassName(),
  1844. dirtyObject.fileName, isSet ? "(g)" : "");
  1845. }
  1846. }
  1847. }
  1848. DefineEngineMethod( PersistenceManager, saveDirty, bool, (), , "()"
  1849. "Saves all of the SimObject's on the dirty list to their respective files.")
  1850. {
  1851. return object->saveDirty();
  1852. }
  1853. DefineEngineMethod( PersistenceManager, saveDirtyObject, bool, (const char * objName), , "(SimObject object)"
  1854. "Save a dirty SimObject to it's file.")
  1855. {
  1856. SimObject *dirtyObject = NULL;
  1857. if (String::compare ( objName, "")!=0)
  1858. {
  1859. if (!Sim::findObject(objName, dirtyObject))
  1860. {
  1861. Con::printf("%s(): Invalid SimObject: %s", object->getName(), objName);
  1862. return false;
  1863. }
  1864. }
  1865. if (dirtyObject)
  1866. return object->saveDirtyObject(dirtyObject);
  1867. return false;
  1868. }
  1869. DefineEngineMethod( PersistenceManager, clearAll, void, (), , "()"
  1870. "Clears all the tracked objects without saving them." )
  1871. {
  1872. object->clearAll();
  1873. }
  1874. DefineEngineMethod( PersistenceManager, removeObjectFromFile, void, (const char * objName, const char * filename),("") , "(SimObject object, [filename])"
  1875. "Remove an existing SimObject from a file (can optionally specify a different file than \
  1876. the one it was created in.")
  1877. {
  1878. SimObject *dirtyObject = NULL;
  1879. if (String::compare ( objName , "")!=0)
  1880. {
  1881. if (!Sim::findObject(objName, dirtyObject))
  1882. {
  1883. Con::printf("PersistenceManager::removeObjectFromFile(): Invalid SimObject: %s", objName);
  1884. return;
  1885. }
  1886. }
  1887. if (dirtyObject)
  1888. {
  1889. if (String::compare( filename,"")!=0)
  1890. object->removeObjectFromFile(dirtyObject, filename);
  1891. else
  1892. object->removeObjectFromFile(dirtyObject);
  1893. }
  1894. }
  1895. DefineEngineMethod( PersistenceManager, removeField, void, (const char * objName, const char * fieldName), , "(SimObject object, string fieldName)"
  1896. "Remove a specific field from an object declaration.")
  1897. {
  1898. SimObject *dirtyObject = NULL;
  1899. if (String::compare(objName,"")!=0)
  1900. {
  1901. if (!Sim::findObject(objName, dirtyObject))
  1902. {
  1903. Con::printf("PersistenceManager::removeField(): Invalid SimObject: %s", objName);
  1904. return;
  1905. }
  1906. }
  1907. if (dirtyObject)
  1908. {
  1909. if (String::compare(fieldName,"") != 0)
  1910. object->addRemoveField(dirtyObject, fieldName);
  1911. }
  1912. }