import Wii as wii import urllib2, struct, time, shutil, os, sys class NUSID(): def __init__(self, titleid, version, size): self.titleid = titleid self.version = version self.size = size def __str__(self): return "[soap] INFO: %08x-%08x %04x %u\n" % (self.titleid >> 32, self.titleid & 0xFFFFFFFF, self.version, self.size) def rawstr(self): return "%08x%08x %04x %u\n" % (self.titleid >> 32, self.titleid & 0xFFFFFFFF, self.version, self.size) def getSOAP(region): # hardcoded device id for now soaprequest = '1.0131981051232191384362227770' + region + '' + region[:2] + '0000000100000001200000001000000023300000001000000095162' headers = {"Content-type":"text/xml; charset=utf-8", "SOAPAction":'"urn:nus.wsapi.broadon.com/"', "User-agent":"wii libnup/1.0"} request = urllib2.Request("http://nus.shop.wii.com/nus/services/NetUpdateSOAP", soaprequest, headers) f = urllib2.urlopen(request) data = f.read() titles = [] data = data[data.find("") + len(""):data.find("")] for i in range(data.count("")): title = data[data.find(""):data.find("") + len("")] titleid = int(data[data.find("") + len(""):data.find("")], 16) #16 for hex version = int(data[data.find("") + len(""):data.find("")]) size = int(data[data.find("") + len(""):data.find("")]) nus = NUSID(titleid, version, size) titles.append(nus) data = data[data.find("") + len(""):] return titles def readableTitleID(lower): out = struct.unpack("4s", struct.pack(">I", lower)) return out[0] def log(text): sys.stdout.write(text) sys.stdout.flush() 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) def summary(report, item): tmd = wii.TMD.loadFile("%08x%08x/tmd" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) #not tmp/ because we are already in tmp report.write(getName(item.titleid) + "\n") report.write(" Title ID: %08x-%08x\n" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) report.write(" Version: 0x%04x\n Size: %u\n" % (item.version, item.size)) shared = 0 contents = tmd.getContents() for i in range(len(contents)): if(contents[i].type & 0x8000): shared += 1 report.write(" Contents: %u (of which %u are shared)\n\n" % (len(contents), shared)) def detailed(report, item): tmd = wii.TMD.loadFile("tmp/%08x%08x/tmd" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) tik = wii.Ticket.loadFile("tmp/%08x%08x/tik" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) log("Importing %s to fake NAND (decrypted)..." % getName(item.titleid)) wii.NAND("nand").importTitle("tmp/%08x%08x" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF), tmd, tik, is_decrypted = False, result_decrypted = True) log("Done!\n") log("Importing %s to fake NAND (encrypted)..." % getName(item.titleid)) wii.NAND("encnand").importTitle("tmp/%08x%08x" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF), tmd, tik, is_decrypted = False, result_decrypted = False) log("Done!\n") shutil.copy("tmp/%08x%08x/cert" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF), "nand/sys/cert.sys") shutil.copy("tmp/%08x%08x/cert" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF), "encnand/sys/cert.sys") report.write(getName(item.titleid) + "\n") report.write(" Title ID: %08x-%08x\n" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) report.write(" Version: 0x%04x\n Size: %u\n" % (item.version, item.size)) report.write(str(tmd)) #do TMD signature and certs. TODO! report.write(str(tik)) #do Ticket signature and certs. TODO! report.write("\n\n") class nullFile: def _null(self, *args, **kwds): pass def __getattr__(self, name): return self._null def changed(region, added, removed, modified, previous, no_log = False): if(no_log): report = nullFile() else: report = open("reports/%s/%s.log" % (region, time.strftime("%y%m%d-%I%M%S")), "wb") report.write("************************************************************************\n*********************** Wii System Update Report ***********************\n************************************************************************\n\n") report.write("Titles added:\t%u\n" % len(added)) report.write("Titles changed:\t%u\n" % len(modified)) report.write("Titles removed:\t%u\n" % len(removed)) report.write("\n************************** SUMMARY OF CHANGES **************************\n") os.chdir("tmp") if(len(added) > 0): report.write("\n====== Titles Added ======\n\n") for item in added: log("Downloading %s..." % getName(item.titleid)) wii.NUS(item.titleid, item.version).download(useidx = False, decrypt = False) log("Done!\n") summary(report, item) if(len(modified) > 0): report.write("\n====== Titles Changed ======\n\n") for item in modified: log("Downloading %s..." % getName(item.titleid)) wii.NUS(item.titleid, item.version).download(useidx = False, decrypt = False) log("Done!\n") summary(report, item) if(len(removed) > 0): report.write("\n====== Titles Removed (showing info from last available version) ======\n\n") for item in removed: summary(report, item) os.chdir("..") report.write("\n**************************** DETAILED DUMPS ****************************\n") if(len(added) > 0): report.write("\n====== Titles Added ======\n\n") for item in added: detailed(report, item) shutil.rmtree("tmp/%08x%08x" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) if(len(modified) > 0): report.write("\n====== Titles Changed ======\n\n") for item in modified: detailed(report, item) shutil.rmtree("tmp/%08x%08x" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF)) if(len(removed) > 0): report.write("\n====== Titles Removed (showing info from last available version) ======\n\n") for item in removed: detailed(report, item) report.write("\n***************************** MESSAGE LOG *****************************\n") if(no_log != True): report.write(open("runlog.%s.txt" % region).read()) def imposter(regions): for region in regions: log("Wiimpostor invoked for region %s...\n" % region) data = "" data += "[check] INFO: Wiimposter: Check invoked for region %s\n" % region try: nodb = False last = open("lastupdate.%s.txt" % region, "rb").read() except: data += "[titlelist] WARNING: TitleList: DB file lastupdate.%s.txt does not exist. Initializing to blank list.\n" % region nodb = True data += "[soap] INFO: Checking for updates...\n" data += ("[soap] INFO: Title ID Version FsSize\n") log("Getting list of titles from Nintendo's SOAP server...") soap = getSOAP(region) for entry in soap: data += str(entry) log("Done!\n") if(nodb): old = [] else: old = last.split("\n") topop = [] for i in range(len(old)): elem = old[i] if(elem == ""): topop.append(i) # >_> continue elem = elem.split(" ") titleid = int(elem[0], 16) version = int(elem[1], 16) sz = int(elem[2]) tmp = NUSID(titleid, version, sz) #is a tmp var needed? old[i] = tmp popped = 0 #don't ask me why I did this for pop in topop: old.pop(pop - popped) popped += 1 #the code from here on down makes no sesne to me already. Dont ask. added = [] removed = [] modified = [] same = [] previous = [] log("Checking for titles removed, modified, or added...") for elem in old: cookie_jar = len(soap) for title in soap: if(elem.titleid != title.titleid): cookie_jar -= 1 elif(elem.version != title.version): #don't check sizes because nintendo randomly changes them modified.append(title) previous.append(elem) else: same.append(title) if(cookie_jar == 0): removed.append(elem) for title in soap: cookie_jar = len(old) for elem in old: if(elem.titleid != title.titleid): cookie_jar -= 1 if(cookie_jar == 0): added.append(title) log("Done!\n") #end code I don't get anymore if(not os.path.isdir("reports")): os.mkdir("reports") if(not os.path.isdir("reports/USA")): os.mkdir("reports/USA") if(not os.path.isdir("reports/EUR")): os.mkdir("reports/EUR") if(not os.path.isdir("reports/JPN")): os.mkdir("reports/JPN") if(not os.path.isdir("reports/KOR")): os.mkdir("reports/KOR") if(not os.path.isdir("tmp")): os.mkdir("tmp") data += "[soap] INFO: Comparing update with previous...\n" data += "[soap] INFO: %u added, %u removed, %u changed, %u unchanged" % (len(added), len(removed), len(modified), len(same)) open("runlog.%s.txt" % region, "wb").write(data) if(len(added) != 0 or len(modified) != 0 or len(removed) != 0): updatelog = "" for title in soap: updatelog += title.rawstr() open("lastupdate.%s.txt" % region, "wb").write(updatelog) changed(region, added, removed, modified, previous) else: log("No new update for this region!\n") log("Complete for region %s!\n\n" % region) if(__name__ == "__main__"): available = ["USA", "EUR", "JPN", "KOR"] script = 0 if(len(sys.argv) > 1): script = 1 if(os.path.isfile(sys.argv[1])): stuff = open(sys.argv[1]).read().split() else: stuff = sys.argv[1:] for i, arg in enumerate(stuff): if(arg in available): imposter(args) elif(len(arg) == 16 and len(stuff) != i + 1 and stuff[i + 1].isdigit()): tmp = NUSID(int(arg, 16), int(stuff[i + 1]), 0) changed("", [tmp], [], [], [], no_log = True) if(script == 0): imposter(available)