/
img_ico.go
140 lines (114 loc) · 3.49 KB
/
img_ico.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package image
// STATUS: 85%
// https://en.wikipedia.org/wiki/ICO_(file_format)
// TODO icon_embedded_png_001.ico has embedded PNG in image data
// TODO decode non-png as "standard BMP image"...
import (
"encoding/binary"
"fmt"
"github.com/martinlindhe/formats/parse"
)
var (
iconTypes = map[uint16]string{
1: "icon",
2: "cursor",
}
)
// ICO parses the Windows Icon / Cursor image resource format
func ICO(c *parse.Checker) (*parse.ParsedLayout, error) {
if !isICO(c.Header) {
return nil, nil
}
return parseICO(c)
}
func isICO(b []byte) bool {
h := readIconHeader(b)
if h[0] != 0 {
return false
}
if h[1] != 1 && h[1] != 2 {
// 1 = icon, 2 = cursor
return false
}
// NOTE: arbitrary check to get less false matches
if h[2] > 500 {
return false
}
// NOTE: check to get less false matches, this value "should be 0"
reserved := b[9]
if reserved != 0 {
return false
}
return true
}
func readIconHeader(b []byte) [3]uint16 {
var h [3]uint16
h[0] = binary.LittleEndian.Uint16(b)
h[1] = binary.LittleEndian.Uint16(b[2:])
h[2] = binary.LittleEndian.Uint16(b[4:])
return h
}
func parseICO(c *parse.Checker) (*parse.ParsedLayout, error) {
c.ParsedLayout.FileKind = parse.Image
c.ParsedLayout.MimeType = "image/x-ico"
pos := int64(0)
hdr := readIconHeader(c.Header)
typeName := "?"
if val, ok := iconTypes[hdr[1]]; ok {
typeName = val
}
fileHeader := parse.Layout{
Offset: pos,
Length: 6,
Info: "header",
Type: parse.Group,
Childs: []parse.Layout{
{Offset: 0, Length: 2, Info: "magic", Type: parse.Uint16le},
{Offset: 2, Length: 2, Info: "type = " + typeName, Type: parse.Uint16le},
{Offset: 4, Length: 2, Info: "number of resources", Type: parse.Uint16le},
}}
pos += fileHeader.Length
numIcons := hdr[2]
resourceEntryLength := int64(16)
c.ParsedLayout.Layout = append(c.ParsedLayout.Layout, fileHeader)
for i := 0; i < int(numIcons); i++ {
id := fmt.Sprintf("%d", i+1)
c.ParsedLayout.Layout = append(c.ParsedLayout.Layout, parse.Layout{
Offset: pos,
Length: resourceEntryLength,
Info: "resource " + id + " header",
Type: parse.Group,
Childs: []parse.Layout{
{Offset: pos, Length: 1, Info: "width", Type: parse.Uint8},
{Offset: pos + 1, Length: 1, Info: "height", Type: parse.Uint8},
{Offset: pos + 2, Length: 1, Info: "max number of colors", Type: parse.Uint8},
{Offset: pos + 3, Length: 1, Info: "reserved", Type: parse.Uint8},
{Offset: pos + 4, Length: 2, Info: "planes", Type: parse.Uint16le},
{Offset: pos + 6, Length: 2, Info: "bit count", Type: parse.Uint16le},
{Offset: pos + 8, Length: 4, Info: "data size of resource " + id, Type: parse.Uint32le},
{Offset: pos + 12, Length: 4, Info: "offset to resource " + id, Type: parse.Uint32le},
}})
fileHeader.Length += resourceEntryLength
pos += resourceEntryLength
}
for i := 0; i < int(numIcons); i++ {
id := fmt.Sprintf("%d", i+1)
dataOffset, err := c.ParsedLayout.ReadUint32leFromInfo(c.File, "offset to resource "+id)
if err != nil {
return nil, err
}
dataSize, err := c.ParsedLayout.ReadUint32leFromInfo(c.File, "data size of resource "+id)
if err != nil {
return nil, err
}
c.ParsedLayout.Layout = append(c.ParsedLayout.Layout, parse.Layout{
Offset: int64(dataOffset),
Length: int64(dataSize),
Info: "resource " + id + " data",
Type: parse.Group,
Childs: []parse.Layout{
{Offset: int64(dataOffset), Length: int64(dataSize), Info: "image data", Type: parse.Bytes},
}})
}
return &c.ParsedLayout, nil
}