summaryrefslogtreecommitdiffstats
path: root/main.cpp
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2013-03-09 15:14:01 +0100
committermanuel <manuel@mausz.at>2013-03-09 15:14:01 +0100
commit41f7119d8631a142fa5a97285a8443f9d7eb7e14 (patch)
tree75e35bffcfc8e82c989331335e11bac0c86da131 /main.cpp
downloadsteamcmd-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.cpp954
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
24IClientEngine* g_pClientEngine = NULL;
25IClientUser* g_pClientUser = NULL;
26IClientAppManager* g_pClientAppManager = NULL;
27IClientApps* g_pClientApps = NULL;
28IClientBilling* g_pClientBilling = NULL;
29IClientConfigStore* g_pClientConfigStore = NULL;
30IClientUtils* g_pClientUtils = NULL;
31
32HSteamPipe g_hPipe = 0;
33HSteamUser g_hUser = 0;
34
35class CApplication
36{
37public:
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
50private:
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
89void 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
104void 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
119void 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
136int CApplication::GetExitCode()
137{
138 return m_iExitCode;
139}
140
141void 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
152bool 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
174void 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
204void CApplication::OnLogOnCredentialsChanged(LogOnCredentialsChanged_t* pParam)
205{
206 m_bWaitingForCredentials = false;
207}
208
209void 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
250void 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
299CApplication::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
373CApplication::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
394void 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
411void CApplication::OnDisconnected(SteamServersDisconnected_t* pParam)
412{
413 Msg("Disconnected from Steam: %s\n", EResult2String(pParam->m_eResult));
414}
415
416void 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
430void 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
457void 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
477void* g_pUsePICS = NULL;
478
479bool 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
568bool 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
712bool 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
823bool 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
885bool 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
936int 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}