-
Notifications
You must be signed in to change notification settings - Fork 0
/
mz.go
242 lines (199 loc) · 4.98 KB
/
mz.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package dosfont
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/lunixbochs/struc"
)
type fileFormat int
const (
other fileFormat = iota
pe
ne
)
// Resource resource table entry
type Resource struct {
Start uint16
Size uint16
Type uint16
}
// MZ dos resource container
type MZ struct {
Signature uint16 `struc:"uint16,little"`
BytesInLastBlock uint16 `struc:"uint16,little"`
BlocksInFile uint16 `struc:"uint16,little"`
NumRelocs uint16 `struc:"uint16,little"`
HeaderParagraphs uint16 `struc:"uint16,little"`
MinExtraParagraphs uint16 `struc:"uint16,little"`
MaxExtraParagraphs uint16 `struc:"uint16,little"`
Ss uint16 `struc:"uint16,little"`
Sp uint16 `struc:"uint16,little"`
Checksum uint16 `struc:"uint16,little"`
IP uint16 `struc:"uint16,little"`
Cs uint16 `struc:"uint16,little"`
RelocTableOffset uint16 `struc:"uint16,little"`
OverlayNumber uint16 `struc:"uint16,little"`
Unknown []byte `struc:"[32]pad"`
COFFHeaderOffset uint32 `struc:"uint32,little"` // at 0x3c
format fileFormat `struc:"skip"`
resources []Resource `struc:"skip"`
r io.ReadSeeker `struc:"skip"`
}
const headerSize int64 = 64
const fileIdentifier = 0x5a4D
// ErrHeaderToShort error in case the mz header is too short
var ErrHeaderToShort = errors.New("Error MZ header too short")
// ErrNoMZFile the passed reader doesn't contain the correct header
var ErrNoMZFile = errors.New("Error no MZ file")
// ErrInvalidOffset the resource table contains invalid offsets
var ErrInvalidOffset = errors.New("Error MZ file contains invalid offset")
// ReadMZ reads the MZ file from the passed reader
func ReadMZ(r io.ReadSeeker) (*MZ, error) {
var mz MZ
mz.r = r
err := mz.readHeader()
if err != nil {
return nil, err
}
err = mz.identifyFileFormat()
if err != nil {
return nil, err
}
if mz.format != ne {
return nil, fmt.Errorf("This file format %d is currently not supported", mz.format)
}
err = mz.readNEResourceTable()
if err != nil {
return nil, err
}
return &mz, nil
}
func (mz *MZ) readHeader() error {
var buf bytes.Buffer
n, err := io.CopyN(&buf, mz.r, headerSize)
if err != nil {
return err
}
if n != headerSize {
return ErrHeaderToShort
}
err = struc.Unpack(&buf, mz)
if err != nil {
return err
}
if mz.Signature != fileIdentifier {
return ErrNoMZFile
}
return nil
}
func (mz *MZ) identifyFileFormat() error {
// goto COFF header
_, err := mz.r.Seek(int64(mz.COFFHeaderOffset), io.SeekStart)
if err != nil {
return err
}
// read preamble
var data [4]byte
_, err = mz.r.Read(data[:])
if err != nil {
return err
}
// find file format
if "NE" == string(data[0:2]) {
mz.format = ne
} else if "PE\x00\x00" == string(data[:]) {
mz.format = pe
}
return nil
}
func (mz *MZ) readNEResourceTable() error {
// goto COFF header
_, err := mz.r.Seek(int64(mz.COFFHeaderOffset+0x24), io.SeekStart)
if err != nil {
return err
}
// find rtable
var rtableStart uint16
err = binary.Read(mz.r, binary.LittleEndian, &rtableStart)
if err != nil {
return err
}
rtable := int64(mz.COFFHeaderOffset + uint32(rtableStart))
// go to table start
_, err = mz.r.Seek(rtable, io.SeekStart)
if err != nil {
return err
}
// read shift value
var rtableShift uint16
err = binary.Read(mz.r, binary.LittleEndian, &rtableShift)
if err != nil {
return err
}
for {
// Read Type
var rtype uint16
err = binary.Read(mz.r, binary.LittleEndian, &rtype)
if err != nil {
return err
}
// check for end of the resource table
if rtype == 0 {
break
}
// Read count
var count uint16
err = binary.Read(mz.r, binary.LittleEndian, &count)
if err != nil {
return err
}
// SKIP 4 bytes reserved
_, err = mz.r.Seek(4, io.SeekCurrent)
if err != nil {
return err
}
for i := 0; i < int(count); i++ {
// read start and size
var start uint16
err = binary.Read(mz.r, binary.LittleEndian, &start)
if err != nil {
return err
}
var size uint16
err = binary.Read(mz.r, binary.LittleEndian, &size)
if err != nil {
return err
}
// shift values
start = start << rtableShift
size = size << rtableShift
if start < 0 || size < 0 {
return ErrInvalidOffset
}
// add fonts
mz.resources = append(mz.resources, Resource{
Start: start,
Size: size,
Type: rtype,
})
// flags, name/id, 4 bytes reserved
_, err = mz.r.Seek(8, io.SeekCurrent)
if err != nil {
return err
}
}
}
return nil
}
// Resources filters for resources of passed type, that are contained inside the MZ file
func (mz *MZ) Resources(filterType uint16) []Resource {
var filtered []Resource
for _, resource := range mz.resources {
if resource.Type == filterType {
filtered = append(filtered, resource)
}
}
return filtered
}