Created
April 22, 2010 21:07
-
-
Save dcramer/375813 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# usage: python <thisfile.py> help -- downloads the files to the current directory | |
# requires libmpq | |
# Attempts to brute force and download specific patches | |
# e.g. python sc2patch.py --current=0.10.0.14803 --next=0.11.0.15097 | |
# This would find and download any patches relevant, the next argument is optional, | |
# but allows you to provide a starting point. This stops at the first patch it finds, | |
# so you would need to continue with a new starting point if you wanted a different patch. | |
# e.g. if you didnt specify next in this example, it would stop at the initial 0.11 build, | |
# which wasnt publicly released | |
import urllib2 | |
import tempfile | |
import mpq | |
import math | |
import re | |
import optparse | |
decimal_match = re.compile('\d') | |
def bdecode(data): | |
'''Main function to decode bencoded data''' | |
def _dechunk(chunks): | |
item = chunks.pop() | |
if (item == 'd'): | |
item = chunks.pop() | |
hash = {} | |
while (item != 'e'): | |
chunks.append(item) | |
key = _dechunk(chunks) | |
hash[key] = _dechunk(chunks) | |
item = chunks.pop() | |
return hash | |
elif (item == 'l'): | |
item = chunks.pop() | |
list = [] | |
while (item != 'e'): | |
chunks.append(item) | |
list.append(_dechunk(chunks)) | |
item = chunks.pop() | |
return list | |
elif (item == 'i'): | |
item = chunks.pop() | |
num = '' | |
while (item != 'e'): | |
num += item | |
item = chunks.pop() | |
return int(num) | |
elif (decimal_match.findall(item)): | |
num = '' | |
while decimal_match.findall(item): | |
num += item | |
item = chunks.pop() | |
line = '' | |
for i in range(1, int(num) + 1): | |
line += chunks.pop() | |
return line | |
raise ValueError("Invalid input!") | |
chunks = list(data) | |
chunks.reverse() | |
root = _dechunk(chunks) | |
return root | |
def fetch_updates(old_version, new_version, new_build, filename, locale='enUS'): | |
def download(base_url, fname, parts=None): | |
def _get_chunk(fp, p, t=None): | |
if t: | |
print "Downloading %s part %s @ %s%%" % (fname, p, round(float(p)/parts*100,2)) | |
else: | |
print "Downloading %s part %s" % (fname, p) | |
try: | |
remote = urllib2.urlopen("%s/%s/%s" % (base_url, fname, p)) | |
for l in remote: | |
fp.write(l) | |
except urllib2.HTTPError: | |
# assume its done | |
return | |
out = open(fname, 'wb') | |
if parts: | |
for p in xrange(0, parts): | |
_get_chunk(out, p, parts) | |
else: | |
while 1: | |
_get_chunk(out, p) | |
out.close() | |
def _fetch_component(name, old_version, new_version): | |
url = 'http://dist.blizzard.com.edgesuite.net/sc2patch/update/B%sB/sc2-%s-%s-x86-Win-enUS-%s-component-dl' % (new_build, old_version, new_version, name) | |
fname = url.rsplit('/', 1)[-1] | |
out = open(fname, 'wb') | |
remote = urllib2.urlopen(url) | |
for l in remote: | |
out.write(l) | |
out.close() | |
x = mpq.Archive(str(fname)) | |
data = x['prepatch.lst'] | |
fname = str(data).split('\n')[0].split(' ', 1)[-1].strip() | |
meta = bdecode(str(x[str(fname)])) | |
chunk_size = meta['info']['piece length'] | |
files = [] | |
for fileinfo in meta['info']['files']: | |
fname = fileinfo['path'][0] | |
parts = int(math.ceil(float(fileinfo['length'])/chunk_size)) | |
files.append((fname, parts)) | |
print "Found %s (%s chunks)" % (fname, parts) | |
base_url = 'http://dist.blizzard.com.edgesuite.net/sc2patch/patches/B%sB' % (new_build,) | |
for fname, parts in files: | |
download(base_url, fname, parts) | |
_fetch_component(filename, old_version, new_version) | |
# download('sc2-%s-%s-x86-Win-%s-locale' % (old_version, new_version, locale)) | |
#http://dist.blizzard.com.edgesuite.net/sc2patch/update/B14803B/sc2-0.9.0.14621-0.10.0.14803-x86-Win-enUS-game-component-dl;sc2-0.9.0.14621-0.10.0.14803-x86-Win-enUS-game-component-dl;13EE085001134AE95E4DFC9D21E4FE67;14803 | |
# where do these come from? | |
# download('sc2-1.0.0.18720-1.0.0.19468-x86-Win-enUS-bnet-bin') | |
# download('sc2-1.0.0.4947-1.0.0.5194-x86-Win-enUS-bnet-base') | |
def find_next_version(old_version, base_version, build=None, component='game', base_build=None, verbose=True): | |
if base_build is None: | |
base_build = int(old_version.rsplit('.', 1)[-1]) | |
if build is None: | |
build = base_build | |
while True: | |
base_build += 1 | |
url = 'http://dist.blizzard.com.edgesuite.net/sc2patch/update/B%sB/sc2-%s-%s.%s-x86-Win-enUS-%s-component-dl' % (build, old_version, base_version, base_build, component) | |
fname = url.rsplit('/', 1)[-1] | |
out = open(fname, 'wb') | |
try: | |
remote = urllib2.urlopen(url) | |
except urllib2.HTTPError: | |
if verbose: | |
print 'No component found for %s at %s.%s' % (component, base_version, base_build) | |
continue | |
else: | |
if verbose: | |
print 'Successfully found %s at %s.%s' % (component, base_version, base_build) | |
return '%s.%s' % (base_version, base_build) | |
if __name__ == '__main__': | |
parser = optparse.OptionParser() | |
parser.add_option('-c', '--current', dest='current', help="Current build version (e.g. 0.10.0.14803)") | |
parser.add_option('-n', '--next', dest='next', help="Version to download (e.g. 0.11.0.15090)") | |
(options, args) = parser.parse_args() | |
current = options.current | |
if not current: | |
raise SyntaxError('You must specifiy your current build version with --current.') | |
next = options.next | |
if not next: | |
# hackish | |
base = current.split('.', 3) | |
base = '%s.%s.%s' % (base[0], int(base[1])+1, base[2]) | |
next = find_next_version(current, base) | |
# bnet-bin and bnet-build both have different build numbers | |
build = int(next.rsplit('.', 1)[-1]) | |
# this doesnt solve fetching the bnet files, as we still dont know their version and need to guess | |
fetch_updates(current, next, build, 'game') | |
fetch_updates(current, next, build, 'locale') | |
# Now we need to find the build numbers for bnet-bin and bnet-base | |
bnet_bin = find_next_version(current, base, build, 'bnet-bin') | |
fetch_updates(current, bnet_bin, build, 'bnet-bin') | |
bnet_base = find_next_version(current, base, build, 'bnet-base') | |
fetch_updates(current, bnet_base, build, 'bnet-base') | |
# now take each file, one at a time and place it in the Support folder within your install | |
# directory. Rename it to sc2-patch.mpq, and run Blizzard Updater.exe. Repeat for each file. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment