mirror of
https://gitlab.com/Nanolx/qwad.git
synced 2024-11-21 18:19:18 +01:00
bump version to 0.7
support relative paths --tmdinfo no longer fails when an IOS-tmd is check don't enforce decrypt=false on unpacking CLI
This commit is contained in:
parent
138af060a3
commit
e573cd1f2f
@ -1,3 +1,9 @@
|
|||||||
|
-- 0.7 --
|
||||||
|
* cli-options now can properly handle relative paths
|
||||||
|
* --tmdinfo does now longer fail when checking an IOS
|
||||||
|
* don't enforce decrypt=false on unpacking cli
|
||||||
|
* automatic whitespace fixes
|
||||||
|
|
||||||
-- 0.6 --
|
-- 0.6 --
|
||||||
* fixed a string in german translation
|
* fixed a string in german translation
|
||||||
* fixed selecting file in TMD-Viewer
|
* fixed selecting file in TMD-Viewer
|
||||||
|
@ -10,7 +10,7 @@ from PyQt4.QtCore import pyqtSignature
|
|||||||
from Ui_AboutQwad import Ui_Dialog
|
from Ui_AboutQwad import Ui_Dialog
|
||||||
|
|
||||||
def Version():
|
def Version():
|
||||||
return "0.5"
|
return "0.7"
|
||||||
def Author():
|
def Author():
|
||||||
return "Christopher Roy Bratusek <nano@tuxfamily.org>"
|
return "Christopher Roy Bratusek <nano@tuxfamily.org>"
|
||||||
|
|
||||||
|
@ -342,6 +342,18 @@ class UnpackingCLI(Thread):
|
|||||||
print e
|
print e
|
||||||
print "Done"
|
print "Done"
|
||||||
|
|
||||||
|
class UnpackingCLIX(Thread):
|
||||||
|
def __init__(self, wadpath, dirpath):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.wadpath = wadpath
|
||||||
|
self.dirpath = dirpath
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
WAD.loadFile(self.wadpath).dumpDir(self.dirpath)
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
print "Done"
|
||||||
|
|
||||||
class Packing(Unpacking):
|
class Packing(Unpacking):
|
||||||
def __init__(self, dirpath, wadpath, QMW, deletedir = False):
|
def __init__(self, dirpath, wadpath, QMW, deletedir = False):
|
||||||
Unpacking.__init__(self, wadpath, dirpath, QMW)
|
Unpacking.__init__(self, wadpath, dirpath, QMW)
|
||||||
@ -349,9 +361,9 @@ class Packing(Unpacking):
|
|||||||
def run(self):
|
def run(self):
|
||||||
self.qobject.emit(SIGNAL("working"),PACKING)
|
self.qobject.emit(SIGNAL("working"),PACKING)
|
||||||
try:
|
try:
|
||||||
print self.dirpath
|
|
||||||
print self.wadpath
|
print self.wadpath
|
||||||
WAD.loadDir(self.dirpath).dumpFile(self.wadpath)
|
print self.dirpath
|
||||||
|
WAD.loadDir(self.dirpath).dumpFile(self.wadpath)
|
||||||
if self.deletedir:
|
if self.deletedir:
|
||||||
print "Cleaning temporary files"
|
print "Cleaning temporary files"
|
||||||
self.qobject.emit(SIGNAL("working"),CLEANING)
|
self.qobject.emit(SIGNAL("working"),CLEANING)
|
||||||
@ -393,6 +405,8 @@ class nusDownloading(Unpacking):
|
|||||||
self.version = None
|
self.version = None
|
||||||
self.decrypt = decrypt
|
self.decrypt = decrypt
|
||||||
self.titleid = titleid
|
self.titleid = titleid
|
||||||
|
print self.titleid
|
||||||
|
print self.version
|
||||||
def run(self):
|
def run(self):
|
||||||
self.qobject.emit(SIGNAL("working"),DOWNLOADING)
|
self.qobject.emit(SIGNAL("working"),DOWNLOADING)
|
||||||
try:
|
try:
|
||||||
@ -425,7 +439,7 @@ class nusDownloadingCLI(UnpackingCLI):
|
|||||||
try:
|
try:
|
||||||
if self.pack:
|
if self.pack:
|
||||||
self.dirpath = tempfile.gettempdir() + "/NUS_"+ str(time.time()).replace(".","")
|
self.dirpath = tempfile.gettempdir() + "/NUS_"+ str(time.time()).replace(".","")
|
||||||
NUS(self.titleid, self.version).download(self.dirpath, decrypt = False)
|
NUS(self.titleid, self.version).download(self.dirpath, decrypt = self.decrypt)
|
||||||
self.packing = PackingCLI(self.dirpath, str(self.outputdir), True)
|
self.packing = PackingCLI(self.dirpath, str(self.outputdir), True)
|
||||||
self.packing.start()
|
self.packing.start()
|
||||||
else:
|
else:
|
||||||
@ -446,7 +460,10 @@ def ShowTMD(tmdpath):
|
|||||||
print "Title Version : %s" % tmd.tmd.title_version
|
print "Title Version : %s" % tmd.tmd.title_version
|
||||||
print "Title Boot Index : %s" % tmd.tmd.boot_index
|
print "Title Boot Index : %s" % tmd.tmd.boot_index
|
||||||
print "Title Contents : %s" % tmd.tmd.numcontents
|
print "Title Contents : %s" % tmd.tmd.numcontents
|
||||||
print "Title IOS : %s" % TitleIDs.TitleSwapDict["%s" % ("%016x" % tmd.tmd.iosversion)]
|
if ("%016x" % tmd.tmd.iosversion) == "0000000000000000":
|
||||||
|
print "Title IOS : --"
|
||||||
|
else:
|
||||||
|
print "Title IOS : %s" % TitleIDs.TitleSwapDict["%s" % ("%016x" % tmd.tmd.iosversion)]
|
||||||
print "Title Access Rights: %s" % tmd.tmd.access_rights
|
print "Title Access Rights: %s" % tmd.tmd.access_rights
|
||||||
print "Title Type : %s" % tmd.tmd.title_type
|
print "Title Type : %s" % tmd.tmd.title_type
|
||||||
print "Title Group ID : %s" % tmd.tmd.group_id
|
print "Title Group ID : %s" % tmd.tmd.group_id
|
||||||
|
12
Qwad.pyw
12
Qwad.pyw
@ -8,6 +8,11 @@ from PyQt4.QtCore import QTranslator, QString, QLocale
|
|||||||
from GUI.VenPri import MWQwad, nusDownloadingCLI, PackingCLI, UnpackingCLI, ShowTMD
|
from GUI.VenPri import MWQwad, nusDownloadingCLI, PackingCLI, UnpackingCLI, ShowTMD
|
||||||
from TitleIDs import TitleDict, IOSdict, swap_dic, ChannelCLIDict, ChannelJAPDict, ChannelPALDict, ChannelUSADict, ChannelJAPVerDict, ChannelPALVerDict, ChannelUSAVerDict
|
from TitleIDs import TitleDict, IOSdict, swap_dic, ChannelCLIDict, ChannelJAPDict, ChannelPALDict, ChannelUSADict, ChannelJAPVerDict, ChannelPALVerDict, ChannelUSAVerDict
|
||||||
|
|
||||||
|
if os.getenv("QWAD_X_DIR"):
|
||||||
|
os.chdir(os.getenv("QWAD_X_DIR"))
|
||||||
|
else:
|
||||||
|
os.chdir(os.getenv("HOME"))
|
||||||
|
|
||||||
class MultipleOption(Option):
|
class MultipleOption(Option):
|
||||||
ACTIONS = Option.ACTIONS + ("extend",)
|
ACTIONS = Option.ACTIONS + ("extend",)
|
||||||
STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
|
STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
|
||||||
@ -20,10 +25,9 @@ class MultipleOption(Option):
|
|||||||
else:
|
else:
|
||||||
Option.take_action(self, action, dest, opt, value, values, parser)
|
Option.take_action(self, action, dest, opt, value, values, parser)
|
||||||
|
|
||||||
VERSION = '0.6'
|
VERSION = '0.7'
|
||||||
|
|
||||||
def opts():
|
def opts():
|
||||||
os.chdir(sys.path[0])
|
|
||||||
description = """NUS-Downloader, WadManager and TMD-Viewer for Linux"""
|
description = """NUS-Downloader, WadManager and TMD-Viewer for Linux"""
|
||||||
parser = OptionParser(option_class=MultipleOption,
|
parser = OptionParser(option_class=MultipleOption,
|
||||||
usage='usage: qwad [OPTIONS] ARGUMENT',
|
usage='usage: qwad [OPTIONS] ARGUMENT',
|
||||||
@ -72,7 +76,7 @@ def opts():
|
|||||||
xarg = TitleDict[str(options.download[0])]
|
xarg = TitleDict[str(options.download[0])]
|
||||||
nusdow = nusDownloadingCLI(int(str(xarg).lower(),16), args[0], args[1], args[2], args[3])
|
nusdow = nusDownloadingCLI(int(str(xarg).lower(),16), args[0], args[1], args[2], args[3])
|
||||||
else:
|
else:
|
||||||
nusdow = nusDownloadingCLI(int(str(options.download[0]).lower(),16), args[0], args[1], args[2], args[3])
|
nusdow = nusDownloadingCLI(int(str(options.download[0]).lower(),16), args[0], args[1], args[2], args[3])
|
||||||
nusdow.start()
|
nusdow.start()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
@ -165,8 +169,6 @@ def main():
|
|||||||
# load Qt translations
|
# load Qt translations
|
||||||
qttranslator = QTranslator()
|
qttranslator = QTranslator()
|
||||||
qttranslator.load(QString("qt_%1").arg(QLocale.system().name()))
|
qttranslator.load(QString("qt_%1").arg(QLocale.system().name()))
|
||||||
# change directory in $HOME, so that file-seletors don't start in Qwads source/data directory
|
|
||||||
os.chdir(os.getenv("HOME"))
|
|
||||||
# misc stuff
|
# misc stuff
|
||||||
Vapp = QApplication(sys.argv)
|
Vapp = QApplication(sys.argv)
|
||||||
Vapp.setOrganizationName("Nanolx")
|
Vapp.setOrganizationName("Nanolx")
|
||||||
|
134
WiiPy/archive.py
134
WiiPy/archive.py
@ -4,7 +4,7 @@ import zlib
|
|||||||
|
|
||||||
class U8(WiiArchive):
|
class U8(WiiArchive):
|
||||||
"""This class can unpack and pack U8 archives, which are used all over the Wii. They are often used in Banners and contents in Downloadable Titles. Please remove all headers and compression first, kthx.
|
"""This class can unpack and pack U8 archives, which are used all over the Wii. They are often used in Banners and contents in Downloadable Titles. Please remove all headers and compression first, kthx.
|
||||||
|
|
||||||
The f parameter is either the source folder to pack, or the source file to unpack."""
|
The f parameter is either the source folder to pack, or the source file to unpack."""
|
||||||
class U8Header(Struct):
|
class U8Header(Struct):
|
||||||
__endian__ = Struct.BE
|
__endian__ = Struct.BE
|
||||||
@ -26,32 +26,32 @@ class U8(WiiArchive):
|
|||||||
def _dump(self):
|
def _dump(self):
|
||||||
header = self.U8Header()
|
header = self.U8Header()
|
||||||
rootnode = self.U8Node()
|
rootnode = self.U8Node()
|
||||||
|
|
||||||
# constants
|
# constants
|
||||||
header.tag = "U\xAA8-"
|
header.tag = "U\xAA8-"
|
||||||
header.rootnode_offset = 0x20
|
header.rootnode_offset = 0x20
|
||||||
header.zeroes = "\x00" * 16
|
header.zeroes = "\x00" * 16
|
||||||
rootnode.type = 0x0100
|
rootnode.type = 0x0100
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
strings = "\x00"
|
strings = "\x00"
|
||||||
data = ''
|
data = ''
|
||||||
|
|
||||||
for item, value in self.files:
|
for item, value in self.files:
|
||||||
node = self.U8Node()
|
node = self.U8Node()
|
||||||
|
|
||||||
recursion = item.count('/')
|
recursion = item.count('/')
|
||||||
if(recursion < 0):
|
if(recursion < 0):
|
||||||
recursion = 0
|
recursion = 0
|
||||||
name = item[item.rfind('/') + 1:]
|
name = item[item.rfind('/') + 1:]
|
||||||
|
|
||||||
node.name_offset = len(strings)
|
node.name_offset = len(strings)
|
||||||
strings += name + '\x00'
|
strings += name + '\x00'
|
||||||
|
|
||||||
if(value == None):
|
if(value == None):
|
||||||
node.type = 0x0100
|
node.type = 0x0100
|
||||||
node.data_offset = recursion
|
node.data_offset = recursion
|
||||||
|
|
||||||
node.size = len(nodes)
|
node.size = len(nodes)
|
||||||
for one, two in self.files:
|
for one, two in self.files:
|
||||||
if(one[:len(item)] == item): # find nodes in the folder
|
if(one[:len(item)] == item): # find nodes in the folder
|
||||||
@ -64,15 +64,15 @@ class U8(WiiArchive):
|
|||||||
node.size = sz
|
node.size = sz
|
||||||
node.type = 0x0000
|
node.type = 0x0000
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
header.header_size = ((len(nodes) + 1) * len(rootnode)) + len(strings)
|
header.header_size = ((len(nodes) + 1) * len(rootnode)) + len(strings)
|
||||||
header.data_offset = align(header.header_size + header.rootnode_offset, 64)
|
header.data_offset = align(header.header_size + header.rootnode_offset, 64)
|
||||||
rootnode.size = len(nodes) + 1
|
rootnode.size = len(nodes) + 1
|
||||||
|
|
||||||
for i in range(len(nodes)):
|
for i in range(len(nodes)):
|
||||||
if(nodes[i].type == 0x0000):
|
if(nodes[i].type == 0x0000):
|
||||||
nodes[i].data_offset += header.data_offset
|
nodes[i].data_offset += header.data_offset
|
||||||
|
|
||||||
fd = ''
|
fd = ''
|
||||||
fd += header.pack()
|
fd += header.pack()
|
||||||
fd += rootnode.pack()
|
fd += rootnode.pack()
|
||||||
@ -81,7 +81,7 @@ class U8(WiiArchive):
|
|||||||
fd += strings
|
fd += strings
|
||||||
fd += "\x00" * (header.data_offset - header.rootnode_offset - header.header_size)
|
fd += "\x00" * (header.data_offset - header.rootnode_offset - header.header_size)
|
||||||
fd += data
|
fd += data
|
||||||
|
|
||||||
return fd
|
return fd
|
||||||
def _dumpDir(self, dir):
|
def _dumpDir(self, dir):
|
||||||
if(not os.path.isdir(dir)):
|
if(not os.path.isdir(dir)):
|
||||||
@ -115,35 +115,35 @@ class U8(WiiArchive):
|
|||||||
self._tmpPath = self._tmpPath[:self._tmpPath.find('/') + 1]
|
self._tmpPath = self._tmpPath[:self._tmpPath.find('/') + 1]
|
||||||
def _load(self, data):
|
def _load(self, data):
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
header = self.U8Header()
|
header = self.U8Header()
|
||||||
header.unpack(data[offset:offset + len(header)])
|
header.unpack(data[offset:offset + len(header)])
|
||||||
offset += len(header)
|
offset += len(header)
|
||||||
|
|
||||||
assert header.tag == "U\xAA8-"
|
assert header.tag == "U\xAA8-"
|
||||||
offset = header.rootnode_offset
|
offset = header.rootnode_offset
|
||||||
|
|
||||||
rootnode = self.U8Node()
|
rootnode = self.U8Node()
|
||||||
rootnode.unpack(data[offset:offset + len(rootnode)])
|
rootnode.unpack(data[offset:offset + len(rootnode)])
|
||||||
offset += len(rootnode)
|
offset += len(rootnode)
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for i in range(rootnode.size - 1):
|
for i in range(rootnode.size - 1):
|
||||||
node = self.U8Node()
|
node = self.U8Node()
|
||||||
node.unpack(data[offset:offset + len(node)])
|
node.unpack(data[offset:offset + len(node)])
|
||||||
offset += len(node)
|
offset += len(node)
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
strings = data[offset:offset + header.data_offset - len(header) - (len(rootnode) * rootnode.size)]
|
strings = data[offset:offset + header.data_offset - len(header) - (len(rootnode) * rootnode.size)]
|
||||||
offset += len(strings)
|
offset += len(strings)
|
||||||
|
|
||||||
recursion = [rootnode.size]
|
recursion = [rootnode.size]
|
||||||
recursiondir = []
|
recursiondir = []
|
||||||
counter = 0
|
counter = 0
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
counter += 1
|
counter += 1
|
||||||
name = strings[node.name_offset:].split('\0', 1)[0]
|
name = strings[node.name_offset:].split('\0', 1)[0]
|
||||||
|
|
||||||
if(node.type == 0x0100): # folder
|
if(node.type == 0x0100): # folder
|
||||||
recursion.append(node.size)
|
recursion.append(node.size)
|
||||||
recursiondir.append(name)
|
recursiondir.append(name)
|
||||||
@ -154,7 +154,7 @@ class U8(WiiArchive):
|
|||||||
offset += node.size
|
offset += node.size
|
||||||
else: # unknown type -- wtf?
|
else: # unknown type -- wtf?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
sz = recursion.pop()
|
sz = recursion.pop()
|
||||||
if(sz != counter + 1):
|
if(sz != counter + 1):
|
||||||
recursion.append(sz)
|
recursion.append(sz)
|
||||||
@ -190,7 +190,7 @@ class U8(WiiArchive):
|
|||||||
self.files[i] = (self.files[i][0], val)
|
self.files[i] = (self.files[i][0], val)
|
||||||
return
|
return
|
||||||
self.files.append((key, val))
|
self.files.append((key, val))
|
||||||
|
|
||||||
|
|
||||||
class WAD(WiiArchive):
|
class WAD(WiiArchive):
|
||||||
def __init__(self, boot2 = False):
|
def __init__(self, boot2 = False):
|
||||||
@ -206,7 +206,7 @@ class WAD(WiiArchive):
|
|||||||
else:
|
else:
|
||||||
headersize, data_offset, certsize, tiksize, tmdsize, padding = struct.unpack('>IIIII12s', data[:32])
|
headersize, data_offset, certsize, tiksize, tmdsize, padding = struct.unpack('>IIIII12s', data[:32])
|
||||||
pos = 32
|
pos = 32
|
||||||
|
|
||||||
rawcert = data[pos:pos + certsize]
|
rawcert = data[pos:pos + certsize]
|
||||||
pos += certsize
|
pos += certsize
|
||||||
if(self.boot2 != True):
|
if(self.boot2 != True):
|
||||||
@ -220,7 +220,7 @@ class WAD(WiiArchive):
|
|||||||
if(tiksize % 64 != 0):
|
if(tiksize % 64 != 0):
|
||||||
pos += 64 - (tiksize % 64)
|
pos += 64 - (tiksize % 64)
|
||||||
self.tik = Ticket.load(rawtik)
|
self.tik = Ticket.load(rawtik)
|
||||||
|
|
||||||
rawtmd = data[pos:pos + tmdsize]
|
rawtmd = data[pos:pos + tmdsize]
|
||||||
pos += tmdsize
|
pos += tmdsize
|
||||||
if(self.boot2 == True):
|
if(self.boot2 == True):
|
||||||
@ -228,7 +228,7 @@ class WAD(WiiArchive):
|
|||||||
else:
|
else:
|
||||||
pos += 64 - (tmdsize % 64)
|
pos += 64 - (tmdsize % 64)
|
||||||
self.tmd = TMD.load(rawtmd)
|
self.tmd = TMD.load(rawtmd)
|
||||||
|
|
||||||
titlekey = self.tik.getTitleKey()
|
titlekey = self.tik.getTitleKey()
|
||||||
contents = self.tmd.getContents()
|
contents = self.tmd.getContents()
|
||||||
for i in range(0, len(contents)):
|
for i in range(0, len(contents)):
|
||||||
@ -244,11 +244,11 @@ class WAD(WiiArchive):
|
|||||||
def _loadDir(self, dir):
|
def _loadDir(self, dir):
|
||||||
origdir = os.getcwd()
|
origdir = os.getcwd()
|
||||||
os.chdir(dir)
|
os.chdir(dir)
|
||||||
|
|
||||||
self.tmd = TMD.loadFile("tmd")
|
self.tmd = TMD.loadFile("tmd")
|
||||||
self.tik = Ticket.loadFile("tik")
|
self.tik = Ticket.loadFile("tik")
|
||||||
self.cert = open("cert", "rb").read()
|
self.cert = open("cert", "rb").read()
|
||||||
|
|
||||||
contents = self.tmd.getContents()
|
contents = self.tmd.getContents()
|
||||||
for i in range(len(contents)):
|
for i in range(len(contents)):
|
||||||
self.contents.append(open("%08x.app" % i, "rb").read())
|
self.contents.append(open("%08x.app" % i, "rb").read())
|
||||||
@ -256,52 +256,52 @@ class WAD(WiiArchive):
|
|||||||
def _dumpDir(self, dir):
|
def _dumpDir(self, dir):
|
||||||
origdir = os.getcwd()
|
origdir = os.getcwd()
|
||||||
os.chdir(dir)
|
os.chdir(dir)
|
||||||
|
|
||||||
contents = self.tmd.getContents()
|
contents = self.tmd.getContents()
|
||||||
for i in range(len(contents)):
|
for i in range(len(contents)):
|
||||||
open("%08x.app" % i, "wb").write(self.contents[i])
|
open("%08x.app" % i, "wb").write(self.contents[i])
|
||||||
self.tmd.dumpFile("tmd")
|
self.tmd.dumpFile("tmd")
|
||||||
self.tik.dumpFile("tik")
|
self.tik.dumpFile("tik")
|
||||||
open("cert", "wb").write(self.cert)
|
open("cert", "wb").write(self.cert)
|
||||||
|
|
||||||
os.chdir(origdir)
|
os.chdir(origdir)
|
||||||
def _dump(self, fakesign = True):
|
def _dump(self, fakesign = True):
|
||||||
titlekey = self.tik.getTitleKey()
|
titlekey = self.tik.getTitleKey()
|
||||||
contents = self.tmd.getContents()
|
contents = self.tmd.getContents()
|
||||||
|
|
||||||
apppack = ""
|
apppack = ""
|
||||||
for i, content in enumerate(contents):
|
for i, content in enumerate(contents):
|
||||||
if(fakesign):
|
if(fakesign):
|
||||||
content.hash = str(Crypto().createSHAHash(self.contents[content.index]))
|
content.hash = str(Crypto().createSHAHash(self.contents[content.index]))
|
||||||
content.size = len(self.contents[content.index])
|
content.size = len(self.contents[content.index])
|
||||||
|
|
||||||
encdata = Crypto().encryptContent(titlekey, content.index, self.contents[content.index])
|
encdata = Crypto().encryptContent(titlekey, content.index, self.contents[content.index])
|
||||||
|
|
||||||
apppack += encdata
|
apppack += encdata
|
||||||
if(len(encdata) % 64 != 0):
|
if(len(encdata) % 64 != 0):
|
||||||
apppack += "\x00" * (64 - (len(encdata) % 64))
|
apppack += "\x00" * (64 - (len(encdata) % 64))
|
||||||
|
|
||||||
if(fakesign):
|
if(fakesign):
|
||||||
self.tmd.setContents(contents)
|
self.tmd.setContents(contents)
|
||||||
self.tmd.fakesign()
|
self.tmd.fakesign()
|
||||||
self.tik.fakesign()
|
self.tik.fakesign()
|
||||||
|
|
||||||
rawtmd = self.tmd.dump()
|
rawtmd = self.tmd.dump()
|
||||||
rawcert = self.cert
|
rawcert = self.cert
|
||||||
rawtik = self.tik.dump()
|
rawtik = self.tik.dump()
|
||||||
|
|
||||||
sz = 0
|
sz = 0
|
||||||
for i in range(len(contents)):
|
for i in range(len(contents)):
|
||||||
sz += contents[i].size
|
sz += contents[i].size
|
||||||
if(sz % 64 != 0):
|
if(sz % 64 != 0):
|
||||||
sz += 64 - (contents[i].size % 64)
|
sz += 64 - (contents[i].size % 64)
|
||||||
|
|
||||||
if(self.boot2 != True):
|
if(self.boot2 != True):
|
||||||
pack = struct.pack('>I4s6I', 32, "Is\x00\x00", len(rawcert), 0, len(rawtik), len(rawtmd), sz, 0)
|
pack = struct.pack('>I4s6I', 32, "Is\x00\x00", len(rawcert), 0, len(rawtik), len(rawtmd), sz, 0)
|
||||||
pack += "\x00" * 32
|
pack += "\x00" * 32
|
||||||
else:
|
else:
|
||||||
pack = struct.pack('>IIIII12s', 32, align(len(rawcert) + len(rawtik) + len(rawtmd), 0x40), len(rawcert), len(rawtik), len(rawtmd), "\x00" * 12)
|
pack = struct.pack('>IIIII12s', 32, align(len(rawcert) + len(rawtik) + len(rawtmd), 0x40), len(rawcert), len(rawtik), len(rawtmd), "\x00" * 12)
|
||||||
|
|
||||||
pack += rawcert
|
pack += rawcert
|
||||||
if(len(rawcert) % 64 != 0 and self.boot2 != True):
|
if(len(rawcert) % 64 != 0 and self.boot2 != True):
|
||||||
pack += "\x00" * (64 - (len(rawcert) % 64))
|
pack += "\x00" * (64 - (len(rawcert) % 64))
|
||||||
@ -311,10 +311,10 @@ class WAD(WiiArchive):
|
|||||||
pack += rawtmd
|
pack += rawtmd
|
||||||
if(len(rawtmd) % 64 != 0 and self.boot2 != True):
|
if(len(rawtmd) % 64 != 0 and self.boot2 != True):
|
||||||
pack += "\x00" * (64 - (len(rawtmd) % 64))
|
pack += "\x00" * (64 - (len(rawtmd) % 64))
|
||||||
|
|
||||||
if(self.boot2 == True):
|
if(self.boot2 == True):
|
||||||
pack += "\x00" * (align(len(rawcert) + len(rawtik) + len(rawtmd), 0x40) - (len(rawcert) + len(rawtik) + len(rawtmd)))
|
pack += "\x00" * (align(len(rawcert) + len(rawtik) + len(rawtmd), 0x40) - (len(rawcert) + len(rawtik) + len(rawtmd)))
|
||||||
|
|
||||||
pack += apppack
|
pack += apppack
|
||||||
return pack
|
return pack
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
@ -325,7 +325,7 @@ class WAD(WiiArchive):
|
|||||||
out = ""
|
out = ""
|
||||||
out += "Wii WAD:\n"
|
out += "Wii WAD:\n"
|
||||||
out += str(self.tmd)
|
out += str(self.tmd)
|
||||||
out += str(self.tik)
|
out += str(self.tik)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ class CCF():
|
|||||||
self.rootOffset = Struct.uint32
|
self.rootOffset = Struct.uint32
|
||||||
self.filesCount = Struct.uint32
|
self.filesCount = Struct.uint32
|
||||||
self.zeroes8 = Struct.string(8)
|
self.zeroes8 = Struct.string(8)
|
||||||
|
|
||||||
class CCFFile(Struct):
|
class CCFFile(Struct):
|
||||||
__endian__ = Struct.LE
|
__endian__ = Struct.LE
|
||||||
def __format__(self):
|
def __format__(self):
|
||||||
@ -350,86 +350,86 @@ class CCF():
|
|||||||
def __init__(self, fileName):
|
def __init__(self, fileName):
|
||||||
self.fileName = fileName
|
self.fileName = fileName
|
||||||
self.fd = open(fileName, 'r+b')
|
self.fd = open(fileName, 'r+b')
|
||||||
|
|
||||||
def compress(self, folder):
|
def compress(self, folder):
|
||||||
fileList = []
|
fileList = []
|
||||||
|
|
||||||
fileHdr = self.CCFHeader()
|
fileHdr = self.CCFHeader()
|
||||||
|
|
||||||
files = os.listdir(folder)
|
files = os.listdir(folder)
|
||||||
|
|
||||||
fileHdr.magic = "\x43\x43\x46\x00"
|
fileHdr.magic = "\x43\x43\x46\x00"
|
||||||
fileHdr.zeroes12 = '\x00' * 12
|
fileHdr.zeroes12 = '\x00' * 12
|
||||||
fileHdr.rootOffset = 0x20
|
fileHdr.rootOffset = 0x20
|
||||||
fileHdr.zeroes8 = '\x00' * 8
|
fileHdr.zeroes8 = '\x00' * 8
|
||||||
|
|
||||||
currentOffset = len(fileHdr)
|
currentOffset = len(fileHdr)
|
||||||
packedFiles = 0
|
packedFiles = 0
|
||||||
previousFileEndOffset = 0
|
previousFileEndOffset = 0
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
if os.path.isdir(folder + '/' + file) or file == '.DS_Store':
|
if os.path.isdir(folder + '/' + file) or file == '.DS_Store':
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
fileList.append(file)
|
fileList.append(file)
|
||||||
|
|
||||||
fileHdr.filesCount = len(fileList)
|
fileHdr.filesCount = len(fileList)
|
||||||
self.fd.write(fileHdr.pack())
|
self.fd.write(fileHdr.pack())
|
||||||
self.fd.write('\x00' * (fileHdr.filesCount * len(self.CCFFile())))
|
self.fd.write('\x00' * (fileHdr.filesCount * len(self.CCFFile())))
|
||||||
|
|
||||||
for fileNumber in range(len(fileList)):
|
for fileNumber in range(len(fileList)):
|
||||||
|
|
||||||
fileEntry = self.CCFFile()
|
fileEntry = self.CCFFile()
|
||||||
|
|
||||||
compressedBuffer = zlib.compress(open(folder + '/' + fileList[fileNumber]).read())
|
compressedBuffer = zlib.compress(open(folder + '/' + fileList[fileNumber]).read())
|
||||||
|
|
||||||
fileEntry.fileName = fileList[fileNumber]
|
fileEntry.fileName = fileList[fileNumber]
|
||||||
fileEntry.fileSize = len(compressedBuffer)
|
fileEntry.fileSize = len(compressedBuffer)
|
||||||
fileEntry.fileSizeDecompressed = os.stat(folder + '/' + fileList[fileNumber]).st_size
|
fileEntry.fileSizeDecompressed = os.stat(folder + '/' + fileList[fileNumber]).st_size
|
||||||
fileEntry.fileOffset = align(self.fd.tell(), 32) / 32
|
fileEntry.fileOffset = align(self.fd.tell(), 32) / 32
|
||||||
|
|
||||||
print 'File {0} ({1}Kb) placed at offset 0x{2:X}'.format(fileEntry.fileName, fileEntry.fileSize / 1024, fileEntry.fileOffset * 32)
|
print 'File {0} ({1}Kb) placed at offset 0x{2:X}'.format(fileEntry.fileName, fileEntry.fileSize / 1024, fileEntry.fileOffset * 32)
|
||||||
|
|
||||||
self.fd.seek(len(fileHdr) + fileNumber * len(self.CCFFile()))
|
self.fd.seek(len(fileHdr) + fileNumber * len(self.CCFFile()))
|
||||||
self.fd.write(fileEntry.pack())
|
self.fd.write(fileEntry.pack())
|
||||||
self.fd.seek(fileEntry.fileOffset * 32)
|
self.fd.seek(fileEntry.fileOffset * 32)
|
||||||
self.fd.write(compressedBuffer)
|
self.fd.write(compressedBuffer)
|
||||||
|
|
||||||
self.fd.close()
|
self.fd.close()
|
||||||
|
|
||||||
def decompress(self):
|
def decompress(self):
|
||||||
fileHdr = self.CCFHeader()
|
fileHdr = self.CCFHeader()
|
||||||
hdrData = self.fd.read(len(fileHdr))
|
hdrData = self.fd.read(len(fileHdr))
|
||||||
fileHdr.unpack(hdrData)
|
fileHdr.unpack(hdrData)
|
||||||
|
|
||||||
print 'Found {0} file/s and root node at 0x{1:X}'.format(fileHdr.filesCount, fileHdr.rootOffset)
|
print 'Found {0} file/s and root node at 0x{1:X}'.format(fileHdr.filesCount, fileHdr.rootOffset)
|
||||||
|
|
||||||
if fileHdr.magic != "\x43\x43\x46\x00":
|
if fileHdr.magic != "\x43\x43\x46\x00":
|
||||||
raise ValueError("Wrong magic, 0x{0}".format(fileHdr.magic))
|
raise ValueError("Wrong magic, 0x{0}".format(fileHdr.magic))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.mkdir(os.path.dirname(self.fileName) + '/' + self.fd.name.replace(".", "_") + "_out")
|
os.mkdir(os.path.dirname(self.fileName) + '/' + self.fd.name.replace(".", "_") + "_out")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
os.chdir(os.path.dirname(self.fileName) + '/' + self.fd.name.replace(".", "_") + "_out")
|
os.chdir(os.path.dirname(self.fileName) + '/' + self.fd.name.replace(".", "_") + "_out")
|
||||||
|
|
||||||
currentOffset = len(fileHdr)
|
currentOffset = len(fileHdr)
|
||||||
|
|
||||||
for x in range(fileHdr.filesCount):
|
for x in range(fileHdr.filesCount):
|
||||||
self.fd.seek(currentOffset)
|
self.fd.seek(currentOffset)
|
||||||
|
|
||||||
fileEntry = self.CCFFile()
|
fileEntry = self.CCFFile()
|
||||||
fileData = self.fd.read(len(fileEntry))
|
fileData = self.fd.read(len(fileEntry))
|
||||||
fileEntry.unpack(fileData)
|
fileEntry.unpack(fileData)
|
||||||
|
|
||||||
fileEntry.fileOffset = fileEntry.fileOffset * 32
|
fileEntry.fileOffset = fileEntry.fileOffset * 32
|
||||||
|
|
||||||
print 'File {0} at offset 0x{1:X}'.format(fileEntry.fileName, fileEntry.fileOffset)
|
print 'File {0} at offset 0x{1:X}'.format(fileEntry.fileName, fileEntry.fileOffset)
|
||||||
print 'File size {0}Kb ({1}Kb decompressed)'.format(fileEntry.fileSize / 1024, fileEntry.fileSizeDecompressed / 1024)
|
print 'File size {0}Kb ({1}Kb decompressed)'.format(fileEntry.fileSize / 1024, fileEntry.fileSizeDecompressed / 1024)
|
||||||
|
|
||||||
output = open(fileEntry.fileName.rstrip('\0'), 'w+b')
|
output = open(fileEntry.fileName.rstrip('\0'), 'w+b')
|
||||||
|
|
||||||
self.fd.seek(fileEntry.fileOffset)
|
self.fd.seek(fileEntry.fileOffset)
|
||||||
if fileEntry.fileSize == fileEntry.fileSizeDecompressed:
|
if fileEntry.fileSize == fileEntry.fileSizeDecompressed:
|
||||||
print 'The file is stored uncompressed'
|
print 'The file is stored uncompressed'
|
||||||
@ -439,7 +439,7 @@ class CCF():
|
|||||||
decompressedBuffer = zlib.decompress(self.fd.read(fileEntry.fileSize))
|
decompressedBuffer = zlib.decompress(self.fd.read(fileEntry.fileSize))
|
||||||
output.write(decompressedBuffer)
|
output.write(decompressedBuffer)
|
||||||
output.close()
|
output.close()
|
||||||
|
|
||||||
currentOffset += len(fileEntry)
|
currentOffset += len(fileEntry)
|
||||||
|
|
||||||
if(__name__ == '__main__'):
|
if(__name__ == '__main__'):
|
||||||
|
Loading…
Reference in New Issue
Block a user