575 lines
12 KiB
C++
575 lines
12 KiB
C++
|
#include "GamesList.h"
|
||
|
#include "InstallerUtil.h"
|
||
|
#include "InstallerMain.h"
|
||
|
#include <stdio.h>
|
||
|
|
||
|
game_database_t g_games =
|
||
|
{
|
||
|
NULL, 0,
|
||
|
{NULL, 0, GAME_LIST_NO_GAMES},
|
||
|
{NULL, 0, GAME_LIST_NO_GAMES},
|
||
|
{NULL, 0, GAME_LIST_NO_GAMES}
|
||
|
};
|
||
|
|
||
|
valve_game_t valve_game_list[] =
|
||
|
{
|
||
|
{_T("counter-strike source"), _T("cstrike"), SOURCE_ENGINE_2004},
|
||
|
{_T("day of defeat source"), _T("dod"), SOURCE_ENGINE_2004},
|
||
|
{_T("half-life 2 deathmatch"), _T("hl2mp"), SOURCE_ENGINE_2004},
|
||
|
{_T("half-life deathmatch source"), _T("hl1mp"), SOURCE_ENGINE_2004},
|
||
|
{_T("team fortress 2"), _T("tf"), SOURCE_ENGINE_2007},
|
||
|
{NULL, NULL, 0},
|
||
|
};
|
||
|
|
||
|
valve_game_t valve_server_list[] =
|
||
|
{
|
||
|
{_T("source dedicated server"), NULL, SOURCE_ENGINE_2004},
|
||
|
{_T("source 2007 dedicated server"), NULL, SOURCE_ENGINE_2007},
|
||
|
{NULL, NULL, 0},
|
||
|
};
|
||
|
|
||
|
int IsValidFolder(const TCHAR *path)
|
||
|
{
|
||
|
DWORD attr;
|
||
|
TCHAR gameinfo_file[MAX_PATH];
|
||
|
|
||
|
UTIL_PathFormat(gameinfo_file, sizeof(gameinfo_file), _T("%s\\gameinfo.txt"), path);
|
||
|
|
||
|
if ((attr = GetFileAttributes(gameinfo_file)) == INVALID_FILE_ATTRIBUTES)
|
||
|
{
|
||
|
return GAMEINFO_DOES_NOT_EXIST;
|
||
|
}
|
||
|
|
||
|
if ((attr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
|
||
|
{
|
||
|
return GAMEINFO_IS_READ_ONLY;
|
||
|
}
|
||
|
|
||
|
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
|
||
|
{
|
||
|
return GAMEINFO_DOES_NOT_EXIST;
|
||
|
}
|
||
|
|
||
|
return GAMEINFO_IS_USABLE;
|
||
|
}
|
||
|
|
||
|
void DisplayBadFolderDialog(HWND hDlg, int reason)
|
||
|
{
|
||
|
TCHAR message_string[255];
|
||
|
UINT resource;
|
||
|
|
||
|
if (reason == GAMEINFO_DOES_NOT_EXIST)
|
||
|
{
|
||
|
resource = IDS_NO_GAMEINFO;
|
||
|
}
|
||
|
else if (reason == GAMEINFO_IS_READ_ONLY)
|
||
|
{
|
||
|
resource = IDS_READONLY_GAMEINFO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (LoadString(g_hInstance,
|
||
|
resource,
|
||
|
message_string,
|
||
|
sizeof(message_string) / sizeof(TCHAR)
|
||
|
) == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
MessageBox(hDlg,
|
||
|
message_string,
|
||
|
_T("SourceMod Installer"),
|
||
|
MB_OK|MB_ICONWARNING);
|
||
|
}
|
||
|
|
||
|
game_list_t *MakeGameList(const TCHAR *name)
|
||
|
{
|
||
|
game_list_t *gl = (game_list_t *)malloc(sizeof(game_list_t));
|
||
|
|
||
|
UTIL_Format(gl->root_name,
|
||
|
sizeof(gl->root_name) / sizeof(TCHAR),
|
||
|
_T("%s"),
|
||
|
name);
|
||
|
gl->game_count = 0;
|
||
|
gl->games = NULL;
|
||
|
|
||
|
return gl;
|
||
|
}
|
||
|
|
||
|
void AttachGameListToGroup(game_group_t *group, game_list_t *gl)
|
||
|
{
|
||
|
if (group->lists == NULL)
|
||
|
{
|
||
|
group->lists = (game_list_t **)malloc(sizeof(game_list_t *));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
group->lists = (game_list_t **)realloc(group->lists,
|
||
|
sizeof(game_list_t *) * (group->list_count + 1));
|
||
|
}
|
||
|
|
||
|
group->lists[group->list_count] = gl;
|
||
|
group->list_count++;
|
||
|
}
|
||
|
|
||
|
void AttachModToGameList(game_list_t *gl, unsigned int mod_id)
|
||
|
{
|
||
|
if (gl->games == NULL)
|
||
|
{
|
||
|
gl->games = (unsigned int *)malloc(sizeof(unsigned int));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gl->games = (unsigned int *)realloc(gl->games,
|
||
|
sizeof(unsigned int) * (gl->game_count + 1));
|
||
|
}
|
||
|
|
||
|
gl->games[gl->game_count] = mod_id;
|
||
|
gl->game_count++;
|
||
|
}
|
||
|
|
||
|
unsigned int AddModToList(game_database_t *db, const game_info_t *mod_info)
|
||
|
{
|
||
|
/* Check if a matching game already exists */
|
||
|
for (unsigned int i = 0; i < db->game_count; i++)
|
||
|
{
|
||
|
if (tstrcasecmp(mod_info->game_path, db->game_list[i].game_path) == 0)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (db->game_list == NULL)
|
||
|
{
|
||
|
db->game_list = (game_info_t *)malloc(sizeof(game_info_t));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
db->game_list = (game_info_t *)realloc(db->game_list,
|
||
|
sizeof(game_info_t) * (db->game_count + 1));
|
||
|
}
|
||
|
|
||
|
memcpy(&db->game_list[db->game_count], mod_info, sizeof(game_info_t));
|
||
|
db->game_count++;
|
||
|
|
||
|
return db->game_count - 1;
|
||
|
}
|
||
|
|
||
|
bool TryToAddMod(const TCHAR *path, int eng_type, game_database_t *db, unsigned int *id)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
TCHAR gameinfo_path[MAX_PATH];
|
||
|
|
||
|
UTIL_PathFormat(gameinfo_path,
|
||
|
sizeof(gameinfo_path),
|
||
|
_T("%s\\gameinfo.txt"),
|
||
|
path);
|
||
|
|
||
|
if ((fp = _tfopen(gameinfo_path, _T("rt"))) == NULL)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int pos;
|
||
|
char buffer[512];
|
||
|
char key[256], value[256];
|
||
|
while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL)
|
||
|
{
|
||
|
if ((pos = BreakStringA(buffer, key, sizeof(key))) == -1)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if ((pos = BreakStringA(&buffer[pos], value, sizeof(value))) == -1)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (strcmp(key, "game") == 0)
|
||
|
{
|
||
|
game_info_t mod;
|
||
|
unsigned int got_id;
|
||
|
|
||
|
AnsiToUnicode(value, mod.name, sizeof(mod.name));
|
||
|
UTIL_Format(mod.game_path, sizeof(mod.game_path), _T("%s"), path);
|
||
|
mod.source_engine = eng_type;
|
||
|
|
||
|
got_id = AddModToList(db, &mod);
|
||
|
|
||
|
if (id != NULL)
|
||
|
{
|
||
|
*id = got_id;
|
||
|
}
|
||
|
|
||
|
fclose(fp);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose(fp);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void AddModsFromFolder(const TCHAR *path,
|
||
|
int eng_type,
|
||
|
game_database_t *db,
|
||
|
game_list_t *gl)
|
||
|
{
|
||
|
HANDLE hFind;
|
||
|
WIN32_FIND_DATA fd;
|
||
|
TCHAR temp_path[MAX_PATH];
|
||
|
TCHAR search_path[MAX_PATH];
|
||
|
unsigned int mod_id;
|
||
|
|
||
|
UTIL_Format(search_path,
|
||
|
sizeof(search_path),
|
||
|
_T("%s\\*.*"),
|
||
|
path);
|
||
|
|
||
|
if ((hFind = FindFirstFile(search_path, &fd)) == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tstrcasecmp(fd.cFileName, _T(".")) == 0
|
||
|
|| tstrcasecmp(fd.cFileName, _T("..")) == 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path),
|
||
|
_T("%s\\%s"),
|
||
|
path,
|
||
|
fd.cFileName);
|
||
|
if (TryToAddMod(temp_path, eng_type, db, &mod_id))
|
||
|
{
|
||
|
AttachModToGameList(gl, mod_id);
|
||
|
}
|
||
|
} while (FindNextFile(hFind, &fd));
|
||
|
|
||
|
FindClose(hFind);
|
||
|
}
|
||
|
|
||
|
void GetSteamGames(game_database_t *db)
|
||
|
{
|
||
|
HKEY hkPath;
|
||
|
DWORD dwLen, dwType;
|
||
|
HANDLE hFind;
|
||
|
WIN32_FIND_DATA fd;
|
||
|
TCHAR temp_path[MAX_PATH];
|
||
|
TCHAR steam_path[MAX_PATH];
|
||
|
TCHAR steamapps_path[MAX_PATH];
|
||
|
|
||
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||
|
_T("Software\\Valve\\Steam"),
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hkPath) != ERROR_SUCCESS)
|
||
|
{
|
||
|
db->listen.error_code = GAME_LIST_CANT_READ;
|
||
|
db->dedicated.error_code = GAME_LIST_CANT_READ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dwLen = sizeof(steam_path) / sizeof(TCHAR);
|
||
|
if (RegQueryValueEx(hkPath,
|
||
|
_T("SteamPath"),
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE)steam_path,
|
||
|
&dwLen) != ERROR_SUCCESS)
|
||
|
{
|
||
|
RegCloseKey(hkPath);
|
||
|
db->listen.error_code = GAME_LIST_CANT_READ;
|
||
|
db->dedicated.error_code = GAME_LIST_CANT_READ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UTIL_PathFormat(steamapps_path,
|
||
|
sizeof(steamapps_path) / sizeof(TCHAR),
|
||
|
_T("%s\\steamapps\\*.*"),
|
||
|
steam_path);
|
||
|
|
||
|
if ((hFind = FindFirstFile(steamapps_path, &fd)) == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
RegCloseKey(hkPath);
|
||
|
db->listen.error_code = GAME_LIST_CANT_READ;
|
||
|
db->dedicated.error_code = GAME_LIST_CANT_READ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tstrcasecmp(fd.cFileName, _T(".")) == 0
|
||
|
|| tstrcasecmp(fd.cFileName, _T("..")) == 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* If we get a folder called "SourceMods," look for third party mods */
|
||
|
if (tstrcasecmp(fd.cFileName, _T("SourceMods")) == 0)
|
||
|
{
|
||
|
game_list_t *gl = MakeGameList(_T("Third-Party Games"));
|
||
|
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path) / sizeof(TCHAR),
|
||
|
_T("%s\\steamapps\\%s"),
|
||
|
steam_path,
|
||
|
fd.cFileName);
|
||
|
|
||
|
AddModsFromFolder(temp_path, SOURCE_ENGINE_UNKNOWN, db, gl);
|
||
|
|
||
|
if (gl->game_count)
|
||
|
{
|
||
|
AttachGameListToGroup(&db->listen, gl);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(gl);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Look for listenserver games */
|
||
|
game_list_t *gl = MakeGameList(fd.cFileName);
|
||
|
|
||
|
for (unsigned int i = 0; valve_game_list[i].folder != NULL; i++)
|
||
|
{
|
||
|
unsigned int mod_id;
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path) / sizeof(TCHAR),
|
||
|
_T("%s\\steamapps\\%s\\%s\\%s"),
|
||
|
steam_path,
|
||
|
fd.cFileName,
|
||
|
valve_game_list[i].folder,
|
||
|
valve_game_list[i].subfolder);
|
||
|
if (TryToAddMod(temp_path, valve_game_list[i].eng_type, db, &mod_id))
|
||
|
{
|
||
|
AttachModToGameList(gl, mod_id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (gl->game_count)
|
||
|
{
|
||
|
AttachGameListToGroup(&db->listen, gl);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(gl);
|
||
|
}
|
||
|
|
||
|
/* Look for dedicated games */
|
||
|
gl = MakeGameList(fd.cFileName);
|
||
|
|
||
|
for (unsigned int i = 0; valve_server_list[i].folder != NULL; i++)
|
||
|
{
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path) / sizeof(TCHAR),
|
||
|
_T("%s\\steamapps\\%s\\%s"),
|
||
|
steam_path,
|
||
|
fd.cFileName,
|
||
|
valve_server_list[i].folder);
|
||
|
AddModsFromFolder(temp_path, valve_server_list[i].eng_type, db, gl);
|
||
|
}
|
||
|
|
||
|
if (gl->game_count)
|
||
|
{
|
||
|
AttachGameListToGroup(&db->dedicated, gl);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(gl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} while (FindNextFile(hFind, &fd));
|
||
|
|
||
|
FindClose(hFind);
|
||
|
RegCloseKey(hkPath);
|
||
|
}
|
||
|
|
||
|
void GetStandaloneGames(game_database_t *db)
|
||
|
{
|
||
|
HKEY hkPath;
|
||
|
DWORD dwLen, dwType, dwAttr;
|
||
|
TCHAR temp_path[MAX_PATH];
|
||
|
TCHAR hlds_path[MAX_PATH];
|
||
|
game_list_t *games_standalone;
|
||
|
|
||
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||
|
_T("Software\\Valve\\HLServer"),
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hkPath) != ERROR_SUCCESS)
|
||
|
{
|
||
|
db->standalone.error_code = GAME_LIST_CANT_READ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dwLen = sizeof(hlds_path) / sizeof(TCHAR);
|
||
|
if (RegQueryValueEx(hkPath,
|
||
|
_T("InstallPath"),
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE)hlds_path,
|
||
|
&dwLen) != ERROR_SUCCESS)
|
||
|
{
|
||
|
RegCloseKey(hkPath);
|
||
|
db->standalone.error_code = GAME_LIST_CANT_READ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Make sure there is a "srcds.exe" file */
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path) / sizeof(TCHAR),
|
||
|
_T("%s\\srcds.exe"),
|
||
|
hlds_path);
|
||
|
dwAttr = GetFileAttributes(temp_path);
|
||
|
if (dwAttr == INVALID_FILE_ATTRIBUTES)
|
||
|
{
|
||
|
db->standalone.error_code = GAME_LIST_HALFLIFE1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
games_standalone = MakeGameList(_T("Standalone"));
|
||
|
|
||
|
/* If there is an "orangebox" sub folder, we can make a better guess
|
||
|
* at the engine state.
|
||
|
*/
|
||
|
UTIL_PathFormat(temp_path,
|
||
|
sizeof(temp_path) / sizeof(TCHAR),
|
||
|
_T("%s\\orangebox"),
|
||
|
hlds_path);
|
||
|
dwAttr = GetFileAttributes(temp_path);
|
||
|
if (dwAttr != INVALID_FILE_ATTRIBUTES
|
||
|
&& ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
|
||
|
{
|
||
|
AddModsFromFolder(temp_path, SOURCE_ENGINE_2007, db, games_standalone);
|
||
|
}
|
||
|
|
||
|
/* Add everything from the server */
|
||
|
AddModsFromFolder(hlds_path, SOURCE_ENGINE_2004, db, games_standalone);
|
||
|
|
||
|
if (games_standalone->game_count)
|
||
|
{
|
||
|
AttachGameListToGroup(&db->standalone, games_standalone);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(games_standalone);
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hkPath);
|
||
|
}
|
||
|
|
||
|
void DisplayBadGamesDialog(HWND hWnd, int reason)
|
||
|
{
|
||
|
TCHAR message[256];
|
||
|
UINT idc = 0;
|
||
|
|
||
|
if (reason == GAME_LIST_CANT_READ)
|
||
|
{
|
||
|
idc = IDS_GAME_FAIL_READ;
|
||
|
}
|
||
|
else if (reason == GAME_LIST_HALFLIFE1)
|
||
|
{
|
||
|
idc = IDS_GAME_FAIL_HL1;
|
||
|
}
|
||
|
else if (reason == GAME_LIST_NO_GAMES)
|
||
|
{
|
||
|
idc = IDS_GAME_FAIL_NONE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (LoadString(g_hInstance,
|
||
|
idc,
|
||
|
message,
|
||
|
sizeof(message) / sizeof(TCHAR)) == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
MessageBox(hWnd,
|
||
|
message,
|
||
|
_T("SourceMod Installer"),
|
||
|
MB_OK|MB_ICONWARNING);
|
||
|
}
|
||
|
|
||
|
int _ModIdCompare(const void *item1, const void *item2)
|
||
|
{
|
||
|
unsigned int mod_id1 = *(unsigned int *)item1;
|
||
|
unsigned int mod_id2 = *(unsigned int *)item2;
|
||
|
|
||
|
return tstrcasecmp(g_games.game_list[mod_id1].name, g_games.game_list[mod_id2].name);
|
||
|
}
|
||
|
|
||
|
int _GroupCompare(const void *item1, const void *item2)
|
||
|
{
|
||
|
game_list_t *g1 = *(game_list_t **)item1;
|
||
|
game_list_t *g2 = *(game_list_t **)item2;
|
||
|
|
||
|
return tstrcasecmp(g1->root_name, g2->root_name);
|
||
|
}
|
||
|
|
||
|
void SortGameGroup(game_group_t *group)
|
||
|
{
|
||
|
qsort(group->lists, group->list_count, sizeof(game_list_t *), _GroupCompare);
|
||
|
|
||
|
for (unsigned int i = 0; i < group->list_count; i++)
|
||
|
{
|
||
|
qsort(group->lists[i]->games,
|
||
|
group->lists[i]->game_count,
|
||
|
sizeof(unsigned int),
|
||
|
_ModIdCompare);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BuildGameDB()
|
||
|
{
|
||
|
ReleaseGameDB();
|
||
|
GetStandaloneGames(&g_games);
|
||
|
GetSteamGames(&g_games);
|
||
|
SortGameGroup(&g_games.dedicated);
|
||
|
SortGameGroup(&g_games.listen);
|
||
|
SortGameGroup(&g_games.standalone);
|
||
|
}
|
||
|
|
||
|
void ReleaseGameGroup(game_group_t *group)
|
||
|
{
|
||
|
for (unsigned int i = 0; i < group->list_count; i++)
|
||
|
{
|
||
|
free(group->lists[i]->games);
|
||
|
free(group->lists[i]);
|
||
|
}
|
||
|
free(group->lists);
|
||
|
}
|
||
|
|
||
|
void ReleaseGameDB()
|
||
|
{
|
||
|
ReleaseGameGroup(&g_games.dedicated);
|
||
|
ReleaseGameGroup(&g_games.listen);
|
||
|
ReleaseGameGroup(&g_games.standalone);
|
||
|
free(g_games.game_list);
|
||
|
memset(&g_games, 0, sizeof(g_games));
|
||
|
}
|