swxruby 0.7
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/CHANGELOG +21 -0
- data/LICENSE +20 -0
- data/README +143 -0
- data/Rakefile +111 -0
- data/bin/swxruby +58 -0
- data/examples/standalone/standalone.fla +0 -0
- data/examples/standalone/standalone.rb +37 -0
- data/examples/standalone/standalone.swf +0 -0
- data/init.rb +22 -0
- data/install.rb +69 -0
- data/lib/swxruby.rb +6 -0
- data/lib/swxruby/bytecode_converter.rb +139 -0
- data/lib/swxruby/core_extensions.rb +41 -0
- data/lib/swxruby/helper_module.rb +72 -0
- data/lib/swxruby/rails_integration/render_decorator.rb +20 -0
- data/lib/swxruby/rails_integration/swx.yml +18 -0
- data/lib/swxruby/rails_integration/swx_controller.rb +6 -0
- data/lib/swxruby/services/arithmetic.rb +9 -0
- data/lib/swxruby/services/discovery_service.rb +3 -0
- data/lib/swxruby/services/hello_world.rb +7 -0
- data/lib/swxruby/services/simple.rb +5 -0
- data/lib/swxruby/services/test_data_types.rb +94 -0
- data/lib/swxruby/swx_assembler.rb +138 -0
- data/lib/swxruby/swx_gateway.rb +104 -0
- data/lib/swxruby/version.rb +9 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/swxruby/bytecode_converter_spec.rb +162 -0
- data/spec/swxruby/core_extensions_spec.rb +16 -0
- data/spec/swxruby/fixtures/number_one_no_debug_compression_4.swx +0 -0
- data/spec/swxruby/fixtures/number_one_no_debug_no_compression.swx +0 -0
- data/spec/swxruby/fixtures/number_one_no_debug_no_compression_arbitrary_allow_domain.swx +0 -0
- data/spec/swxruby/fixtures/number_one_with_debug_compression_4.swx +0 -0
- data/spec/swxruby/fixtures/number_one_with_debug_no_compression.swx +0 -0
- data/spec/swxruby/rails_integration/init_spec.rb +0 -0
- data/spec/swxruby/rails_integration/render_decorator_spec.rb +73 -0
- data/spec/swxruby/rails_integration/swx_controller_spec.rb +5 -0
- data/spec/swxruby/swx_assembler_spec.rb +60 -0
- data/spec/swxruby/swx_gateway_spec.rb +160 -0
- metadata +108 -0
data/lib/swxruby.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'core_extensions'
|
2
|
+
require 'date'
|
3
|
+
require 'helper_module'
|
4
|
+
|
5
|
+
class BytecodeConverter
|
6
|
+
include HelperMethods
|
7
|
+
class << self
|
8
|
+
NULL_TERMINATOR = '00'
|
9
|
+
|
10
|
+
def convert(data)
|
11
|
+
conversion_method = "#{data.class.to_s.downcase}_to_bytecode"
|
12
|
+
|
13
|
+
if respond_to?(conversion_method)
|
14
|
+
send(conversion_method, data)
|
15
|
+
else
|
16
|
+
# Convert the object to a hash of its instance variables
|
17
|
+
object_hash = data.instance_values
|
18
|
+
|
19
|
+
if object_hash.empty?
|
20
|
+
raise StandardError, "#{data.class} is an unhandled data type."
|
21
|
+
else
|
22
|
+
hash_to_bytecode(object_hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
# = Data Type Conversion Methods
|
29
|
+
def complex_data_structure_to_bytecode(data) #:nodoc#
|
30
|
+
bytecode = []
|
31
|
+
|
32
|
+
# Keeps track of bytecode when recursing into nested data structures
|
33
|
+
stack = []
|
34
|
+
|
35
|
+
# Add the bytecode to initialize the data structure
|
36
|
+
bytecode.push(data.is_a?(Array) ? ActionCodes::INIT_ARRAY : ActionCodes::INIT_OBJECT)
|
37
|
+
|
38
|
+
# Add the length of the data structure to the bytecode
|
39
|
+
bytecode.push integer_to_bytecode(data.length)
|
40
|
+
|
41
|
+
# Convert each element in the data structure to bytecode
|
42
|
+
data.each do |element|
|
43
|
+
|
44
|
+
# assume we're not iterating into a recursive data structure
|
45
|
+
complex_data_structure = false
|
46
|
+
|
47
|
+
# if we're converting a hash, then convert the hash's value
|
48
|
+
if data.is_a?(Hash)
|
49
|
+
value_bytecode = convert(element[1])
|
50
|
+
# if we just converted an array or a hash, set complex_data_structure to true
|
51
|
+
complex_data_structure = true if value_bytecode.concludes_with?(ActionCodes::INIT_OBJECT, ActionCodes::INIT_ARRAY)
|
52
|
+
# set the element local variable to the key of the hash
|
53
|
+
element = element[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
# element will always contain something (whether iterating over a hash or an array)
|
57
|
+
element_bytecode = convert(element)
|
58
|
+
# if we just converted an array or a hash, set complex_data_structure to true
|
59
|
+
complex_data_structure = true if element_bytecode.concludes_with?(ActionCodes::INIT_OBJECT, ActionCodes::INIT_ARRAY)
|
60
|
+
|
61
|
+
# Create a push of the current bytecode, if
|
62
|
+
# we recursed into a complex data structure
|
63
|
+
# or we're approaching the 65535 byte limit that can be stored in a single push.
|
64
|
+
if (complex_data_structure || calculate_bytecode_length(bytecode) > 65518)
|
65
|
+
|
66
|
+
# If we haven't written any bytecode into the local
|
67
|
+
# buffer yet (if it's empty), or all the data is already pushed, skip writing the push statement
|
68
|
+
bytecode.push generate_push_statement(bytecode) unless bytecode.empty? || bytecode.last.begins_with?('96')
|
69
|
+
|
70
|
+
# Store current instruction on the stack (SWF bytecode is stored in reverse, so we reverse it here)
|
71
|
+
stack.push bytecode.reverse.join
|
72
|
+
|
73
|
+
# Reset the bytecode
|
74
|
+
bytecode = []
|
75
|
+
end
|
76
|
+
|
77
|
+
# value_bytecode will be nil unless we converted a Hash
|
78
|
+
bytecode.push value_bytecode unless value_bytecode.nil?
|
79
|
+
|
80
|
+
bytecode.push element_bytecode
|
81
|
+
end
|
82
|
+
|
83
|
+
# If we haven't written any bytecode into the local
|
84
|
+
# buffer yet (if it's empty), or all the data is already pushed, skip writing the push statement
|
85
|
+
bytecode.push generate_push_statement(bytecode) unless bytecode.empty? || bytecode.last.begins_with?('96')
|
86
|
+
|
87
|
+
# Add the bytecode to the local stack variable (SWF bytecode is stored in reverse, so we reverse it here)
|
88
|
+
stack.push bytecode.reverse.join
|
89
|
+
|
90
|
+
# Join the stack array into a string and return it (SWF bytecode is stored in reverse, so we reverse it here)
|
91
|
+
stack.reverse.join
|
92
|
+
end
|
93
|
+
alias array_to_bytecode complex_data_structure_to_bytecode
|
94
|
+
alias hash_to_bytecode complex_data_structure_to_bytecode
|
95
|
+
|
96
|
+
def date_to_bytecode(date) #:nodoc#
|
97
|
+
# Format: 2006-09-14
|
98
|
+
string_to_bytecode(date.strftime('%Y-%m-%d'))
|
99
|
+
end
|
100
|
+
|
101
|
+
def datetime_to_bytecode(datetime) #:nodoc#
|
102
|
+
# Format: 2006-09-14 02:21:10
|
103
|
+
string_to_bytecode(datetime.strftime('%Y-%m-%d %I:%M:%S'))
|
104
|
+
end
|
105
|
+
|
106
|
+
def falseclass_to_bytecode(*args) #:nodoc#
|
107
|
+
DataTypeCodes::BOOLEAN + '00'
|
108
|
+
end
|
109
|
+
|
110
|
+
def float_to_bytecode(float) #:nodoc#
|
111
|
+
hex = []
|
112
|
+
[float].pack('E').each_byte { |byte| hex << '%02X' % byte }
|
113
|
+
# Aral did this in SWX PHP, so I'm doing it here
|
114
|
+
DataTypeCodes::FLOAT + (hex[4..-1] + hex[0..3]).join
|
115
|
+
end
|
116
|
+
|
117
|
+
def integer_to_bytecode(integer) #:nodoc#
|
118
|
+
DataTypeCodes::INTEGER + integer_to_hexadecimal(integer, 4)
|
119
|
+
end
|
120
|
+
alias bignum_to_bytecode integer_to_bytecode
|
121
|
+
alias fixnum_to_bytecode integer_to_bytecode
|
122
|
+
|
123
|
+
def nilclass_to_bytecode(*args) #:nodoc#
|
124
|
+
'02'
|
125
|
+
end
|
126
|
+
|
127
|
+
def string_to_bytecode(string) #:nodoc#
|
128
|
+
DataTypeCodes::STRING + string.unpack('H*').join.upcase + NULL_TERMINATOR
|
129
|
+
end
|
130
|
+
|
131
|
+
def symbol_to_bytecode(symbol)
|
132
|
+
string_to_bytecode(symbol.to_s)
|
133
|
+
end
|
134
|
+
|
135
|
+
def trueclass_to_bytecode(*args) #:nodoc#
|
136
|
+
DataTypeCodes::BOOLEAN + '01'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# = Core Extensions
|
2
|
+
class Array
|
3
|
+
def begins_with?(string)
|
4
|
+
self.join.begins_with?(string)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Object
|
9
|
+
# Taken from ActiveRecord
|
10
|
+
def instance_values
|
11
|
+
instance_variables.inject({}) do |values, name|
|
12
|
+
values[name[1..-1]] = instance_variable_get(name)
|
13
|
+
values
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class String
|
19
|
+
def begins_with?(string)
|
20
|
+
self[0..string.length-1] == string
|
21
|
+
end
|
22
|
+
|
23
|
+
def concludes_with?(*strings)
|
24
|
+
strings.any? { |string| self[-(string.length)..-1] == string }
|
25
|
+
end
|
26
|
+
|
27
|
+
def hex_to_ascii
|
28
|
+
hex = self.gsub(' ', '')
|
29
|
+
[hex].pack('H*')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Taken from Rails' Inflector module
|
33
|
+
def underscore
|
34
|
+
self.to_s.gsub(/::/, '/').
|
35
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
36
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
37
|
+
tr("-", "_").
|
38
|
+
downcase
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'core_extensions'
|
2
|
+
|
3
|
+
module DataTypeCodes
|
4
|
+
BOOLEAN = '05'
|
5
|
+
FLOAT = '06'
|
6
|
+
INTEGER = '07'
|
7
|
+
NULL = '02'
|
8
|
+
STRING = '00'
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActionCodes
|
12
|
+
DO_ACTION = '3F03'
|
13
|
+
END_SWF = '0000'
|
14
|
+
INIT_ARRAY = '42'
|
15
|
+
INIT_OBJECT = '43'
|
16
|
+
PUSH = '96LLLL'
|
17
|
+
SET_VARIABLE = '1D' # 00
|
18
|
+
SHOW_FRAME = '4000'
|
19
|
+
end
|
20
|
+
|
21
|
+
module HelperMethods
|
22
|
+
module ClassMethods
|
23
|
+
def calculate_bytecode_length(bytecode)
|
24
|
+
# Calculate bytecode length *without* counting the init array or init object action
|
25
|
+
bytecode.join.sub(/^(#{ActionCodes::INIT_ARRAY}|#{ActionCodes::INIT_OBJECT})/, '').length/2
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate_push_statement(bytecode)
|
29
|
+
unpushed_data = []
|
30
|
+
# Iterate over the bytecode array in reverse and add all of the unpushed
|
31
|
+
# data to 'unpushed_data'
|
32
|
+
bytecode.reverse_each do |bytecode_chunk|
|
33
|
+
if bytecode_chunk.begins_with?('96') || bytecode_chunk.begins_with?('1D') then break else unpushed_data << bytecode_chunk end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Since we iterated over the bytecode in reverse, unpushed_data is reversed, so
|
37
|
+
# reverse it again before passing it into #calculate_bytecode_length
|
38
|
+
bytecode_length = calculate_bytecode_length(unpushed_data.reverse)
|
39
|
+
|
40
|
+
# TODO: Replace with constant
|
41
|
+
'96' + integer_to_hexadecimal(bytecode_length, 2)
|
42
|
+
end
|
43
|
+
|
44
|
+
def integer_to_hexadecimal(integer, number_of_bytes=1)
|
45
|
+
make_little_endian("%0#{number_of_bytes*2}X" % integer)
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_little_endian(hex_string)
|
49
|
+
# split into an array of string pairs
|
50
|
+
# reverse the array and join back into a string
|
51
|
+
pad_string_to_byte_boundary(hex_string).scan(/../).reverse.join
|
52
|
+
end
|
53
|
+
|
54
|
+
def pad_string_to_byte_boundary(hex_string)
|
55
|
+
hex_string += '0' if hex_string.length % 2 == 1
|
56
|
+
hex_string
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a string with the length of the passed hex string in bytes
|
60
|
+
# padded to display in 'number_of_bytes' bytes.
|
61
|
+
def string_length_in_bytes_hex(string, number_of_bytes)
|
62
|
+
# Divide length in chars by 2 to get length in bytes
|
63
|
+
bytecode_length_in_hex = integer_to_hexadecimal(string.length/2, number_of_bytes)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
extend ClassMethods
|
68
|
+
|
69
|
+
def self.included(receiver)
|
70
|
+
receiver.extend(ClassMethods)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'swx_assembler'
|
2
|
+
require 'swx_gateway'
|
3
|
+
|
4
|
+
ActionController::Base.class_eval do
|
5
|
+
def render_with_swx(options = nil, &block)
|
6
|
+
if options.is_a?(Hash) && options.keys.include?(:swx)
|
7
|
+
swf_bytecode = SwxAssembler.write_swf(
|
8
|
+
options[:swx],
|
9
|
+
params[:debug],
|
10
|
+
SwxGateway.swx_config['compression_level'],
|
11
|
+
params[:url],
|
12
|
+
SwxGateway.swx_config['allow_domain']
|
13
|
+
)
|
14
|
+
send_data(swf_bytecode, :type => 'application/swf', :filename => 'data.swf')
|
15
|
+
else
|
16
|
+
render_without_swx(options, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
alias_method_chain :render, :swx
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Service Path: Put your service classes in this folder.
|
2
|
+
# Is relative to RAILS_ROOT
|
3
|
+
services_path: app/services
|
4
|
+
|
5
|
+
# Allow any domain to access the data SWFs from the SWX gateway?
|
6
|
+
# If you set this to false, you will only be able to call the
|
7
|
+
# SWX gateway from SWFs that reside on exactly the same domain
|
8
|
+
# (not even subdomains are allowed.)
|
9
|
+
#
|
10
|
+
# TODO: Allow user to specify a domain (or domains) to allow by
|
11
|
+
# providing URLs.
|
12
|
+
allow_domain: true
|
13
|
+
|
14
|
+
# How much to compress the returned SWF files. Values range from
|
15
|
+
# 0 (no compression) to 9 (maximum compression). The default
|
16
|
+
# compression level is 4. Higher levels of compression may result in
|
17
|
+
# smaller SWF files but will take longer to process.
|
18
|
+
compression_level: 4
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class TestTO
|
2
|
+
def initialize
|
3
|
+
@prop1 = 'A string'
|
4
|
+
@prop2 = 42
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
###
|
9
|
+
# Data type test service. Run the TestDataTypes.fla to run through all these tests using SWX RPC.
|
10
|
+
#
|
11
|
+
# @package default
|
12
|
+
# @author Aral Balkan
|
13
|
+
###
|
14
|
+
class TestDataTypes
|
15
|
+
###
|
16
|
+
# Returns the boolean true.
|
17
|
+
# @author Aral Balkan
|
18
|
+
###
|
19
|
+
def test_true
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
###
|
24
|
+
# Returns the boolean false.
|
25
|
+
# @author Aral Balkan
|
26
|
+
###
|
27
|
+
def test_false
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
###
|
32
|
+
# Returns the array ['It', 'works']
|
33
|
+
# @author Aral Balkan
|
34
|
+
###
|
35
|
+
def test_array
|
36
|
+
%w(It works)
|
37
|
+
end
|
38
|
+
|
39
|
+
###
|
40
|
+
# Returns the nested array ['It', ['also'], 'works]
|
41
|
+
# @author Aral Balkan
|
42
|
+
###
|
43
|
+
def test_nested_array
|
44
|
+
['It', ['also'], 'works']
|
45
|
+
end
|
46
|
+
|
47
|
+
###
|
48
|
+
# Returns the integer 42.
|
49
|
+
# @author Aral Balkan
|
50
|
+
###
|
51
|
+
def test_integer
|
52
|
+
42
|
53
|
+
end
|
54
|
+
|
55
|
+
###
|
56
|
+
# Returns the float 42.12345.
|
57
|
+
# @author Aral Balkan
|
58
|
+
###
|
59
|
+
def test_float
|
60
|
+
42.12345
|
61
|
+
end
|
62
|
+
|
63
|
+
###
|
64
|
+
# Returns the string "It works!"
|
65
|
+
# @author Aral Balkan
|
66
|
+
###
|
67
|
+
def test_string
|
68
|
+
"It works!"
|
69
|
+
end
|
70
|
+
|
71
|
+
###
|
72
|
+
# Returns the associative array ['it' => 'works', 'number' => 42]
|
73
|
+
# @author Aral Balkan
|
74
|
+
###
|
75
|
+
def test_associative_array
|
76
|
+
{'it' => 'works', 'number' => 42}
|
77
|
+
end
|
78
|
+
|
79
|
+
###
|
80
|
+
# Returns an instance of the TestTO class with properties prop1: "A string" and prop2: 42.
|
81
|
+
# @author Aral Balkan
|
82
|
+
###
|
83
|
+
def test_object
|
84
|
+
TestTO.new
|
85
|
+
end
|
86
|
+
|
87
|
+
###
|
88
|
+
# Returns null.
|
89
|
+
# @author Aral Balkan
|
90
|
+
###
|
91
|
+
def test_null
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'bytecode_converter'
|
2
|
+
require 'helper_module'
|
3
|
+
require 'uri/common.rb'
|
4
|
+
require 'zlib'
|
5
|
+
|
6
|
+
class SwxAssembler
|
7
|
+
include HelperMethods
|
8
|
+
|
9
|
+
# Header - FCS (uncompressed), version Flash 6
|
10
|
+
UNCOMPRESSED_SWF = '46'
|
11
|
+
COMPRESSED_SWF = '43'
|
12
|
+
HEADER = '575306LLLLLLLL300A00A0000101004302FFFFFF'
|
13
|
+
|
14
|
+
# Misc
|
15
|
+
NULL_TERMINATOR = '00'
|
16
|
+
|
17
|
+
# Allow domain (*)
|
18
|
+
ALLOW_DOMAIN = '960900005F706172656E74001C960600005F75726C004E960D0007010000000053797374656D001C960A00007365637572697479004E960D0000616C6C6F77446F6D61696E005217'
|
19
|
+
SYSTEM_ALLOW_DOMAIN = '07010000000053797374656D001C960A00007365637572697479004E960D0000616C6C6F77446F6D61696E005217'
|
20
|
+
|
21
|
+
# Debug SWX bytecode. Creates a local connection to the SWX Debugger front-end.)
|
22
|
+
DEBUG_START = '883C000700726573756C74006C63004C6F63616C436F6E6E656374696F6E005F737778446562756767657200636F6E6E6563740064656275670073656E6400'
|
23
|
+
DEBUG_END = '960D0008010600000000000000000802403C9609000803070100000008011C9602000804521796020008001C960500070100000042960B0008050803070300000008011C96020008065217'
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def allow_domain_bytecode(allow_domain_url = '')
|
27
|
+
if (allow_domain_url.nil? || allow_domain_url.empty?)
|
28
|
+
# No URL passed -- possibly called by legacy code, use the old _parent._url version.
|
29
|
+
# error_log('[SWX] INFO: No URL passed from client. Defaulting to old behavior. You must call System.security.allowDomain on the dataHolder for cross domain data loading to work.');
|
30
|
+
ALLOW_DOMAIN
|
31
|
+
else
|
32
|
+
# Firefox/Flash (at least, and tested only on a Mac), sends
|
33
|
+
# file:/// (three slashses) in the URI and that fails the validation
|
34
|
+
# so replacing that with two slashes instead.
|
35
|
+
allow_domain_url.gsub!('///', '//')
|
36
|
+
# URL is passed, write that into the returned code
|
37
|
+
allow_domain_bytecode = BytecodeConverter.convert(URI.unescape(allow_domain_url))
|
38
|
+
|
39
|
+
# The -13 is to accomodate the other elements being pushed to the
|
40
|
+
# stack in the hard-coded part of the bytecode.
|
41
|
+
allow_domain_bytecode_length_dec = allow_domain_bytecode.length/2 + 13
|
42
|
+
|
43
|
+
allow_domain_bytecode_length = integer_to_hexadecimal(allow_domain_bytecode_length_dec, 2);
|
44
|
+
allow_domain_bytecode = '96' + allow_domain_bytecode_length + allow_domain_bytecode + SYSTEM_ALLOW_DOMAIN;
|
45
|
+
allow_domain_bytecode
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def compress_swx_file(swx_file, compression_level)
|
50
|
+
# The first eight bytes of a compressed SWF file are left uncompressed
|
51
|
+
swx_file.slice!(0...8) + Zlib::Deflate.deflate(swx_file, compression_level)
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_data_bytecode(data)
|
55
|
+
data_bytecode = []
|
56
|
+
|
57
|
+
# Add a flag to the beginning of the bytecode that tells Flash we're setting a variable (result)
|
58
|
+
data_bytecode.push ActionCodes::SET_VARIABLE
|
59
|
+
|
60
|
+
# Convert the data (payload) to bytecode
|
61
|
+
data_bytecode.push BytecodeConverter.convert(data)
|
62
|
+
|
63
|
+
# Generate a push tag if the data was not an Array or a Hash
|
64
|
+
data_bytecode.push generate_push_statement(data_bytecode) unless data.is_a?(Array) || data.is_a?(Hash)
|
65
|
+
|
66
|
+
# Add the 'result' variable name -- either
|
67
|
+
# using the constant table if in debug mode
|
68
|
+
# or as a regular string otherwise
|
69
|
+
if @debug
|
70
|
+
data_bytecode.push '9602000800'
|
71
|
+
else
|
72
|
+
data_bytecode.push '96080000726573756C7400'
|
73
|
+
end
|
74
|
+
|
75
|
+
# (SWF bytecode is stored in reverse, so we reverse it here)
|
76
|
+
data_bytecode.reverse.join
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_swx_bytecode(data)
|
80
|
+
# Create the DoAction tag
|
81
|
+
do_action_block = []
|
82
|
+
|
83
|
+
# Wrap the data bytecode in debug flags if debugging is turned on
|
84
|
+
do_action_block.push DEBUG_START if @debug
|
85
|
+
|
86
|
+
# Generate bytecode for the data (payload)
|
87
|
+
do_action_block.push generate_data_bytecode(data)
|
88
|
+
|
89
|
+
# Allow domain? If so add allow domain statement to the SWF
|
90
|
+
if (@allow_domain)
|
91
|
+
do_action_block.push allow_domain_bytecode(@allow_domain_url)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Wrap the data bytecode in debug flags if debugging is turned on
|
95
|
+
do_action_block.push DEBUG_END if @debug
|
96
|
+
|
97
|
+
# Calculate the size of the do_action block
|
98
|
+
do_action_block_size_in_bytes = string_length_in_bytes_hex(do_action_block.join, 4)
|
99
|
+
# Add the appropriate flags to the do_action block and concat the finished do_action block into a string
|
100
|
+
do_action_block_string = ActionCodes::DO_ACTION + do_action_block_size_in_bytes + do_action_block.join
|
101
|
+
|
102
|
+
# Create the rest of the SWF
|
103
|
+
header_type = if @compression_level > 0 then COMPRESSED_SWF else UNCOMPRESSED_SWF end
|
104
|
+
|
105
|
+
swf = header_type + HEADER + do_action_block_string + ActionCodes::SHOW_FRAME + ActionCodes::END_SWF
|
106
|
+
swf_size_in_bytes = string_length_in_bytes_hex(swf, 4)
|
107
|
+
|
108
|
+
# Replace length placeholder (from HEADER constant) with actual bytecode length
|
109
|
+
swf.sub('LLLLLLLL', swf_size_in_bytes)
|
110
|
+
end
|
111
|
+
|
112
|
+
def write_swf(data, debug=false, compression_level=4, allow_domain_url='', allow_domain=true)
|
113
|
+
# Set up SwfAssembler state
|
114
|
+
@debug = debug
|
115
|
+
@compression_level = compression_level
|
116
|
+
@allow_domain_url = allow_domain_url
|
117
|
+
@allow_domain = allow_domain
|
118
|
+
|
119
|
+
swx_bytecode = generate_swx_bytecode(data)
|
120
|
+
|
121
|
+
# Convert the bytecode string to ASCII file format
|
122
|
+
swx_file = swx_bytecode.hex_to_ascii
|
123
|
+
|
124
|
+
# Compress the file if compression is turned on
|
125
|
+
swx_file = compress_swx_file(swx_file, compression_level) if compression_level > 0
|
126
|
+
|
127
|
+
# ====================================
|
128
|
+
# = TODO: Remove this before release =
|
129
|
+
# ====================================
|
130
|
+
# Write the file (for manual 'loadMovie' testing in Flash)
|
131
|
+
# File.open('/Users/Jed/Development/Libraries/rSWX/testing/flash/rswx_data.swx', 'w+') do |file|
|
132
|
+
# file << swx_file
|
133
|
+
# end
|
134
|
+
|
135
|
+
swx_file
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|