mirror of
https://github.com/grp/Wii.py.git
synced 2025-06-18 23:05:48 -04:00
Reworked all the structure, added decryption (thx bLASty) and apploader parsing, the reading function fail when reading files on more than one block
This commit is contained in:
parent
f6a76782c6
commit
5f839ca19b
196
disc.py
196
disc.py
@ -2,6 +2,7 @@ import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
|
||||
import wx
|
||||
import png
|
||||
|
||||
from title import *
|
||||
from Crypto.Cipher import AES
|
||||
from Struct import Struct
|
||||
|
||||
@ -9,9 +10,6 @@ from common import *
|
||||
|
||||
|
||||
class WOD: #WiiOpticalDisc
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
|
||||
class fsentry:
|
||||
name = ""
|
||||
parent = None
|
||||
@ -31,9 +29,185 @@ class WOD: #WiiOpticalDisc
|
||||
size = 0
|
||||
offset = 0
|
||||
|
||||
class discHeader(Struct):
|
||||
__endian__ = Struct.BE
|
||||
def __format__(self):
|
||||
self.discId = Struct.string(1)
|
||||
self.gameCode = Struct.string(2)
|
||||
self.region = Struct.string(1)
|
||||
self.makerCode = Struct.uint8[2]
|
||||
self.h = Struct.uint8
|
||||
self.version = Struct.uint8
|
||||
self.audioStreaming = Struct.uint8
|
||||
self.streamingBufSize = Struct.uint8
|
||||
self.unused = Struct.uint8[14]
|
||||
self.magic = Struct.uint32
|
||||
self.title = Struct.string(64)
|
||||
self.hashVerify = Struct.uint8
|
||||
self.h3verify = Struct.uint8
|
||||
|
||||
# Many many thanks to Wiipower
|
||||
class Apploader(Struct):
|
||||
__endian__ = Struct.BE
|
||||
def __format__(self):
|
||||
self.buildDate = Struct.string(16)
|
||||
self.entryPoint = Struct.uint32
|
||||
self.size = Struct.uint32
|
||||
self.trailingSize = Struct.uint32
|
||||
self.padding = Struct.uint8[4]
|
||||
|
||||
def __str__(self):
|
||||
ret = ''
|
||||
ret += '%s [%s%s%s]\n' % (self.discHdr.title, self.discHdr.discId, self.discHdr.gameCode, self.discHdr.region)
|
||||
if self.discHdr.region == 'P':
|
||||
ret += 'Region : PAL\n'
|
||||
elif self.discHdr.region == 'E':
|
||||
ret += 'Region : NTSC\n'
|
||||
elif self.discHdr.region == 'J':
|
||||
ret += 'Region : JPN\n'
|
||||
ret += 'Version 0x%x Maker %i%i Audio streaming %x\n' % (self.discHdr.version, self.discHdr.makerCode[0], self.discHdr.makerCode[1], self.discHdr.audioStreaming)
|
||||
ret += 'Hash verify flag 0x%x H3 verify flag : 0x%x\n' % (self.discHdr.hashVerify, self.discHdr.h3verify)
|
||||
ret += 'Found %i partitions (table at 0x%x)\n' % (self.partitionCount, self.partsTableOffset)
|
||||
ret += 'Found %i channels (table at 0x%x)\n' % (self.channelsCount, self.chansTableOffset)
|
||||
ret += 'Partition %i opened (type 0x%x) at 0x%x)\n' % (self.partitionOpen, self.partitionType, self.partitionOffset)
|
||||
ret += 'Partition title : %s\n' % self.partitionHdr.title
|
||||
ret += 'Partition key %s\n' % hexdump(self.partitionKey)
|
||||
ret += 'Tmd at 0x%x\n' % self.tmdOffset
|
||||
ret += 'main.dol at 0x%x fst at 0x%x (%xb)\n' % (self.dolOffset, self.fstSize, self.fstOffset)
|
||||
ret += 'Apploader built on %s (%x bytes)\n' % (self.appLdr.buildDate, self.appLdr.size + self.appLdr.trailingSize)
|
||||
|
||||
return ret
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
self.fp = open(f, 'rb')
|
||||
|
||||
self.discHdr = self.discHeader().unpack(self.fp.read(0x400))
|
||||
if self.discHdr.magic != 0x5D1C9EA3:
|
||||
raise Exception('Wrong disc magic')
|
||||
|
||||
self.fp.seek(0x40000)
|
||||
|
||||
self.partitionCount = 1 + struct.unpack(">I", self.fp.read(4))[0]
|
||||
self.partsTableOffset = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
|
||||
self.channelsCount = struct.unpack(">I", self.fp.read(4))[0]
|
||||
self.chansTableOffset = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
|
||||
self.partitionOpen = -1
|
||||
self.partitionOffset = -1
|
||||
self.partitionType = -1
|
||||
|
||||
def decryptBlock(self, block):
|
||||
if len(block) != 0x8000:
|
||||
raise Exception('Block size too big/small')
|
||||
|
||||
blockIV = block[0x3d0:0x3dF + 1]
|
||||
print 'IV %s (len %i)\n' % (hexdump(blockIV), len(blockIV))
|
||||
blockData = block[0x0400:0x7FFF]
|
||||
|
||||
return Crypto().DecryptData(self.partitionKey, blockIV, blockData, True)
|
||||
|
||||
|
||||
def openPartition(self, index):
|
||||
if index > self.partitionCount:
|
||||
raise ValueError('Partition index too big')
|
||||
|
||||
self.partitionOpen = index
|
||||
|
||||
self.partitionOffset = self.partsTableOffset + (8 * self.partitionOpen)
|
||||
|
||||
self.fp.seek(self.partsTableOffset + (8 * self.partitionOpen))
|
||||
|
||||
self.partitionOffset = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
self.partitionType = struct.unpack(">I", self.fp.read(4))[0]
|
||||
|
||||
self.fp.seek(self.partitionOffset)
|
||||
|
||||
self.tikData = self.fp.read(0x2A4)
|
||||
self.partitionKey = Ticket(self.tikData).getTitleKey()
|
||||
|
||||
self.tmdSize = struct.unpack(">I", self.fp.read(4))[0]
|
||||
self.tmdOffset = struct.unpack(">I", self.fp.read(4))[0] >> 2
|
||||
|
||||
self.certsSize = struct.unpack(">I", self.fp.read(4))[0]
|
||||
self.certsOffset = struct.unpack(">I", self.fp.read(4))[0] >> 2
|
||||
|
||||
self.H3TableOffset = struct.unpack(">I", self.fp.read(4))[0] >> 2
|
||||
|
||||
self.dataOffset = struct.unpack(">I", self.fp.read(4))[0] >> 2
|
||||
self.dataSize = struct.unpack(">I", self.fp.read(4))[0] >> 2
|
||||
|
||||
self.dolOffset = 4 * struct.unpack(">I", self.readPartition (0x420, 4))[0]
|
||||
|
||||
self.fstOffset = 4 * struct.unpack(">I", self.readPartition (0x424, 4))[0]
|
||||
self.fstSize = 4 * struct.unpack(">I", self.readPartition (0x428, 4))[0]
|
||||
|
||||
self.appLdr = self.Apploader().unpack(self.readPartition (0x2440, 32))
|
||||
self.partitionHdr = self.discHeader().unpack(self.readPartition (0x0, 0x400))
|
||||
|
||||
def readPartition(self, offset, size):
|
||||
if size > 0x8000:
|
||||
pass#raise Exception('To be implemented')
|
||||
|
||||
readBlocks = size / 0x8000
|
||||
blockToRead = offset / 0x8000
|
||||
blob = ''
|
||||
|
||||
print 'Read at 0x%x for %i bytes' % (offset, size)
|
||||
print 'Going to read %i blocks' % readBlocks
|
||||
|
||||
self.fp.seek(self.partitionOffset + 0x20000 + (0x8000 * blockToRead))
|
||||
if readBlocks == 0:
|
||||
blob += self.decryptBlock(self.fp.read(0x8000))
|
||||
else:
|
||||
for x in range(readBlocks):
|
||||
blob += self.decryptBlock(self.fp.read(0x8000))
|
||||
|
||||
print 'Read from 0x%x to 0x%x' % (offset, offset + size)
|
||||
return blob[offset:offset + size]
|
||||
|
||||
def getIsoBootmode(self):
|
||||
if self.discHdr.discId == 'R' or self.discHdr.discId == '_':
|
||||
return 2
|
||||
elif self.discHdr.discId == '0':
|
||||
return 1
|
||||
|
||||
def getOpenedPartition(self):
|
||||
return self.partitionOpen
|
||||
|
||||
def getOpenedPartitionOffset(self):
|
||||
return self.partitionOffset
|
||||
|
||||
def getOpenedPartitionType(self):
|
||||
return self.partitionType
|
||||
|
||||
def getPartitionsCount(self):
|
||||
return self.partitionCount
|
||||
|
||||
def getChannelsCount(self):
|
||||
return self.channelsCount
|
||||
|
||||
def getPartitionCerts(self):
|
||||
self.fp.seek(self.partitionOffset + self.certsOffset)
|
||||
return self.fp.read(self.certsSize)
|
||||
|
||||
def getPartitionH3Table(self):
|
||||
self.fp.seek(self.partitionOffset + self.H3TableOffset)
|
||||
return self.fp.read(0x18000)
|
||||
|
||||
def getPartitionTmd(self):
|
||||
self.fp.seek(self.partitionOffset + self.tmdOffset)
|
||||
return self.fp.read(self.tmdSize)
|
||||
|
||||
def getPartitionTik(self):
|
||||
self.fp.seek(self.partitionOffset)
|
||||
return self.fp.read(0x2A4)
|
||||
|
||||
def getPartitionApploader(self):
|
||||
return self.readPartition (0x2460, self.appLdr.size + self.appLdr.trailingSize)
|
||||
|
||||
def extractPartition(self, index, fn = ""):
|
||||
self.fp = open(self.f, "rb")
|
||||
|
||||
if(fn == ""):
|
||||
fn = os.path.dirname(self.f) + "/" + os.path.basename(self.f).replace(".", "_") + "_out"
|
||||
@ -44,25 +218,11 @@ class WOD: #WiiOpticalDisc
|
||||
pass
|
||||
os.chdir(fn)
|
||||
|
||||
self.titleid = self.fp.read(4)
|
||||
self.publisher = self.fp.read(2)
|
||||
|
||||
self.fp.seek(0x18)
|
||||
if(struct.unpack(">I", self.fp.read(4))[0] != 0x5D1C9EA3):
|
||||
self.fp.seek(-4, 1)
|
||||
raise ValueError("Not a valid Wii Disc (GC not supported)! Magic: %08x" % struct.unpack(">I", self.fp.read(4))[0])
|
||||
|
||||
self.fp.seek(0x40000)
|
||||
partitions = struct.unpack(">I", self.fp.read(4))[0]
|
||||
parttableoffs = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
|
||||
channels = struct.unpack(">I", self.fp.read(4))[0]
|
||||
chantableoffs = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
|
||||
self.fp.seek(parttableoffs + (8 * index))
|
||||
partitionoffs = struct.unpack(">I", self.fp.read(4))[0] << 2
|
||||
partitiontype = struct.unpack(">I", self.fp.read(4))[0] #0 is data, 1 is update, 2 is installer
|
||||
|
||||
self.fp.seek(partitionoffs)
|
||||
|
||||
tikdata = self.fp.read(0x2A3)
|
||||
|
Loading…
Reference in New Issue
Block a user