1.1 --- a/core/GameConfigs.cpp Mon Jan 12 01:32:53 2009 -0500
1.2 +++ b/core/GameConfigs.cpp Mon Jan 12 02:28:22 2009 -0500
1.3 @@ -90,9 +90,37 @@
1.4 unsigned int s_ServerBinCRC;
1.5 bool s_ServerBinCRC_Ok = false;
1.6
1.7 +static bool DoesGameMatch(const char *value)
1.8 +{
1.9 + if (strcmp(value, g_Game) == 0 ||
1.10 + strcmp(value, g_GameDesc) == 0 ||
1.11 + strcmp(value, g_GameName) == 0)
1.12 + {
1.13 + return true;
1.14 + }
1.15 + return false;
1.16 +}
1.17 +
1.18 +static bool DoesEngineMatch(const char *value)
1.19 +{
1.20 +#if SOURCE_ENGINE == SE_EPISODEONE
1.21 + if (strcmp(value, "original") == 0)
1.22 +#elif SOURCE_ENGINE == SE_ORANGEBOX
1.23 + if (strcmp(value, "orangebox") == 0)
1.24 +#elif SOURCE_ENGINE == SE_LEFT4DEAD
1.25 + if (strcmp(value, "left4dead") == 0)
1.26 +#else
1.27 +#error "Unknown engine type"
1.28 +#endif
1.29 + {
1.30 + return true;
1.31 + }
1.32 + return false;
1.33 +}
1.34 +
1.35 CGameConfig::CGameConfig(const char *file)
1.36 {
1.37 - m_pFile = sm_strdup(file);
1.38 + strncopy(m_File, file, sizeof(m_File));
1.39 m_pOffsets = sm_trie_create();
1.40 m_pProps = sm_trie_create();
1.41 m_pKeys = sm_trie_create();
1.42 @@ -110,7 +138,6 @@
1.43 sm_trie_destroy(m_pProps);
1.44 sm_trie_destroy(m_pKeys);
1.45 sm_trie_destroy(m_pSigs);
1.46 - delete [] m_pFile;
1.47 delete m_pStrings;
1.48 }
1.49
1.50 @@ -136,11 +163,9 @@
1.51 }
1.52 case PSTATE_GAMES:
1.53 {
1.54 - if ((strcmp(name, "*") == 0)
1.55 - || (strcmp(name, "#default") == 0)
1.56 - || (strcasecmp(name, g_Game) == 0)
1.57 - || (strcasecmp(name, g_GameDesc) == 0)
1.58 - || (strcasecmp(name, g_GameName) == 0))
1.59 + if (strcmp(name, "*") == 0 ||
1.60 + strcmp(name, "#default") == 0 ||
1.61 + DoesGameMatch(name))
1.62 {
1.63 bShouldBeReadingDefault = true;
1.64 m_ParseState = PSTATE_GAMEDEFS;
1.65 @@ -160,12 +185,15 @@
1.66 {
1.67 m_ParseState = PSTATE_GAMEDEFS_KEYS;
1.68 }
1.69 - else if ((strcmp(name, "#supported") == 0)
1.70 - && (strcmp(m_Game, "#default") == 0))
1.71 + else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
1.72 {
1.73 m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
1.74 /* Ignore this section unless we get a game. */
1.75 bShouldBeReadingDefault = false;
1.76 + had_game = false;
1.77 + matched_game = false;
1.78 + had_engine = false;
1.79 + matched_engine = false;
1.80 }
1.81 else if (strcmp(name, "Signatures") == 0)
1.82 {
1.83 @@ -244,7 +272,7 @@
1.84 if (error[0] != '\0')
1.85 {
1.86 m_IgnoreLevel = 1;
1.87 - g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_pFile);
1.88 + g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
1.89 g_Logger.LogError("[SM] %s", error);
1.90 } else {
1.91 m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
1.92 @@ -297,13 +325,29 @@
1.93 int id = m_pStrings->AddString(value);
1.94 sm_trie_replace(m_pKeys, key, (void *)id);
1.95 } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
1.96 - if (strcmp(key, "game") == 0
1.97 - && (strcasecmp(value, g_Game) == 0
1.98 - || strcasecmp(value, g_GameDesc) == 0
1.99 - || strcasecmp(value, g_GameName) == 0)
1.100 - )
1.101 + if (strcmp(key, "game") == 0)
1.102 {
1.103 - bShouldBeReadingDefault = true;
1.104 + had_game = true;
1.105 + if (DoesGameMatch(value))
1.106 + {
1.107 + matched_game = true;
1.108 + }
1.109 + if ((!had_engine && matched_game) || (matched_engine && matched_game))
1.110 + {
1.111 + bShouldBeReadingDefault = true;
1.112 + }
1.113 + }
1.114 + else if (strcmp(key, "engine") == 0)
1.115 + {
1.116 + had_engine = true;
1.117 + if (DoesEngineMatch(value))
1.118 + {
1.119 + matched_engine = true;
1.120 + }
1.121 + if ((!had_game && matched_engine) || (matched_game && matched_engine))
1.122 + {
1.123 + bShouldBeReadingDefault = true;
1.124 + }
1.125 }
1.126 } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
1.127 if (strcmp(key, PLATFORM_NAME) == 0)
1.128 @@ -390,7 +434,7 @@
1.129 g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
1.130 m_Class,
1.131 m_Prop,
1.132 - m_pFile,
1.133 + m_CurFile,
1.134 m_Game);
1.135 }
1.136 }
1.137 @@ -440,7 +484,7 @@
1.138 {
1.139 g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")",
1.140 s_TempSig.library,
1.141 - m_pFile);
1.142 + m_CurFile);
1.143 } else {
1.144 #if defined PLATFORM_LINUX
1.145 if (s_TempSig.sig[0] == '@')
1.146 @@ -457,12 +501,12 @@
1.147 } else {
1.148 g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
1.149 s_TempSig.library,
1.150 - m_pFile);
1.151 + m_File);
1.152 }
1.153 } else {
1.154 g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
1.155 s_TempSig.library,
1.156 - m_pFile);
1.157 + m_File);
1.158 }
1.159 }
1.160 if (final_addr)
1.161 @@ -526,37 +570,206 @@
1.162 return SMCResult_Continue;
1.163 }
1.164
1.165 +#define MSTATE_NONE 0
1.166 +#define MSTATE_MAIN 1
1.167 +#define MSTATE_FILE 2
1.168 +
1.169 +class MasterReader : public ITextListener_SMC
1.170 +{
1.171 +public:
1.172 + virtual void ReadSMC_ParseStart()
1.173 + {
1.174 + state = MSTATE_NONE;
1.175 + ignoreLevel = 0;
1.176 + }
1.177 +
1.178 + virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
1.179 + {
1.180 + if (ignoreLevel)
1.181 + {
1.182 + return SMCResult_Continue;
1.183 + }
1.184 +
1.185 + if (state == MSTATE_NONE)
1.186 + {
1.187 + if (strcmp(name, "Game Master") == 0)
1.188 + {
1.189 + state = MSTATE_MAIN;
1.190 + }
1.191 + else
1.192 + {
1.193 + ignoreLevel++;
1.194 + }
1.195 + }
1.196 + else if (state == MSTATE_MAIN)
1.197 + {
1.198 + strncopy(cur_file, name, sizeof(cur_file));
1.199 + had_engine = false;
1.200 + matched_engine = false;
1.201 + had_game = false;
1.202 + matched_game = false;
1.203 + state = MSTATE_FILE;
1.204 + }
1.205 + else if (state == MSTATE_FILE)
1.206 + {
1.207 + ignoreLevel++;
1.208 + }
1.209 +
1.210 + return SMCResult_Continue;
1.211 + }
1.212 +
1.213 + virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.214 + {
1.215 + if (ignoreLevel || state != MSTATE_FILE)
1.216 + {
1.217 + return SMCResult_Continue;
1.218 + }
1.219 +
1.220 + if (strcmp(key, "engine") == 0)
1.221 + {
1.222 + had_engine = true;
1.223 + if (DoesEngineMatch(value))
1.224 + {
1.225 + matched_engine = true;
1.226 + }
1.227 + }
1.228 + else if (strcmp(key, "game") == 0)
1.229 + {
1.230 + had_game = true;
1.231 + if (DoesGameMatch(value))
1.232 + {
1.233 + matched_game = true;
1.234 + }
1.235 + }
1.236 +
1.237 + return SMCResult_Continue;
1.238 + }
1.239 +
1.240 + virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
1.241 + {
1.242 + if (ignoreLevel)
1.243 + {
1.244 + ignoreLevel--;
1.245 + return SMCResult_Continue;
1.246 + }
1.247 +
1.248 + if (state == MSTATE_FILE)
1.249 + {
1.250 + /* The four success conditions:
1.251 + * 1. Needed nothing.
1.252 + * 2. Needed game only.
1.253 + * 3. Needed engine only.
1.254 + * 4. Needed both engine and game.
1.255 + * Final result is minimized via k-map.
1.256 + */
1.257 +#if 0
1.258 + if ((!had_engine && !had_game) ||
1.259 + (!had_engine && (had_game && matched_game)) ||
1.260 + (!had_game && (had_engine && matched_engine)) ||
1.261 + ((had_game && had_engine) && (matched_game && matched_engine)))
1.262 +#endif
1.263 + if ((!had_engine && !had_game) ||
1.264 + (!had_engine && matched_game) ||
1.265 + (!had_game && matched_engine) ||
1.266 + (matched_engine && matched_game))
1.267 + {
1.268 + fileList->push_back(cur_file);
1.269 + }
1.270 + state = MSTATE_MAIN;
1.271 + }
1.272 + else if (state == MSTATE_MAIN)
1.273 + {
1.274 + state = MSTATE_NONE;
1.275 + }
1.276 +
1.277 + return SMCResult_Continue;
1.278 + }
1.279 +public:
1.280 + List<String> *fileList;
1.281 + unsigned int state;
1.282 + unsigned int ignoreLevel;
1.283 + char cur_file[PLATFORM_MAX_PATH];
1.284 + bool had_engine;
1.285 + bool matched_engine;
1.286 + bool had_game;
1.287 + bool matched_game;
1.288 +};
1.289 +
1.290 +static MasterReader master_reader;
1.291 +
1.292 bool CGameConfig::Reparse(char *error, size_t maxlength)
1.293 {
1.294 - SMCError err;
1.295 - SMCStates state = {0, 0};
1.296 -
1.297 - char path[PLATFORM_MAX_PATH];
1.298 - g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile);
1.299 - /* Backwards compatibility */
1.300 - if (!g_LibSys.PathExists(path))
1.301 - {
1.302 - g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile);
1.303 - }
1.304 -
1.305 - /* Initialize parse states */
1.306 - m_IgnoreLevel = 0;
1.307 - bShouldBeReadingDefault = true;
1.308 - m_ParseState = PSTATE_NONE;
1.309 /* Reset cached data */
1.310 m_pStrings->Reset();
1.311 sm_trie_clear(m_pOffsets);
1.312 sm_trie_clear(m_pProps);
1.313 sm_trie_clear(m_pKeys);
1.314
1.315 - if ((err=textparsers->ParseSMCFile(path, this, &state, error, maxlength))
1.316 + char path[PLATFORM_MAX_PATH];
1.317 +
1.318 + /* See if we can use the extended gamedata format. */
1.319 + g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
1.320 + if (!g_LibSys.PathExists(path))
1.321 + {
1.322 + /* Nope, use the old mechanism. */
1.323 + UTIL_Format(path, sizeof(path), "%s.txt", m_File);
1.324 + return EnterFile(path, error, maxlength);
1.325 + }
1.326 +
1.327 + /* Otherwise, it's time to parse the master. */
1.328 + SMCError err;
1.329 + SMCStates state = {0, 0};
1.330 + List<String> fileList;
1.331 + master_reader.fileList = &fileList;
1.332 +
1.333 + err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
1.334 + if (err != SMCError_Okay)
1.335 + {
1.336 + const char *msg = textparsers->GetSMCErrorString(err);
1.337 +
1.338 + g_Logger.LogError("[SM] Error parsing master gameconf file \"%s\":", path);
1.339 + g_Logger.LogError("[SM] Error %d on line %d, col %d: %s",
1.340 + err,
1.341 + state.line,
1.342 + state.col,
1.343 + msg ? msg : "Unknown error");
1.344 + return false;
1.345 + }
1.346 +
1.347 + /* Go through each file we found and parse it. */
1.348 + List<String>::iterator iter;
1.349 + for (iter = fileList.begin(); iter != fileList.end(); iter++)
1.350 + {
1.351 + UTIL_Format(path, sizeof(path), "%s/%s", m_File, (*iter).c_str());
1.352 + if (!EnterFile(path, error, maxlength))
1.353 + {
1.354 + return false;
1.355 + }
1.356 + }
1.357 +
1.358 + return true;
1.359 +}
1.360 +
1.361 +bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
1.362 +{
1.363 + SMCError err;
1.364 + SMCStates state = {0, 0};
1.365 +
1.366 + g_SourceMod.BuildPath(Path_SM, m_CurFile, sizeof(m_CurFile), "gamedata/%s", file);
1.367 +
1.368 + /* Initialize parse states */
1.369 + m_IgnoreLevel = 0;
1.370 + bShouldBeReadingDefault = true;
1.371 + m_ParseState = PSTATE_NONE;
1.372 +
1.373 + if ((err=textparsers->ParseSMCFile(m_CurFile, this, &state, error, maxlength))
1.374 != SMCError_Okay)
1.375 {
1.376 const char *msg;
1.377
1.378 msg = textparsers->GetSMCErrorString(err);
1.379
1.380 - g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", path);
1.381 + g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", m_CurFile);
1.382 g_Logger.LogError("[SM] Error %d on line %d, col %d: %s",
1.383 err,
1.384 state.line,
1.385 @@ -722,7 +935,7 @@
1.386
1.387 if (pConfig->DecRefCount() == 0)
1.388 {
1.389 - sm_trie_delete(m_pLookup, pConfig->m_pFile);
1.390 + sm_trie_delete(m_pLookup, pConfig->m_File);
1.391 delete pConfig;
1.392 }
1.393 }