swf_ruby 0.1.1 → 0.2.0

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/.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