diff options
| author | manuel <manuel@mausz.at> | 2013-03-09 15:14:01 +0100 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2013-03-09 15:14:01 +0100 |
| commit | 41f7119d8631a142fa5a97285a8443f9d7eb7e14 (patch) | |
| tree | 75e35bffcfc8e82c989331335e11bac0c86da131 /main.cpp | |
| download | steamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.tar.gz steamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.tar.bz2 steamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.zip | |
initial import of UpdateTool 0.4
Diffstat (limited to 'main.cpp')
| -rw-r--r-- | main.cpp | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..db06f63 --- /dev/null +++ b/main.cpp | |||
| @@ -0,0 +1,954 @@ | |||
| 1 | /* | ||
| 2 | This file is a part of "Didrole's Update Tool" | ||
| 3 | ©2k12, Didrole | ||
| 4 | |||
| 5 | License : Public domain | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <stdarg.h> | ||
| 9 | #include <math.h> | ||
| 10 | |||
| 11 | #include "CCommandLine.h" | ||
| 12 | |||
| 13 | #include "../../Open Steamworks/Steamworks.h" | ||
| 14 | #include "enum2string.h" | ||
| 15 | #include "utils.h" | ||
| 16 | |||
| 17 | #include "ModuleScanner.h" | ||
| 18 | |||
| 19 | #ifdef _WIN32 | ||
| 20 | #define isfinite(x) _finite(x) | ||
| 21 | #define strcasecmp _stricmp | ||
| 22 | #endif | ||
| 23 | |||
| 24 | IClientEngine* g_pClientEngine = NULL; | ||
| 25 | IClientUser* g_pClientUser = NULL; | ||
| 26 | IClientAppManager* g_pClientAppManager = NULL; | ||
| 27 | IClientApps* g_pClientApps = NULL; | ||
| 28 | IClientBilling* g_pClientBilling = NULL; | ||
| 29 | IClientConfigStore* g_pClientConfigStore = NULL; | ||
| 30 | IClientUtils* g_pClientUtils = NULL; | ||
| 31 | |||
| 32 | HSteamPipe g_hPipe = 0; | ||
| 33 | HSteamUser g_hUser = 0; | ||
| 34 | |||
| 35 | class CApplication | ||
| 36 | { | ||
| 37 | public: | ||
| 38 | CApplication(int argc, char** argv); | ||
| 39 | |||
| 40 | bool InitSteam(); | ||
| 41 | bool CheckCommandline(); | ||
| 42 | bool ParseScript(const char* szFilename); | ||
| 43 | bool LogOn(); | ||
| 44 | bool RunFrame(); | ||
| 45 | void ShutdownSteam(); | ||
| 46 | |||
| 47 | void Exit(int iCode); | ||
| 48 | int GetExitCode(); | ||
| 49 | |||
| 50 | private: | ||
| 51 | enum EUpdateResult | ||
| 52 | { | ||
| 53 | k_EUpdateResultFailed, | ||
| 54 | k_EUpdateResultSuccess, | ||
| 55 | k_EUpdateResultAlreadyUpToDate, | ||
| 56 | }; | ||
| 57 | EUpdateResult InstallOrUpdateApp(AppId_t uAppId, bool bVerifyAll = false, const char* cszBetaKey = NULL, const char* cszBetaPassword = NULL); | ||
| 58 | bool UninstallApp(AppId_t uAppId); | ||
| 59 | void ShowAvailableApps(); | ||
| 60 | |||
| 61 | void Msg(const char* cszFormat, ...); | ||
| 62 | void Error(const char* cszFormat, ...); | ||
| 63 | void ProgressMsg(const char* cszFormat, ...); | ||
| 64 | bool m_bWasProgressMsg; | ||
| 65 | unsigned int m_uLastProgressMsgSize; | ||
| 66 | |||
| 67 | CCommandLine commandLine; | ||
| 68 | |||
| 69 | CSteamAPILoader m_steamLoader; | ||
| 70 | AppId_t m_uInstallingAppId; | ||
| 71 | AppId_t m_uUninstallingAppId; | ||
| 72 | |||
| 73 | bool m_bExit; | ||
| 74 | int m_iExitCode; | ||
| 75 | |||
| 76 | bool m_bWaitingForAppInfoUpdate; | ||
| 77 | bool m_bWaitingForLicensesUpdate; | ||
| 78 | bool m_bInitialConnection; | ||
| 79 | bool m_bWaitingForCredentials; | ||
| 80 | STEAM_CALLBACK(CApplication, OnConnected, SteamServersConnected_t, m_onConnected); | ||
| 81 | STEAM_CALLBACK(CApplication, OnConnectFailure, SteamServerConnectFailure_t, m_onConnectFailure); | ||
| 82 | STEAM_CALLBACK(CApplication, OnDisconnected, SteamServersDisconnected_t, m_onConnectDisconnected); | ||
| 83 | STEAM_CALLBACK(CApplication, OnAppInfoUpdateComplete, AppInfoUpdateComplete_t, m_onAppInfoUpdateComplete); | ||
| 84 | STEAM_CALLBACK(CApplication, OnAppEventStateChange, AppEventStateChange_t, m_onAppEventStateChange); | ||
| 85 | STEAM_CALLBACK(CApplication, OnLogOnCredentialsChanged, LogOnCredentialsChanged_t, m_onLogOnCredentialsChanged); | ||
| 86 | STEAM_CALLBACK(CApplication, OnLicensesUpdated, LicensesUpdated_t, m_onLicensesUpdated); | ||
| 87 | }; | ||
| 88 | |||
| 89 | void CApplication::Msg(const char* cszFormat, ...) | ||
| 90 | { | ||
| 91 | if(m_bWasProgressMsg) | ||
| 92 | { | ||
| 93 | printf("\n"); | ||
| 94 | } | ||
| 95 | |||
| 96 | va_list args; | ||
| 97 | va_start(args, cszFormat); | ||
| 98 | vprintf(cszFormat, args); | ||
| 99 | va_end(args); | ||
| 100 | |||
| 101 | m_bWasProgressMsg = false; | ||
| 102 | } | ||
| 103 | |||
| 104 | void CApplication::Error(const char* cszFormat, ...) | ||
| 105 | { | ||
| 106 | if(m_bWasProgressMsg) | ||
| 107 | { | ||
| 108 | printf("\n"); | ||
| 109 | } | ||
| 110 | |||
| 111 | va_list args; | ||
| 112 | va_start(args, cszFormat); | ||
| 113 | vfprintf(stderr, cszFormat, args); | ||
| 114 | va_end(args); | ||
| 115 | |||
| 116 | m_bWasProgressMsg = false; | ||
| 117 | } | ||
| 118 | |||
| 119 | void CApplication::ProgressMsg(const char* cszFormat, ...) | ||
| 120 | { | ||
| 121 | if(m_bWasProgressMsg) | ||
| 122 | { | ||
| 123 | printf("\r%*s\r", m_uLastProgressMsgSize, ""); | ||
| 124 | } | ||
| 125 | |||
| 126 | va_list args; | ||
| 127 | va_start(args, cszFormat); | ||
| 128 | m_uLastProgressMsgSize = vprintf(cszFormat, args); | ||
| 129 | va_end(args); | ||
| 130 | |||
| 131 | fflush(stdout); | ||
| 132 | |||
| 133 | m_bWasProgressMsg = true; | ||
| 134 | } | ||
| 135 | |||
| 136 | int CApplication::GetExitCode() | ||
| 137 | { | ||
| 138 | return m_iExitCode; | ||
| 139 | } | ||
| 140 | |||
| 141 | void CApplication::Exit(int iCode) | ||
| 142 | { | ||
| 143 | if(m_bWaitingForCredentials && g_pClientUser->BLoggedOn()) | ||
| 144 | { | ||
| 145 | Msg("Waiting for credentials to cache.\n"); | ||
| 146 | } | ||
| 147 | |||
| 148 | m_iExitCode = iCode; | ||
| 149 | m_bExit = true; | ||
| 150 | } | ||
| 151 | |||
| 152 | bool CApplication::UninstallApp(AppId_t uAppId) | ||
| 153 | { | ||
| 154 | if(g_pClientAppManager->GetAppInstallState(uAppId) & k_EAppStateUninstalled) | ||
| 155 | { | ||
| 156 | Error("This app (%u:%s) isn't installed\n", uAppId, GetAppName(uAppId)); | ||
| 157 | return false; | ||
| 158 | } | ||
| 159 | |||
| 160 | EAppUpdateError eError = g_pClientAppManager->UninstallApp(uAppId, true); | ||
| 161 | if(eError != k_EAppErrorNone) | ||
| 162 | { | ||
| 163 | Error("Uninstallation failed: %s\n", EAppUpdateError2String(eError)); | ||
| 164 | return false; | ||
| 165 | } | ||
| 166 | |||
| 167 | Msg("Uninstalling %u:%s ...\n", uAppId, GetAppName(uAppId)); | ||
| 168 | |||
| 169 | m_uUninstallingAppId = uAppId; | ||
| 170 | |||
| 171 | return true; | ||
| 172 | } | ||
| 173 | |||
| 174 | void CApplication::ShowAvailableApps() | ||
| 175 | { | ||
| 176 | uint32 cLicenses = g_pClientBilling->GetNumLicenses(); | ||
| 177 | for(uint32 i = 0; i < cLicenses; i++) | ||
| 178 | { | ||
| 179 | PackageId_t uPackageID = g_pClientBilling->GetLicensePackageID(i); | ||
| 180 | |||
| 181 | uint32 cAppIds = 0; | ||
| 182 | g_pClientBilling->GetPackageInfo(uPackageID, &cAppIds, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); | ||
| 183 | |||
| 184 | AppId_t* pAppIds = new AppId_t[cAppIds]; | ||
| 185 | g_pClientBilling->GetAppsInPackage(uPackageID, pAppIds, cAppIds, true, false); | ||
| 186 | |||
| 187 | char szAppName[256]; | ||
| 188 | char szContentType[2]; | ||
| 189 | for(uint32 j = 0; j < cAppIds; j++) | ||
| 190 | { | ||
| 191 | if(!g_pClientApps->GetAppData(pAppIds[j], "config/contenttype", szContentType, sizeof(szContentType)) || strcmp(szContentType, "3") != 0) | ||
| 192 | continue; | ||
| 193 | |||
| 194 | // That's an ugly way to filter dedicated servers, but I haven't found a better way (yet) | ||
| 195 | if(!g_pClientApps->GetAppData(pAppIds[j], "common/name", szAppName, sizeof(szAppName)) || strstr(szAppName, "Dedicated Server") == NULL) | ||
| 196 | continue; | ||
| 197 | |||
| 198 | Msg("%u: %s, %s\n", pAppIds[j], szAppName, AppState2String(g_pClientAppManager->GetAppInstallState(pAppIds[j]))); | ||
| 199 | } | ||
| 200 | delete[] pAppIds; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | void CApplication::OnLogOnCredentialsChanged(LogOnCredentialsChanged_t* pParam) | ||
| 205 | { | ||
| 206 | m_bWaitingForCredentials = false; | ||
| 207 | } | ||
| 208 | |||
| 209 | void CApplication::OnLicensesUpdated(LicensesUpdated_t* pParam) | ||
| 210 | { | ||
| 211 | if(m_bWaitingForLicensesUpdate) | ||
| 212 | { | ||
| 213 | m_bWaitingForLicensesUpdate = false; | ||
| 214 | |||
| 215 | const char* cszCommand = commandLine.ParmValue("-command", ""); | ||
| 216 | if(strcasecmp(cszCommand, "list") == 0) | ||
| 217 | { | ||
| 218 | ShowAvailableApps(); | ||
| 219 | this->Exit(EXIT_SUCCESS); | ||
| 220 | } | ||
| 221 | else if(strcasecmp(cszCommand, "update") == 0) | ||
| 222 | { | ||
| 223 | Msg("Requesting appinfo update.\n"); | ||
| 224 | |||
| 225 | AppId_t uAppId = commandLine.ParmValue("-game", (int)k_uAppIdInvalid); | ||
| 226 | |||
| 227 | if(!g_pClientApps->RequestAppInfoUpdate(&uAppId, 1, true)) | ||
| 228 | { | ||
| 229 | Error("Failed to request appinfo update.\n"); | ||
| 230 | this->Exit(EXIT_FAILURE); | ||
| 231 | } | ||
| 232 | m_bWaitingForAppInfoUpdate = true; | ||
| 233 | } | ||
| 234 | else if(strcasecmp(cszCommand, "uninstall") == 0) | ||
| 235 | { | ||
| 236 | AppId_t uAppId = commandLine.ParmValue("-game", (int)k_uAppIdInvalid); | ||
| 237 | |||
| 238 | if(!UninstallApp(uAppId)) | ||
| 239 | this->Exit(EXIT_FAILURE); | ||
| 240 | } | ||
| 241 | else | ||
| 242 | { | ||
| 243 | Error("Unknown command: %s\n", cszCommand); | ||
| 244 | this->Exit(EXIT_FAILURE); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | |||
| 250 | void CApplication::OnAppEventStateChange(AppEventStateChange_t* pParam) | ||
| 251 | { | ||
| 252 | if(pParam->m_nAppID == this->m_uInstallingAppId) | ||
| 253 | { | ||
| 254 | if(pParam->m_eAppError != k_EAppErrorNone) | ||
| 255 | { | ||
| 256 | Msg("Update failed: %s\n", EAppUpdateError2String(pParam->m_eAppError)); | ||
| 257 | m_uInstallingAppId = k_uAppIdInvalid; | ||
| 258 | g_pClientAppManager->SetDownloadingEnabled(false); | ||
| 259 | this->Exit(EXIT_FAILURE); | ||
| 260 | } | ||
| 261 | else | ||
| 262 | { | ||
| 263 | if(!(pParam->m_eNewState & k_EAppStateUpdateRequired) && !(pParam->m_eNewState & k_EAppStateUpdateRunning) && !(pParam->m_eNewState & k_EAppStateValidating) && pParam->m_eNewState & k_EAppStateFullyInstalled) | ||
| 264 | { | ||
| 265 | Msg("Up to date.\n", m_uInstallingAppId); | ||
| 266 | m_uInstallingAppId = k_uAppIdInvalid; | ||
| 267 | g_pClientAppManager->SetDownloadingEnabled(false); | ||
| 268 | this->Exit(EXIT_SUCCESS); | ||
| 269 | } | ||
| 270 | } | ||
| 271 | } | ||
| 272 | else if(pParam->m_nAppID == this->m_uUninstallingAppId) | ||
| 273 | { | ||
| 274 | if(pParam->m_eAppError != k_EAppErrorNone) | ||
| 275 | { | ||
| 276 | Msg("Uninstallation failed: %s\n", EAppUpdateError2String(pParam->m_eAppError)); | ||
| 277 | m_uUninstallingAppId = k_uAppIdInvalid; | ||
| 278 | this->Exit(EXIT_FAILURE); | ||
| 279 | } | ||
| 280 | else | ||
| 281 | { | ||
| 282 | if(pParam->m_eNewState & k_EAppStateUninstalled) | ||
| 283 | { | ||
| 284 | Msg("App uninstalled successfully.\n"); | ||
| 285 | m_uUninstallingAppId = k_uAppIdInvalid; | ||
| 286 | this->Exit(EXIT_SUCCESS); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | else | ||
| 291 | { | ||
| 292 | if(pParam->m_eNewState & k_EAppStateUpdateRequired || pParam->m_eNewState & k_EAppStateUpdateRunning || pParam->m_eNewState & k_EAppStateValidating) | ||
| 293 | { | ||
| 294 | g_pClientAppManager->ChangeAppPriority(pParam->m_nAppID, k_EAppDownloadPriorityPaused); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | CApplication::EUpdateResult CApplication::InstallOrUpdateApp(AppId_t uAppId, bool bVerifyAll, const char* cszBetaKey, const char* cszBetaPassword) | ||
| 300 | { | ||
| 301 | if(!cszBetaKey) | ||
| 302 | cszBetaKey = "Public"; | ||
| 303 | |||
| 304 | char szKeyName[256]; | ||
| 305 | szKeyName[sizeof(szKeyName) - 1] = '\0'; | ||
| 306 | char szBuildID[11]; | ||
| 307 | snprintf(szKeyName, sizeof(szKeyName) - 1, "depots/branches/%s/buildid", cszBetaKey); | ||
| 308 | |||
| 309 | int32 iResult = g_pClientApps->GetAppData(uAppId, szKeyName, szBuildID, sizeof(szBuildID)); | ||
| 310 | if(iResult <= 0) | ||
| 311 | { | ||
| 312 | Error("There is no beta named '%s' for this app, using public branch instead.\n", cszBetaKey); | ||
| 313 | } | ||
| 314 | else | ||
| 315 | { | ||
| 316 | if(cszBetaPassword && !g_pClientAppManager->BCacheBetaPassword(uAppId, cszBetaKey, cszBetaPassword)) | ||
| 317 | { | ||
| 318 | Error("Invalid beta password, using public branch instead.\n"); | ||
| 319 | } | ||
| 320 | else | ||
| 321 | { | ||
| 322 | int32 iKVSize = 1 + sizeof("internal") + sizeof("\x01""BetaKey") + strlen(cszBetaKey) + 3; | ||
| 323 | uint8 *pKV = new uint8[iKVSize]; | ||
| 324 | pKV[0] = '\0'; | ||
| 325 | memcpy(pKV + 1, "internal", sizeof("internal")); | ||
| 326 | memcpy(pKV + 1 + sizeof("internal"), "\x01""BetaKey", sizeof("\x01""BetaKey")); | ||
| 327 | strcpy((char*)pKV + 1 + sizeof("internal") + sizeof("\x01""BetaKey"), cszBetaKey); | ||
| 328 | memcpy(pKV + 1 + sizeof("internal") + sizeof("\x01""BetaKey") + strlen(cszBetaKey) + 1, "\x08\x08", 2); | ||
| 329 | |||
| 330 | g_pClientAppManager->SetAppConfig(uAppId, pKV, iKVSize, false); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | EAppState eState = g_pClientAppManager->GetAppInstallState(uAppId); | ||
| 335 | if(eState == k_EAppStateInvalid || eState & k_EAppStateUninstalled) | ||
| 336 | { | ||
| 337 | Msg("Installing %u:%s ...\n", uAppId, GetAppName(uAppId)); | ||
| 338 | EAppUpdateError eError = g_pClientAppManager->InstallApp(uAppId, NULL, 0, false); | ||
| 339 | if(eError != k_EAppErrorNone) | ||
| 340 | { | ||
| 341 | Error("Installation failed: %s\n", EAppUpdateError2String(eError)); | ||
| 342 | return k_EUpdateResultFailed; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | else if(bVerifyAll) | ||
| 346 | { | ||
| 347 | Msg("Validating %u:%s ...\n", uAppId, GetAppName(uAppId)); | ||
| 348 | if(!g_pClientAppManager->StartValidatingApp(uAppId)) | ||
| 349 | { | ||
| 350 | Error("StartValidatingApp returned false\n"); | ||
| 351 | //return k_EUpdateResultFailed; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | else | ||
| 355 | { | ||
| 356 | if(g_pClientAppManager->BIsAppUpToDate(uAppId)) | ||
| 357 | { | ||
| 358 | Msg("%u:%s is already up to date.\n", uAppId, GetAppName(uAppId)); | ||
| 359 | return k_EUpdateResultAlreadyUpToDate; | ||
| 360 | } | ||
| 361 | else | ||
| 362 | Msg("Updating %u:%s ...\n", uAppId, GetAppName(uAppId)); | ||
| 363 | } | ||
| 364 | |||
| 365 | g_pClientAppManager->SetDownloadingEnabled(true); | ||
| 366 | g_pClientAppManager->ChangeAppPriority(uAppId, k_EAppDownloadPriorityFirst); | ||
| 367 | |||
| 368 | m_uInstallingAppId = uAppId; | ||
| 369 | |||
| 370 | return k_EUpdateResultSuccess; | ||
| 371 | } | ||
| 372 | |||
| 373 | CApplication::CApplication(int argc, char** argv) : | ||
| 374 | m_onConnected(this, &CApplication::OnConnected), | ||
| 375 | m_onConnectFailure(this, &CApplication::OnConnectFailure), | ||
| 376 | m_onConnectDisconnected(this, &CApplication::OnDisconnected), | ||
| 377 | m_onAppInfoUpdateComplete(this, &CApplication::OnAppInfoUpdateComplete), | ||
| 378 | m_onAppEventStateChange(this, &CApplication::OnAppEventStateChange), | ||
| 379 | m_onLogOnCredentialsChanged(this, &CApplication::OnLogOnCredentialsChanged), | ||
| 380 | m_onLicensesUpdated(this, &CApplication::OnLicensesUpdated), | ||
| 381 | commandLine(argc, argv) | ||
| 382 | { | ||
| 383 | m_iExitCode = 0; | ||
| 384 | m_bExit = false; | ||
| 385 | m_bInitialConnection = true; | ||
| 386 | m_bWaitingForAppInfoUpdate = false; | ||
| 387 | m_bWaitingForLicensesUpdate = false; | ||
| 388 | m_bWaitingForCredentials = false; | ||
| 389 | m_bWasProgressMsg = false; | ||
| 390 | m_uInstallingAppId = k_uAppIdInvalid; | ||
| 391 | m_uUninstallingAppId = k_uAppIdInvalid; | ||
| 392 | } | ||
| 393 | |||
| 394 | void CApplication::OnAppInfoUpdateComplete(AppInfoUpdateComplete_t* pParam) | ||
| 395 | { | ||
| 396 | if(m_bWaitingForAppInfoUpdate && strcasecmp(commandLine.ParmValue("-command", ""), "update") == 0) | ||
| 397 | { | ||
| 398 | m_bWaitingForAppInfoUpdate = false; | ||
| 399 | |||
| 400 | AppId_t uAppId = commandLine.ParmValue("-game", (int)k_uAppIdInvalid); | ||
| 401 | |||
| 402 | bool bVerifyAll = commandLine.FindParm("-verify_all") != 0; | ||
| 403 | EUpdateResult eResult = InstallOrUpdateApp(uAppId, bVerifyAll, commandLine.ParmValue("-beta"), commandLine.ParmValue("-beta_password")); | ||
| 404 | if(eResult == k_EUpdateResultFailed) | ||
| 405 | this->Exit(EXIT_FAILURE); | ||
| 406 | else if(eResult == k_EUpdateResultAlreadyUpToDate) | ||
| 407 | this->Exit(EXIT_SUCCESS); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | void CApplication::OnDisconnected(SteamServersDisconnected_t* pParam) | ||
| 412 | { | ||
| 413 | Msg("Disconnected from Steam: %s\n", EResult2String(pParam->m_eResult)); | ||
| 414 | } | ||
| 415 | |||
| 416 | void CApplication::OnConnectFailure(SteamServerConnectFailure_t* pParam) | ||
| 417 | { | ||
| 418 | if(pParam->m_eResult == k_EResultAccountLogonDenied) | ||
| 419 | { | ||
| 420 | Error("Steam Guard has rejected the connection. An access code has been sent to your email address.\n"); | ||
| 421 | } | ||
| 422 | else | ||
| 423 | { | ||
| 424 | Error("Logon failed: %s\n", EResult2String(pParam->m_eResult)); | ||
| 425 | } | ||
| 426 | |||
| 427 | this->Exit(EXIT_FAILURE); | ||
| 428 | } | ||
| 429 | |||
| 430 | void CApplication::OnConnected(SteamServersConnected_t* pParam) | ||
| 431 | { | ||
| 432 | Msg("Logged on\n"); | ||
| 433 | |||
| 434 | if(!g_pClientUser->GetSteamID().BAnonAccount()) | ||
| 435 | { | ||
| 436 | // At this point the steamclient has saved the SteamID of the account in Software/Valve/Steam/Accounts/[AccountName]/SteamID | ||
| 437 | // But we are logged with the console instance, we don't want to save it with this instance since that could interfere with an existing Steam installation. | ||
| 438 | CSteamID userSteamID = g_pClientUser->GetSteamID(); | ||
| 439 | userSteamID.SetAccountInstance(k_unSteamUserDesktopInstance); | ||
| 440 | |||
| 441 | char szSteamIDKey[256]; | ||
| 442 | snprintf(szSteamIDKey, sizeof(szSteamIDKey) - 1, "Software/Valve/Steam/Accounts/%s/SteamID", commandLine.ParmValue("-username", "")); | ||
| 443 | szSteamIDKey[sizeof(szSteamIDKey) - 1] = '\0'; | ||
| 444 | |||
| 445 | g_pClientConfigStore->SetUint64(k_EConfigStoreInstall, szSteamIDKey, userSteamID.ConvertToUint64()); | ||
| 446 | } | ||
| 447 | |||
| 448 | if(!m_bInitialConnection) | ||
| 449 | return; | ||
| 450 | |||
| 451 | m_bInitialConnection = false; | ||
| 452 | |||
| 453 | m_bWaitingForLicensesUpdate = true; | ||
| 454 | Msg("Waiting for licenses update...\n"); | ||
| 455 | } | ||
| 456 | |||
| 457 | void CApplication::ShutdownSteam() | ||
| 458 | { | ||
| 459 | if(g_pClientUser && g_pClientUser->BLoggedOn()) | ||
| 460 | { | ||
| 461 | Msg("Logging off\n"); | ||
| 462 | g_pClientUser->LogOff(); | ||
| 463 | while(g_pClientUser->GetLogonState() != k_ELogonStateNotLoggedOn) | ||
| 464 | mSleep(100); | ||
| 465 | } | ||
| 466 | |||
| 467 | if(g_pClientEngine) | ||
| 468 | { | ||
| 469 | if(g_hPipe && g_hUser) | ||
| 470 | g_pClientEngine->ReleaseUser(g_hPipe, g_hUser); | ||
| 471 | if(g_hPipe) | ||
| 472 | g_pClientEngine->BReleaseSteamPipe(g_hPipe); | ||
| 473 | g_pClientEngine->BShutdownIfAllPipesClosed(); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | void* g_pUsePICS = NULL; | ||
| 478 | |||
| 479 | bool CApplication::InitSteam() | ||
| 480 | { | ||
| 481 | CreateInterfaceFn pCreateInterface = m_steamLoader.GetSteam3Factory(); | ||
| 482 | if(!pCreateInterface) | ||
| 483 | { | ||
| 484 | Error("Unable to get Steam3 factory.\n"); | ||
| 485 | return false; | ||
| 486 | } | ||
| 487 | |||
| 488 | g_pClientEngine = (IClientEngine*)pCreateInterface(CLIENTENGINE_INTERFACE_VERSION, NULL); | ||
| 489 | if(!g_pClientEngine) | ||
| 490 | { | ||
| 491 | Error("Unable to get IClientEngine.\n"); | ||
| 492 | return false; | ||
| 493 | } | ||
| 494 | |||
| 495 | g_hUser = g_pClientEngine->CreateLocalUser(&g_hPipe, k_EAccountTypeIndividual); | ||
| 496 | if(!g_hPipe || !g_hUser) | ||
| 497 | { | ||
| 498 | Error("Unable to create a local user.\n"); | ||
| 499 | return false; | ||
| 500 | } | ||
| 501 | |||
| 502 | g_pClientUser = g_pClientEngine->GetIClientUser(g_hUser, g_hPipe, CLIENTUSER_INTERFACE_VERSION); | ||
| 503 | if(!g_pClientUser) | ||
| 504 | { | ||
| 505 | Error("Unable to get IClientUser.\n"); | ||
| 506 | return false; | ||
| 507 | } | ||
| 508 | |||
| 509 | g_pClientAppManager = g_pClientEngine->GetIClientAppManager(g_hUser, g_hPipe, CLIENTAPPMANAGER_INTERFACE_VERSION); | ||
| 510 | if(!g_pClientAppManager) | ||
| 511 | { | ||
| 512 | Error("Unable to get IClientAppManager.\n"); | ||
| 513 | return false; | ||
| 514 | } | ||
| 515 | |||
| 516 | g_pClientApps = g_pClientEngine->GetIClientApps(g_hUser, g_hPipe, CLIENTAPPS_INTERFACE_VERSION); | ||
| 517 | if(!g_pClientApps) | ||
| 518 | { | ||
| 519 | Error("Unable to get IClientApps.\n"); | ||
| 520 | return false; | ||
| 521 | } | ||
| 522 | |||
| 523 | g_pClientBilling = g_pClientEngine->GetIClientBilling(g_hUser, g_hPipe, CLIENTBILLING_INTERFACE_VERSION); | ||
| 524 | if(!g_pClientBilling) | ||
| 525 | { | ||
| 526 | Error("Unable to get IClientBilling.\n"); | ||
| 527 | return false; | ||
| 528 | } | ||
| 529 | |||
| 530 | g_pClientConfigStore = g_pClientEngine->GetIClientConfigStore(g_hUser, g_hPipe, CLIENTCONFIGSTORE_INTERFACE_VERSION); | ||
| 531 | if(!g_pClientConfigStore) | ||
| 532 | { | ||
| 533 | Error("Unable to get IClientConfigStore.\n"); | ||
| 534 | return false; | ||
| 535 | } | ||
| 536 | |||
| 537 | g_pClientUtils = g_pClientEngine->GetIClientUtils(g_hPipe, CLIENTUTILS_INTERFACE_VERSION); | ||
| 538 | if(!g_pClientUtils) | ||
| 539 | { | ||
| 540 | Error("Unable to get IClientUtils.\n"); | ||
| 541 | return false; | ||
| 542 | } | ||
| 543 | |||
| 544 | // Reset the appid to 0 in case someone put a steam_appid.txt in our directory. | ||
| 545 | g_pClientUtils->SetAppIDForCurrentPipe(k_uAppIdInvalid, false); | ||
| 546 | |||
| 547 | CModuleScanner steamclientScanner((void*)pCreateInterface); | ||
| 548 | |||
| 549 | #ifdef _WIN32 | ||
| 550 | void* pppUsePICS = steamclientScanner.FindSignature("\x00\x00\x00\x00\x83\x78\x34\x00\x75\x00\xc6\x81\x74\x0c\x00\x00\x00\x5d\xc2\x04\x00", "????xxxxx?xxxxxxxxxxx"); | ||
| 551 | g_pUsePICS = **(void***)pppUsePICS; | ||
| 552 | #else | ||
| 553 | unsigned char* pSig = (unsigned char*)steamclientScanner.FindSignature("\x55\x89\xE5\xE8\x00\x00\x00\x00\x81\xC1\x00\x00\x00\x00\x8B\x45\x08\x80\x7D\x0C\x00", "xxxx????xx????xxxxxxx"); | ||
| 554 | int uOffset = *(int*)(pSig + 10); | ||
| 555 | int uOffset2 = *(int*)(pSig + 34); | ||
| 556 | |||
| 557 | void* ppUsePICS = pSig + 8 + uOffset + uOffset2; | ||
| 558 | g_pUsePICS = *(void**)ppUsePICS; | ||
| 559 | #endif | ||
| 560 | |||
| 561 | const char* cszDir = commandLine.ParmValue("-dir"); | ||
| 562 | if(cszDir) | ||
| 563 | g_pClientAppManager->ForceInstallDirOverride(cszDir); | ||
| 564 | |||
| 565 | return true; | ||
| 566 | } | ||
| 567 | |||
| 568 | bool CApplication::ParseScript(const char* szFilename) | ||
| 569 | { | ||
| 570 | FILE* hFile = fopen(szFilename, "rb"); | ||
| 571 | if(!hFile) | ||
| 572 | { | ||
| 573 | Error("Unable to open '%s'.\n", szFilename); | ||
| 574 | return false; | ||
| 575 | } | ||
| 576 | |||
| 577 | char szLine[256]; | ||
| 578 | while(!feof(hFile)) | ||
| 579 | { | ||
| 580 | if(!fgets(szLine, sizeof(szLine), hFile)) | ||
| 581 | break; | ||
| 582 | |||
| 583 | int argc = 0; | ||
| 584 | char* argv[64]; | ||
| 585 | |||
| 586 | char* pchCurrent = szLine; | ||
| 587 | while(argc < 64) | ||
| 588 | { | ||
| 589 | while(isspace(*pchCurrent)) | ||
| 590 | pchCurrent++; | ||
| 591 | |||
| 592 | if(!*pchCurrent) | ||
| 593 | break; | ||
| 594 | |||
| 595 | bool bQuoted = false; | ||
| 596 | |||
| 597 | if(*pchCurrent == '"') | ||
| 598 | { | ||
| 599 | bQuoted = true; | ||
| 600 | } | ||
| 601 | |||
| 602 | char* szArg = pchCurrent; | ||
| 603 | |||
| 604 | while(1) | ||
| 605 | { | ||
| 606 | while(*pchCurrent && !isspace(*pchCurrent)) | ||
| 607 | pchCurrent++; | ||
| 608 | |||
| 609 | if(bQuoted) | ||
| 610 | { | ||
| 611 | if(*(pchCurrent - 1) == '"') | ||
| 612 | { | ||
| 613 | szArg++; | ||
| 614 | *(pchCurrent - 1) = '\0'; | ||
| 615 | break; | ||
| 616 | } | ||
| 617 | else if(!*pchCurrent) | ||
| 618 | { | ||
| 619 | break; | ||
| 620 | } | ||
| 621 | else | ||
| 622 | { | ||
| 623 | while(isspace(*pchCurrent)) | ||
| 624 | pchCurrent++; | ||
| 625 | } | ||
| 626 | } | ||
| 627 | else | ||
| 628 | { | ||
| 629 | if(*pchCurrent) | ||
| 630 | *pchCurrent++ = '\0'; | ||
| 631 | break; | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | argv[argc++] = szArg; | ||
| 636 | } | ||
| 637 | |||
| 638 | if(argc > 0) | ||
| 639 | { | ||
| 640 | if(strcasecmp(argv[0], "login") == 0) | ||
| 641 | { | ||
| 642 | if(argc >= 2 || argc <= 4) | ||
| 643 | { | ||
| 644 | if(strcasecmp(argv[1], "anonymous") != 0) | ||
| 645 | { | ||
| 646 | commandLine.AddParm("-username"); | ||
| 647 | commandLine.AddParm(argv[1]); | ||
| 648 | if(argc >= 3) | ||
| 649 | { | ||
| 650 | commandLine.AddParm("-password"); | ||
| 651 | commandLine.AddParm(argv[2]); | ||
| 652 | } | ||
| 653 | if(argc == 4) | ||
| 654 | { | ||
| 655 | commandLine.AddParm("-steam_guard_code"); | ||
| 656 | commandLine.AddParm(argv[3]); | ||
| 657 | } | ||
| 658 | } | ||
| 659 | } | ||
| 660 | } | ||
| 661 | else if(strcasecmp(argv[0], "set_steam_guard_code") == 0) | ||
| 662 | { | ||
| 663 | if(argc == 2) | ||
| 664 | { | ||
| 665 | commandLine.AddParm("-steam_guard_code"); | ||
| 666 | commandLine.AddParm(argv[1]); | ||
| 667 | } | ||
| 668 | } | ||
| 669 | else if(strcasecmp(argv[0], "force_install_dir") == 0) | ||
| 670 | { | ||
| 671 | if(argc == 2) | ||
| 672 | { | ||
| 673 | commandLine.AddParm("-dir"); | ||
| 674 | commandLine.AddParm(argv[1]); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | else if(strcasecmp(argv[0], "app_update") == 0) | ||
| 678 | { | ||
| 679 | if(argc >= 2) | ||
| 680 | { | ||
| 681 | commandLine.AddParm("-command"); | ||
| 682 | commandLine.AddParm("update"); | ||
| 683 | commandLine.AddParm("-game"); | ||
| 684 | commandLine.AddParm(argv[1]); | ||
| 685 | |||
| 686 | for(int i = 2; i < argc; i++) | ||
| 687 | { | ||
| 688 | if(strcasecmp(argv[i], "validate") == 0 || strcasecmp(argv[i], "-validate") == 0) | ||
| 689 | { | ||
| 690 | commandLine.AddParm("-verify_all"); | ||
| 691 | } | ||
| 692 | else if(strcasecmp(argv[i], "-beta") == 0 && i + 1 < argc) | ||
| 693 | { | ||
| 694 | commandLine.AddParm("-beta"); | ||
| 695 | commandLine.AddParm(argv[i + 1]); | ||
| 696 | } | ||
| 697 | else if(strcasecmp(argv[i], "-betapassword") == 0 && i + 1 < argc) | ||
| 698 | { | ||
| 699 | commandLine.AddParm("-beta_password"); | ||
| 700 | commandLine.AddParm(argv[i + 1]); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | } | ||
| 704 | } | ||
| 705 | } | ||
| 706 | } | ||
| 707 | fclose(hFile); | ||
| 708 | |||
| 709 | return true; | ||
| 710 | } | ||
| 711 | |||
| 712 | bool CApplication::CheckCommandline() | ||
| 713 | { | ||
| 714 | if(commandLine.ParmCount() <= 1) | ||
| 715 | { | ||
| 716 | printf | ||
| 717 | ( | ||
| 718 | "Use: %s%s -command <command> [parameters] [flags]\n" | ||
| 719 | "Or: %s%s +runscript <steamcmd_script_file>\n" | ||
| 720 | "\n" | ||
| 721 | "Commands:\n" | ||
| 722 | " update: Install or update a game\n" | ||
| 723 | " uninstall: Remove a game\n" | ||
| 724 | " list: View available games and their status\n" | ||
| 725 | "\n" | ||
| 726 | "Parameters:\n" | ||
| 727 | " -game <appid> - Game AppID to install / update / uninstall\n" | ||
| 728 | " (use '-command list' to see available games)\n" | ||
| 729 | " -dir <installdir> - Game install dir\n" | ||
| 730 | " (if dir not specified, will use %s/steamapps/common/<gamename>/)\n" | ||
| 731 | " -username <username> - Steam account username\n" | ||
| 732 | " -password <password> - Steam account password\n" | ||
| 733 | " -steam_guard_code <code> - Steam guard authorization code\n" | ||
| 734 | " -beta <beta_name> - Beta name to use\n" | ||
| 735 | " -beta_password <password> - Beta password (if any)\n" | ||
| 736 | "\n" | ||
| 737 | "Flags:\n" | ||
| 738 | " -verify_all - Verify all game files are up to date\n" | ||
| 739 | " -remember_password - Remember password\n" | ||
| 740 | "\n" | ||
| 741 | "For example: %s%s -command update -game 740 -dir %s -username foo -password bar\n" | ||
| 742 | , commandLine.GetParm(0), | ||
| 743 | #ifdef _WIN32 | ||
| 744 | "" | ||
| 745 | #else | ||
| 746 | ".sh" | ||
| 747 | #endif | ||
| 748 | , commandLine.GetParm(0), | ||
| 749 | #ifdef _WIN32 | ||
| 750 | "" | ||
| 751 | #else | ||
| 752 | ".sh" | ||
| 753 | #endif | ||
| 754 | , | ||
| 755 | #ifdef _WIN32 | ||
| 756 | ".", | ||
| 757 | #else | ||
| 758 | "~/Steam", | ||
| 759 | #endif | ||
| 760 | commandLine.GetParm(0), | ||
| 761 | #ifdef _WIN32 | ||
| 762 | "" | ||
| 763 | #else | ||
| 764 | ".sh" | ||
| 765 | #endif | ||
| 766 | , | ||
| 767 | #ifdef _WIN32 | ||
| 768 | "C:\\csgo" | ||
| 769 | #else | ||
| 770 | "/csgo" | ||
| 771 | #endif | ||
| 772 | ); | ||
| 773 | return false; | ||
| 774 | } | ||
| 775 | |||
| 776 | const char* cszScript = commandLine.ParmValue("+runscript"); | ||
| 777 | if(cszScript) | ||
| 778 | { | ||
| 779 | if(!ParseScript(cszScript)) | ||
| 780 | return false; | ||
| 781 | } | ||
| 782 | |||
| 783 | const char* cszCommand = commandLine.ParmValue("-command"); | ||
| 784 | const char* cszUsername = commandLine.ParmValue("-username"); | ||
| 785 | const char* cszPassword = commandLine.ParmValue("-password"); | ||
| 786 | const char* cszDir = commandLine.ParmValue("-dir"); | ||
| 787 | |||
| 788 | if(!cszCommand) | ||
| 789 | { | ||
| 790 | Error("No command specified.\n"); | ||
| 791 | return false; | ||
| 792 | } | ||
| 793 | |||
| 794 | if(strcasecmp(cszCommand, "list") != 0 && strcasecmp(cszCommand, "update") != 0 && strcasecmp(cszCommand, "uninstall") != 0) | ||
| 795 | { | ||
| 796 | Error("Invalid command specified.\n"); | ||
| 797 | return false; | ||
| 798 | } | ||
| 799 | |||
| 800 | if(strcasecmp(cszCommand, "update") == 0 || strcasecmp(cszCommand, "uninstall") == 0) | ||
| 801 | { | ||
| 802 | if(commandLine.FindParm("-game") == 0) | ||
| 803 | { | ||
| 804 | Error("No game specified.\n"); | ||
| 805 | return false; | ||
| 806 | } | ||
| 807 | else if(commandLine.ParmValue("-game", (int)k_uAppIdInvalid) == k_uAppIdInvalid) | ||
| 808 | { | ||
| 809 | Error("Invalid game specified %s.\n", commandLine.ParmValue("-game", "")); | ||
| 810 | return false; | ||
| 811 | } | ||
| 812 | } | ||
| 813 | |||
| 814 | if(cszDir && !IsDir(cszDir)) | ||
| 815 | { | ||
| 816 | Error("'%s' doesn't exist or isn't a directory.\n", cszDir); | ||
| 817 | return false; | ||
| 818 | } | ||
| 819 | |||
| 820 | return true; | ||
| 821 | } | ||
| 822 | |||
| 823 | bool CApplication::LogOn() | ||
| 824 | { | ||
| 825 | g_pClientAppManager->SetDownloadingEnabled(false); | ||
| 826 | m_bInitialConnection = true; | ||
| 827 | |||
| 828 | const char* cszUsername = commandLine.ParmValue("-username"); | ||
| 829 | if(!cszUsername) | ||
| 830 | { | ||
| 831 | #ifdef _WIN32 | ||
| 832 | void (__thiscall* pSetValue)(void* pThis, const char* cszValue) = (void (__thiscall *)(void *,const char *)) (*(void***)g_pUsePICS)[12]; | ||
| 833 | #else | ||
| 834 | void (*pSetValue)(void* pThis, const char* cszValue) = (void (*)(void *,const char *)) (*(void***)g_pUsePICS)[13]; | ||
| 835 | #endif | ||
| 836 | pSetValue(g_pUsePICS, "1"); | ||
| 837 | |||
| 838 | g_pClientUser->LogOn(false, CSteamID(-1, k_EUniversePublic, k_EAccountTypeAnonUser)); // HACK: The accountID should be 0 not -1, but 0 triggers asserts in the config store. | ||
| 839 | |||
| 840 | Msg("Logging on Steam anonymously ...\n"); | ||
| 841 | } | ||
| 842 | else | ||
| 843 | { | ||
| 844 | const char* cszPassword = commandLine.ParmValue("-password"); | ||
| 845 | |||
| 846 | m_bWaitingForCredentials = false; | ||
| 847 | |||
| 848 | if(!cszPassword) | ||
| 849 | { | ||
| 850 | bool bSuccess = g_pClientUser->SetAccountNameForCachedCredentialLogin(cszUsername, false); | ||
| 851 | if(!bSuccess) | ||
| 852 | { | ||
| 853 | Error("No cached credential found for account %s.\n", cszUsername); | ||
| 854 | return false; | ||
| 855 | } | ||
| 856 | } | ||
| 857 | else | ||
| 858 | { | ||
| 859 | bool bRememberPassword = commandLine.FindParm("-remember_password") != 0; | ||
| 860 | |||
| 861 | m_bWaitingForCredentials = bRememberPassword; | ||
| 862 | g_pClientUser->SetLoginInformation(cszUsername, cszPassword, bRememberPassword); | ||
| 863 | |||
| 864 | const char* cszSteamGuardCode = commandLine.ParmValue("-steam_guard_code"); | ||
| 865 | if(cszSteamGuardCode) | ||
| 866 | { | ||
| 867 | g_pClientUser->Set2ndFactorAuthCode(cszSteamGuardCode, false); | ||
| 868 | } | ||
| 869 | } | ||
| 870 | |||
| 871 | char szSteamIDKey[256]; | ||
| 872 | snprintf(szSteamIDKey, sizeof(szSteamIDKey) - 1, "Software/Valve/Steam/Accounts/%s/SteamID", cszUsername); | ||
| 873 | szSteamIDKey[sizeof(szSteamIDKey) - 1] = '\0'; | ||
| 874 | |||
| 875 | uint64 ullSteamID = g_pClientConfigStore->GetUint64(k_EConfigStoreInstall, szSteamIDKey, 0); | ||
| 876 | |||
| 877 | g_pClientUser->LogOn(false, CSteamID(ullSteamID & 0xFFFFFFFF, k_unSteamUserConsoleInstance, k_EUniversePublic, k_EAccountTypeIndividual)); | ||
| 878 | |||
| 879 | Msg("Logging on Steam with account %s ...\n", cszUsername); | ||
| 880 | } | ||
| 881 | |||
| 882 | return true; | ||
| 883 | } | ||
| 884 | |||
| 885 | bool CApplication::RunFrame() | ||
| 886 | { | ||
| 887 | if(!m_bWaitingForCredentials || !g_pClientUser->BLoggedOn()) | ||
| 888 | { | ||
| 889 | if(m_bExit) | ||
| 890 | { | ||
| 891 | return false; | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | SteamAPI_RunCallbacks(); | ||
| 896 | |||
| 897 | if(m_uInstallingAppId != k_uAppIdInvalid) | ||
| 898 | { | ||
| 899 | EAppState eState = g_pClientAppManager->GetAppInstallState(m_uInstallingAppId); | ||
| 900 | if(eState & k_EAppStateUpdateRequired || eState & k_EAppStateValidating || eState & k_EAppStateUpdateRunning) | ||
| 901 | { | ||
| 902 | AppUpdateInfo_s updateInfo; | ||
| 903 | if(g_pClientAppManager->GetUpdateInfo(m_uInstallingAppId, &updateInfo)) | ||
| 904 | { | ||
| 905 | uint64 ullCurrent; | ||
| 906 | uint64 ullMax; | ||
| 907 | |||
| 908 | if(updateInfo.m_unBytesDownloaded == updateInfo.m_unBytesToDownload) | ||
| 909 | { | ||
| 910 | ullCurrent = updateInfo.m_unBytesProcessed; | ||
| 911 | ullMax = updateInfo.m_unBytesToProcess; | ||
| 912 | } | ||
| 913 | else | ||
| 914 | { | ||
| 915 | ullCurrent = updateInfo.m_unBytesDownloaded; | ||
| 916 | ullMax = updateInfo.m_unBytesToDownload; | ||
| 917 | } | ||
| 918 | |||
| 919 | float flProgress = float(ullCurrent * (double)100.00f / ullMax); | ||
| 920 | if(!isfinite(flProgress)) | ||
| 921 | flProgress = 0.0; | ||
| 922 | |||
| 923 | char szCurrent[128]; | ||
| 924 | char szMax[128]; | ||
| 925 | FormatSize(szCurrent, sizeof(szCurrent), ullCurrent); | ||
| 926 | FormatSize(szMax, sizeof(szMax), ullMax); | ||
| 927 | |||
| 928 | ProgressMsg("%s, progress: %.2f%% (%s / %s)", AppState2String(eState), flProgress, szCurrent, szMax); | ||
| 929 | } | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | return true; | ||
| 934 | } | ||
| 935 | |||
| 936 | int main(int argc, char** argv) | ||
| 937 | { | ||
| 938 | CApplication app(argc, argv); | ||
| 939 | |||
| 940 | if(!app.CheckCommandline() || !app.InitSteam() || !app.LogOn()) | ||
| 941 | { | ||
| 942 | app.ShutdownSteam(); | ||
| 943 | return EXIT_FAILURE; | ||
| 944 | } | ||
| 945 | |||
| 946 | while(app.RunFrame()) | ||
| 947 | { | ||
| 948 | mSleep(100); | ||
| 949 | } | ||
| 950 | |||
| 951 | app.ShutdownSteam(); | ||
| 952 | |||
| 953 | return app.GetExitCode(); | ||
| 954 | } | ||
