persistenceManager.cpp 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515
  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<String>& 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].c_str();
  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<String>& 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<String> 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) || f->flag.test(AbstractClassRep::FieldFlags::FIELD_DontWriteToFile))
  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. String value = object->getSpecialFieldOut(f->pFieldname, j);
  1091. // Make sure we got a value
  1092. if (value.isEmpty())
  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. continue;
  1104. }
  1105. // Run the parsed value through the console system conditioners so
  1106. // that it will better match the data we got back from the object.
  1107. String evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
  1108. // If our data doesn't match then we get to update it.
  1109. //
  1110. // As for copy-sources, we just assume here that if a property setting
  1111. // is there in the file, the user does not want it inherited from the copy-source
  1112. // even in the case the actual values are identical.
  1113. if (value != evalue)
  1114. {
  1115. if (value.isEmpty() &&
  1116. dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value.c_str()) == 0 &&
  1117. (!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value.c_str()) == 0))
  1118. {
  1119. removeField(prop);
  1120. }
  1121. else
  1122. {
  1123. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value.c_str(), true);
  1124. }
  1125. }
  1126. }
  1127. else
  1128. {
  1129. // No need to process a removed field that doesn't exist in the file
  1130. if (findRemoveField(object, f->pFieldname, j))
  1131. {
  1132. continue;
  1133. }
  1134. bool mustUpdate = false;
  1135. // If we didn't find the property in the ParsedObject
  1136. // then we need to compare against the default value
  1137. // for this property and save it out if it is different
  1138. String defaultValue = defaultObject->getSpecialFieldOut(f->pFieldname, j);
  1139. if (defaultValue.isEmpty() || value != defaultValue)
  1140. {
  1141. // Value differs. Check whether it also differs from the
  1142. // value in the copy source if there is one.
  1143. if (object->getCopySource())
  1144. {
  1145. String copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1146. if (copySourceValue.isEmpty() || copySourceValue != value)
  1147. mustUpdate = true;
  1148. }
  1149. else
  1150. mustUpdate = true;
  1151. }
  1152. else
  1153. {
  1154. // Value does not differ. If it differs from the copy source's value,
  1155. // though, we still want to write it out as otherwise we'll see the
  1156. // copy source's value override us.
  1157. if (object->getCopySource())
  1158. {
  1159. String copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1160. if (!copySourceValue.isEmpty() && copySourceValue != value)
  1161. mustUpdate = true;
  1162. }
  1163. }
  1164. // The default value for most string type fields is
  1165. // NULL so we can't just continue here or we'd never ever
  1166. // write them out...
  1167. //
  1168. //if (!defaultValue)
  1169. // continue;
  1170. // If the object's value is different from the default
  1171. // value then add it to the ParsedObject's newLines
  1172. if (mustUpdate)
  1173. {
  1174. newLines.push_back(value);
  1175. }
  1176. }
  1177. //dFree(value);
  1178. }
  1179. }
  1180. else
  1181. {
  1182. for (U32 j = 0; S32(j) < f->elementCount; j++)
  1183. {
  1184. String value = getFieldValue(object, f->pFieldname, j);
  1185. // Make sure we got a value
  1186. if (value.isEmpty())
  1187. continue;
  1188. // Let's see if this field is already in the file
  1189. S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j);
  1190. if (propertyIndex > -1)
  1191. {
  1192. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1193. // If this field is on the remove list then remove it and continue
  1194. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value.c_str()))
  1195. {
  1196. removeField(parsedObject->properties[propertyIndex]);
  1197. continue;
  1198. }
  1199. // Run the parsed value through the console system conditioners so
  1200. // that it will better match the data we got back from the object.
  1201. String evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
  1202. // If our data doesn't match then we get to update it.
  1203. //
  1204. // As for copy-sources, we just assume here that if a property setting
  1205. // is there in the file, the user does not want it inherited from the copy-source
  1206. // even in the case the actual values are identical.
  1207. if (value != evalue)
  1208. {
  1209. if (value.isEmpty() &&
  1210. dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value.c_str()) == 0 &&
  1211. (!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value.c_str()) == 0))
  1212. {
  1213. removeField(prop);
  1214. }
  1215. else
  1216. {
  1217. // TODO: This should be wrapped in a helper method... probably.
  1218. // Detect and collapse relative path information
  1219. if (f->type == TypeFilename ||
  1220. f->type == TypeStringFilename ||
  1221. f->type == TypeImageFilename ||
  1222. f->type == TypePrefabFilename ||
  1223. f->type == TypeShapeFilename ||
  1224. f->type == TypeSoundFilename)
  1225. {
  1226. char fnBuf[1024];
  1227. Con::collapseScriptFilename(fnBuf, 1024, value.c_str());
  1228. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
  1229. }
  1230. else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
  1231. {
  1232. char cmdBuf[1024];
  1233. expandEscape(cmdBuf, value.c_str());
  1234. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
  1235. }
  1236. else
  1237. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
  1238. }
  1239. }
  1240. }
  1241. else
  1242. {
  1243. // No need to process a removed field that doesn't exist in the file
  1244. if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value.c_str()))
  1245. {
  1246. continue;
  1247. }
  1248. bool mustUpdate = false;
  1249. // If we didn't find the property in the ParsedObject
  1250. // then we need to compare against the default value
  1251. // for this property and save it out if it is different
  1252. String defaultValue = getFieldValue(defaultObject, f->pFieldname, j);
  1253. if (defaultValue.isEmpty() || value != defaultValue)
  1254. {
  1255. // Value differs. Check whether it also differs from the
  1256. // value in the copy source if there is one.
  1257. if (object->getCopySource())
  1258. {
  1259. String copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1260. if (copySourceValue.isEmpty() || copySourceValue != value)
  1261. mustUpdate = true;
  1262. }
  1263. else
  1264. mustUpdate = true;
  1265. }
  1266. else
  1267. {
  1268. // Value does not differ. If it differs from the copy source's value,
  1269. // though, we still want to write it out as otherwise we'll see the
  1270. // copy source's value override us.
  1271. if (object->getCopySource())
  1272. {
  1273. String copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
  1274. if (!copySourceValue.isEmpty() && copySourceValue != value)
  1275. mustUpdate = true;
  1276. }
  1277. }
  1278. // The default value for most string type fields is
  1279. // NULL so we can't just continue here or we'd never ever
  1280. // write them out...
  1281. //
  1282. //if (!defaultValue)
  1283. // continue;
  1284. // If the object's value is different from the default
  1285. // value then add it to the ParsedObject's newLines
  1286. if (mustUpdate)
  1287. {
  1288. // TODO: This should be wrapped in a helper method... probably.
  1289. // Detect and collapse relative path information
  1290. if (f->type == TypeFilename ||
  1291. f->type == TypeStringFilename ||
  1292. f->type == TypeImageFilename ||
  1293. f->type == TypePrefabFilename ||
  1294. f->type == TypeShapeFilename ||
  1295. f->type == TypeSoundFilename)
  1296. {
  1297. char fnBuf[1024];
  1298. Con::collapseScriptFilename(fnBuf, 1024, value.c_str());
  1299. newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
  1300. }
  1301. else if (f->type == TypeCommand)
  1302. {
  1303. char cmdBuf[1024];
  1304. expandEscape(cmdBuf, value.c_str());
  1305. newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
  1306. }
  1307. else
  1308. newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
  1309. }
  1310. }
  1311. }
  1312. }
  1313. }
  1314. // Handle dynamic fields
  1315. SimFieldDictionary* fieldDict = object->getFieldDictionary();
  1316. for(SimFieldDictionaryIterator itr(fieldDict); *itr; ++itr)
  1317. {
  1318. SimFieldDictionary::Entry * entry = (*itr);
  1319. if( !entry->value )
  1320. continue;
  1321. // Let's see if this field is already in the file
  1322. S32 propertyIndex = getPropertyIndex(parsedObject, entry->slotName);
  1323. if (propertyIndex > -1)
  1324. {
  1325. ParsedProperty& prop = parsedObject->properties[propertyIndex];
  1326. // If this field is on the remove list then remove it and continue
  1327. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1328. {
  1329. removeField( parsedObject->properties[ propertyIndex ] );
  1330. continue;
  1331. }
  1332. if( object->getCopySource() )
  1333. {
  1334. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1335. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1336. {
  1337. removeField( prop );
  1338. continue;
  1339. }
  1340. }
  1341. const char* evalue = prop.value;
  1342. const char *entryVal = entry->value;
  1343. if ( entryVal[0] == StringTagPrefixByte )
  1344. entryVal = gNetStringTable->lookupString( dAtoi( entryVal+1 ) );
  1345. else
  1346. {
  1347. // Run the parsed value through the console system conditioners so
  1348. // that it will better match the data we got back from the object.
  1349. evalue = Con::getFormattedData(TypeString, evalue);
  1350. }
  1351. // If our data doesn't match then we get to update it
  1352. if (dStricmp(entryVal, evalue) != 0)
  1353. updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, entryVal);
  1354. }
  1355. else
  1356. {
  1357. // No need to process a removed field that doesn't exist in the file
  1358. if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value))
  1359. continue;
  1360. if( object->getCopySource() )
  1361. {
  1362. const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL );
  1363. if( String::compare( copySourceFieldValue, entry->value ) == 0 )
  1364. continue;
  1365. }
  1366. newLines.push_back(createNewProperty(entry->slotName, entry->value));
  1367. }
  1368. }
  1369. // If we have a parsedObject and the name changed
  1370. // then update the parsedObject to the new name.
  1371. // NOTE: an object 'can' have a NULL name which crashes in dStricmp.
  1372. if (parsedObject && parsedObject->name != StringTable->insert(object->getName(), true) )
  1373. {
  1374. StringTableEntry objectName = StringTable->insert(object->getName(), true);
  1375. if (parsedObject->name != objectName)
  1376. {
  1377. // Update the name in the file
  1378. updateToken(parsedObject->nameLine, parsedObject->namePosition, dStrlen(parsedObject->name), object->getName());
  1379. // Updated the parsedObject's name
  1380. parsedObject->name = objectName;
  1381. // Updated the object's "original" name to the one that is now in the file
  1382. object->setOriginalName(objectName);
  1383. }
  1384. }
  1385. if (parsedObject && newLines.size() > 0)
  1386. {
  1387. U32 lastPropLine = parsedObject->endLine;
  1388. if (parsedObject->properties.size() > 0)
  1389. lastPropLine = parsedObject->properties.last().valueLine + 1;
  1390. U32 currInsertLine = lastPropLine;
  1391. const char* indent = getObjectIndent(parsedObject);
  1392. // This should handle adding the opening { to an object
  1393. // that formerly did not have {};
  1394. if (!parsedObject->hasBraces)
  1395. {
  1396. updateToken(parsedObject->endLine, parsedObject->endPosition, 1, "\r\n{");
  1397. currInsertLine++;
  1398. }
  1399. currInsertLine += writeProperties(newLines, currInsertLine, indent);
  1400. // This should handle adding the opening } to an object
  1401. // that formerly did not have {};
  1402. if (!parsedObject->hasBraces)
  1403. {
  1404. U32 len = dStrlen(indent) + 3;
  1405. char* newLine = ( char* ) dMalloc( len );
  1406. dSprintf(newLine, len, "%s};", indent);
  1407. mLineBuffer.insert(currInsertLine, newLine);
  1408. currInsertLine++;
  1409. }
  1410. // Update the line offsets to account for the new lines
  1411. updateLineOffsets(lastPropLine, currInsertLine - lastPropLine);
  1412. }
  1413. else if (!parsedObject)
  1414. {
  1415. U32 insertLine = mLineBuffer.size();
  1416. if (!parentObject)
  1417. parentObject = findParentObject(object, parentObject);
  1418. if (parentObject && parentObject->endLine > -1)
  1419. insertLine = parentObject->endLine;
  1420. parsedObject = writeNewObject(object, newLines, insertLine, parentObject);
  1421. }
  1422. // Clean up the newLines memory
  1423. newLines.clear();
  1424. SimSet* set = dynamic_cast<SimSet*>(object);
  1425. if (set)
  1426. {
  1427. for(SimSet::iterator i = set->begin(); i != set->end(); i++)
  1428. {
  1429. SimObject* subObject = (SimObject*)(*i);
  1430. updateObject(subObject, parsedObject);
  1431. }
  1432. }
  1433. // Loop through the children of this parsedObject
  1434. // If they haven't been updated then assume that they
  1435. // don't exist in the file anymore
  1436. if (parsedObject)
  1437. {
  1438. for (S32 i = 0; i < mObjectBuffer.size(); i++)
  1439. {
  1440. ParsedObject* removeObj = mObjectBuffer[i];
  1441. if (removeObj->parentObject == parsedObject && !removeObj->updated)
  1442. {
  1443. removeParsedObject(removeObj);
  1444. mObjectBuffer.erase(i);
  1445. i--;
  1446. deleteObject(removeObj);
  1447. }
  1448. }
  1449. }
  1450. // Flag this as an updated object
  1451. if (parsedObject)
  1452. parsedObject->updated = true;
  1453. // Cleanup our created default object
  1454. delete defaultConObject;
  1455. }
  1456. bool PersistenceManager::saveDirtyFile()
  1457. {
  1458. FileStream stream;
  1459. stream.open( mCurrentFile, Torque::FS::File::Write );
  1460. if ( stream.getStatus() != Stream::Ok )
  1461. {
  1462. clearFileData();
  1463. return false;
  1464. }
  1465. for (U32 i = 0; i < mLineBuffer.size(); i++)
  1466. stream.writeLine((const U8*)mLineBuffer[i]);
  1467. stream.close();
  1468. //Con::printf("Successfully opened and wrote %s", mCurrentFile);
  1469. //Con::errorf("Updated Results:");
  1470. //for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1471. //{
  1472. // ParsedObject* parsedObject = mObjectBuffer[i];
  1473. // Con::warnf(" mObjectBuffer[%d]:", i);
  1474. // Con::warnf(" name = %s", parsedObject->name);
  1475. // Con::warnf(" className = %s", parsedObject->className);
  1476. // Con::warnf(" startLine = %d", parsedObject->startLine + 1);
  1477. // Con::warnf(" endLine = %d", parsedObject->endLine + 1);
  1478. // //if (mObjectBuffer[i]->properties.size() > 0)
  1479. // //{
  1480. // // Con::warnf(" properties:");
  1481. // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++)
  1482. // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name,
  1483. // // mObjectBuffer[i]->properties[j].value);
  1484. // //}
  1485. // if (!parsedObject->simObject.isNull())
  1486. // {
  1487. // SimObject* simObject = parsedObject->simObject;
  1488. // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId());
  1489. // Con::warnf(" declaration line = %d", simObject->getDeclarationLine());
  1490. // }
  1491. //}
  1492. // Clear our file data
  1493. clearFileData();
  1494. return true;
  1495. }
  1496. S32 QSORT_CALLBACK PersistenceManager::compareFiles(const void* a,const void* b)
  1497. {
  1498. DirtyObject* objectA = (DirtyObject*)(a);
  1499. DirtyObject* objectB = (DirtyObject*)(b);
  1500. if (objectA->isNull())
  1501. return -1;
  1502. else if (objectB->isNull())
  1503. return 1;
  1504. if (objectA->fileName == objectB->fileName)
  1505. return objectA->getObject()->getDeclarationLine() - objectB->getObject()->getDeclarationLine();
  1506. return dStricmp(objectA->fileName, objectB->fileName);
  1507. }
  1508. bool PersistenceManager::setDirty(SimObject* inObject, const char* inFileName)
  1509. {
  1510. // Check if the object is already in the dirty list...
  1511. DirtyObject *pDirty = findDirtyObject( inObject );
  1512. // The filename we will save this object to (later)..
  1513. String saveFile;
  1514. // Expand the script filename if we were passed one.
  1515. if ( inFileName )
  1516. {
  1517. char buffer[4096];
  1518. Con::expandScriptFilename( buffer, 4096, inFileName );
  1519. saveFile = buffer;
  1520. }
  1521. // If no filename was passed in, and the object was already dirty,
  1522. // we have nothing to do.
  1523. if ( saveFile.isEmpty() && pDirty )
  1524. return true;
  1525. // Otherwise default to the simObject's filename.
  1526. if ( saveFile.isEmpty() )
  1527. saveFile = inObject->getFilename();
  1528. // Error if still no filename.
  1529. if ( saveFile.isEmpty() )
  1530. {
  1531. if (inObject->getName())
  1532. Con::errorf("PersistenceManager::setDirty() - SimObject %s has no file name associated with it - can not save", inObject->getName());
  1533. else
  1534. Con::errorf("PersistenceManager::setDirty() - SimObject %d has no file name associated with it - can not save", inObject->getId());
  1535. return false;
  1536. }
  1537. // Update the DirtyObject's fileName if we have it
  1538. // else create a new one.
  1539. if ( pDirty )
  1540. pDirty->fileName = StringTable->insert( saveFile );
  1541. else
  1542. {
  1543. // Add the newly dirty object.
  1544. mDirtyObjects.increment();
  1545. mDirtyObjects.last().setObject( inObject );
  1546. mDirtyObjects.last().fileName = StringTable->insert( saveFile );
  1547. }
  1548. return true;
  1549. }
  1550. void PersistenceManager::removeDirty(SimObject* object)
  1551. {
  1552. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1553. {
  1554. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1555. if (dirtyObject.isNull())
  1556. continue;
  1557. if (dirtyObject.getObject() == object)
  1558. {
  1559. mDirtyObjects.erase(i);
  1560. break;
  1561. }
  1562. }
  1563. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1564. {
  1565. const RemoveField& field = mRemoveFields[i];
  1566. if (field.object != object)
  1567. continue;
  1568. mRemoveFields.erase(i);
  1569. if (i > 0)
  1570. i--;
  1571. }
  1572. }
  1573. void PersistenceManager::addRemoveField(SimObject* object, const char* fieldName)
  1574. {
  1575. // Check to see if this is an array variable
  1576. U32 arrayPos = 0;
  1577. const char* name = fieldName;
  1578. if (dStrlen(fieldName) > 3 && fieldName[dStrlen(fieldName) - 1] == ']')
  1579. {
  1580. // The last character is a ']' which *should* mean
  1581. // there is also a corresponding '['
  1582. const char* arrayPosStart = dStrrchr(fieldName, '[');
  1583. if (!arrayPosStart)
  1584. {
  1585. Con::errorf("PersistenceManager::addRemoveField() - error parsing array position - \
  1586. was expecting a '[' character");
  1587. }
  1588. else
  1589. {
  1590. // Parse the array position for the variable name
  1591. dSscanf(arrayPosStart, "[%d]", &arrayPos);
  1592. // Trim off the [<pos>] from the variable name
  1593. char* variableName = dStrdup(fieldName);
  1594. variableName[arrayPosStart - fieldName] = 0;
  1595. // Set the variable name to our new shortened name
  1596. name = StringTable->insert(variableName, true);
  1597. // Cleanup our variableName buffer
  1598. dFree( variableName );
  1599. }
  1600. }
  1601. // Make sure this field isn't already on the list
  1602. if (!findRemoveField(object, name, arrayPos))
  1603. {
  1604. mRemoveFields.increment();
  1605. RemoveField& field = mRemoveFields.last();
  1606. field.object = object;
  1607. field.fieldName = StringTable->insert(name);
  1608. field.arrayPos = arrayPos;
  1609. }
  1610. }
  1611. bool PersistenceManager::isDirty(SimObject* object)
  1612. {
  1613. return ( findDirtyObject( object ) != NULL );
  1614. }
  1615. PersistenceManager::DirtyObject* PersistenceManager::findDirtyObject(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. return &mDirtyObjects[i];
  1624. }
  1625. return NULL;
  1626. }
  1627. bool PersistenceManager::findRemoveField(SimObject* object, const char* fieldName, U32 arrayPos)
  1628. {
  1629. for (U32 i = 0; i < mRemoveFields.size(); i++)
  1630. {
  1631. if (mRemoveFields[i].object == object &&
  1632. mRemoveFields[i].arrayPos == arrayPos &&
  1633. dStricmp(mRemoveFields[i].fieldName, fieldName) == 0)
  1634. {
  1635. return true;
  1636. }
  1637. }
  1638. return false;
  1639. }
  1640. bool PersistenceManager::saveDirty()
  1641. {
  1642. // Remove any null SimObject's first
  1643. for (S32 i = 0; i < mDirtyObjects.size(); i++)
  1644. {
  1645. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1646. if (dirtyObject.isNull())
  1647. {
  1648. mDirtyObjects.erase(i);
  1649. i--;
  1650. }
  1651. }
  1652. // Sort by filename and declaration lines
  1653. dQsort(mDirtyObjects.address(), mDirtyObjects.size(), sizeof(DirtyList::value_type), compareFiles);
  1654. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1655. {
  1656. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1657. if (dirtyObject.isNull())
  1658. continue;
  1659. SimObject* object = dirtyObject.getObject();
  1660. if (!mCurrentFile || dStricmp(mCurrentFile, dirtyObject.fileName) != 0)
  1661. {
  1662. // If mCurrentFile is set then that means we
  1663. // changed file names so save our previous one
  1664. if (mCurrentFile)
  1665. saveDirtyFile();
  1666. // Open our new file and parse it
  1667. bool success = parseFile(dirtyObject.fileName);
  1668. if (!success)
  1669. {
  1670. const char *name = object->getName();
  1671. if (name)
  1672. {
  1673. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s %s (%d)",
  1674. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1675. }
  1676. else
  1677. {
  1678. Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s (%d)",
  1679. dirtyObject.fileName, object->getClassName(), object->getId());
  1680. }
  1681. continue;
  1682. }
  1683. }
  1684. // Update this object's properties
  1685. //
  1686. // An empty script file (with 1 line) gets here with zero
  1687. // elements in the linebuffer, so this would prevent us from
  1688. // ever writing to it... Or is this test preventing bad things from
  1689. // happening if the file didn't exist at all?
  1690. //
  1691. if (mCurrentFile /*&& mLineBuffer.size() > 0*/)
  1692. updateObject(object);
  1693. }
  1694. // Save out our last file
  1695. if (mCurrentFile)
  1696. saveDirtyFile();
  1697. // Done writing out our dirty objects so reset everything
  1698. clearAll();
  1699. return true;
  1700. }
  1701. bool PersistenceManager::saveDirtyObject(SimObject* object)
  1702. {
  1703. // find our object passed in
  1704. for (U32 i = 0; i < mDirtyObjects.size(); i++)
  1705. {
  1706. const DirtyObject& dirtyObject = mDirtyObjects[i];
  1707. if (dirtyObject.isNull())
  1708. continue;
  1709. if (dirtyObject.getObject() == object)
  1710. {
  1711. // Open our new file and parse it
  1712. bool success = parseFile(dirtyObject.fileName);
  1713. if (!success)
  1714. {
  1715. const char *name = object->getName();
  1716. if (name)
  1717. {
  1718. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s %s (%d)",
  1719. dirtyObject.fileName, object->getClassName(), name, object->getId());
  1720. }
  1721. else
  1722. {
  1723. Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s (%d)",
  1724. dirtyObject.fileName, object->getClassName(), object->getId());
  1725. }
  1726. return false;
  1727. }
  1728. // if the file exists then lets update and save
  1729. if(mCurrentFile)
  1730. {
  1731. updateObject(object);
  1732. saveDirtyFile();
  1733. }
  1734. break;
  1735. }
  1736. }
  1737. // remove this object from the dirty list
  1738. removeDirty(object);
  1739. return true;
  1740. }
  1741. void PersistenceManager::removeObjectFromFile(SimObject* object, const char* fileName)
  1742. {
  1743. if (mCurrentFile)
  1744. {
  1745. Con::errorf("PersistenceManager::removeObjectFromFile(): Can't remove an object from a \
  1746. file while another is currently opened");
  1747. return;
  1748. }
  1749. const char* file = object->getFilename();
  1750. if (fileName)
  1751. {
  1752. char buffer[1024];
  1753. Con::expandScriptFilename( buffer, 1024, fileName );
  1754. file = StringTable->insert(buffer);
  1755. }
  1756. bool success = false;
  1757. if ( file && file[ 0 ] )
  1758. success = parseFile(file);
  1759. if (!success)
  1760. {
  1761. const char *name = object->getName();
  1762. String errorNameStr;
  1763. if ( name )
  1764. errorNameStr = String::ToString( "%s %s (%d)", object->getClassName(), name, object->getId() );
  1765. else
  1766. errorNameStr = String::ToString( "%s (%d)", object->getClassName(), object->getId() );
  1767. if ( !file )
  1768. Con::errorf("PersistenceManager::removeObjectFromFile(): File was null trying to save %s", errorNameStr.c_str() );
  1769. else
  1770. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to open %s to save %s", file, errorNameStr.c_str() );
  1771. // Reset everything
  1772. clearAll();
  1773. return;
  1774. }
  1775. ParsedObject* parsedObject = findParsedObject(object);
  1776. if (!parsedObject)
  1777. {
  1778. const char *name = object->getName();
  1779. if (name)
  1780. {
  1781. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s %s (%d) in %s",
  1782. object->getClassName(), name, object->getId(), file);
  1783. }
  1784. else
  1785. {
  1786. Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s (%d) in %s",
  1787. object->getClassName(), object->getId(), file);
  1788. }
  1789. // Reset everything
  1790. clearAll();
  1791. return;
  1792. }
  1793. removeParsedObject(parsedObject);
  1794. for (U32 i = 0; i < mObjectBuffer.size(); i++)
  1795. {
  1796. ParsedObject* removeObj = mObjectBuffer[i];
  1797. if (removeObj == parsedObject)
  1798. {
  1799. mObjectBuffer.erase(i);
  1800. break;
  1801. }
  1802. }
  1803. deleteObject(parsedObject);
  1804. // Save out the file
  1805. if (mCurrentFile)
  1806. saveDirtyFile();
  1807. // Reset everything
  1808. clearAll();
  1809. }
  1810. void PersistenceManager::deleteObjectsFromFile(const char* fileName)
  1811. {
  1812. if ( mCurrentFile )
  1813. {
  1814. Con::errorf( "PersistenceManager::deleteObjectsFromFile(): Cannot process while file while another is currently open." );
  1815. return;
  1816. }
  1817. // Expand Script File.
  1818. char buffer[1024];
  1819. Con::expandScriptFilename( buffer, 1024, fileName );
  1820. // Parse File.
  1821. if ( !parseFile( StringTable->insert(buffer) ) )
  1822. {
  1823. // Invalid.
  1824. return;
  1825. }
  1826. // Iterate over the objects.
  1827. for ( Vector<ParsedObject*>::iterator itr = mObjectBuffer.begin(); itr != mObjectBuffer.end(); itr++ )
  1828. {
  1829. SimObject *object;
  1830. if ( Sim::findObject( ( *itr )->name, object ) )
  1831. {
  1832. // Delete the Object.
  1833. object->deleteObject();
  1834. }
  1835. }
  1836. // Clear.
  1837. clearAll();
  1838. }
  1839. DefineEngineMethod( PersistenceManager, deleteObjectsFromFile, void, ( const char * fileName ), , "( fileName )"
  1840. "Delete all of the objects that are created from the given file." )
  1841. {
  1842. // Delete Objects.
  1843. object->deleteObjectsFromFile( fileName );
  1844. }
  1845. DefineEngineMethod( PersistenceManager, setDirty, void, ( const char * objName, const char * fileName ), (""), "(SimObject object, [filename])"
  1846. "Mark an existing SimObject as dirty (will be written out when saveDirty() is called).")
  1847. {
  1848. SimObject *dirtyObject = NULL;
  1849. if (String::compare(objName,"") != 0)
  1850. {
  1851. if (!Sim::findObject(objName, dirtyObject))
  1852. {
  1853. Con::printf("PersistenceManager::setDirty(): Invalid SimObject: %s", objName);
  1854. return;
  1855. }
  1856. }
  1857. // Prevent ourselves from shooting us in the foot.
  1858. if( dirtyObject == Sim::getRootGroup() )
  1859. {
  1860. Con::errorf( "PersistenceManager::setDirty(): Cannot save RootGroup" );
  1861. return;
  1862. }
  1863. if (dirtyObject)
  1864. {
  1865. if (String::compare( fileName,"")!=0)
  1866. object->setDirty(dirtyObject, fileName);
  1867. else
  1868. object->setDirty(dirtyObject);
  1869. }
  1870. }
  1871. DefineEngineMethod( PersistenceManager, removeDirty, void, ( const char * objName ), , "(SimObject object)"
  1872. "Remove a SimObject from the dirty list.")
  1873. {
  1874. SimObject *dirtyObject = NULL;
  1875. if (String::compare( objName,"")!=0)
  1876. {
  1877. if (!Sim::findObject(objName, dirtyObject))
  1878. {
  1879. Con::printf("PersistenceManager::removeDirty(): Invalid SimObject: %s", objName);
  1880. return;
  1881. }
  1882. }
  1883. if (dirtyObject)
  1884. object->removeDirty(dirtyObject);
  1885. }
  1886. DefineEngineMethod( PersistenceManager, isDirty, bool, ( const char * objName ), , "(SimObject object)"
  1887. "Returns true if the SimObject is on the dirty list.")
  1888. {
  1889. SimObject *dirtyObject = NULL;
  1890. if (String::compare ( objName,"")!=0)
  1891. {
  1892. if (!Sim::findObject(objName, dirtyObject))
  1893. {
  1894. Con::printf("PersistenceManager::isDirty(): Invalid SimObject: %s", objName);
  1895. return false;
  1896. }
  1897. }
  1898. if (dirtyObject)
  1899. return object->isDirty(dirtyObject);
  1900. return false;
  1901. }
  1902. DefineEngineMethod( PersistenceManager, hasDirty, bool, (), , "()"
  1903. "Returns true if the manager has dirty objects to save." )
  1904. {
  1905. return object->hasDirty();
  1906. }
  1907. DefineEngineMethod( PersistenceManager, getDirtyObjectCount, S32, (), , "()"
  1908. "Returns the number of dirty objects." )
  1909. {
  1910. return object->getDirtyList().size();
  1911. }
  1912. DefineEngineMethod( PersistenceManager, getDirtyObject, S32, (S32 index), , "( index )"
  1913. "Returns the ith dirty object." )
  1914. {
  1915. if ( index < 0 || index >= object->getDirtyList().size() )
  1916. {
  1917. Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", index );
  1918. return 0;
  1919. }
  1920. // Fetch Object.
  1921. const PersistenceManager::DirtyObject& dirtyObject = object->getDirtyList()[index];
  1922. // Return Id.
  1923. return ( dirtyObject.getObject() ) ? dirtyObject.getObject()->getId() : 0;
  1924. }
  1925. DefineEngineMethod( PersistenceManager, listDirty, void, (), , "()"
  1926. "Prints the dirty list to the console.")
  1927. {
  1928. const PersistenceManager::DirtyList dirtyList = object->getDirtyList();
  1929. for(U32 i = 0; i < dirtyList.size(); i++)
  1930. {
  1931. const PersistenceManager::DirtyObject& dirtyObject = dirtyList[i];
  1932. if (dirtyObject.isNull())
  1933. continue;
  1934. SimObject *obj = dirtyObject.getObject();
  1935. bool isSet = dynamic_cast<SimSet *>(obj) != 0;
  1936. const char *name = obj->getName();
  1937. if (name)
  1938. {
  1939. Con::printf(" %d,\"%s\": %s %s %s", obj->getId(), name,
  1940. obj->getClassName(), dirtyObject.fileName, isSet ? "(g)":"");
  1941. }
  1942. else
  1943. {
  1944. Con::printf(" %d: %s %s, %s", obj->getId(), obj->getClassName(),
  1945. dirtyObject.fileName, isSet ? "(g)" : "");
  1946. }
  1947. }
  1948. }
  1949. DefineEngineMethod( PersistenceManager, saveDirty, bool, (), , "()"
  1950. "Saves all of the SimObject's on the dirty list to their respective files.")
  1951. {
  1952. return object->saveDirty();
  1953. }
  1954. DefineEngineMethod( PersistenceManager, saveDirtyObject, bool, (const char * objName), , "(SimObject object)"
  1955. "Save a dirty SimObject to it's file.")
  1956. {
  1957. SimObject *dirtyObject = NULL;
  1958. if (String::compare ( objName, "")!=0)
  1959. {
  1960. if (!Sim::findObject(objName, dirtyObject))
  1961. {
  1962. Con::printf("%s(): Invalid SimObject: %s", object->getName(), objName);
  1963. return false;
  1964. }
  1965. }
  1966. if (dirtyObject)
  1967. return object->saveDirtyObject(dirtyObject);
  1968. return false;
  1969. }
  1970. DefineEngineMethod( PersistenceManager, clearAll, void, (), , "()"
  1971. "Clears all the tracked objects without saving them." )
  1972. {
  1973. object->clearAll();
  1974. }
  1975. DefineEngineMethod( PersistenceManager, removeObjectFromFile, void, (const char * objName, const char * filename),("") , "(SimObject object, [filename])"
  1976. "Remove an existing SimObject from a file (can optionally specify a different file than \
  1977. the one it was created in.")
  1978. {
  1979. SimObject *dirtyObject = NULL;
  1980. if (String::compare ( objName , "")!=0)
  1981. {
  1982. if (!Sim::findObject(objName, dirtyObject))
  1983. {
  1984. Con::printf("PersistenceManager::removeObjectFromFile(): Invalid SimObject: %s", objName);
  1985. return;
  1986. }
  1987. }
  1988. if (dirtyObject)
  1989. {
  1990. if (String::compare( filename,"")!=0)
  1991. object->removeObjectFromFile(dirtyObject, filename);
  1992. else
  1993. object->removeObjectFromFile(dirtyObject);
  1994. }
  1995. }
  1996. DefineEngineMethod( PersistenceManager, removeField, void, (const char * objName, const char * fieldName), , "(SimObject object, string fieldName)"
  1997. "Remove a specific field from an object declaration.")
  1998. {
  1999. SimObject *dirtyObject = NULL;
  2000. if (String::compare(objName,"")!=0)
  2001. {
  2002. if (!Sim::findObject(objName, dirtyObject))
  2003. {
  2004. Con::printf("PersistenceManager::removeField(): Invalid SimObject: %s", objName);
  2005. return;
  2006. }
  2007. }
  2008. if (dirtyObject)
  2009. {
  2010. if (String::compare(fieldName,"") != 0)
  2011. object->addRemoveField(dirtyObject, fieldName);
  2012. }
  2013. }