-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmdh.go
119 lines (105 loc) · 3.14 KB
/
smdh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package ctrsigcheck
import (
"bytes"
"encoding/binary"
"fmt"
"image/png"
"io"
"strings"
"github.com/connesc/ctrsigcheck/ctrutil"
)
// SMDH describes the result of SMDH parsing.
type SMDH struct {
Title SMDHTitle
Regions []string
Graphics SMDHGraphics
}
// SMDHTitle describes a title section embedded in a SMDH file.
type SMDHTitle struct {
ShortDescription string
LongDescription string
Publisher string
}
// SMDHGraphics contains the PNG-encoded icons embedded in a SMDH file.
type SMDHGraphics struct {
Small []byte
Large []byte
}
// ParseSMDH extracts some content from the given SMDH file.
func ParseSMDH(input io.Reader) (*SMDH, error) {
reader := ctrutil.NewReader(input)
data := make([]byte, 0x36c0)
_, err := io.ReadFull(reader, data)
if err != nil {
return nil, fmt.Errorf("smdh: failed to read data: %w", err)
}
if string(data[:0x4]) != "SMDH" {
return nil, fmt.Errorf("smdh: magic not found")
}
title := data[0x208:0x408]
shortDescription := strings.TrimRight(ctrutil.DecodeUTF16(title[:0x80], binary.LittleEndian), "\x00")
longDescription := strings.TrimRight(ctrutil.DecodeUTF16(title[0x80:0x180], binary.LittleEndian), "\x00")
publisher := strings.TrimRight(ctrutil.DecodeUTF16(title[0x180:0x200], binary.LittleEndian), "\x00")
regionFlags := binary.LittleEndian.Uint32(data[0x2018:])
regions := make([]string, 0, 1)
if regionFlags == 0x7fffffff {
regions = append(regions, "World")
} else {
if regionFlags > 0x7f {
return nil, fmt.Errorf("smdh: unexpected region flags: %s", Hex32(regionFlags))
} else if (regionFlags&0x04)<<1 != regionFlags&0x08 {
return nil, fmt.Errorf("smdh: regions flags must be the same for Europe and Australia: %s", Hex32(regionFlags))
}
if regionFlags&0x01 != 0 {
regions = append(regions, "Japan")
}
if regionFlags&0x02 != 0 {
regions = append(regions, "North America")
}
if regionFlags&0x04 != 0 {
regions = append(regions, "Europe")
}
if regionFlags&0x10 != 0 {
regions = append(regions, "China")
}
if regionFlags&0x20 != 0 {
regions = append(regions, "Korea")
}
if regionFlags&0x40 != 0 {
regions = append(regions, "Taiwan")
}
}
var pngBuffer bytes.Buffer
rawSmallIcon, err := DecodeIconImage(data[0x2040:0x24c0], 24)
if err != nil {
return nil, fmt.Errorf("smdh: failed to decode small icon image: %w", err)
}
err = png.Encode(&pngBuffer, rawSmallIcon)
if err != nil {
return nil, fmt.Errorf("smdh: failed to encode small icon image: %w", err)
}
smallIcon := make([]byte, pngBuffer.Len())
pngBuffer.Read(smallIcon)
rawLargeIcon, err := DecodeIconImage(data[0x24c0:0x36c0], 48)
if err != nil {
return nil, fmt.Errorf("smdh: failed to decode large icon image: %w", err)
}
err = png.Encode(&pngBuffer, rawLargeIcon)
if err != nil {
return nil, fmt.Errorf("smdh: failed to encode large icon image: %w", err)
}
largeIcon := make([]byte, pngBuffer.Len())
pngBuffer.Read(largeIcon)
return &SMDH{
Title: SMDHTitle{
ShortDescription: shortDescription,
LongDescription: longDescription,
Publisher: publisher,
},
Regions: regions,
Graphics: SMDHGraphics{
Small: smallIcon,
Large: largeIcon,
},
}, nil
}