409 lines
20 KiB
C++

/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INI.h ////////////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: INI Reader
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef __INI_H_
#define __INI_H_
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stddef.h> // for offsetof, which we don't use but everyone who includes us does
#include "Common/STLTypedefs.h"
#include "Common/AsciiString.h"
#include "Common/GameCommon.h"
//-------------------------------------------------------------------------------------------------
class INI;
class Xfer;
class File;
enum ScienceType;
//-------------------------------------------------------------------------------------------------
/** These control the behavior of loading the INI data into items */
//-------------------------------------------------------------------------------------------------
enum INILoadType
{
INI_LOAD_INVALID, ///< invalid load type
INI_LOAD_OVERWRITE, ///< create new or load *over* existing data instance
INI_LOAD_CREATE_OVERRIDES, ///< create new or load into *new* override data instance
INI_LOAD_MULTIFILE ///< create new or continue loading into existing data instance.
};
//-------------------------------------------------------------------------------------------------
/** INI constant defines */
//-------------------------------------------------------------------------------------------------
enum
{
INI_MAX_CHARS_PER_LINE = 1028, ///< max characters per line entry in any ini file
};
//-------------------------------------------------------------------------------------------------
/** Status return codes for the INI reader */
//-------------------------------------------------------------------------------------------------
enum
{
// we map all of these to the same "real" error code, because
// we generally don't care why it failed; but since the code distinguishes,
// I didn't want to wipe out that intelligence. if we ever need to distinguish
// failure modes at runtime, just put in real values for these.
INI_CANT_SEARCH_DIR = ERROR_BAD_INI,
INI_INVALID_DIRECTORY = ERROR_BAD_INI,
INI_INVALID_PARAMS = ERROR_BAD_INI,
INI_INVALID_NAME_LIST = ERROR_BAD_INI,
INI_INVALID_DATA = ERROR_BAD_INI,
INI_MISSING_END_TOKEN = ERROR_BAD_INI,
INI_UNKNOWN_TOKEN = ERROR_BAD_INI,
INI_BUFFER_TOO_SMALL = ERROR_BAD_INI,
INI_FILE_NOT_OPEN = ERROR_BAD_INI,
INI_FILE_ALREADY_OPEN = ERROR_BAD_INI,
INI_CANT_OPEN_FILE = ERROR_BAD_INI,
INI_UNKNOWN_ERROR = ERROR_BAD_INI,
INI_END_OF_FILE = ERROR_BAD_INI
};
//-------------------------------------------------------------------------------------------------
/** Function typedef for parsing data block fields.
*
* buffer - the character buffer of the line from INI that we are reading and parsing
* instance - instance of what we're loading (for example a thingtemplate instance)
* store - where to store the data parsed, this is a field in the *instance* 'instance'
*/
//-------------------------------------------------------------------------------------------------
typedef void (*INIFieldParseProc)( INI *ini, void *instance, void *store, const void* userData );
//-------------------------------------------------------------------------------------------------
typedef const char* ConstCharPtr;
typedef const ConstCharPtr* ConstCharPtrArray;
//-------------------------------------------------------------------------------------------------
struct LookupListRec
{
const char* name;
Int value;
};
typedef const LookupListRec *ConstLookupListRecArray;
//-------------------------------------------------------------------------------------------------
/** Parse tables for all fields of each data block are created using these */
//-------------------------------------------------------------------------------------------------
struct FieldParse
{
const char* token; ///< token of the field
INIFieldParseProc parse; ///< the parse function
const void* userData; ///< field-specific data
Int offset; ///< offset to data field
inline void set(const char* t, INIFieldParseProc p, const void* u, Int o)
{
token = t;
parse = p;
userData = u;
offset = o;
}
};
//-------------------------------------------------------------------------------------------------
class MultiIniFieldParse
{
private:
enum { MAX_MULTI_FIELDS = 16 };
const FieldParse* m_fieldParse[MAX_MULTI_FIELDS];
UnsignedInt m_extraOffset[MAX_MULTI_FIELDS];
Int m_count;
public:
MultiIniFieldParse() : m_count(0)
{
//Added By Sadullah Nader
//Initializations missing and needed
for(Int i = 0; i < MAX_MULTI_FIELDS; i++)
m_extraOffset[i] = 0;
//
}
void add(const FieldParse* f, UnsignedInt e = 0);
inline Int getCount() const { return m_count; }
inline const FieldParse* getNthFieldParse(Int i) const { return m_fieldParse[i]; }
inline UnsignedInt getNthExtraOffset(Int i) const { return m_extraOffset[i]; }
};
//-------------------------------------------------------------------------------------------------
/** Function typedef for parsing INI types blocks */
//-------------------------------------------------------------------------------------------------
typedef void (*INIBlockParse)( INI *ini );
typedef void (*BuildMultiIniFieldProc)(MultiIniFieldParse& p);
//-------------------------------------------------------------------------------------------------
/** INI Reader interface */
//-------------------------------------------------------------------------------------------------
class INI
{
public:
INI();
~INI();
void loadDirectory( AsciiString dirName, Bool subdirs, INILoadType loadType, Xfer *pXfer ); ///< load directory of INI files
void load( AsciiString filename, INILoadType loadType, Xfer *pXfer ); ///< load INI file
static Bool isDeclarationOfType( AsciiString blockType, AsciiString blockName, char *bufferToCheck );
static Bool isEndOfBlock( char *bufferToCheck );
// data type parsing (the highest level of what type of thing we're parsing)
static void parseObjectDefinition( INI *ini );
static void parseObjectReskinDefinition( INI *ini );
static void parseWeaponTemplateDefinition( INI *ini );
static void parseScienceDefinition( INI *ini );
static void parseRankDefinition( INI *ini );
static void parseCrateTemplateDefinition( INI *ini );
static void parseLocomotorTemplateDefinition( INI *ini );
static void parseLanguageDefinition( INI *ini );
static void parsePlayerTemplateDefinition( INI *ini );
static void parseGameDataDefinition( INI *ini );
static void parseMapDataDefinition( INI *ini );
static void parseAnim2DDefinition( INI *ini );
static void parseAudioEventDefinition( INI *ini );
static void parseDialogDefinition( INI *ini );
static void parseMusicTrackDefinition( INI *ini );
static void parseWebpageURLDefinition( INI *ini );
static void parseHeaderTemplateDefinition( INI *ini );
static void parseParticleSystemDefinition( INI *ini );
static void parseWaterSettingDefinition( INI *ini );
static void parseWaterTransparencyDefinition( INI *ini );
static void parseMappedImageDefinition( INI *ini );
static void parseArmorDefinition( INI *ini );
static void parseDamageFXDefinition( INI *ini );
static void parseDrawGroupNumberDefinition( INI *ini );
static void parseTerrainDefinition( INI *ini );
static void parseTerrainRoadDefinition( INI *ini );
static void parseTerrainBridgeDefinition( INI *ini );
static void parseMetaMapDefinition( INI *ini );
static void parseFXListDefinition( INI *ini );
static void parseObjectCreationListDefinition( INI* ini );
static void parseMultiplayerSettingsDefinition( INI* ini );
static void parseMultiplayerColorDefinition( INI* ini );
static void parseOnlineChatColorDefinition( INI* ini );
static void parseMapCacheDefinition( INI* ini );
static void parseVideoDefinition( INI* ini );
static void parseCommandButtonDefinition( INI *ini );
static void parseCommandSetDefinition( INI *ini );
static void parseUpgradeDefinition( INI *ini );
static void parseMouseDefinition( INI* ini );
static void parseMouseCursorDefinition( INI* ini );
static void parseAIDataDefinition( INI *ini );
static void parseSpecialPowerDefinition( INI *ini );
static void parseInGameUIDefinition( INI *ini );
static void parseControlBarSchemeDefinition( INI *ini );
static void parseControlBarResizerDefinition( INI *ini );
static void parseShellMenuSchemeDefinition( INI *ini );
static void parseCampaignDefinition( INI *ini );
static void parseAudioSettingsDefinition( INI *ini );
static void parseMiscAudio( INI *ini );
static void parseStaticGameLODDefinition( INI *ini);
static void parseDynamicGameLODDefinition( INI *ini);
static void parseStaticGameLODLevel( INI* ini, void * , void *store, const void*);
static void parseDynamicGameLODLevel( INI* ini, void * , void *store, const void*);
static void parseLODPreset( INI* ini);
static void parseBenchProfile( INI* ini);
static void parseEvaEvent( INI* ini );
static void parseCredits( INI* ini );
static void parseWindowTransitions( INI* ini );
inline AsciiString getFilename( void ) const { return m_filename; }
inline INILoadType getLoadType( void ) const { return m_loadType; }
inline UnsignedInt getLineNum( void ) const { return m_lineNum; }
inline const char *getSeps( void ) const { return m_seps; }
inline const char *getSepsPercent( void ) const { return m_sepsPercent; }
inline const char *getSepsColon( void ) const { return m_sepsColon; }
inline const char *getSepsQuote( void ) { return m_sepsQuote; }
inline Bool isEOF( void ) const { return m_endOfFile; }
void initFromINI( void *what, const FieldParse* parseTable );
void initFromINIMulti( void *what, const MultiIniFieldParse& parseTableList );
void initFromINIMultiProc( void *what, BuildMultiIniFieldProc proc );
static void parseUnsignedByte( INI *ini, void *instance, void *store, const void* userData );
static void parseShort( INI *ini, void *instance, void *store, const void* userData );
static void parseUnsignedShort( INI *ini, void *instance, void *store, const void* userData );
static void parseInt( INI *ini, void *instance, void *store, const void* userData );
static void parseUnsignedInt( INI *ini, void *instance, void *store, const void* userData );
static void parseReal( INI *ini, void *instance, void *store, const void* userData );
static void parsePositiveNonZeroReal( INI *ini, void *instance, void *store, const void* userData );
static void parseBool( INI *ini, void *instance, void *store, const void* userData );
static void parseBitInInt32( INI *ini, void *instance, void *store, const void* userData );
static void parseAsciiString( INI *ini, void *instance, void *store, const void* userData );
static void parseQuotedAsciiString( INI *ini, void *instance, void *store, const void* userData );
static void parseAsciiStringVector( INI *ini, void *instance, void *store, const void* userData );
static void parseAsciiStringVectorAppend( INI *ini, void *instance, void *store, const void* userData );
static void parseAndTranslateLabel( INI *ini, void *instance, void *store, const void* userData );
static void parseMappedImage( INI *ini, void *instance, void *store, const void *userData );
static void parseAnim2DTemplate( INI *ini, void *instance, void *store, const void *userData );
static void parsePercentToReal( INI *ini, void *instance, void *store, const void* userData );
static void parseRGBColor( INI *ini, void *instance, void *store, const void* userData );
static void parseRGBAColorInt( INI *ini, void *instance, void *store, const void* userData );
static void parseColorInt( INI *ini, void *instance, void *store, const void* userData );
static void parseCoord3D( INI *ini, void *instance, void *store, const void* userData );
static void parseCoord2D( INI *ini, void *instance, void *store, const void *userData );
static void parseICoord2D( INI *ini, void *instance, void *store, const void *userData );
static void parseDynamicAudioEventRTS( INI *ini, void *instance, void *store, const void* userData );
static void parseAudioEventRTS( INI *ini, void *instance, void *store, const void* userData );
static void parseFXList( INI *ini, void *instance, void *store, const void* userData );
static void parseParticleSystemTemplate( INI *ini, void *instance, void *store, const void *userData );
static void parseObjectCreationList( INI *ini, void *instance, void *store, const void* userData );
static void parseSpecialPowerTemplate( INI *ini, void *instance, void *store, const void *userData );
static void parseUpgradeTemplate( INI *ini, void *instance, void *store, const void *userData );
static void parseScience( INI *ini, void *instance, void *store, const void *userData );
static void parseScienceVector( INI *ini, void *instance, void *store, const void *userData );
static void parseGameClientRandomVariable( INI* ini, void *instance, void *store, const void* userData );
static void parseBitString8( INI *ini, void *instance, void *store, const void* userData );
static void parseBitString32( INI *ini, void *instance, void *store, const void* userData );
static void parseByteSizedIndexList( INI *ini, void *instance, void *store, const void* userData );
static void parseIndexList( INI *ini, void *instance, void *store, const void* userData );
static void parseLookupList( INI *ini, void *instance, void *store, const void* userData );
static void parseThingTemplate( INI *ini, void *instance, void *store, const void* userData );
static void parseArmorTemplate( INI *ini, void *instance, void *store, const void* userData );
static void parseDamageFX( INI *ini, void *instance, void *store, const void* userData );
static void parseWeaponTemplate( INI *ini, void *instance, void *store, const void* userData );
// parse a duration in msec and convert to duration in frames
static void parseDurationReal( INI *ini, void *instance, void *store, const void* userData );
// parse a duration in msec and convert to duration in integral number of frames, (unsignedint) rounding UP
static void parseDurationUnsignedInt( INI *ini, void *instance, void *store, const void* userData );
static void parseDurationUnsignedShort( INI *ini, void *instance, void *store, const void *userData );
// parse acceleration in (dist/sec) and convert to (dist/frame)
static void parseVelocityReal( INI *ini, void *instance, void *store, const void* userData );
// parse acceleration in (dist/sec^2) and convert to (dist/frame^2)
static void parseAccelerationReal( INI *ini, void *instance, void *store, const void* userData );
// parse angle in degrees and convert to radians
static void parseAngleReal( INI *ini, void *instance, void *store, const void *userData );
// note that this parses in degrees/sec, and converts to rads/frame!
static void parseAngularVelocityReal( INI *ini, void *instance, void *store, const void *userData );
static void parseDamageTypeFlags(INI* ini, void* instance, void* store, const void* userData);
static void parseDeathTypeFlags(INI* ini, void* instance, void* store, const void* userData);
static void parseVeterancyLevelFlags(INI* ini, void* instance, void* store, const void* userData);
static void parseSoundsList( INI* ini, void *instance, void *store, const void* /*userData*/ );
/**
return the next token. if seps is null (or omitted), the standard seps are used.
this will *never* return null; if there are no more tokens, an exception will be thrown.
*/
const char* getNextToken(const char* seps = NULL);
/**
just like getNextToken(), except that null is returned if no more tokens are present
(rather than throwing an exception). usually you should call getNextToken(),
but for some cases this is handier (ie, parsing a variable-length number of tokens).
*/
const char* getNextTokenOrNull(const char* seps = NULL);
/**
This is called when the next thing you expect is something like:
Tag:value
pass "Tag" (without the colon) for 'expected', and you will have the 'value'
token returned.
If "Tag" is not the next token, an error is thrown.
*/
const char* getNextSubToken(const char* expected);
/**
return the next ascii string. this is usually the same the result of getNextToken(),
except that it allows for quote-delimited strings (eg, "foo bar"), so you can
get strings with spaces, and/or empty strings.
*/
AsciiString getNextAsciiString();
AsciiString getNextQuotedAsciiString(); //fixed version of above. We can't fix the regular one for fear of breaking existing code. :-(
/**
utility routine that does a sscanf() on the string to get the Science, and throws
an exception if not of the right form.
*/
static ScienceType scanScience(const char* token);
/**
utility routine that does a sscanf() on the string to get the int, and throws
an exception if not of the right form.
*/
static Int scanInt(const char* token);
/**
utility routine that does a sscanf() on the string to get the unsigned int, and throws
an exception if not of the right form.
*/
static UnsignedInt scanUnsignedInt(const char* token);
/**
utility routine that does a sscanf() on the string to get the real, and throws
an exception if not of the right form.
*/
static Real scanReal(const char* token);
static Real scanPercentToReal(const char* token);
static Int scanIndexList(const char* token, ConstCharPtrArray nameList);
static Int scanLookupList(const char* token, ConstLookupListRecArray lookupList);
static Bool scanBool(const char* token);
protected:
static Bool isValidINIFilename( const char *filename ); ///< is this a valid .ini filename
void prepFile( AsciiString filename, INILoadType loadType );
void unPrepFile();
void readLine( void );
// FILE *m_file; ///< file pointer of file currently loading
File *m_file; ///< file pointer of file currently loading
AsciiString m_filename; ///< filename of file currently loading
INILoadType m_loadType; ///< load time for current file
UnsignedInt m_lineNum; ///< current line number that's been read
char m_buffer[ INI_MAX_CHARS_PER_LINE ]; ///< buffer to read file contents into
const char *m_seps; ///< for strtok parsing
const char *m_sepsPercent; ///< m_seps with percent delimiter as well
const char *m_sepsColon; ///< m_seps with colon delimiter as well
const char *m_sepsQuote; ///< token to represent a quoted ascii string
const char *m_blockEndToken; ///< token to represent end of data block
Bool m_endOfFile; ///< TRUE when we've hit EOF
#if defined(_DEBUG) || defined(_INTERNAL)
char m_curBlockStart[ INI_MAX_CHARS_PER_LINE ]; ///< first line of cur block
#endif
};
#endif // __INI_H_