/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const fs = require('fs');
const Utils = require("./../../../unisharp/Utils");
const Hashtable = require("./../../../unisharp/Hashtable");
const RefOutArgWrapper = require("./../../../unisharp/RefOutArgWrapper");
const Stream = require("./../../../unisharp/Stream");
const FileStream = require("./../../../unisharp/FileStream");

const UpdateCommand = require("./UpdateCommand");
const IDynamicDataSource = require("./IDynamicDataSource");
const DynamicDiskDataSource = require("./DynamicDiskDataSource");
const GeneralBitFlags = require("./GeneralBitFlags");
const ZipExtraData = require("./ZipExtraData");
const ZipNameTransform = require("./ZipNameTransform");
const ZipUpdate = require("./ZipUpdate");
const StreamUtils = require("./StreamUtils");
const Deflater = require("./Deflater");
const FileUpdateMode = require("./FileUpdateMode");
const DeflaterOutputStream = require("./DeflaterOutputStream");
const MemoryArchiveStorage = require("./MemoryArchiveStorage");
const DiskArchiveStorage = require("./DiskArchiveStorage");
const IArchiveStorage = require("./IArchiveStorage");
const UncompressedStream = require("./UncompressedStream");
const MiscHelper = require("./../../../util/MiscHelper");
const ZipConstants = require("./ZipConstants");
const ZipString = require("./ZipString");
const PartialInputStream = require("./PartialInputStream");
const Inflater = require("./Inflater");
const CompressionMethod = require("./CompressionMethod");
const UseZip64 = require("./UseZip64");
const IEntryFactory = require("./IEntryFactory");
const ZipEntry = require("./ZipEntry");
const ZipEntryFactory = require("./ZipEntryFactory");
const Crc32 = require("./Crc32");
const TestOperation = require("./TestOperation");
const DescriptorData = require("./DescriptorData");
const ZipHelperStream = require("./ZipHelperStream");
const TestStrategy = require("./TestStrategy");
const InflaterInputStream = require("./InflaterInputStream");
const ZipFileHeaderTest = require("./ZipFileHeaderTest");
const TestStatus = require("./TestStatus");

// This class represents a Zip archive.  You can ask for the contained
// entries, or get an input stream for a file entry.  The entry is
// automatically decompressed.
class ZipFile {
    
    constructor(_name, stream) {
        this.isDisposed_ = false;
        this.name_ = null;
        this.comment_ = null;
        this.m_BaseStream = null;
        this.m_StreamOwner = false;
        this.offsetOfFirstEntry = 0;
        this.entries_ = null;
        this.isNewArchive_ = false;
        this.useZip64_ = UseZip64.DYNAMIC;
        this.m_Updates = null;
        this.updateCount_ = 0;
        this.updateIndex_ = null;
        this.archiveStorage_ = null;
        this.updateDataSource_ = null;
        this.contentsEdited_ = false;
        this.bufferSize_ = ZipFile.defaultBufferSize;
        this.copyBuffer_ = null;
        this.newComment_ = null;
        this.commentEdited_ = false;
        this.updateEntryFactory_ = new ZipEntryFactory();
        this.name_ = _name;
        if (stream === null) {
            this.m_BaseStream = new FileStream(_name, "r", false);
            this.m_StreamOwner = true;
        }
        else {
            this.m_BaseStream = stream;
            this.m_StreamOwner = false;
        }
        try {
            this.readEntries();
        } catch (ex561) {
            this.disposeInternal(true);
            throw ex561;
        }
    }
    
    close() {
        this.disposeInternal(true);
    }
    
    static createFile(fileName) {
        if (fileName === null) 
            throw new Error("fileName");
        let f = new FileStream(fileName, "w+", false);
        let result = new ZipFile(null, f);
        result.name_ = fileName;
        result.m_StreamOwner = true;
        return result;
    }
    
    static createStream(outStream) {
        if (outStream === null) 
            throw new Error("outStream");
        if (!outStream.canWrite()) 
            throw new Error("Stream is not writeable", "outStream");
        if (!outStream.canSeek()) 
            throw new Error("Stream is not seekable", "outStream");
        let result = new ZipFile(null, outStream);
        return result;
    }
    
    get isStreamOwner() {
        return this.m_StreamOwner;
    }
    set isStreamOwner(value) {
        this.m_StreamOwner = value;
        return value;
    }
    
    get isEmbeddedArchive() {
        return this.offsetOfFirstEntry > 0;
    }
    
    get isNewArchive() {
        return this.isNewArchive_;
    }
    
    get zipFileComment() {
        return this.comment_;
    }
    
    get name() {
        return this.name_;
    }
    
    get size() {
        return this.entries_.length;
    }
    
    get count() {
        return this.entries_.length;
    }
    
    getIndexerItem(index) {
        return this.entries_[index].clone();
    }
    
    get zipEntries() {
        return Array.from(this.entries_);
    }
    
    findEntry(_name, ignoreCase) {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        for (let i = 0; i < this.entries_.length; i++) {
            if (Utils.compareStrings(_name, this.entries_[i].name, ignoreCase) === 0) 
                return i;
        }
        return -1;
    }
    
    getEntry(_name) {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        let index = this.findEntry(_name, true);
        return (index >= 0 ? this.entries_[index].clone() : null);
    }
    
    getInputStream(entry) {
        if (entry === null) 
            throw new Error("entry");
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        let index = entry.zipFileIndex;
        if (((index < 0)) || (index >= this.entries_.length) || (this.entries_[index].name !== entry.name)) {
            index = this.findEntry(entry.name, true);
            if (index < 0) 
                throw new Error("Entry cannot be found");
        }
        return this.getInputStream0(index);
    }
    
    getInputStream0(entryIndex) {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        let start = this.locateEntry(this.entries_[entryIndex]);
        let method = this.entries_[entryIndex]._CompressionMethod;
        let result = new PartialInputStream(this, start, this.entries_[entryIndex].compressedSize);
        if (this.entries_[entryIndex].isCrypted === true) 
            throw new Error("Unable to decrypt this entry");
        switch (method) { 
        case CompressionMethod.STORED: {
            break;
        }
        case CompressionMethod.DEFLATED: {
            result = new InflaterInputStream(result, new Inflater(true));
            break;
        }
        default: {
            throw new Error(("Unsupported compression method " + String(method)));
        }
        }
        return result;
    }
    
    testArchive(testData) {
        return this.testArchiveEx(testData, TestStrategy.FINDFIRSTERROR, null);
    }
    
    testArchiveEx(testData, strategy, resultHandler) {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        let status = new TestStatus(this);
        if (resultHandler !== null) 
            resultHandler.call(status, null);
        let test = ZipFileHeaderTest.of((testData ? ((ZipFileHeaderTest.HEADER.value()) | (ZipFileHeaderTest.EXTRACT.value())) : ZipFileHeaderTest.HEADER.value()));
        let testing = true;
        try {
            let entryIndex = 0;
            while (testing && ((entryIndex < this.count))) {
                if (resultHandler !== null) {
                    status.setEntry(this.getIndexerItem(entryIndex));
                    status.setOperation(TestOperation.ENTRYHEADER);
                    resultHandler.call(status, null);
                }
                try {
                    this.testLocalHeader(this.getIndexerItem(entryIndex), test);
                } catch (ex) {
                    status.addError();
                    if (resultHandler !== null) 
                        resultHandler.call(status, ("Exception during test - '" + ex.message + "'"));
                    if (strategy === TestStrategy.FINDFIRSTERROR) 
                        testing = false;
                }
                if (testing && testData && this.getIndexerItem(entryIndex).isFile) {
                    if (resultHandler !== null) {
                        status.setOperation(TestOperation.ENTRYDATA);
                        resultHandler.call(status, null);
                    }
                    let crc = new Crc32();
                    let entryStream = this.getInputStream(this.getIndexerItem(entryIndex)); 
                    try {
                        let buffer = new Uint8Array(4096);
                        let totalBytes = 0;
                        let bytesRead = 0;
                        while ((((bytesRead = entryStream.read(buffer, 0, buffer.length)))) > 0) {
                            crc.updateByBufEx(buffer, 0, bytesRead);
                            if (resultHandler !== null) {
                                totalBytes += bytesRead;
                                status.setBytesTested(totalBytes);
                                resultHandler.call(status, null);
                            }
                        }
                    }
                    finally {
                        entryStream.close();
                    }
                    if (Utils.compareUnsigned(this.getIndexerItem(entryIndex).crc, crc.value) != 0) {
                        status.addError();
                        if (resultHandler !== null) 
                            resultHandler.call(status, "CRC mismatch");
                        if (strategy === TestStrategy.FINDFIRSTERROR) 
                            testing = false;
                    }
                    if (((this.getIndexerItem(entryIndex).flags & (GeneralBitFlags.DESCRIPTOR.value()))) !== 0) {
                        let helper = new ZipHelperStream(null, this.m_BaseStream); 
                        try {
                            let data = new DescriptorData();
                            helper.readDataDescriptor(this.getIndexerItem(entryIndex).localHeaderRequiresZip64, data);
                            if (Utils.compareUnsigned(this.getIndexerItem(entryIndex).crc, data.crc) != 0) 
                                status.addError();
                            if (this.getIndexerItem(entryIndex).compressedSize !== data.compressedSize) 
                                status.addError();
                            if (this.getIndexerItem(entryIndex).size !== data.size) 
                                status.addError();
                        }
                        finally {
                            helper.close();
                        }
                    }
                }
                if (resultHandler !== null) {
                    status.setOperation(TestOperation.ENTRYCOMPLETE);
                    resultHandler.call(status, null);
                }
                entryIndex += 1;
            }
            if (resultHandler !== null) {
                status.setOperation(TestOperation.MISCELLANEOUSTESTS);
                resultHandler.call(status, null);
            }
        } catch (ex) {
            status.addError();
            if (resultHandler !== null) 
                resultHandler.call(status, ("Exception during test - '" + ex.message + "'"));
        }
        if (resultHandler !== null) {
            status.setOperation(TestOperation.COMPLETE);
            status.setEntry(null);
            resultHandler.call(status, null);
        }
        return status.errorCount === 0;
    }
    
    testLocalHeader(entry, tests) {
        let testHeader = (((tests.value()) & (ZipFileHeaderTest.HEADER.value()))) !== 0;
        let testData = (((tests.value()) & (ZipFileHeaderTest.EXTRACT.value()))) !== 0;
        this.m_BaseStream.seek(this.offsetOfFirstEntry + entry.offset, 0);
        if ((this.readLEUint()) !== ZipConstants.LOCAL_HEADER_SIGNATURE) 
            throw new Error(("Wrong local header signature @" + ((this.offsetOfFirstEntry + entry.offset)).toString(16).toUpperCase()));
        let extractVersion = this.readLEUshort();
        let localFlags = this.readLEUshort();
        let compressionMethod = this.readLEUshort();
        let fileTime = this.readLEUshort();
        let fileDate = this.readLEUshort();
        let crcValue = this.readLEUint();
        let compressedSize = this.readLEUint();
        let _size = this.readLEUint();
        let storedNameLength = this.readLEUshort();
        let extraDataLength = this.readLEUshort();
        let nameData = new Uint8Array(storedNameLength);
        StreamUtils.readFully(this.m_BaseStream, nameData);
        let extraData = new Uint8Array(extraDataLength);
        StreamUtils.readFully(this.m_BaseStream, extraData);
        let localExtraData = new ZipExtraData(extraData);
        if (localExtraData.find(1)) {
            _size = localExtraData.readLong();
            compressedSize = localExtraData.readLong();
            if ((((localFlags) & (GeneralBitFlags.DESCRIPTOR.value()))) !== 0) {
                if ((_size !== -1) && (_size !== entry.size)) 
                    throw new Error("Size invalid for descriptor");
                if ((compressedSize !== -1) && (compressedSize !== entry.compressedSize)) 
                    throw new Error("Compressed size invalid for descriptor");
            }
        }
        else if ((extractVersion >= ZipConstants.VERSION_ZIP64) && (((Utils.compareUnsigned(_size, 4294967295) == 0) || (Utils.compareUnsigned(compressedSize, 4294967295) == 0)))) 
            throw new Error("Required Zip64 extended information missing");
        if (testData) {
            if (entry.isFile) {
                if (!entry.isCompressionMethodSupported()) 
                    throw new Error("Compression method not supported");
                if ((extractVersion > ZipConstants.VERSION_MADE_BY) || (((extractVersion > (20)) && ((extractVersion < ZipConstants.VERSION_ZIP64))))) 
                    throw new Error(("Version required to extract this entry not supported (" + extractVersion + ")"));
                if ((((localFlags) & ((((GeneralBitFlags.PATCHED.value()) | (GeneralBitFlags.STRONGENCRYPTION.value()) | (GeneralBitFlags.ENHANCEDCOMPRESS.value())) | (GeneralBitFlags.HEADERMASKED.value()))))) !== 0) 
                    throw new Error("The library does not support the zip version required to extract this entry");
            }
        }
        if (testHeader) {
            if ((((((((extractVersion <= (63)) && (extractVersion !== (10)) && (extractVersion !== (11))) && (extractVersion !== (20)) && (extractVersion !== (21))) && (extractVersion !== (25)) && (extractVersion !== (27))) && (extractVersion !== (45)) && (extractVersion !== (46))) && (extractVersion !== (50)) && (extractVersion !== (51))) && (extractVersion !== (52)) && (extractVersion !== (61))) && (extractVersion !== (62)) && (extractVersion !== (63))) 
                throw new Error(("Version required to extract this entry is invalid (" + extractVersion + ")"));
            if ((((localFlags) & (((GeneralBitFlags.RESERVEDPKWARE4.value()) | (GeneralBitFlags.RESERVEDPKWARE14.value()) | (GeneralBitFlags.RESERVEDPKWARE15.value()))))) !== 0) 
                throw new Error("Reserved bit flags cannot be set.");
            if (((((localFlags) & (GeneralBitFlags.ENCRYPTED.value()))) !== 0) && ((extractVersion < (20)))) 
                throw new Error(("Version required to extract this entry is too low for encryption (" + extractVersion + ")"));
            if ((((localFlags) & (GeneralBitFlags.STRONGENCRYPTION.value()))) !== 0) {
                if ((((localFlags) & (GeneralBitFlags.ENCRYPTED.value()))) === 0) 
                    throw new Error("Strong encryption flag set but encryption flag is not set");
                if (extractVersion < (50)) 
                    throw new Error(("Version required to extract this entry is too low for encryption (" + extractVersion + ")"));
            }
            if (((((localFlags) & (GeneralBitFlags.PATCHED.value()))) !== 0) && ((extractVersion < (27)))) 
                throw new Error(("Patched data requires higher version than (" + extractVersion + ")"));
            if (localFlags !== entry.flags) 
                throw new Error("Central header/local header flags mismatch");
            if (entry._CompressionMethod !== (CompressionMethod.of(compressionMethod))) 
                throw new Error("Central header/local header compression method mismatch");
            if (entry.version !== extractVersion) 
                throw new Error("Extract version mismatch");
            if ((((localFlags) & (GeneralBitFlags.STRONGENCRYPTION.value()))) !== 0) {
                if (extractVersion < (62)) 
                    throw new Error("Strong encryption flag set but version not high enough");
            }
            if ((((localFlags) & (GeneralBitFlags.HEADERMASKED.value()))) !== 0) {
                if ((fileTime !== (0)) || (fileDate !== (0))) 
                    throw new Error("Header masked set but date/time values non-zero");
            }
            if ((((localFlags) & (GeneralBitFlags.DESCRIPTOR.value()))) === 0) {
                if (Utils.compareUnsigned(crcValue, entry.crc) != 0) 
                    throw new Error("Central header/local header crc mismatch");
            }
            if ((_size === 0) && (compressedSize === 0)) {
                if (Utils.compareUnsigned(crcValue, 0) != 0) 
                    throw new Error("Invalid CRC for empty entry");
            }
            if (entry.name.length > storedNameLength) 
                throw new Error("File name length mismatch");
            let localName = ZipConstants.convertToStringExt0(localFlags, nameData);
            if (localName !== entry.name) 
                throw new Error("Central header and local header file name mismatch");
            if (entry.isDirectory) {
                if (_size > 0) 
                    throw new Error("Directory cannot have size");
                if (entry.isCrypted) {
                    if (compressedSize > (ZipConstants.CRYPTO_HEADER_SIZE + 2)) 
                        throw new Error("Directory compressed size invalid");
                }
                else if (compressedSize > 2) 
                    throw new Error("Directory compressed size invalid");
            }
            if (!ZipNameTransform.isValidNameEx(localName, true)) 
                throw new Error("Name is invalid");
        }
        if (((((localFlags) & (GeneralBitFlags.DESCRIPTOR.value()))) === 0) || (((_size > 0) || (compressedSize > 0)))) {
        }
        let extraLength = storedNameLength + extraDataLength;
        return (this.offsetOfFirstEntry + entry.offset + ZipConstants.LOCAL_HEADER_BASE_SIZE) + extraLength;
    }
    
    get nameTransform() {
        return this.updateEntryFactory_.nameTransform;
    }
    set nameTransform(value) {
        this.updateEntryFactory_.nameTransform = value;
        return value;
    }
    
    get entryFactory() {
        return this.updateEntryFactory_;
    }
    set entryFactory(value) {
        if (value === null) 
            this.updateEntryFactory_ = new ZipEntryFactory();
        else 
            this.updateEntryFactory_ = value;
        return value;
    }
    
    get bufferSize() {
        return this.bufferSize_;
    }
    set bufferSize(value) {
        if (value < 1024) 
            throw new Error("value", "cannot be below 1024");
        if (this.bufferSize_ !== value) {
            this.bufferSize_ = value;
            this.copyBuffer_ = null;
        }
        return value;
    }
    
    get isUpdating() {
        return this.m_Updates !== null;
    }
    
    get useZip64() {
        return this.useZip64_;
    }
    set useZip64(value) {
        this.useZip64_ = value;
        return value;
    }
    
    beginUpdateEx(archiveStorage, dataSource) {
        if (archiveStorage === null) 
            throw new Error("archiveStorage");
        if (dataSource === null) 
            throw new Error("dataSource");
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        if (this.isEmbeddedArchive) 
            throw new Error("Cannot update embedded/SFX archives");
        this.archiveStorage_ = archiveStorage;
        this.updateDataSource_ = dataSource;
        this.updateIndex_ = new Hashtable();
        if (this.entries_ === null) 
            this.entries_ = Array(0).fill(null);
        this.m_Updates = new Array();
        for (const entry of this.entries_) {
            let index = this.m_Updates.length;
            this.m_Updates.push(new ZipUpdate(entry, UpdateCommand.COPY));
            this.updateIndex_.put(entry.name, index);
        }
        let cmp = new ZipFile.UpdateComparer();
        for (let ii = 0; ii < this.m_Updates.length; ii++) {
            let ch = false;
            for (let jj = 0; jj < (this.m_Updates.length - 1); jj++) {
                if (cmp.compare(this.m_Updates[jj], this.m_Updates[jj + 1]) > 0) {
                    let u = this.m_Updates[jj];
                    this.m_Updates[jj] = this.m_Updates[jj + 1];
                    this.m_Updates[jj + 1] = u;
                    ch = true;
                }
            }
            if (!ch) 
                break;
        }
        let idx = 0;
        for (const up of this.m_Updates) {
            let update = Utils.as(up, ZipUpdate);
            if (up === null) 
                continue;
            if (idx === (this.m_Updates.length - 1)) 
                break;
            update.offsetBasedSize = this.m_Updates[idx + 1].entry.offset - update.entry.offset;
            idx++;
        }
        this.updateCount_ = this.m_Updates.length;
        this.contentsEdited_ = false;
        this.commentEdited_ = false;
        this.newComment_ = null;
    }
    
    beginUpdate(archiveStorage) {
        this.beginUpdateEx(archiveStorage, new DynamicDiskDataSource());
    }
    
    beginUpdate0() {
        if (this.name === null) 
            this.beginUpdateEx(new MemoryArchiveStorage(), new DynamicDiskDataSource());
        else 
            this.beginUpdateEx(new DiskArchiveStorage(this), new DynamicDiskDataSource());
    }
    
    commitUpdate() {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        this.checkUpdating();
        try {
            this.updateIndex_.clear();
            this.updateIndex_ = null;
            if (this.contentsEdited_) 
                this.runUpdates();
            else if (this.commentEdited_) 
                this.updateCommentOnly();
            else if (this.entries_.length === 0) {
                let theComment = (this.newComment_ !== null ? this.newComment_.rawComment : ZipConstants.convertToArrayStr(this.comment_));
                let zhs = new ZipHelperStream(null, this.m_BaseStream); 
                try {
                    zhs.writeEndOfCentralDirectory(0, 0, 0, theComment);
                }
                finally {
                    zhs.close();
                }
            }
        } finally {
            this.postUpdateCleanup();
        }
    }
    
    abortUpdate() {
        this.postUpdateCleanup();
    }
    
    setComment(comment) {
        if (this.isDisposed_) 
            throw new Error("ZipFile");
        this.checkUpdating();
        this.newComment_ = new ZipString(comment);
        if (this.newComment_.rawLength > 0xffff) {
            this.newComment_ = null;
            throw new Error("Comment length exceeds maximum - 65535");
        }
        this.commentEdited_ = true;
    }
    
    addUpdate(update) {
        this.contentsEdited_ = true;
        let index = this.findExistingUpdate(update.entry.name);
        if (index >= 0) {
            if (this.m_Updates[index] === null) 
                this.updateCount_ += 1;
            this.m_Updates[index] = update;
        }
        else {
            index = this.m_Updates.length;
            this.m_Updates.push(update);
            this.updateCount_ += 1;
            this.updateIndex_.put(update.entry.name, index);
        }
    }
    
    add(fileName, entryName) {
        if (fileName === null) 
            throw new Error("fileName");
        if (entryName === null) 
            throw new Error("entryName");
        this.checkUpdating();
        this.addUpdate(new ZipUpdate(this.entryFactory.makeFileEntryEx(entryName, false), UpdateCommand.ADD, null, fileName));
    }
    
    addStream(str, entryName) {
        if (entryName === null) 
            throw new Error("entryName");
        this.checkUpdating();
        this.addUpdate(new ZipUpdate(this.entryFactory.makeFileEntryEx(entryName, false), UpdateCommand.ADD, null, null, str));
    }
    
    addDirectory(directoryName) {
        if (directoryName === null) 
            throw new Error("directoryName");
        this.checkUpdating();
        let dirEntry = this.entryFactory.makeDirectoryEntry(directoryName);
        this.addUpdate(new ZipUpdate(dirEntry, UpdateCommand.ADD));
    }
    
    _delete(fileName) {
        if (fileName === null) 
            throw new Error("fileName");
        this.checkUpdating();
        let result = false;
        let index = this.findExistingUpdate(fileName);
        if ((index >= 0) && (this.m_Updates[index] !== null)) {
            result = true;
            this.contentsEdited_ = true;
            this.m_Updates[index] = null;
            this.updateCount_ -= 1;
        }
        else 
            throw new Error("Cannot find entry to delete");
        return result;
    }
    
    writeLEShort(value) {
        this.m_BaseStream.writeByte((value & 0xff));
        this.m_BaseStream.writeByte((((value >> 8)) & 0xff));
    }
    
    writeLEUshort(value) {
        this.m_BaseStream.writeByte(((value) & 0xff));
        this.m_BaseStream.writeByte(((value) >>> 8));
    }
    
    writeLEInt(value) {
        this.writeLEShort(value & 0xffff);
        this.writeLEShort(value >> 16);
    }
    
    writeLEUint(value) {
        this.writeLEUshort(((value) & 0xffff));
        this.writeLEUshort(((value) >>> 16));
    }
    
    writeLeLong(value1, value2) {
        this.writeLEInt(value1);
        this.writeLEInt(value2);
    }
    
    writeLEUlong(value1, value2) {
        this.writeLEUint(value1);
        this.writeLEUint(value2);
    }
    
    writeLocalEntryHeader(update) {
        let entry = update.outEntry;
        entry.offset = this.m_BaseStream.position;
        if (update.command !== UpdateCommand.COPY) {
            if (entry._CompressionMethod === CompressionMethod.DEFLATED) {
                if (entry.size === 0) {
                    entry.compressedSize = entry.size;
                    entry.crc = 0;
                    entry._CompressionMethod = CompressionMethod.STORED;
                }
            }
            else if (entry._CompressionMethod === CompressionMethod.STORED) 
                entry.flags &= (~(GeneralBitFlags.DESCRIPTOR.value()));
            entry.isCrypted = false;
            switch (this.useZip64_) { 
            case UseZip64.DYNAMIC: {
                if (entry.size < 0) 
                    entry.forceZip64();
                break;
            }
            case UseZip64.ON: {
                entry.forceZip64();
                break;
            }
            case UseZip64.OFF: {
                break;
            }
            }
        }
        this.writeLEInt(ZipConstants.LOCAL_HEADER_SIGNATURE);
        this.writeLEShort(entry.version);
        this.writeLEShort(entry.flags);
        this.writeLEShort(entry._CompressionMethod.value());
        this.writeLEInt(entry.dosTime);
        if (!entry.hasCrc) {
            update.crcPatchOffset = this.m_BaseStream.position;
            this.writeLEInt(0);
        }
        else 
            this.writeLEInt(entry.crc);
        if (entry.localHeaderRequiresZip64) {
            this.writeLEInt(-1);
            this.writeLEInt(-1);
        }
        else {
            if (((entry.compressedSize < 0)) || ((entry.size < 0))) 
                update.sizePatchOffset = this.m_BaseStream.position;
            this.writeLEInt(entry.compressedSize);
            this.writeLEInt(entry.size);
        }
        let _name = ZipConstants.convertToArray(entry.flags, entry.name);
        if (_name.length > 0xFFFF) 
            throw new Error("Entry name too long.");
        let ed = new ZipExtraData(entry.extraData);
        if (entry.localHeaderRequiresZip64) {
            ed.startNewEntry();
            ed.addLeLong(entry.size, 0);
            ed.addLeLong(entry.compressedSize, 0);
            ed.addNewEntry(1);
        }
        else 
            ed._delete(1);
        entry.extraData = ed.getEntryData();
        this.writeLEShort(_name.length);
        this.writeLEShort(entry.extraData.length);
        if (_name.length > 0) 
            this.m_BaseStream.write(_name, 0, _name.length);
        if (entry.localHeaderRequiresZip64) {
            if (!ed.find(1)) 
                throw new Error("Internal error cannot find extra data");
            update.sizePatchOffset = (this.m_BaseStream.position) + ed.currentReadIndex;
        }
        if (entry.extraData.length > 0) 
            this.m_BaseStream.write(entry.extraData, 0, entry.extraData.length);
    }
    
    writeCentralDirectoryHeader(entry) {
        if (entry.compressedSize < 0) 
            throw new Error("Attempt to write central directory entry with unknown csize");
        if (entry.size < 0) 
            throw new Error("Attempt to write central directory entry with unknown size");
        this.writeLEInt(ZipConstants.CENTRAL_HEADER_SIGNATURE);
        this.writeLEShort(ZipConstants.VERSION_MADE_BY);
        this.writeLEShort(entry.version);
        this.writeLEShort(entry.flags);
        /* unchecked C# block !!! */ {
            this.writeLEShort(entry._CompressionMethod.value());
            this.writeLEInt(entry.dosTime);
            this.writeLEInt(entry.crc);
        }
        if (entry.isZip64Forced()) 
            this.writeLEInt(-1);
        else 
            this.writeLEInt((entry.compressedSize & 0xffffffff));
        if (entry.isZip64Forced()) 
            this.writeLEInt(-1);
        else 
            this.writeLEInt(entry.size);
        let _name = ZipConstants.convertToArray(entry.flags, entry.name);
        if (_name.length > 0xFFFF) 
            throw new Error("Entry name is too long.");
        this.writeLEShort(_name.length);
        let ed = new ZipExtraData(entry.extraData);
        if (entry.centralHeaderRequiresZip64) {
            ed.startNewEntry();
            if (this.useZip64_ === UseZip64.ON) 
                ed.addLeLong(entry.size, 0);
            if (this.useZip64_ === UseZip64.ON) 
                ed.addLeLong(entry.compressedSize, 0);
            ed.addNewEntry(1);
        }
        else 
            ed._delete(1);
        let centralExtraData = ed.getEntryData();
        this.writeLEShort(centralExtraData.length);
        this.writeLEShort((entry.comment !== null ? entry.comment.length : 0));
        this.writeLEShort(0);
        this.writeLEShort(0);
        if (entry.externalFileAttributes !== -1) 
            this.writeLEInt(entry.externalFileAttributes);
        else if (entry.isDirectory) 
            this.writeLEUint(16);
        else 
            this.writeLEUint(0);
        this.writeLEUint(entry.offset);
        if (_name.length > 0) 
            this.m_BaseStream.write(_name, 0, _name.length);
        if (centralExtraData.length > 0) 
            this.m_BaseStream.write(centralExtraData, 0, centralExtraData.length);
        let rawComment = (entry.comment !== null ? MiscHelper.encodeStringAscii(entry.comment) : new Uint8Array(0));
        if (rawComment.length > 0) 
            this.m_BaseStream.write(rawComment, 0, rawComment.length);
        return (ZipConstants.CENTRAL_HEADER_BASE_SIZE + _name.length + centralExtraData.length) + rawComment.length;
    }
    
    postUpdateCleanup() {
        this.updateDataSource_ = null;
        this.m_Updates = null;
        this.updateIndex_ = null;
        if (this.archiveStorage_ !== null) {
            this.archiveStorage_.close();
            this.archiveStorage_ = null;
        }
    }
    
    getTransformedFileName(_name) {
        let transform = this.nameTransform;
        return (transform !== null ? transform.transformFile(_name) : _name);
    }
    
    getTransformedDirectoryName(_name) {
        let transform = this.nameTransform;
        return (transform !== null ? transform.transformDirectory(_name) : _name);
    }
    
    getBuffer() {
        if (this.copyBuffer_ === null) 
            this.copyBuffer_ = new Uint8Array(this.bufferSize_);
        return this.copyBuffer_;
    }
    
    copyDescriptorBytes(update, dest, source) {
        let bytesToCopy = this.getDescriptorSize(update);
        if (bytesToCopy > 0) {
            let buffer = this.getBuffer();
            while (bytesToCopy > 0) {
                let readSize = Math.min(buffer.length, bytesToCopy);
                let bytesRead = source.read(buffer, 0, readSize);
                if (bytesRead > 0) {
                    dest.write(buffer, 0, bytesRead);
                    bytesToCopy -= bytesRead;
                }
                else 
                    throw new Error("Unxpected end of stream");
            }
        }
    }
    
    copyBytes(update, destination, source, bytesToCopy, updateCrc) {
        if (destination === source) 
            throw new Error("Destination and source are the same");
        let crc = new Crc32();
        let buffer = this.getBuffer();
        let targetBytes = bytesToCopy;
        let totalBytesRead = 0;
        let bytesRead = 0;
        do{
            let readSize = buffer.length;
            if (bytesToCopy < readSize) 
                readSize = bytesToCopy;
            bytesRead = source.read(buffer, 0, readSize);
            if (bytesRead > 0) {
                if (updateCrc) 
                    crc.updateByBufEx(buffer, 0, bytesRead);
                destination.write(buffer, 0, bytesRead);
                bytesToCopy -= bytesRead;
                totalBytesRead += bytesRead;
            }
        } while ((bytesRead > 0) && (bytesToCopy > 0)); 
        if (totalBytesRead !== targetBytes) 
            throw new Error(("Failed to copy bytes expected " + targetBytes + " read " + totalBytesRead));
        if (updateCrc) 
            update.outEntry.crc = crc.value;
    }
    
    getDescriptorSize(update) {
        let result = 0;
        if (((update.entry.flags & (GeneralBitFlags.DESCRIPTOR.value()))) !== 0) {
            result = ZipConstants.DATA_DESCRIPTOR_SIZE - 4;
            if (update.entry.localHeaderRequiresZip64) 
                result = ZipConstants.ZIP64DATA_DESCRIPTOR_SIZE - 4;
        }
        return result;
    }
    
    copyDescriptorBytesDirect(update, stream, destinationPosition, sourcePosition) {
        let bytesToCopy = this.getDescriptorSize(update);
        while (bytesToCopy > 0) {
            let readSize = bytesToCopy;
            let buffer = this.getBuffer();
            stream.position = sourcePosition;
            let bytesRead = stream.read(buffer, 0, readSize);
            if (bytesRead > 0) {
                stream.position = destinationPosition.value;
                stream.write(buffer, 0, bytesRead);
                bytesToCopy -= bytesRead;
                destinationPosition.value += bytesRead;
                sourcePosition += bytesRead;
            }
            else 
                throw new Error("Unxpected end of stream");
        }
    }
    
    copyEntryDataDirect(update, stream, updateCrc, destinationPosition, sourcePosition) {
        let bytesToCopy = update.entry.compressedSize;
        let crc = new Crc32();
        let buffer = this.getBuffer();
        let targetBytes = bytesToCopy;
        let totalBytesRead = 0;
        let bytesRead = 0;
        do{
            let readSize = buffer.length;
            if (bytesToCopy < readSize) 
                readSize = bytesToCopy;
            stream.position = sourcePosition.value;
            bytesRead = stream.read(buffer, 0, readSize);
            if (bytesRead > 0) {
                if (updateCrc) 
                    crc.updateByBufEx(buffer, 0, bytesRead);
                stream.position = destinationPosition.value;
                stream.write(buffer, 0, bytesRead);
                destinationPosition.value += bytesRead;
                sourcePosition.value += bytesRead;
                bytesToCopy -= bytesRead;
                totalBytesRead += bytesRead;
            }
        } while ((bytesRead > 0) && (bytesToCopy > 0)); 
        if (totalBytesRead !== targetBytes) 
            throw new Error(("Failed to copy bytes expected " + targetBytes + " read " + totalBytesRead));
        if (updateCrc) 
            update.outEntry.crc = crc.value;
    }
    
    findExistingUpdate(fileName) {
        let result = -1;
        let convertedName = this.getTransformedFileName(fileName);
        if (this.updateIndex_.containsKey(convertedName)) 
            result = this.updateIndex_.get(convertedName);
        return result;
    }
    
    getOutputStream(entry) {
        let result = this.m_BaseStream;
        if (entry.isCrypted === true) 
            throw new Error("Encryption not supported");
        switch (entry._CompressionMethod) { 
        case CompressionMethod.STORED: {
            return new UncompressedStream(result);
        }
        case CompressionMethod.DEFLATED: {
            let dos = new DeflaterOutputStream(result, new Deflater(9, true));
            dos.isStreamOwner = false;
            return dos;
        }
        default: {
            throw new Error(("Unknown compression method " + String(entry._CompressionMethod)));
        }
        }
    }
    
    addEntry(workFile, update) {
        let source = null;
        try {
            if (update.entry.isFile) {
                source = update.getSource();
                if (source === null) 
                    source = this.updateDataSource_.getSource(update.entry, update.filename);
            }
            if (source !== null) {
                let sourceStreamLength = source.length;
                if (update.outEntry.size < 0) 
                    update.outEntry.size = sourceStreamLength;
                else if (update.outEntry.size !== sourceStreamLength) {
                }
                workFile.writeLocalEntryHeader(update);
                let dataStart = workFile.m_BaseStream.position;
                let output = workFile.getOutputStream(update.outEntry); 
                try {
                    this.copyBytes(update, output, source, sourceStreamLength, true);
                }
                finally {
                    output.close();
                }
                let dataEnd = workFile.m_BaseStream.position;
                update.outEntry.compressedSize = dataEnd - dataStart;
                if (((update.outEntry.flags & (GeneralBitFlags.DESCRIPTOR.value()))) === (GeneralBitFlags.DESCRIPTOR.value())) {
                    let helper = new ZipHelperStream(null, workFile.m_BaseStream); 
                    try {
                        helper.writeDataDescriptor(update.outEntry);
                    }
                    finally {
                        helper.close();
                    }
                }
            }
            else {
                workFile.writeLocalEntryHeader(update);
                update.outEntry.compressedSize = 0;
            }
        } finally {
            if (source !== null) 
                source.close();
        }
    }
    
    modifyEntry(workFile, update) {
        workFile.writeLocalEntryHeader(update);
        let dataStart = workFile.m_BaseStream.position;
        if (update.entry.isFile && (update.filename !== null)) {
            let output = workFile.getOutputStream(update.outEntry); 
            try {
                let source = this.getInputStream(update.entry); 
                try {
                    this.copyBytes(update, output, source, source.length, true);
                }
                finally {
                    source.close();
                }
            }
            finally {
                output.close();
            }
        }
        let dataEnd = workFile.m_BaseStream.position;
        update.entry.compressedSize = dataEnd - dataStart;
    }
    
    copyEntryDirect(workFile, update, destinationPosition) {
        let skipOver = false;
        if (update.entry.offset === destinationPosition.value) 
            skipOver = true;
        if (!skipOver) {
            this.m_BaseStream.position = destinationPosition.value;
            workFile.writeLocalEntryHeader(update);
            destinationPosition.value = this.m_BaseStream.position;
        }
        let sourcePosition = 0;
        let nameLengthOffset = 26;
        let entryDataOffset = update.entry.offset + nameLengthOffset;
        this.m_BaseStream.seek(entryDataOffset, 0);
        let nameLength = this.readLEUshort();
        let extraLength = this.readLEUshort();
        sourcePosition = (this.m_BaseStream.position) + (nameLength) + (extraLength);
        if (skipOver) {
            if (update.offsetBasedSize !== -1) 
                destinationPosition.value += update.offsetBasedSize;
            else 
                destinationPosition.value += ((((sourcePosition - entryDataOffset)) + nameLengthOffset + update.entry.compressedSize) + this.getDescriptorSize(update));
        }
        else {
            if (update.entry.compressedSize > 0) {
                let wrapsourcePosition562 = new RefOutArgWrapper(sourcePosition);
                this.copyEntryDataDirect(update, this.m_BaseStream, false, destinationPosition, wrapsourcePosition562);
                sourcePosition = wrapsourcePosition562.value;
            }
            this.copyDescriptorBytesDirect(update, this.m_BaseStream, destinationPosition, sourcePosition);
        }
    }
    
    copyEntry(workFile, update) {
        workFile.writeLocalEntryHeader(update);
        if (update.entry.compressedSize > 0) {
            let nameLengthOffset = 26;
            let entryDataOffset = (update.entry.offset) + nameLengthOffset;
            this.m_BaseStream.seek(entryDataOffset, 0);
            let nameLength = this.readLEUshort();
            let extraLength = this.readLEUshort();
            this.m_BaseStream.seek((nameLength) + (extraLength), 1);
            this.copyBytes(update, workFile.m_BaseStream, this.m_BaseStream, update.entry.compressedSize, false);
        }
        this.copyDescriptorBytes(update, workFile.m_BaseStream, this.m_BaseStream);
    }
    
    reopen(source) {
        if (source === null) 
            throw new Error("Failed to reopen archive - no source");
        this.isNewArchive_ = false;
        this.m_BaseStream = source;
        this.readEntries();
    }
    
    updateCommentOnly() {
        let baseLength = this.m_BaseStream.length;
        let updateFile = null;
        if (this.archiveStorage_.updateMode === FileUpdateMode.SAFE) {
            let copyStream = this.archiveStorage_.makeTemporaryCopy(this.m_BaseStream); 
            try {
                updateFile = new ZipHelperStream(null, copyStream);
                updateFile.isStreamOwner = true;
                this.m_BaseStream.close();
                this.m_BaseStream = null;
            }
            finally {
                copyStream.close();
            }
        }
        else if (this.archiveStorage_.updateMode === FileUpdateMode.DIRECT) {
            this.m_BaseStream = this.archiveStorage_.openForDirectUpdate(this.m_BaseStream);
            updateFile = new ZipHelperStream(null, this.m_BaseStream);
        }
        else {
            this.m_BaseStream.close();
            this.m_BaseStream = null;
            updateFile = new ZipHelperStream(this.name);
        }
        try {
            let locatedCentralDirOffset = updateFile.locateBlockWithSignature(ZipConstants.END_OF_CENTRAL_DIRECTORY_SIGNATURE, baseLength, ZipConstants.END_OF_CENTRAL_RECORD_BASE_SIZE, 0xffff);
            if (locatedCentralDirOffset < 0) 
                throw new Error("Cannot find central directory");
            let centralHeaderCommentSizeOffset = 16;
            updateFile.position = updateFile.position + (centralHeaderCommentSizeOffset);
            let rawComment = this.newComment_.rawComment;
            updateFile.writeLEShort(rawComment.length);
            updateFile.write(rawComment, 0, rawComment.length);
            updateFile.setLength(updateFile.position);
        } finally {
            updateFile.close();
        }
        if (this.archiveStorage_.updateMode === FileUpdateMode.SAFE) 
            this.reopen(this.archiveStorage_.convertTemporaryToFinal());
        else 
            this.readEntries();
    }
    
    runUpdates() {
        let sizeEntries = 0;
        let endOfStream = 0;
        let directUpdate = false;
        let destinationPosition = 0;
        let workFile = null;
        if (this.isNewArchive) {
            workFile = this;
            workFile.m_BaseStream.position = 0;
            directUpdate = true;
        }
        else if (this.archiveStorage_.updateMode === FileUpdateMode.DIRECT) {
            workFile = this;
            workFile.m_BaseStream.position = 0;
            directUpdate = true;
            let cmp = new ZipFile.UpdateComparer();
            for (let ii = 0; ii < this.m_Updates.length; ii++) {
                let ch = false;
                for (let jj = 0; jj < (this.m_Updates.length - 1); jj++) {
                    if (cmp.compare(this.m_Updates[jj], this.m_Updates[jj + 1]) > 0) {
                        let u = this.m_Updates[jj];
                        this.m_Updates[jj] = this.m_Updates[jj + 1];
                        this.m_Updates[jj + 1] = u;
                        ch = true;
                    }
                }
                if (!ch) 
                    break;
            }
        }
        else {
            workFile = ZipFile.createStream(this.archiveStorage_.getTemporaryOutput());
            workFile.useZip64 = this.useZip64;
        }
        try {
            for (const up of this.m_Updates) {
                let update = Utils.as(up, ZipUpdate);
                if (update !== null) {
                    switch (update.command) { 
                    case UpdateCommand.COPY: {
                        if (directUpdate) {
                            let wrapdestinationPosition563 = new RefOutArgWrapper(destinationPosition);
                            this.copyEntryDirect(workFile, update, wrapdestinationPosition563);
                            destinationPosition = wrapdestinationPosition563.value;
                        }
                        else 
                            this.copyEntry(workFile, update);
                        break;
                    }
                    case UpdateCommand.MODIFY: {
                        this.modifyEntry(workFile, update);
                        break;
                    }
                    case UpdateCommand.ADD: {
                        if (!this.isNewArchive && directUpdate) 
                            workFile.m_BaseStream.position = destinationPosition;
                        this.addEntry(workFile, update);
                        if (directUpdate) 
                            destinationPosition = workFile.m_BaseStream.position;
                        break;
                    }
                    }
                }
            }
            if (!this.isNewArchive && directUpdate) 
                workFile.m_BaseStream.position = destinationPosition;
            let centralDirOffset = workFile.m_BaseStream.position;
            for (const up of this.m_Updates) {
                let update = Utils.as(up, ZipUpdate);
                if (update !== null) 
                    sizeEntries += workFile.writeCentralDirectoryHeader(update.outEntry);
            }
            let theComment = (this.newComment_ !== null ? this.newComment_.rawComment : ZipConstants.convertToArrayStr(this.comment_));
            let zhs = new ZipHelperStream(null, workFile.m_BaseStream); 
            try {
                zhs.writeEndOfCentralDirectory(this.updateCount_, sizeEntries, centralDirOffset, theComment);
            }
            finally {
                zhs.close();
            }
            endOfStream = workFile.m_BaseStream.position;
            for (const up of this.m_Updates) {
                let update = Utils.as(up, ZipUpdate);
                if (update !== null) {
                    if ((update.crcPatchOffset > 0) && (update.outEntry.compressedSize > 0)) {
                        workFile.m_BaseStream.position = update.crcPatchOffset;
                        workFile.writeLEInt(update.outEntry.crc);
                    }
                    if (update.sizePatchOffset > 0) {
                        workFile.m_BaseStream.position = update.sizePatchOffset;
                        if (update.outEntry.localHeaderRequiresZip64) {
                            workFile.writeLeLong(update.outEntry.size, 0);
                            workFile.writeLeLong(update.outEntry.compressedSize, 0);
                        }
                        else {
                            workFile.writeLEInt(update.outEntry.compressedSize);
                            workFile.writeLEInt(update.outEntry.size);
                        }
                    }
                }
            }
        } catch (ex564) {
            if (!directUpdate && (workFile.name !== null)) 
                fs.unlinkSync(workFile.name);
            throw ex564;
        } finally {
            if (workFile !== null) 
                workFile.close();
            workFile = null;
        }
        if (directUpdate) {
            if (workFile !== null) {
                workFile.m_BaseStream.setLength(endOfStream);
                workFile.m_BaseStream.flush();
            }
            this.isNewArchive_ = false;
            this.readEntries();
        }
        else {
            this.m_BaseStream.close();
            this.reopen(this.archiveStorage_.convertTemporaryToFinal());
        }
    }
    
    checkUpdating() {
        if (this.m_Updates === null) 
            throw new Error("BeginUpdate has not been called");
    }
    
    disposeInternal(disposing) {
        if (!this.isDisposed_) {
            this.isDisposed_ = true;
            this.entries_ = Array(0).fill(null);
            if (this.isStreamOwner && (this.m_BaseStream !== null)) {
                this.m_BaseStream.close();
                this.m_BaseStream = null;
            }
            this.postUpdateCleanup();
        }
    }
    
    dispose(disposing) {
        this.disposeInternal(disposing);
    }
    
    readLEUshort() {
        let data1 = this.m_BaseStream.readByte();
        if (data1 < 0) 
            throw new Error("End of stream");
        let data2 = this.m_BaseStream.readByte();
        if (data2 < 0) 
            throw new Error("End of stream");
        return ((data1) | (data2 << 8));
    }
    
    readLEUint() {
        return ((this.readLEUshort()) | ((this.readLEUshort()) << 16));
    }
    
    locateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData) {
        let les = new ZipHelperStream(null, this.m_BaseStream); 
        try {
            return les.locateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData);
        }
        finally {
            les.close();
        }
    }
    
    readEntries() {
        if (this.m_BaseStream === null) 
            return;
        if (this.m_BaseStream.length === (0)) 
            return;
        if (this.m_BaseStream.canSeek() === false) 
            throw new Error("ZipFile stream must be seekable");
        let locatedEndOfCentralDir = this.locateBlockWithSignature(ZipConstants.END_OF_CENTRAL_DIRECTORY_SIGNATURE, this.m_BaseStream.length, ZipConstants.END_OF_CENTRAL_RECORD_BASE_SIZE, 0xffff);
        if (locatedEndOfCentralDir < 0) 
            throw new Error("Cannot find central directory");
        let thisDiskNumber = this.readLEUshort();
        let startCentralDirDisk = this.readLEUshort();
        let entriesForThisDisk = this.readLEUshort();
        let entriesForWholeCentralDir = this.readLEUshort();
        let centralDirSize = this.readLEUint();
        let offsetOfCentralDir = this.readLEUint();
        let commentSize = this.readLEUshort();
        if (Utils.compareUnsigned(commentSize, 0) > 0) {
            let comment = new Uint8Array(commentSize);
            StreamUtils.readFully(this.m_BaseStream, comment);
            this.comment_ = ZipConstants.convertToString0(comment);
        }
        else 
            this.comment_ = "";
        let isZip64 = false;
        if (Utils.compareUnsigned(thisDiskNumber, 0xffff) == 0) {
            isZip64 = true;
            let offset = this.locateBlockWithSignature(ZipConstants.ZIP64CENTRAL_DIR_LOCATOR_SIGNATURE, locatedEndOfCentralDir, 0, 0x1000);
            if (offset < 0) 
                throw new Error("Cannot find Zip64 locator");
            this.readLEUint();
            let offset64 = this.readLEUint();
            this.readLEUint();
            let totalDisks = this.readLEUint();
            this.m_BaseStream.position = offset64;
            let sig64 = this.readLEUint();
            if (sig64 !== ZipConstants.ZIP64CENTRAL_FILE_HEADER_SIGNATURE) 
                throw new Error(("Invalid Zip64 Central directory signature at " + (offset64).toString(16).toUpperCase()));
            let recordSize = this.readLEUint();
            this.readLEUint();
            let versionMadeBy = this.readLEUshort();
            let versionToExtract = this.readLEUshort();
            let thisDisk = this.readLEUint();
            let centralDirDisk = this.readLEUint();
            entriesForThisDisk = this.readLEUint();
            this.readLEUint();
            entriesForWholeCentralDir = this.readLEUint();
            this.readLEUint();
            centralDirSize = this.readLEUint();
            this.readLEUint();
            offsetOfCentralDir = this.readLEUint();
            this.readLEUint();
        }
        this.entries_ = Array(entriesForThisDisk).fill(null);
        if (!isZip64 && ((offsetOfCentralDir < ((locatedEndOfCentralDir) - (((4) + (centralDirSize))))))) {
            this.offsetOfFirstEntry = locatedEndOfCentralDir - ((4 + centralDirSize + offsetOfCentralDir));
            if (this.offsetOfFirstEntry <= 0) 
                throw new Error("Invalid embedded zip archive");
        }
        this.m_BaseStream.seek(this.offsetOfFirstEntry + offsetOfCentralDir, 0);
        for (let i = 0; i < entriesForThisDisk; i++) {
            if ((this.readLEUint()) !== ZipConstants.CENTRAL_HEADER_SIGNATURE) 
                throw new Error("Wrong Central Directory signature");
            let versionMadeBy = this.readLEUshort();
            let versionToExtract = this.readLEUshort();
            let bitFlags = this.readLEUshort();
            let method = this.readLEUshort();
            let dostime = this.readLEUint();
            let crc = this.readLEUint();
            let csize = this.readLEUint();
            let _size = this.readLEUint();
            let nameLen = this.readLEUshort();
            let extraLen = this.readLEUshort();
            let commentLen = this.readLEUshort();
            let diskStartNo = this.readLEUshort();
            let internalAttributes = this.readLEUshort();
            let externalAttributes = this.readLEUint();
            let offset = this.readLEUint();
            let buffer = new Uint8Array(Math.max(nameLen, commentLen));
            StreamUtils.readFullyEx(this.m_BaseStream, buffer, 0, nameLen);
            let _name = ZipConstants.convertToStringExt(bitFlags, buffer, nameLen);
            let entry = new ZipEntry(_name, versionToExtract, versionMadeBy, CompressionMethod.of(method));
            entry.crc = crc;
            entry.size = _size;
            entry.compressedSize = csize;
            entry.flags = bitFlags;
            entry.dosTime = dostime;
            entry.zipFileIndex = i;
            entry.offset = offset;
            entry.externalFileAttributes = externalAttributes;
            if (((bitFlags & 8)) === 0) 
                entry.cryptoCheckValue = ((crc) >>> 24);
            else 
                entry.cryptoCheckValue = ((((dostime) >>> 8)) & 0xff);
            if (extraLen > 0) {
                let extra = new Uint8Array(extraLen);
                StreamUtils.readFully(this.m_BaseStream, extra);
                entry.extraData = extra;
            }
            entry.processExtraData(false);
            if (commentLen > 0) {
                StreamUtils.readFullyEx(this.m_BaseStream, buffer, 0, commentLen);
                entry.comment = ZipConstants.convertToStringExt(bitFlags, buffer, commentLen);
            }
            this.entries_[i] = entry;
        }
    }
    
    locateEntry(entry) {
        return this.testLocalHeader(entry, ZipFileHeaderTest.EXTRACT);
    }
    
    static static_constructor() {
        ZipFile.defaultBufferSize = 4096;
    }
}


/**
 * Class used to sort updates.
 */
ZipFile.UpdateComparer = class  {
    
    /**
     * Compares two objects and returns a value indicating whether one is 
     * less than, equal to or greater than the other.
     * @param x First object to compare
     * @param y Second object to compare.
     * @return Compare result.
     */
    compare(x, y) {
        const UpdateCommand = require("./UpdateCommand");
        const ZipUpdate = require("./ZipUpdate");
        let zx = Utils.as(x, ZipUpdate);
        let zy = Utils.as(y, ZipUpdate);
        let result = 0;
        if (zx === null) {
            if (zy === null) 
                result = 0;
            else 
                result = -1;
        }
        else if (zy === null) 
            result = 1;
        else {
            let xCmdValue = (((zx.command === UpdateCommand.COPY) || (zx.command === UpdateCommand.MODIFY)) ? 0 : 1);
            let yCmdValue = (((zy.command === UpdateCommand.COPY) || (zy.command === UpdateCommand.MODIFY)) ? 0 : 1);
            result = xCmdValue - yCmdValue;
            if (result === 0) {
                let offsetDiff = zx.entry.offset - zy.entry.offset;
                if (offsetDiff < 0) 
                    result = -1;
                else if (offsetDiff === 0) 
                    result = 0;
                else 
                    result = 1;
            }
        }
        return result;
    }
}


ZipFile.static_constructor();

module.exports = ZipFile