-
Notifications
You must be signed in to change notification settings - Fork 6
/
message.go
126 lines (113 loc) · 3.4 KB
/
message.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
/*
* Copyright (c) 2014 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the license is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"github.com/winlabs/gowin32/wrappers"
"sort"
"syscall"
"unsafe"
)
type idSlice []uint32
func (ids idSlice) Len() int {
return len(ids)
}
func (ids idSlice) Less(i, j int) bool {
return ids[i] < ids[j]
}
func (ids idSlice) Swap(i, j int) {
ids[i], ids[j] = ids[j], ids[i]
}
func EncodeMessageTable(messages map[uint32]string) []byte {
ids := make(idSlice, 0, len(messages))
for id := range messages {
ids = append(ids, id)
}
sort.Sort(ids)
// partition the ID list into blocks of consecutive numbers
blockLengths := []int{}
var curBlockLength int
var idLast uint32
for _, id := range ids {
if curBlockLength == 0 || id != idLast+1 {
if curBlockLength > 0 {
blockLengths = append(blockLengths, curBlockLength)
}
curBlockLength = 0
}
idLast = id
curBlockLength++
}
if curBlockLength > 0 {
blockLengths = append(blockLengths, curBlockLength)
}
// build an entry data structure for each message
entries := make([][]byte, 0, len(ids))
for _, id := range ids {
messageText := messages[id] + "\u000D\u000A"
textByteLength := 2 * len(messageText)
var nullByteCount uint16
if textByteLength%4 == 0 {
nullByteCount = 4
} else {
nullByteCount = 2
}
var entryHeader wrappers.MESSAGE_RESOURCE_ENTRY
entryHeader.Length = uint16(unsafe.Sizeof(entryHeader)) + uint16(textByteLength) + nullByteCount
entryHeader.Flags = 1
entry := make([]byte, entryHeader.Length)
wrappers.RtlMoveMemory(
&entry[0],
(*byte)(unsafe.Pointer(&entryHeader)),
unsafe.Sizeof(entryHeader))
wrappers.RtlMoveMemory(
&entry[unsafe.Sizeof(entryHeader)],
(*byte)(unsafe.Pointer(syscall.StringToUTF16Ptr(messageText))),
uintptr(textByteLength))
entries = append(entries, entry)
}
// build the file header
header := wrappers.MESSAGE_RESOURCE_DATA{
NumberOfBlocks: uint32(len(blockLengths)),
}
// build the block headers
blocks := make([]wrappers.MESSAGE_RESOURCE_BLOCK, len(blockLengths))
headerLength := uint32(unsafe.Sizeof(header) + unsafe.Sizeof(blocks[0])*uintptr(len(blocks)))
offset := headerLength
entryIndex := 0
for i := range blocks {
blockLength := blockLengths[i]
blocks[i].LowId = ids[entryIndex]
blocks[i].HighId = ids[entryIndex+blockLength-1]
blocks[i].OffsetToEntries = offset
for j := 0; j < blockLength; j++ {
offset += uint32(len(entries[entryIndex+j]))
}
entryIndex += blockLength
}
// concatenate everything
data := make([]byte, headerLength)
offset = 0
wrappers.RtlMoveMemory(&data[offset], (*byte)(unsafe.Pointer(&header)), unsafe.Sizeof(header))
offset += uint32(unsafe.Sizeof(header))
for _, block := range blocks {
wrappers.RtlMoveMemory(&data[offset], (*byte)(unsafe.Pointer(&block)), unsafe.Sizeof(block))
offset += uint32(unsafe.Sizeof(block))
}
for _, entry := range entries {
data = append(data, entry...)
}
return data
}