INI.cpp 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: INI.cpp //////////////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, November 2001
  25. // Desc: INI Reader
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_DAMAGE_NAMES
  30. #define DEFINE_DEATH_NAMES
  31. #include "Common/INI.h"
  32. #include "Common/INIException.h"
  33. #include "Common/DamageFX.h"
  34. #include "Common/File.h"
  35. #include "Common/FileSystem.h"
  36. #include "Common/GameAudio.h"
  37. #include "Common/Science.h"
  38. #include "Common/SpecialPower.h"
  39. #include "Common/ThingFactory.h"
  40. #include "Common/ThingTemplate.h"
  41. #include "Common/Upgrade.h"
  42. #include "Common/Xfer.h"
  43. #include "Common/XferCRC.h"
  44. #include "GameClient/Anim2D.h"
  45. #include "GameClient/Color.h"
  46. #include "GameClient/FXList.h"
  47. #include "GameClient/GameText.h"
  48. #include "GameClient/Image.h"
  49. #include "GameClient/ParticleSys.h"
  50. #include "GameLogic/Armor.h"
  51. #include "GameLogic/ExperienceTracker.h"
  52. #include "GameLogic/FPUControl.h"
  53. #include "GameLogic/ObjectCreationList.h"
  54. #include "GameLogic/Weapon.h"
  55. #ifdef _INTERNAL
  56. // for occasional debugging...
  57. //#pragma optimize("", off)
  58. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  59. #endif
  60. ///////////////////////////////////////////////////////////////////////////////////////////////////
  61. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  62. ///////////////////////////////////////////////////////////////////////////////////////////////////
  63. static Xfer *s_xfer = NULL;
  64. //-------------------------------------------------------------------------------------------------
  65. /** This is the table of data types we can have in INI files. To add a new data type
  66. * block make a new entry in this table and add an appropriate parsing function */
  67. //-------------------------------------------------------------------------------------------------
  68. extern void parseReallyLowMHz( INI* ini); // yeah, so sue me (srj)
  69. struct BlockParse
  70. {
  71. const char *token;
  72. INIBlockParse parse;
  73. };
  74. static const BlockParse theTypeTable[] =
  75. {
  76. { "AIData", INI::parseAIDataDefinition },
  77. { "Animation", INI::parseAnim2DDefinition },
  78. { "Armor", INI::parseArmorDefinition },
  79. { "AudioEvent", INI::parseAudioEventDefinition },
  80. { "AudioSettings", INI::parseAudioSettingsDefinition },
  81. { "Bridge", INI::parseTerrainBridgeDefinition },
  82. { "Campaign", INI::parseCampaignDefinition },
  83. { "CommandButton", INI::parseCommandButtonDefinition },
  84. { "CommandMap", INI::parseMetaMapDefinition },
  85. { "CommandSet", INI::parseCommandSetDefinition },
  86. { "ControlBarScheme", INI::parseControlBarSchemeDefinition },
  87. { "ControlBarResizer", INI::parseControlBarResizerDefinition },
  88. { "CrateData", INI::parseCrateTemplateDefinition },
  89. { "Credits", INI::parseCredits},
  90. { "WindowTransition", INI::parseWindowTransitions},
  91. { "DamageFX", INI::parseDamageFXDefinition },
  92. { "DialogEvent", INI::parseDialogDefinition },
  93. { "DrawGroupInfo", INI::parseDrawGroupNumberDefinition },
  94. { "EvaEvent", INI::parseEvaEvent },
  95. { "FXList", INI::parseFXListDefinition },
  96. { "GameData", INI::parseGameDataDefinition },
  97. { "InGameUI", INI::parseInGameUIDefinition },
  98. { "Locomotor", INI::parseLocomotorTemplateDefinition },
  99. { "Language", INI::parseLanguageDefinition },
  100. { "MapCache", INI::parseMapCacheDefinition },
  101. { "MapData", INI::parseMapDataDefinition },
  102. { "MappedImage", INI::parseMappedImageDefinition },
  103. { "MiscAudio", INI::parseMiscAudio},
  104. { "Mouse", INI::parseMouseDefinition },
  105. { "MouseCursor", INI::parseMouseCursorDefinition },
  106. { "MultiplayerColor", INI::parseMultiplayerColorDefinition },
  107. { "OnlineChatColors", INI::parseOnlineChatColorDefinition },
  108. { "MultiplayerSettings",INI::parseMultiplayerSettingsDefinition },
  109. { "MusicTrack", INI::parseMusicTrackDefinition },
  110. { "Object", INI::parseObjectDefinition },
  111. { "ObjectCreationList", INI::parseObjectCreationListDefinition },
  112. { "ObjectReskin", INI::parseObjectReskinDefinition },
  113. { "ParticleSystem", INI::parseParticleSystemDefinition },
  114. { "PlayerTemplate", INI::parsePlayerTemplateDefinition },
  115. { "Road", INI::parseTerrainRoadDefinition },
  116. { "Science", INI::parseScienceDefinition },
  117. { "Rank", INI::parseRankDefinition },
  118. { "SpecialPower", INI::parseSpecialPowerDefinition },
  119. { "ShellMenuScheme", INI::parseShellMenuSchemeDefinition },
  120. { "Terrain", INI::parseTerrainDefinition },
  121. { "Upgrade", INI::parseUpgradeDefinition },
  122. { "Video", INI::parseVideoDefinition },
  123. { "WaterSet", INI::parseWaterSettingDefinition },
  124. { "WaterTransparency", INI::parseWaterTransparencyDefinition},
  125. { "Weapon", INI::parseWeaponTemplateDefinition },
  126. { "WebpageURL", INI::parseWebpageURLDefinition },
  127. { "HeaderTemplate", INI::parseHeaderTemplateDefinition },
  128. { "StaticGameLOD", INI::parseStaticGameLODDefinition },
  129. { "DynamicGameLOD", INI::parseDynamicGameLODDefinition },
  130. { "LODPreset", INI::parseLODPreset },
  131. { "BenchProfile", INI::parseBenchProfile },
  132. { "ReallyLowMHz", parseReallyLowMHz },
  133. { NULL, NULL }, // keep this last!
  134. };
  135. ///////////////////////////////////////////////////////////////////////////////////////////////////
  136. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
  137. ///////////////////////////////////////////////////////////////////////////////////////////////////
  138. Bool INI::isValidINIFilename( const char *filename )
  139. {
  140. if( filename == NULL )
  141. return FALSE;
  142. Int len = strlen( filename );
  143. if( len < 3 )
  144. return FALSE;
  145. if( filename[ len - 1 ] != 'I' && filename[ len - 1 ] != 'i' )
  146. return FALSE;
  147. if( filename[ len - 2 ] != 'N' && filename[ len - 2 ] != 'n' )
  148. return FALSE;
  149. if( filename[ len - 3 ] != 'I' && filename[ len - 3 ] != 'i' )
  150. return FALSE;
  151. return TRUE;
  152. }
  153. ///////////////////////////////////////////////////////////////////////////////////////////////////
  154. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  155. ///////////////////////////////////////////////////////////////////////////////////////////////////
  156. //-------------------------------------------------------------------------------------------------
  157. //-------------------------------------------------------------------------------------------------
  158. INI::INI( void )
  159. {
  160. m_file = NULL;
  161. m_filename = "None";
  162. m_loadType = INI_LOAD_INVALID;
  163. m_lineNum = 0;
  164. m_seps = " \n\r\t="; ///< make sure you update m_sepsPercent/m_sepsColon as well
  165. m_sepsPercent = " \n\r\t=%%";
  166. m_sepsColon = " \n\r\t=:";
  167. m_sepsQuote = "\"\n="; ///< stop at " = EOL
  168. m_blockEndToken = "END";
  169. m_endOfFile = FALSE;
  170. m_buffer[0] = 0;
  171. #if defined(_DEBUG) || defined(_INTERNAL)
  172. m_curBlockStart[0] = 0;
  173. #endif
  174. } // end INI
  175. //-------------------------------------------------------------------------------------------------
  176. //-------------------------------------------------------------------------------------------------
  177. INI::~INI( void )
  178. {
  179. } // end ~INI
  180. //-------------------------------------------------------------------------------------------------
  181. /** Load all INI files in the specified directory (and subdirectories if indicated).
  182. * If we are to load subdirectories, we will load them *after* we load all the
  183. * files in the current directory */
  184. //-------------------------------------------------------------------------------------------------
  185. void INI::loadDirectory( AsciiString dirName, Bool subdirs, INILoadType loadType, Xfer *pXfer )
  186. {
  187. // sanity
  188. if( dirName.isEmpty() )
  189. throw INI_INVALID_DIRECTORY;
  190. try
  191. {
  192. FilenameList filenameList;
  193. dirName.concat('\\');
  194. TheFileSystem->getFileListInDirectory(dirName, "*.ini", filenameList, TRUE);
  195. // Load the INI files in the dir now, in a sorted order. This keeps things the same between machines
  196. // in a network game.
  197. FilenameList::const_iterator it = filenameList.begin();
  198. while (it != filenameList.end())
  199. {
  200. AsciiString tempname;
  201. tempname = (*it).str() + dirName.getLength();
  202. if ((tempname.find('\\') == NULL) && (tempname.find('/') == NULL)) {
  203. // this file doesn't reside in a subdirectory, load it first.
  204. load( *it, loadType, pXfer );
  205. }
  206. ++it;
  207. }
  208. it = filenameList.begin();
  209. while (it != filenameList.end())
  210. {
  211. AsciiString tempname;
  212. tempname = (*it).str() + dirName.getLength();
  213. if ((tempname.find('\\') != NULL) || (tempname.find('/') != NULL)) {
  214. load( *it, loadType, pXfer );
  215. }
  216. ++it;
  217. }
  218. }
  219. catch (...)
  220. {
  221. // propagate the exception
  222. throw;
  223. }
  224. } // end loadDirectory
  225. //-------------------------------------------------------------------------------------------------
  226. //-------------------------------------------------------------------------------------------------
  227. void INI::prepFile( AsciiString filename, INILoadType loadType )
  228. {
  229. // if we have a file open already -- we can't do another one
  230. if( m_file != NULL )
  231. {
  232. DEBUG_CRASH(( "INI::load, cannot open file '%s', file already open\n", filename.str() ));
  233. throw INI_FILE_ALREADY_OPEN;
  234. } // end if
  235. // open the file
  236. m_file = TheFileSystem->openFile(filename.str(), File::READ);
  237. if( m_file == NULL )
  238. {
  239. DEBUG_CRASH(( "INI::load, cannot open file '%s'\n", filename.str() ));
  240. throw INI_CANT_OPEN_FILE;
  241. } // end if
  242. m_file = m_file->convertToRAMFile();
  243. // save our filename
  244. m_filename = filename;
  245. // save our load time
  246. m_loadType = loadType;
  247. }
  248. //-------------------------------------------------------------------------------------------------
  249. //-------------------------------------------------------------------------------------------------
  250. void INI::unPrepFile()
  251. {
  252. // close the file
  253. m_file->close();
  254. m_file = NULL;
  255. m_filename = "None";
  256. m_loadType = INI_LOAD_INVALID;
  257. m_lineNum = 0;
  258. m_endOfFile = FALSE;
  259. s_xfer = NULL;
  260. }
  261. //-------------------------------------------------------------------------------------------------
  262. static INIBlockParse findBlockParse(const char* token)
  263. {
  264. for (const BlockParse* parse = theTypeTable; parse->token; ++parse)
  265. {
  266. if (strcmp( parse->token, token ) == 0)
  267. {
  268. return parse->parse;
  269. }
  270. }
  271. return NULL;
  272. }
  273. //-------------------------------------------------------------------------------------------------
  274. static INIFieldParseProc findFieldParse(const FieldParse* parseTable, const char* token, int& offset, const void*& userData)
  275. {
  276. for (const FieldParse* parse = parseTable; parse->token; ++parse)
  277. {
  278. if (strcmp( parse->token, token ) == 0)
  279. {
  280. offset = parse->offset;
  281. userData = parse->userData;
  282. return parse->parse;
  283. }
  284. }
  285. if (!parse->token && parse->parse)
  286. {
  287. offset = parse->offset;
  288. userData = token;
  289. return parse->parse;
  290. }
  291. else
  292. {
  293. return NULL;
  294. }
  295. }
  296. //-------------------------------------------------------------------------------------------------
  297. /** Load and parse an INI file */
  298. //-------------------------------------------------------------------------------------------------
  299. void INI::load( AsciiString filename, INILoadType loadType, Xfer *pXfer )
  300. {
  301. setFPMode(); // so we have consistent Real values for GameLogic -MDC
  302. s_xfer = pXfer;
  303. prepFile(filename, loadType);
  304. try
  305. {
  306. // read all lines in the file
  307. DEBUG_ASSERTCRASH( m_endOfFile == FALSE, ("INI::load, EOF at the beginning!\n") );
  308. while( m_endOfFile == FALSE )
  309. {
  310. // read this line
  311. readLine();
  312. AsciiString currentLine = m_buffer;
  313. // the first word is the type of data we're processing
  314. const char *token = strtok( m_buffer, m_seps );
  315. if( token )
  316. {
  317. INIBlockParse parse = findBlockParse(token);
  318. if (parse)
  319. {
  320. #if defined(_DEBUG) || defined(_INTERNAL)
  321. strcpy(m_curBlockStart, m_buffer);
  322. #endif
  323. try {
  324. (*parse)( this );
  325. } catch (...) {
  326. DEBUG_CRASH(("Error parsing block '%s' in INI file '%s'\n", token, m_filename.str()) );
  327. char buff[1024];
  328. sprintf(buff, "Error parsing INI file '%s' (Line: '%s')\n", m_filename.str(), currentLine.str());
  329. throw INIException(buff);
  330. }
  331. #if defined(_DEBUG) || defined(_INTERNAL)
  332. strcpy(m_curBlockStart, "NO_BLOCK");
  333. #endif
  334. }
  335. else
  336. {
  337. DEBUG_ASSERTCRASH( 0, ("[LINE: %d - FILE: '%s'] Unknown block '%s'\n",
  338. getLineNum(), getFilename().str(), token ) );
  339. throw INI_UNKNOWN_TOKEN;
  340. }
  341. } // end if
  342. } // end while
  343. }
  344. catch (...)
  345. {
  346. unPrepFile();
  347. // propagate the exception.
  348. throw;
  349. }
  350. unPrepFile();
  351. } // end load
  352. //-------------------------------------------------------------------------------------------------
  353. /** Read a line from the already open file. Any comments will be remved and
  354. * therefore ignored from any given line */
  355. //-------------------------------------------------------------------------------------------------
  356. void INI::readLine( void )
  357. {
  358. Bool isComment = FALSE;
  359. // sanity
  360. DEBUG_ASSERTCRASH( m_file, ("readLine(), file pointer is NULL\n") );
  361. // if we've reached end of file we'll just keep returning empty string in our buffer
  362. if( m_endOfFile )
  363. {
  364. m_buffer[ 0 ] = '\0';
  365. }
  366. else
  367. {
  368. // read up till the newline character or until out of space
  369. Int i = 0;
  370. Bool done = FALSE;
  371. while( !done )
  372. {
  373. // read character
  374. m_endOfFile = (m_file->read(m_buffer + i, 1) == 0);
  375. // check for end of file
  376. if( m_endOfFile )
  377. {
  378. done = TRUE;
  379. m_buffer[ i ] = '\0';
  380. } // end if
  381. // check for new line
  382. if( m_buffer[ i ] == '\n' )
  383. done = TRUE;
  384. DEBUG_ASSERTCRASH(m_buffer[ i ] != '\t', ("tab characters are not allowed in INI files (%s). please check your editor settings. Line Number %d\n",m_filename.str(), getLineNum()));
  385. // make all whitespace characters actual spaces
  386. if( isspace( m_buffer[ i ] ) )
  387. m_buffer[ i ] = ' ';
  388. // if this is a semicolon, that represents the start of a comment
  389. if( m_buffer[ i ] == ';' )
  390. isComment = TRUE;
  391. // if we've set the comment flag, just insert terminators in the place of each character read
  392. if( isComment == TRUE )
  393. m_buffer[ i ] = '\0';
  394. //
  395. // when we've become done, as the last thing just set the current index position + 1
  396. // to the string terminator
  397. //
  398. if( done == TRUE && i + 1 < INI_MAX_CHARS_PER_LINE )
  399. m_buffer[ i + 1 ] = '\0';
  400. // increase our buffer index, but watch out for the max
  401. if( ++i == INI_MAX_CHARS_PER_LINE )
  402. done = TRUE;
  403. } // end while
  404. // increase our line count
  405. m_lineNum++;
  406. // check for at the max
  407. if( i == INI_MAX_CHARS_PER_LINE )
  408. {
  409. DEBUG_ASSERTCRASH( 0, ("Buffer too small (%d) and was truncated, increase INI_MAX_CHARS_PER_LINE\n",
  410. INI_MAX_CHARS_PER_LINE) );
  411. } // end if
  412. }
  413. if (s_xfer)
  414. {
  415. s_xfer->xferUser( m_buffer, sizeof( char ) * strlen( m_buffer ) );
  416. //DEBUG_LOG(("Xfer val is now 0x%8.8X in %s, line %s\n", ((XferCRC *)s_xfer)->getCRC(),
  417. //m_filename.str(), m_buffer));
  418. }
  419. }
  420. //-------------------------------------------------------------------------------------------------
  421. /** Parse UnsignedByte from buffer and assign at location 'store' */
  422. //-------------------------------------------------------------------------------------------------
  423. void INI::parseUnsignedByte( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  424. {
  425. const char *token = ini->getNextToken();
  426. Int value = scanInt(token);
  427. if (value < 0 || value > 255)
  428. {
  429. DEBUG_CRASH(("Bad value INI::parseUnsignedByte"));
  430. throw ERROR_BUG;
  431. }
  432. *(Byte *)store = (Byte)value;
  433. }
  434. //-------------------------------------------------------------------------------------------------
  435. /** Parse signed short from buffer and assign at location 'store' */
  436. //-------------------------------------------------------------------------------------------------
  437. void INI::parseShort( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  438. {
  439. const char *token = ini->getNextToken();
  440. Int value = scanInt(token);
  441. if (value < -32768 || value > 32767)
  442. {
  443. DEBUG_CRASH(("Bad value INI::parseShort"));
  444. throw ERROR_BUG;
  445. }
  446. *(Short *)store = (Short)value;
  447. }
  448. //-------------------------------------------------------------------------------------------------
  449. /** Parse unsigned short from buffer and assign at location 'store' */
  450. //-------------------------------------------------------------------------------------------------
  451. void INI::parseUnsignedShort( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  452. {
  453. const char *token = ini->getNextToken();
  454. Int value = scanInt(token);
  455. if (value < 0 || value > 65535)
  456. {
  457. DEBUG_CRASH(("Bad value INI::parseUnsignedShort"));
  458. throw ERROR_BUG;
  459. }
  460. *(UnsignedShort *)store = (UnsignedShort)value;
  461. }
  462. //-------------------------------------------------------------------------------------------------
  463. /** Parse integer from buffer and assign at location 'store' */
  464. //-------------------------------------------------------------------------------------------------
  465. void INI::parseInt( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  466. {
  467. const char *token = ini->getNextToken();
  468. *(Int *)store = scanInt(token);
  469. }
  470. //-------------------------------------------------------------------------------------------------
  471. /** Parse unsigned integer from buffer and assign at location 'store' */
  472. //-------------------------------------------------------------------------------------------------
  473. void INI::parseUnsignedInt( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  474. {
  475. const char *token = ini->getNextToken();
  476. *(UnsignedInt *)store = scanUnsignedInt(token);
  477. }
  478. //-------------------------------------------------------------------------------------------------
  479. /** Parse real from buffer and assign at location 'store' */
  480. //-------------------------------------------------------------------------------------------------
  481. void INI::parseReal( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  482. {
  483. const char *token = ini->getNextToken();
  484. *(Real *)store = scanReal(token);
  485. }
  486. //-------------------------------------------------------------------------------------------------
  487. /** Parse real from buffer and assign at location 'store' */
  488. //-------------------------------------------------------------------------------------------------
  489. void INI::parsePositiveNonZeroReal( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  490. {
  491. const char *token = ini->getNextToken();
  492. *(Real *)store = scanReal(token);
  493. if (*(Real *)store <= 0.0f)
  494. {
  495. DEBUG_CRASH(("invalid Real value %f -- expected > 0\n",*(Real*)store));
  496. throw INI_INVALID_DATA;
  497. }
  498. }
  499. //-------------------------------------------------------------------------------------------------
  500. /** Parse a degree value (0 to 360) and store the radian value of that degree
  501. * in a Real */
  502. //-------------------------------------------------------------------------------------------------
  503. void INI::parseAngleReal( INI *ini, void * /*instance*/,
  504. void *store, const void *userData )
  505. {
  506. const char *token = ini->getNextToken();
  507. const Real RADS_PER_DEGREE = PI / 180.0f;
  508. *(Real *)store = scanReal( token ) * RADS_PER_DEGREE;
  509. }
  510. //-------------------------------------------------------------------------------------------------
  511. /** Parse an angular velocity in degrees-per-sec and store the rads-per-frame value of that degree
  512. * in a Real */
  513. //-------------------------------------------------------------------------------------------------
  514. void INI::parseAngularVelocityReal( INI *ini, void * /*instance*/,
  515. void *store, const void *userData )
  516. {
  517. const char *token = ini->getNextToken();
  518. // scan the int and convert to radian and store as a real
  519. *(Real *)store = ConvertAngularVelocityInDegreesPerSecToRadsPerFrame(scanReal( token ));
  520. }
  521. //-------------------------------------------------------------------------------------------------
  522. /** Parse Bool from buffer and assign at location 'store'. The buffer token must
  523. * be in the form of a string "Yes" or "No" (case is ignored) */
  524. //-------------------------------------------------------------------------------------------------
  525. void INI::parseBool( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  526. {
  527. *(Bool*)store = INI::scanBool(ini->getNextToken());
  528. }
  529. //-------------------------------------------------------------------------------------------------
  530. /** Parse Bool from buffer; if true, or in MASK, otherwise and out MASK. The buffer token must
  531. * be in the form of a string "Yes" or "No" (case is ignored) */
  532. //-------------------------------------------------------------------------------------------------
  533. void INI::parseBitInInt32( INI *ini, void *instance, void *store, const void* userData )
  534. {
  535. UnsignedInt* s = (UnsignedInt*)store;
  536. UnsignedInt mask = (UnsignedInt)userData;
  537. if (INI::scanBool(ini->getNextToken()))
  538. *s |= mask;
  539. else
  540. *s &= ~mask;
  541. }
  542. //-------------------------------------------------------------------------------------------------
  543. //-------------------------------------------------------------------------------------------------
  544. /*static*/ Bool INI::scanBool(const char* token)
  545. {
  546. // translate string yes/no into TRUE/FALSE
  547. if( stricmp( token, "yes" ) == 0 )
  548. return TRUE;
  549. else if( stricmp( token, "no" ) == 0 )
  550. return FALSE;
  551. else
  552. {
  553. DEBUG_CRASH(("invalid boolean token %s -- expected Yes or No\n",token));
  554. throw INI_INVALID_DATA;
  555. return false; // keep compiler happy
  556. }
  557. }
  558. //-------------------------------------------------------------------------------------------------
  559. /** Parse an *ASCII* string from buffer and assign at location 'store' */
  560. //-------------------------------------------------------------------------------------------------
  561. void INI::parseAsciiString( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  562. {
  563. AsciiString* asciiString = (AsciiString *)store;
  564. *asciiString = ini->getNextAsciiString();
  565. }
  566. //-------------------------------------------------------------------------------------------------
  567. /** Parse an *ASCII* string from buffer and assign at location 'store'. Has better support for quoted strings.
  568. We don't really need this function, but parseString() is broken and we want to leave it broken to
  569. maintain existing code.
  570. */
  571. //-------------------------------------------------------------------------------------------------
  572. void INI::parseQuotedAsciiString( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  573. {
  574. AsciiString* asciiString = (AsciiString *)store;
  575. *asciiString = ini->getNextQuotedAsciiString();
  576. }
  577. //-------------------------------------------------------------------------------------------------
  578. //-------------------------------------------------------------------------------------------------
  579. void INI::parseAsciiStringVector( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  580. {
  581. std::vector<AsciiString>* asv = (std::vector<AsciiString>*)store;
  582. asv->clear();
  583. for (const char *token = ini->getNextTokenOrNull(); token != NULL; token = ini->getNextTokenOrNull())
  584. {
  585. asv->push_back(token);
  586. }
  587. }
  588. //-------------------------------------------------------------------------------------------------
  589. //-------------------------------------------------------------------------------------------------
  590. void INI::parseAsciiStringVectorAppend( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  591. {
  592. std::vector<AsciiString>* asv = (std::vector<AsciiString>*)store;
  593. // nope, don't clear. duh.
  594. // asv->clear();
  595. for (const char *token = ini->getNextTokenOrNull(); token != NULL; token = ini->getNextTokenOrNull())
  596. {
  597. asv->push_back(token);
  598. }
  599. }
  600. //-------------------------------------------------------------------------------------------------
  601. //-------------------------------------------------------------------------------------------------
  602. /* static */void INI::parseScienceVector( INI *ini, void * /*instance*/, void *store, const void *userData )
  603. {
  604. ScienceVec* asv = (ScienceVec*)store;
  605. asv->clear();
  606. for (const char *token = ini->getNextTokenOrNull(); token != NULL; token = ini->getNextTokenOrNull())
  607. {
  608. if (stricmp(token, "None") == 0)
  609. {
  610. asv->clear();
  611. return;
  612. }
  613. asv->push_back(INI::scanScience( token ));
  614. }
  615. }
  616. //-------------------------------------------------------------------------------------------------
  617. //-------------------------------------------------------------------------------------------------
  618. AsciiString INI::getNextQuotedAsciiString()
  619. {
  620. AsciiString result;
  621. char buff[INI_MAX_CHARS_PER_LINE];
  622. const char *token = getNextTokenOrNull(); // if null, just leave an empty string
  623. if (token != NULL)
  624. {
  625. if (token[0] != '\"')
  626. {
  627. // if token is simply "
  628. result.set( token ); // Start following the "
  629. }
  630. else
  631. { int strLen=0;
  632. Bool done=FALSE;
  633. if ((strLen=strlen(token)) > 1)
  634. {
  635. strcpy(buff, &token[1]); //skip the starting quote
  636. //Check for end of quoted string. Checking here fixes cases where quoted string on same line with other data.
  637. if (buff[strLen-2]=='"') //skip ending quote if present
  638. { buff[strLen-2]='\0';
  639. done=TRUE;
  640. }
  641. }
  642. if (!done)
  643. {
  644. token = getNextToken(getSepsQuote());
  645. if (strlen(token) > 1 && token[1] != '\t')
  646. {
  647. strcat(buff, " ");
  648. strcat(buff, token);
  649. }
  650. else
  651. { Int buflen=strlen(buff);
  652. if (buff[buflen-1]=='\"')
  653. buff[buflen-1]='\0';
  654. }
  655. }
  656. result.set(buff);
  657. }
  658. }
  659. return result;
  660. }
  661. //-------------------------------------------------------------------------------------------------
  662. //-------------------------------------------------------------------------------------------------
  663. AsciiString INI::getNextAsciiString()
  664. {
  665. AsciiString result;
  666. const char *token = getNextTokenOrNull(); // if null, just leave an empty string
  667. if (token != NULL)
  668. {
  669. if (token[0] != '\"')
  670. {
  671. // if token is simply "
  672. result.set( token ); // Start following the "
  673. }
  674. else
  675. {
  676. static char buff[INI_MAX_CHARS_PER_LINE];
  677. if (strlen(token) > 1)
  678. {
  679. strcpy(buff, &token[1]);
  680. }
  681. token = getNextToken(getSepsQuote());
  682. if (strlen(token) > 1 && token[1] != '\t')
  683. {
  684. strcat(buff, " ");
  685. }
  686. strcat(buff, token);
  687. result.set(buff);
  688. }
  689. }
  690. return result;
  691. }
  692. //-------------------------------------------------------------------------------------------------
  693. /** Parse a string label, get the *translated* actual text from the label and store
  694. * into a *UNICODE* string. */
  695. //-------------------------------------------------------------------------------------------------
  696. void INI::parseAndTranslateLabel( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  697. {
  698. const char *token = ini->getNextToken();
  699. // translate
  700. UnicodeString translated = TheGameText->fetch( token );
  701. if( translated.isEmpty() )
  702. throw INI_INVALID_DATA;
  703. // save the translated text
  704. UnicodeString *theString = (UnicodeString *)store;
  705. theString->set( translated.str() );
  706. } // end parseAndTranslateLabel
  707. //-------------------------------------------------------------------------------------------------
  708. /** Parse a string label assumed as an image as part of the image collection. Translate
  709. * to an image pointer for storage */
  710. //-------------------------------------------------------------------------------------------------
  711. void INI::parseMappedImage( INI *ini, void * /*instance*/, void *store, const void *userData )
  712. {
  713. const char *token = ini->getNextToken();
  714. if( TheMappedImageCollection )
  715. {
  716. typedef const Image* ConstImagePtr;
  717. *(ConstImagePtr*)store = TheMappedImageCollection->findImageByName( AsciiString( token ) );
  718. }
  719. //KM: If we are in the worldbuilder, we want to parse commandbuttons for informational purposes,
  720. //but we don't care about the images -- because we never access them. In RTS/GUIEdit, they always
  721. //exist -- and in those cases, it will never call this code anyways because it'll throw long before.
  722. //else
  723. // throw INI_UNKNOWN_ERROR;
  724. } // end parseMappedImage
  725. // ------------------------------------------------------------------------------------------------
  726. /** Parse a string label assumed as a Anim2D template name. Translate that name to an
  727. * actual template pointer for storage */
  728. // ------------------------------------------------------------------------------------------------
  729. /*static*/ void INI::parseAnim2DTemplate( INI *ini, void *instance, void *store, const void *userData )
  730. {
  731. const char *token = ini->getNextToken();
  732. if( TheAnim2DCollection )
  733. {
  734. Anim2DTemplate **anim2DTemplate = (Anim2DTemplate **)store;
  735. *anim2DTemplate = TheAnim2DCollection->findTemplate( AsciiString( token ) );
  736. } // end if
  737. else
  738. {
  739. DEBUG_CRASH(( "INI::parseAnim2DTemplate - TheAnim2DCollection is NULL\n" ));
  740. throw INI_UNKNOWN_ERROR;
  741. } // end else
  742. } // end parseAnim2DTemplate
  743. //-------------------------------------------------------------------------------------------------
  744. /** Parse a percent in int or real form such as "23%" or "95.4%" and assign
  745. * to location 'store' as a number from 0.0 to 1.0 */
  746. //-------------------------------------------------------------------------------------------------
  747. void INI::parsePercentToReal( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  748. {
  749. const char *token = ini->getNextToken(ini->getSepsPercent());
  750. Real *theReal = (Real *)store;
  751. *theReal = scanPercentToReal(token);
  752. } // end parsePercentToReal
  753. //-------------------------------------------------------------------------------------------------
  754. /** 'store' points to an 32 bit unsigned integer. We will zero that integer, parse each token
  755. * in the buffer, if the token is in the userData table of strings, we will set the
  756. * according bit flag for it */
  757. //-------------------------------------------------------------------------------------------------
  758. void INI::parseBitString8( INI* ini, void * /*instance*/, void *store, const void* userData )
  759. {
  760. UnsignedInt tmp;
  761. INI::parseBitString32(ini, NULL, &tmp, userData);
  762. if (tmp & 0xffffff00)
  763. {
  764. DEBUG_CRASH(("Bad bitstring list INI::parseBitString8"));
  765. throw ERROR_BUG;
  766. }
  767. *(Byte*)store = (Byte)tmp;
  768. }
  769. //-------------------------------------------------------------------------------------------------
  770. /** 'store' points to an 32 bit unsigned integer. We will zero that integer, parse each token
  771. * in the buffer, if the token is in the userData table of strings, we will set the
  772. * according bit flag for it */
  773. //-------------------------------------------------------------------------------------------------
  774. void INI::parseBitString32( INI* ini, void * /*instance*/, void *store, const void* userData )
  775. {
  776. ConstCharPtrArray flagList = (ConstCharPtrArray)userData;
  777. UnsignedInt *bits = (UnsignedInt *)store;
  778. if( flagList == NULL || flagList[ 0 ] == NULL)
  779. {
  780. DEBUG_ASSERTCRASH( flagList, ("INTERNAL ERROR! parseBitString32: No flag list provided!\n") );
  781. throw INI_INVALID_NAME_LIST;
  782. }
  783. Bool foundNormal = false;
  784. Bool foundAddOrSub = false;
  785. // loop through all tokens
  786. for (const char *token = ini->getNextTokenOrNull(); token != NULL; token = ini->getNextTokenOrNull())
  787. {
  788. if (stricmp(token, "NONE") == 0)
  789. {
  790. if (foundNormal || foundAddOrSub)
  791. {
  792. DEBUG_CRASH(("you may not mix normal and +- ops in bitstring lists"));
  793. throw INI_INVALID_NAME_LIST;
  794. }
  795. *bits = 0;
  796. break;
  797. }
  798. if (token[0] == '+')
  799. {
  800. if (foundNormal)
  801. {
  802. DEBUG_CRASH(("you may not mix normal and +- ops in bitstring lists"));
  803. throw INI_INVALID_NAME_LIST;
  804. }
  805. Int bitIndex = INI::scanIndexList(token+1, flagList); // this throws if the token is not found
  806. *bits |= (1 << bitIndex);
  807. foundAddOrSub = true;
  808. }
  809. else if (token[0] == '-')
  810. {
  811. if (foundNormal)
  812. {
  813. DEBUG_CRASH(("you may not mix normal and +- ops in bitstring lists"));
  814. throw INI_INVALID_NAME_LIST;
  815. }
  816. Int bitIndex = INI::scanIndexList(token+1, flagList); // this throws if the token is not found
  817. *bits &= ~(1 << bitIndex);
  818. foundAddOrSub = true;
  819. }
  820. else
  821. {
  822. if (foundAddOrSub)
  823. {
  824. DEBUG_CRASH(("you may not mix normal and +- ops in bitstring lists"));
  825. throw INI_INVALID_NAME_LIST;
  826. }
  827. if (!foundNormal)
  828. *bits = 0;
  829. Int bitIndex = INI::scanIndexList(token, flagList); // this throws if the token is not found
  830. *bits |= (1 << bitIndex);
  831. foundNormal = true;
  832. }
  833. }
  834. }
  835. //-------------------------------------------------------------------------------------------------
  836. /** Parse a color in the form of
  837. *
  838. * RGB_COLOR = R:100 G:114 B:245
  839. * and store in "RGBColor" structure pointed to by 'store' */
  840. //-------------------------------------------------------------------------------------------------
  841. void INI::parseRGBColor( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  842. {
  843. const char* names[3] = { "R", "G", "B" };
  844. Int colors[3];
  845. for( Int i = 0; i < 3; i++ )
  846. {
  847. colors[i] = scanInt(ini->getNextSubToken(names[i]));
  848. if( colors[ i ] < 0 )
  849. throw INI_INVALID_DATA;
  850. if( colors[ i ] > 255 )
  851. throw INI_INVALID_DATA;
  852. }
  853. // assign the color components to the "RGBColor" pointer at 'store'
  854. RGBColor *theColor = (RGBColor *)store;
  855. theColor->red = (Real)colors[ 0 ] / 255.0f;
  856. theColor->green = (Real)colors[ 1 ] / 255.0f;
  857. theColor->blue = (Real)colors[ 2 ] / 255.0f;
  858. }
  859. //-------------------------------------------------------------------------------------------------
  860. /** Parse a color in the form of
  861. *
  862. * RGB_COLOR = R:100 G:114 B:245 [A:233]
  863. * and store in "RGBAColorInt" structure pointed to by 'store' */
  864. //-------------------------------------------------------------------------------------------------
  865. void INI::parseRGBAColorInt( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  866. {
  867. const char* names[4] = { "R", "G", "B", "A" };
  868. Int colors[4];
  869. for( Int i = 0; i < 4; i++ )
  870. {
  871. const char* token = ini->getNextTokenOrNull(ini->getSepsColon());
  872. if (token == NULL)
  873. {
  874. if (i < 3)
  875. {
  876. throw INI_INVALID_DATA;
  877. }
  878. else
  879. {
  880. // it's ok for A to be omitted.
  881. colors[i] = 255;
  882. }
  883. }
  884. else
  885. {
  886. // if present, the token must match.
  887. if (stricmp(token, names[i]) != 0)
  888. {
  889. throw INI_INVALID_DATA;
  890. }
  891. colors[i] = scanInt(ini->getNextToken(ini->getSepsColon()));
  892. }
  893. if( colors[ i ] < 0 )
  894. throw INI_INVALID_DATA;
  895. if( colors[ i ] > 255 )
  896. throw INI_INVALID_DATA;
  897. }
  898. //
  899. // assign the color components to the "RGBColorInt" pointer at 'store', keep
  900. // the numbers as between 0 and 255
  901. //
  902. RGBAColorInt *theColor = (RGBAColorInt *)store;
  903. theColor->red = colors[ 0 ];
  904. theColor->green = colors[ 1 ];
  905. theColor->blue = colors[ 2 ];
  906. theColor->alpha = colors[ 3 ];
  907. } // end parseRGBAColorInt
  908. //-------------------------------------------------------------------------------------------------
  909. /** Parse a color in the form of
  910. *
  911. * RGB_COLOR = R:100 G:114 B:245 [A:233]
  912. * and store in "Color" structure pointed to by 'store' */
  913. //-------------------------------------------------------------------------------------------------
  914. void INI::parseColorInt( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  915. {
  916. const char* names[4] = { "R", "G", "B", "A" };
  917. Int colors[4];
  918. for( Int i = 0; i < 4; i++ )
  919. {
  920. const char* token = ini->getNextTokenOrNull(ini->getSepsColon());
  921. if (token == NULL)
  922. {
  923. if (i < 3)
  924. {
  925. throw INI_INVALID_DATA;
  926. }
  927. else
  928. {
  929. // it's ok for A to be omitted.
  930. colors[i] = 255;
  931. }
  932. }
  933. else
  934. {
  935. // if present, the token must match.
  936. if (stricmp(token, names[i]) != 0)
  937. {
  938. throw INI_INVALID_DATA;
  939. }
  940. colors[i] = scanInt(ini->getNextToken(ini->getSepsColon()));
  941. }
  942. if( colors[ i ] < 0 )
  943. throw INI_INVALID_DATA;
  944. if( colors[ i ] > 255 )
  945. throw INI_INVALID_DATA;
  946. }
  947. //
  948. // assign the color components to the "Color" pointer at 'store', keep
  949. // the numbers as between 0 and 255
  950. //
  951. Color *theColor = (Color *)store;
  952. *theColor = GameMakeColor(colors[0], colors[1], colors[2], colors[3]);
  953. } // end parseColorInt
  954. //-------------------------------------------------------------------------------------------------
  955. /** Parse a 3D coordinate of reals in the form of:
  956. * FIELD_NAME = X:400 Y:-214.3 Z:8.6 */
  957. //-------------------------------------------------------------------------------------------------
  958. void INI::parseCoord3D( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  959. {
  960. Coord3D *theCoord = (Coord3D *)store;
  961. theCoord->x = scanReal(ini->getNextSubToken("X"));
  962. theCoord->y = scanReal(ini->getNextSubToken("Y"));
  963. theCoord->z = scanReal(ini->getNextSubToken("Z"));
  964. } // end parseCoord3D
  965. //-------------------------------------------------------------------------------------------------
  966. /** Parse a 2D coordinate of reals in the form of:
  967. * FIELD_NAME = X:400 Y:-214.3 */
  968. //-------------------------------------------------------------------------------------------------
  969. void INI::parseCoord2D( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  970. {
  971. Coord2D *theCoord = (Coord2D *)store;
  972. theCoord->x = scanReal(ini->getNextSubToken("X"));
  973. theCoord->y = scanReal(ini->getNextSubToken("Y"));
  974. } // end parseCoord2D
  975. //-------------------------------------------------------------------------------------------------
  976. /** Parse a 2D coordinate of Ints in the form of:
  977. * FIELD_NAME = X:400 Y:-214 */
  978. //-------------------------------------------------------------------------------------------------
  979. void INI::parseICoord2D( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  980. {
  981. ICoord2D *theCoord = (ICoord2D *)store;
  982. theCoord->x = scanInt(ini->getNextSubToken("X"));
  983. theCoord->y = scanInt(ini->getNextSubToken("Y"));
  984. } // end parseICoord2D
  985. //-------------------------------------------------------------------------------------------------
  986. /** Parse an audio event and assign to the 'AudioEventRTS*' at store */
  987. //-------------------------------------------------------------------------------------------------
  988. void INI::parseDynamicAudioEventRTS( INI *ini, void * /*instance*/, void *store, const void* userData )
  989. {
  990. const char *token = ini->getNextToken();
  991. DynamicAudioEventRTS** theSound = (DynamicAudioEventRTS**)store;
  992. // translate the string into a sound
  993. if (stricmp(token, "NoSound") == 0)
  994. {
  995. if (*theSound)
  996. {
  997. (*theSound)->deleteInstance();
  998. *theSound = NULL;
  999. }
  1000. }
  1001. else
  1002. {
  1003. if (*theSound == NULL)
  1004. *theSound = newInstance(DynamicAudioEventRTS);
  1005. (*theSound)->m_event.setEventName(AsciiString(token));
  1006. }
  1007. if (*theSound)
  1008. TheAudio->getInfoForAudioEvent(&(*theSound)->m_event);
  1009. }
  1010. //-------------------------------------------------------------------------------------------------
  1011. /** Parse an audio event and assign to the 'AudioEventRTS*' at store */
  1012. //-------------------------------------------------------------------------------------------------
  1013. void INI::parseAudioEventRTS( INI *ini, void * /*instance*/, void *store, const void* userData )
  1014. {
  1015. const char *token = ini->getNextToken();
  1016. AudioEventRTS *theSound = (AudioEventRTS*)store;
  1017. // translate the string into a sound
  1018. if (stricmp(token, "NoSound") != 0) {
  1019. theSound->setEventName(AsciiString(token));
  1020. }
  1021. TheAudio->getInfoForAudioEvent(theSound);
  1022. }
  1023. //-------------------------------------------------------------------------------------------------
  1024. /** Parse an ThingTemplate and assign to the 'ThingTemplate *' at store */
  1025. //-------------------------------------------------------------------------------------------------
  1026. void INI::parseThingTemplate( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1027. {
  1028. const char *token = ini->getNextToken();
  1029. if (!TheThingFactory)
  1030. {
  1031. DEBUG_CRASH(("TheThingFactory not inited yet"));
  1032. throw ERROR_BUG;
  1033. }
  1034. typedef const ThingTemplate *ConstThingTemplatePtr;
  1035. ConstThingTemplatePtr* theThingTemplate = (ConstThingTemplatePtr*)store;
  1036. if (stricmp(token, "None") == 0)
  1037. {
  1038. *theThingTemplate = NULL;
  1039. }
  1040. else
  1041. {
  1042. const ThingTemplate *tt = TheThingFactory->findTemplate(token); // could be null!
  1043. DEBUG_ASSERTCRASH(tt, ("ThingTemplate %s not found!\n",token));
  1044. // assign it, even if null!
  1045. *theThingTemplate = tt;
  1046. }
  1047. }
  1048. //-------------------------------------------------------------------------------------------------
  1049. /** Parse an ArmorTemplate and assign to the 'ArmorTemplate *' at store */
  1050. //-------------------------------------------------------------------------------------------------
  1051. void INI::parseArmorTemplate( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1052. {
  1053. const char *token = ini->getNextToken();
  1054. typedef const ArmorTemplate *ConstArmorTemplatePtr;
  1055. ConstArmorTemplatePtr* theArmorTemplate = (ConstArmorTemplatePtr*)store;
  1056. if (stricmp(token, "None") == 0)
  1057. {
  1058. *theArmorTemplate = NULL;
  1059. }
  1060. else
  1061. {
  1062. const ArmorTemplate *tt = TheArmorStore->findArmorTemplate(token); // could be null!
  1063. DEBUG_ASSERTCRASH(tt, ("ArmorTemplate %s not found!\n",token));
  1064. // assign it, even if null!
  1065. *theArmorTemplate = tt;
  1066. }
  1067. }
  1068. //-------------------------------------------------------------------------------------------------
  1069. /** Parse an WeaponTemplate and assign to the 'WeaponTemplate *' at store */
  1070. //-------------------------------------------------------------------------------------------------
  1071. void INI::parseWeaponTemplate( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1072. {
  1073. const char *token = ini->getNextToken();
  1074. typedef const WeaponTemplate *ConstWeaponTemplatePtr;
  1075. ConstWeaponTemplatePtr* theWeaponTemplate = (ConstWeaponTemplatePtr*)store;
  1076. const WeaponTemplate *tt = TheWeaponStore->findWeaponTemplate(token); // could be null!
  1077. DEBUG_ASSERTCRASH(tt || stricmp(token, "None") == 0, ("WeaponTemplate %s not found!\n",token));
  1078. // assign it, even if null!
  1079. *theWeaponTemplate = tt;
  1080. }
  1081. //-------------------------------------------------------------------------------------------------
  1082. /** Parse an FXList and assign to the 'FXList *' at store */
  1083. //-------------------------------------------------------------------------------------------------
  1084. void INI::parseFXList( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1085. {
  1086. const char *token = ini->getNextToken();
  1087. typedef const FXList *ConstFXListPtr;
  1088. ConstFXListPtr* theFXList = (ConstFXListPtr*)store;
  1089. const FXList *fxl = TheFXListStore->findFXList(token); // could be null!
  1090. DEBUG_ASSERTCRASH(fxl != NULL || stricmp(token, "None") == 0, ("FXList %s not found!\n",token));
  1091. // assign it, even if null!
  1092. *theFXList = fxl;
  1093. }
  1094. //-------------------------------------------------------------------------------------------------
  1095. /** Parse a particle system and assign to 'ParticleSystemTemplate *' at store */
  1096. //-------------------------------------------------------------------------------------------------
  1097. void INI::parseParticleSystemTemplate( INI *ini, void * /*instance*/, void *store, const void *userData )
  1098. {
  1099. const char *token = ini->getNextToken();
  1100. const ParticleSystemTemplate *pSystemT = TheParticleSystemManager->findTemplate( AsciiString( token ) );
  1101. DEBUG_ASSERTCRASH( pSystemT || stricmp( token, "None" ) == 0, ("ParticleSystem %s not found!\n",token) );
  1102. typedef const ParticleSystemTemplate* ConstParticleSystemTemplatePtr;
  1103. ConstParticleSystemTemplatePtr* theParticleSystemTemplate = (ConstParticleSystemTemplatePtr*)store;
  1104. *theParticleSystemTemplate = pSystemT;
  1105. } // end parseParticleSystemTemplate
  1106. //-------------------------------------------------------------------------------------------------
  1107. /** Parse an DamageFX and assign to the 'DamageFX *' at store */
  1108. //-------------------------------------------------------------------------------------------------
  1109. void INI::parseDamageFX( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1110. {
  1111. const char *token = ini->getNextToken();
  1112. typedef const DamageFX *ConstDamageFXPtr;
  1113. ConstDamageFXPtr* theDamageFX = (ConstDamageFXPtr*)store;
  1114. if (stricmp(token, "None") == 0)
  1115. {
  1116. *theDamageFX = NULL;
  1117. }
  1118. else
  1119. {
  1120. const DamageFX *fxl = TheDamageFXStore->findDamageFX(token); // could be null!
  1121. DEBUG_ASSERTCRASH(fxl, ("DamageFX %s not found!\n",token));
  1122. // assign it, even if null!
  1123. *theDamageFX = fxl;
  1124. }
  1125. }
  1126. //-------------------------------------------------------------------------------------------------
  1127. /** Parse an ObjectCreationList and assign to the 'ObjectCreationList *' at store */
  1128. //-------------------------------------------------------------------------------------------------
  1129. void INI::parseObjectCreationList( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1130. {
  1131. const char *token = ini->getNextToken();
  1132. typedef const ObjectCreationList *ConstObjectCreationListPtr;
  1133. ConstObjectCreationListPtr* theObjectCreationList = (ConstObjectCreationListPtr*)store;
  1134. const ObjectCreationList *ocl = TheObjectCreationListStore->findObjectCreationList(token); // could be null!
  1135. DEBUG_ASSERTCRASH(ocl || stricmp(token, "None") == 0, ("ObjectCreationList %s not found!\n",token));
  1136. // assign it, even if null!
  1137. *theObjectCreationList = ocl;
  1138. }
  1139. //-------------------------------------------------------------------------------------------------
  1140. /** Parse a upgrade template string and store as template pointer */
  1141. //-------------------------------------------------------------------------------------------------
  1142. void INI::parseUpgradeTemplate( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1143. {
  1144. const char *token = ini->getNextToken();
  1145. if (!TheUpgradeCenter)
  1146. {
  1147. DEBUG_CRASH(("TheUpgradeCenter not inited yet"));
  1148. throw ERROR_BUG;
  1149. }
  1150. const UpgradeTemplate *uu = TheUpgradeCenter->findUpgrade( AsciiString( token ) );
  1151. DEBUG_ASSERTCRASH( uu || stricmp( token, "None" ) == 0, ("Upgrade %s not found!\n",token) );
  1152. typedef const UpgradeTemplate* ConstUpgradeTemplatePtr;
  1153. ConstUpgradeTemplatePtr* theUpgradeTemplate = (ConstUpgradeTemplatePtr *)store;
  1154. *theUpgradeTemplate = uu;
  1155. }
  1156. //-------------------------------------------------------------------------------------------------
  1157. /** Parse a special power template string and store as template pointer */
  1158. //-------------------------------------------------------------------------------------------------
  1159. void INI::parseSpecialPowerTemplate( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1160. {
  1161. const char *token = ini->getNextToken();
  1162. if (!TheSpecialPowerStore)
  1163. {
  1164. DEBUG_CRASH(("TheSpecialPowerStore not inited yet"));
  1165. throw ERROR_BUG;
  1166. }
  1167. const SpecialPowerTemplate *sPowerT = TheSpecialPowerStore->findSpecialPowerTemplate( AsciiString( token ) );
  1168. DEBUG_ASSERTCRASH( sPowerT || stricmp( token, "None" ) == 0, ("Specialpower %s not found!\n",token) );
  1169. typedef const SpecialPowerTemplate* ConstSpecialPowerTemplatePtr;
  1170. ConstSpecialPowerTemplatePtr* theSpecialPowerTemplate = (ConstSpecialPowerTemplatePtr *)store;
  1171. *theSpecialPowerTemplate = sPowerT;
  1172. }
  1173. //-------------------------------------------------------------------------------------------------
  1174. /** Parse a science string and store as science type */
  1175. //-------------------------------------------------------------------------------------------------
  1176. /* static */void INI::parseScience( INI *ini, void * /*instance*/, void *store, const void *userData )
  1177. {
  1178. const char *token = ini->getNextToken();
  1179. if (!TheScienceStore)
  1180. {
  1181. DEBUG_CRASH(("TheScienceStore not inited yet"));
  1182. throw ERROR_BUG;
  1183. }
  1184. *((ScienceType *)store) = INI::scanScience(token);
  1185. }
  1186. //-------------------------------------------------------------------------------------------------
  1187. /** Parse a single string token, check for that token in the index list
  1188. * of names provided and store the index into that list.
  1189. *
  1190. * NOTE: Is is assumed that we are going to store the index into
  1191. * a 4 byte integer. This works well for INT and ENUM definitions */
  1192. //-------------------------------------------------------------------------------------------------
  1193. void INI::parseIndexList( INI* ini, void * /*instance*/, void *store, const void* userData )
  1194. {
  1195. ConstCharPtrArray nameList = (ConstCharPtrArray)userData;
  1196. *(Int *)store = scanIndexList(ini->getNextToken(), nameList);
  1197. }
  1198. //-------------------------------------------------------------------------------------------------
  1199. /** Parse a single string token, check for that token in the index list
  1200. * of names provided and store the index into that list.
  1201. *
  1202. * NOTE: Is is assumed that we are going to store the index into
  1203. * a 4 byte integer. This works well for INT and ENUM definitions */
  1204. //-------------------------------------------------------------------------------------------------
  1205. void INI::parseByteSizedIndexList( INI* ini, void * /*instance*/, void *store, const void* userData )
  1206. {
  1207. ConstCharPtrArray nameList = (ConstCharPtrArray)userData;
  1208. Int value = scanIndexList(ini->getNextToken(), nameList);
  1209. if (value < 0 || value > 255)
  1210. {
  1211. DEBUG_CRASH(("Bad index list INI::parseByteSizedIndexList"));
  1212. throw ERROR_BUG;
  1213. }
  1214. *(Byte *)store = (Byte)value;
  1215. }
  1216. //-------------------------------------------------------------------------------------------------
  1217. /** Parse a single string token, check for that token in the index list
  1218. * of names provided and store the associated value into that list.
  1219. *
  1220. * NOTE: Is is assumed that we are going to store the index into
  1221. * a 4 byte integer. This works well for INT and ENUM definitions */
  1222. //-------------------------------------------------------------------------------------------------
  1223. void INI::parseLookupList( INI* ini, void * /*instance*/, void *store, const void* userData )
  1224. {
  1225. ConstLookupListRecArray lookupList = (ConstLookupListRecArray)userData;
  1226. *(Int *)store = scanLookupList(ini->getNextToken(), lookupList);
  1227. }
  1228. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1229. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
  1230. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1231. //-------------------------------------------------------------------------------------------------
  1232. void MultiIniFieldParse::add(const FieldParse* f, UnsignedInt e)
  1233. {
  1234. if (m_count < MAX_MULTI_FIELDS)
  1235. {
  1236. m_fieldParse[m_count] = f;
  1237. m_extraOffset[m_count] = e;
  1238. ++m_count;
  1239. }
  1240. else
  1241. {
  1242. DEBUG_CRASH(("too many multi-fields in INI::initFromINIMultiProc"));
  1243. throw ERROR_BUG;
  1244. }
  1245. }
  1246. //-------------------------------------------------------------------------------------------------
  1247. void INI::initFromINI( void *what, const FieldParse* parseTable )
  1248. {
  1249. MultiIniFieldParse p;
  1250. p.add(parseTable);
  1251. initFromINIMulti(what, p);
  1252. }
  1253. //-------------------------------------------------------------------------------------------------
  1254. void INI::initFromINIMultiProc( void *what, BuildMultiIniFieldProc proc )
  1255. {
  1256. MultiIniFieldParse p;
  1257. (*proc)(p);
  1258. initFromINIMulti(what, p);
  1259. }
  1260. //-------------------------------------------------------------------------------------------------
  1261. void INI::initFromINIMulti( void *what, const MultiIniFieldParse& parseTableList )
  1262. {
  1263. Bool done = FALSE;
  1264. if( what == NULL )
  1265. {
  1266. DEBUG_ASSERTCRASH( 0, ("INI::initFromINI - Invalid parameters supplied!\n") );
  1267. throw INI_INVALID_PARAMS;
  1268. }
  1269. // read each of the data fields
  1270. while( !done )
  1271. {
  1272. // read next line
  1273. readLine();
  1274. // check for end token
  1275. const char* field = strtok( m_buffer, INI::getSeps() );
  1276. if( field )
  1277. {
  1278. if( stricmp( field, m_blockEndToken ) == 0 )
  1279. {
  1280. done = TRUE;
  1281. }
  1282. else
  1283. {
  1284. Bool found = false;
  1285. for (int ptIdx = 0; ptIdx < parseTableList.getCount(); ++ptIdx)
  1286. {
  1287. int offset = 0;
  1288. void* userData = 0;
  1289. INIFieldParseProc parse = findFieldParse(parseTableList.getNthFieldParse(ptIdx), field, offset, userData);
  1290. if (parse)
  1291. {
  1292. // parse this block and check for parse errors
  1293. try {
  1294. (*parse)( this, what, (char *)what + offset + parseTableList.getNthExtraOffset(ptIdx), userData );
  1295. } catch (...) {
  1296. DEBUG_CRASH( ("[LINE: %d - FILE: '%s'] Error reading field '%s' of block '%s'\n",
  1297. INI::getLineNum(), INI::getFilename().str(), field, m_curBlockStart) );
  1298. char buff[1024];
  1299. sprintf(buff, "[LINE: %d - FILE: '%s'] Error reading field '%s'\n", INI::getLineNum(), INI::getFilename().str(), field);
  1300. throw INIException(buff);
  1301. }
  1302. found = true;
  1303. break;
  1304. }
  1305. }
  1306. if (!found)
  1307. {
  1308. DEBUG_ASSERTCRASH( 0, ("[LINE: %d - FILE: '%s'] Unknown field '%s' in block '%s'\n",
  1309. INI::getLineNum(), INI::getFilename().str(), field, m_curBlockStart) );
  1310. throw INI_UNKNOWN_TOKEN;
  1311. }
  1312. } // end else
  1313. } // end if
  1314. // sanity check for reaching end of file with no closing end token
  1315. if( done == FALSE && INI::isEOF() == TRUE )
  1316. {
  1317. done = TRUE;
  1318. DEBUG_ASSERTCRASH( 0, ("Error parsing block '%s', in INI file '%s'. Missing '%s' token\n",
  1319. m_curBlockStart, getFilename().str(), m_blockEndToken) );
  1320. throw INI_MISSING_END_TOKEN;
  1321. } // end if
  1322. } // end while
  1323. }
  1324. //-------------------------------------------------------------------------------------------------
  1325. /*static*/ const char* INI::getNextToken(const char* seps)
  1326. {
  1327. if (!seps) seps = getSeps();
  1328. const char *token = ::strtok(NULL, seps);
  1329. if (!token)
  1330. throw INI_INVALID_DATA;
  1331. return token;
  1332. }
  1333. //-------------------------------------------------------------------------------------------------
  1334. /*static*/ const char* INI::getNextTokenOrNull(const char* seps)
  1335. {
  1336. if (!seps) seps = getSeps();
  1337. const char *token = ::strtok(NULL, seps);
  1338. return token;
  1339. }
  1340. //-------------------------------------------------------------------------------------------------
  1341. /*static*/ ScienceType INI::scanScience(const char* token)
  1342. {
  1343. return TheScienceStore->friend_lookupScience( token );
  1344. }
  1345. //-------------------------------------------------------------------------------------------------
  1346. /*static*/ Int INI::scanInt(const char* token)
  1347. {
  1348. Int value;
  1349. if (sscanf( token, "%d", &value ) != 1)
  1350. throw INI_INVALID_DATA;
  1351. return value;
  1352. }
  1353. //-------------------------------------------------------------------------------------------------
  1354. /*static*/ UnsignedInt INI::scanUnsignedInt(const char* token)
  1355. {
  1356. UnsignedInt value;
  1357. if (sscanf( token, "%u", &value ) != 1) // unsigned int is %u, not %d
  1358. throw INI_INVALID_DATA;
  1359. return value;
  1360. }
  1361. //-------------------------------------------------------------------------------------------------
  1362. /*static*/ Real INI::scanReal(const char* token)
  1363. {
  1364. Real value;
  1365. if (sscanf( token, "%f", &value ) != 1)
  1366. throw INI_INVALID_DATA;
  1367. return value;
  1368. }
  1369. //-------------------------------------------------------------------------------------------------
  1370. /*static*/ Real INI::scanPercentToReal(const char* token)
  1371. {
  1372. Real value;
  1373. if (sscanf( token, "%f", &value ) != 1)
  1374. throw INI_INVALID_DATA;
  1375. return value / 100.0f;
  1376. }
  1377. //-------------------------------------------------------------------------------------------------
  1378. /*static*/ Int INI::scanIndexList(const char* token, ConstCharPtrArray nameList)
  1379. {
  1380. if( nameList == NULL || nameList[ 0 ] == NULL )
  1381. {
  1382. DEBUG_ASSERTCRASH( 0, ("INTERNAL ERROR! scanIndexList, invalid name list\n") );
  1383. throw INI_INVALID_NAME_LIST;
  1384. }
  1385. // search for matching name
  1386. Int count = 0;
  1387. for(ConstCharPtrArray name = nameList; *name; name++, count++ )
  1388. {
  1389. if( stricmp( *name, token ) == 0 )
  1390. {
  1391. return count;
  1392. }
  1393. }
  1394. DEBUG_CRASH(("token %s is not a valid member of the index list\n",token));
  1395. throw INI_INVALID_DATA;
  1396. return 0; // never executed, but keeps compiler happy
  1397. }
  1398. //-------------------------------------------------------------------------------------------------
  1399. /*static*/ Int INI::scanLookupList(const char* token, ConstLookupListRecArray lookupList)
  1400. {
  1401. if( lookupList == NULL || lookupList[ 0 ].name == NULL )
  1402. {
  1403. DEBUG_ASSERTCRASH( 0, ("INTERNAL ERROR! scanLookupList, invalid name list\n") );
  1404. throw INI_INVALID_NAME_LIST;
  1405. }
  1406. // search for matching name
  1407. Bool found = false;
  1408. for( const LookupListRec* lookup = &lookupList[0]; lookup->name; lookup++ )
  1409. {
  1410. if( stricmp( lookup->name, token ) == 0 )
  1411. {
  1412. return lookup->value;
  1413. found = true;
  1414. break;
  1415. }
  1416. }
  1417. DEBUG_CRASH(("token %s is not a valid member of the lookup list\n",token));
  1418. throw INI_INVALID_DATA;
  1419. return 0; // never executed, but keeps compiler happy
  1420. }
  1421. //-------------------------------------------------------------------------------------------------
  1422. const char* INI::getNextSubToken(const char* expected)
  1423. {
  1424. const char* token = getNextToken(getSepsColon());
  1425. if (stricmp(token, expected) != 0)
  1426. throw INI_INVALID_DATA;
  1427. return getNextToken(getSepsColon());
  1428. }
  1429. //-------------------------------------------------------------------------------------------------
  1430. /**
  1431. * Parse a "random variable".
  1432. * The format is "FIELD = low high [distribution]".
  1433. */
  1434. void INI::parseGameClientRandomVariable( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1435. {
  1436. GameClientRandomVariable *var = static_cast<GameClientRandomVariable *>(store);
  1437. const char* token;
  1438. token = ini->getNextToken();
  1439. Real low = INI::scanReal(token);
  1440. token = ini->getNextToken();
  1441. Real high = INI::scanReal(token);
  1442. // if omitted, assume uniform
  1443. GameClientRandomVariable::DistributionType type = GameClientRandomVariable::UNIFORM;
  1444. token = ini->getNextTokenOrNull();
  1445. if (token)
  1446. type = (GameClientRandomVariable::DistributionType)INI::scanIndexList(token, GameClientRandomVariable::DistributionTypeNames);
  1447. // set the range of the random variable
  1448. var->setRange( low, high, type );
  1449. }
  1450. //-------------------------------------------------------------------------------------------------
  1451. // parse a duration in msec and convert to duration in frames
  1452. void INI::parseDurationReal( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1453. {
  1454. Real val = scanReal(ini->getNextToken());
  1455. *(Real *)store = ConvertDurationFromMsecsToFrames(val);
  1456. }
  1457. //-------------------------------------------------------------------------------------------------
  1458. // parse a duration in msec and convert to duration in integral number of frames, (unsignedint) rounding UP
  1459. void INI::parseDurationUnsignedInt( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1460. {
  1461. UnsignedInt val = scanUnsignedInt(ini->getNextToken());
  1462. *(UnsignedInt *)store = (UnsignedInt)ceilf(ConvertDurationFromMsecsToFrames((Real)val));
  1463. }
  1464. // ------------------------------------------------------------------------------------------------
  1465. // parse a duration in msec and convert to duration in integral number of frames, (unsignedshort) rounding UP
  1466. void INI::parseDurationUnsignedShort( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1467. {
  1468. UnsignedInt val = scanUnsignedInt(ini->getNextToken());
  1469. *(UnsignedShort *)store = (UnsignedShort)ceilf(ConvertDurationFromMsecsToFrames((Real)val));
  1470. }
  1471. //-------------------------------------------------------------------------------------------------
  1472. // parse acceleration in (dist/sec) and convert to (dist/frame)
  1473. void INI::parseVelocityReal( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1474. {
  1475. const char *token = ini->getNextToken();
  1476. Real val = scanReal(token);
  1477. *(Real *)store = ConvertVelocityInSecsToFrames(val);
  1478. }
  1479. //-------------------------------------------------------------------------------------------------
  1480. // parse acceleration in (dist/sec^2) and convert to (dist/frame^2)
  1481. void INI::parseAccelerationReal( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
  1482. {
  1483. const char *token = ini->getNextToken();
  1484. Real val = scanReal(token);
  1485. *(Real *)store = ConvertAccelerationInSecsToFrames(val);
  1486. }
  1487. //-------------------------------------------------------------------------------------------------
  1488. //-------------------------------------------------------------------------------------------------
  1489. void INI::parseVeterancyLevelFlags(INI* ini, void* /*instance*/, void* store, const void* /*userData*/)
  1490. {
  1491. VeterancyLevelFlags flags = VETERANCY_LEVEL_FLAGS_ALL;
  1492. for (const char* token = ini->getNextToken(); token; token = ini->getNextTokenOrNull())
  1493. {
  1494. if (stricmp(token, "ALL") == 0)
  1495. {
  1496. flags = VETERANCY_LEVEL_FLAGS_ALL;
  1497. continue;
  1498. }
  1499. else if (stricmp(token, "NONE") == 0)
  1500. {
  1501. flags = VETERANCY_LEVEL_FLAGS_NONE;
  1502. continue;
  1503. }
  1504. else if (token[0] == '+')
  1505. {
  1506. VeterancyLevel dt = (VeterancyLevel)INI::scanIndexList(token+1, TheVeterancyNames);
  1507. flags = setVeterancyLevelFlag(flags, dt);
  1508. continue;
  1509. }
  1510. else if (token[0] == '-')
  1511. {
  1512. VeterancyLevel dt = (VeterancyLevel)INI::scanIndexList(token+1, TheVeterancyNames);
  1513. flags = clearVeterancyLevelFlag(flags, dt);
  1514. continue;
  1515. }
  1516. else
  1517. {
  1518. throw INI_UNKNOWN_TOKEN;
  1519. }
  1520. }
  1521. *(VeterancyLevelFlags*)store = flags;
  1522. }
  1523. //-------------------------------------------------------------------------------------------------
  1524. //-------------------------------------------------------------------------------------------------
  1525. void INI::parseSoundsList( INI* ini, void *instance, void *store, const void* /*userData*/ )
  1526. {
  1527. std::vector<AsciiString> *vec = (std::vector<AsciiString>*) store;
  1528. vec->clear();
  1529. const char* SEPS = " \t,=";
  1530. const char *c = ini->getNextTokenOrNull(SEPS);
  1531. while ( c )
  1532. {
  1533. vec->push_back( c );
  1534. c = ini->getNextTokenOrNull(SEPS);
  1535. }
  1536. }
  1537. //-------------------------------------------------------------------------------------------------
  1538. //-------------------------------------------------------------------------------------------------
  1539. void INI::parseDamageTypeFlags(INI* ini, void* /*instance*/, void* store, const void* /*userData*/)
  1540. {
  1541. DamageTypeFlags flags = DAMAGE_TYPE_FLAGS_ALL;
  1542. for (const char* token = ini->getNextToken(); token; token = ini->getNextTokenOrNull())
  1543. {
  1544. if (stricmp(token, "ALL") == 0)
  1545. {
  1546. flags = DAMAGE_TYPE_FLAGS_ALL;
  1547. continue;
  1548. }
  1549. if (stricmp(token, "NONE") == 0)
  1550. {
  1551. flags = DAMAGE_TYPE_FLAGS_NONE;
  1552. continue;
  1553. }
  1554. if (token[0] == '+')
  1555. {
  1556. DamageType dt = (DamageType)INI::scanIndexList(token+1, TheDamageNames);
  1557. flags = setDamageTypeFlag(flags, dt);
  1558. continue;
  1559. }
  1560. if (token[0] == '-')
  1561. {
  1562. DamageType dt = (DamageType)INI::scanIndexList(token+1, TheDamageNames);
  1563. flags = clearDamageTypeFlag(flags, dt);
  1564. continue;
  1565. }
  1566. throw INI_UNKNOWN_TOKEN;
  1567. }
  1568. *(DamageTypeFlags*)store = flags;
  1569. }
  1570. //-------------------------------------------------------------------------------------------------
  1571. //-------------------------------------------------------------------------------------------------
  1572. void INI::parseDeathTypeFlags(INI* ini, void* /*instance*/, void* store, const void* /*userData*/)
  1573. {
  1574. DeathTypeFlags flags = DEATH_TYPE_FLAGS_ALL;
  1575. for (const char* token = ini->getNextToken(); token; token = ini->getNextTokenOrNull())
  1576. {
  1577. if (stricmp(token, "ALL") == 0)
  1578. {
  1579. flags = DEATH_TYPE_FLAGS_ALL;
  1580. continue;
  1581. }
  1582. if (stricmp(token, "NONE") == 0)
  1583. {
  1584. flags = DEATH_TYPE_FLAGS_NONE;
  1585. continue;
  1586. }
  1587. if (token[0] == '+')
  1588. {
  1589. DeathType dt = (DeathType)INI::scanIndexList(token+1, TheDeathNames);
  1590. flags = setDeathTypeFlag(flags, dt);
  1591. continue;
  1592. }
  1593. if (token[0] == '-')
  1594. {
  1595. DeathType dt = (DeathType)INI::scanIndexList(token+1, TheDeathNames);
  1596. flags = clearDeathTypeFlag(flags, dt);
  1597. continue;
  1598. }
  1599. throw INI_UNKNOWN_TOKEN;
  1600. }
  1601. *(DeathTypeFlags*)store = flags;
  1602. }
  1603. //-------------------------------------------------------------------------------------------------
  1604. // parse the line and return whether the given line is a Block declaration of the form
  1605. // [whitespace] blockType [whitespace] blockName [EOL]
  1606. // both blockType and blockName are case insensitive
  1607. Bool INI::isDeclarationOfType( AsciiString blockType, AsciiString blockName, char *bufferToCheck )
  1608. {
  1609. Bool retVal = true;
  1610. if (!bufferToCheck || blockType.isEmpty() || blockName.isEmpty()) {
  1611. return false;
  1612. }
  1613. // DO NOT RETURN EARLY FROM THIS FUNCTION. (beyond this point)
  1614. // we have to restore the bufferToCheck to its previous state before returning, so
  1615. // it is important to get through all the checks.
  1616. char restoreChar;
  1617. char *tempBuff = bufferToCheck;
  1618. int blockTypeLength = blockType.getLength();
  1619. int blockNameLength = blockName.getLength();
  1620. while (isspace(*tempBuff)) {
  1621. ++tempBuff;
  1622. }
  1623. if (strlen(tempBuff) > blockTypeLength) {
  1624. restoreChar = tempBuff[blockTypeLength];
  1625. tempBuff[blockTypeLength] = 0;
  1626. if (stricmp(blockType.str(), tempBuff) != 0) {
  1627. retVal = false;
  1628. }
  1629. tempBuff[blockTypeLength] = restoreChar;
  1630. tempBuff = tempBuff + blockTypeLength;
  1631. } else {
  1632. retVal = false;
  1633. }
  1634. while (isspace(*tempBuff)) {
  1635. ++tempBuff;
  1636. }
  1637. if (strlen(tempBuff) > blockNameLength) {
  1638. restoreChar = tempBuff[blockNameLength];
  1639. tempBuff[blockNameLength] = 0;
  1640. if (stricmp(blockName.str(), tempBuff) != 0) {
  1641. retVal = false;
  1642. }
  1643. tempBuff[blockNameLength] = restoreChar;
  1644. tempBuff = tempBuff + blockNameLength;
  1645. } else {
  1646. retVal = false;
  1647. }
  1648. while (strlen(tempBuff)) {
  1649. retVal = retVal && isspace(tempBuff[0]);
  1650. ++tempBuff;
  1651. }
  1652. return retVal;
  1653. }
  1654. //-------------------------------------------------------------------------------------------------
  1655. // parse the line and return whether the given line is a Block declaration of the form
  1656. // [whitespace] end [EOL]
  1657. Bool INI::isEndOfBlock( char *bufferToCheck )
  1658. {
  1659. Bool retVal = true;
  1660. if (!bufferToCheck) {
  1661. return false;
  1662. }
  1663. // DO NOT RETURN EARLY FROM THIS FUNCTION (beyond this point)
  1664. // we have to restore the bufferToCheck to its previous state before returning, so
  1665. // it is important to get through all the checks.
  1666. static const char* endString = "End";
  1667. int endStringLength = strlen(endString);
  1668. char restoreChar;
  1669. char *tempBuff = bufferToCheck;
  1670. while (isspace(*tempBuff)) {
  1671. ++tempBuff;
  1672. }
  1673. if (strlen(tempBuff) > endStringLength) {
  1674. restoreChar = tempBuff[endStringLength];
  1675. tempBuff[endStringLength] = 0;
  1676. if (stricmp(endString, tempBuff) != 0) {
  1677. retVal = false;
  1678. }
  1679. tempBuff[endStringLength] = restoreChar;
  1680. tempBuff = tempBuff + endStringLength;
  1681. } else {
  1682. retVal = false;
  1683. }
  1684. while (strlen(tempBuff)) {
  1685. retVal = retVal && isspace(tempBuff[0]);
  1686. ++tempBuff;
  1687. }
  1688. return retVal;
  1689. }