-
Notifications
You must be signed in to change notification settings - Fork 0
/
mpq.go
176 lines (154 loc) · 3.96 KB
/
mpq.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
package mpq
import (
"./file"
"./encryptedfile"
"encoding/binary"
// "bytes"
"os"
"fmt"
// "strings"
)
// derived from: http://wiki.devklog.net/index.php?title=The_MoPaQ_Archive_Format
// with some help from: https://github.com/arkx/mpyq/blob/master/mpyq.py
type MPQHeader struct {
Magic [4]byte
HeaderSize int32
ArchiveSize int32
FormatVersion int16
SectorSizeShift int16 // MoPaQ wiki lists this as int8, but this fails
HashTableOffset int32
BlockTableOffset int32
HashTableEntries int32
BlockTableEntries int32
}
type MPQUserDataHeader struct {
Magic [4]byte
UserDataSize int32
ArchiveHeaderOffset int32
UserData []byte
}
type MPQHeaderExt struct {
ExtendedBlockTableOffset int64
HashTableOffsetHigh int16
BlockTableOffsetHigh int16
}
type BlockTableEntry struct {
BlockOffset int32
BlockSize int32
FileSize int32
Flags int32
}
type HashTableEntry struct {
FilePathHashA int32
FilePathHashB int32
Language int16
Platform int16 // another case where int8 is listed
FileBlockIndex int32
}
type MPQFile struct {
file *file.File
UserDataHeader *MPQUserDataHeader
Header *MPQHeader
HeaderExt *MPQHeaderExt
BlockTable []BlockTableEntry
HashTable []HashTableEntry
}
func (f *MPQFile) readUserDataHeader() (e os.Error) {
f.UserDataHeader = new(MPQUserDataHeader)
e = binary.Read(f.file, binary.LittleEndian,
&f.UserDataHeader.Magic)
if e != nil {
return e
}
e = binary.Read(f.file, binary.LittleEndian,
&f.UserDataHeader.UserDataSize)
if e != nil {
return e
}
e = binary.Read(f.file, binary.LittleEndian,
&f.UserDataHeader.ArchiveHeaderOffset)
if e != nil {
return e
}
f.UserDataHeader.UserData = make([]byte, f.UserDataHeader.UserDataSize)
_, e = f.file.Read(f.UserDataHeader.UserData)
return e
}
func (f *MPQFile) readHeader() (e os.Error) {
f.Header = new(MPQHeader)
e = binary.Read(f.file, binary.LittleEndian, f.Header)
return e
}
func (f *MPQFile) readHeaderExt() (e os.Error) {
f.HeaderExt = new(MPQHeaderExt)
e = binary.Read(f.file, binary.LittleEndian, f.HeaderExt)
return e
}
func (f *MPQFile) readHashTable() (e os.Error) {
var key uint32 = encryptedfile.HashString("(hash table)",
encryptedfile.MPQ_HASH_TABLE_OFFSET)
f.file.Seek(int64(f.Header.HashTableOffset), 0)
f.HashTable = make([]HashTableEntry, f.Header.HashTableEntries)
fmt.Printf("trying to read %d hash table entries", f.Header.HashTableEntries)
ef := encryptedfile.NewEncryptedFile(f.file, key)
e = binary.Read(ef, binary.LittleEndian, f.HashTable)
return e
}
func readFile(f *file.File) (mpqFile *MPQFile, err os.Error) {
mpqFile = new(MPQFile)
mpqFile.file = f
var magic [4]byte
r, e := mpqFile.file.Read(magic[:])
mpqFile.file.Seek(0, 0)
if r < 1 { // 0 is EOF, TODO: support this case too
fmt.Printf("Problem reading header: %s\n", e.String())
return nil,e
}
if magic[0] != 'M' || magic[1] != 'P' || magic[2] != 'Q' {
fmt.Printf("not a valid mpq file: %d", magic)
return nil,e
}
if magic[3] == '\x1B' { // user data header is first
e = mpqFile.readUserDataHeader()
if e != nil {
return nil,e
}
mpqFile.file.Seek(int64(mpqFile.UserDataHeader.ArchiveHeaderOffset), 0)
r, e := mpqFile.file.Read(magic[:])
mpqFile.file.Seek(int64(mpqFile.UserDataHeader.ArchiveHeaderOffset), 0)
switch {
case r < 1:
//fmt.Printf("Problem reading header: %s\n", e.String())
return nil,e
case r == 0: // EOF
return mpqFile,e
default:
}
} else {
mpqFile.UserDataHeader = new(MPQUserDataHeader)
mpqFile.UserDataHeader.ArchiveHeaderOffset = 0
}
if magic[3] != '\x1A' {
fmt.Printf("Unexpected magic value: %d\n", magic)
return nil,os.NewError("Unexpected magic value")
}
e = mpqFile.readHeader()
if e != nil {
return nil,e
}
if mpqFile.Header.FormatVersion == 1 {
e = mpqFile.readHeaderExt()
if e != nil {
return nil,e
}
}
e = mpqFile.readHashTable()
return mpqFile, e
}
func Open(filename string) (mpqFile *MPQFile, err os.Error) {
f, e := file.Open(filename)
if f == nil {
return nil,e
}
return readFile(f)
}