tools/installer/GamesList.cpp
author David Anderson <dvander@alliedmods.net>
Wed Feb 25 20:07:12 2009 -0500 (2009-02-25)
changeset 2607 f3b8bbe17f85
parent 2289 44045ead61cd
permissions -rw-r--r--
Backed out changeset fa286305bd3f
     1 #include "GamesList.h"
     2 #include "InstallerUtil.h"
     3 #include "InstallerMain.h"
     4 #include <stdio.h>
     5 
     6 game_database_t g_games = 
     7 {
     8 	NULL, 0, 
     9 	{NULL, 0, GAME_LIST_NO_GAMES}, 
    10 	{NULL, 0, GAME_LIST_NO_GAMES}, 
    11 	{NULL, 0, GAME_LIST_NO_GAMES}
    12 };
    13 
    14 valve_game_t valve_game_list[] = 
    15 {
    16 	{_T("counter-strike source"),		_T("cstrike"),	SOURCE_ENGINE_2004},
    17 	{_T("day of defeat source"),		_T("dod"),		SOURCE_ENGINE_2004},
    18 	{_T("half-life 2 deathmatch"),		_T("hl2mp"),	SOURCE_ENGINE_2004},
    19 	{_T("half-life deathmatch source"),	_T("hl1mp"),	SOURCE_ENGINE_2004},
    20 	{_T("team fortress 2"),				_T("tf"),		SOURCE_ENGINE_2007},
    21 	{NULL,								NULL,			0},
    22 };
    23 
    24 valve_game_t valve_server_list[] = 
    25 {
    26 	{_T("source dedicated server"),		NULL,			SOURCE_ENGINE_2004},
    27 	{_T("source 2007 dedicated server"), NULL,			SOURCE_ENGINE_2007},
    28 	{NULL,								NULL,			0},
    29 };
    30 
    31 int IsValidFolder(const TCHAR *path)
    32 {
    33 	DWORD attr;
    34 	TCHAR gameinfo_file[MAX_PATH];
    35 
    36 	UTIL_PathFormat(gameinfo_file, sizeof(gameinfo_file), _T("%s\\gameinfo.txt"), path);
    37 
    38 	if ((attr = GetFileAttributes(gameinfo_file)) == INVALID_FILE_ATTRIBUTES)
    39 	{
    40 		return GAMEINFO_DOES_NOT_EXIST;
    41 	}
    42 
    43 	if ((attr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
    44 	{
    45 		return GAMEINFO_IS_READ_ONLY;
    46 	}
    47 
    48 	if ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
    49 	{
    50 		return GAMEINFO_DOES_NOT_EXIST;
    51 	}
    52 
    53 	return GAMEINFO_IS_USABLE;
    54 }
    55 
    56 void DisplayBadFolderDialog(HWND hDlg, int reason)
    57 {
    58 	TCHAR message_string[255];
    59 	UINT resource;
    60 
    61 	if (reason == GAMEINFO_DOES_NOT_EXIST)
    62 	{
    63 		resource = IDS_NO_GAMEINFO;
    64 	}
    65 	else if (reason == GAMEINFO_IS_READ_ONLY)
    66 	{
    67 		resource = IDS_READONLY_GAMEINFO;
    68 	}
    69 	else
    70 	{
    71 		return;
    72 	}
    73 
    74 	if (LoadString(g_hInstance,
    75 		resource,
    76 		message_string,
    77 		sizeof(message_string) / sizeof(TCHAR)
    78 		) == 0)
    79 	{
    80 		return;
    81 	}
    82 
    83 	MessageBox(hDlg, 
    84 		message_string, 
    85 		_T("SourceMod Installer"),
    86 		MB_OK|MB_ICONWARNING);
    87 }
    88 
    89 game_list_t *MakeGameList(const TCHAR *name)
    90 {
    91 	game_list_t *gl = (game_list_t *)malloc(sizeof(game_list_t));
    92 
    93 	UTIL_Format(gl->root_name, 
    94 		sizeof(gl->root_name) / sizeof(TCHAR),
    95 		_T("%s"),
    96 		name);
    97 	gl->game_count = 0;
    98 	gl->games = NULL;
    99 
   100 	return gl;
   101 }
   102 
   103 void AttachGameListToGroup(game_group_t *group, game_list_t *gl)
   104 {
   105 	if (group->lists == NULL)
   106 	{
   107 		group->lists = (game_list_t **)malloc(sizeof(game_list_t *));
   108 	}
   109 	else
   110 	{
   111 		group->lists = (game_list_t **)realloc(group->lists,
   112 			sizeof(game_list_t *) * (group->list_count + 1));
   113 	}
   114 
   115 	group->lists[group->list_count] = gl;
   116 	group->list_count++;
   117 }
   118 
   119 void AttachModToGameList(game_list_t *gl, unsigned int mod_id)
   120 {
   121 	if (gl->games == NULL)
   122 	{
   123 		gl->games = (unsigned int *)malloc(sizeof(unsigned int));
   124 	}
   125 	else
   126 	{
   127 		gl->games = (unsigned int *)realloc(gl->games, 
   128 			sizeof(unsigned int) * (gl->game_count + 1));
   129 	}
   130 
   131 	gl->games[gl->game_count] = mod_id;
   132 	gl->game_count++;
   133 }
   134 
   135 unsigned int AddModToList(game_database_t *db, const game_info_t *mod_info)
   136 {
   137 	/* Check if a matching game already exists */
   138 	for (unsigned int i = 0; i < db->game_count; i++)
   139 	{
   140 		if (tstrcasecmp(mod_info->game_path, db->game_list[i].game_path) == 0)
   141 		{
   142 			return i;
   143 		}
   144 	}
   145 
   146 	if (db->game_list == NULL)
   147 	{
   148 		db->game_list = (game_info_t *)malloc(sizeof(game_info_t));
   149 	}
   150 	else
   151 	{
   152 		db->game_list = (game_info_t *)realloc(db->game_list, 
   153 			sizeof(game_info_t) * (db->game_count + 1));
   154 	}
   155 
   156 	memcpy(&db->game_list[db->game_count], mod_info, sizeof(game_info_t));
   157 	db->game_count++;
   158 
   159 	return db->game_count - 1;
   160 }
   161 
   162 bool TryToAddMod(const TCHAR *path, int eng_type, game_database_t *db, unsigned int *id)
   163 {
   164 	FILE *fp;
   165 	TCHAR gameinfo_path[MAX_PATH];
   166 
   167 	UTIL_PathFormat(gameinfo_path,
   168 		sizeof(gameinfo_path),
   169 		_T("%s\\gameinfo.txt"),
   170 		path);
   171 
   172 	if ((fp = _tfopen(gameinfo_path, _T("rt"))) == NULL)
   173 	{
   174 		return false;
   175 	}
   176 
   177 	int pos;
   178 	char buffer[512];
   179 	char key[256], value[256];
   180 	while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL)
   181 	{
   182 		if ((pos = BreakStringA(buffer, key, sizeof(key))) == -1)
   183 		{
   184 			continue;
   185 		}
   186 		if ((pos = BreakStringA(&buffer[pos], value, sizeof(value))) == -1)
   187 		{
   188 			continue;
   189 		}
   190 		if (strcmp(key, "game") == 0)
   191 		{
   192 			game_info_t mod;
   193 			unsigned int got_id;
   194 
   195 			AnsiToUnicode(value, mod.name, sizeof(mod.name));
   196 			UTIL_Format(mod.game_path, sizeof(mod.game_path), _T("%s"), path);
   197 			mod.source_engine = eng_type;
   198 
   199 			got_id = AddModToList(db, &mod);
   200 
   201 			if (id != NULL)
   202 			{
   203 				*id = got_id;
   204 			}
   205 
   206 			fclose(fp);
   207 
   208 			return true;
   209 		}
   210 	}
   211 
   212 	fclose(fp);
   213 
   214 	return false;
   215 }
   216 
   217 void AddModsFromFolder(const TCHAR *path,
   218 					   int eng_type,
   219 					   game_database_t *db,
   220 					   game_list_t *gl)
   221 {
   222 	HANDLE hFind;
   223 	WIN32_FIND_DATA fd;
   224 	TCHAR temp_path[MAX_PATH];
   225 	TCHAR search_path[MAX_PATH];
   226 	unsigned int mod_id;
   227 
   228 	UTIL_Format(search_path,
   229 		sizeof(search_path),
   230 		_T("%s\\*.*"),
   231 		path);
   232 
   233 	if ((hFind = FindFirstFile(search_path, &fd)) == INVALID_HANDLE_VALUE)
   234 	{
   235 		return;
   236 	}
   237 
   238 	do 
   239 	{
   240 		if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
   241 		{
   242 			continue;
   243 		}
   244 
   245 		if (tstrcasecmp(fd.cFileName, _T(".")) == 0
   246 			|| tstrcasecmp(fd.cFileName, _T("..")) == 0)
   247 		{
   248 			continue;
   249 		}
   250 
   251 		UTIL_PathFormat(temp_path,
   252 			sizeof(temp_path),
   253 			_T("%s\\%s"),
   254 			path,
   255 			fd.cFileName);
   256 		if (TryToAddMod(temp_path, eng_type, db, &mod_id))
   257 		{
   258 			AttachModToGameList(gl, mod_id);
   259 		}
   260 	} while (FindNextFile(hFind, &fd));
   261 
   262 	FindClose(hFind);
   263 }
   264 
   265 void GetSteamGames(game_database_t *db)
   266 {
   267 	HKEY hkPath;
   268 	DWORD dwLen, dwType;
   269 	HANDLE hFind;
   270 	WIN32_FIND_DATA fd;
   271 	TCHAR temp_path[MAX_PATH];
   272 	TCHAR steam_path[MAX_PATH];
   273 	TCHAR steamapps_path[MAX_PATH];
   274 
   275 	if (RegOpenKeyEx(HKEY_CURRENT_USER,
   276 		_T("Software\\Valve\\Steam"),
   277 		0,
   278 		KEY_READ,
   279 		&hkPath) != ERROR_SUCCESS)
   280 	{
   281 		db->listen.error_code = GAME_LIST_CANT_READ;
   282 		db->dedicated.error_code = GAME_LIST_CANT_READ;
   283 		return;
   284 	}
   285 
   286 	dwLen = sizeof(steam_path) / sizeof(TCHAR);
   287 	if (RegQueryValueEx(hkPath,
   288 		_T("SteamPath"),
   289 		NULL,
   290 		&dwType,
   291 		(LPBYTE)steam_path,
   292 		&dwLen) != ERROR_SUCCESS)
   293 	{
   294 		RegCloseKey(hkPath);
   295 		db->listen.error_code = GAME_LIST_CANT_READ;
   296 		db->dedicated.error_code = GAME_LIST_CANT_READ;
   297 		return;
   298 	}
   299 
   300 	UTIL_PathFormat(steamapps_path,
   301 		sizeof(steamapps_path) / sizeof(TCHAR),
   302 		_T("%s\\steamapps\\*.*"),
   303 		steam_path);
   304 
   305 	if ((hFind = FindFirstFile(steamapps_path, &fd)) == INVALID_HANDLE_VALUE)
   306 	{
   307 		RegCloseKey(hkPath);
   308 		db->listen.error_code = GAME_LIST_CANT_READ;
   309 		db->dedicated.error_code = GAME_LIST_CANT_READ;
   310 		return;
   311 	}
   312 
   313 	do 
   314 	{
   315 		if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
   316 		{
   317 			continue;
   318 		}
   319 
   320 		if (tstrcasecmp(fd.cFileName, _T(".")) == 0
   321 			|| tstrcasecmp(fd.cFileName, _T("..")) == 0)
   322 		{
   323 			continue;
   324 		}
   325 
   326 		/* If we get a folder called "SourceMods," look for third party mods */
   327 		if (tstrcasecmp(fd.cFileName, _T("SourceMods")) == 0)
   328 		{
   329 			game_list_t *gl = MakeGameList(_T("Third-Party Games"));
   330 
   331 			UTIL_PathFormat(temp_path,
   332 				sizeof(temp_path) / sizeof(TCHAR),
   333 				_T("%s\\steamapps\\%s"),
   334 				steam_path,
   335 				fd.cFileName);
   336 
   337 			AddModsFromFolder(temp_path, SOURCE_ENGINE_UNKNOWN, db, gl);
   338 
   339 			if (gl->game_count)
   340 			{
   341 				AttachGameListToGroup(&db->listen, gl);
   342 			}
   343 			else
   344 			{
   345 				free(gl);
   346 			}
   347 		}
   348 		else
   349 		{
   350 			/* Look for listenserver games */
   351 			game_list_t *gl = MakeGameList(fd.cFileName);
   352 
   353 			for (unsigned int i = 0; valve_game_list[i].folder != NULL; i++)
   354 			{
   355 				unsigned int mod_id;
   356 				UTIL_PathFormat(temp_path,
   357 					sizeof(temp_path) / sizeof(TCHAR),
   358 					_T("%s\\steamapps\\%s\\%s\\%s"),
   359 					steam_path,
   360 					fd.cFileName,
   361 					valve_game_list[i].folder,
   362 					valve_game_list[i].subfolder);
   363 				if (TryToAddMod(temp_path, valve_game_list[i].eng_type, db, &mod_id))
   364 				{
   365 					AttachModToGameList(gl, mod_id);
   366 				}
   367 			}
   368 
   369 			if (gl->game_count)
   370 			{
   371 				AttachGameListToGroup(&db->listen, gl);
   372 			}
   373 			else
   374 			{
   375 				free(gl);
   376 			}
   377 
   378 			/* Look for dedicated games */
   379 			gl = MakeGameList(fd.cFileName);
   380 
   381 			for (unsigned int i = 0; valve_server_list[i].folder != NULL; i++)
   382 			{
   383 				UTIL_PathFormat(temp_path,
   384 					sizeof(temp_path) / sizeof(TCHAR),
   385 					_T("%s\\steamapps\\%s\\%s"),
   386 					steam_path,
   387 					fd.cFileName,
   388 					valve_server_list[i].folder);
   389 				AddModsFromFolder(temp_path, valve_server_list[i].eng_type, db, gl);
   390 			}
   391 
   392 			if (gl->game_count)
   393 			{
   394 				AttachGameListToGroup(&db->dedicated, gl);
   395 			}
   396 			else
   397 			{
   398 				free(gl);
   399 			}
   400 		}
   401 
   402 	} while (FindNextFile(hFind, &fd));
   403 
   404 	FindClose(hFind);
   405 	RegCloseKey(hkPath);
   406 }
   407 
   408 void GetStandaloneGames(game_database_t *db)
   409 {
   410 	HKEY hkPath;
   411 	DWORD dwLen, dwType, dwAttr;
   412 	TCHAR temp_path[MAX_PATH];
   413 	TCHAR hlds_path[MAX_PATH];
   414 	game_list_t *games_standalone;
   415 
   416 	if (RegOpenKeyEx(HKEY_CURRENT_USER,
   417 		_T("Software\\Valve\\HLServer"),
   418 		0,
   419 		KEY_READ,
   420 		&hkPath) != ERROR_SUCCESS)
   421 	{
   422 		db->standalone.error_code = GAME_LIST_CANT_READ;
   423 		return;
   424 	}
   425 
   426 	dwLen = sizeof(hlds_path) / sizeof(TCHAR);
   427 	if (RegQueryValueEx(hkPath,
   428 		_T("InstallPath"),
   429 		NULL,
   430 		&dwType,
   431 		(LPBYTE)hlds_path,
   432 		&dwLen) != ERROR_SUCCESS)
   433 	{
   434 		RegCloseKey(hkPath);
   435 		db->standalone.error_code = GAME_LIST_CANT_READ;
   436 		return;
   437 	}
   438 
   439 	/* Make sure there is a "srcds.exe" file */
   440 	UTIL_PathFormat(temp_path,
   441 		sizeof(temp_path) / sizeof(TCHAR), 
   442 		_T("%s\\srcds.exe"), 
   443 		hlds_path);
   444 	dwAttr = GetFileAttributes(temp_path);
   445 	if (dwAttr == INVALID_FILE_ATTRIBUTES)
   446 	{
   447 		db->standalone.error_code = GAME_LIST_HALFLIFE1;
   448 		return;
   449 	}
   450 
   451 	games_standalone = MakeGameList(_T("Standalone"));
   452 
   453 	/* If there is an "orangebox" sub folder, we can make a better guess 
   454 	 * at the engine state.
   455 	 */
   456 	UTIL_PathFormat(temp_path,
   457 		sizeof(temp_path) / sizeof(TCHAR), 
   458 		_T("%s\\orangebox"), 
   459 		hlds_path);
   460 	dwAttr = GetFileAttributes(temp_path);
   461 	if (dwAttr != INVALID_FILE_ATTRIBUTES
   462 		&& ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
   463 	{
   464 		AddModsFromFolder(temp_path, SOURCE_ENGINE_2007, db, games_standalone);
   465 	}
   466 
   467 	/* Add everything from the server */
   468 	AddModsFromFolder(hlds_path, SOURCE_ENGINE_2004, db, games_standalone);
   469 
   470 	if (games_standalone->game_count)
   471 	{
   472 		AttachGameListToGroup(&db->standalone, games_standalone);
   473 	}
   474 	else
   475 	{
   476 		free(games_standalone);
   477 	}
   478 
   479 	RegCloseKey(hkPath);
   480 }
   481 
   482 void DisplayBadGamesDialog(HWND hWnd, int reason)
   483 {
   484 	TCHAR message[256];
   485 	UINT idc = 0;
   486 
   487 	if (reason == GAME_LIST_CANT_READ)
   488 	{
   489 		idc = IDS_GAME_FAIL_READ;
   490 	}
   491 	else if (reason == GAME_LIST_HALFLIFE1)
   492 	{
   493 		idc = IDS_GAME_FAIL_HL1;
   494 	}
   495 	else if (reason == GAME_LIST_NO_GAMES)
   496 	{
   497 		idc = IDS_GAME_FAIL_NONE;
   498 	}
   499 	else
   500 	{
   501 		return;
   502 	}
   503 
   504 	if (LoadString(g_hInstance,
   505 		idc,
   506 		message,
   507 		sizeof(message) / sizeof(TCHAR)) == 0)
   508 	{
   509 		return;
   510 	}
   511 
   512 	MessageBox(hWnd, 
   513 		message, 
   514 		_T("SourceMod Installer"),
   515 		MB_OK|MB_ICONWARNING);
   516 }
   517 
   518 int _ModIdCompare(const void *item1, const void *item2)
   519 {
   520 	unsigned int mod_id1 = *(unsigned int *)item1;
   521 	unsigned int mod_id2 = *(unsigned int *)item2;
   522 
   523 	return tstrcasecmp(g_games.game_list[mod_id1].name, g_games.game_list[mod_id2].name);
   524 }
   525 
   526 int _GroupCompare(const void *item1, const void *item2)
   527 {
   528 	game_list_t *g1 = *(game_list_t **)item1;
   529 	game_list_t *g2 = *(game_list_t **)item2;
   530 
   531 	return tstrcasecmp(g1->root_name, g2->root_name);
   532 }
   533 
   534 void SortGameGroup(game_group_t *group)
   535 {
   536 	qsort(group->lists, group->list_count, sizeof(game_list_t *), _GroupCompare);
   537 
   538 	for (unsigned int i = 0; i < group->list_count; i++)
   539 	{
   540 		qsort(group->lists[i]->games,
   541 			  group->lists[i]->game_count,
   542 			  sizeof(unsigned int),
   543 			  _ModIdCompare);
   544 	}
   545 }
   546 
   547 void BuildGameDB()
   548 {
   549 	ReleaseGameDB();
   550 	GetStandaloneGames(&g_games);
   551 	GetSteamGames(&g_games);
   552 	SortGameGroup(&g_games.dedicated);
   553 	SortGameGroup(&g_games.listen);
   554 	SortGameGroup(&g_games.standalone);
   555 }
   556 
   557 void ReleaseGameGroup(game_group_t *group)
   558 {
   559 	for (unsigned int i = 0; i < group->list_count; i++)
   560 	{
   561 		free(group->lists[i]->games);
   562 		free(group->lists[i]);
   563 	}
   564 	free(group->lists);
   565 }
   566 
   567 void ReleaseGameDB()
   568 {
   569 	ReleaseGameGroup(&g_games.dedicated);
   570 	ReleaseGameGroup(&g_games.listen);
   571 	ReleaseGameGroup(&g_games.standalone);
   572 	free(g_games.game_list);
   573 	memset(&g_games, 0, sizeof(g_games));
   574 }