swf_recompress 0.0.6

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.
@@ -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
+