Skip to content

Instantly share code, notes, and snippets.

@mwgamera
Created April 26, 2020 04:32

Revisions

  1. mwgamera created this gist Apr 26, 2020.
    94 changes: 94 additions & 0 deletions swfjpgfix.pl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    #!/usr/bin/env perl
    # Fix some JPG data in SWF file so that swfrender doesn't trip on it.
    # klg, Apr 2020
    use strict;
    use warnings;
    use IO::Compress::Deflate;
    use IO::Uncompress::Inflate;

    my $src = \*STDIN;
    my $dst = \*STDOUT;

    open $src, '<', shift or die $! if @ARGV;

    # read the uncompressed part of the header
    read $src, $_, 8 or die $!;

    my ($magic, $version, $size) = unpack 'A3CV';
    die 'Not an SWF file.' unless $magic =~ /^[FCZ]WS$/;
    die 'LZMA not supported' if $magic eq 'ZWS';

    if ($magic eq 'CWS') {
    $src = IO::Uncompress::Inflate->new($src)
    or die "inflate: $!";
    }

    print $dst pack 'A3CV', 'CWS', $version, $size or die $!;
    $dst = IO::Compress::Deflate->new($dst) or die "deflate: $!";

    # read the rest of a header and copy it out
    read $src, $_, 1 or die $!;
    print $dst $_ or die $!;
    read $src, $_, (((ord >> 3) * 4 + 5 - 1) >> 3) + 4 or die $!;
    print $dst $_ or die $!;

    # read and copy all tags, modifying some
    until (eof $src) {
    read $src, $_, 2 or die $!;
    print $dst $_ or die $!;
    my $tag = unpack 'v';
    my $len = $tag & 0x3f;
    if ($len == 0x3f) {
    read $src, $_, 4, or die $!;
    print $dst $_ or die $!;
    $len = unpack 'V';
    }
    $tag >>= 6;
    if ($len) {
    read $src, $_, $len or die $!;
    $_ = fixjpgtags($tag, $_);
    print $dst $_ or die $!;
    }
    }

    sub fixjpgtags {
    my ($tag, $data) = @_;
    if ($tag == 6) { # DefineBits
    substr($data, 2) = fixjpgdata(substr $data, 2);
    } elsif ($tag == 8) { # JPEGTables
    $data = fixjpgdata($data);
    } elsif ($tag == 21) { # DefineBitsJPEG2
    substr($data, 2) = fixjpgdata(substr $data, 2);
    } elsif ($tag == 35) { # DefineBitsJPEG3
    my $len = unpack 'x2V', $data;
    substr($data, 6, $len) = fixjpgdata(substr $data, 6, $len);
    }
    return $data;
    }

    =for comment
    SWF File Format Specification says:
    "The data in this tag begins with the JPEG SOI marker 0xFF, 0xD8 and ends with
    the EOI marker 0xFF, 0xD9. Before version 8 of the SWF file format, SWF files
    could contain an erroneous header of 0xFF, 0xD9, 0xFF, 0xD8 before the JPEG SOI
    marker. In addition to specifying JPEG data, DefineBitsJPEG2 can also contain
    PNG image data and non-animated GIF89a."
    =for comment
    The offending files contained valid JFIF prefixed by EOI, i.e. data that
    started with FF D9 FF D8 FF E0... Swftools would remove FF D9 FF D8 and
    then complain that the rest starts with FF E0 so it does not look like JPEG.
    Normalize things to start with single SOI, and pad with zeros to keep sizes
    and alignment unchanged.
    =cut

    sub fixjpgdata {
    local ($_) = @_;
    return $_ if /\A\x89PNG|\AGIF/;
    m/\A((\xff[\xd8\xd9])+)/ or die 'unexpected data in tag';
    my $L = length $1;
    return "\xff\xd8" . substr($_, $L) . "\0" x ($L-2);
    }

    1;