furnace-swf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/furnace-swf +96 -0
- data/furnace-swf.gemspec +22 -0
- data/lib/furnace-swf.rb +8 -0
- data/lib/furnace-swf/swf.rb +17 -0
- data/lib/furnace-swf/swf/file.rb +40 -0
- data/lib/furnace-swf/swf/header.rb +9 -0
- data/lib/furnace-swf/swf/rect.rb +9 -0
- data/lib/furnace-swf/swf/sbit.rb +21 -0
- data/lib/furnace-swf/swf/stream.rb +25 -0
- data/lib/furnace-swf/swf/tag.rb +34 -0
- data/lib/furnace-swf/swf/tag_wrapper.rb +85 -0
- data/lib/furnace-swf/swf/tags/define_binary_data_tag.rb +9 -0
- data/lib/furnace-swf/swf/tags/do_abc_tag.rb +9 -0
- data/lib/furnace-swf/swf/tags/symbol_class_tag.rb +11 -0
- data/lib/furnace-swf/swf/ubit.rb +21 -0
- data/lib/furnace-swf/version.rb +3 -0
- metadata +91 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/furnace-swf
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/setup"
|
5
|
+
|
6
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
7
|
+
|
8
|
+
require "trollop"
|
9
|
+
require "furnace-swf"
|
10
|
+
|
11
|
+
include Furnace
|
12
|
+
|
13
|
+
SUBCOMMANDS = %w(abclist abcextract abcreplace)
|
14
|
+
|
15
|
+
opts = Trollop::options do
|
16
|
+
version "furnace-swf #{SWF::VERSION}"
|
17
|
+
banner <<-EOS
|
18
|
+
furnace-swf is a processing tool which operates on Flash SWF files.
|
19
|
+
|
20
|
+
Possible subcommands: #{SUBCOMMANDS.join ' '}
|
21
|
+
Try #{__FILE__} subcommand --help.
|
22
|
+
|
23
|
+
Usage: #{__FILE__} [options] <subcommand>
|
24
|
+
EOS
|
25
|
+
|
26
|
+
opt :input, "SWF input file", :type => :io
|
27
|
+
opt :trace, "Trace reading", :default => false
|
28
|
+
|
29
|
+
stop_on SUBCOMMANDS
|
30
|
+
end
|
31
|
+
|
32
|
+
subcommand = ARGV.shift
|
33
|
+
subopts = case subcommand
|
34
|
+
when 'abclist'
|
35
|
+
Trollop::options do
|
36
|
+
end
|
37
|
+
|
38
|
+
when 'abcextract'
|
39
|
+
Trollop::options do
|
40
|
+
opt :name, "ABC tag name", :type => :string, :required => true
|
41
|
+
opt :output, "ABC output file", :type => :string, :required => true
|
42
|
+
end
|
43
|
+
|
44
|
+
when 'abcreplace'
|
45
|
+
Trollop::options do
|
46
|
+
opt :name, "ABC tag name", :type => :string, :required => true
|
47
|
+
opt :input, "ABC input file", :type => :io, :required => true
|
48
|
+
opt :output, "Combined SWF output file", :type => :string, :required => true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Trollop::die "Option --input is required" unless opts[:input]
|
53
|
+
|
54
|
+
swf = nil
|
55
|
+
File.open(opts[:input]) do |file|
|
56
|
+
swf = SWF::File.new
|
57
|
+
|
58
|
+
if opts[:trace]
|
59
|
+
BinData::trace_reading do
|
60
|
+
swf.read(file)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
swf.read(file)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
case subcommand
|
68
|
+
when 'abclist'
|
69
|
+
puts "ABC tags:"
|
70
|
+
|
71
|
+
swf.stream.tags(SWF::DoABCTag).each do |tag|
|
72
|
+
puts " #{tag.name.inspect}: #{tag.bytecode.length} byte(s)"
|
73
|
+
end
|
74
|
+
|
75
|
+
when 'abcextract'
|
76
|
+
if tag = swf.stream.tags(SWF::DoABCTag).find { |tag| tag.name == subopts[:name] }
|
77
|
+
File.open(subopts[:output], 'w') do |f|
|
78
|
+
f.write tag.bytecode
|
79
|
+
end
|
80
|
+
else
|
81
|
+
puts "Tag #{subopts[:name].inspect} was not found"
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
|
85
|
+
when 'abcreplace'
|
86
|
+
if tag = swf.stream.tags(SWF::DoABCTag).find { |tag| tag.name == subopts[:name] }
|
87
|
+
tag.bytecode = subopts[:input].read
|
88
|
+
|
89
|
+
File.open(subopts[:output], 'w') do |f|
|
90
|
+
swf.write f
|
91
|
+
end
|
92
|
+
else
|
93
|
+
puts "Tag #{subopts[:name].inspect} was not found"
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
end
|
data/furnace-swf.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "furnace-swf"
|
6
|
+
s.version = "0.0.1"
|
7
|
+
s.authors = ["Peter Zotov", "Sergey Gridassov"]
|
8
|
+
s.email = ["whitequark@whitequark.org", "grindars@gmail.com"]
|
9
|
+
s.homepage = "http://github.com/whitequark/furnace-swf"
|
10
|
+
s.summary = %q{Flash SWF file reader and writer}
|
11
|
+
s.description = %q{furnace-swf allows one to load, modify and write back } <<
|
12
|
+
%q{Flash SWF files. It can be used with furnace-avm2 in } <<
|
13
|
+
%q{order to achieve impressive results.}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency "bindata"
|
21
|
+
s.add_runtime_dependency "trollop"
|
22
|
+
end
|
data/lib/furnace-swf.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bindata"
|
2
|
+
|
3
|
+
require_relative "swf/sbit"
|
4
|
+
require_relative "swf/ubit"
|
5
|
+
|
6
|
+
require_relative "swf/rect"
|
7
|
+
|
8
|
+
require_relative "swf/tag_wrapper"
|
9
|
+
require_relative "swf/tag"
|
10
|
+
|
11
|
+
Dir[File.join(File.dirname(__FILE__), 'swf', 'tags', '*.rb')].each do |tag|
|
12
|
+
require tag
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative "swf/header"
|
16
|
+
require_relative "swf/stream"
|
17
|
+
require_relative "swf/file"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
module Furnace::SWF
|
4
|
+
class File < BinData::Record
|
5
|
+
header :header
|
6
|
+
stream :stream
|
7
|
+
|
8
|
+
def do_read(io)
|
9
|
+
instantiate_all_objs
|
10
|
+
|
11
|
+
header = find_obj_for_name(:header)
|
12
|
+
stream = find_obj_for_name(:stream)
|
13
|
+
|
14
|
+
header.do_read(io)
|
15
|
+
|
16
|
+
case header.signature
|
17
|
+
when 'FWS'
|
18
|
+
stream.do_read io
|
19
|
+
when 'CWS'
|
20
|
+
uncompressed = Zlib.inflate(io.read_all_bytes)
|
21
|
+
stream.do_read BinData::IO.new(StringIO.new(uncompressed))
|
22
|
+
else
|
23
|
+
raise ArgumentError, "invalid signature"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_write(io)
|
28
|
+
instantiate_all_objs
|
29
|
+
|
30
|
+
header = find_obj_for_name(:header)
|
31
|
+
stream = find_obj_for_name(:stream)
|
32
|
+
|
33
|
+
header.signature = 'FWS'
|
34
|
+
header.file_length = header.num_bytes + stream.real_num_bytes
|
35
|
+
|
36
|
+
header.do_write(io)
|
37
|
+
stream.do_write(io)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Furnace::SWF
|
2
|
+
class Sbit < BinData::BasePrimitive
|
3
|
+
mandatory_parameter :length
|
4
|
+
|
5
|
+
def do_write(io)
|
6
|
+
io.writebits(_value, eval_parameter(:length), :big)
|
7
|
+
end
|
8
|
+
|
9
|
+
def do_num_bytes
|
10
|
+
eval_parameter(:length) / 8.0
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_and_return_value(io)
|
14
|
+
io.readbits(eval_parameter(:length), :big)
|
15
|
+
end
|
16
|
+
|
17
|
+
def sensible_default
|
18
|
+
0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Furnace::SWF
|
2
|
+
class Stream < BinData::Record
|
3
|
+
endian :little
|
4
|
+
|
5
|
+
rect :frame_size
|
6
|
+
uint8 :frame_rate_lo
|
7
|
+
uint8 :frame_rate_hi
|
8
|
+
uint16 :frame_count
|
9
|
+
|
10
|
+
array :tag_wrappers, :type => :tag_wrapper, :read_until => :eof
|
11
|
+
|
12
|
+
def frame_rate
|
13
|
+
BigDecimal.new(frame_rate_hi) +
|
14
|
+
BigDecimal.new(frame_rate_lo) / 100
|
15
|
+
end
|
16
|
+
|
17
|
+
def tags(type)
|
18
|
+
tag_wrappers.select { |tw| tw.content.is_a? type }.map(&:content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def real_num_bytes
|
22
|
+
num_bytes + tag_wrappers.map(&:content_size).reduce(0, :+)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Furnace::SWF
|
2
|
+
class Tag < BinData::Record
|
3
|
+
mandatory_parameter :stream
|
4
|
+
|
5
|
+
endian :little
|
6
|
+
|
7
|
+
REGISTRY = {}
|
8
|
+
|
9
|
+
def self.exists_for?(type)
|
10
|
+
REGISTRY.has_key? type
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.instantiate(type, parent, stream)
|
14
|
+
REGISTRY[type].new({ stream: stream }, parent)
|
15
|
+
end
|
16
|
+
|
17
|
+
# DSL
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def type(id)
|
21
|
+
Tag::REGISTRY[id] = self
|
22
|
+
|
23
|
+
define_method(:type) do
|
24
|
+
id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize_instance
|
30
|
+
super
|
31
|
+
@stream = @params[:stream]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Furnace::SWF
|
2
|
+
class TagWrapper < BinData::Record
|
3
|
+
endian :little
|
4
|
+
|
5
|
+
uint16 :type_and_length
|
6
|
+
int32 :long_length, :onlyif => lambda { short_length == 0x3f }
|
7
|
+
|
8
|
+
attr_accessor :content
|
9
|
+
|
10
|
+
# Accessors for braindead format.
|
11
|
+
|
12
|
+
def type
|
13
|
+
type_and_length >> 6
|
14
|
+
end
|
15
|
+
|
16
|
+
def type=(value)
|
17
|
+
self.type_and_length = (type_and_length & 0x3f) | (value << 6)
|
18
|
+
end
|
19
|
+
|
20
|
+
def short_length
|
21
|
+
type_and_length & 0x3f
|
22
|
+
end
|
23
|
+
|
24
|
+
def short_length=(value)
|
25
|
+
self.type_and_length = (type_and_length & 0xfffc) | (value & 0x3f)
|
26
|
+
end
|
27
|
+
|
28
|
+
def real_length
|
29
|
+
short_length == 0x3f ? long_length : short_length
|
30
|
+
end
|
31
|
+
|
32
|
+
def real_length=(value)
|
33
|
+
if value > 0x3f
|
34
|
+
self.short_length = 0x3f
|
35
|
+
self.long_length = value
|
36
|
+
else
|
37
|
+
self.short_length = value
|
38
|
+
self.long_length = 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Nested reads/writes.
|
43
|
+
|
44
|
+
def do_read(io)
|
45
|
+
super
|
46
|
+
|
47
|
+
orig_offset = io.offset
|
48
|
+
|
49
|
+
if Tag.exists_for?(type)
|
50
|
+
@content = Tag.instantiate(type, self, parent)
|
51
|
+
@content.do_read io
|
52
|
+
|
53
|
+
if io.offset - orig_offset != real_length
|
54
|
+
raise "Invalid tag #{@content}"
|
55
|
+
end
|
56
|
+
else
|
57
|
+
@content = io.readbytes(real_length)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def do_write(io)
|
62
|
+
super
|
63
|
+
|
64
|
+
if Tag.exists_for?(type)
|
65
|
+
@content.do_write io
|
66
|
+
else
|
67
|
+
io.writebytes @content
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def do_num_bytes
|
72
|
+
self.real_length = content_size
|
73
|
+
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def content_size
|
78
|
+
if @content.is_a? Tag
|
79
|
+
@content.num_bytes
|
80
|
+
else
|
81
|
+
@content.length
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Furnace::SWF
|
2
|
+
class Ubit < BinData::BasePrimitive
|
3
|
+
mandatory_parameter :length
|
4
|
+
|
5
|
+
def do_write(io)
|
6
|
+
io.writebits(_value, eval_parameter(:length), :little)
|
7
|
+
end
|
8
|
+
|
9
|
+
def do_num_bytes
|
10
|
+
eval_parameter(:length) / 8.0
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_and_return_value(io)
|
14
|
+
io.readbits(eval_parameter(:length), :big)
|
15
|
+
end
|
16
|
+
|
17
|
+
def sensible_default
|
18
|
+
0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: furnace-swf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Peter Zotov
|
9
|
+
- Sergey Gridassov
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-02-04 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bindata
|
17
|
+
requirement: &71683110 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *71683110
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: trollop
|
28
|
+
requirement: &71682470 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *71682470
|
37
|
+
description: furnace-swf allows one to load, modify and write back Flash SWF files.
|
38
|
+
It can be used with furnace-avm2 in order to achieve impressive results.
|
39
|
+
email:
|
40
|
+
- whitequark@whitequark.org
|
41
|
+
- grindars@gmail.com
|
42
|
+
executables:
|
43
|
+
- furnace-swf
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- Gemfile
|
49
|
+
- Rakefile
|
50
|
+
- bin/furnace-swf
|
51
|
+
- furnace-swf.gemspec
|
52
|
+
- lib/furnace-swf.rb
|
53
|
+
- lib/furnace-swf/swf.rb
|
54
|
+
- lib/furnace-swf/swf/file.rb
|
55
|
+
- lib/furnace-swf/swf/header.rb
|
56
|
+
- lib/furnace-swf/swf/rect.rb
|
57
|
+
- lib/furnace-swf/swf/sbit.rb
|
58
|
+
- lib/furnace-swf/swf/stream.rb
|
59
|
+
- lib/furnace-swf/swf/tag.rb
|
60
|
+
- lib/furnace-swf/swf/tag_wrapper.rb
|
61
|
+
- lib/furnace-swf/swf/tags/define_binary_data_tag.rb
|
62
|
+
- lib/furnace-swf/swf/tags/do_abc_tag.rb
|
63
|
+
- lib/furnace-swf/swf/tags/symbol_class_tag.rb
|
64
|
+
- lib/furnace-swf/swf/ubit.rb
|
65
|
+
- lib/furnace-swf/version.rb
|
66
|
+
homepage: http://github.com/whitequark/furnace-swf
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.10
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Flash SWF file reader and writer
|
90
|
+
test_files: []
|
91
|
+
has_rdoc:
|