Concatenate a Crystal binary with a Zip file

I’ve seen a few examples of binaries concatenated with zip files (the three line single binary compiler free blog or https://code.rosaelefanten.org/zb/dir?ci=tip) and I was wondering if it can be done in Crystal too.

I expected it to just work, but seems it doesn’t.

$ echo foo >a.txt             
$ echo bar >b.txt             
$ zip archive.zip *.txt
  adding: a.txt (stored 0%)
  adding: b.txt (stored 0%)

$ cat >test.cr                                                                       
require "compress/zip"

Compress::Zip::File.open("test") do |zip|
  zip["b.txt"].open do |io|
    p io.gets_to_end
  end
end
$ cr build test.cr          
Using compiled compiler at ~/repos/crystal/.build/crystal

$ cat archive.zip >>test

$ ./test 
Unhandled exception: Expected end of central directory header signature, not 0xd5d00000 (Compress::Zip::Error)
  from ~/repos/crystal/src/compress/zip/file.cr:121:7 in 'read_directory_end'
  from ~/repos/crystal/src/compress/zip/file.cr:40:38 in 'initialize'
  from ~/repos/crystal/src/compress/zip/file.cr:38:3 in 'new:sync_close'
  from ~/repos/crystal/src/compress/zip/file.cr:48:5 in 'new'
  from ~/repos/crystal/src/compress/zip/file.cr:61:5 in '__crystal_main'
  from ~/repos/crystal/src/crystal/main.cr:115:5 in 'main_user_code'
  from ~/repos/crystal/src/crystal/main.cr:101:7 in 'main'
  from ~/repos/crystal/src/crystal/main.cr:127:3 in 'main'
  from /usr/lib/libc.so.6 in '??'
  from /usr/lib/libc.so.6 in '__libc_start_main'
  from ./test in '_start'
  from ???

Probably lines such as this one would need to somehow seek relative to the end of the IO, rather than the beginning of the IO.

1 Like

One approach:

require "datapack"
require "compress/zip"

Datapack.add "./foo.zip"

Compress::Zip::File.open(IO::Memory.new(Datapack::Data.find("foo.zip").data)) do |file|
  file.entries.each do |entry|
    p entry.filename
  end
end

This uses this shard:

3 Likes