summaryrefslogtreecommitdiffstats
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
downloadsteamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.tar.gz
steamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.tar.bz2
steamcmd-41f7119d8631a142fa5a97285a8443f9d7eb7e14.zip
initial import of UpdateTool 0.4
-rw-r--r--CCommandLine.cpp79
-rw-r--r--CCommandLine.h31
-rw-r--r--CallbackDispatcher.cpp66
-rw-r--r--Makefile25
-rw-r--r--UpdateTool.sln20
-rw-r--r--UpdateTool.vcxproj102
-rw-r--r--UpdateTool.vcxproj.filters50
-rw-r--r--enum2string.cpp163
-rw-r--r--enum2string.h16
-rw-r--r--main.cpp954
-rw-r--r--utils.cpp60
-rw-r--r--utils.h14
12 files changed, 1580 insertions, 0 deletions
diff --git a/CCommandLine.cpp b/CCommandLine.cpp
new file mode 100644
index 0000000..3df07b1
--- /dev/null
+++ b/CCommandLine.cpp
@@ -0,0 +1,79 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#include "CCommandLine.h"
9#include <stdlib.h>
10#include <string.h>
11
12CCommandLine::CCommandLine(int argc, char **argv)
13{
14 m_argc = 0;
15 for(int i = 0; i < argc; i++)
16 AddParm(argv[i]);
17}
18
19CCommandLine::~CCommandLine()
20{
21 for(int i = 0; i < m_argc; i++)
22 {
23 delete[] m_argv[i];
24 }
25}
26
27void CCommandLine::AddParm(const char *psz)
28{
29 m_argv[m_argc] = new char[strlen(psz) + 1];
30 strcpy(m_argv[m_argc], psz);
31 m_argc++;
32}
33
34const char* CCommandLine::ParmValue(const char *psz, const char *pDefaultVal) const
35{
36 int nIndex = FindParm(psz);
37 if((nIndex == 0) || (nIndex == m_argc - 1))
38 return pDefaultVal;
39
40 if(m_argv[nIndex + 1][0] == '-' || m_argv[nIndex + 1][0] == '+')
41 return pDefaultVal;
42
43 return m_argv[nIndex + 1];
44}
45
46int CCommandLine::ParmValue(const char *psz, int nDefaultVal) const
47{
48 const char* cszValue = ParmValue(psz);
49
50 if(!cszValue)
51 return nDefaultVal;
52
53 return atoi(cszValue);
54}
55
56unsigned int CCommandLine::ParmCount() const
57{
58 return m_argc;
59}
60
61unsigned int CCommandLine::FindParm(const char *psz) const
62{
63 for(int i = 1; i < m_argc; i++)
64 {
65 if(strcmp(m_argv[i], psz) == 0)
66 return i;
67 }
68
69 return 0;
70}
71
72const char* CCommandLine::GetParm(unsigned int nIndex) const
73{
74 if(nIndex < (unsigned int)m_argc)
75 return m_argv[nIndex];
76
77 return NULL;
78}
79
diff --git a/CCommandLine.h b/CCommandLine.h
new file mode 100644
index 0000000..899a875
--- /dev/null
+++ b/CCommandLine.h
@@ -0,0 +1,31 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#pragma once
9
10class CCommandLine
11{
12public:
13 CCommandLine(int argc, char **argv);
14 ~CCommandLine();
15
16 const char* ParmValue(const char *psz, const char *pDefaultVal = 0) const;
17 int ParmValue(const char *psz, int nDefaultVal) const;
18
19 unsigned int ParmCount() const;
20 unsigned int FindParm(const char *psz) const;
21 const char* GetParm(unsigned int nIndex) const;
22
23 void AddParm(const char *psz);
24
25private:
26
27 static const unsigned int k_nMaxArgs = 64;
28
29 int m_argc;
30 char *m_argv[k_nMaxArgs];
31};
diff --git a/CallbackDispatcher.cpp b/CallbackDispatcher.cpp
new file mode 100644
index 0000000..12b4906
--- /dev/null
+++ b/CallbackDispatcher.cpp
@@ -0,0 +1,66 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#include "../../Open Steamworks/Steamworks.h"
9#include <map>
10
11extern HSteamPipe g_hPipe;
12
13
14std::multimap<int, CCallbackBase *> g_callbacksMap;
15
16class Callback : public CCallbackBase
17{
18public:
19 void AddRegisteredFlag()
20 {
21 m_nCallbackFlags |= k_ECallbackFlagsRegistered;
22 }
23};
24
25void STEAM_CALL SteamAPI_RegisterCallback( class CCallbackBase *pCallback, int iCallback )
26{
27 ((Callback*)pCallback)->AddRegisteredFlag();
28 g_callbacksMap.insert(std::pair<int, CCallbackBase *>(iCallback, pCallback));
29}
30
31void STEAM_CALL SteamAPI_UnregisterCallback( class CCallbackBase *pCallback )
32{
33 for(std::multimap<int, CCallbackBase *>::iterator i = g_callbacksMap.begin(); i != g_callbacksMap.end();)
34 {
35 if(i->second == pCallback)
36 {
37 g_callbacksMap.erase(i++);
38 }
39 else
40 i++;
41 }
42}
43
44void STEAM_CALL SteamAPI_RunCallbacks()
45{
46 CallbackMsg_t callbackMsg;
47 while(Steam_BGetCallback(g_hPipe, &callbackMsg))
48 {
49 // We are making a copy of the data to be able to call Steam_FreeLastCallback before sending the data to the callback handlers
50 // That will allow recursive calls to this function to work properly (ie: no infinite processing of the same callback)
51
52 unsigned char* pubData = new unsigned char[callbackMsg.m_cubParam];
53 memcpy(pubData, callbackMsg.m_pubParam, callbackMsg.m_cubParam);
54
55 Steam_FreeLastCallback(g_hPipe);
56
57 std::pair<std::multimap<int,CCallbackBase *>::iterator, std::multimap<int, CCallbackBase *>::iterator> range = g_callbacksMap.equal_range(callbackMsg.m_iCallback);
58
59 for(std::multimap<int, CCallbackBase *>::const_iterator i = range.first; i != range.second; ++i)
60 {
61 i->second->Run(pubData);
62 }
63
64 delete[] pubData;
65 }
66}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b5ce903
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
1NAME = UpdateTool
2
3CXX = g++
4CFLAGS = -O3 -I"/cygdrive/c/C++/ModuleScanner/include" -fvisibility=hidden -fvisibility-inlines-hidden
5LDFLAGS = -ldl -lpthread /cygdrive/c/C++/ModuleScanner/Linux/ModuleScanner.a ../../Resources/Libs/Linux32/steamclient.a
6SRC = $(wildcard *.cpp)
7OBJ_DIR = Objs
8OBJ = $(addprefix $(OBJ_DIR)/, $(SRC:.cpp=.o))
9
10all: dirs $(NAME)
11
12dirs:
13 mkdir -p $(OBJ_DIR)
14
15$(NAME): $(OBJ)
16 $(CXX) -o $@ $^ $(LDFLAGS)
17
18$(OBJ_DIR)/%.o: %.cpp
19 $(CXX) -o $@ -c $< $(CFLAGS)
20
21clean:
22 rm -rf $(OBJ_DIR)/*.o
23
24mrproper: clean
25 rm -f $(NAME) \ No newline at end of file
diff --git a/UpdateTool.sln b/UpdateTool.sln
new file mode 100644
index 0000000..83ef3a5
--- /dev/null
+++ b/UpdateTool.sln
@@ -0,0 +1,20 @@
1
2Microsoft Visual Studio Solution File, Format Version 11.00
3# Visual Studio 2010
4Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdateTool", "UpdateTool.vcxproj", "{484A0EB0-6B0A-4426-AB30-9A083F4C5943}"
5EndProject
6Global
7 GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 Debug|Win32 = Debug|Win32
9 Release|Win32 = Release|Win32
10 EndGlobalSection
11 GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 {484A0EB0-6B0A-4426-AB30-9A083F4C5943}.Debug|Win32.ActiveCfg = Debug|Win32
13 {484A0EB0-6B0A-4426-AB30-9A083F4C5943}.Debug|Win32.Build.0 = Debug|Win32
14 {484A0EB0-6B0A-4426-AB30-9A083F4C5943}.Release|Win32.ActiveCfg = Release|Win32
15 {484A0EB0-6B0A-4426-AB30-9A083F4C5943}.Release|Win32.Build.0 = Release|Win32
16 EndGlobalSection
17 GlobalSection(SolutionProperties) = preSolution
18 HideSolutionNode = FALSE
19 EndGlobalSection
20EndGlobal
diff --git a/UpdateTool.vcxproj b/UpdateTool.vcxproj
new file mode 100644
index 0000000..c244626
--- /dev/null
+++ b/UpdateTool.vcxproj
@@ -0,0 +1,102 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup Label="ProjectConfigurations">
4 <ProjectConfiguration Include="Debug|Win32">
5 <Configuration>Debug</Configuration>
6 <Platform>Win32</Platform>
7 </ProjectConfiguration>
8 <ProjectConfiguration Include="Release|Win32">
9 <Configuration>Release</Configuration>
10 <Platform>Win32</Platform>
11 </ProjectConfiguration>
12 </ItemGroup>
13 <PropertyGroup Label="Globals">
14 <ProjectGuid>{484A0EB0-6B0A-4426-AB30-9A083F4C5943}</ProjectGuid>
15 <Keyword>Win32Proj</Keyword>
16 <RootNamespace>UpdateTool</RootNamespace>
17 </PropertyGroup>
18 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
19 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
20 <ConfigurationType>Application</ConfigurationType>
21 <UseDebugLibraries>true</UseDebugLibraries>
22 <CharacterSet>Unicode</CharacterSet>
23 </PropertyGroup>
24 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
25 <ConfigurationType>Application</ConfigurationType>
26 <UseDebugLibraries>false</UseDebugLibraries>
27 <WholeProgramOptimization>true</WholeProgramOptimization>
28 <CharacterSet>Unicode</CharacterSet>
29 </PropertyGroup>
30 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
31 <ImportGroup Label="ExtensionSettings">
32 </ImportGroup>
33 <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
34 <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
35 </ImportGroup>
36 <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
37 <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
38 </ImportGroup>
39 <PropertyGroup Label="UserMacros" />
40 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
41 <LinkIncremental>true</LinkIncremental>
42 </PropertyGroup>
43 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
44 <LinkIncremental>false</LinkIncremental>
45 </PropertyGroup>
46 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
47 <ClCompile>
48 <PrecompiledHeader>
49 </PrecompiledHeader>
50 <WarningLevel>Level3</WarningLevel>
51 <Optimization>Disabled</Optimization>
52 <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;STEAMWORKS_CLIENT_INTERFACES;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
53 <AdditionalIncludeDirectories>$(MODULESCANNER)/include</AdditionalIncludeDirectories>
54 <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
55 </ClCompile>
56 <Link>
57 <SubSystem>Console</SubSystem>
58 <GenerateDebugInformation>true</GenerateDebugInformation>
59 <AdditionalLibraryDirectories>$(MODULESCANNER)/Windows/Debug</AdditionalLibraryDirectories>
60 <AdditionalDependencies>ModuleScanner.lib;%(AdditionalDependencies)</AdditionalDependencies>
61 </Link>
62 </ItemDefinitionGroup>
63 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
64 <ClCompile>
65 <WarningLevel>Level3</WarningLevel>
66 <PrecompiledHeader>
67 </PrecompiledHeader>
68 <Optimization>MaxSpeed</Optimization>
69 <FunctionLevelLinking>true</FunctionLevelLinking>
70 <IntrinsicFunctions>true</IntrinsicFunctions>
71 <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;STEAMWORKS_CLIENT_INTERFACES;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
72 <AdditionalIncludeDirectories>$(MODULESCANNER)/include</AdditionalIncludeDirectories>
73 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
74 </ClCompile>
75 <Link>
76 <SubSystem>Console</SubSystem>
77 <GenerateDebugInformation>true</GenerateDebugInformation>
78 <EnableCOMDATFolding>true</EnableCOMDATFolding>
79 <OptimizeReferences>true</OptimizeReferences>
80 <AdditionalLibraryDirectories>$(MODULESCANNER)/Windows/Release</AdditionalLibraryDirectories>
81 <AdditionalDependencies>ModuleScanner.lib;%(AdditionalDependencies)</AdditionalDependencies>
82 </Link>
83 </ItemDefinitionGroup>
84 <ItemGroup>
85 <ClCompile Include="CallbackDispatcher.cpp" />
86 <ClCompile Include="CCommandLine.cpp" />
87 <ClCompile Include="enum2string.cpp" />
88 <ClCompile Include="main.cpp" />
89 <ClCompile Include="utils.cpp" />
90 </ItemGroup>
91 <ItemGroup>
92 <Library Include="..\..\Resources\Libs\Win32\steamclient.lib" />
93 </ItemGroup>
94 <ItemGroup>
95 <ClInclude Include="CCommandLine.h" />
96 <ClInclude Include="enum2string.h" />
97 <ClInclude Include="utils.h" />
98 </ItemGroup>
99 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
100 <ImportGroup Label="ExtensionTargets">
101 </ImportGroup>
102</Project> \ No newline at end of file
diff --git a/UpdateTool.vcxproj.filters b/UpdateTool.vcxproj.filters
new file mode 100644
index 0000000..a4c6716
--- /dev/null
+++ b/UpdateTool.vcxproj.filters
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="main.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="CallbackDispatcher.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="enum2string.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="CCommandLine.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 <ClCompile Include="utils.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
33 </ItemGroup>
34 <ItemGroup>
35 <Library Include="..\..\Resources\Libs\Win32\steamclient.lib">
36 <Filter>Resource Files</Filter>
37 </Library>
38 </ItemGroup>
39 <ItemGroup>
40 <ClInclude Include="enum2string.h">
41 <Filter>Header Files</Filter>
42 </ClInclude>
43 <ClInclude Include="CCommandLine.h">
44 <Filter>Header Files</Filter>
45 </ClInclude>
46 <ClInclude Include="utils.h">
47 <Filter>Header Files</Filter>
48 </ClInclude>
49 </ItemGroup>
50</Project> \ No newline at end of file
diff --git a/enum2string.cpp b/enum2string.cpp
new file mode 100644
index 0000000..870aeba
--- /dev/null
+++ b/enum2string.cpp
@@ -0,0 +1,163 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#include "../../Open Steamworks/Steamworks.h"
9
10const char* AppState2String(EAppState eState)
11{
12 if(eState & k_EAppStateReconfiguring)
13 return "Reconfiguring";
14 if(eState & k_EAppStatePreallocating)
15 return "Preallocating";
16 if(eState & k_EAppStateDownloading)
17 return "Downloading";
18 if(eState & k_EAppStateStaging)
19 return "Staging";
20 if(eState & k_EAppStateCommitting)
21 return "Committing";
22 if(eState & k_EAppStateValidating)
23 return "Validating";
24 if(eState & k_EAppStateFullyInstalled)
25 return "Installed";
26 if (eState & k_EAppStateUpdateRequired)
27 return "Update required";
28 if (eState & k_EAppStateUninstalled)
29 return "Not installed";
30
31 return "Unknown state";
32}
33
34const char* EResult2String(EResult eResult)
35{
36 static const char* s_szStrings[] =
37 {
38 "Invalid EResult",
39 "OK",
40 "Failure",
41 "No Connection",
42 "Invalid EResult",
43 "Invalid Password",
44 "Logged In Elsewhere",
45 "Invalid Protocol",
46 "Invalid Parameter",
47 "File Not Found",
48 "Busy",
49 "Invalid State",
50 "Invalid Name",
51 "Invalid Email",
52 "Duplicate Name",
53 "Access Denied",
54 "Timeout",
55 "Banned",
56 "Account Not Found",
57 "Invalid Steam ID",
58 "Service Unavailable",
59 "Not Logged On",
60 "Pending",
61 "Encryption Failure",
62 "Insufficient Privilege",
63 "Limit exceeded",
64 "Request revoked",
65 "License expired",
66 "Already Redeemed",
67 "Duplicated Request",
68 "Already Owned",
69 "IP Address Not Found",
70 "Persistence Failed",
71 "Locking Failed",
72 "Session Replaced",
73 "Connection Failed",
74 "Handshake Failed",
75 "I/O Operation Failed",
76 "Disconnected By Remote Host",
77 "Shopping Cart Not Found",
78 "Blocked",
79 "Ignored",
80 "No match",
81 "Account Disabled",
82 "Service Read only",
83 "Account Not Featured",
84 "Administrator OK",
85 "Content version mismatch",
86 "Try another CM",
87 "Password required to kick session",
88 "Already Logged In Elsewhere",
89 "Request suspended/paused",
90 "Request has been canceled",
91 "Corrupted or unrecoverable data error",
92 "Not enough free disk space",
93 "Remote call failed",
94 "Password is not set",
95 "External Account is not linked to a Steam account",
96 "PSN Ticket is invalid",
97 "External Account linked to another Steam account",
98 "Remote File Conflict",
99 "Illegal password",
100 "Same as previous value",
101 "Account Logon Denied",
102 "Cannot Use Old Password",
103 "Invalid Login Auth Code",
104 "Account Logon Denied no mail sent",
105 "Hardware not capable of IPT",
106 "IPT init error",
107 "Operation failed due to parental control restrictions for current user",
108 "Facebook query returned an error",
109 "Expired Login Auth Code",
110 "IP Login Restriction Failed",
111 "Account Locked Down",
112 "Account Logon Denied Verified Email Required",
113 "No matching URL",
114 "Bad response",
115 };
116
117 if(eResult >= k_EResultOK && eResult <= k_EResultBadResponse)
118 return s_szStrings[eResult];
119 else
120 return s_szStrings[0];
121}
122
123const char* EAppUpdateError2String(EAppUpdateError eError)
124{
125 static const char* s_szStrings[] =
126 {
127 "No Error",
128 "Unspecified Error",
129 "Paused",
130 "Canceled",
131 "Suspended",
132 "No subscription",
133 "No connection",
134 "Connection timeout",
135 "Missing decryption key",
136 "Missing configuration",
137 "Missing content",
138 "Disk IO failure",
139 "Not enough disk space",
140 "Corrupt game files",
141 "Waiting for disk",
142 "Invalid install path",
143 "Application running",
144 "Dependency failure",
145 "Not installed",
146 "Update required",
147 "Still busy",
148 "No connection to content servers",
149 "Invalid application configuration",
150 "Invalid content configuration",
151 "Missing manifest",
152 "Not released",
153 "Region restricted",
154 "Corrupt depot cache",
155 "Missing executable",
156 "Invalid platform",
157 };
158
159 if(eError >= k_EAppErrorNone && eError <= k_EAppErrorInvalidPlatform)
160 return s_szStrings[eError];
161 else
162 return "Invalid EAppUpdateError";
163}
diff --git a/enum2string.h b/enum2string.h
new file mode 100644
index 0000000..3e441b6
--- /dev/null
+++ b/enum2string.h
@@ -0,0 +1,16 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#pragma once
9
10enum EAppState;
11enum EResult;
12enum EAppUpdateError;
13
14const char* AppState2String(EAppState eState);
15const char* EResult2String(EResult eResult);
16const char* EAppUpdateError2String(EAppUpdateError eError);
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}
diff --git a/utils.cpp b/utils.cpp
new file mode 100644
index 0000000..1a82531
--- /dev/null
+++ b/utils.cpp
@@ -0,0 +1,60 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#include <sys/stat.h>
9#include "../../Open Steamworks/Steamworks.h"
10
11extern IClientApps* g_pClientApps;
12
13const char* GetAppName(AppId_t uAppId)
14{
15 static char szName[25][256];
16 static unsigned int i = 0;
17 i = (i + 1) % (sizeof(szName) / sizeof(*szName));
18
19 int iSize = g_pClientApps->GetAppData(uAppId, "common/name", szName[i], sizeof(szName[i]));
20 if(iSize <= 0)
21 strcpy(szName[i], "Unknown App");
22
23 return szName[i];
24}
25
26void FormatSize(char* szOutput, unsigned int uOutputSize, unsigned long long ullBytes)
27{
28 static const char *suffixes[] = {"iB", "KiB", "MiB", "GiB", "TiB", "PiB"};
29
30 int i;
31 long double flSize = ullBytes;
32 for(i = 0; flSize >= 1024 && i < sizeof(suffixes) / sizeof(*suffixes) - 1 ; flSize /= 1024, i++);
33
34 szOutput[uOutputSize - 1] = '\0';
35 snprintf(szOutput, uOutputSize - 1, "%.2Lf %s", flSize, suffixes[i]);
36}
37
38bool IsDir(const char* cszPath)
39{
40#ifdef _WIN32
41 DWORD dwAttrib = GetFileAttributesA(cszPath);
42
43 return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
44#else
45 struct stat dirStat;
46 if(stat(cszPath, &dirStat) != 0 || !S_ISDIR(dirStat.st_mode))
47 return false;
48
49 return true;
50#endif
51}
52
53void mSleep(unsigned int uMS)
54{
55#ifdef _WIN32
56 Sleep(uMS);
57#else
58 usleep(uMS * 1000);
59#endif
60}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..6530e3f
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,14 @@
1/*
2 This file is a part of "Didrole's Update Tool"
3 ©2k12, Didrole
4
5 License : Public domain
6*/
7
8#pragma once
9typedef unsigned int AppId_t;
10
11const char* GetAppName(AppId_t uAppId);
12void FormatSize(char* szOutput, unsigned int uOutputSize, unsigned long long ullBytes);
13bool IsDir(const char* cszPath);
14void mSleep(unsigned int uMS);