From 142fb2f6522b94aa95523ab516e31a6d63925f9f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 28 Oct 2007 20:15:51 +0000 Subject: [PATCH] initial import of incomplete installer --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401657 --- tools/installer/ChooseMethod.cpp | 203 ++++++++++++ tools/installer/ChooseMethod.h | 8 + tools/installer/GamesList.cpp | 496 ++++++++++++++++++++++++++++ tools/installer/GamesList.h | 39 +++ tools/installer/ICopyMethod.h | 23 ++ tools/installer/InstallerMain.cpp | 112 +++++++ tools/installer/InstallerMain.h | 11 + tools/installer/InstallerUtil.cpp | 231 +++++++++++++ tools/installer/InstallerUtil.h | 18 + tools/installer/LocalCopyMethod.cpp | 158 +++++++++ tools/installer/LocalCopyMethod.h | 29 ++ tools/installer/PerformInstall.cpp | 244 ++++++++++++++ tools/installer/PerformInstall.h | 10 + tools/installer/Resource.h | 59 ++++ tools/installer/SelectGame.cpp | 138 ++++++++ tools/installer/SelectGame.h | 8 + tools/installer/Welcome.cpp | 48 +++ tools/installer/Welcome.h | 6 + tools/installer/installer.ico | Bin 0 -> 23558 bytes tools/installer/installer.rc | 208 ++++++++++++ tools/installer/installer.sln | 20 ++ tools/installer/installer.vcproj | 286 ++++++++++++++++ tools/installer/platform_headers.h | 33 ++ 23 files changed, 2388 insertions(+) create mode 100644 tools/installer/ChooseMethod.cpp create mode 100644 tools/installer/ChooseMethod.h create mode 100644 tools/installer/GamesList.cpp create mode 100644 tools/installer/GamesList.h create mode 100644 tools/installer/ICopyMethod.h create mode 100644 tools/installer/InstallerMain.cpp create mode 100644 tools/installer/InstallerMain.h create mode 100644 tools/installer/InstallerUtil.cpp create mode 100644 tools/installer/InstallerUtil.h create mode 100644 tools/installer/LocalCopyMethod.cpp create mode 100644 tools/installer/LocalCopyMethod.h create mode 100644 tools/installer/PerformInstall.cpp create mode 100644 tools/installer/PerformInstall.h create mode 100644 tools/installer/Resource.h create mode 100644 tools/installer/SelectGame.cpp create mode 100644 tools/installer/SelectGame.h create mode 100644 tools/installer/Welcome.cpp create mode 100644 tools/installer/Welcome.h create mode 100644 tools/installer/installer.ico create mode 100644 tools/installer/installer.rc create mode 100644 tools/installer/installer.sln create mode 100644 tools/installer/installer.vcproj create mode 100644 tools/installer/platform_headers.h diff --git a/tools/installer/ChooseMethod.cpp b/tools/installer/ChooseMethod.cpp new file mode 100644 index 00000000..0191030e --- /dev/null +++ b/tools/installer/ChooseMethod.cpp @@ -0,0 +1,203 @@ +#include "InstallerMain.h" +#include "InstallerUtil.h" +#include "ChooseMethod.h" +#include "Welcome.h" +#include "GamesList.h" +#include "SelectGame.h" + +unsigned int method_chosen = 0; +TCHAR method_path[MAX_PATH]; + +bool SelectFolder(HWND hOwner) +{ + BROWSEINFO info; + LPITEMIDLIST pidlist; + TCHAR path[MAX_PATH]; + + if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) + { + return false; + } + + info.hwndOwner = hOwner; + info.pidlRoot = NULL; + info.pszDisplayName = path; + info.lpszTitle = _T("Select a game/mod folder"); + info.ulFlags = BIF_EDITBOX | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; + info.lpfn = NULL; + info.lParam = 0; + info.iImage = 0; + + if ((pidlist = SHBrowseForFolder(&info)) == NULL) + { + CoUninitialize(); + return false; + } + + /* This hellish code is from MSDN and translate shortcuts to real targets. + * God almighty, I wish Window used real symlinks. + */ + bool acquire_success = false; + bool is_link = false; + IShellFolder *psf = NULL; + LPCITEMIDLIST new_item_list; + HRESULT hr; + + hr = SHBindToParent(pidlist, IID_IShellFolder, (void **)&psf, &new_item_list); + if (SUCCEEDED(hr)) + { + IShellLink *psl = NULL; + + hr = psf->GetUIObjectOf(hOwner, 1, &new_item_list, IID_IShellLink, NULL, (void **)&psl); + if (SUCCEEDED(hr)) + { + LPITEMIDLIST new_item_list; + + hr = psl->GetIDList(&new_item_list); + if (SUCCEEDED(hr)) + { + is_link = true; + + hr = SHGetPathFromIDList(new_item_list, method_path); + if (SUCCEEDED(hr)) + { + acquire_success = true; + } + + CoTaskMemFree(new_item_list); + } + psl->Release(); + } + psf->Release(); + } + + if (!acquire_success && !is_link) + { + hr = SHGetPathFromIDList(pidlist, method_path); + if (SUCCEEDED(hr)) + { + acquire_success = true; + } + } + + /* That was awful. shoo, shoo, COM */ + CoTaskMemFree(pidlist); + CoUninitialize(); + + return acquire_success; +} + +INT_PTR CALLBACK ChooseMethodHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + { + if (LOWORD(wParam) == ID_METHOD_BACK) + { + EndDialog(hDlg, (INT_PTR)DisplayWelcome); + return (INT_PTR)TRUE; + } + else if (LOWORD(wParam) == ID_METHOD_EXIT + || LOWORD(wParam) == ID_CLOSE) + { + return AskToExit(hDlg); + } + else if (LOWORD(wParam) == IDC_METHOD_DED_SERVER + || LOWORD(wParam) == IDC_METHOD_ALONE_SERVER + || LOWORD(wParam) == IDC_METHOD_LISTEN_SERVER + || LOWORD(wParam) == IDC_METHOD_UPLOAD_FTP + || LOWORD(wParam) == IDC_METHOD_CUSTOM_FOLDER) + { + method_chosen = LOWORD(wParam); + HWND button = GetDlgItem(hDlg, ID_METHOD_NEXT); + EnableWindow(button, TRUE); + break; + } + else if (LOWORD(wParam) == ID_METHOD_NEXT) + { + unsigned int game_type = 0; + + switch (method_chosen) + { + case IDC_METHOD_DED_SERVER: + { + game_type = GAMES_DEDICATED; + break; + } + case IDC_METHOD_ALONE_SERVER: + { + game_type = GAMES_STANDALONE; + break; + } + case IDC_METHOD_LISTEN_SERVER: + { + game_type = GAMES_LISTEN; + break; + } + case IDC_METHOD_UPLOAD_FTP: + { + break; + } + case IDC_METHOD_CUSTOM_FOLDER: + { + int val; + + if (!SelectFolder(hDlg)) + { + break; + } + + val = IsValidFolder(method_path); + if (val != GAMEINFO_IS_USABLE) + { + DisplayBadFolderDialog(hDlg, val); + break; + } + } + } + + if (game_type != 0) + { + int reason; + + if ((reason = FindGames(game_type)) < 1) + { + DisplayBadGamesDialog(hDlg, game_type, reason); + break; + } + + /* If we got a valid games list, we can display the next + * dialog box. + */ + EndDialog(hDlg, (INT_PTR)DisplaySelectGame); + return (INT_PTR)TRUE; + } + } + break; + } + case WM_INITDIALOG: + { + return (INT_PTR)TRUE; + } + } + + return (INT_PTR)FALSE; +} + +void *DisplayChooseMethod(HWND hWnd) +{ + INT_PTR val; + + if ((val = DialogBox( + g_hInstance, + MAKEINTRESOURCE(IDD_CHOOSE_METHOD), + hWnd, + ChooseMethodHandler)) == -1) + { + return NULL; + } + + return (void *)val; +} + diff --git a/tools/installer/ChooseMethod.h b/tools/installer/ChooseMethod.h new file mode 100644 index 00000000..21459a8f --- /dev/null +++ b/tools/installer/ChooseMethod.h @@ -0,0 +1,8 @@ +#ifndef _INCLUDE_INSTALLER_CHOOSE_METHOD_H_ +#define _INCLUDE_INSTALLER_CHOOSE_METHOD_H_ + +#include "InstallerMain.h" + +void *DisplayChooseMethod(HWND hWnd); + +#endif //_INCLUDE_INSTALLER_CHOOSE_METHOD_H_ diff --git a/tools/installer/GamesList.cpp b/tools/installer/GamesList.cpp new file mode 100644 index 00000000..2d93c697 --- /dev/null +++ b/tools/installer/GamesList.cpp @@ -0,0 +1,496 @@ +#include "GamesList.h" +#include "InstallerUtil.h" +#include "InstallerMain.h" +#include + +mod_info_t *g_mod_list = NULL; +unsigned int g_mod_count = 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); +} + +void AddModToList(mod_info_t **mod_list, + unsigned int *total_mods, + const mod_info_t *mod_info) +{ + mod_info_t *mods = *mod_list; + unsigned int total = *total_mods; + + if (mods == NULL) + { + mods = (mod_info_t *)malloc(sizeof(mod_info_t)); + } + else + { + mods = (mod_info_t *)realloc(mods, sizeof(mod_info_t) * (total + 1)); + } + + memcpy(&mods[total], mod_info, sizeof(mod_info_t)); + total++; + + *mod_list = mods; + *total_mods = total; +} + +void TryToAddMod(const TCHAR *path, int eng_type, mod_info_t **mod_list, unsigned *total_mods) +{ + 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; + } + + 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) + { + mod_info_t mod; + AnsiToUnicode(value, mod.name, sizeof(mod.name)); + UTIL_Format(mod.mod_path, sizeof(mod.mod_path), _T("%s"), path); + mod.source_engine = eng_type; + AddModToList(mod_list, total_mods, &mod); + } + } + + fclose(fp); +} + +void AddModsFromFolder(const TCHAR *path, int eng_type, mod_info_t **mod_list, unsigned int *total_mods) +{ + HANDLE hFind; + WIN32_FIND_DATA fd; + TCHAR temp_path[MAX_PATH]; + TCHAR search_path[MAX_PATH]; + + 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); + TryToAddMod(temp_path, eng_type, mod_list, total_mods); + + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); +} + +void AddValveModsFromFolder(const TCHAR *path, + unsigned int game_type, + mod_info_t **mod_list, + unsigned int *total_mods) +{ + HANDLE hFind; + WIN32_FIND_DATA fd; + TCHAR temp_path[MAX_PATH]; + TCHAR search_path[MAX_PATH]; + + UTIL_PathFormat(search_path, + sizeof(search_path) / sizeof(TCHAR), + _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; + } + + TCHAR *mod_folder = NULL; + int eng_type = SOURCE_ENGINE_UNKNOWN; + + if (game_type == GAMES_LISTEN) + { + if (tstrcasecmp(fd.cFileName, _T( "counter-strike source")) == 0) + { + mod_folder = _T("cstrike"); + } + else if (tstrcasecmp(fd.cFileName, _T("day of defeat source")) == 0) + { + mod_folder = _T("dod"); + } + else if (tstrcasecmp(fd.cFileName, _T("half-life 2 deathmatch")) == 0) + { + mod_folder = _T("hl2mp"); + } + else if (tstrcasecmp(fd.cFileName, _T("half-life deathmatch source")) == 0) + { + mod_folder = _T("hl1mp"); + } + else if (tstrcasecmp(fd.cFileName, _T("team fortress 2")) == 0) + { + mod_folder = _T("tf"); + eng_type = SOURCE_ENGINE_2007; + } + } + else if (game_type == GAMES_DEDICATED) + { + if (tstrcasecmp(fd.cFileName, _T("source dedicated server")) == 0) + { + UTIL_PathFormat(temp_path, + sizeof(temp_path) / sizeof(TCHAR), + _T("%s\\%s"), + path, + fd.cFileName); + AddModsFromFolder(temp_path, SOURCE_ENGINE_2004, mod_list, total_mods); + } + else if (tstrcasecmp(fd.cFileName, _T("source 2007 dedicated server")) == 0) + { + UTIL_PathFormat(temp_path, + sizeof(temp_path) / sizeof(TCHAR), + _T("%s\\%s"), + path, + fd.cFileName); + AddModsFromFolder(temp_path, SOURCE_ENGINE_2007, mod_list, total_mods); + } + } + + if (mod_folder != NULL) + { + UTIL_PathFormat(temp_path, + sizeof(temp_path) / sizeof(TCHAR), + _T("%s\\%s\\%s"), + path, + fd.cFileName, + mod_folder); + TryToAddMod(temp_path, eng_type, mod_list, total_mods); + } + + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); +} + +int BuildGameList(unsigned int game_type, mod_info_t **mod_list) +{ + unsigned int total_mods = 0; + + if (game_type == GAMES_LISTEN + || game_type == GAMES_DEDICATED) + { + 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) + { + DWORD err = GetLastError(); + return GAME_LIST_CANT_READ; + } + + dwLen = sizeof(steam_path) / sizeof(TCHAR); + if (RegQueryValueEx(hkPath, + _T("SteamPath"), + NULL, + &dwType, + (LPBYTE)steam_path, + &dwLen) != ERROR_SUCCESS) + { + RegCloseKey(hkPath); + return GAME_LIST_CANT_READ; + } + + 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); + return GAME_LIST_CANT_READ; + } + + 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 (game_type == GAMES_LISTEN + && tstrcasecmp(fd.cFileName, _T("SourceMods")) == 0) + { + UTIL_PathFormat(temp_path, + sizeof(temp_path) / sizeof(TCHAR), + _T("%s\\steamapps\\%s"), + steam_path, + fd.cFileName); + AddModsFromFolder(temp_path, SOURCE_ENGINE_UNKNOWN, mod_list, &total_mods); + } + else + { + UTIL_PathFormat(temp_path, + sizeof(temp_path) / sizeof(TCHAR), + _T("%s\\steamapps\\%s"), + steam_path, + fd.cFileName); + AddValveModsFromFolder(temp_path, game_type, mod_list, &total_mods); + } + + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); + RegCloseKey(hkPath); + } + else if (game_type == GAMES_STANDALONE) + { + HKEY hkPath; + int eng_type = SOURCE_ENGINE_UNKNOWN; + DWORD dwLen, dwType, dwAttr; + TCHAR temp_path[MAX_PATH]; + TCHAR hlds_path[MAX_PATH]; + + if (RegOpenKeyEx(HKEY_CURRENT_USER, + _T("Software\\Valve\\HLServer"), + 0, + KEY_READ, + &hkPath) != ERROR_SUCCESS) + { + return GAME_LIST_CANT_READ; + } + + dwLen = sizeof(hlds_path) / sizeof(TCHAR); + if (RegQueryValueEx(hkPath, + _T("InstallPath"), + NULL, + &dwType, + (LPBYTE)hlds_path, + &dwLen) != ERROR_SUCCESS) + { + RegCloseKey(hkPath); + return GAME_LIST_CANT_READ; + } + + /* 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) + { + return GAME_LIST_HALFLIFE1; + } + + /* 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)) + { + eng_type = SOURCE_ENGINE_2004; + AddModsFromFolder(temp_path, SOURCE_ENGINE_2007, mod_list, &total_mods); + } + + /* Add everything from the server */ + AddModsFromFolder(hlds_path, eng_type, mod_list, &total_mods); + + RegCloseKey(hkPath); + } + + return (g_mod_list == NULL) ? GAME_LIST_NO_GAMES : (int)total_mods; +} + +void FreeGameList(mod_info_t *mod_list) +{ + free(mod_list); +} + +int _SortModList(const void *item1, const void *item2) +{ + const mod_info_t *mod1 = (const mod_info_t *)item1; + const mod_info_t *mod2 = (const mod_info_t *)item2; + + return tstrcasecmp(mod1->name, mod2->name); +} + +int FindGames(unsigned int game_type) +{ + int reason; + + ReleaseGamesList(); + + if ((reason = BuildGameList(game_type, &g_mod_list)) > 0) + { + g_mod_count = (unsigned)reason; + qsort(g_mod_list, g_mod_count, sizeof(mod_info_t), _SortModList); + } + + return reason; +} + +void ReleaseGamesList() +{ + if (g_mod_list == NULL) + { + return; + } + + free(g_mod_list); + g_mod_list = NULL; + g_mod_count = 0; +} + +void DisplayBadGamesDialog(HWND hWnd, unsigned int game_type, 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); +} diff --git a/tools/installer/GamesList.h b/tools/installer/GamesList.h new file mode 100644 index 00000000..5b26fadb --- /dev/null +++ b/tools/installer/GamesList.h @@ -0,0 +1,39 @@ +#ifndef _INCLUDE_INSTALLER_GAMES_LIST_H_ +#define _INCLUDE_INSTALLER_GAMES_LIST_H_ + +#include "platform_headers.h" + +#define GAMEINFO_IS_USABLE 0 +#define GAMEINFO_DOES_NOT_EXIST 1 +#define GAMEINFO_IS_READ_ONLY 2 + +#define GAME_LIST_HALFLIFE1 -2 +#define GAME_LIST_CANT_READ -1 +#define GAME_LIST_NO_GAMES 0 + +#define GAMES_DEDICATED 1 +#define GAMES_LISTEN 2 +#define GAMES_STANDALONE 3 + +#define SOURCE_ENGINE_UNKNOWN 0 +#define SOURCE_ENGINE_2004 1 +#define SOURCE_ENGINE_2007 2 + +struct mod_info_t +{ + TCHAR name[128]; + TCHAR mod_path[MAX_PATH]; + int source_engine; +}; + +int IsValidFolder(const TCHAR *path); +void DisplayBadFolderDialog(HWND hWnd, int reason); + +int FindGames(unsigned int game_type); +void DisplayBadGamesDialog(HWND hWnd, unsigned int game_type, int reason); +void ReleaseGamesList(); + +extern mod_info_t *g_mod_list; +extern unsigned int g_mod_count; + +#endif //_INCLUDE_INSTALLER_GAMES_LIST_H_ diff --git a/tools/installer/ICopyMethod.h b/tools/installer/ICopyMethod.h new file mode 100644 index 00000000..af9a2277 --- /dev/null +++ b/tools/installer/ICopyMethod.h @@ -0,0 +1,23 @@ +#ifndef _INCLUDE_INSTALLER_COPY_METHOD_H_ +#define _INCLUDE_INSTALLER_COPY_METHOD_H_ + +#include "platform_headers.h" + +class ICopyProgress +{ +public: + virtual void UpdateProgress(float percent_complete) =0; +}; + +class ICopyMethod +{ +public: + virtual bool CheckForExistingInstall() =0; + virtual void TrackProgress(ICopyProgress *pProgress) =0; + virtual bool SetCurrentFolder(const TCHAR *path, TCHAR *buffer, size_t maxchars) =0; + virtual bool SendFile(const TCHAR *path, TCHAR *buffer, size_t maxchars) =0; + virtual bool CreateFolder(const TCHAR *name, TCHAR *buffer, size_t maxchars) =0; + virtual void CancelCurrentCopy() =0; +}; + +#endif //_INCLUDE_INSTALLER_COPY_METHOD_H_ diff --git a/tools/installer/InstallerMain.cpp b/tools/installer/InstallerMain.cpp new file mode 100644 index 00000000..e3d9515c --- /dev/null +++ b/tools/installer/InstallerMain.cpp @@ -0,0 +1,112 @@ +#include "InstallerMain.h" +#include "Welcome.h" + +#define WMU_INIT_INSTALLER WM_USER+1 + +HINSTANCE g_hInstance; +NEXT_DIALOG next_dialog = DisplayWelcome; + +LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WMU_INIT_INSTALLER: + { + while (next_dialog != NULL) + { + next_dialog = (NEXT_DIALOG)next_dialog(hWnd); + } + PostQuitMessage(0); + break; + } + case WM_DESTROY: + { + PostQuitMessage(0); + break; + } + default: + { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + + return 0; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + WNDCLASSEX wcex; + BOOL bRet; + + wcex.cbSize = sizeof(wcex); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = MainWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_INSTALLER)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = _T("InstallerMenu"); + wcex.lpszClassName = _T("Installer"); + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + if (!RegisterClassEx(&wcex)) + { + return 1; + } + + INITCOMMONCONTROLSEX ccex; + ccex.dwSize = sizeof(ccex); + ccex.dwICC = ICC_BAR_CLASSES + |ICC_HOTKEY_CLASS + |ICC_LISTVIEW_CLASSES + |ICC_PROGRESS_CLASS + |ICC_WIN95_CLASSES + |ICC_TAB_CLASSES; + + if (!InitCommonControlsEx(&ccex)) + { + return 1; + } + + g_hInstance = hInstance; + + HWND hWnd = CreateWindow( + _T("Installer"), + _T("InstallerMain"), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + (HWND)NULL, + (HMENU)NULL, + hInstance, + NULL); + if (hWnd == NULL) + { + return 1; + } + + ShowWindow(hWnd, SW_HIDE); + UpdateWindow(hWnd); + + PostMessage(hWnd, WMU_INIT_INSTALLER, 0, 0); + + MSG msg; + while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) + { + if (bRet == -1) + { + return 1; + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int)msg.wParam; +} diff --git a/tools/installer/InstallerMain.h b/tools/installer/InstallerMain.h new file mode 100644 index 00000000..0fcf359c --- /dev/null +++ b/tools/installer/InstallerMain.h @@ -0,0 +1,11 @@ +#ifndef _INCLUDE_INSTALLER_H_ +#define _INCLUDE_INSTALLER_H_ + +#include "platform_headers.h" +#include "Resource.h" + +typedef void *(*NEXT_DIALOG)(HWND); + +extern HINSTANCE g_hInstance; + +#endif //_INCLUDE_INSTALLER_H_ diff --git a/tools/installer/InstallerUtil.cpp b/tools/installer/InstallerUtil.cpp new file mode 100644 index 00000000..7a796384 --- /dev/null +++ b/tools/installer/InstallerUtil.cpp @@ -0,0 +1,231 @@ +#include "InstallerUtil.h" +#include "InstallerMain.h" +#include +#include + +int tstrcasecmp(const TCHAR *str1, const TCHAR *str2) +{ +#if defined _UNICODE + return _wcsicmp(str1, str2); +#else + return _stricmp(str1, str2); +#endif +} + +size_t AnsiToUnicode(const char *str, wchar_t *buffer, size_t maxchars) +{ + if (maxchars < 1) + { + return 0; + } + + size_t total = + (size_t)MultiByteToWideChar(CP_UTF8, + 0, + str, + -1, + buffer, + (int)maxchars); + + return total; +} + +bool IsWhiteSpaceA(const char *stream) +{ + char c = *stream; + if (c & (1<<7)) + { + return false; + } + else + { + return isspace(c) != 0; + } +} + +int BreakStringA(const char *str, char *out, size_t maxchars) +{ + const char *inptr = str; + while (*inptr != '\0' && IsWhiteSpaceA(inptr)) + { + inptr++; + } + + if (*inptr == '\0') + { + if (maxchars) + { + *out = '\0'; + } + return -1; + } + + const char *start, *end = NULL; + + bool quoted = (*inptr == '"'); + if (quoted) + { + inptr++; + start = inptr; + /* Read input until we reach a quote. */ + while (*inptr != '\0' && *inptr != '"') + { + /* Update the end point, increment the stream. */ + end = inptr++; + } + /* Read one more token if we reached an end quote */ + if (*inptr == '"') + { + inptr++; + } + } + else + { + start = inptr; + /* Read input until we reach a space */ + while (*inptr != '\0' && !IsWhiteSpaceA(inptr)) + { + /* Update the end point, increment the stream. */ + end = inptr++; + } + } + + /* Copy the string we found, if necessary */ + if (end == NULL) + { + if (maxchars) + { + *out = '\0'; + } + } + else if (maxchars) + { + char *outptr = out; + maxchars--; + for (const char *ptr=start; + (ptr <= end) && ((unsigned)(outptr - out) < (maxchars)); + ptr++, outptr++) + { + *outptr = *ptr; + } + *outptr = '\0'; + } + + /* Consume more of the string until we reach non-whitespace */ + while (*inptr != '\0' && IsWhiteSpaceA(inptr)) + { + inptr++; + } + + return (int)(inptr - str); +} + +size_t UTIL_Format(TCHAR *buffer, size_t count, const TCHAR *fmt, ...) +{ + va_list ap; + size_t len; + + va_start(ap, fmt); + len = UTIL_FormatArgs(buffer, count, fmt, ap); + va_end(ap); + + if (len >= count) + { + len = count - 1; + buffer[len] = '\0'; + } + + return len; +} + +size_t UTIL_FormatArgs(TCHAR *buffer, size_t count, const TCHAR *fmt, va_list ap) +{ + size_t len = _vsntprintf(buffer, count, fmt, ap); + + if (len >= count) + { + len = count - 1; + buffer[len] = '\0'; + } + + return len; +} + +size_t UTIL_PathFormat(TCHAR *buffer, size_t count, const TCHAR *fmt, ...) +{ + va_list ap; + size_t len; + + va_start(ap, fmt); + len = UTIL_FormatArgs(buffer, count, fmt, ap); + va_end(ap); + + for (size_t i = 0; i < len; i++) + { + if (buffer[i] == '/') + { + buffer[i] = '\\'; + } + } + + return len; +} + +const TCHAR *GetFileFromPath(const TCHAR *path) +{ + size_t len = _tcslen(path); + + for (size_t i = 0; + i >= 0 && i < len - 1; + i--) + { + if (path[i] == '\\' || path[i] == '/') + { + return &path[i]; + } + } + + return NULL; +} + +void GenerateErrorMessage(DWORD err, TCHAR *buffer, size_t maxchars) +{ + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + (DWORD)maxchars, + NULL) == 0) + { + UTIL_Format(buffer, maxchars, _T("Unknown error")); + } +} + +INT_PTR AskToExit(HWND hWnd) +{ + TCHAR verify_exit[100]; + + if (LoadString(g_hInstance, + IDS_VERIFY_EXIT, + verify_exit, + sizeof(verify_exit) / sizeof(TCHAR) + ) == 0) + { + return (INT_PTR)FALSE; + } + + int val = MessageBox( + hWnd, + _T("Are you sure you want to exit?"), + _T("SourceMod Installer"), + MB_YESNO|MB_ICONQUESTION); + + if (val == 0 || val == IDYES) + { + EndDialog(hWnd, NULL); + return (INT_PTR)TRUE; + } + + return (INT_PTR)FALSE; +} diff --git a/tools/installer/InstallerUtil.h b/tools/installer/InstallerUtil.h new file mode 100644 index 00000000..a9d0c12c --- /dev/null +++ b/tools/installer/InstallerUtil.h @@ -0,0 +1,18 @@ +#ifndef _INCLUDE_INSTALLER_UTIL_H_ +#define _INCLUDE_INSTALLER_UTIL_H_ + +#include "platform_headers.h" + +bool IsWhiteSpaceA(const char *stream); +size_t UTIL_FormatArgs(TCHAR *buffer, size_t count, const TCHAR *fmt, va_list ap); +size_t UTIL_Format(TCHAR *buffer, size_t count, const TCHAR *fmt, ...); +size_t UTIL_PathFormat(TCHAR *buffer, size_t count, const TCHAR *fmt, ...); +int tstrcasecmp(const TCHAR *str1, const TCHAR *str2); +int BreakStringA(const char *str, char *out, size_t maxchars); +size_t AnsiToUnicode(const char *str, wchar_t *buffer, size_t maxchars); +const TCHAR *GetFileFromPath(const TCHAR *path); +void GenerateErrorMessage(DWORD err, TCHAR *buffer, size_t maxchars); + +INT_PTR AskToExit(HWND hWnd); + +#endif //_INCLUDE_INSTALLER_UTIL_H_ diff --git a/tools/installer/LocalCopyMethod.cpp b/tools/installer/LocalCopyMethod.cpp new file mode 100644 index 00000000..a6a72ad4 --- /dev/null +++ b/tools/installer/LocalCopyMethod.cpp @@ -0,0 +1,158 @@ +#include "InstallerUtil.h" +#include "LocalCopyMethod.h" + +LocalCopyMethod g_LocalCopier; + +DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, + LARGE_INTEGER TotalBytesTransferred, + LARGE_INTEGER StreamSize, + LARGE_INTEGER StreamBytesTransferred, + DWORD dwStreamNumber, + DWORD dwCallbackReason, + HANDLE hSourceFile, + HANDLE hDestinationFile, + LPVOID lpData) +{ + ICopyProgress *progress = (ICopyProgress *)lpData; + + float percent = (float)(TotalBytesTransferred.QuadPart) / (float)(TotalFileSize.QuadPart); + + progress->UpdateProgress(percent); + + return PROGRESS_CONTINUE; +} + +LocalCopyMethod::LocalCopyMethod() +{ + m_pProgress = NULL; +} + +void LocalCopyMethod::SetOutputPath(const TCHAR *path) +{ + UTIL_PathFormat(m_OutputPath, + sizeof(m_OutputPath) / sizeof(TCHAR), + _T("%s"), + path); + + UTIL_PathFormat(m_CurrentPath, + sizeof(m_CurrentPath) / sizeof(TCHAR), + _T("%s"), + path); +} + +void LocalCopyMethod::TrackProgress(ICopyProgress *pProgress) +{ + m_pProgress = pProgress; +} + +bool LocalCopyMethod::CreateFolder(const TCHAR *name, TCHAR *buffer, size_t maxchars) +{ + TCHAR path[MAX_PATH]; + + UTIL_PathFormat(path, + sizeof(path) / sizeof(TCHAR), + _T("%s\\%s"), + m_CurrentPath, + name); + + if (CreateDirectory(name, NULL)) + { + return true; + } + + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + return true; + } + + GenerateErrorMessage(error, buffer, maxchars); + + return false; +} + +bool LocalCopyMethod::SetCurrentFolder(const TCHAR *path, TCHAR *buffer, size_t maxchars) +{ + if (path == NULL) + { + UTIL_PathFormat(m_CurrentPath, + sizeof(m_CurrentPath) / sizeof(TCHAR), + _T("%s"), + m_OutputPath); + } + else + { + UTIL_PathFormat(m_CurrentPath, + sizeof(m_CurrentPath) / sizeof(TCHAR), + _T("%s\\%s"), + m_OutputPath, + path); + } + + return true; +} + +bool LocalCopyMethod::SendFile(const TCHAR *path, TCHAR *buffer, size_t maxchars) +{ + const TCHAR *filename = GetFileFromPath(path); + + if (filename == NULL) + { + UTIL_Format(buffer, maxchars, _T("Invalid filename")); + return false; + } + + TCHAR new_path[MAX_PATH]; + UTIL_PathFormat(new_path, + sizeof(new_path) / sizeof(TCHAR), + _T("%s\\%s"), + m_CurrentPath, + filename); + + m_bCancelStatus = FALSE; + + if (CopyFileEx(path, + new_path, + m_pProgress ? CopyProgressRoutine : NULL, + m_pProgress, + &m_bCancelStatus, + 0) == 0) + { + /* Delete the file in case it was a partial copy */ + DeleteFile(new_path); + + GenerateErrorMessage(GetLastError(), buffer, maxchars); + + return false; + } + + return true; +} + +void LocalCopyMethod::CancelCurrentCopy() +{ + m_bCancelStatus = TRUE; +} + +bool LocalCopyMethod::CheckForExistingInstall() +{ + TCHAR path[MAX_PATH]; + + UTIL_PathFormat(path, + sizeof(path) / sizeof(TCHAR), + _T("%s\\addons\\sourcemod"), + m_CurrentPath); + if (GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES) + { + UTIL_PathFormat(path, + sizeof(path) / sizeof(TCHAR), + _T("%s\\cfg\\sourcemod"), + m_CurrentPath); + if (GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES) + { + return false; + } + } + + return true; +} diff --git a/tools/installer/LocalCopyMethod.h b/tools/installer/LocalCopyMethod.h new file mode 100644 index 00000000..b82f8573 --- /dev/null +++ b/tools/installer/LocalCopyMethod.h @@ -0,0 +1,29 @@ +#ifndef _INCLUDE_INSTALL_LOCAL_COPY_METHOD_H_ +#define _INCLUDE_INSTALL_LOCAL_COPY_METHOD_H_ + +#include "platform_headers.h" +#include "ICopyMethod.h" + +class LocalCopyMethod : public ICopyMethod +{ +public: + LocalCopyMethod(); +public: + virtual void TrackProgress(ICopyProgress *pProgress); + virtual bool SetCurrentFolder(const TCHAR *path, TCHAR *buffer, size_t maxchars); + virtual bool SendFile(const TCHAR *path, TCHAR *buffer, size_t maxchars); + virtual bool CreateFolder(const TCHAR *name, TCHAR *buffer, size_t maxchars); + virtual void CancelCurrentCopy(); + virtual bool CheckForExistingInstall(); +public: + void SetOutputPath(const TCHAR *path); +private: + ICopyProgress *m_pProgress; + TCHAR m_OutputPath[MAX_PATH]; + TCHAR m_CurrentPath[MAX_PATH]; + BOOL m_bCancelStatus; +}; + +extern LocalCopyMethod g_LocalCopier; + +#endif //_INCLUDE_INSTALL_LOCAL_COPY_METHOD_H_ diff --git a/tools/installer/PerformInstall.cpp b/tools/installer/PerformInstall.cpp new file mode 100644 index 00000000..78e70d67 --- /dev/null +++ b/tools/installer/PerformInstall.cpp @@ -0,0 +1,244 @@ +#include "InstallerMain.h" +#include "InstallerUtil.h" +#include "PerformInstall.h" + +struct folder_t +{ + TCHAR path[MAX_PATH]; +}; + +ICopyMethod *g_pCopyMethod = NULL; +bool do_not_copy_binaries = false; +TCHAR source_path[MAX_PATH]; + +void SetInstallMethod(ICopyMethod *pCopyMethod) +{ + g_pCopyMethod = pCopyMethod; +} + +bool CopyStructureRecursively(const TCHAR *basepath, + const TCHAR *local_path, + TCHAR *errbuf, + size_t maxchars) +{ + HANDLE hFind; + WIN32_FIND_DATA fd; + TCHAR search_path[MAX_PATH]; + folder_t *folder_list = NULL; + unsigned int folder_count = 0; + + if (local_path == NULL) + { + UTIL_PathFormat(search_path, + sizeof(search_path) / sizeof(TCHAR), + _T("%s\\*.*"), + basepath); + } + else + { + UTIL_PathFormat(search_path, + sizeof(search_path) / sizeof(TCHAR), + _T("%s\\%s\\*.*"), + basepath, + local_path); + } + + if (!g_pCopyMethod->SetCurrentFolder(local_path, errbuf, maxchars)) + { + /* :TODO: set fail state */ + return false; + } + + if ((hFind = FindFirstFile(search_path, &fd)) == INVALID_HANDLE_VALUE) + { + /* :TODO: set a fail state */ + return false; + } + + do + { + if (tstrcasecmp(fd.cFileName, _T(".")) == 0 + || tstrcasecmp(fd.cFileName, _T("..")) == 0) + { + continue; + } + + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) + { + /* We cache the folder list so we don't have to keep changing folders back and forth, + * which could be annoying on a slow copy connection. + */ + if (folder_list == NULL) + { + folder_list = (folder_t *)malloc(sizeof(folder_t) * (folder_count + 1)); + } + else + { + folder_list = (folder_t *)realloc(folder_list, sizeof(folder_t) * (folder_count + 1)); + } + + UTIL_Format(folder_list[folder_count].path, MAX_PATH, _T("%s"), fd.cFileName); + folder_count++; + } + else + { + TCHAR file_path[MAX_PATH]; + + if (local_path == NULL) + { + UTIL_PathFormat(file_path, + sizeof(file_path), + _T("%s\\%s"), + basepath, + fd.cFileName); + } + else + { + UTIL_PathFormat(file_path, + sizeof(file_path), + _T("%s\\%s\\%s"), + basepath, + local_path, + fd.cFileName); + } + + if (!g_pCopyMethod->SendFile(file_path, errbuf, maxchars)) + { + FindClose(hFind); + free(folder_list); + return false; + } + } + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); + + /* Now copy folders */ + for (unsigned int i = 0; i < folder_count; i++) + { + /* Try creating the folder */ + if (!g_pCopyMethod->CreateFolder(folder_list[i].path, errbuf, maxchars)) + { + free(folder_list); + return false; + } + + TCHAR new_local_path[MAX_PATH]; + if (local_path == NULL) + { + UTIL_PathFormat(new_local_path, + sizeof(new_local_path) / sizeof(TCHAR), + _T("%s"), + folder_list[i].path); + } + else + { + UTIL_PathFormat(new_local_path, + sizeof(new_local_path) / sizeof(TCHAR), + _T("%s\\%s"), + local_path, + folder_list[i].path); + } + + if (!CopyStructureRecursively(basepath, new_local_path, errbuf, maxchars)) + { + free(folder_list); + return false; + } + } + + free(folder_list); + + return true; +} + +bool StartInstallProcess(HWND hWnd) +{ + if (g_pCopyMethod->CheckForExistingInstall()) + { + int val = MessageBox( + hWnd, + _T("It looks like a previous SourceMod installation exists. Do you want to upgrade? Select \"Yes\" to upgrade and keep configuration files. Select \"No\" to perform a full re-install."), + _T("SourceMod Installer"), + MB_YESNO|MB_ICONQUESTION); + + if (val == 0 || val == IDYES) + { + do_not_copy_binaries = true; + } + else + { + do_not_copy_binaries = false; + } + } + + TCHAR cur_path[MAX_PATH]; + if (_tgetcwd(cur_path, sizeof(cur_path)) == NULL) + { + MessageBox( + hWnd, + _T("Could not locate current directory!"), + _T("SourceMod Installer"), + MB_OK|MB_ICONERROR); + return false; + } + + UTIL_PathFormat(source_path, + sizeof(source_path) / sizeof(TCHAR), + _T("%s\\files"), + cur_path); + + if (GetFileAttributes(source_path) == INVALID_FILE_ATTRIBUTES) + { + MessageBox( + hWnd, + _T("Could not locate the source installation files!"), + _T("SourceMod Installer"), + MB_OK|MB_ICONERROR); + return false; + } + + + + return true; +} + +INT_PTR CALLBACK PerformInstallHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + return (INT_PTR)TRUE; + } + case WM_COMMAND: + { + if (LOWORD(wParam) == ID_INSTALL_CANCEL + || LOWORD(wParam) == ID_CLOSE) + { + /* :TODO: enhance */ + EndDialog(hDlg, NULL); + return (INT_PTR)TRUE; + } + break; + } + } + + return (INT_PTR)FALSE; +} + +void *DisplayPerformInstall(HWND hWnd) +{ + INT_PTR val; + + if ((val = DialogBox( + g_hInstance, + MAKEINTRESOURCE(IDD_PERFORM_INSTALL), + hWnd, + PerformInstallHandler)) == -1) + { + return NULL; + } + + return (void *)val; +} diff --git a/tools/installer/PerformInstall.h b/tools/installer/PerformInstall.h new file mode 100644 index 00000000..82870f94 --- /dev/null +++ b/tools/installer/PerformInstall.h @@ -0,0 +1,10 @@ +#ifndef _INCLUDE_PERFORM_INSTALL_H_ +#define _INCLUDE_PERFORM_INSTALL_H_ + +#include "InstallerMain.h" +#include "ICopyMethod.h" + +void *DisplayPerformInstall(HWND hWnd); +void SetInstallMethod(ICopyMethod *pCopyMethod); + +#endif //_INCLUDE_PERFORM_INSTALL_H_ diff --git a/tools/installer/Resource.h b/tools/installer/Resource.h new file mode 100644 index 00000000..ce94d3be --- /dev/null +++ b/tools/installer/Resource.h @@ -0,0 +1,59 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by installer.rc +// +#define ID_CLOSE 2 +#define IDD_INSTALLER_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_INSTALLER 107 +#define IDI_SMALL 108 +#define IDC_INSTALLER 109 +#define IDR_MAINFRAME 128 +#define IDD_WELCOME 130 +#define IDD_CHOOSE_METHOD 132 +#define IDS_NO_GAMEINFO 132 +#define IDS_READONLY_GAMEINFO 133 +#define IDS_GAME_FAIL_HL1 134 +#define IDS_GAME_FAIL_READ 135 +#define IDS_GAME_FAIL_NONE 136 +#define IDS_VERIFY_EXIT 137 +#define ID_WELCOME_NEXT 1001 +#define IDC_WELCOME_PANEL 1002 +#define IDC_METHOD_TEXT 1003 +#define ID_WELCOME_EXIT 1003 +#define ID_METHOD_NEXT 1004 +#define ID_METHOD_EXIT 1005 +#define ID_METHOD_BACK 1006 +#define IDC_METHOD_DED_SERVER 1007 +#define IDC_METHOD_LISTEN_SERVER 1008 +#define IDC_SELGAME_LIST 1008 +#define IDC_METHOD_ALONE_SERVER 1009 +#define IDC_METHOD_CUSTOM_FOLDER 1010 +#define IDC_METHOD_UPLOAD_FTP 1011 +#define ID_SELGAME_NEXT 1012 +#define ID_SELGAME_EXIT 1013 +#define ID_SELGAME_BACK 1014 +#define IDC_SELGAME_TEXT 1015 +#define IDC_SELGAME_PANEL 1016 +#define IDD_SELECT_GAME 1017 +#define IDD_PERFORM_INSTALL 1018 +#define ID_INSTALL_CANCEL 1019 +#define IDC_INSTALL_PANEL 1020 +#define IDC_INSTALL_TEXT 1021 +#define IDC_STATIC -1 +#define IDC_WELCOME_TEXT -1 +#define IDC_METHOD_PANEL -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 133 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1009 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/tools/installer/SelectGame.cpp b/tools/installer/SelectGame.cpp new file mode 100644 index 00000000..87cd8c8a --- /dev/null +++ b/tools/installer/SelectGame.cpp @@ -0,0 +1,138 @@ +#include "InstallerMain.h" +#include "InstallerUtil.h" +#include "SelectGame.h" +#include "GamesList.h" +#include "ChooseMethod.h" +#include "PerformInstall.h" +#include "LocalCopyMethod.h" + +int selected_game_index = -1; +mod_info_t **game_sel_list = NULL; +unsigned int game_sel_count = 0; + +void AppendSelectableGame(mod_info_t *mod) +{ + if (game_sel_list == NULL) + { + game_sel_list = + (mod_info_t **)malloc(sizeof(mod_info_t *) * (game_sel_count + 1)); + } + else + { + game_sel_list = + (mod_info_t **)realloc(game_sel_list, + sizeof(mod_info_t *) * (game_sel_count + 1)); + } + + game_sel_list[game_sel_count++] = mod; +} + +INT_PTR CALLBACK ChooseGameHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + HWND lbox = GetDlgItem(hDlg, IDC_SELGAME_LIST); + + for (unsigned int i = 0; i < g_mod_count; i++) + { + LRESULT res = SendMessage(lbox, + LB_ADDSTRING, + 0, + (LPARAM)g_mod_list[i].name); + + if (res == LB_ERR || res == LB_ERRSPACE) + { + continue; + } + + AppendSelectableGame(&g_mod_list[i]); + } + + UpdateWindow(lbox); + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + { + if (LOWORD(wParam) == ID_SELGAME_EXIT + || LOWORD(wParam) == ID_CLOSE) + { + return AskToExit(hDlg); + } + else if (LOWORD(wParam) == ID_SELGAME_BACK) + { + EndDialog(hDlg, (INT_PTR)DisplayChooseMethod); + return (INT_PTR)TRUE; + } + else if (LOWORD(wParam) == IDC_SELGAME_LIST) + { + if (HIWORD(wParam) == LBN_SELCHANGE) + { + HWND lbox = (HWND)lParam; + LRESULT cursel = SendMessage(lbox, LB_GETCURSEL, 0, 0); + + selected_game_index = -1; + + if (cursel == LB_ERR) + { + break; + } + + if (cursel >= (LRESULT)g_mod_count) + { + break; + } + + selected_game_index = (int)cursel; + + HWND button = GetDlgItem(hDlg, ID_SELGAME_NEXT); + EnableWindow(button, TRUE); + } + } + else if (LOWORD(wParam) == ID_SELGAME_NEXT) + { + if (selected_game_index == -1) + { + break; + } + + g_LocalCopier.SetOutputPath(game_sel_list[selected_game_index]->mod_path); + SetInstallMethod(&g_LocalCopier); + + EndDialog(hDlg, (INT_PTR)DisplayPerformInstall); + + return (INT_PTR)TRUE; + } + break; + } + case WM_DESTROY: + { + ReleaseGamesList(); + free(game_sel_list); + game_sel_list = NULL; + game_sel_count = 0; + break; + } + } + + return (INT_PTR)FALSE; +} + +void *DisplaySelectGame(HWND hWnd) +{ + INT_PTR val; + + if ((val = DialogBox( + g_hInstance, + MAKEINTRESOURCE(IDD_SELECT_GAME), + hWnd, + ChooseGameHandler)) == -1) + { + return NULL; + } + + return (void *)val; +} + diff --git a/tools/installer/SelectGame.h b/tools/installer/SelectGame.h new file mode 100644 index 00000000..3030b04d --- /dev/null +++ b/tools/installer/SelectGame.h @@ -0,0 +1,8 @@ +#ifndef _INCLUDE_INSTALLER_SELECT_GAME_H_ +#define _INCLUDE_INSTALLER_SELECT_GAME_H_ + +#include "InstallerMain.h" + +void *DisplaySelectGame(HWND hWnd); + +#endif //_INCLUDE_INSTALLER_SELECT_GAME_H_ diff --git a/tools/installer/Welcome.cpp b/tools/installer/Welcome.cpp new file mode 100644 index 00000000..a4171edb --- /dev/null +++ b/tools/installer/Welcome.cpp @@ -0,0 +1,48 @@ +#include "InstallerMain.h" +#include "Welcome.h" +#include "ChooseMethod.h" + +INT_PTR CALLBACK WelcomeHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + return (INT_PTR)TRUE; + } + case WM_COMMAND: + { + if (LOWORD(wParam) == ID_WELCOME_EXIT + || LOWORD(wParam) == ID_CLOSE) + { + EndDialog(hDlg, NULL); + return (INT_PTR)TRUE; + } + else if (LOWORD(wParam) == ID_WELCOME_NEXT) + { + EndDialog(hDlg, (INT_PTR)DisplayChooseMethod); + return (INT_PTR)TRUE; + } + break; + } + } + + return (INT_PTR)FALSE; +} + +void *DisplayWelcome(HWND hWnd) +{ + INT_PTR val; + + if ((val = DialogBox( + g_hInstance, + MAKEINTRESOURCE(IDD_WELCOME), + hWnd, + WelcomeHandler)) == -1) + { + return NULL; + } + + return (void *)val; +} + diff --git a/tools/installer/Welcome.h b/tools/installer/Welcome.h new file mode 100644 index 00000000..b09ce483 --- /dev/null +++ b/tools/installer/Welcome.h @@ -0,0 +1,6 @@ +#ifndef _INCLUDE_INSTALLER_WELCOME_H_ +#define _INCLUDE_INSTALLER_WELCOME_H_ + +void *DisplayWelcome(HWND hWnd); + +#endif //_INCLUDE_INSTALLER_WELCOME_H_ diff --git a/tools/installer/installer.ico b/tools/installer/installer.ico new file mode 100644 index 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/installer/platform_headers.h b/tools/installer/platform_headers.h new file mode 100644 index 00000000..096fd307 --- /dev/null +++ b/tools/installer/platform_headers.h @@ -0,0 +1,33 @@ +#ifndef _INCLUDE_INSTALLER_PLATFORM_HEADERS_H_ +#define _INCLUDE_INSTALLER_PLATFORM_HEADERS_H_ + +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +// C RunTime Header Files +#include +#include +#include +#include +#include +#include + +#endif //_INCLUDE_INSTALLER_PLATFORM_HEADERS_H_