mirror of
https://github.com/grp/Wii.py.git
synced 2025-06-18 06:45:46 -04:00
398 lines
13 KiB
Python
398 lines
13 KiB
Python
#----------------------------------------------------------------------
|
|
# NUS WAD Packer 1.0.1 - a (sorta) simple GUI for NUS downloading.
|
|
# (c) 2009 Xuzz (icefire) and Xuzz Productions.
|
|
#
|
|
# Wii.py (c) Xuzz, SquidMan, megazig, TheLemonMan, Omega|, and Matt_P.
|
|
#----------------------------------------------------------------------
|
|
|
|
import os, wx, shutil, sys, threading
|
|
import Wii
|
|
|
|
def readableTitleID(lower):
|
|
out = struct.unpack("4s", struct.pack(">I", lower))
|
|
return out[0]
|
|
|
|
def getName(titleid):
|
|
upper = (titleid >> 32)
|
|
lower = (titleid & 0xFFFFFFFF)
|
|
if(upper == 0x00000001):
|
|
if(lower > 0x02 and lower < 0x100):
|
|
return "IOS%d" % lower
|
|
elif(lower == 0x02):
|
|
return "SystemMenu"
|
|
elif(lower == 0x100):
|
|
return "BC"
|
|
elif(lower == 0x101):
|
|
return "MIOS"
|
|
else:
|
|
return "Unknown System Title (%08x)" % lower
|
|
elif(upper == 0x00010002 or upper == 0x00010008):
|
|
read = readableTitleID(lower)
|
|
|
|
if(read[3] == "K"):
|
|
rgn = "Korea"
|
|
elif(read[3] == "A"):
|
|
rgn = "All Regions"
|
|
elif(read[3] == "P"):
|
|
rgn = "Europe/PAL"
|
|
elif(read[3] == "E"):
|
|
rgn = "North America"
|
|
elif(read[3] == "J"):
|
|
rgn = "Japan"
|
|
else:
|
|
rgn = "Unknown Region"
|
|
|
|
if(read[:3] == "HAB"):
|
|
return "Wii Shop Channel (%s)" % rgn
|
|
if(read[:3] == "HAL"):
|
|
return "EULA (%s)" % rgn
|
|
if(read[:3] == "HAA"):
|
|
return "Photo Channel (%s)" % rgn
|
|
if(read[:3] == "HAC"):
|
|
return "Mii Channel (%s)" % rgn
|
|
if(read[:3] == "HAE"):
|
|
return "Wii Message Board (%s)" % rgn
|
|
if(read[:3] == "HAF"):
|
|
return "Weather Channel (%s)" % rgn
|
|
if(read[:3] == "HAG"):
|
|
return "News Channel (%s)" % rgn
|
|
if(read[:3] == "HAK"):
|
|
return "Region Select (%s)" % rgn
|
|
if(read[:3] == "HAY"):
|
|
return "Photo Channel 1.1 (%s)" % rgn
|
|
|
|
|
|
if(upper == 0x00010002):
|
|
return "Channel '%s'" % read
|
|
if(upper == 0x00010008):
|
|
return "Hidden Channel '%s'" % read
|
|
else:
|
|
return "Other (%08x-%08x)" % (upper, lower)
|
|
|
|
queue = []
|
|
|
|
class Downloader(wx.App):
|
|
def OnInit(self):
|
|
self.selected = -1
|
|
frame = wx.Frame(None, -1, "NUS WAD Packer", (150, 150), (600, 650))#, wx.SYSTEM_MENU | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.CLOSE_BOX)
|
|
panel = wx.Panel(frame)
|
|
panel.Show(True)
|
|
|
|
wx.StaticText(panel, -1, "NUS WAD Packer (c) 2009 Xuzz. Powered by the Wii.py framework.", (5, 5))
|
|
|
|
wx.StaticText(panel, -1, "Title ID:", (5, 35))
|
|
self.titleid = wx.TextCtrl(panel, -1, "", (60, 30), (150, -1))
|
|
wx.StaticText(panel, -1, "Version:", (216, 35))
|
|
self.version = wx.TextCtrl(panel, -1, "", (270, 30), (50, -1))
|
|
|
|
wx.StaticText(panel, -1, "Output:", (5, 70))
|
|
self.out = wx.TextCtrl(panel, -1, "", (60, 65), (150, -1))
|
|
browsebtn = wx.Button(panel, -1, "Browse", (216, 65), (105, 27))
|
|
self.Bind(wx.EVT_BUTTON, self.browse, browsebtn)
|
|
|
|
wx.StaticText(panel, -1, "Output Format:", (330, 35))
|
|
selectbox = ['WAD', 'Enc Contents', 'Dec Contents']
|
|
self.outputtype = wx.ComboBox(panel, -1, "", (330, 65), (125, 27), choices = selectbox, style = wx.CB_READONLY)
|
|
self.outputtype.SetStringSelection(selectbox[0])
|
|
|
|
wx.StaticBox(panel, -1, "Scripting", (460, 5), (130, 86))
|
|
loadbtn = wx.Button(panel, -1, "Load Script", (470, 25), (110, 25))
|
|
self.Bind(wx.EVT_BUTTON, self.load, loadbtn)
|
|
savescriptbtn = wx.Button(panel, -1, "Save Script", (470, 55), (110, 25))
|
|
self.Bind(wx.EVT_BUTTON, self.savescript, savescriptbtn)
|
|
|
|
addbtn = wx.Button(panel, -1, "Add", (5, 100), (75, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.add, addbtn)
|
|
savebtn = wx.Button(panel, -1, "Save", (250, 100), (75, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.save, savebtn)
|
|
|
|
rmbtn = wx.Button(panel, -1, "Remove", (85, 100), (75, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.remove, rmbtn)
|
|
|
|
upbtn = wx.Button(panel, -1, "Up", (440, 100), (75, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.up, upbtn)
|
|
downbtn = wx.Button(panel, -1, "Down", (520, 100), (75, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.down, downbtn)
|
|
|
|
|
|
self.list = wx.ListCtrl(panel, -1, (5, 140), (590, 250), wx.LC_REPORT | wx.SUNKEN_BORDER)
|
|
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.select, self.list)
|
|
self.list.Show(True)
|
|
self.list.InsertColumn(0, "Title ID")
|
|
self.list.InsertColumn(1, "Version")
|
|
self.list.InsertColumn(2, "Output")
|
|
self.list.InsertColumn(3, "Format")
|
|
|
|
|
|
wx.StaticText(panel, -1, "Message Log:", (5, 400))
|
|
self.output = wx.TextCtrl(panel, -1, "", (5, 420), (590, 170), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
|
|
font = wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL)
|
|
self.output.SetFont(font)
|
|
self.output.AppendText("NUS WAD Packer started and ready...\n\n")
|
|
|
|
|
|
downloadbtn = wx.Button(panel, -1, "Download", (5, 600), (120, -1))
|
|
self.Bind(wx.EVT_BUTTON, self.go, downloadbtn)
|
|
self.progress = wx.Gauge(panel, -1, 100, (135, 605), (460, -1))
|
|
self.progress.SetValue(0)
|
|
|
|
frame.Show(True)
|
|
|
|
return True
|
|
def getSelected(self):
|
|
i = self.list.GetFirstSelected()
|
|
self.selected = i
|
|
return i
|
|
def select(self, evt):
|
|
if(self.getSelected() == -1):
|
|
return
|
|
item = queue[self.selected]
|
|
self.titleid.SetValue(item[0])
|
|
self.version.SetValue(item[1])
|
|
self.out.SetValue(item[2])
|
|
self.outputtype.SetValue(item[3])
|
|
def setList(self):
|
|
self.list.DeleteAllItems()
|
|
for i, elem in enumerate(queue):
|
|
self.list.InsertStringItem(i, elem[0])
|
|
self.list.SetStringItem(i, 1, elem[1])
|
|
self.list.SetStringItem(i, 2, elem[2])
|
|
self.list.SetStringItem(i, 3, elem[3])
|
|
def add(self, evt):
|
|
titleid = self.titleid.GetValue()
|
|
if(len(titleid) != 16 and not titleid.isdigit()):
|
|
self.dialog("Title ID must be 16 numbers long! No dashes.")
|
|
return
|
|
ver = self.version.GetValue()
|
|
if(not ver.isdigit() and ver != ""):
|
|
self.dialog("Version must be all numbers!")
|
|
return
|
|
out = self.out.GetValue()
|
|
fmt = self.outputtype.GetValue()
|
|
if(not os.path.isdir(os.path.dirname(out)) and fmt == "WAD"):
|
|
out = os.path.expanduser("~/" + getName(int(titleid, 16)))
|
|
if(ver != ""):
|
|
out += "v" + str(ver)
|
|
out += ".wad"
|
|
elif(not os.path.isdir(os.path.dirname(out))):
|
|
out = os.path.expanduser("~/" + getName(int(titleid, 16)))
|
|
if(ver != ""):
|
|
out += "v" + str(ver)
|
|
queue.append((titleid, ver, out, fmt))
|
|
self.setList()
|
|
self.list.Select(len(queue) - 1)
|
|
def dialog(self, message):
|
|
wx.MessageBox(message, "Error")
|
|
def save(self, evt):
|
|
if(self.getSelected() == -1):
|
|
return
|
|
titleid = self.titleid.GetValue()
|
|
if(len(titleid) != 16 and titleid.isdigit()):
|
|
self.dialog("Title ID must be 16 numbers long! No dashes.")
|
|
return
|
|
ver = self.version.GetValue()
|
|
if(ver.isdigit()):
|
|
self.dialog("Version must be all numbers!")
|
|
return
|
|
out = self.out.GetValue()
|
|
fmt = self.outputtype.GetValue()
|
|
if(not os.path.isdir(os.path.dirname(out)) and fmt == "WAD"):
|
|
out = os.path.expanduser("~/" + getName(int(titleid, 16)))
|
|
if(ver != ""):
|
|
out += "v" + str(ver)
|
|
out += ".wad"
|
|
elif(not os.path.isdir(os.path.dirname(out))):
|
|
out = os.path.expanduser("~/" + getName(int(titleid, 16)))
|
|
if(ver != ""):
|
|
out += + "v" + str(ver)
|
|
queue[self.selected] = (titleid, ver, out, fmt)
|
|
self.setList()
|
|
self.list.Select(self.selected)
|
|
def remove(self, evt):
|
|
if(self.getSelected() == -1):
|
|
return
|
|
queue.pop(self.selected)
|
|
self.setList()
|
|
self.list.Select(min(len(queue) - 1, self.selected))
|
|
def browse(self, evt):
|
|
if(self.outputtype.GetValue() == "WAD"):
|
|
dlg = wx.FileDialog(None, "Browse For Destination...", "", "", "Wii WAD files (*.wad)|*.wad|All Files (*.*)|*.*", wx.SAVE)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
self.out.SetValue(dlg.GetPath())
|
|
dlg.Destroy()
|
|
else:
|
|
dlg = wx.DirDialog(None, "Browse For Destination...", "")
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
self.out.SetValue(dlg.GetPath())
|
|
dlg.Destroy()
|
|
def up(self, evt):
|
|
global queue
|
|
if(self.getSelected() == -1):
|
|
return
|
|
if(self.selected == 0):
|
|
return
|
|
tmp1 = queue[self.selected - 1]
|
|
tmp2 = queue[self.selected]
|
|
queue[self.selected - 1] = tmp2
|
|
queue[self.selected] = tmp1
|
|
self.setList()
|
|
self.list.Select(self.selected - 1)
|
|
self.getSelected()
|
|
def down(self, evt):
|
|
global queue
|
|
if(self.getSelected() == -1):
|
|
return
|
|
if(self.selected == len(queue) - 1):
|
|
return
|
|
tmp1 = queue[self.selected + 1]
|
|
tmp2 = queue[self.selected]
|
|
queue[self.selected + 1] = tmp2
|
|
queue[self.selected] = tmp1
|
|
self.setList()
|
|
self.list.Select(self.selected + 1)
|
|
self.getSelected()
|
|
def load(self, evt):
|
|
dlg = wx.FileDialog(None, "Open Script...", "", "", "Wii NUS Packer/Wiiimposter scripts (*.nus)|*.nus|All Files (*.*)|*.*", wx.OPEN)
|
|
if(dlg.ShowModal() == wx.ID_OK):
|
|
script = dlg.GetPath()
|
|
else:
|
|
dlg.Destroy()
|
|
return
|
|
dlg.Destroy()
|
|
stuff = open(script).read().split()
|
|
for i, arg in enumerate(stuff):
|
|
if(len(arg) == 16 and len(stuff) != i + 1 and stuff[i + 1].isdigit()):
|
|
tmp = (arg, str(int(stuff[i + 1], 16)), os.path.expanduser("~/" + getName(int(arg, 16)) + "v" + str(int(stuff[i + 1], 16)) + ".wad"), "WAD")
|
|
queue.append(tmp)
|
|
self.setList()
|
|
self.output.AppendText("Script loaded from %s.\n" % script)
|
|
def savescript(self, evt):
|
|
dlg = wx.FileDialog(None, "Save Script...", "", "", "Wii NUS Packer/Wiiimposter scripts (*.nus)|*.nus|All Files (*.*)|*.*", wx.SAVE)
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
script = dlg.GetPath()
|
|
else:
|
|
dlg.Destroy()
|
|
return
|
|
dlg.Destroy()
|
|
fd = open(script, "wb")
|
|
for i, elem in enumerate(queue):
|
|
fd.write("%08x%08x %04x\n" % (int(elem[0][:8], 16), int(elem[0][8:], 16), int(elem[1])))
|
|
self.output.AppendText("Script written to %s.\n" % script)
|
|
def go(self, evt):
|
|
self.progress.SetValue(0)
|
|
self.progress.SetRange(len(queue) * 3)
|
|
if(os.path.isdir("tmp")):
|
|
shutil.rmtree("tmp")
|
|
olddir = os.getcwd()
|
|
for i, elem in enumerate(queue):
|
|
os.chdir(olddir)
|
|
try:
|
|
titleid = int(elem[0], 16)
|
|
if(len(elem[1]) > 0):
|
|
ver = int(elem[1])
|
|
else:
|
|
ver = 0
|
|
except:
|
|
self.output.AppendText("Invalid title %s" % elem[0])
|
|
if(elem[1] != ""):
|
|
self.output.AppendText(" version %s" % elem[1])
|
|
self.output.AppendText(", skipping...\n\n")
|
|
continue
|
|
|
|
outfile = elem[2]
|
|
fmt = elem[3]
|
|
|
|
self.output.AppendText("Downloading %08x-%08x (%s)" % (titleid >> 32, titleid & 0xFFFFFFF, getName(titleid),))
|
|
if(ver != 0):
|
|
self.output.AppendText(" version %u" % ver)
|
|
self.output.AppendText("...")
|
|
|
|
self.progress.SetValue(i * 3 + 1)
|
|
if(fmt == "Dec Contents"):
|
|
dlthread = download(titleid, ver, True)
|
|
else:
|
|
dlthread = download(titleid, ver)
|
|
dlthread.start()
|
|
while(dlthread.is_alive()):
|
|
wx.Yield()
|
|
|
|
self.progress.SetValue(i * 3 + 2)
|
|
|
|
if(os.path.exists("tmp/tik") == False):
|
|
self.output.AppendText("not found!\n\n")
|
|
continue
|
|
self.output.AppendText("done!\n")
|
|
if(fmt == "WAD"):
|
|
if(os.path.isdir(os.path.dirname(outfile))):
|
|
self.output.AppendText("Packing WAD to %s..." % outfile)
|
|
dlthread = pack("tmp", outfile)
|
|
dlthread.start()
|
|
while(dlthread.is_alive()):
|
|
wx.Yield()
|
|
else:
|
|
if(os.path.isdir("/".join(outfile.split("/")[:-1]))):
|
|
if(not os.path.isdir(outfile)):
|
|
os.mkdir(outfile)
|
|
self.output.AppendText("Copying files to %s..." % outfile)
|
|
for file in os.listdir("tmp"):
|
|
shutil.copy("tmp/" + file, outfile)
|
|
wx.Yield()
|
|
self.output.AppendText("done!\n")
|
|
|
|
self.progress.SetValue(i * 3 + 3)
|
|
|
|
self.output.AppendText("Title Info:\n")
|
|
self.output.AppendText(str(Wii.TMD.loadFile("tmp/tmd")))
|
|
self.output.AppendText(str(Wii.Ticket.loadFile("tmp/tik")))
|
|
|
|
self.output.AppendText("\n")
|
|
|
|
os.chdir(olddir)
|
|
os.chdir(olddir)
|
|
self.output.AppendText("Queue Downloaded!\n\n")
|
|
|
|
class download(threading.Thread):
|
|
def __init__(self, titleid, ver, decrypt = True):
|
|
threading.Thread.__init__(self)
|
|
self.titleid = titleid
|
|
self.ver = ver
|
|
self.decrypt = decrypt
|
|
def run(self):
|
|
try:
|
|
if(self.ver != 0):
|
|
Wii.NUS.download(self.titleid, self.ver).dumpDir("tmp", decrypt = self.decrypt)
|
|
else:
|
|
Wii.NUS.download(self.titleid).dumpDir("tmp", decrypt = self.decrypt)
|
|
except:
|
|
pass
|
|
|
|
class pack(threading.Thread):
|
|
def __init__(self, dir, outfile):
|
|
threading.Thread.__init__(self)
|
|
self.outfile = outfile
|
|
self.dir = dir
|
|
def run(self):
|
|
try:
|
|
Wii.WAD.loadDir(self.dir).dumpFile(self.outfile, fakesign = False)
|
|
except:
|
|
pass
|
|
|
|
tb = ''
|
|
def excepthook(type, value, traceb):
|
|
import traceback
|
|
class dummy:
|
|
def write(self, text):
|
|
global tb
|
|
tb += text
|
|
dummyf = dummy()
|
|
traceback.print_exception(type, value, traceb, file=dummyf)
|
|
wx.MessageBox('NUS WAD Packer has encountered a fatal error. Please inform the author of the following traceback:\n\n%s' % tb, 'Fatal Error', wx.OK | wx.ICON_ERROR)
|
|
clean()
|
|
sys.exit(1)
|
|
|
|
if(__name__ == '__main__'):
|
|
dl = Downloader(redirect = False)
|
|
dl.MainLoop()
|
|
if(os.path.isdir(os.getcwd() + "/tmp")):
|
|
shutil.rmtree(os.getcwd() + "/tmp")
|