claus / as3swf

Low level Actionscript 3 library to parse, create, modify and publish SWF files.
MIT License
525 stars 127 forks source link

Added LZMA support #23

Closed DmitriyYukhanov closed 12 years ago

DmitriyYukhanov commented 12 years ago

Let's make an another try!)

claus commented 12 years ago

Cheers, mate!

DmitriyYukhanov commented 12 years ago

Glad to help! Hope it will work, haha)

claus commented 12 years ago

OK this works:

public function swfUncompress(compressionMethod:String, uncompressedLength:uint = 0):void {
    var pos:uint = position;
    var ba:ByteArray = new ByteArray();

    if(compressionMethod == SWF.COMPRESSION_METHOD_ZLIB) {
        readBytes(ba);
        ba.position = 0;
        ba.uncompress();
    } else if(compressionMethod == SWF.COMPRESSION_METHOD_LZMA) {
        // Write LZMA properties
        for(var i:uint = 0; i < 5; i++) {
            ba.writeByte(this[i + 12]);
        }
        // Write uncompressed length (64 bit)
        ba.endian = Endian.LITTLE_ENDIAN;
        ba.writeUnsignedInt(uncompressedLength - 8);
        ba.writeUnsignedInt(0);
        // Write compressed data
        position = 17;
        readBytes(ba, 13);
        // Uncompress
        ba.position = 0;
        ba.uncompress(compressionMethod);
    } else {
        throw(new Error("Unknown compression method: " + compressionMethod));
    }

    length = position = pos;
    writeBytes(ba);
    position = pos;
}
DmitriyYukhanov commented 12 years ago

Hey, that's great! We need a renewed SWF spec to make working code from the start... Would you push those changes in the GitHub repo?

claus commented 12 years ago

Yeah i've been nagging Thibault for an updated SWF spec like forever now ;) We're at 17, the spec is at 10. I guess not that much changed, but anyways.

I'll commit once i feel confident that this actually works correctly, and once swfCompress is working too.

claus commented 12 years ago

For the record:

The uncompressed part of a compressed SWF is always (first 8 bytes): bytes 0-3: CWS/ZWS + version bytes 4-7: Uncompressed size

What follows is compressed data until end of file.

A LZMA compressed SWF looks like this (example):

0000 5A 57 53 0F   // ZWS + Version 15
0004 DF 52 00 00   // Uncompressed size: 21215
// ZWS-specific:
0008 94 3B 00 00   // Compressed size: 15252
000C 5D 00 00 00 01   // LZMA Properties
0011 00 3B FF FC A6 14 16 5A ...   // LZMA Compressed Data (until EOF)

To convert the compressed data into something that can be uncompressed by ByteArray.uncompress (7z), the compressed size has to be thrown away, and the uncompressed size (padded to 64 bits) has to be injected after the LZMA properties.

As the uncompressed size in SWF refers to the size of the entire SWF (including the uncompressed header of 8 bytes), but uncompressed size in the LZMA header refers to only the LZMA data, we need to subtract 8.

0000 5D 00 00 00 01   // LZMA Properties
0005 D7 52 00 00 00 00 00 00   // Uncompressed size: 21207 (64 bit)
000D 00 3B FF FC A6 14 16 5A ...   // LZMA Compressed Data (until EOF)
DmitriyYukhanov commented 12 years ago

Thanks, good to know! It should be in docs as example somewhere in ByteArray.uncompress() description. Or even better, it should be done internally, so we just call ByteArray.uncompress() on everything after ZWS+Version and voila!