use strict; use Compress::Zlib; # Load templates for quine portions of the code. These are stored as # is in a 2-element array. my $template = '@o=("'; my $file; open $file, "< templates/rikka1final.txt" or die $!; my $text = join '', <$file>; close $file; $template .= $text . '","'; open $file, "< templates/rikka2final.txt" or die $!; $text = join '', <$file>; close $file; $template .= $text . '");'; # Decoder, selects one of two templates and fill Xs with characters # from compressed+uuencoded data. my $code = q% $t = $o[$O]; $s = 0; $s++ foreach ($t =~ /X/g); while( length($q) < $s - 129 ) { $i = int(rand(length($q))); $q = substr($q, 0, $i) . chr(34 + int(rand(24))) . substr($q, $i); } $q = '$O=' . ($O ^ 1) . ';useCompress::Zlib;$q=q!' . $q . '!;$q=~s/["-9\s]//gs;$l=uncompress(unpack"u",join"",(map{chr($_==124?67:$_-25)}unpack"C*",$q));eval$l;'; @i = unpack 'C*', $q; foreach $x (unpack 'C*', $t) { $x = chr($x); print ("X" eq $x ? chr(shift @i) : $x); } %; # Remove whitespaces from decode stub to save space. $code =~ s/\s//gs; # Compress+uuencode the decode stub. my $encoded = pack 'u', compress($template . $code); $encoded =~ s/\s//gs; # Shift the uuencoded characters. This is so that we can use some of # the lower ASCII characters for some extra highlights in the output # code. Because backslashes are a pain to encode, we convert them to # pipe characters here, and covert them back before decode. (We have # to make sure that two backslashes never appear consecutively, # otherwise they are folded into one). $encoded = join '', map {chr($_ == 67 ? 124 : $_ + 25)} unpack 'C*', $encoded; $encoded =~ s/\\/#/g; # Load template for the entry program (outside the two-entry quine loop). open $file, "< templates/rikka0final.txt" or die; $template = join '', <$file>; close $file; # Decoder header and footer, outside the compressed+uuencoded portion # of the program. These contains a few extra bits that are not in the # $encoded string above, mainly to deal with spacing issues in the # template. my $header = 'useCompress::Zlib;$O=0;$q=q!'; my $footer = '!;' . '$q=~s/["-9\s]//gs;' . '$l=uncompress(' . '(q{}x0).' . 'unpack"u",' . 'join' . '#c2012' . '"",(map{chr($_==124?67:$_-25)}unpack"C*",$q));' . 'eval$l;'; # Fill in random characters until there are enough data to cover all # Xs in the template. These will be removed by the ["-9] part of the # regular expression. In practice this is not used for the first # program, since we portioned the Xs exactly to match the number of # code characters. This was done so that the initial program remains # deterministic. I am keeping this code and comment here for # illustrative purposes only, since it matches what happens in the # inner programs. my $encode_size = 0; foreach (unpack 'C*', $template) { if( chr($_) eq 'X' ) { $encode_size++; } } while( length($encoded) < $encode_size - length($header) - length($footer) ) { my $p = int(rand(length($encoded))); $encoded = substr($encoded, 0, $p) . chr(34 + int(rand(24))) . substr($encoded, $p); } # Merge code and template together to stdout. my @i = unpack 'C*', $header . $encoded . $footer; foreach my $j (unpack 'C*', $template) { $j = chr($j); if( $j eq 'X' ) { print chr(shift(@i)); } else { print $j; } }