-
Notifications
You must be signed in to change notification settings - Fork 0
/
translate.go
177 lines (163 loc) · 4.88 KB
/
translate.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
package demo
import (
"fmt"
"strconv"
"github.com/mewspring/demo/dmo/ast"
)
// translate translates the given AST file into a corresponding demo file.
func translate(root *ast.File) *File {
file := &File{
Hdr: translateFileHeader(root.FileHeader()),
}
for _, astCommand := range root.Commands() {
command := translateCommand(astCommand)
file.Commands = append(file.Commands, command)
}
return file
}
// translateFileHeader translates the given AST file header into a corresponding
// demo file header.
func translateFileHeader(astHdr ast.FileHeader) FileHeader {
return FileHeader{
VersionNum: intLit(astHdr.VersionNum()),
SaveNum: intLit(astHdr.SaveNum()),
ScreenWidth: intLit(astHdr.ScreenWidth()),
ScreenHeight: intLit(astHdr.ScreenHeight()),
}
}
// translateCommand translates the given AST command into a corresponding demo
// command.
func translateCommand(astCommand ast.Command) Command {
commandType := commandTypeFromString(astCommand.CommandType().Text())
switch commandType {
case CommandTypeGameTick:
return GameTickCommand{
CommandType: CommandTypeGameTick,
GameTickProgress: floatLit(astCommand.GameTickProgress()),
}
case CommandTypeRendering:
return RenderingCommand{
CommandType: CommandTypeRendering,
GameTickProgress: floatLit(astCommand.GameTickProgress()),
}
case CommandTypeEvent:
eventData, ok := astCommand.EventData()
if !ok {
panic(fmt.Errorf("missing event data for event command"))
}
wparam := uint32(intLit(eventData.WParam()))
lparam := uint32(intLit(eventData.LParam()))
event := EventCommand{
CommandType: CommandTypeEvent,
GameTickProgress: floatLit(astCommand.GameTickProgress()),
DvlEventType: dvlEventTypeFromString(eventData.EventType().Text()),
}
switch event.DvlEventType {
case DvlEventTypeMouseMove:
x, y := mousePos(uint32(lparam))
event.X = x
event.Y = y
// TODO: figure out how to handle wParam: mods.
case DvlEventTypeMouseButtonLeftDown:
x, y := mousePos(uint32(lparam))
event.X = x
event.Y = y
// TODO: figure out how to handle wParam: mods.
case DvlEventTypeMouseButtonLeftUp:
x, y := mousePos(uint32(lparam))
event.X = x
event.Y = y
// TODO: figure out how to handle wParam: mods.
case DvlEventTypeMouseButtonRightDown:
x, y := mousePos(uint32(lparam))
event.X = x
event.Y = y
// TODO: figure out how to handle wParam: mods.
case DvlEventTypeMouseButtonRightUp:
x, y := mousePos(uint32(lparam))
event.X = x
event.Y = y
// TODO: figure out how to handle wParam: mods.
case DvlEventTypeKeyDown:
key := int(wparam)
event.Key = key
// TODO: figure out how to handle lParam: mods.
case DvlEventTypeKeyUp:
key := int(wparam)
event.Key = key
// TODO: figure out how to handle lParam: mods.
case DvlEventTypeChar:
r := rune(wparam)
event.Rune = r
case DvlEventTypeQuit, DvlEventTypeCaptureChanged, DvlEventTypePaint, DvlEventTypeQueryEndSession:
// nothing to do.
default:
if event.DvlEventType&DvlEventTypeTrigMsgUser != DvlEventTypeTrigMsgUser {
panic(fmt.Errorf("support for event type %d not yet implemented", event.DvlEventType))
}
}
return event
default:
panic(fmt.Errorf("support for command type %d not yet implemented", commandType))
}
}
// CommandType specifies the type of a demo command.
type CommandType uint8
// Command types.
const (
CommandTypeGameTick = 0
CommandTypeRendering = 1
CommandTypeEvent = 2
)
// commandTypeFromString converts the given string to the corresponding command
// type enum.
func commandTypeFromString(s string) CommandType {
switch s {
case "0":
return CommandTypeGameTick
case "1":
return CommandTypeRendering
case "2":
return CommandTypeEvent
default:
panic(fmt.Errorf("support for command type %q not yet implemented", s))
}
}
// dvlEventTypeFromString converts the given string to the corresponding event
// type enum.
func dvlEventTypeFromString(s string) DvlEventType {
x, err := strconv.ParseUint(s, 10, 32)
if err != nil {
panic(fmt.Errorf("unable to parse event type %q; %+v", s, err))
}
return DvlEventType(x)
}
// intLit converts the given integer literal to the corresponding integer.
func intLit(n ast.IntLit) int {
s := n.Text()
x, err := strconv.Atoi(s)
if err != nil {
panic(fmt.Errorf("unable to parse integer literal %q; %+v", s, err))
}
return x
}
// floatLit converts the given floating-point literal to the corresponding
// float.
func floatLit(n ast.FloatLit) float64 {
s := n.Text()
x1, err := strconv.ParseFloat(s, 64)
if err != nil {
x2, err2 := strconv.Atoi(s)
if err2 != nil {
panic(fmt.Errorf("unable to parse floating-point literal %q; %+v; %+v", s, err, err2))
}
return float64(x2)
}
return x1
}
// mousePos returns the X-Y mouse coordinate corresponding to the given lparam.
func mousePos(lparam uint32) (x, y int) {
x = int(lparam & 0xFFFF)
y = int(lparam>>16) & 0xFFFF
return x, y
}