194 lines
5.6 KiB
Haxe
194 lines
5.6 KiB
Haxe
|
/*
|
||
|
* Copyright (C)2005-2019 Haxe Foundation
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
package haxe.zip;
|
||
|
|
||
|
import haxe.ds.List;
|
||
|
|
||
|
class Writer {
|
||
|
/**
|
||
|
The next constant is required for computing the Central
|
||
|
Directory Record(CDR) size. CDR consists of some fields
|
||
|
of constant size and a filename. Constant represents
|
||
|
total length of all fields with constant size for each
|
||
|
file in archive
|
||
|
**/
|
||
|
inline static var CENTRAL_DIRECTORY_RECORD_FIELDS_SIZE = 46;
|
||
|
|
||
|
/**
|
||
|
The following constant is the total size of all fields
|
||
|
of Local File Header. It's required for calculating
|
||
|
offset of start of central directory record
|
||
|
**/
|
||
|
inline static var LOCAL_FILE_HEADER_FIELDS_SIZE = 30;
|
||
|
|
||
|
var o:haxe.io.Output;
|
||
|
var files:List<{
|
||
|
name:String,
|
||
|
compressed:Bool,
|
||
|
clen:Int,
|
||
|
size:Int,
|
||
|
crc:Int,
|
||
|
date:Date,
|
||
|
fields:haxe.io.Bytes
|
||
|
}>;
|
||
|
|
||
|
public function new(o:haxe.io.Output) {
|
||
|
this.o = o;
|
||
|
files = new List();
|
||
|
}
|
||
|
|
||
|
function writeZipDate(date:Date) {
|
||
|
var hour = date.getHours();
|
||
|
var min = date.getMinutes();
|
||
|
var sec = date.getSeconds() >> 1;
|
||
|
o.writeUInt16((hour << 11) | (min << 5) | sec);
|
||
|
var year = date.getFullYear() - 1980;
|
||
|
var month = date.getMonth() + 1;
|
||
|
var day = date.getDate();
|
||
|
o.writeUInt16((year << 9) | (month << 5) | day);
|
||
|
}
|
||
|
|
||
|
public function writeEntryHeader(f:Entry) {
|
||
|
var o = this.o;
|
||
|
var flags = 0;
|
||
|
if (f.extraFields != null) {
|
||
|
for (e in f.extraFields)
|
||
|
switch (e) {
|
||
|
case FUtf8:
|
||
|
flags |= 0x800;
|
||
|
default:
|
||
|
}
|
||
|
}
|
||
|
o.writeInt32(0x04034B50);
|
||
|
o.writeUInt16(0x0014); // version
|
||
|
o.writeUInt16(flags); // flags
|
||
|
if (f.data == null) {
|
||
|
f.fileSize = 0;
|
||
|
f.dataSize = 0;
|
||
|
f.crc32 = 0;
|
||
|
f.compressed = false;
|
||
|
f.data = haxe.io.Bytes.alloc(0);
|
||
|
} else {
|
||
|
if (f.crc32 == null) {
|
||
|
if (f.compressed)
|
||
|
throw "CRC32 must be processed before compression";
|
||
|
f.crc32 = haxe.crypto.Crc32.make(f.data);
|
||
|
}
|
||
|
if (!f.compressed)
|
||
|
f.fileSize = f.data.length;
|
||
|
f.dataSize = f.data.length;
|
||
|
}
|
||
|
o.writeUInt16(f.compressed ? 8 : 0);
|
||
|
writeZipDate(f.fileTime);
|
||
|
o.writeInt32(f.crc32);
|
||
|
o.writeInt32(f.dataSize);
|
||
|
o.writeInt32(f.fileSize);
|
||
|
o.writeUInt16(f.fileName.length);
|
||
|
var e = new haxe.io.BytesOutput();
|
||
|
if (f.extraFields != null) {
|
||
|
for (f in f.extraFields)
|
||
|
switch (f) {
|
||
|
case FInfoZipUnicodePath(name, crc):
|
||
|
var namebytes = haxe.io.Bytes.ofString(name);
|
||
|
e.writeUInt16(0x7075);
|
||
|
e.writeUInt16(namebytes.length + 5);
|
||
|
e.writeByte(1); // version
|
||
|
e.writeInt32(crc);
|
||
|
e.write(namebytes);
|
||
|
case FUnknown(tag, bytes):
|
||
|
e.writeUInt16(tag);
|
||
|
e.writeUInt16(bytes.length);
|
||
|
e.write(bytes);
|
||
|
case FUtf8:
|
||
|
// nothing
|
||
|
}
|
||
|
}
|
||
|
var ebytes = e.getBytes();
|
||
|
o.writeUInt16(ebytes.length);
|
||
|
o.writeString(f.fileName);
|
||
|
o.write(ebytes);
|
||
|
files.add({
|
||
|
name: f.fileName,
|
||
|
compressed: f.compressed,
|
||
|
clen: f.data.length,
|
||
|
size: f.fileSize,
|
||
|
crc: f.crc32,
|
||
|
date: f.fileTime,
|
||
|
fields: ebytes
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public function write(files:List<Entry>) {
|
||
|
for (f in files) {
|
||
|
writeEntryHeader(f);
|
||
|
o.writeFullBytes(f.data, 0, f.data.length);
|
||
|
}
|
||
|
writeCDR();
|
||
|
}
|
||
|
|
||
|
public function writeCDR() {
|
||
|
var cdr_size = 0;
|
||
|
var cdr_offset = 0;
|
||
|
for (f in files) {
|
||
|
var namelen = f.name.length;
|
||
|
var extraFieldsLength = f.fields.length;
|
||
|
o.writeInt32(0x02014B50); // header
|
||
|
o.writeUInt16(0x0014); // version made-by
|
||
|
o.writeUInt16(0x0014); // version
|
||
|
o.writeUInt16(0); // flags
|
||
|
o.writeUInt16(f.compressed ? 8 : 0);
|
||
|
writeZipDate(f.date);
|
||
|
o.writeInt32(f.crc);
|
||
|
o.writeInt32(f.clen);
|
||
|
o.writeInt32(f.size);
|
||
|
o.writeUInt16(namelen);
|
||
|
o.writeUInt16(extraFieldsLength);
|
||
|
o.writeUInt16(0); // comment length always 0
|
||
|
o.writeUInt16(0); // disk number start
|
||
|
o.writeUInt16(0); // internal file attributes
|
||
|
o.writeInt32(0); // external file attributes
|
||
|
o.writeInt32(cdr_offset); // relative offset of local header
|
||
|
o.writeString(f.name);
|
||
|
o.write(f.fields);
|
||
|
cdr_size += CENTRAL_DIRECTORY_RECORD_FIELDS_SIZE + namelen + extraFieldsLength;
|
||
|
cdr_offset += LOCAL_FILE_HEADER_FIELDS_SIZE + namelen + extraFieldsLength + f.clen;
|
||
|
}
|
||
|
// end of central dir signature
|
||
|
o.writeInt32(0x06054B50);
|
||
|
// number of this disk
|
||
|
o.writeUInt16(0);
|
||
|
// number of the disk with the start of the central directory
|
||
|
o.writeUInt16(0);
|
||
|
// total number of entries in the central directory on this disk
|
||
|
o.writeUInt16(files.length);
|
||
|
// total number of entries in the central directory
|
||
|
o.writeUInt16(files.length);
|
||
|
// size of the central directory record
|
||
|
o.writeInt32(cdr_size);
|
||
|
// offset of start of central directory with respect to the starting disk number
|
||
|
o.writeInt32(cdr_offset);
|
||
|
// .ZIP file comment length
|
||
|
o.writeUInt16(0);
|
||
|
}
|
||
|
}
|