libsl 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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