libsl 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/packet.rb ADDED
@@ -0,0 +1,279 @@
1
+
2
+ module LibSL
3
+ class Packet
4
+ def self.packet_id()
5
+ 0
6
+ end
7
+
8
+ def self.decode(data)
9
+ flags, data = LLU8.decode(data)
10
+ sequence_number, data = LLSequenceNumber.decode(data)
11
+ extra, data = LLU8.decode(data)
12
+ if extra.value > 0 then
13
+ extra, data = LLFixed.decode(data, extra)
14
+ end
15
+ data = zero_decode(data) if flags.value & 0x80 == 0x80
16
+ packet_number, data = LLPacketNumber.decode(data)
17
+ klass = MESSAGE_MAP[packet_number.number]
18
+ throw "Unknown message type!" if klass.nil?
19
+
20
+ packet = klass.new
21
+ packet.zero_coded_flag = flags.value & 0x80 == 0x80
22
+ packet.reliable_flag = flags.value & 0x40 == 0x40
23
+ packet.resent_flag = flags.value & 0x20 == 0x20
24
+ packet.acks_flag = flags.value & 0x10 == 0x10
25
+ packet.sequence_number = sequence_number
26
+ data = packet.decode_msg(data)
27
+ if packet.acks_flag && data.length > 0 then
28
+ # Extract acks
29
+ ack_count = data[-1]
30
+ packet.acks = data.unpack('N'+ack_count)
31
+ end
32
+ packet
33
+ end
34
+
35
+ def self.zero_decode(data)
36
+ # Decode 0x00 0xXX to a sequence of 0xXX zeros
37
+ decoded = []
38
+ zero = false
39
+ data.bytes do |byte|
40
+ if byte == 0x00
41
+ zero = true
42
+ elsif zero == true
43
+ byte.times{ decoded << 0x00 }
44
+ zero = false
45
+ else
46
+ decoded << byte
47
+ end
48
+ end
49
+ decoded.pack('C*')
50
+ end
51
+
52
+ attr_accessor :acks, :zero_coded_flag, :reliable_flag, :resent_flag, :acks_flag,
53
+ :sequence_number, :resent_count
54
+
55
+ # Create a new packet
56
+ # @param [Hash] blocks (optional) Data blocks
57
+ # Example:
58
+ # {
59
+ # :Block1 => { # Block
60
+ # :Var1 => LLU8.new(0xff),
61
+ # :Var2 => LLVariable1.new("abcd")
62
+ # },
63
+ # :Block2 => [ # Block collection
64
+ # {
65
+ # :Var1 => LLFixed.new("block1"),
66
+ # :Var2 => LLS32.new(0x12345678)
67
+ # },
68
+ # {
69
+ # :Var1 => LLFixed.new("block2"),
70
+ # :Var2 => LLS32.new(0x12345678)
71
+ # }
72
+ # ]
73
+ # }
74
+ def initialize(blocks={})
75
+ @zero_coded_flag = false
76
+ @reliable_flag = false
77
+ @resent_flag = false
78
+ @acks_flag = false
79
+ @acks = []
80
+ @resent_count = 0
81
+ build_structure
82
+ blocks.each do |name, block|
83
+ case block
84
+ when Array then
85
+ block.each_index do |i|
86
+ collection = self.send(name)
87
+ case collection
88
+ when VariableBlockCollection then
89
+ b = collection.add
90
+ when FixedBlockCollection then
91
+ b = collection[i]
92
+ end
93
+
94
+ block[i].each do |v|
95
+ b.send((v[0].to_s + "=").to_sym, v[1])
96
+ end
97
+ end
98
+ when Hash then
99
+ block.each do |v|
100
+ self.send(name).send((v[0].to_s + "=").to_sym, v[1])
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def encode()
107
+ flags = 0
108
+ flags |= 0x80 if @zero_coded_flag
109
+ flags |= 0x40 if @reliable_flag
110
+ flags |= 0x20 if @resent_flag
111
+ flags |= 0x10 if @acks_flag
112
+
113
+ data = LLU8.new(flags).encode +
114
+ LLSequenceNumber.new(@sequence_number).encode +
115
+ LLU8.new(0).encode
116
+ pid = LLPacketNumber.new(self.class.packet_id).encode
117
+ data += pid
118
+ data += encode_msg
119
+ # append acks
120
+ data += ([*@acks] + [@acks.length]).pack("N#{@acks.length}C") if @acks_flag
121
+ data
122
+ end
123
+
124
+ # Subclasses have to override this method to build the message structure
125
+ # IMPORTANT: Use arrays to describe the structure, as hashes don't preserve the order
126
+ # Example:
127
+ # def build_structure()
128
+ # @blocks = [
129
+ # [:AgentData, Block.new([
130
+ # [:AgentID, :LLUUID],
131
+ # [:SessionID, :LLUUID]
132
+ # ])],
133
+ # [:SomeData, FixedBlockCollection.new(4, [
134
+ # [:Data, :LLVariable1]
135
+ # ])]
136
+ # ]
137
+ # end
138
+ def build_structure()
139
+ end
140
+
141
+ def decode_msg(data)
142
+ @blocks.each do |block|
143
+ data = block[1].decode(data)
144
+ end
145
+ data
146
+ rescue => e
147
+ puts "[ERROR] #{self.class.name}: #{self.inspect}"
148
+ raise e
149
+ end
150
+
151
+ def encode_msg()
152
+ data = ""
153
+ @blocks.each{ |block| data += block[1].encode }
154
+ data
155
+ end
156
+
157
+ def method_missing(name, *args)
158
+ block = @blocks.assoc(name)
159
+ throw "The method #{name} does not exist in #{self.class.name}" if block.nil?
160
+ block[1]
161
+ end
162
+ end
163
+
164
+ class Block
165
+ def initialize(structure)
166
+ @structure = structure
167
+ @data = []
168
+ @structure.length.times{ @data << nil }
169
+ end
170
+
171
+ def decode(data)
172
+ @data = []
173
+ @structure.each do |var|
174
+ begin
175
+ args = [data]
176
+ args << var[2] if var[1] == :LLFixed
177
+ value, data = LibSL.const_get(var[1]).decode(*args)
178
+ #rescue => e
179
+ # throw e
180
+ # throw "Type #{type.to_s} does not exist!"
181
+ end
182
+ @data << [var[0], value]
183
+ end
184
+ data
185
+ end
186
+
187
+ def encode()
188
+ throw "Data has to be an array." unless @data.is_a? Array
189
+ throw "Block is missing data." if @data.length != @structure.length
190
+ data = ""
191
+ @data.each{ |var| data += var[1].encode }
192
+ data
193
+ end
194
+
195
+ def method_missing(name, *args)
196
+ name = name.to_s
197
+ if name[-1..-1] == "=" then
198
+ # Setter
199
+ var = name[0..-2].to_sym
200
+ throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
201
+ index = @structure.index{|v| v[0] == var}
202
+ @data[index] = [var, args[0]]
203
+ else
204
+ # Getter
205
+ var = name.to_sym
206
+ throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
207
+ @data.assoc(var)[1]
208
+ end
209
+ end
210
+ end
211
+
212
+ class FixedBlockCollection
213
+ attr_reader :blocks
214
+ def initialize(size, structure)
215
+ @size = size
216
+ @blocks = []
217
+ @size.times{@blocks << Block.new(structure)}
218
+ end
219
+
220
+ def length()
221
+ @size
222
+ end
223
+
224
+ def [](index)
225
+ throw "Index out of range: #{index} is not in [0, #{length - 1}]" if index < 0 or index >= length
226
+ @blocks[index]
227
+ end
228
+
229
+ def each(&block)
230
+ @blocks.each{|block| yield block}
231
+ end
232
+
233
+ def decode(data)
234
+ @blocks.each{ |block| data = block.decode(data) }
235
+ data
236
+ end
237
+
238
+ def encode()
239
+ data = ""
240
+ @blocks.each{ |block| data += block.encode }
241
+ data
242
+ end
243
+ end
244
+
245
+ class VariableBlockCollection < FixedBlockCollection
246
+ def initialize(structure)
247
+ @structure = structure
248
+ @blocks = []
249
+ end
250
+
251
+ def length()
252
+ @blocks.length
253
+ end
254
+
255
+ def add()
256
+ @blocks << Block.new(@structure)
257
+ @blocks.last
258
+ end
259
+
260
+ def decode(data)
261
+ size, data = LLU8.decode(data)
262
+ size.value.times{ data = add.decode(data) }
263
+ data
264
+ end
265
+
266
+ def encode()
267
+ data = LLU8.new(length).encode
268
+ @blocks.each{ |block| data += block.encode }
269
+ data
270
+ end
271
+ end
272
+ end
273
+
274
+ # Include packet types generated from the specification
275
+ begin
276
+ require File.join File.dirname(__FILE__), "_packets"
277
+ rescue LoadError => e
278
+ puts "Packets not found! Please build them."
279
+ end