-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathalg.go
138 lines (117 loc) · 4.83 KB
/
alg.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
// Hashing and decryption algorithms used in MPQ files.
package mpq
import (
"bytes"
"compress/bzip2"
"compress/zlib"
"io"
)
// Different hash types for the hashString() function.
const (
hashTypeTableOffset = iota << 8
hashTypeNameA
hashTypeNameB
hashTypeFileKey
)
// Converts ASCII characters to uppercase.
// Converts slash (0x2F) to backslash (0x5C)
var asciiToUpperTable = []uint32{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
}
// A number table used by the decryption and hashing algorithms.
var cryptTable = make([]uint32, 0x500)
func init() {
// Initialize/compute the cryptTable:
// The encryption/decryption and hashing functions use a number table in their procedures.
// This table must be initialized before the functions are called the first time.
var seed uint32 = 0x00100001
var index1, index2 uint32
var i int
for ; index1 < 0x100; index1++ {
for index2, i = index1, 0; i < 5; i, index2 = i+1, index2+0x100 {
seed = (seed*125 + 3) % 0x2aaaab
temp := (seed & 0xffff) << 0x10
seed = (seed*125 + 3) % 0x2aaaab
cryptTable[index2] = temp | (seed & 0xffff)
}
}
}
// decrypt decrypts the given encrypted data with the specified key.
// The same byte slice is used for the result, so the decrypted data will be written back into the input data slice.
func decrypt(data []byte, key uint32) {
var seed1 = key
var seed2 = uint32(0xeeeeeeee)
var ch uint32
for i, size := 0, len(data); i < size; i += 4 {
seed2 += cryptTable[0x400+(seed1&0xff)]
// littleEndian byte order:
ch = uint32(data[i]) | uint32(data[i+1])<<8 | uint32(data[i+2])<<16 | uint32(data[i+3])<<24
ch ^= seed1 + seed2
seed1 = ((^seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B)
seed2 = ch + seed2 + (seed2 << 5) + 3
data[i] = byte(ch)
data[i+1] = byte(ch >> 8)
data[i+2] = byte(ch >> 16)
data[i+3] = byte(ch >> 24)
}
}
// hashString computes the hash of a string.
func hashString(s string, hashType uint32) uint32 {
var seed1 uint32 = 0x7fed7fed
var seed2 uint32 = 0xeeeeeeee
for i, size := 0, len(s); i < size; i++ {
ch := asciiToUpperTable[s[i]]
// Value of hashType is the offset itself
seed1 = cryptTable[hashType+ch] ^ (seed1 + seed2)
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3
}
return seed1
}
// FileNameHash returns different hashes of the file name,
// exactly the ones that are needed by MPQ.FileByHash().
func FileNameHash(name string) (h1, h2, h3 uint32) {
return hashString(name, hashTypeTableOffset),
hashString(name, hashTypeNameA),
hashString(name, hashTypeNameB)
}
// decompressMulti decompresses a block which was compressed using the multi compression method (beFlagCompressedMulti).
func decompressMulti(dst, src []byte) error {
// Check if block is really compressed, some blocks have set the compression flag, but are not compressed.
if len(src) >= len(dst) {
// Copy block
copy(dst, src)
return nil
}
switch src[0] { // The compression flag
case 0x02: // Flag to indicate zlib compression
zr, err := zlib.NewReader(bytes.NewReader(src[1:]))
if err != nil {
return ErrInvalidArchive
}
if _, err := io.ReadFull(zr, dst); err != nil {
return ErrInvalidArchive
}
case 0x10: // Flag to indicate BZip2 compression
if _, err := io.ReadFull(bzip2.NewReader(bytes.NewReader(src[1:])), dst); err != nil {
return ErrInvalidArchive
}
default: // Compression not supported!
return ErrInvalidArchive
}
return nil
}