mirror of
https://github.com/Xpl0itU/WiiUDownloader.git
synced 2025-06-02 16:19:52 -04:00
WIP Support for Wii's TMD
This commit is contained in:
parent
164f3fc620
commit
e5c555c2a7
3 changed files with 54 additions and 70 deletions
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
var cetkData []byte
|
var cetkData []byte
|
||||||
|
|
||||||
func getCert(tmdData []byte, id int, numContents uint16) ([]byte, error) {
|
func getCert(tmdData []byte, id int, numContents uint16) ([]byte, error) { // TODO: Add support for Wii's TMD
|
||||||
var certSlice []byte
|
var certSlice []byte
|
||||||
if len(tmdData) == int((0x0B04+0x30*numContents+0xA00)-0x300) {
|
if len(tmdData) == int((0x0B04+0x30*numContents+0xA00)-0x300) {
|
||||||
certSlice = tmdData[0x0B04+0x30*numContents : 0x0B04+0x30*numContents+0xA00-0x300]
|
certSlice = tmdData[0x0B04+0x30*numContents : 0x0B04+0x30*numContents+0xA00-0x300]
|
||||||
|
|
|
@ -412,69 +412,21 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// find title id and content id
|
tmdData, err := os.ReadFile(tmdPath)
|
||||||
var titleID []byte
|
|
||||||
var contentCount uint16
|
|
||||||
tmd, err := os.Open(tmdPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer tmd.Close()
|
tmd, err := parseTMD(tmdData)
|
||||||
|
if err != nil {
|
||||||
tmd.Seek(0x18C, io.SeekStart)
|
|
||||||
titleID = make([]byte, 8)
|
|
||||||
if _, err := io.ReadFull(tmd, titleID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmd.Seek(0x1DE, io.SeekStart)
|
// Check if all contents are present and how they are named
|
||||||
if err := binary.Read(tmd, binary.BigEndian, &contentCount); err != nil {
|
for i := 0; i < len(tmd.Contents); i++ {
|
||||||
return err
|
_, err := os.Stat(filepath.Join(path, tmd.Contents[i].CIDStr+".app"))
|
||||||
}
|
|
||||||
|
|
||||||
tmd.Seek(0x204, io.SeekStart)
|
|
||||||
tmdIndex := make([]byte, 2)
|
|
||||||
if _, err := io.ReadFull(tmd, tmdIndex); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
contents := make([]Content, contentCount)
|
|
||||||
|
|
||||||
for c := uint16(0); c < contentCount; c++ {
|
|
||||||
offset := 2820 + (48 * c)
|
|
||||||
tmd.Seek(int64(offset), io.SeekStart)
|
|
||||||
if err := binary.Read(tmd, binary.BigEndian, &contents[c].ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmd.Seek(0xB08+(0x30*int64(c)), io.SeekStart)
|
|
||||||
contents[c].Index = make([]byte, 2)
|
|
||||||
if _, err := io.ReadFull(tmd, contents[c].Index); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmd.Seek(0xB0A+(0x30*int64(c)), io.SeekStart)
|
|
||||||
if err := binary.Read(tmd, binary.BigEndian, &contents[c].Type); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmd.Seek(0xB0C+(0x30*int64(c)), io.SeekStart)
|
|
||||||
if err := binary.Read(tmd, binary.BigEndian, &contents[c].Size); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmd.Seek(0xB14+(0x30*int64(c)), io.SeekStart)
|
|
||||||
contents[c].Hash = make([]byte, 0x14)
|
|
||||||
if _, err := io.ReadFull(tmd, contents[c].Hash); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
contents[c].CIDStr = fmt.Sprintf("%08X", contents[c].ID)
|
|
||||||
|
|
||||||
_, err := os.Stat(filepath.Join(path, contents[c].CIDStr+".app"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
contents[c].CIDStr = fmt.Sprintf("%08x", contents[c].ID)
|
tmd.Contents[i].CIDStr = fmt.Sprintf("%08x", tmd.Contents[i].ID)
|
||||||
_, err = os.Stat(filepath.Join(path, contents[c].CIDStr+".app"))
|
_, err = os.Stat(filepath.Join(path, tmd.Contents[i].CIDStr+".app"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("content not found")
|
return errors.New("content not found")
|
||||||
}
|
}
|
||||||
|
@ -500,7 +452,9 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cbc := cipher.NewCBCDecrypter(c, append(titleID, make([]byte, 8)...))
|
titleIDBytes := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(titleIDBytes, tmd.TitleID)
|
||||||
|
cbc := cipher.NewCBCDecrypter(c, append(titleIDBytes, make([]byte, 8)...))
|
||||||
|
|
||||||
decryptedTitleKey := make([]byte, len(encryptedTitleKey))
|
decryptedTitleKey := make([]byte, len(encryptedTitleKey))
|
||||||
cbc.CryptBlocks(decryptedTitleKey, encryptedTitleKey)
|
cbc.CryptBlocks(decryptedTitleKey, encryptedTitleKey)
|
||||||
|
@ -510,14 +464,14 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry
|
||||||
return fmt.Errorf("failed to create AES cipher: %w", err)
|
return fmt.Errorf("failed to create AES cipher: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fstEncFile, err := os.Open(filepath.Join(path, contents[0].CIDStr+".app"))
|
fstEncFile, err := os.Open(filepath.Join(path, tmd.Contents[0].CIDStr+".app"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fstEncFile.Close()
|
defer fstEncFile.Close()
|
||||||
|
|
||||||
decryptedBuffer := bytes.Buffer{}
|
decryptedBuffer := bytes.Buffer{}
|
||||||
if err := decryptContentToBuffer(fstEncFile, &decryptedBuffer, cipherHashTree, contents[0]); err != nil {
|
if err := decryptContentToBuffer(fstEncFile, &decryptedBuffer, cipherHashTree, tmd.Contents[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fst := FSTData{FSTReader: bytes.NewReader(bytes.Clone(decryptedBuffer.Bytes())), FSTEntries: make([]FEntry, 0), EntryCount: 0, Entries: 0, NamesOffset: 0}
|
fst := FSTData{FSTReader: bytes.NewReader(bytes.Clone(decryptedBuffer.Bytes())), FSTEntries: make([]FEntry, 0), EntryCount: 0, Entries: 0, NamesOffset: 0}
|
||||||
|
@ -560,7 +514,7 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry
|
||||||
contentOffset <<= 5
|
contentOffset <<= 5
|
||||||
}
|
}
|
||||||
if fst.FSTEntries[i].Type&0x80 == 0 {
|
if fst.FSTEntries[i].Type&0x80 == 0 {
|
||||||
matchingContent := contents[fst.FSTEntries[i].ContentID]
|
matchingContent := tmd.Contents[fst.FSTEntries[i].ContentID]
|
||||||
tmdFlags := matchingContent.Type
|
tmdFlags := matchingContent.Type
|
||||||
srcFile, err := os.Open(filepath.Join(path, matchingContent.CIDStr+".app"))
|
srcFile, err := os.Open(filepath.Join(path, matchingContent.CIDStr+".app"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
48
tmd.go
48
tmd.go
|
@ -13,6 +13,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type TMD struct {
|
type TMD struct {
|
||||||
|
TitleID uint64
|
||||||
Version byte
|
Version byte
|
||||||
TitleVersion uint16
|
TitleVersion uint16
|
||||||
ContentCount uint16
|
ContentCount uint16
|
||||||
|
@ -30,6 +31,11 @@ func parseTMD(data []byte) (*TMD, error) {
|
||||||
|
|
||||||
switch tmd.Version {
|
switch tmd.Version {
|
||||||
case TMD_VERSION_WII:
|
case TMD_VERSION_WII:
|
||||||
|
reader.Seek(0x18C, io.SeekStart)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
reader.Seek(0x1DC, io.SeekStart)
|
reader.Seek(0x1DC, io.SeekStart)
|
||||||
|
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil {
|
||||||
|
@ -50,7 +56,11 @@ func parseTMD(data []byte) (*TMD, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(2, io.SeekCurrent)
|
reader.Seek(0x1E8+(0x24*int64(i)), io.SeekStart)
|
||||||
|
tmd.Contents[i].Index = make([]byte, 2)
|
||||||
|
if _, err := io.ReadFull(reader, tmd.Contents[i].Index); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Type); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Type); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -59,8 +69,17 @@ func parseTMD(data []byte) (*TMD, error) {
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Size); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Size); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmd.Contents[i].Hash = make([]byte, 0x14)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Hash); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case TMD_VERSION_WIIU:
|
case TMD_VERSION_WIIU:
|
||||||
|
reader.Seek(0x18C, io.SeekStart)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
reader.Seek(0x1DC, io.SeekStart)
|
reader.Seek(0x1DC, io.SeekStart)
|
||||||
|
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil {
|
||||||
|
@ -73,21 +92,32 @@ func parseTMD(data []byte) (*TMD, error) {
|
||||||
|
|
||||||
tmd.Contents = make([]Content, tmd.ContentCount)
|
tmd.Contents = make([]Content, tmd.ContentCount)
|
||||||
|
|
||||||
for i := 0; i < int(tmd.ContentCount); i++ {
|
for c := uint16(0); c < tmd.ContentCount; c++ {
|
||||||
offset := 0xB04 + (0x30 * i)
|
offset := 2820 + (48 * c)
|
||||||
|
|
||||||
reader.Seek(int64(offset), io.SeekStart)
|
reader.Seek(int64(offset), io.SeekStart)
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].ID); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].ID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(2, io.SeekCurrent)
|
reader.Seek(0xB08+(0x30*int64(c)), io.SeekStart)
|
||||||
|
tmd.Contents[c].Index = make([]byte, 2)
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Type); err != nil {
|
if _, err := io.ReadFull(reader, tmd.Contents[c].Index); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Size); err != nil {
|
reader.Seek(0xB0A+(0x30*int64(c)), io.SeekStart)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Type); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Seek(0xB0C+(0x30*int64(c)), io.SeekStart)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Size); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Seek(0xB14+(0x30*int64(c)), io.SeekStart)
|
||||||
|
tmd.Contents[c].Hash = make([]byte, 0x14)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Hash); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue