-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathsf_install
More file actions
executable file
·292 lines (275 loc) · 12.2 KB
/
Copy pathsf_install
File metadata and controls
executable file
·292 lines (275 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/bin/env python3
import os
os.chdir(os.path.dirname(os.path.realpath(__file__)))
from stat import S_IRWXU, S_IRWXG, S_IROTH
from urllib.request import urlopen, Request
from getpass import getpass
from collections import defaultdict
from installer.steamcmd import SteamCmdInterface
from installer.steaminterface import LaunchInfo
from installer.steam import SteamNativeInterface
from traceback import print_exc
from json import load, dump
from installer.dllsearch import findMatchingLibrary
import subprocess
import tempfile
import re
import argparse
def find_steamapi_dll(installdir):
for root, dirs, files in os.walk(installdir):
if "steam_api.dll" in files:
return os.path.join(root, "steam_api.dll")
print("Can not find steam_api.dll inside the game directory! Probably, " +
"the game is incompatible with SteamForwarder")
exit(1)
def detectSteamAppsInVolume(voldir):
for thedir in os.listdir(voldir):
steamappsdir = os.path.join(voldir, thedir)
if re.match("[Ss]team[Aa]pps",thedir) and os.path.isdir(steamappsdir):
return steamappsdir
raise Exception("Can not find steamapps directory in the volume folder: " +
voldir)
def get_app_config_http(appid):
namere = re.compile("""<td itemprop="name">(?P<name>[^<>]+)</td>""")
startinfore = re.compile("""<tr>[\n\s]*
<td>(?P<description>.*?)</td>[\n\s]*
<td>(?P<executable>[^<>]*)</td>[\n\s]*
<td>(?P<arguments>[^<>]*)</td>[\n\s]*
<td>(?P<type>.*?)</td>[\n\s]*
<td\ class="nowrap">[\n\s]*
(?P<os>
<i\ class="[^"]+"\ aria-label="Windows">[\n\s]*
</i>[\n\s]*)?
</td>[\n\s]*
<td>(?P<extra>[^<>]*)</td>[\n\s]*
</tr>""", re.X)
installdirre = re.compile("""<tr>[\n\s]*
<td>installdir</td>[\n\s]*
<td>(?P<installdir>[^<>]+)</td>[\n\s]*
</tr>[\n\s]*
""", re.X)
configurl = "http://steamdb.info/app/" + str(appid) + "/config/"
hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)'}
req = Request(configurl, headers = hdr)
content = urlopen(req).read().decode('utf-8')
startinfos = dict()
startinfos["id"] = appid
startinfos["infos"] = dict()
startinfos["name"] = namere.search(content).groupdict()["name"]
installsearch = installdirre.search(content)
if installsearch is None:
raise Exception("Incorrect AppID!!!")
installdir = installsearch.groupdict()["installdir"]
startinfos["installdir"] = installdir
for startinfo_m in startinfore.finditer(content):
match_dict = startinfo_m.groupdict()
startinfo = LaunchInfo(match_dict, installdir)
name = startinfo.description
if '<' in name or name == "":
name = startinfos["name"]
startinfos["infos"][name] = startinfo
return startinfos
def generate_manifest(appinfos):
manifest = """
"AppState"
{
"appid" "%d"
"Universe" "1"
"name" "%s"
"StateFlags" "4"
"installdir" "%s"
"AutoUpdateBehavior" "1"
}
""" % (appinfos["id"], appinfos["name"], appinfos["installdir"])
return manifest
def generate_runscript(appinfo: LaunchInfo, config, ldpaths, dlldir, steamapps):
print("...for " + appinfo.executable)
libsteamapi = ":".join(ldpaths)
runscript = """
#!/bin/bash
export WINEPREFIX="{0}"
export WINEDLLPATH+=":{4}:{8}"
export LD_LIBRARY_PATH+=":{5}/bin32:{5}/bin64:{8}"
export WINEDEBUG="trace+steam_api"
export WINEDLLOVERRIDES="*steam_api=b;*steam_api64=b"
export SteamAppId="{6}"
export SteamControllerAppId="{6}"
export SteamGameId="{6}"
export SteamUser="{7}"
export SteamAppUser="{7}"
cd "{1}/common/{9}"
LD_PRELOAD="gameoverlayrenderer.so" wine "{1}/common/{2}" {3} &> "$(dirname "$0")/lastrun.log"
""".format(config['wineprefix'], steamapps, appinfo.executable,
appinfo.arguments, dlldir, config['steampath'],
config['appid'], config['login'], libsteamapi, appinfo.workdir)
return runscript
config = defaultdict(str)
configdir = os.getenv("HOME") + '/.config/SteamForwarder/'
try:
with open(configdir + "config.json", "r") as f:
newconfig = load(f)
for k, v in newconfig.items():
config[k] = v
except:
print("Configuration file not found! Using default values... " +
"You may change them at " + configdir)
config['steampath'] = os.getenv("HOME")+"/.steam"
config['login'] = 'anonymous'
os.makedirs(configdir, exist_ok=True)
with open(configdir + "config.json", "w") as f:
dump(config, f, indent=2)
if not 'steamcmdclient' in config:
tmpsteamcmd = '/usr/share/steamcmd/linux32/steamclient.so'
if os.path.isfile(tmpsteamcmd):
config['steamcmdclient'] = tmpsteamcmd
else:
config['steamcmdclient'] = ''
class printvollist(argparse.Action):
def __call__(self, parser, ns, values, ostring):
if ns.steamnative:
steaminterface = SteamNativeInterface(config)
else:
steaminterface = SteamCmdInterface(config)
for p in steaminterface.vollist:
print(p)
quit(0)
def parselist(inp):
return inp.split(',')
aparser = argparse.ArgumentParser(description="The windows steam games " +
"installation script")
aparser.add_argument('--steamnative', default=False, dest='steamnative',
help='use native steam instead of steamcmd (experimental)',
action='store_true')
aparser.add_argument('--volumelist', nargs=0,
help='print the list of available volumes in your steam',
action=printvollist)
aparser.add_argument('appid', type=int, metavar='appID',
help="appid of the game to be installed")
aparser.add_argument('-w', '--wineprefix', type=str, dest='wineprefix',
help='path to wineprefix to be used to launch this app',
default=os.getenv('HOME') + '/.wine')
aparser.add_argument('-l', '--login', help='your login at steam ' +
'(may be needed only without --steamnative)', type=str,
dest='login', default=config['login'])
aparser.add_argument('--with-dlc', help='download dlc content from depot',
dest='dlcdepot', default=list(), type=parselist)
aparser.add_argument('--dlc-only', help='download only dlc depot, without ' +
'the game itself',
dest='dlconly', default=False, action='store_true')
aparser.add_argument('--depot', help='download directly from steam depot',
dest='depot', default=False, action='store_true')
aparser.add_argument('--strict', help='do not try to match the precompiled ' +
'library partialy, compile the new one instead',
dest='strict', default=False, action='store_true')
aparser.add_argument('--depot-path', type=str, default=config["depotpath"],
help='path where temporary data will be stored while ' +
'depot downloading (needed only without --steamnative)',
dest='depotpath')
aparser.add_argument('--steamcmdclient', dest='steamclient', type=str,
help='path to steamclient.so related to steamcmd' +
'(needed only without --steamnative and with --depot)',
default=config["steamcmdclient"])
aparser.add_argument('-o', '--steam-dir', dest='steampath', type=str,
help='path to the bin32, bin64, sdk32, sdk64 and other' +
' steam folders', default=config["steampath"])
aparser.add_argument('-p', '--password', dest='askPassword', default=False,
help='ask password of your steam account (may be ' +
'necessary for non-free apps). Note: password will ' +
'not be saved anywhere including configuration file ' +
'(may be needed only without --steamnative)',
action='store_true')
aparser.add_argument('--no-download', default=False, dest='nodl',
help='just generate steam_api.dll.so for existing game',
action='store_true')
aparser.add_argument('--store', default=False, dest='store',
help='save configuration for futher use as default',
action='store_true')
aparser.add_argument('-v', '--volume', type=int, dest='volume',
help='select steam volume index for game to download into' +
' use --volumelist to get list of volumes',
default=int(config["volume"] or 0))
aparser.add_argument('-s', '--steamapps', type=str, dest='steamapps',
help='use a custom path for the steamapps folder ' +
'(works only with --no-download option)',
default=None)
config_args = aparser.parse_args()
config['login'] = config_args.login
config['wineprefix'] = config_args.wineprefix
config['steampath'] = config_args.steampath
config['steamcmdclient'] = config_args.steamclient
config['depotpath'] = config_args.depotpath
config['volume'] = config_args.volume
config['dlcs'] = config_args.dlcdepot
config['dlconly'] = config_args.dlconly
config['strict'] = config_args.strict
appid = config_args.appid
config['appid'] = appid
if config_args.steamnative:
steaminterface = SteamNativeInterface(config)
else:
if config['steamcmdclient'] == "" and not config_args.steamnative:
print('Can not find steamcmd\'s steamclient.so location. Please specify ' +
'it using --steamcmdclient option to unlock --depot option.')
if config_args.depot:
config_args.depot = False
print("Depot downloading disabled. Falling back to default method.")
if config_args.askPassword:
config['password'] = getpass()
else:
config['password'] = ""
steaminterface = SteamCmdInterface(config)
if config_args.store:
os.makedirs(configdir, exist_ok=True)
with open(configdir + "config.json", "w") as f:
dump(config, f, indent=2)
if config_args.nodl and not config_args.steamapps is None:
steamapps = config_args.steamapps
else:
steamapps = detectSteamAppsInVolume(steaminterface.volumes[config['volume']])
print("Obtaining app info...")
try:
appinfos = steaminterface.getAppInfo()
except:
print_exc()
print("Failed to get appinfo via steamcmd, falling back to http method...")
appinfos = get_app_config_http(appid)
if config_args.depot:
print("Depot downloading is not available with http method!")
config_args.depot = False
manifest = generate_manifest(appinfos)
manifest_location = steamapps+ '/appmanifest_' + str(appid) + '.acf'
print("Generating manifest...")
with open(manifest_location, "w") as f:
f.write(manifest)
rs_location = steamapps + '/common/' + appinfos["installdir"] + '/'
if not config_args.nodl:
print("Downloading " + appinfos["name"] + " via steamcmd...")
if config_args.depot:
steaminterface.depotDownload(rs_location)
else:
steaminterface.appUpdate()
if 'regfile' in appinfos:
print("Applying registry installation file...")
content = "REGEDIT4\n" + appinfos['regfile'].replace("%INSTALLDIR%", rs_location).replace(
"%COMMON MYDOCS%", os.getenv("HOME"))
print("Following file will be applied:")
print(content)
with tempfile.NamedTemporaryFile('w') as tmpfile:
tmpfile.write(content)
tmpfile.flush()
p = subprocess.Popen(["regedit", tmpfile.name],
env={"WINEPREFIX": config["wineprefix"]})
p.wait()
print("Generating the steam_api.dll wrapper just for this game...")
ldpaths = findMatchingLibrary(rs_location, config['strict'])
print("Generating runscripts...")
for name, appinfo in appinfos["infos"].items():
runscript = generate_runscript(appinfo, config, ldpaths, rs_location, steamapps)
runscript_location = (rs_location + name + '.sh')
with open(runscript_location, "w") as f:
f.write(runscript)
os.chmod(runscript_location, S_IRWXU | S_IRWXG | S_IROTH)
print("Placing steam_appid.txt to the game location...")
with open(rs_location + '/steam_appid.txt', "w") as f:
f.write(str(appid))
print("Done! You may launch the game via scripts located at " + rs_location)