swf_ruby 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  pkg/*
2
- *.gem
2
+ *.gem
3
+ *.swp
data/README.md CHANGED
@@ -9,7 +9,7 @@ SwfRuby is a utilities to dump or manipulate a SWF with Ruby.
9
9
  * Used from 'swf_dump' command.
10
10
  * SwfRuby::SwfTamperer
11
11
  * Manipulating(replagcing) resources in SWF.
12
- * Used from 'swf_jpeg_replace' and 'swf_lossless_replace' command.
12
+ * Used from 'swf_jpeg_replace', 'swf_lossless_replace', 'swf_as_var_replace' and 'swf_sprite_replace' command.
13
13
  * compatible on ruby-1.8.7 and ruby-1.9.2.
14
14
 
15
15
  Dependencies
@@ -57,6 +57,18 @@ Replacing GIF/PNG in SWF
57
57
  $ swf_lossless_replace samples/sample.swf 120 samples/icon.gif > samples/sample3.swf
58
58
  # <120> is offset to DefineBitsLossless2 resource getting by 'swf_dump'.
59
59
 
60
+ Replacing ActionScript Variable in SWF
61
+ --------------------------------------
62
+
63
+ $ swf_as_var_replace foo.swf bar piyo > foo2.swf
64
+ # <bar> is variable name. <piyo> is new value to the variable.
65
+
66
+ Replacing Sprite(internal movieclip) in SWF
67
+ -------------------------------------------
68
+
69
+ $ swf_sprite_replace foo.swf bar piyo.swf > foo2.swf
70
+ # <bar> is instance variable name of movieclip. <piyo.swf> is new movieclip file to replace.
71
+
60
72
  Thanks
61
73
  ======
62
74
 
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
3
+ # SWF中のAS変数の値(foo = var; な箇所に限る)を差し替える.
4
+ require 'swf_ruby'
5
+
6
+ if ARGV.size != 3
7
+ print "Specify target swf path, AS var name, and new value for arguments.\n"
8
+ exit
9
+ end
10
+
11
+ swf_dumper = SwfRuby::SwfDumper.new
12
+ swf_dumper.open(ARGV[0])
13
+
14
+ repl_targets = SwfRuby::AsVarReplaceTarget.build_by_var_name(swf_dumper, ARGV[1]).collect do |t|
15
+ t.str = ARGV[2].dup
16
+ t
17
+ end
18
+
19
+ st = SwfRuby::SwfTamperer.new
20
+
21
+ print st.replace(swf_dumper.swf.dup, repl_targets)
data/bin/swf_dump CHANGED
@@ -12,14 +12,24 @@ end
12
12
  swf = SwfRuby::SwfDumper.new
13
13
  swf.open(ARGV[0])
14
14
  swf.tags.each_with_index do |tag, i|
15
- print "#{SwfRuby::Swf::TAG_TYPE[tag.code]}, offset: #{swf.tags_addresses[i]}, length: #{tag.length}\n"
15
+ character_id = tag.character_id ? "character_id: #{tag.character_id}, " : ""
16
+ refer_character_id = tag.refer_character_id ? "refer_character_id: #{tag.refer_character_id}, " : ""
17
+ refer_character_inst_name = tag.refer_character_inst_name ? "refer_character_inst_name: #{tag.refer_character_inst_name}, " : ""
18
+ refer_bitmap_id = ""
19
+ tag.refer_bitmap_offsets_to_ids.each do |offset, bitmap_id|
20
+ refer_bitmap_id += "bitmap_id: #{bitmap_id}(offset: #{offset}), "
21
+ end if tag.refer_bitmap_offsets_to_ids
22
+ print "#{SwfRuby::Swf::TAG_TYPE[tag.code]}, offset: #{swf.tags_addresses[i]}, #{character_id}#{refer_character_id}#{refer_character_inst_name}#{refer_bitmap_id}length: #{tag.length}\n"
16
23
  if tag.code == 39
17
24
  # DefineSprite
18
25
  sd = SwfRuby::SpriteDumper.new
19
26
  sd.dump(tag)
20
27
  print " Sprite ID: #{sd.sprite_id}, Frame Count: #{sd.frame_count}\n"
21
28
  sd.tags.each_with_index do |tag2, k|
22
- print " #{SwfRuby::Swf::TAG_TYPE[tag2.code]}, offset: #{sd.tags_addresses[k]}, length: #{tag2.length}\n"
29
+ character_id = tag2.character_id ? "character_id: #{tag2.character_id}, " : ""
30
+ refer_character_id = tag2.refer_character_id ? "refer_character_id: #{tag2.refer_character_id}, " : ""
31
+ refer_character_inst_name = tag2.refer_character_inst_name ? "refer_character_inst_name: #{tag2.refer_character_inst_name}, " : ""
32
+ print " #{SwfRuby::Swf::TAG_TYPE[tag2.code]}, offset: #{sd.tags_addresses[k]}, #{character_id}#{refer_character_id}#{refer_character_inst_name}length: #{tag2.length}\n"
23
33
  if tag2.code == 12
24
34
  # DoAction
25
35
  dad = SwfRuby::DoActionDumper.new
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
3
+ # SWF中の指定したインスタンス変数名で設置されているDefineSpriteを入れ替える.
4
+ require 'swf_ruby'
5
+
6
+ if ARGV.size != 3
7
+ print "Specify target swf path, sprite instance variable name, and swf path to embed for arguments.\n"
8
+ exit
9
+ end
10
+
11
+ swf_dumper = SwfRuby::SwfDumper.new
12
+ swf_dumper.open(ARGV[0])
13
+ sprite_swf = File.open(ARGV[2], "rb").read
14
+
15
+ repl_targets = SwfRuby::SpriteReplaceTarget.build_list_by_instance_var_names(swf_dumper, { ARGV[1] => sprite_swf })
16
+
17
+ st = SwfRuby::SwfTamperer.new
18
+
19
+ print st.replace(swf_dumper.swf.dup, repl_targets)
@@ -23,16 +23,162 @@ module SwfRuby
23
23
  end
24
24
  end
25
25
 
26
+ class SpriteReplaceTarget < ReplaceTarget
27
+ attr_accessor :swf
28
+ attr_accessor :frame_count
29
+ attr_accessor :define_tags
30
+ attr_accessor :control_tags
31
+ attr_accessor :idmap
32
+ attr_accessor :target_define_tags_string
33
+ attr_accessor :target_control_tags_string
34
+
35
+ def initialize(offset, swf)
36
+ @offset = offset
37
+ @swf = swf
38
+ @target_swf_dumper = SwfDumper.new.dump(@swf)
39
+ @frame_count = @target_swf_dumper.header.frame_count
40
+ @define_tags = @target_swf_dumper.tags.select { |t| t.define_tag? }
41
+ @control_tags = @target_swf_dumper.tags - @define_tags
42
+ @idmap = {}
43
+ end
44
+
45
+ def self.build_list_by_instance_var_names(swf_dumper, var_name_to_swf)
46
+ from_character_id = (swf_dumper.tags.collect { |t| t.define_tag? ? t.character_id : nil }).compact.max + 1
47
+ repl_targets = []
48
+ var_name_to_swf.each do |var_name, swf|
49
+ repl_target, from_character_id = SwfRuby::SpriteReplaceTarget.build_by_instance_var_name(swf_dumper, var_name, swf, from_character_id)
50
+ repl_targets << repl_target
51
+ end
52
+ repl_targets
53
+ end
54
+
55
+ # 指定したインスタンス変数名に対するSpriteReplaceTargetを生成する
56
+ def self.build_by_instance_var_name(swf_dumper, var_name, swf, from_character_id = nil)
57
+ from_character_id ||= (swf_dumper.tags.collect { |t| t.define_tag? ? t.character_id : nil }).compact.max + 1
58
+ refer_character_id = nil
59
+ sprite_indices = {}
60
+ swf_dumper.tags.each_with_index do |t,i|
61
+ if t.character_id
62
+ sprite_indices[t.character_id] = i
63
+ end
64
+ if var_name == t.refer_character_inst_name
65
+ refer_character_id = t.refer_character_id
66
+ break
67
+ end
68
+ end
69
+ raise ReplaceTargetError unless refer_character_id
70
+ offset = swf_dumper.tags_addresses[sprite_indices[refer_character_id]]
71
+ srt = SpriteReplaceTarget.new(offset, swf)
72
+ srt.target_define_tags_string, from_character_id = srt.build_define_tags_string(from_character_id)
73
+ srt.target_control_tags_string = srt.build_control_tags_string
74
+ [srt, from_character_id]
75
+ end
76
+
77
+ # 置換するSWFからCharacterIdを付け替えながらDefineタグを抽出する.
78
+ # 対象のSWFにBitmapIDの参照が含まれる場合、これも合わせて付け替える.
79
+ # 同時に、CharacterIdの対応付けマップを作成する.
80
+ def build_define_tags_string(from_character_id)
81
+ str = ""
82
+ @define_tags.each do |t|
83
+ if t.character_id
84
+ from_character_id += 1
85
+ @idmap[t.character_id] = from_character_id
86
+ str << t.rawdata_with_define_character_id(@idmap, @idmap[t.character_id])
87
+ end
88
+ end
89
+ [str, from_character_id+1]
90
+ end
91
+
92
+ # DefineSpriteに埋め込むためのControl tagsのみを抽出する.
93
+ # 参照先のcharacter_idを変更する必要がある場合は付け替える.
94
+ def build_control_tags_string
95
+ str = ""
96
+ valid_control_tag_codes = [0, 1, 4, 5, 12, 18, 19, 26, 28, 43, 45, 70, 72]
97
+ @control_tags.each do |t|
98
+ next unless valid_control_tag_codes.include? t.code
99
+ if @idmap[t.refer_character_id]
100
+ str << t.rawdata_with_refer_character_id(@idmap[t.refer_character_id])
101
+ else
102
+ str << t.rawdata
103
+ end
104
+ end
105
+ str
106
+ end
107
+ end
108
+
26
109
  class AsVarReplaceTarget < ReplaceTarget
27
110
  attr_accessor :do_action_offset
28
111
  attr_accessor :str
29
- attr_accessor :parent_sprite_offsets
112
+ attr_accessor :parent_sprite_offset
30
113
 
31
- def initialize(action_push_offset, do_action_offset, str, parent_sprite_offsets = [])
114
+ def initialize(action_push_offset, do_action_offset, str, parent_sprite_offset = nil)
32
115
  @offset = action_push_offset
33
116
  @do_action_offset = do_action_offset
34
117
  @str = str
35
- @parent_sprite_offsets = parent_sprite_offsets
118
+ @parent_sprite_offset = parent_sprite_offset
119
+ end
120
+
121
+ # 指定したAS変数名に対するAsVarReplaceTargetのリストを生成する
122
+ def self.build_by_var_name(swf_dumper, var_name)
123
+ as_var_replace_targets = []
124
+ swf_dumper.tags.each_with_index do |t, i|
125
+ if t.code == 39
126
+ # DefineSprite
127
+ sd = SpriteDumper.new
128
+ sd.dump(t)
129
+ sd.tags.each_with_index do |u, j|
130
+ if u.code == 12
131
+ # DoAction in DefineSprite
132
+ as_var_replace_targets += AsVarReplaceTarget.generate_as_var_replace_target_by_do_action(var_name, swf_dumper, j, sd, swf_dumper.tags_addresses[i])
133
+ end
134
+ end
135
+ end
136
+ if t.code == 12
137
+ # DoAction
138
+ as_var_replace_targets += AsVarReplaceTarget.generate_as_var_replace_target_by_do_action(var_name, swf_dumper, i)
139
+ end
140
+ end
141
+ as_var_replace_targets
142
+ end
143
+
144
+ # 指定したインデックス(SWFまたはSpriteの先頭からカウント)にあるDoAction以下を走査し、
145
+ # 指定したAS変数名の代入部分を発見し、AsVarReplaceTargetのリストを生成する.
146
+ def self.generate_as_var_replace_target_by_do_action(var_name, swf_dumper, do_action_index, sprite_dumper = nil, parent_sprite_offset = nil)
147
+ as_var_replace_targets = []
148
+ action_records = []
149
+ do_action_offset = 0
150
+
151
+ dad = DoActionDumper.new
152
+ if sprite_dumper
153
+ do_action_offset = parent_sprite_offset + sprite_dumper.tags_addresses[do_action_index]
154
+ dad.dump(swf_dumper.swf[do_action_offset, sprite_dumper.tags[do_action_index].length])
155
+ else
156
+ do_action_offset = swf_dumper.tags_addresses[do_action_index]
157
+ dad.dump(swf_dumper.swf[do_action_offset, swf_dumper.tags[do_action_index].length])
158
+ end
159
+ dad.actions.each_with_index do |ar, i|
160
+ # ActionPush, ActionPush, SetVariableの並びを検出したら変数名をチェック.
161
+ action_records.shift if action_records.length > 2
162
+ action_records << ar
163
+ if ar.code == 29 and
164
+ action_records[0] and
165
+ action_records[0].code == 150 and
166
+ action_records[0].data.delete("\0") == var_name and
167
+ action_records[1].code == 150
168
+ # 対象の代入式を発見.
169
+ ap = Swf::ActionPush.new(action_records[1])
170
+ as_var_replace_targets << AsVarReplaceTarget.new(
171
+ do_action_offset + dad.actions_addresses[i] - action_records[1].length,
172
+ do_action_offset,
173
+ "",
174
+ parent_sprite_offset
175
+ )
176
+ end
177
+ end
178
+ as_var_replace_targets
36
179
  end
37
180
  end
181
+
182
+ # 置換対象指定エラー.
183
+ class ReplaceTargetError < StandardError; end
38
184
  end
@@ -0,0 +1,47 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Cxformwithalpha
6
+ attr_accessor :has_add_terms
7
+ attr_accessor :has_mult_terms
8
+ attr_accessor :nbits
9
+ attr_accessor :red_mult_term
10
+ attr_accessor :green_mult_term
11
+ attr_accessor :blue_mult_term
12
+ attr_accessor :alpha_mult_term
13
+ attr_accessor :red_add_term
14
+ attr_accessor :green_add_term
15
+ attr_accessor :blue_add_term
16
+ attr_accessor :alpha_add_term
17
+ attr_reader :length # byte_aligned
18
+
19
+ def initialize(bytearray)
20
+ bits_str = bytearray.unpack("B*").first
21
+ @has_add_terms = bits_str[0, 1].to_i(2)
22
+ @has_mult_terms = bits_str[1, 1].to_i(2)
23
+ @nbits = bits_str[2, 4].to_i(2)
24
+ offset = 6
25
+ if @has_mult_terms == 1
26
+ # TODO to get as signed bit values!!
27
+ @red_mult_term = bits_str[offset, @nbits].to_i(2)
28
+ @green_mult_term = bits_str[offset+@nbits, @nbits].to_i(2)
29
+ @blue_mult_term = bits_str[offset+2*@nbits, @nbits].to_i(2)
30
+ @alpha_mult_term = bits_str[offset+3*@nbits, @nbits].to_i(2)
31
+ offset += 4 * @nbits
32
+ end
33
+ if @has_add_terms == 1
34
+ # TODO to get as signed bit values!!
35
+ @red_add_term = bits_str[offset, @nbits].to_i(2)
36
+ @green_add_term = bits_str[offset+@nbits, @nbits].to_i(2)
37
+ @blue_add_term = bits_str[offset+2*@nbits, @nbits].to_i(2)
38
+ @alpha_add_term = bits_str[offset+3*@nbits, @nbits].to_i(2)
39
+ offset += 4 * @nbits
40
+ end
41
+ @length = offset >> 3
42
+ @length += 1 if offset & 7 > 0
43
+ self
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,51 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Fillstyle
6
+ attr_accessor :shape_version
7
+ attr_accessor :fill_style_type
8
+ attr_accessor :color
9
+ attr_accessor :gradient_matrix
10
+ attr_accessor :gradient
11
+ attr_accessor :bitmap_id
12
+ attr_accessor :bitmap_id_offset
13
+ attr_accessor :bitmap_matrix
14
+ attr_reader :length
15
+
16
+ def initialize(bytearray, shape_version)
17
+ @shape_version = shape_version
18
+ @fill_style_type = bytearray[0, 1].unpack("C").first
19
+ offset = 1
20
+ if @fill_style_type == 0x00
21
+ if @shape_version >= 3
22
+ @color = SwfRuby::Swf::Rgba.new(bytearray[offset..-1])
23
+ else
24
+ @color = SwfRuby::Swf::Rgb.new(bytearray[offset..-1])
25
+ end
26
+ offset += @color.length
27
+ end
28
+ if @fill_style_type == 0x10 or @fill_style_type == 0x12 or @fill_style_type == 0x13
29
+ @gradient_matrix = SwfRuby::Swf::Matrix.new(bytearray[offset..-1])
30
+ offset += @gradient_matrix.length
31
+ end
32
+ if @fill_style_type == 0x10 or @fill_style_type == 0x12
33
+ @gradient = SwfRuby::Swf::Gradient.new(bytearray[offset..-1], @shape_version)
34
+ offset += @gradient.length
35
+ elsif @fill_style_type == 0x13
36
+ @gradient = SwfRuby::Swf::Focalgradient.new(bytearray[offset..-1], @shape_version)
37
+ offset += @gradient.length
38
+ end
39
+ if @fill_style_type == 0x40 or @fill_style_type == 0x41 or @fill_style_type == 0x42 or @fill_style_type == 0x43
40
+ @bitmap_id = bytearray[offset, 2].unpack("v").first
41
+ @bitmap_id_offset = offset
42
+ offset += 2
43
+ @bitmap_matrix = SwfRuby::Swf::Matrix.new(bytearray[offset..-1])
44
+ offset += @bitmap_matrix.length
45
+ end
46
+ @length = offset
47
+ self
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Focalgradient
6
+ attr_accessor :shape_version
7
+ attr_accessor :spread_mode
8
+ attr_accessor :interpolation_mode
9
+ attr_accessor :num_gradients
10
+ attr_accessor :gradient_records
11
+ attr_accessor :focal_point
12
+ attr_reader :length
13
+
14
+ def initialize(bytearray, shape_version)
15
+ @shape_version = shape_version
16
+ bits_str = bytearray[0, 1].unpack("B").first
17
+ @spread_mode = bits_str[0, 2].to_i(2)
18
+ @interpolation_mode = bits_str[2, 2].to_i(2)
19
+ @num_gradients = bits_str[4, 4].to_i(2)
20
+ offset = 1
21
+ @gradient_records = []
22
+ @num_gradients.times do
23
+ gradient_record = SwfRuby::Swf::Gradrecord.new(bytearray[offset..-1], @shape_version)
24
+ @gradient_records << gradient_record
25
+ offset += gradient_record.length
26
+ end
27
+ # TODO focal_point to be 8bit.8bit fixed_point number.
28
+ @focal_point = bytearray[offset, 2]
29
+ offset += 2
30
+ @length = offset
31
+ self
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Gradient
6
+ attr_accessor :shape_version
7
+ attr_accessor :spread_mode
8
+ attr_accessor :interpolation_mode
9
+ attr_accessor :num_gradients
10
+ attr_accessor :gradient_records
11
+ attr_reader :length
12
+
13
+ def initialize(bytearray, shape_version)
14
+ @shape_version = shape_version
15
+ bits_str = bytearray[0, 1].unpack("B").first
16
+ @spread_mode = bits_str[0, 2].to_i(2)
17
+ @interpolation_mode = bits_str[2, 2].to_i(2)
18
+ @num_gradients = bits_str[4, 4].to_i(2)
19
+ offset = 1
20
+ @gradient_records = []
21
+ @num_gradients.times do
22
+ gradient_record = SwfRuby::Swf::Gradrecord.new(bytearray[offset..-1], @shape_version)
23
+ @gradient_records << gradient_record
24
+ offset += gradient_record.length
25
+ end
26
+ @length = offset
27
+ self
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Gradrecord
6
+ attr_accessor :shape_version
7
+ attr_accessor :ratio
8
+ attr_accessor :color
9
+ attr_reader :length
10
+
11
+ def initialize(bytearray, shape_version)
12
+ @shape_version = shape_version
13
+ @ratio = bytearray[0, 1].unpack("C").first
14
+ offset = 1
15
+ if @shape_version >= 3
16
+ @color = SwfRuby::Swf::Rgba.new(bytearray[offset..-1])
17
+ else
18
+ @color = SwfRuby::Swf::Rgb.new(bytearray[offset..-1])
19
+ end
20
+ offset += @color.length
21
+ @length = offset
22
+ self
23
+ end
24
+ end
25
+ end
26
+ end
@@ -9,30 +9,39 @@ module SwfRuby
9
9
  attr_accessor :frame_size
10
10
  attr_accessor :frame_rate
11
11
  attr_accessor :frame_count
12
- attr_reader :length
12
+ attr_accessor :length
13
13
 
14
- def initialize(swf)
14
+ def self.parse(swf)
15
+ header = Header.new
15
16
  # check if compressed
16
17
  if swf[1].chr == "W" and swf[2].chr == "S"
17
18
  if swf[0].chr == "F"
18
- @compressed = false
19
+ header.compressed = false
19
20
  elsif swf[0].chr == "C"
20
- @compressed = true
21
+ header.compressed = true
21
22
  end
22
23
  end
23
24
  # version
24
- @version = swf[3].chr.unpack("C").first
25
+ header.version = swf[3].chr.unpack("C").first
25
26
  # file size
26
- @file_length = swf[4, 4].unpack("V").first
27
+ header.file_length = swf[4, 4].unpack("V").first
28
+
29
+ # Zlib inflate
30
+ if header.compressed
31
+ swf[8..-1] = Zlib::Inflate.inflate(swf[8..-1])
32
+ end
33
+
27
34
  # frame size
28
- @frame_size = Rectangle.new(swf[8..-1])
35
+ header.frame_size = Rectangle.new(swf[8..-1])
29
36
  # frame rate
30
- @frame_rate = (swf[8+@frame_size.length].chr.unpack("C").first).to_f / 0x100
31
- @frame_rate += swf[9+@frame_size.length].chr.unpack("C").first
37
+ header.frame_rate = (swf[8+header.frame_size.length].chr.unpack("C").first).to_f / 0x100
38
+ header.frame_rate += swf[9+header.frame_size.length].chr.unpack("C").first
32
39
  # frame count
33
- @frame_count = swf[10+@frame_size.length, 2].unpack("v").first
40
+ header.frame_count = swf[10+header.frame_size.length, 2].unpack("v").first
34
41
  # header length
35
- @length = 12+@frame_size.length
42
+ header.length = 12+header.frame_size.length
43
+
44
+ [header, swf]
36
45
  end
37
46
  end
38
47
  end
@@ -0,0 +1,50 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Matrix
6
+ attr_accessor :has_scale
7
+ attr_accessor :n_scale_bits
8
+ attr_accessor :scale_x
9
+ attr_accessor :scale_y
10
+ attr_accessor :has_rotate
11
+ attr_accessor :n_rotate_bits
12
+ attr_accessor :rotate_skew_0
13
+ attr_accessor :rotate_skew_1
14
+ attr_accessor :n_translate_bits
15
+ attr_accessor :translate_x
16
+ attr_accessor :translate_y
17
+ attr_reader :length # byte_aligned
18
+
19
+ def initialize(bytearray)
20
+ bits_str = bytearray.unpack("B*").first
21
+ @has_scale = bits_str[0, 1].to_i(2)
22
+ offset = 1
23
+ if @has_scale == 1
24
+ @n_scale_bits = bits_str[offset, 5].to_i(2)
25
+ offset += 5
26
+ @scale_x = bits_str[offset, self.n_scale_bits].to_i(2)
27
+ @scale_y = bits_str[offset+self.n_scale_bits, self.n_scale_bits].to_i(2)
28
+ offset += 2 * self.n_scale_bits
29
+ end
30
+ @has_rotate = bits_str[offset, 1].to_i(2)
31
+ offset += 1
32
+ if @has_rotate == 1
33
+ @n_rotate_bits = bits_str[offset, 5].to_i(2)
34
+ offset += 5
35
+ @rotate_skew_0 = bits_str[offset, self.n_rotate_bits].to_i(2)
36
+ @rotate_skew_1 = bits_str[offset+self.n_rotate_bits, self.n_rotate_bits].to_i(2)
37
+ offset += 2 * self.n_rotate_bits
38
+ end
39
+ @n_translate_bits = bits_str[offset, 5].to_i(2)
40
+ offset += 5
41
+ @translate_x = bits_str[offset, self.n_translate_bits].to_i(2)
42
+ @translate_y = bits_str[offset+self.n_translate_bits, self.n_translate_bits].to_i(2)
43
+ offset += 2 * self.n_translate_bits
44
+ @length = offset >> 3
45
+ @length += 1 if offset & 7 > 0
46
+ self
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Rgb
6
+
7
+ attr_accessor :red
8
+ attr_accessor :green
9
+ attr_accessor :blue
10
+ attr_reader :length
11
+
12
+ def initialize(bytearray)
13
+ @red = bytearray[0, 1].unpack("C").first
14
+ @green = bytearray[1, 1].unpack("C").first
15
+ @blue = bytearray[2, 1].unpack("C").first
16
+ @length = 3
17
+ self
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Rgba
6
+ attr_accessor :red
7
+ attr_accessor :green
8
+ attr_accessor :blue
9
+ attr_accessor :alpha
10
+ attr_reader :length
11
+
12
+ def initialize(bytearray)
13
+ @red = bytearray[0, 1].unpack("C").first
14
+ @green = bytearray[1, 1].unpack("C").first
15
+ @blue = bytearray[2, 1].unpack("C").first
16
+ @alpha = bytearray[3, 1].unpack("C").first
17
+ @length = 4
18
+ self
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class Shapewithstyle
6
+ attr_accessor :shape_version
7
+ attr_accessor :fill_styles_with_offset
8
+ attr_accessor :after_line_styles
9
+
10
+ def initialize(bytearray, shape_version)
11
+ @shape_version = shape_version
12
+ fill_style_count = bytearray[0, 1].unpack("C").first
13
+ offset = 1
14
+ if fill_style_count == 255
15
+ fill_style_count = bytearray[1, 2].unpack("v").first
16
+ offset += 2
17
+ end
18
+ @fill_styles_with_offset = {}
19
+ fill_style_count.times do
20
+ fill_style = SwfRuby::Swf::Fillstyle.new(bytearray[offset..-1], @shape_version)
21
+ @fill_styles_with_offset[offset] = fill_style
22
+ offset += fill_style.length
23
+ end
24
+ # TODO after_line_styles are including outside tag data..
25
+ @after_line_styles = bytearray[offset..-1]
26
+ self
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ # vim: set fileencoding=utf-8 filetype=ruby ts=2 :
2
+
3
+ module SwfRuby
4
+ module Swf
5
+ class SwfString
6
+ attr_accessor :string
7
+ attr_reader :length # including StringEndMark
8
+
9
+ def initialize(bytearray)
10
+ @length = 0
11
+ bytearray.each_byte do |byte|
12
+ break if byte == 0
13
+ @length += 1
14
+ end
15
+ @string = bytearray[0, @length]
16
+ @length += 1 # Endmark
17
+ self
18
+ end
19
+ end
20
+ end
21
+ end
@@ -3,10 +3,16 @@
3
3
  module SwfRuby
4
4
  module Swf
5
5
  class Tag
6
+ attr_reader :rawdata
6
7
  attr_reader :code
7
8
  attr_reader :length
8
9
  attr_reader :long_header
9
10
  attr_reader :data
11
+ attr_reader :character_id
12
+ attr_reader :refer_character_id
13
+ attr_reader :refer_character_id_offset
14
+ attr_reader :refer_character_inst_name
15
+ attr_reader :refer_bitmap_offsets_to_ids
10
16
 
11
17
  def initialize(swf)
12
18
  swf.force_encoding("ASCII-8BIT") if swf.respond_to? :force_encoding
@@ -26,9 +32,129 @@ module SwfRuby
26
32
  @data = swf[2, @length]
27
33
  @length += 2
28
34
  end
35
+ @character_id = nil
36
+ if self.define_tag?
37
+ @character_id = @data[0, 2].unpack("v").first
38
+ end
39
+ @refer_character_id = nil
40
+ @refer_character_id_offset = nil
41
+ @refer_character_inst_name = nil
42
+ data_offset = @long_header ? 6 : 2
43
+ case Swf::TAG_TYPE[@code]
44
+ when "PlaceObject"
45
+ @refer_character_id_offset = data_offset
46
+ @refer_character_id = @data[0, 2].unpack("v").first
47
+ when "PlaceObject2"
48
+ flags = @data[0, 1].unpack("C").first
49
+ offset = 3
50
+ if flags & 2 == 2
51
+ @refer_character_id_offset = data_offset + offset
52
+ @refer_character_id = @data[offset, 2].unpack("v").first
53
+ offset += 2
54
+ end
55
+ if flags & 32 == 32
56
+ if flags & 4 == 4
57
+ matrix = SwfRuby::Swf::Matrix.new(@data[offset..-1])
58
+ offset += matrix.length
59
+ end
60
+ if flags & 8 == 8
61
+ cxtfm = SwfRuby::Swf::Cxformwithalpha.new(@data[offset..-1])
62
+ offset += cxtfm.length
63
+ end
64
+ offset += 2 if flags & 16 == 16
65
+ @refer_character_inst_name = SwfRuby::Swf::SwfString.new(@data[offset..-1]).string
66
+ end
67
+ when "PlaceObject3"
68
+ flags = @data[0, 2].unpack("n").first
69
+ offset = 4
70
+ if flags & 8 == 8
71
+ class_name = SwfRuby::Swf::SwfString.new(@data[offset..-1])
72
+ offset += class_name.length
73
+ end
74
+ if flags & 512 == 512
75
+ @refer_character_id_offset = data_offset + offset
76
+ @refer_character_id = @data[offset, 2].unpack("v").first
77
+ end
78
+ if flags & 8192 == 8192
79
+ if flags & 1024 == 1024
80
+ matrix = SwfRuby::Swf::Matrix.new(@data[offset..-1])
81
+ offset += matrix.length
82
+ end
83
+ if flags & 2048 == 2048
84
+ cxtfm = SwfRuby::Swf::Cxformwithalpha.new(@data[offset..-1])
85
+ offset += cxtfm.length
86
+ end
87
+ offset += 2 if flags & 4096 == 4096
88
+ @refer_character_inst_name = SwfRuby::Swf::SwfString.new(@data[offset..-1]).string
89
+ end
90
+ when "RemoveObject"
91
+ @refer_character_id_offset = data_offset
92
+ @refer_character_id = @data[0, 2].unpack("v").first
93
+ when "DefineShape"
94
+ when "DefineShape2"
95
+ when "DefineShape3"
96
+ version = Swf::TAG_TYPE[@code][-1].to_i
97
+ version = 1 if version == 0
98
+ @refer_bitmap_offsets_to_ids = {}
99
+ rect = SwfRuby::Swf::Rectangle.new(@data[2..-1])
100
+ offset = 2+rect.length
101
+ shapewithstyle = SwfRuby::Swf::Shapewithstyle.new(@data[offset..-1], version)
102
+ shapewithstyle.fill_styles_with_offset.each do |fs_offset, fs|
103
+ if fs.bitmap_id
104
+ o = offset + fs_offset + fs.bitmap_id_offset
105
+ o += @long_header ? 6 : 2
106
+ @refer_bitmap_offsets_to_ids[o] = fs.bitmap_id
107
+ end
108
+ end
109
+ when "DefineShape4"
110
+ version = 4
111
+ @refer_bitmap_offsets_to_ids = {}
112
+ shape_rect = SwfRuby::Swf::Rectangle.new(@data[2..-1])
113
+ offset = 2+shape_rect.length
114
+ edge_rect = SwfRuby::Swf::Rectangle.new(@data[offset..-1])
115
+ offset += edge_rect.length
116
+ offset += 1
117
+ shapewithstyle = SwfRuby::Swf::Shapewithstyle.new(@data[offset..-1], version)
118
+ shapewithstyle.fill_styles_with_offset.each do |fs_offset, fs|
119
+ if fs.bitmap_id
120
+ o = offset + fs_offset + fs.bitmap_id_offset
121
+ o += @long_header ? 6 : 2
122
+ @refer_bitmap_offsets_to_ids[o] = fs.bitmap_id
123
+ end
124
+ end
125
+ else
126
+ # do nothing.
127
+ end
128
+ @rawdata = swf[0, @length]
129
+ self
130
+ end
131
+
132
+ # Define系タグの冒頭のcharacter_id参照を指定のIDに書き換える.
133
+ # 内部にBitmapIDの参照を含む場合、渡されたidmapから読み替える値を参照し、書き換える.
134
+ def rawdata_with_define_character_id(idmap, character_id)
135
+ if self.define_tag?
136
+ offset = @long_header ? 6 : 2
137
+ @rawdata[offset, 2] = [character_id].pack("v")
138
+ @refer_bitmap_offsets_to_ids.each do |bitmap_id_offset, bitmap_id|
139
+ @rawdata[bitmap_id_offset, 2] = [idmap[bitmap_id]].pack("v")
140
+ end if @refer_bitmap_offsets_to_ids
141
+ end
142
+ @rawdata
29
143
  end
144
+
145
+ def rawdata_with_refer_character_id(character_id)
146
+ @rawdata[@refer_character_id_offset, 2] = [character_id].pack("v") if @refer_character_id_offset
147
+ @rawdata
148
+ end
149
+
150
+ def define_tag?
151
+ Swf::TAG_TYPE[@code] =~ /^Define/
152
+ end
153
+
30
154
  end
31
155
 
156
+ class TagError < StandardError; end
157
+
32
158
  TAG_TYPE = {
33
159
  0=>"End",
34
160
  1=>"ShowFrame",
@@ -7,6 +7,7 @@ module SwfRuby
7
7
  attr_reader :header
8
8
  attr_reader :tags
9
9
  attr_reader :tags_addresses
10
+ attr_accessor :character_ids
10
11
 
11
12
  # 初期化.
12
13
  def initialize
@@ -27,7 +28,7 @@ module SwfRuby
27
28
  # ダンプして構造をインスタンス変数に格納.
28
29
  def dump(swf)
29
30
  @swf = swf
30
- @header = Swf::Header.new(@swf)
31
+ @header, @swf = Swf::Header.parse(@swf)
31
32
  @tags = []
32
33
  @tags_addresses = []
33
34
  tags_length = 0
@@ -40,5 +41,6 @@ module SwfRuby
40
41
  end
41
42
  self
42
43
  end
44
+
43
45
  end
44
46
  end
@@ -8,20 +8,48 @@ module SwfRuby
8
8
  replace_targets.sort_by { |a| a.offset }.reverse.each do |rt|
9
9
  case rt
10
10
  when Jpeg2ReplaceTarget
11
- swf = self.repl_jpeg2(swf, rt.offset, rt.jpeg)
11
+ swf = repl_jpeg2(swf, rt.offset, rt.jpeg)
12
12
  when Lossless2ReplaceTarget
13
- swf = self.repl_lossless2(swf, rt.offset, rt.image)
13
+ swf = repl_lossless2(swf, rt.offset, rt.image)
14
14
  when AsVarReplaceTarget
15
- swf = self.repl_action_push_string(swf, rt.do_action_offset, rt.offset, rt.str, rt.parent_sprite_offsets)
15
+ swf = repl_action_push_string(swf, rt.do_action_offset, rt.offset, rt.str, rt.parent_sprite_offset)
16
+ when SpriteReplaceTarget
17
+ swf = repl_sprite(swf, rt.offset, rt.target_define_tags_string, rt.frame_count, rt.target_control_tags_string)
16
18
  end
17
19
  end
18
20
  swf
19
21
  end
20
22
 
21
- protected
23
+ private
22
24
 
23
- # ActionScriptに含まれる文字列を置換.
24
- def repl_action_push_string(swf, do_action_offset, action_push_offset, str, parent_sprite_offsets = [])
25
+ # 対象オフセット位置にあるDefineSpriteを指定したControlTags文字列で置換.
26
+ # 新しいSprite含むDefineTagsは、DefineSpriteの直前に挿入する.
27
+ def repl_sprite(swf, offset, define_tags, frame_count, control_tags)
28
+ swf.force_encoding("ASCII-8BIT") if swf.respond_to? :force_encoding
29
+ define_tags.force_encoding("ASCII-8BIT") if define_tags.respond_to? :force_encoding
30
+ control_tags.force_encoding("ASCII-8BIT") if control_tags.respond_to? :force_encoding
31
+ record_header = swf[offset, 2].unpack("v").first
32
+ # tag check
33
+ raise ReplaceTargetError if (record_header >> 6) & 1023 != 39
34
+ # error for short header (not implemented yet.)
35
+ raise ReplaceTargetError if record_header & 63 < 63
36
+ # rewrite frame count
37
+ swf[offset+8, 2] = [frame_count].pack("v")
38
+ # rewrite control tag and length
39
+ sprite_len = swf[offset+2, 4].unpack("i").first # without recordheader
40
+ old_control_tags_len = sprite_len - 4
41
+ delta_control_tags_len = control_tags.length - old_control_tags_len
42
+ swf[offset+10, old_control_tags_len] = control_tags
43
+ swf[offset+2, 4] = [sprite_len + delta_control_tags_len].pack("V")
44
+ # insert define tags before define sprite
45
+ swf[offset, 0] = define_tags
46
+ # rewrite swf header
47
+ swf[4, 4] = [swf[4, 4].unpack("V").first + define_tags.length + delta_control_tags_len].pack("V")
48
+ swf
49
+ end
50
+
51
+ # 対象オフセット位置にあるActionPushデータを置換.
52
+ def repl_action_push_string(swf, do_action_offset, action_push_offset, str, parent_sprite_offset = nil)
25
53
  swf.force_encoding("ASCII-8BIT") if swf.respond_to? :force_encoding
26
54
  str.force_encoding("ASCII-8BIT") if str.respond_to? :force_encoding
27
55
  record_header = swf[do_action_offset, 2].unpack("v").first
@@ -30,10 +58,10 @@ module SwfRuby
30
58
  # action check
31
59
  raise ReplaceTargetError if swf[action_push_offset].chr.unpack("C").first != 0x96
32
60
  raise ReplaceTargetError if swf[action_push_offset + 3].chr.unpack("C").first != 0
33
- # error on short header
61
+ # error for short header (not implemented yet.)
34
62
  raise ReplaceTargetError if record_header & 63 < 63
35
63
  # calc length
36
- do_action_len = swf[do_action_offset+2, 4].unpack("i").first
64
+ do_action_len = swf[do_action_offset+2, 4].unpack("i").first # without recordheader
37
65
  action_push_len = swf[action_push_offset+1, 2].unpack("v").first
38
66
  org_str_length = action_push_len - 2 # data type & terminated null
39
67
  delta_str_length = str.length - org_str_length
@@ -41,7 +69,7 @@ module SwfRuby
41
69
  swf[action_push_offset+4, org_str_length] = str
42
70
  swf[action_push_offset+1, 2] = [action_push_len + delta_str_length].pack("v")
43
71
  swf[do_action_offset+2, 4] = [do_action_len + delta_str_length].pack("V")
44
- parent_sprite_offsets.sort.reverse.each do |parent_sprite_offset|
72
+ if parent_sprite_offset
45
73
  swf[parent_sprite_offset+2, 4] = [swf[parent_sprite_offset+2, 4].unpack("V").first + delta_str_length].pack("V")
46
74
  end
47
75
  swf[4, 4] = [swf[4, 4].unpack("V").first + delta_str_length].pack("V")
@@ -143,6 +171,4 @@ module SwfRuby
143
171
  end
144
172
  end
145
173
 
146
- # 置換対象指定エラー.
147
- class ReplaceTargetError < StandardError; end
148
174
  end
@@ -1,3 +1,3 @@
1
1
  module SwfRuby
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/swf_ruby.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "rmagick", ">= 2.13.0"
20
20
 
21
21
  s.bindir = 'bin'
22
- s.executables = ['swf_dump', 'swf_jpeg_replace', 'swf_lossless_replace']
22
+ s.executables = ['swf_dump', 'swf_jpeg_replace', 'swf_lossless_replace', 'swf_as_var_replace', 'swf_sprite_replace']
23
23
 
24
24
  s.files = `git ls-files`.split("\n")
25
25
  s.require_path = 'lib'
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 1
9
- version: 0.1.1
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors: []
12
12
 
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-27 00:00:00 +09:00
17
+ date: 2011-04-27 00:00:00 +09:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -56,6 +56,8 @@ executables:
56
56
  - swf_dump
57
57
  - swf_jpeg_replace
58
58
  - swf_lossless_replace
59
+ - swf_as_var_replace
60
+ - swf_sprite_replace
59
61
  extensions: []
60
62
 
61
63
  extra_rdoc_files: []
@@ -68,9 +70,11 @@ files:
68
70
  - Gemfile.lock
69
71
  - README.md
70
72
  - Rakefile
73
+ - bin/swf_as_var_replace
71
74
  - bin/swf_dump
72
75
  - bin/swf_jpeg_replace
73
76
  - bin/swf_lossless_replace
77
+ - bin/swf_sprite_replace
74
78
  - lib/swf_ruby.rb
75
79
  - lib/swf_ruby/do_action_dumper.rb
76
80
  - lib/swf_ruby/replace_target.rb
@@ -79,8 +83,18 @@ files:
79
83
  - lib/swf_ruby/swf/action_push.rb
80
84
  - lib/swf_ruby/swf/action_record.rb
81
85
  - lib/swf_ruby/swf/bits_lossless2.rb
86
+ - lib/swf_ruby/swf/cxformwithalpha.rb
87
+ - lib/swf_ruby/swf/fillstyle.rb
88
+ - lib/swf_ruby/swf/focalgradient.rb
89
+ - lib/swf_ruby/swf/gradient.rb
90
+ - lib/swf_ruby/swf/gradrecord.rb
82
91
  - lib/swf_ruby/swf/header.rb
92
+ - lib/swf_ruby/swf/matrix.rb
83
93
  - lib/swf_ruby/swf/rectangle.rb
94
+ - lib/swf_ruby/swf/rgb.rb
95
+ - lib/swf_ruby/swf/rgba.rb
96
+ - lib/swf_ruby/swf/shapewithstyle.rb
97
+ - lib/swf_ruby/swf/swf_string.rb
84
98
  - lib/swf_ruby/swf/tag.rb
85
99
  - lib/swf_ruby/swf_dumper.rb
86
100
  - lib/swf_ruby/swf_tamperer.rb