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
|
# SPDX-License-Identifier: GPL-3.0+
# Copyright (C) 2020 nlscc
import click
import os
import base64
import xml.etree.ElementTree as ET
from clint.textui import progress
from . import request
from . import crypt
from . import fusclient
from . import versionfetch
def getbinaryfile(client, fw, region, model):
req = request.binaryinform(fw, region, model, client.nonce)
resp = client.makereq("NF_DownloadBinaryInform.do", req)
root = ET.fromstring(resp)
status = root.find("./FUSBody/Results/Status").text
if status != 200:
raise Exception("DownloadBinaryInform returned {}, firmware could not be found?".format(status))
filename = root.find("./FUSBody/Put/BINARY_NAME/Data").text
path = root.find("./FUSBody/Put/MODEL_PATH/Data").text
return path, filename
def initdownload(client, filename):
req = request.binaryinit(filename, client.nonce)
resp = client.makereq("NF_DownloadBinaryInitForMass.do", req)
@click.group()
def cli():
pass
@cli.command(help="Check the update server for the latest available firmware.")
@click.argument("model")
@click.argument("region")
def checkupdate(model, region):
fw = versionfetch.getlatestver(region, model)
print(fw)
@cli.command(help="Download the specified firmware version.")
@click.argument("version")
@click.argument("model")
@click.argument("region")
@click.argument("out")
def download(version, model, region, out):
client = fusclient.FUSClient()
path, filename = getbinaryfile(client, version, region, model)
initdownload(client, filename)
if os.path.isdir(out):
out = os.path.join(out, filename)
if os.path.exists(out):
f = open(out, "ab")
start = os.stat(out).st_size
print("Resuming {} at {}".format(path+filename, start))
else:
f = open(out, "wb")
start = 0
print("Downloading {}".format(path+filename))
r = client.downloadfile(path+filename, start)
length = int(r.headers["Content-Length"])
if "Content-MD5" in r.headers:
md5 = base64.b64decode(r.headers["Content-MD5"]).hex()
print("MD5: {}".format(md5))
for chunk in progress.bar(r.iter_content(chunk_size=0x10000), expected_size=(length/0x10000)+1):
if chunk:
f.write(chunk)
f.flush()
f.close()
print("Done!")
@cli.command(help="Decrypt enc4 files.")
@click.argument("version")
@click.argument("model")
@click.argument("region")
@click.argument("infile")
@click.argument("outfile")
def decrypt4(version, model, region, infile, outfile):
key = crypt.getv4key(version, model, region)
print("Decrypting with key {}...".format(key.hex()))
length = os.stat(infile).st_size
with open(infile, "rb") as inf:
with open(outfile, "wb") as outf:
crypt.decrypt_progress(inf, outf, key, length)
print("Done!")
@cli.command(help="Decrypt enc2 files.")
@click.argument("version")
@click.argument("model")
@click.argument("region")
@click.argument("infile")
@click.argument("outfile")
def decrypt2(version, model, region, infile, outfile):
key = crypt.getv2key(version, model, region)
print("Decrypting with key {}...".format(key.hex()))
length = os.stat(infile).st_size
with open(infile, "rb") as inf:
with open(outfile, "wb") as outf:
crypt.decrypt_progress(inf, outf, key, length)
print("Done!")
if __name__ == "__main__":
cli()
|