INI.cpp 66 KB

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