furnace-swf 0.0.1
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 +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:
|