libsl 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/types.rb ADDED
@@ -0,0 +1,490 @@
1
+
2
+ module LibSL
3
+ class LLPacketNumber
4
+ def self.decode(data)
5
+ data = data.unpack('C1a*')
6
+ num = data.shift()
7
+ unless num < 0xff then
8
+ data = data[0].unpack('C1a*')
9
+ num = (num << 8) + data.shift()
10
+ end
11
+ unless num < 0xffff then
12
+ data = data[0].unpack('C2a*')
13
+ num = (num << 16) + (data.shift() << 8) + data.shift()
14
+ end
15
+ if [*data].length > 0 then
16
+ data = data[0]
17
+ else
18
+ data = nil
19
+ end
20
+ return self.new(num), data
21
+ end
22
+
23
+ def encode()
24
+ case @frequency
25
+ when :high then data = [@number].pack('C')
26
+ when :medium then data = [@number].pack('n')
27
+ else data = [@number].pack('N')
28
+ end
29
+ return data
30
+ end
31
+
32
+ attr_accessor :number, :frequency
33
+ def initialize(number)
34
+ @number = number
35
+ if @number < 0xff then
36
+ @frequency = :high
37
+ elsif @number < 0xffff then
38
+ @frequency = :medium
39
+ elsif @number < 0xfffffffa then
40
+ @frequency = :low
41
+ else
42
+ @frequency = :fixed
43
+ end
44
+ end
45
+ end
46
+
47
+ class LLSequenceNumber
48
+ def self.decode(data)
49
+ data = data.unpack('N1a*')
50
+ return self.new(data[0]), data[1]
51
+ end
52
+
53
+ def encode()
54
+ [@number].pack('N')
55
+ end
56
+
57
+ attr_accessor :number
58
+ def initialize(num)
59
+ @number = num
60
+ end
61
+
62
+ def inc()
63
+ @number += 1
64
+ @number
65
+ end
66
+ end
67
+
68
+ class LLNull
69
+ def self.decode(data)
70
+ return self.new, data
71
+ end
72
+
73
+ def encode()
74
+ ""
75
+ end
76
+
77
+ def initialize(val)
78
+ end
79
+ end
80
+
81
+ class LLFixed
82
+ def self.decode(data, len)
83
+ data = data.unpack("a#{len}a*")
84
+ return self.new(data[0]), data[1]
85
+ end
86
+
87
+ def encode()
88
+ @data
89
+ end
90
+
91
+ attr_accessor :data
92
+ def initialize(data)
93
+ @data = data
94
+ end
95
+ end
96
+
97
+ class LLVariable1
98
+ def self.decode(data)
99
+ data = data.unpack('C1a*')
100
+ data = data[1].unpack("a#{data[0]}a*")
101
+ return self.new(data[0]), data[1]
102
+ end
103
+
104
+ def encode()
105
+ [@data.length, @data].pack('C1a*')
106
+ end
107
+
108
+ attr_accessor :data
109
+ def initialize(data)
110
+ @data = data
111
+ end
112
+ end
113
+
114
+ class LLVariable2 < LLVariable1
115
+ def self.decode(data)
116
+ data = data.unpack('n1a*')
117
+ data = data[1].unpack("a#{data[0]}a*")
118
+ return self.new(data[0]), data[1]
119
+ end
120
+
121
+ def encode()
122
+ [@data.length, @data].pack('n1a*')
123
+ end
124
+ end
125
+
126
+ class LLNumber
127
+ def self.decode(data, pattern, klass)
128
+ data = data.unpack(pattern)
129
+ return klass.new(data[0]), data[1]
130
+ end
131
+
132
+ def self.endianness()
133
+ little = "78563412"
134
+ big = "12345678"
135
+ case "%8x" % ([0x12345678].pack('l').unpack('N')[0])
136
+ when little then :little
137
+ when big then :big
138
+ else :other
139
+ end
140
+ end
141
+
142
+ def encode(pattern)
143
+ [@value].pack(pattern)
144
+ end
145
+
146
+ attr_accessor :value
147
+ def initialize(value)
148
+ @value = value
149
+ end
150
+ end
151
+
152
+ class LLU8 < LLNumber
153
+ def self.decode(data)
154
+ LLNumber.decode(data, 'C1a*', LLU8)
155
+ end
156
+
157
+ def encode()
158
+ raise ArgumentError, "Value out of range: #{@value}" if @value < 0 or @value > 0xff
159
+ super('C')
160
+ end
161
+ end
162
+
163
+ class LLU16 < LLNumber
164
+ def self.decode(data)
165
+ LLNumber.decode(data, 'v1a*', LLU16)
166
+ end
167
+
168
+ def encode()
169
+ raise ArgumentError, "Value out of range: #{@value}" if @value < 0 or @value > 0xffff
170
+ super('v1')
171
+ end
172
+ end
173
+
174
+ class LLU32 < LLNumber
175
+ def self.decode(data)
176
+ LLNumber.decode(data, 'V1a*', LLU32)
177
+ end
178
+
179
+ def encode()
180
+ raise ArgumentError, "Value out of range: #{@value}" if @value < 0 or @value > 0xffffffff
181
+ super('V1')
182
+ end
183
+ end
184
+
185
+ class LLU64 < LLNumber
186
+ def self.decode(data)
187
+ u64, data = LLNumber.decode(data, 'Q1a*', LLU64)
188
+ if self.endianness == :big
189
+ # Convert to little endian
190
+ u64.value = u64.value >> 56 |
191
+ (u64.value & 0xff000000000000) >> 40 |
192
+ (u64.value & 0xff0000000000) >> 24 |
193
+ (u64.value & 0xff00000000) >> 8 |
194
+ (u64.value & 0xff000000) << 8 |
195
+ (u64.value & 0xff0000) << 24 |
196
+ (u64.value & 0xff00) << 40 |
197
+ (u64.value & 0xff) << 56
198
+ end
199
+ return u64, data
200
+ end
201
+
202
+ def encode()
203
+ raise ArgumentError, "Value out of range: #{@value}" if @value < 0 or @value > 0xffffffffffffffff
204
+ ms = @value >> 32
205
+ ls = @value - (ms << 32)
206
+ [ls, ms].pack('V2')
207
+ end
208
+ end
209
+
210
+ class LLS8 < LLNumber
211
+ def self.decode(data)
212
+ LLNumber.decode(data, 'c1a*', LLS8)
213
+ end
214
+
215
+ def encode()
216
+ raise ArgumentError, "Value out of range: #{@value}" if @value < -0x80 or @value > 0x7f
217
+ super('c1')
218
+ end
219
+ end
220
+
221
+ class LLS16 < LLNumber
222
+ def self.decode(data)
223
+ s16, data = LLNumber.decode(data, 's1a*', LLS16)
224
+ if self.endianness == :big
225
+ # Convert to little endian
226
+ ms = s16.value >> 8
227
+ ls = s16 - (ms << 8)
228
+ s16.value = (ls << 8) + ms
229
+ end
230
+ return s16, data
231
+ end
232
+
233
+ def encode()
234
+ raise ArgumentError, "Value out of range: #{@value}" if @value < -0x8000 or @value > 0x7fff
235
+ data = super('s')
236
+ if LLNumber::endianness == :big
237
+ data = data.unpack('C2').reverse.pack('C2')
238
+ end
239
+ return data
240
+ end
241
+ end
242
+
243
+ class LLS32 < LLNumber
244
+ def self.decode(data)
245
+ s32, data = LLNumber.decode(data, 'l1a*', LLS32)
246
+ if self.endianness == :big
247
+ # Convert to little endian
248
+ s32.value = s32.value >> 24 |
249
+ (s32.value & 0xff0000) >> 8 |
250
+ (s32.value & 0xff00) << 8 |
251
+ (s32.value & 0xff) << 24
252
+ end
253
+ return s32, data
254
+ end
255
+
256
+ def encode()
257
+ raise ArgumentError, "Value out of range: #{@value}" if @value < -0x80000000 or @value > 0x7fffffff
258
+ data = super('l')
259
+ if LLNumber::endianness == :big
260
+ data = data.unpack('C4').reverse.pack('C4')
261
+ end
262
+ return data
263
+ end
264
+ end
265
+
266
+ class LLS64 < LLNumber
267
+ def self.decode(data)
268
+ s64, data = LLNumber.decode(data, 'q1a*', LLS64)
269
+ if self.endianness == :big
270
+ # Convert to little endian
271
+ s64.value = s64.value >> 56 |
272
+ (s64.value & 0xff000000000000) >> 40 |
273
+ (s64.value & 0xff0000000000) >> 24 |
274
+ (s64.value & 0xff00000000) >> 8 |
275
+ (s64.value & 0xff000000) << 8 |
276
+ (s64.value & 0xff0000) << 24 |
277
+ (s64.value & 0xff00) << 40 |
278
+ (s64.value & 0xff) << 56
279
+ end
280
+ return s64, data
281
+ end
282
+
283
+ def encode()
284
+ raise ArgumentError, "Value out of range: #{@value}" if @value < -0x8000000000000000 or @value > 0x7fffffffffffffff
285
+ data = super('q')
286
+ if LLNumber::endianness == :big
287
+ data = data.unpack('C8').reverse.pack('C8')
288
+ end
289
+ return data
290
+ end
291
+ end
292
+
293
+ class LLF32 < LLNumber
294
+ def self.decode(data)
295
+ data = data.unpack('ea*')
296
+ return self.new(data[0]), data[1]
297
+ end
298
+
299
+ def encode()
300
+ [@value].pack('e')
301
+ end
302
+ end
303
+
304
+ class LLF64 < LLNumber
305
+ def self.decode(data)
306
+ data = data.unpack('Ea*')
307
+ return self.new(data[0]), data[1]
308
+ end
309
+
310
+ def encode()
311
+ [@value].pack('E')
312
+ end
313
+ end
314
+
315
+ class LLVector3
316
+ def self.decode(data)
317
+ data = data.unpack('e3a*')
318
+ return self.new(data[0], data[1], data[2]), data[3]
319
+ end
320
+
321
+ def encode()
322
+ [@x, @y, @z].pack('e3')
323
+ end
324
+
325
+ attr_accessor :x, :y, :z
326
+ def initialize(x, y, z)
327
+ @x, @y, @z = x, y, z
328
+ end
329
+ end
330
+
331
+ class LLVector3d < LLVector3
332
+ def self.decode(data)
333
+ data = data.unpack('E3a*')
334
+ return self.new(data[0], data[1], data[2]), data[3]
335
+ end
336
+
337
+ def encode()
338
+ [@x, @y, @z].pack('E3')
339
+ end
340
+ end
341
+
342
+ class LLVector4
343
+ def self.decode(data)
344
+ data = data.unpack('e4a*')
345
+ return self.new(data[0], data[1], data[2], data[3]), data[4]
346
+ end
347
+
348
+ def encode()
349
+ [@a, @b, @c, @d].pack('e4')
350
+ end
351
+
352
+ attr_accessor :a, :b, :c, :d
353
+ def initialize(a, b, c, d)
354
+ @a, @b, @c, @d = a, b, c, d
355
+ end
356
+ end
357
+
358
+ class LLQuaternion
359
+ def self.decode(data)
360
+ data = data.unpack 'e3a*'
361
+ return self.new(data[0], data[1], data[2]), data[3]
362
+ end
363
+
364
+ def encode()
365
+ [@x, @y, @z].pack 'e3'
366
+ end
367
+
368
+ attr_accessor :x, :y, :z, :w
369
+ def initialize(x, y, z, w=nil)
370
+ @x, @y, @z = x, y, z
371
+ if w.nil? then
372
+ sum = 1 - @x**2 - @y**2 - @z**2
373
+ @w = if sum > 0 then Math.sqrt sum else 0 end
374
+ else
375
+ @w = w
376
+ end
377
+ end
378
+ end
379
+
380
+ class LLUUID
381
+ attr_reader :uuid
382
+
383
+ def self.decode(data)
384
+ data = data.unpack 'a16a*'
385
+ return self.new(data[0]), data[1]
386
+ end
387
+
388
+ def self.null()
389
+ return self.new("00000000-0000-0000-0000-000000000000")
390
+ end
391
+
392
+ def encode()
393
+ to_bytes
394
+ end
395
+
396
+ def initialize(uuid=nil)
397
+ if uuid.nil?
398
+ # Generate a random UUID
399
+ uuid = [
400
+ rand(0x100000000),
401
+ rand(0x100000000),
402
+ rand(0x100000000),
403
+ rand(0x100000000),
404
+ ].pack "N4"
405
+ uuid[6] &= 0b00001111
406
+ uuid[6] |= 48
407
+ uuid[8] &= 0b00111111
408
+ uuid[8] |= 0b10000000
409
+ end
410
+
411
+ raise "Invalid uuid format" unless uuid.is_a? String
412
+ @uuid = if uuid.length == 36 and uuid.count("-") == 4 then
413
+ # String representation (Guid)
414
+ uuid.delete("-").scan(/../).map{|s| s.to_i(16)}
415
+ else
416
+ # Unpack from binary string
417
+ uuid.unpack('C16')
418
+ end
419
+ end
420
+
421
+ def to_s()
422
+ sprintf('%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x', *(@uuid)).upcase
423
+ end
424
+
425
+ def to_bytes()
426
+ @uuid.pack('C16')
427
+ end
428
+ end
429
+
430
+ class LLBool
431
+ def self.decode(data)
432
+ data = data.unpack('C1a*')
433
+ return self.new(data[0] == 1), data[1]
434
+ end
435
+
436
+ def encode()
437
+ [@value ? 1 : 0].pack('C')
438
+ end
439
+
440
+ def eql?(val)
441
+ @value == val
442
+ end
443
+
444
+ def ==(val)
445
+ @value == val
446
+ end
447
+
448
+ attr_accessor :value
449
+ def initialize(value)
450
+ @value = value
451
+ end
452
+ end
453
+
454
+ class LLIPAddress
455
+ def self.decode(data)
456
+ data = data.unpack('C4a*')
457
+ return self.new(sprintf("%d.%d.%d.%d", data[0], data[1], data[2], data[3])), data[4]
458
+ end
459
+
460
+ def encode()
461
+ @ip.scan(/[0-9]+/).map {|s| s.to_i}.pack('C4')
462
+ end
463
+
464
+ attr_accessor :ip
465
+ def initialize(ip)
466
+ raise ArgumentError, "ip must be a string (e.g. 1.2.3.4)" unless ip.is_a? String
467
+ @ip = ip
468
+ end
469
+
470
+ def to_s()
471
+ @ip.to_s
472
+ end
473
+ end
474
+
475
+ class LLIPPort
476
+ def self.decode(data)
477
+ data = data.unpack('n1a*')
478
+ return self.new(data[0]), data[1]
479
+ end
480
+
481
+ def encode()
482
+ [@port].pack('n1')
483
+ end
484
+
485
+ attr_accessor :port
486
+ def initialize(port)
487
+ @port = port
488
+ end
489
+ end
490
+ end
data/libsl.gemspec ADDED
@@ -0,0 +1,30 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.name = "libsl"
4
+ s.version = File.read('VERSION')
5
+ s.platform = Gem::Platform::RUBY
6
+ s.authors = ["Lukas Botsch"]
7
+ s.email = ["lukas.botsch@gmail.com"]
8
+ s.homepage = "http://github.com/lbotsch/libsl-ruby"
9
+ s.summary = "Library for building SecondLife client applications"
10
+ s.description = "Build SecondLife clients using a simple DSL"
11
+ s.rubyforge_project = s.name
12
+
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+
15
+ # If you have runtime dependencies, add them here
16
+ s.add_runtime_dependency "eventmachine", ">= 0.12.10"
17
+
18
+ # If you have development dependencies, add them here
19
+ # s.add_development_dependency "another", "= 0.9"
20
+
21
+ # The list of files to be contained in the gem
22
+ s.files = `git ls-files`.split("\n").delete_if{|f| f =~ /^(example|scripts|\.)\//}
23
+ # s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
24
+ # s.extensions = `git ls-files ext/extconf.rb`.split("\n")
25
+
26
+ s.require_path = 'lib'
27
+
28
+ # For C extensions
29
+ # s.extensions = "ext/extconf.rb"
30
+ end
data/test/tc_packet.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'test/unit'
2
+ %w{types packet}.each{|f|
3
+ require File.join File.dirname(__FILE__), "..", "lib", f
4
+ }
5
+
6
+ module LibSL
7
+ class StubPacket < Packet
8
+ def self.packet_id()
9
+ 1
10
+ end
11
+
12
+ def build_structure()
13
+ @blocks = [
14
+ [:SomeBlock, Block.new([
15
+ [:Data, :LLU8]
16
+ ])],
17
+ [:FixedCollection, FixedBlockCollection.new(2, [
18
+ [:Data, :LLU8]
19
+ ])],
20
+ [:VariableCollection, VariableBlockCollection.new([
21
+ [:Data, :LLU8]
22
+ ])]
23
+ ]
24
+ end
25
+ end
26
+
27
+ MESSAGE_MAP = {1 => StubPacket}
28
+
29
+ class TestPacket < Test::Unit::TestCase
30
+ def test_decode()
31
+ packet = Packet.decode(
32
+ "\x40" + # Flags
33
+ "\x00\x00\x00\x01" + # Sequence Number
34
+ "\x00" + # Extra header length
35
+ "\x01" + # Message Number
36
+ "\xff" + # SomeBlock
37
+ "\xff\xff" + # FixedCollection
38
+ "\x02\xff\xff") # VariableCollection
39
+ assert_equal(StubPacket, packet.class)
40
+ assert_equal(true, packet.reliable_flag)
41
+ assert_equal(0xff, packet.SomeBlock.Data.value)
42
+ assert_equal(0xff, packet.FixedCollection[0].Data.value)
43
+ assert_equal(0xff, packet.FixedCollection[1].Data.value)
44
+ assert_equal(0xff, packet.VariableCollection[0].Data.value)
45
+ assert_equal(0xff, packet.VariableCollection[1].Data.value)
46
+ end
47
+
48
+ def test_zero_decode()
49
+ packet = Packet.decode(
50
+ "\x80" + # Flags zero_coded
51
+ "\x00\x00\x00\x01" + # Sequence Number
52
+ "\x00" + # Extra header length
53
+ "\x01" + # Message Number
54
+ "\x00\x01" + # SomeBlock
55
+ "\x00\x02" + # FixedCollection
56
+ "\x02\x00\x02") # VariableCollection
57
+ assert_equal(StubPacket, packet.class)
58
+ assert_equal(true, packet.zero_coded_flag)
59
+ assert_equal(0x00, packet.SomeBlock.Data.value)
60
+ assert_equal(0x00, packet.FixedCollection[0].Data.value)
61
+ assert_equal(0x00, packet.FixedCollection[1].Data.value)
62
+ assert_equal(0x00, packet.VariableCollection[0].Data.value)
63
+ assert_equal(0x00, packet.VariableCollection[1].Data.value)
64
+ end
65
+
66
+ def test_encode()
67
+ packet = StubPacket.new
68
+ packet.reliable_flag = true
69
+ packet.sequence_number = 1
70
+ packet.SomeBlock.Data = LLU8.new(0xff)
71
+ packet.FixedCollection[0].Data = LLU8.new(0xff)
72
+ packet.FixedCollection[1].Data = LLU8.new(0xff)
73
+ packet.VariableCollection.add.Data = LLU8.new(0xff)
74
+ packet.VariableCollection.add.Data = LLU8.new(0xff)
75
+
76
+ data = packet.encode
77
+ assert_equal("\x40\x00\x00\x00\x01\x00\x01\xff\xff\xff\x02\xff\xff", data)
78
+ end
79
+
80
+ def test_new()
81
+ packet = StubPacket.new({
82
+ :SomeBlock => { :Data => LLU8.new(0xff) },
83
+ :FixedCollection => [
84
+ { :Data => LLU8.new(0xff) },
85
+ { :Data => LLU8.new(0xff) }
86
+ ],
87
+ :VariableCollection => [
88
+ { :Data => LLU8.new(0xff) },
89
+ { :Data => LLU8.new(0xff) }
90
+ ]
91
+ })
92
+ assert_equal(0xff, packet.SomeBlock.Data.value)
93
+ assert_equal(0xff, packet.FixedCollection[0].Data.value)
94
+ assert_equal(0xff, packet.FixedCollection[1].Data.value)
95
+ assert_equal(0xff, packet.VariableCollection[0].Data.value)
96
+ assert_equal(0xff, packet.VariableCollection[1].Data.value)
97
+ end
98
+ end
99
+ end