swf_recompress 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require File.join(File.dirname(__FILE__), '../lib/swf_recompress')
5
+
6
+ ACQUIRE_KZIP_TEXT = "Run with --acquire-kzip to download the kzip tool"
7
+ SWF_RECOMPRESS_BANNER = "swf_recompress version #{SWFRecompress::VERSION}#{SWFRecompress.kzip_available? ? '' : ', but no kzip installed'}"
8
+
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.banner = "#{SWF_RECOMPRESS_BANNER}\nRecompress a swf file with more aggressive DEFLATE settings\nUsage: swf_recompress [options] swf_filename [output_swf_filename]"
12
+ opts.on('-i', '--in-place', 'Compress the swf in-place, replacing the original file') do
13
+ options[:use_input_filename] = true
14
+ end
15
+ opts.on('-a', '--acquire-kzip', 'Download kzip tool') do
16
+ options[:acquire_kzip] = true
17
+ end
18
+ opts.on_tail('-v', '--version', 'Show version & contributors') do
19
+ puts SWF_RECOMPRESS_BANNER
20
+ puts " Based on Jos-Iven Hirth's \"Improving SWF Compression\": http://kaioa.com/node/87"
21
+ puts SWFRecompress::KZIP_ABOUT
22
+ if !SWFRecompress.kzip_available?
23
+ puts SWFRecompress::KZIP_INSTALL_TEXT
24
+ end
25
+ exit
26
+ end
27
+ opts.on_tail("-h", "--help", "Show this message") do
28
+ puts opts
29
+ puts "If only one filename is given and the -i flag is not supplied, a new swf"
30
+ puts "with the suffix '_compressed' will be created alongside the original file."
31
+ exit
32
+ end
33
+ end.parse!(ARGV)
34
+
35
+ options[:input_filename] = ARGV[0]
36
+ options[:output_filename] = ARGV[0] if options[:use_input_filename]
37
+
38
+ def require_kzip!
39
+ if !SWFRecompress.kzip_available?
40
+ $stderr.puts "No kzip installed"
41
+ $stderr.puts "#{ACQUIRE_KZIP_TEXT}\n#{SWFRecompress::KZIP_ABOUT}"
42
+ exit 1
43
+ end
44
+ end
45
+
46
+ begin
47
+ if options[:acquire_kzip]
48
+ if SWFRecompress.kzip_available?
49
+ puts "kzip already available"
50
+ else
51
+ SWFRecompress.acquire_kzip
52
+ puts "Installed lib/kzip"
53
+ end
54
+ end
55
+
56
+ if options[:output_filename]
57
+ require_kzip!
58
+ SWFRecompress.recompress_to(options[:input_filename], options[:output_filename])
59
+ elsif options[:input_filename]
60
+ require_kzip!
61
+ SWFRecompress.recompress(options[:input_filename])
62
+ elsif !options[:acquire_kzip]
63
+ $stderr.puts "Please specify a swf file to recompress"
64
+ exit 1
65
+ end
66
+ rescue => e
67
+ $stderr.puts "There was an error recompressing #{options[:input_filename].inspect}\n#{e}"
68
+ exit 1
69
+ end
data/lib/kzip ADDED
Binary file
@@ -0,0 +1,208 @@
1
+ module SWFRecompress
2
+ VERSION = "0.0.6"
3
+
4
+ require 'fileutils'
5
+ require 'pathname'
6
+ require 'tempfile'
7
+
8
+ ROOT = File.join(File.dirname(__FILE__), '..')
9
+ TMP_DIR = Dir::tmpdir
10
+
11
+ KZIP_HOST = 'static.jonof.id.au'
12
+ KZIP_MD5 = 'fdbf05e2bd12b16e899df0f3b6a3e87d'
13
+ KZIP_PATH = '/dl/kenutils/kzipmix-20091108-darwin.tar.gz'
14
+ KZIP_ABOUT = <<-END_KZIP_ABOUT
15
+ kzip by Ken Silverman: http://advsys.net/ken/utils.htm
16
+ Mac OS X and Linux binaries maintained by Jonathan Fowler: http://www.jonof.id.au/
17
+ END_KZIP_ABOUT
18
+ KZIP_INSTALL_TEXT = " Install kzip binary to #{File.expand_path(File.join(ROOT, 'lib/kzip'))}"
19
+
20
+ class Tempfile
21
+ def self.open(temp_stem, write_mode = nil)
22
+ begin
23
+ instance_tmp_dir = nil
24
+ begin
25
+ instance_tmp_dir = File.join(TMP_DIR, rand(1_000_000).to_s)
26
+ end until !File.exists?(instance_tmp_dir)
27
+ FileUtils.mkdir_p(instance_tmp_dir)
28
+ ext = File.extname(temp_stem)
29
+ temp_filename = Pathname.new(
30
+ File.expand_path(File.join(instance_tmp_dir, '%s%s' % [ File.basename(temp_stem, ext), ext ]))
31
+ ).relative_path_from(Pathname.new(Dir.pwd))
32
+ File.open(temp_filename, write_mode || 'w') do |f|
33
+ yield(f)
34
+ end
35
+ rescue => e
36
+ raise "Error during Tempfile open #{e.message}"
37
+ ensure
38
+ FileUtils.rm(temp_filename)
39
+ FileUtils.rm_rf(instance_tmp_dir)
40
+ end
41
+ end
42
+ end
43
+
44
+ class SWFRecompressor
45
+ attr_reader :data_filename, :data_zip_filename, :info_filename, :swf_filename, :output_filename
46
+ def initialize(swf_filename, output_filename)
47
+ if !File.exists?(swf_filename)
48
+ raise "The file #{swf_filename.inspect} does not exist"
49
+ end
50
+ @swf_filename = swf_filename
51
+ @output_filename = output_filename
52
+ end
53
+
54
+ def recompress!
55
+ with_tempfiles do
56
+ SWFRecompress.execute(swf_extract, kzip_data, swf_inject)
57
+ end
58
+ end
59
+
60
+ private
61
+ def swf_extract
62
+ java('SWFExtract', swf_filename, data_filename, info_filename)
63
+ end
64
+
65
+ def swf_inject
66
+ java('SWFInject', data_zip_filename, info_filename, output_filename)
67
+ end
68
+
69
+ def kzip_data
70
+ kzip('-y', '-k0', data_zip_filename, data_filename)
71
+ end
72
+
73
+ def with_tempfiles
74
+ Dir.chdir(ROOT) do
75
+ with_temp_info_file do
76
+ with_temp_data_file do
77
+ with_temp_data_zip_file do
78
+ yield
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def with_temp_info_file
86
+ Tempfile.open('INFO') do |f|
87
+ f.close
88
+ @info_filename = f.path
89
+ yield
90
+ end
91
+ end
92
+
93
+ def with_temp_data_file
94
+ Tempfile.open('SWF_DATA') do |f|
95
+ f.close
96
+ @data_filename = f.path
97
+ yield
98
+ end
99
+ end
100
+
101
+ def with_temp_data_zip_file
102
+ Tempfile.open('SWF_DATA.zip') do |f|
103
+ f.close
104
+ @data_zip_filename = f.path
105
+ yield
106
+ end
107
+ end
108
+
109
+ def java(*args)
110
+ command('java', '-classpath', 'src', *args)
111
+ end
112
+
113
+ def kzip(*args)
114
+ command('lib/kzip', *args)
115
+ end
116
+
117
+ def zip(*args)
118
+ command('zip', *args)
119
+ end
120
+
121
+ def command(*args)
122
+ SWFRecompress.command(*args)
123
+ end
124
+ end
125
+
126
+ module ClassMethods
127
+ def recompress(filename)
128
+ expanded_filename = File.expand_path(filename)
129
+ ext = File.extname(expanded_filename)
130
+ dirname = File.dirname(expanded_filename)
131
+ new_filename = File.join(dirname, '%s%s%s' % [ File.basename(expanded_filename, ext), '_compressed', ext ])
132
+ compressor = SWFRecompressor.new(expanded_filename, new_filename)
133
+ compressor.recompress!
134
+ end
135
+
136
+ def recompress_to(filename, new_filename)
137
+ expanded_filename = File.expand_path(filename)
138
+ compressor = SWFRecompressor.new(
139
+ File.expand_path(filename),
140
+ File.expand_path(new_filename))
141
+ compressor.recompress!
142
+ end
143
+
144
+ def execute(*ramulons)
145
+ execution = ramulons.join(' && ')
146
+ results = ramulons.map { |command| `#{command} 2&> /dev/null` }
147
+ puts results.join("\n")
148
+ results
149
+ end
150
+
151
+ def command(*args)
152
+ args.map { |arg| '"%s"' % arg }.join(' ')
153
+ end
154
+
155
+ def kzip_available?
156
+ @kzip_available ||= File.exists?('lib/kzip') && KZIP_MD5 == kzip_md5('lib/kzip')
157
+ end
158
+
159
+ def kzip_md5(kzip_filename)
160
+ require 'digest/md5'
161
+ Digest::MD5.hexdigest(File.read(kzip_filename))
162
+ end
163
+
164
+ def acquire_kzip
165
+ begin
166
+ Tempfile.open('kzipmix.tar.gz', 'wb') do |f|
167
+ begin
168
+ download_kzipmix(f)
169
+ rescue => e
170
+ raise "There was an error downloading kzipmix"
171
+ end
172
+ extracted_kzip_filename = extract_kzipmix(f)
173
+ if File.exists?(extracted_kzip_filename)
174
+ extracted_kzip_md5 = kzip_md5(extracted_zip_filename)
175
+ if KZIP_MD5 == extracted_kzip_md5
176
+ FileUtils.cp(extracted_kzip_filename, File.expand_path(File.join(ROOT, 'lib/kzip')))
177
+ else
178
+ raise "The MD5 of the downloaded kzip #{extracted_kzip_md5} did not match the expected MD5 #{KZIP_MD5}"
179
+ end
180
+ else
181
+ raise "Failed to extract kzip from the downloaded kzipmix archive"
182
+ end
183
+ end
184
+ rescue => e
185
+ raise "Unable to acquire kzip utility: #{e.message}\n#{KZIP_ABOUT}#{KZIP_INSTALL_TEXT}"
186
+ end
187
+ end
188
+
189
+ private
190
+ def download_kzipmix(f)
191
+ require 'net/http'
192
+ Net::HTTP.start(KZIP_HOST) do |http|
193
+ f.write(http.get(KZIP_PATH).body)
194
+ f.close
195
+ end
196
+ end
197
+
198
+ def extract_kzipmix(f)
199
+ fdir = Pathname.new(File.dirname(f.path))
200
+ Dir.chdir(fdir) do
201
+ execute(command('tar', 'xvzf', Pathname.new(f.path).relative_path_from(fdir), '--include', '*/kzip', '-s', '/.*kzip$/kzip/'))
202
+ end
203
+ File.join(File.dirname(f.path), 'kzip')
204
+ end
205
+ end
206
+
207
+ extend ClassMethods
208
+ end
Binary file
@@ -0,0 +1,61 @@
1
+ import java.io.*;
2
+ import java.util.zip.*;
3
+ public class SWFExtract{
4
+ public static void main(String[]args)throws Exception{
5
+ String name = args[0];
6
+ String dataOutputFileName = args[1];
7
+ String infoOutputFileName = args[2];
8
+ File file = new File(name);
9
+ DataInputStream in = new DataInputStream(new FileInputStream(file));
10
+
11
+ boolean compressed = true;
12
+ byte first = in.readByte();
13
+
14
+ if (first == 0x46) //'F'
15
+ compressed = false;
16
+ else if (first != 0x43) //'C'
17
+ noSWF(name, "Not a swf file");
18
+ if (in.readByte() != 0x57) //'W'
19
+ noSWF(name, "Not a swf file");
20
+ if (in.readByte() != 0x53) //'S'
21
+ noSWF(name, "Not a swf file");
22
+
23
+ byte version = in.readByte();
24
+ int reportedFileSize = Integer.reverseBytes(in.readInt());
25
+ int actualFileSize = (int)file.length();
26
+ if (compressed) {
27
+ System.out.println("Initially compressed");
28
+ } else {
29
+ System.out.println("Initially uncompressed");
30
+ }
31
+
32
+ DataOutputStream out=new DataOutputStream(new FileOutputStream(dataOutputFileName));
33
+ InputStream in2;
34
+ if(compressed)
35
+ in2=new InflaterInputStream(in);
36
+ else
37
+ in2=new DataInputStream(in);
38
+ byte[]data=new byte[4096];
39
+ int r;
40
+ Adler32 a32=new Adler32();
41
+ while((r=in2.read(data))!=-1){
42
+ out.write(data,0,r);
43
+ a32.update(data,0,r);
44
+ }
45
+
46
+ in2.close();
47
+ out.flush();
48
+ out.close();
49
+
50
+ DataOutputStream iout=new DataOutputStream(new FileOutputStream(infoOutputFileName));
51
+ iout.write(version);
52
+ iout.writeInt((int)a32.getValue());
53
+ iout.writeLong(file.length());
54
+ iout.flush();
55
+ iout.close();
56
+ }
57
+ private static void noSWF(String filename, String message){
58
+ System.err.printf("Could not compress swf [%s]: %s", filename, message);
59
+ System.exit(1);
60
+ }
61
+ }
Binary file
@@ -0,0 +1,55 @@
1
+ import java.io.*;
2
+ import java.util.zip.*;
3
+ import java.text.*;
4
+ public class SWFInject{
5
+ public static void main(String[]args)throws Exception{
6
+ String zipInputFileName = args[0];
7
+ String infoInputFileName = args[1];
8
+ String outputFileName = args[2];
9
+
10
+ DataInputStream iin=new DataInputStream(new FileInputStream(infoInputFileName));
11
+ byte version = iin.readByte();
12
+ int a32 = iin.readInt();
13
+ long oldLength = iin.readLong();
14
+ iin.close();
15
+
16
+ File outFile = new File(outputFileName);
17
+ DataInputStream in = new DataInputStream(new FileInputStream(zipInputFileName));
18
+ DataOutputStream out = new DataOutputStream(new FileOutputStream(outFile));
19
+
20
+ // Skip zipfile header
21
+ in.skipBytes(4 + 2 + 2 + 2 + 2 + 2 + 4);
22
+ int compressedSize = Integer.reverseBytes(in.readInt());
23
+ int uncompressedSize = Integer.reverseBytes(in.readInt());
24
+ // Skip some more
25
+ in.skipBytes(2 + 2 + 8);
26
+
27
+ byte[]data = new byte[compressedSize];
28
+ in.readFully(data);
29
+ in.close();
30
+ int newSize = 1 + 1 + 1 // Header fields CWS
31
+ + 1 // SWF version
32
+ + 4 // File length
33
+ + 2 // For DEFLATE - maximum compression sigil
34
+ + uncompressedSize
35
+ + 4; // Adler32 checksum
36
+
37
+ out.write(0x43); //'C'
38
+ out.write(0x57); //'W'
39
+ out.write(0x53); //'S'
40
+ out.write(version < 6 ? 6 : version);
41
+ out.writeInt(Integer.reverseBytes(newSize));
42
+
43
+ out.writeShort(0x78DA);
44
+ out.write(data, 0, data.length);
45
+ out.writeInt(a32);
46
+ out.flush();
47
+ out.close();
48
+
49
+ DecimalFormat byteFormat = new DecimalFormat("###,###");
50
+ long newLength = outFile.length();
51
+ System.out.printf("old Length: %10s\n",byteFormat.format(oldLength));
52
+ System.out.printf("new Length: %10s\n",byteFormat.format(newLength));
53
+ System.out.printf("saved : %10s (%.2f%%)\n",byteFormat.format(oldLength-newLength),100-(100.0/oldLength*newLength));
54
+ }
55
+ }
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swf_recompress
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 6
10
+ version: 0.0.6
11
+ platform: ruby
12
+ authors:
13
+ - Duncan Beevers
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-21 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Recompress a swf file with more aggressive DEFLATE settings
23
+ email: duncan@dweebd.com
24
+ executables:
25
+ - swf_recompress
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/kzip
32
+ - lib/swf_recompress.rb
33
+ - src/SWFExtract.class
34
+ - src/SWFExtract.java
35
+ - src/SWFInject.class
36
+ - src/SWFInject.java
37
+ - bin/swf_recompress
38
+ has_rdoc: true
39
+ homepage: http://github.com/duncanbeevers/swf_recompress
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements:
66
+ - none
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.7
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Recompress a swf file with more aggressive DEFLATE settings
72
+ test_files: []
73
+