-
Notifications
You must be signed in to change notification settings - Fork 0
/
PS3-GUDT.py
207 lines (178 loc) · 8.28 KB
/
PS3-GUDT.py
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
# -*- coding: utf-8 -*-
import os
import argparse
import traceback
import urllib.request
import urllib3
import xmltodict
import hashlib
import yaml
import datetime
import time
version = "PS3-Game-Update-Download-Tool v1.2"
PS3GameUpdateDataURL = "https://a0.ww.np.dl.playstation.net/tpl/np/%s/%s-ver.xml"
args = None
logFile = None
requestDelay = [0, 0]
delayCounter = 0
def getxml(url):
urllib3.disable_warnings()
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
response = http.request('GET', url)
try:
data = xmltodict.parse(response.data)
return data
except:
# print("Failed to parse xml from response (%s)" % traceback.format_exc())
return None
def sha1sumChecker(file_name, sha1sum):
# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!
sha1 = hashlib.sha1()
with open(file_name, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
sha1.update(data)
# print("SHA1: {0}".format(sha1.hexdigest()))
return sha1.hexdigest() == sha1sum
def formatByteSize(n):
n = int(n)
sizeDesignator = "B"
if n > 1000:
n = round(n / 1000, 2)
sizeDesignator = "KB"
if n > 1000:
n = round(n / 1000, 2)
sizeDesignator = "MB"
if n > 1000:
n = round(n / 1000, 2)
sizeDesignator = "GB"
return str(n) + sizeDesignator
def delay():
global requestDelay, delayCounter
if requestDelay[0] != 0:
delayCounter += 1
if delayCounter > requestDelay[1]:
delayCounter = 0
time.sleep(requestDelay[0])
def removeIllegalFileNameCharacters(filename):
return filename.replace(":", "-").replace("\n", " ").replace("/", "-").replace("*", "-").replace("?", " ")\
.replace("\"", "'").replace("<", "-").replace(">", "-")
def downloadPackage(package, downloadFolder, overwriteExistingFiles):
packageURL = package["@url"]
packageName = package["@url"].split("/")[-1]
print(" * Downloading version %s: \"%s\" (%s)" %
(package["@version"], packageURL, formatByteSize(package["@size"])))
logFile.write("Beginning download of %s (%s)\n" % (packageName, formatByteSize(package["@size"])))
# If download folder does not exist, create it
if not os.path.exists(downloadFolder):
logFile.write(" * Creating directory \"%s\"" % downloadFolder)
os.makedirs(downloadFolder)
# If file already exists and file overwriting is disabled, skip it
if os.path.exists(downloadFolder + os.path.sep + packageName) and not overwriteExistingFiles:
print("\t+ File already exists. Skipping.")
logFile.write(" * File already exists for version %s. Skipping.\n" % package["@version"])
return
logFile.write(" * Version %s: %s\n" % (package["@version"], packageURL))
# Make sure log file is written before starting a download
logFile.flush()
os.fsync(logFile.fileno())
# Download update file
urllib.request.urlretrieve(packageURL, downloadFolder + os.path.sep + packageName)
# File hashes appear to always be different. Unsure why.
# print("\t+Comparing file hash...", end="")
# print("Pass") if sha1sumChecker(downloadFolder + os.path.sep + packageName, package["@sha1sum"]) else print("Fail")
def main(game_ID, downloadFolder, overwriteExistingFiles=False):
if not downloadFolder[-1] == os.path.sep:
downloadFolder += os.path.sep
game_ID = game_ID.upper()
print("Retrieving update data for \"%s\" from \"%s\"" % (game_ID, PS3GameUpdateDataURL % (game_ID, game_ID)))
logFile.write("Retrieving update data for \"%s\" from \"%s\"\n" % (game_ID, PS3GameUpdateDataURL % (game_ID, game_ID)))
xml = getxml(PS3GameUpdateDataURL % (game_ID, game_ID))
logFile.write("Data retrieved: ")
# If the XML file does not exist, no updates are available
if xml is None or xml.__contains__('Error'):
print("There are no updates available for this game")
logFile.write("There are no updates available for this game\n")
return
# If there are multiple updates for a game
if type(xml["titlepatch"]["tag"]["package"]) is list:
gameName = removeIllegalFileNameCharacters(xml["titlepatch"]["tag"]["package"][-1]["paramsfo"]["TITLE"])
print("Updates were found for \"%s\"" % gameName)
logFile.write("Updates were found for \"%s\"\n" % gameName)
downloadFolder += ("%s [%s] Updates" % (gameName, game_ID))
for package in xml["titlepatch"]["tag"]["package"]:
if args.downloadUpdates:
downloadPackage(package, downloadFolder, overwriteExistingFiles)
else:
f = open(downloadFolder + "update_links.txt", "a")
f.write(package["@url"] + "\n")
f.close()
delay()
# If there is only one update for a game
else:
gameName = removeIllegalFileNameCharacters(xml["titlepatch"]["tag"]["package"]["paramsfo"]["TITLE"])
logFile.write("Updates were found for \"%s\"\n" % gameName)
print("Updates were found for \"%s\"" % gameName)
downloadFolder += ("%s [%s] Updates" % (gameName, game_ID))
package = xml["titlepatch"]["tag"]["package"]
if args.downloadUpdates:
downloadPackage(package, downloadFolder, overwriteExistingFiles)
else:
f = open(downloadFolder + ".txt", "a")
f.write(package["@url"] + "\n")
f.close()
print("All updates have been downloaded!")
delay()
if __name__ == '__main__':
# Set up argument parser
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version", action="version", version=version)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--game_id", dest="gameID", default=None,
help="ID of PlayStation 3 game (eg. BCUS98114)")
group.add_argument("--game_list", dest="gameList", default=None,
help="Path to the games.yml file created by RPCS3")
parser.add_argument("--dest", dest="downloadFolder", default="PS3 Game Update Downloads",
help="Path to folder where downloads should be stored")
parser.add_argument("--overwrite", dest="overwrite", action="store_true",
help="Overwrite existing files")
parser.add_argument("--store", dest="downloadUpdates", action="store_false",
help="Create list of update download links instead of downloading updates directly")
parser.add_argument("--delay", dest="requestDelay", nargs='+', default=[0, 0],
help="Add a time delay between requests to SONY's servers")
# Parse arguments
args = parser.parse_args()
if len(args.requestDelay) == 1:
args.requestDelay = [args.requestDelay[0], 1]
if len(args.requestDelay) != 2:
print("Delay only takes either one or two arguments.")
print(" * DELAY_DURATION or DELAY_DURATION DELAY_INTERVAL")
exit(1)
requestDelay[0] = float(args.requestDelay[0])
requestDelay[1] = float(args.requestDelay[1])
# Set up log file
logFile = open('log.txt', 'a', encoding="utf-8")
logFile.write(("#"*120) + "\n")
logFile.write("Script (%s) started at %s (Local %s)\n" %
(version, datetime.datetime.utcnow().isoformat(), datetime.datetime.now().isoformat()))
logFile.write("Inputs: game_id %s game_list %s dest %s overwrite %s delay %s\n" %
(args.gameID, args.gameList, args.downloadFolder, args.overwrite, args.requestDelay))
logFile.write(("=" * 120) + "\n")
# If download folder does not exist, create it
if not os.path.exists(args.downloadFolder):
logFile.write("Creating directory \"%s\"\n" % args.downloadFolder)
os.makedirs(args.downloadFolder)
# Download a specific games updates
if args.gameID is not None:
main(args.gameID, args.downloadFolder, args.overwrite)
# Download updates for multiple games at a time
else:
with open(args.gameList) as f:
gameIDs = yaml.load(f, Loader=yaml.loader.SafeLoader)
logFile.write("Loaded games list from %s\n" % args.gameList)
for ID in gameIDs:
main(ID, args.downloadFolder, args.overwrite)
logFile.close()