Bazaar developer reference

CvDllDatabaseUtility::ValidateGameDatabase()

Defined in
CvDllDatabaseUtility.cpp, line 450
Class
CvDllDatabaseUtility
Hook name
CivHooks::CvDllDatabaseUtility_ValidateGameDatabase

Hooks

static bool BeforeCvDllDatabaseUtility_ValidateGameDatabase(CvDllDatabaseUtility* self, bool& result);static void AfterCvDllDatabaseUtility_ValidateGameDatabase(CvDllDatabaseUtility* self, bool& result);static bool ReplaceCvDllDatabaseUtility_ValidateGameDatabase(bool (*original)(CvDllDatabaseUtility*), CvDllDatabaseUtility* self);// CivHooks.h gives this hook point no types, so nothing checks these hooks' parameters.host->RegisterHook(host->pluginIndex, CivHooks::CvDllDatabaseUtility_ValidateGameDatabase::ID, CIV_HOOK_BEFORE, (void*)&BeforeCvDllDatabaseUtility_ValidateGameDatabase);host->RegisterHook(host->pluginIndex, CivHooks::CvDllDatabaseUtility_ValidateGameDatabase::ID, CIV_HOOK_AFTER, (void*)&AfterCvDllDatabaseUtility_ValidateGameDatabase);host->RegisterHook(host->pluginIndex, CivHooks::CvDllDatabaseUtility_ValidateGameDatabase::ID, CIV_HOOK_REPLACE, (void*)&ReplaceCvDllDatabaseUtility_ValidateGameDatabase);

Code

450bool CvDllDatabaseUtility::ValidateGameDatabase()451{452	//This function contains a suite of useful game database unit tests that will453	//ensure the database conforms to certain unique rules of Civ5.454	if(!gDLL->ShouldValidateGameDatabase())455	{456		LogMsg("*** SKIPPING Game Database Validation. ****");457		LogMsg("You can enable it by setting 'ValidateGameDatabase = 1' in config.ini");458		return true;459	}460461	cvStopWatch kPerfTest("Validating Game Database", "xml-perf.log");462	bool bError = false;463464	LogMsg("**** Validating Game Database *****");465466	//Test that all Tables w/ 'ID' column start at 0 and not 1.467	{468		cvStopWatch watch("Ensure All Tables with 'ID' column start at 0");469		Database::Results kTables("name");470		if(DB.SelectAt(kTables, "sqlite_master", "type", "table"))471		{472			while(kTables.Step())473			{474				const char* szTableName = kTables.GetText(0);475				if(DB.Count(szTableName) > 0)476				{477					//Test if table has 'ID' column478					bool bHasIDColumn = false;479					{480						//Execute "select ID from <table_name> limit 1;481						//If there's a SQL error, it's most likely due to a lack of an 'id' column.482						char szSQL[512];483						sprintf_s(szSQL, "pragma table_info(%s)", szTableName);484						Database::Results kResults;485						DB.Execute(kResults, szSQL);486						while(kResults.Step())487						{488							const char* szName = kResults.GetText("name");489							if(strcmp(szTableName, szName) == 0)490							{491								bHasIDColumn = true;492								break;493							}494495						}496					}497498					if(bHasIDColumn)499					{500						Database::SingleResult kTest;501						if(!DB.SelectAt(kTest, szTableName, "ID", 0))502						{503							//Table has 'ID' column and contains data but does not use ID 0.504							char szError[512];505							sprintf_s(szError, "Table '%s' contains 'ID' column that starts at 1 instead of 0.", szTableName);506							LogMsg(szError);507							bError = true;508						}509					}510511				}512			}513		}514	}515516	//Validate FK constraints517	{518		cvStopWatch watch("Validate FK Constraints");519		DB.ValidateFKConstraints();520	}521522	LogMsg("Performing Localization Checks");523	{524		cvStopWatch watch("Localization Checks");525		LogMsg("Checking Tag Format...");526		LogMsg("Note: Tags must only use [A-Z_] characters, start with 'TXT_KEY_', and be under 128 characters long.");527528		Database::Results kLanguages;529		if(DB.SelectAll(kLanguages, "Languages"))530		{531			while(kLanguages.Step())532			{533				const char* szTagColumnName = kLanguages.GetText("TagColumnName");534				const char* szTableName = kLanguages.GetText("TableName");535536				Database::Results kResults;537				char szSQL[512];538				sprintf_s(szSQL, "SELECT DISTINCT %s from %s", szTagColumnName, szTableName);539				if(DB.Execute(kResults, szSQL))540				{541					bool bFirst = true;542					while(kResults.Step())543					{544						const char* szTag = kResults.GetText(0);545						const size_t len = strlen(szTag);546						if(len > 128)547						{548							if(bFirst)549							{550								LogMsg("In table %s...", szTableName);551								bFirst = false;552							}553554							bError =  true;555							LogMsg("Tag (%s) has size greater than 127 characters.", szTag);556						}557						else558						{559							if(strncmp("TXT_KEY_", szTag, 8) != 0)560							{561								if(bFirst)562								{563									LogMsg("In table %s...", szTableName);564									bFirst = false;565								}566567								bError = true;568								LogMsg("Tag (%s) does not start with 'TXT_KEY_'", szTag);569							}570571							for(size_t i = 0; i < len; ++i)572							{573								char ch = szTag[i];574								if(ch != '_' && !((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))575								{576									if(bFirst)577									{578										LogMsg("In table %s...", szTableName);579										bFirst = false;580									}581582									bError = true;583									LogMsg("Tag (%s) contains invalid character (%.1s)", szTag, &ch);584								}585							}586						}587					}588				}589			}590		}591	}592593	LogMsg("Validating UnitGameplay2DScripts");594	{595		cvStopWatch watch("Validating UnitGameplay2DScripts");596		Database::Results kResults;597		if(DB.Execute(kResults, "Select Type from Units where not exists (select 1 from UnitGameplay2DScripts where UnitType = Units.Type limit 1)"))598		{599			while(kResults.Step())600			{601				const char* szUnitType = kResults.GetText(0);602				LogMsg("Missing Entry for %s", szUnitType);603				bError = true;604			}605		}606	}607608	if(bError)609	{610		LogMsg("**** VALIDATION FAILED *****");611	}612	else613	{614		LogMsg("**** Validation Success *****");615	}616617	kPerfTest.EndPerfTest();618	LogMsg("Validation Took %f seconds", kPerfTest.GetDeltaInSeconds());619620	return !bError;621}

Calls

Called by