Skip to content

Instantly share code, notes, and snippets.

@jorrizza
Created August 19, 2011 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jorrizza/292abd462a04c6fcd4d9 to your computer and use it in GitHub Desktop.
Save jorrizza/292abd462a04c6fcd4d9 to your computer and use it in GitHub Desktop.
Codebrawl Whyday

Foxes

Adds Ruby code to a PNG file. This allows foxes to run a PNG file as Ruby code, while keeping the PNG file intact and viewable. It basically just adds the "ruby" ancillary FourCC to the PNG spec and throws the code in.

If you read the PNG spec, you'll notice PNG is just like bacon.

The foxes are licensed by why the lucky stiff under the CC BY-SA 2.0 license, and are part of why's (poignant) guide to Ruby.

Usage

$ ./foxify foxes.png bacon.rb output.png
$ ./foxes output.png
Chunky bacon!!
Chunky bacon!!
2.times do
puts "Chunky bacon!!"
end
#!/usr/bin/env ruby
require File.join(File.dirname(__FILE__), 'foxes.rb')
unless ARGV.length == 1
$stderr.puts "Usage: #{$0} input.png"
exit 1
end
begin
input = File.open(ARGV[0], 'r')
foxes = Foxes.new input
rescue
$stderr.puts "Argh! #{$!}"
exit 1
end
begin
foxes.execute
rescue Foxes::NotFoxified
$stderr.puts "#{ARGV[0]} is not yet foxified!"
exit 1
end
require 'zlib'
class Foxes
PNG_HEADER = "\x89PNG\r\n\x1A\n"
class NotFoxified < RuntimeError; end
# Foxes accepts a File pointing to a PNG image.
def initialize(file)
@file = file
@file.seek 0
if @file.read(8) != PNG_HEADER
raise ArgumentError, 'Not a PNG file!'
end
# Simple way to read the PNG file.
@chunks = {}
while !@file.eof?
length, type = *@file.read(8).unpack('NA*')
data = @file.read length
crc = @file.read(4).unpack('N').first
if Zlib::crc32(type + data) != crc
raise RuntimeError, "CRC checksum error on #{type}"
end
@chunks[type] ||= []
@chunks[type] << data
end
end
# Adds to code in the file to the PNG file.
def foxify(file)
@chunks['ruby'] = [Zlib::Deflate.deflate(file.read)]
end
# Executes the Ruby code in the PNG file.
def execute
raise NotFoxified unless @chunks['ruby']
Object.class_eval(Zlib::Inflate.inflate(@chunks['ruby'].join),
@file.path)
end
# Writes the PNG data to file.
def png(file)
file.write PNG_HEADER
# Make sure IEND is actually at the end (Ruby 1.9).
iend = @chunks.delete 'IEND'
@chunks['IEND'] = iend
@chunks.each do |type, data|
data.each do |data_part|
file.write [data_part.length, type].pack('NA*')
file.write data_part
file.write [Zlib::crc32(type + data_part)].pack('N')
end
end
end
end
#!/usr/bin/env ruby
require File.join(File.dirname(__FILE__), 'foxes.rb')
unless ARGV.length == 3
$stderr.puts "Usage: #{$0} input.png code.rb output.png"
exit 1
end
begin
modes = %w{r r w+}
input, code, output = *ARGV.map { |a| File.open(a, modes.shift) }
foxes = Foxes.new input
foxes.foxify code
foxes.png output
rescue
$stderr.puts "Argh! #{$!}"
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment