123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
- Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
- MODULE Zip; (** AUTHOR "Stefan Walthert"; PURPOSE "ZIP codec" **)
- IMPORT
- Streams, Files, Zlib, ZlibReaders, ZlibWriters;
- CONST
- (** result codes **)
- Ok* = 0; (** operation on zip-file was successful **)
- FileError* = -1; (** file not found **)
- NotZipArchiveError* = -2; (** file is not in zip format **)
- EntryNotFound* = -3; (** specified file was not found in zip-file **)
- EntryAlreadyExists* = -4; (** file is already stored in zip-file -> can not add specified file to zip-file **)
- NotSupportedError* = -5; (** can not extract specified file (compression method not supported/file is encrypted) **)
- DataError* = -6; (** file is corrupted **)
- BadName* = -7; (** bad file name *)
- ReaderError* = -8; (** e.g. Reader not opened before Read **)
- (** compression levels **)
- DefaultCompression* = ZlibWriters.DefaultCompression;
- NoCompression* = ZlibWriters.NoCompression;
- BestSpeed* = ZlibWriters.BestSpeed;
- BestCompression* = ZlibWriters.BestCompression;
- (** compression strategies **)
- DefaultStrategy* = ZlibWriters.DefaultStrategy;
- Filtered* = ZlibWriters.Filtered;
- HuffmanOnly* = ZlibWriters.HuffmanOnly;
- (* support *)
- Supported = 0; (* can extract file *)
- IncompatibleVersion = 1; (* version needed to extract < PKZIP 1.00 *)
- Encrypted = 2; (* file is encrypted *)
- UnsupCompMethod = 3; (* file not stored or deflated *)
- Stored = 0; (* file is stored (no compression) *)
- Deflated = 8; (* file is deflated *)
- SupportedCompMethods = {Stored, Deflated};
- CompatibleVersions = 1; (* versions >= CompatibleVersions are supported *)
- (* headers *)
- LocalFileHeaderSignature = 04034B50H;
- CentralFileHeaderSignature = 02014B50H;
- EndOfCentralDirSignature = 06054B50H;
- TYPE
- Entry* = POINTER TO EntryDesc; (** description of a file stored in the zip-archive **)
- EntryDesc* = RECORD
- name-: ARRAY 256 OF CHAR; (** name of file stored in the zip-archive **)
- method: INTEGER; (* compression method *)
- time-, date-: LONGINT; (** (Oberon) time and date when file was last modified **)
- crc32: LONGINT; (* checksum of uncompressed file data *)
- compSize-, uncompSize-: LONGINT; (** size of compressed / uncompressed file **)
- intFileAttr: INTEGER; (* internal file attributes, not used in this implementation *)
- extFileAttr: LONGINT; (* external file attributes, not used in this implementation *)
- extraField (* for future expansions *), comment-: POINTER TO ARRAY OF CHAR; (** comment for this file **)
- genPurpBitFlag: INTEGER;
- support: SHORTINT;
- dataDescriptor: BOOLEAN; (* if set, data descriptor after (compressed) file data *)
- offsetLocal: LONGINT; (* offset of file header in central directory *)
- offsetFileData: LONGINT; (* offset of (compressed) file data *)
- offsetCentralDir: LONGINT; (* offset of local file header *)
- next: Entry
- END;
- Archive* = POINTER TO ArchiveDesc; (** description of a zipfile **)
- ArchiveDesc* = RECORD
- nofEntries-: INTEGER; (** total number of files stored in the zipfile **)
- comment-: POINTER TO ARRAY OF CHAR; (** comment for zipfile **)
- file-: Files.File; (* pointer to the according zip-file *)
- offset: LONGINT; (* offset of end of central dir record *)
- firstEntry, lastEntry: Entry (* first and last Entry of Archive *)
- END;
- Reader* = POINTER TO ReaderDesc;
- ReaderDesc* = RECORD (** structure for reading from a zip-file into a buffer **)
- res-: LONGINT; (** result of last operation **)
- open: BOOLEAN;
- ent: Entry
- END;
- UncompReader = POINTER TO UncompReaderDesc;
- UncompReaderDesc = RECORD (ReaderDesc) (* structur for reading from a uncompressed entry *)
- fr: Files.Rider;
- crc32: LONGINT; (* crc32 of uncomressed data *)
- END;
- DefReader = POINTER TO DefReaderDesc;
- DefReaderDesc = RECORD (ReaderDesc) (* structure for reading from a deflated entry *)
- zr: ZlibReaders.Reader
- END;
- PROCEDURE ShowError*(errorCode : WORD; out : Streams.Writer);
- BEGIN
- ASSERT(out # NIL);
- CASE errorCode OF
- |Ok: out.String("No Error");
- |FileError: out.String("File not found");
- |NotZipArchiveError: out.String("File is not in zip format");
- |EntryNotFound: out.String("File not found in zip archive");
- |EntryAlreadyExists: out.String("File already present in zip archive");
- |NotSupportedError: out.String("Compression method not supported or file encrypted");
- |DataError: out.String("File is corrupted");
- |BadName: out.String("Bad file name");
- |ReaderError: out.String("Reader Error");
- ELSE
- out.String("Unkown error, res: "); out.Int(errorCode, 0);
- END;
- END ShowError;
- (* length of str *)
- PROCEDURE StringLength(CONST str: ARRAY OF CHAR): LONGINT;
- VAR i, l: LONGINT;
- BEGIN
- l := LEN(str); i := 0;
- WHILE (i < l) & (str[i] # 0X) DO
- INC(i)
- END;
- RETURN i
- END StringLength;
- (* Converts Oberon time into MS-DOS time *)
- PROCEDURE OberonToDosTime(t: LONGINT): INTEGER;
- BEGIN
- RETURN SHORT(t DIV 1000H MOD 20H * 800H + t DIV 40H MOD 40H * 20H + t MOD 40H DIV 2)
- END OberonToDosTime;
- (* Converts Oberon date into MS-DOS time *)
- PROCEDURE OberonToDosDate(d: LONGINT): INTEGER;
- BEGIN
- RETURN SHORT((d DIV 200H + 1900 - 1980) * 200H + d MOD 200H)
- END OberonToDosDate;
- (* Converts MS-DOS time into Oberon time *)
- PROCEDURE DosToOberonTime(t: INTEGER): LONGINT;
- BEGIN
- RETURN LONG(t) DIV 800H MOD 20H * 1000H + t DIV 20H MOD 40H * 40H + t MOD 20H * 2
- END DosToOberonTime;
- (* Converts MS-DOS date into Oberon date *)
- PROCEDURE DosToOberonDate(d: INTEGER): LONGINT;
- BEGIN
- RETURN (LONG(d) DIV 200H MOD 80H + 1980 - 1900) * 200H + d MOD 200H
- END DosToOberonDate;
- (* Copy len bytes from src to dst; if compCRC32 is set, then the crc 32-checksum is computed *)
- PROCEDURE Copy(VAR src, dst: Files.Rider; len: LONGINT; compCRC32: BOOLEAN; VAR crc32: LONGINT);
- CONST
- BufSize = 4000H;
- VAR
- n: LONGINT;
- buf: ARRAY BufSize OF CHAR;
- BEGIN
- IF compCRC32 THEN crc32 := Zlib.CRC32(0, buf, -1, -1) END;
- REPEAT
- IF len < BufSize THEN n := len
- ELSE n := BufSize
- END;
- src.file.ReadBytes(src, buf, 0, n);
- IF compCRC32 THEN crc32 := Zlib.CRC32(crc32, buf, 0, n - src.res) END;
- dst.file.WriteBytes(dst, buf, 0, n - src.res);
- DEC(len, n)
- UNTIL len = 0
- END Copy;
- (* Reads an Entry, r must be at the start of a file header; returns NIL if read was not successful *)
- PROCEDURE ReadEntry(VAR r: Files.Rider): Entry;
- VAR
- ent: Entry;
- intDummy, nameLen, extraLen, commentLen: INTEGER;
- longDummy: LONGINT;
- BEGIN
- Files.ReadLInt(r, longDummy);
- IF longDummy = CentralFileHeaderSignature THEN
- NEW(ent);
- ent.offsetCentralDir := r.file.Pos(r) - 4;
- ent.support := 0;
- Files.ReadInt(r, intDummy); (* version made by *)
- Files.ReadInt(r, intDummy); (* version needed to extract *)
- IF (intDummy MOD 100H) / 10 < CompatibleVersions THEN
- ent.support := IncompatibleVersion
- END;
- Files.ReadInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
- IF ODD(ent.genPurpBitFlag) THEN
- ent.support := Encrypted (* bit 0: if set, file encrypted *)
- END;
- ent.dataDescriptor := ODD(intDummy DIV 8); (* bit 3: data descriptor after (compressed) file data *)
- Files.ReadInt(r, ent.method); (* compression method *)
- IF (ent.support = Supported) & ~(ent.method IN SupportedCompMethods) THEN
- ent.support := UnsupCompMethod
- END;
- Files.ReadInt(r, intDummy); ent.time := DosToOberonTime(intDummy); (* last mod file time *)
- Files.ReadInt(r, intDummy); ent.date := DosToOberonDate(intDummy); (* last mod file date *)
- Files.ReadLInt(r, ent.crc32); (* crc-32 *)
- Files.ReadLInt(r, ent.compSize); (* compressed size *)
- Files.ReadLInt(r, ent.uncompSize); (* uncompressed size *)
- Files.ReadInt(r, nameLen); (* filename length *)
- Files.ReadInt(r, extraLen); (* extra field length *)
- Files.ReadInt(r, commentLen); (* file comment length *)
- Files.ReadInt(r, intDummy); (* disk number start *)
- Files.ReadInt(r, ent.intFileAttr); (* internal file attributes *)
- Files.ReadLInt(r, ent.extFileAttr); (* external file attributes *)
- Files.ReadLInt(r, ent.offsetLocal); (* relative offset of local header *)
- r.file.ReadBytes(r, ent.name, 0, nameLen); (* filename *)
- IF extraLen # 0 THEN
- NEW(ent.extraField, extraLen);
- r.file.ReadBytes(r, ent.extraField^, 0, extraLen) (* extra field *)
- END;
- IF commentLen > 0 THEN
- NEW(ent.comment, commentLen);
- r.file.ReadBytes(r, ent.comment^, 0, commentLen) (* file comment *)
- END;
- (* read extra field length in the local file header (can be different from extra field length stored in the file header...) *)
- longDummy := r.file.Pos(r); (* store actual position of file reader *)
- r.file.Set(r, ent.offsetLocal + 28); (* set r to position of extra field length in local file header *)
- Files.ReadInt(r, extraLen); (* extra field length *)
- ent.offsetFileData := ent.offsetLocal + 30 + nameLen + extraLen; (* compute offset of file data *)
- r.file.Set(r, longDummy); (* set position of file reader to previous position *)
- IF r.eof THEN (* if file is a zip-archive, r is not at end of file *)
- ent := NIL
- END
- END;
- RETURN ent;
- END ReadEntry;
- (* Writes a local file header *)
- PROCEDURE WriteLocalFileHeader(ent: Entry; VAR r: Files.Rider);
- BEGIN
- Files.WriteLInt(r, LocalFileHeaderSignature); (* local file header signature *)
- Files.WriteInt(r, CompatibleVersions * 10); (* version needed to extract *)
- Files.WriteInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
- Files.WriteInt(r, ent.method); (* compression method *)
- Files.WriteInt(r, OberonToDosTime(ent.time)); (* last mod file time *)
- Files.WriteInt(r, OberonToDosDate(ent.date)); (* last mod file date *)
- Files.WriteLInt(r, ent.crc32); (* crc-32 *)
- Files.WriteLInt(r, ent.compSize); (* compressed size *)
- Files.WriteLInt(r, ent.uncompSize); (* uncompressed size *)
- Files.WriteInt(r, SHORT(StringLength(ent.name))); (* filename length *)
- IF ent.extraField # NIL THEN
- Files.WriteInt(r, SHORT(LEN(ent.extraField^))) (* extra field length *)
- ELSE
- Files.WriteInt(r, 0)
- END;
- r.file.WriteBytes(r, ent.name, 0, StringLength(ent.name)); (* filename *)
- IF ent.extraField # NIL THEN
- r.file.WriteBytes(r, ent.extraField^, 0, LEN(ent.extraField^)) (* extra field *)
- END
- END WriteLocalFileHeader;
- (* Writes file header in central directory, updates ent.offsetCentralDir *)
- PROCEDURE WriteFileHeader(ent: Entry; VAR r: Files.Rider);
- BEGIN
- ent.offsetCentralDir := r.file.Pos(r);
- Files.WriteLInt(r, CentralFileHeaderSignature); (* central file header signature *)
- Files.WriteInt(r, CompatibleVersions * 10); (* version made by *)
- Files.WriteInt(r, CompatibleVersions * 10); (* version needed to extract *)
- Files.WriteInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
- Files.WriteInt(r, ent.method); (* compression method *)
- Files.WriteInt(r, OberonToDosTime(ent.time)); (* last mod file time *)
- Files.WriteInt(r, OberonToDosDate(ent.date)); (* last mod file date *)
- Files.WriteLInt(r, ent.crc32); (* crc-32 *)
- Files.WriteLInt(r, ent.compSize); (* compressed size *)
- Files.WriteLInt(r, ent.uncompSize); (* uncompressed size *)
- Files.WriteInt(r, SHORT(StringLength(ent.name))); (* filename length *)
- IF ent.extraField = NIL THEN
- Files.WriteInt(r, 0)
- ELSE
- Files.WriteInt(r, SHORT(LEN(ent.extraField^))); (* extra field length *)
- END;
- IF ent.comment = NIL THEN
- Files.WriteInt(r, 0)
- ELSE
- Files.WriteInt(r, SHORT(LEN(ent.comment^))); (* file comment length *)
- END;
- Files.WriteInt(r, 0); (* disk number start *)
- Files.WriteInt(r, ent.intFileAttr); (* internal file attributes *)
- Files.WriteLInt(r, ent.extFileAttr); (* external file attributes *)
- Files.WriteLInt(r, ent.offsetLocal); (* relative offset of local header *)
- r.file.WriteBytes(r, ent.name, 0, StringLength(ent.name)); (* filename *)
- IF ent.extraField # NIL THEN
- r.file.WriteBytes(r, ent.extraField^, 0, LEN(ent.extraField^)) (* extra field *)
- END;
- IF ent.comment # NIL THEN
- r.file.WriteBytes(r, ent.comment^, 0, LEN(ent.comment^)) (* file comment *)
- END
- END WriteFileHeader;
- (* Writes end of central directory record *)
- PROCEDURE WriteEndOfCentDir(arc: Archive; VAR r: Files.Rider);
- BEGIN
- Files.WriteLInt(r, EndOfCentralDirSignature); (* end of central dir signature *)
- Files.WriteInt(r, 0); (* number of this disk *)
- Files.WriteInt(r, 0); (* number of the disk with the start of the central directory *)
- Files.WriteInt(r, arc.nofEntries); (* total number of entries in the central dir on this disk *)
- Files.WriteInt(r, arc.nofEntries); (* total number of entries in the central dir *)
- IF arc.firstEntry # NIL THEN
- Files.WriteLInt(r, arc.offset - arc.firstEntry.offsetCentralDir) (* size of the central directory (without end of central dir record) *)
- ELSE
- Files.WriteLInt(r, 0)
- END;
- IF arc.firstEntry = NIL THEN
- Files.WriteLInt(r, arc.offset) (* offset of start of central directory with respect to the starting disk number *)
- ELSE
- Files.WriteLInt(r, arc.firstEntry.offsetCentralDir) (* offset of start of central directory with respect to the starting disk number *)
- END;
- IF arc.comment = NIL THEN
- Files.WriteInt(r, 0) (* zipfile comment length *)
- ELSE
- Files.WriteInt(r, SHORT(LEN(arc.comment^))); (* zipfile comment length *)
- r.file.WriteBytes(r, arc.comment^, 0, LEN(arc.comment^)) (* zipfile comment *)
- END
- END WriteEndOfCentDir;
- (* Writes central directory + end of central directory record, updates arc.offset and offsetCentralDir of entries *)
- PROCEDURE WriteCentralDirectory(arc: Archive; VAR r: Files.Rider);
- VAR
- ent: Entry;
- BEGIN
- ent := arc.firstEntry;
- WHILE ent # NIL DO
- WriteFileHeader(ent, r);
- ent := ent.next
- END;
- arc.offset := r.file.Pos(r);
- WriteEndOfCentDir(arc, r)
- END WriteCentralDirectory;
- (** Returns an Archive data structure corresponding to the specified zipfile;
- possible results:
- - Ok: operation was successful
- - FileError: file with specified name does not exist
- - NotZipArchiveError: file is not a correct zipfile **)
- PROCEDURE OpenArchive*(CONST name: ARRAY OF CHAR; VAR res: WORD): Archive;
- VAR
- arc: Archive;
- ent: Entry;
- f: Files.File;
- r: Files.Rider;
- longDummy: LONGINT;
- intDummy: INTEGER;
- BEGIN
- res := Ok;
- f := Files.Old(name);
- IF f = NIL THEN
- res := FileError
- ELSIF f.Length() < 22 THEN
- res := NotZipArchiveError
- ELSE
- longDummy := 0;
- f.Set(r, f.Length() - 17);
- WHILE (longDummy # EndOfCentralDirSignature) & (r.file.Pos(r) > 4) DO
- f.Set(r, f.Pos(r) - 5);
- Files.ReadLInt(r, longDummy)
- END;
- IF longDummy # EndOfCentralDirSignature THEN
- res := NotZipArchiveError
- ELSE
- NEW(arc);
- arc.file := f;
- arc.offset := f.Pos(r) - 4;
- Files.ReadInt(r, intDummy); (* number of this disk *)
- Files.ReadInt(r, intDummy); (* number of the disk with the start of the central directory *)
- Files.ReadInt(r, intDummy); (* total number of entries in the central dir on this disk *)
- Files.ReadInt(r, arc.nofEntries); (* total number of entries in the central dir *)
- Files.ReadLInt(r, longDummy); (* size of the central directory *)
- Files.ReadLInt(r, longDummy); (* offset of start of central directory with respect to the starting disk number *)
- Files.ReadInt(r, intDummy); (* zipfile comment length *)
- IF intDummy # 0 THEN
- NEW(arc.comment, intDummy);
- r.file.ReadBytes(r, arc.comment^, 0, intDummy) (* zipfile comment *)
- END;
- IF r.file.Pos(r) # r.file.Length() THEN
- res := NotZipArchiveError;
- arc := NIL
- ELSE
- r.file.Set(r, longDummy); (* set r on position of first file header in central dir *)
- arc.firstEntry := ReadEntry(r); arc.lastEntry := arc.firstEntry;
- ent := arc.firstEntry; intDummy := 0;
- WHILE ent # NIL DO
- arc.lastEntry := ent; INC(intDummy); (* count number of entries *)
- ent.next := ReadEntry(r);
- ent := ent.next
- END;
- IF intDummy # arc.nofEntries THEN
- res := NotZipArchiveError;
- arc := NIL
- END
- END;
- f.Update();
- END
- END;
- RETURN arc
- END OpenArchive;
- (** Returns an Archive that corresponds to a file with specified name;
- if there is already a zip-file with the same name, this already existing archive is returned;
- possible results: cf. OpenArchive **)
- PROCEDURE CreateArchive*(CONST name: ARRAY OF CHAR; VAR res: WORD): Archive;
- VAR
- f: Files.File;
- r: Files.Rider;
- arc: Archive;
- BEGIN
- IF name#"" THEN f := Files.Old(name); END;
- IF f # NIL THEN
- RETURN OpenArchive(name, res)
- ELSE
- f := Files.New(name);
- NEW(arc);
- arc.file := f;
- arc.nofEntries := 0;
- arc.offset := 0;
- f.Set(r, 0);
- WriteEndOfCentDir(arc, r);
- IF name#"" THEN Files.Register(f) END;
- res := Ok;
- RETURN arc
- END
- END CreateArchive;
- (** Returns the first entry of the Archive arc (NIL if there is no Entry) **)
- PROCEDURE FirstEntry*(arc: Archive): Entry;
- BEGIN
- IF arc = NIL THEN
- RETURN NIL
- ELSE
- RETURN arc.firstEntry
- END
- END FirstEntry;
- (** Returns the next Entry after ent **)
- PROCEDURE NextEntry*(ent: Entry): Entry;
- BEGIN
- RETURN ent.next
- END NextEntry;
- (** Returns the Entry that corresponds to the file with the specified name and that is stored in the Archive arc;
- possible results:
- - Ok: Operation was successful
- - NotZipArchiveError: arc is not a valid Archive
- - EntryNotFound: no Entry corresponding to name was found **)
- PROCEDURE GetEntry*(arc: Archive; CONST name: ARRAY OF CHAR; VAR res: WORD): Entry;
- VAR
- ent: Entry;
- BEGIN
- IF arc = NIL THEN
- res := NotZipArchiveError
- ELSE
- ent := arc.firstEntry;
- WHILE (ent # NIL) & (ent.name # name) DO
- ent := ent.next
- END;
- IF ent = NIL THEN
- res := EntryNotFound
- ELSE
- res := Ok
- END
- END;
- RETURN ent
- END GetEntry;
- (** Uncompresses and writes the data of Entry ent to Files.Rider dst;
- possible results:
- - Ok: Data extracted
- - NotZipArchiveError: arc is not a valid zip-archive
- - EntryNotFound: ent is not an Entry of arc
- - NotSupportedError: data of ent are encrypted or compression method is not supported
- - DataError: zipfile is corrupted
- - BadName: entry has a bad file name **)
- PROCEDURE ExtractEntry*(arc: Archive; ent: Entry; VAR dst: Files.Rider; VAR res: WORD);
- VAR
- src: Files.Rider; crc32: LONGINT;
- BEGIN
- IF arc = NIL THEN
- res := NotZipArchiveError
- ELSIF (dst.file = NIL) THEN
- res := BadName
- ELSIF (ent = NIL) OR (ent # GetEntry(arc, ent.name, res)) THEN
- res := EntryNotFound
- ELSIF ~(ent.method IN SupportedCompMethods) OR (ent.support > Supported) THEN
- res := NotSupportedError
- ELSE
- CASE ent.method OF
- | Stored:
- arc.file.Set(src, ent.offsetFileData);
- Copy(src, dst, ent.uncompSize, TRUE, crc32);
- IF crc32 = ent.crc32 THEN
- res := Ok
- ELSE
- res := DataError
- END
- | Deflated:
- arc.file.Set(src, ent.offsetFileData);
- ZlibReaders.Uncompress(src, dst, crc32, res);
- IF (res = ZlibReaders.Ok) & (crc32 = ent.crc32) THEN
- res := Ok
- ELSE
- res := DataError
- END
- END;
- IF res = Ok THEN
- dst.file.Update();
- END
- END
- END ExtractEntry;
- (** Reads and compresses len bytes from Files.Rider src with specified level and strategy
- and writes them to a new Entry in the Archive arc;
- possible results:
- - Ok: file was added to arc
- - NotZipArchiveError: arc is not a valid zip-archive
- - EntryAlreadyExists: there is already an Entry in arc with the same name
- - DataError: error during compression
- - BadName: src is not based on a valid file **)
- PROCEDURE AddEntry*(arc: Archive; CONST name: ARRAY OF CHAR; VAR src: Files.Rider; len: LONGINT; level, strategy: SHORTINT; VAR res: WORD);
- VAR
- dst: Files.Rider; ent: Entry; start: LONGINT;
- BEGIN
- IF arc = NIL THEN
- res := NotZipArchiveError
- ELSIF (src.file = NIL) THEN
- res := BadName
- ELSIF (GetEntry(arc, name, res) # NIL) & (res = Ok) THEN
- res := EntryAlreadyExists
- ELSE
- NEW(ent);
- COPY(name, ent.name);
- ent.genPurpBitFlag := 0;
- IF level = NoCompression THEN
- ent.method := Stored
- ELSE
- ent.method := Deflated
- END;
- src.file.GetDate(ent.time, ent.date);
- ent.uncompSize := len;
- ent.intFileAttr := 0;
- ent.extFileAttr := 0;
- ent.comment := NIL;
- ent.support := Supported;
- ent.dataDescriptor := FALSE;
- IF arc.firstEntry # NIL THEN
- ent.offsetLocal := arc.firstEntry.offsetCentralDir
- ELSE
- ent.offsetLocal := 0
- END;
- arc.file.Set(dst, ent.offsetLocal);
- WriteLocalFileHeader(ent, dst);
- ent.offsetFileData := dst.file.Pos(dst);
- arc.file.Update();
- start := src.file.Pos(src);
- IF level = 0 THEN
- Copy(src, dst, len, TRUE, ent.crc32);
- ent.compSize := len;
- res := Ok
- ELSE
- ZlibWriters.Compress(src, dst, len, ent.compSize, level, strategy, ent.crc32, res);
- IF res # ZlibWriters.Ok THEN
- res := DataError
- ELSE
- res := Ok
- END
- END;
- IF res = Ok THEN
- ent.uncompSize := src.file.Pos(src) - start;
- arc.file.Update();
- arc.file.Set(dst, ent.offsetLocal + 14);
- Files.WriteLInt(dst, ent.crc32);
- Files.WriteLInt(dst, ent.compSize);
- arc.file.Update;
- IF arc.lastEntry # NIL THEN
- arc.lastEntry.next := ent
- ELSE (* archive has no entries *)
- arc.firstEntry := ent
- END;
- arc.lastEntry := ent;
- INC(arc.nofEntries);
- arc.file.Set(dst, ent.offsetFileData + ent.compSize);
- WriteCentralDirectory(arc, dst);
- arc.file.Update();
- res := Ok
- END;
- END
- END AddEntry;
- (** Deletes Entry ent from Archive arc;
- Possible results:
- - Ok: ent was deleted, ent is set to NIL
- - NotZipArchiveError: arc is not a valid zip-archive
- - EntryNotFound: ent is not an Entry of Archive arc **)
- PROCEDURE DeleteEntry*(arc: Archive; VAR ent: Entry; VAR res: WORD);
- VAR
- f: Files.File; r1, r2: Files.Rider;
- ent2: Entry;
- arcname: ARRAY 256 OF CHAR;
- offset, diff: LONGINT;
- BEGIN
- IF arc = NIL THEN
- res := NotZipArchiveError
- ELSIF arc.firstEntry = NIL THEN
- res := EntryNotFound
- ELSIF arc.firstEntry = ent THEN
- offset := arc.firstEntry.offsetLocal; (* arc.firstEntry.offsetLocal = 0 *)
- IF arc.lastEntry = arc.firstEntry THEN
- arc.lastEntry := arc.firstEntry.next (* = NIL *)
- END;
- arc.firstEntry := arc.firstEntry.next;
- ent2 := arc.firstEntry;
- res := Ok
- ELSE
- ent2 := arc.firstEntry;
- WHILE (ent2.next # NIL) & (ent2.next # ent) DO
- ent2 := ent2.next
- END;
- IF ent2.next = NIL THEN
- res := EntryNotFound
- ELSE
- IF arc.lastEntry = ent2.next THEN
- arc.lastEntry := ent2
- END;
- offset := ent2.next.offsetLocal;
- ent2.next := ent2.next.next;
- ent2 := ent2.next;
- res := Ok
- END
- END;
- IF res = Ok THEN
- arc.file.GetName(arcname);
- f := Files.New(arcname);
- f.Set(r2, 0);
- arc.file.Set(r1, 0);
- Copy(r1, r2, offset, FALSE, diff); (* no crc 32-checksum is computed -> diff used as dummy *)
- f.Update;
- ASSERT(ent2 = ent.next);
- IF ent2 # NIL THEN
- arc.file.Set(r1, ent2.offsetLocal);
- Copy(r1, r2, arc.firstEntry.offsetCentralDir - ent2.offsetLocal, FALSE, diff); (* arc.firstEntry can not be NIL because ent # NIL *)
- f.Update;
- diff := ent2.offsetLocal - offset
- ELSE
- diff := arc.offset - offset
- END;
- WHILE (ent2 # NIL) DO (* update offsets of entries *)
- DEC(ent2.offsetLocal, diff); DEC(ent2.offsetFileData, diff); DEC(ent2.offsetCentralDir, diff);
- ent2 := ent2.next
- END;
- DEC(arc.offset, diff);
- DEC(arc.nofEntries);
- WriteCentralDirectory(arc, r2);
- Files.Register(f); arc.file := f; ent := NIL
- END
- END DeleteEntry;
- (** open a Reader to read uncompressed data from a zip entry directly to memory **)
- PROCEDURE OpenReader*(arc: Archive; ent: Entry): Reader;
- VAR
- dummyBuf: ARRAY 1 OF CHAR;
- fr: Files.Rider;
- r: Reader;
- ur: UncompReader;
- dr: DefReader;
- BEGIN
- IF ent.support = Supported THEN
- IF ent.method = Stored THEN
- NEW(ur);
- ur.crc32 := Zlib.CRC32(0, dummyBuf, -1, -1);
- arc.file.Set(ur.fr, ent.offsetFileData);
- r := ur;
- r.open := TRUE;
- r.res := Ok
- ELSIF ent.method = Deflated THEN
- arc.file.Set(fr, ent.offsetFileData);
- NEW(dr);
- ZlibReaders.Open(dr.zr, FALSE, fr);
- dr.res := dr.zr.res;
- r := dr;
- r.open := TRUE
- ELSE
- NEW(r);
- r.open := FALSE;
- r.res := NotSupportedError
- END;
- ELSE
- NEW(r);
- r.open := FALSE;
- r.res := NotSupportedError
- END;
- r.ent := ent;
- RETURN r;
- END OpenReader;
- (** read len bytes of uncompressed data into buf[offset] and return number of bytes actually read; Reader must be opened **)
- PROCEDURE ReadBytes*(r: Reader; VAR buf: ARRAY OF CHAR; offset, len: LONGINT; VAR read: LONGINT);
- VAR
- bufp: POINTER TO ARRAY OF CHAR; i: LONGINT;
- BEGIN
- IF r.open THEN
- IF r IS UncompReader THEN
- IF offset = 0 THEN
- r(UncompReader).fr.file.ReadBytes(r(UncompReader).fr, buf, 0, len);
- ELSE
- NEW(bufp, len);
- r(UncompReader).fr.file.ReadBytes(r(UncompReader).fr, bufp^, 0, len);
- FOR i := 0 TO len - 1 DO
- buf[offset + i] := bufp[i]
- END
- END;
- read := len - r(UncompReader).fr.res;
- r(UncompReader).crc32 := Zlib.CRC32(r(UncompReader).crc32, buf, offset, read)
- ELSIF r IS DefReader THEN
- ZlibReaders.ReadBytes(r(DefReader).zr, buf, offset, len, read);
- r.res := r(DefReader).zr.res
- END
- ELSE
- r.res := ReaderError
- END
- END ReadBytes;
- (** read decompressed byte **)
- PROCEDURE Read*(r: Reader; VAR ch: CHAR);
- VAR
- buf: ARRAY 1 OF CHAR; read: LONGINT;
- BEGIN
- ReadBytes(r, buf, 0, 1, read);
- ch := buf[0];
- END Read;
- (** close Reader **)
- PROCEDURE Close*(r: Reader);
- BEGIN
- IF r.open THEN
- IF r IS UncompReader THEN
- IF r(UncompReader).crc32 # r.ent.crc32 THEN
- r.res := DataError
- ELSE
- r.res := Ok
- END
- ELSIF r IS DefReader THEN
- ZlibReaders.Close(r(DefReader).zr);
- IF r(DefReader).zr.crc32 # r.ent.crc32 THEN
- r.res := DataError
- ELSE
- r.res := r(DefReader).zr.res
- END
- ELSE
- r.res := ReaderError
- END;
- r.open := FALSE
- ELSE
- r.res := ReaderError
- END
- END Close;
- END Zip.
|