Wii.py/nus wadpacker.py
2009-08-20 13:22:59 -04:00

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")