예제 #1
0
        /// <summary>
        /// Reads the local file header in the ZIP archive
        /// </summary>
        /// <returns>The local file header</returns>
        private ZipLocalFileHeader ReadFileHeader()
        {
            ZipLocalFileHeader header = new ZipLocalFileHeader();

            header.Signature         = ReadULong();
            header.VersionToExtract  = ReadUShort();
            header.Flags             = ReadUShort();
            header.CompressionMethod = ReadUShort();
            header.LastModTime       = ReadUShort();
            header.LastModDate       = ReadUShort();
            header.CRC32             = ReadULong();
            header.CompressedSize    = ReadULong();
            header.UncompressedSize  = ReadULong();
            header.FileNameLength    = ReadUShort();
            header.ExtraFieldLength  = ReadUShort();
            header.Filename          = ReadUtf8String(header.FileNameLength);

            if (header.ExtraFieldLength > 0)
            {
                SkipBytes(header.ExtraFieldLength);
            }

            // Invoke the OnEntityEvent event. We need to do some work to get there, through...
            // -- Create a new archive information record
            EntityInformation info = new EntityInformation();

            // -- Incorporate bits from the file header
            info.CompressedSize   = header.CompressedSize;
            info.UncompressedSize = header.UncompressedSize;
            info.StoredName       = header.Filename;
            // -- Get the absolute path of the file
            info.AbsoluteName = "";

            if (DataWriter != null)
            {
                info.AbsoluteName = DataWriter.GetAbsoluteFilePath(header.Filename);
            }
            // -- Get some bits from the currently open archive file
            info.PartNumber = CurrentPartNumber ?? 1;
            info.PartOffset = InputStream.Position;
            // -- Create the event arguments object
            EntityEventArgs args = new EntityEventArgs(info);

            // -- Finally, invoke the event
            OnEntityEvent(args);

            return(header);
        }
예제 #2
0
        /// <summary>
        /// Processes a data block in a JPA file located in the current file position
        /// </summary>
        /// <param name="dataBlockHeader">The header of the block being processed</param>
        /// <param name="token">A cancellation token, allowing the called to cancel the processing</param>
        public void ProcessDataBlock(ZipLocalFileHeader dataBlockHeader, CancellationToken token)
        {
            // Get the compression type
            TCompressionType CompressionType = TCompressionType.Uncompressed;

            switch (dataBlockHeader.CompressionMethod)
            {
            case 0:
                CompressionType = TCompressionType.Uncompressed;
                break;

            case 8:
                CompressionType = TCompressionType.GZip;
                break;

            case 12:
                CompressionType = TCompressionType.BZip2;
                break;

            default:
                throw new InvalidArchiveException(Language.ResourceManager.GetString("ERR_FORMAT_INVALID_COMPRESSION_METHOD"));
            }

            // Decide on the file type
            TEntityType EntityType = TEntityType.File;

            if (dataBlockHeader.Filename.EndsWith("/"))
            {
                EntityType = TEntityType.Directory;
            }
            else if (dataBlockHeader.VersionToExtract == BitConverter.ToUInt16(new byte[] { 0x10, 0x03 }, 0))
            {
                EntityType = TEntityType.Symlink;
            }

            // Process the data block
            ProcessDataBlock(dataBlockHeader.CompressedSize, dataBlockHeader.UncompressedSize, CompressionType,
                             EntityType, dataBlockHeader.Filename, token);
        }
        private static void GetStreamingAssetsInfoFromJar(string apkPath, List <string> paths, List <PartInfo> parts)
        {
            using (var stream = File.OpenRead(apkPath))
                using (var reader = new BinaryReader(stream))
                {
                    if (!stream.CanRead)
                    {
                        throw new ArgumentException();
                    }
                    if (!stream.CanSeek)
                    {
                        throw new ArgumentException();
                    }

                    long expectedNumberOfEntries;
                    long centralDirectoryStart;
                    ZipArchiveUtils.ReadEndOfCentralDirectory(stream, reader, out expectedNumberOfEntries, out centralDirectoryStart);

                    try
                    {
                        stream.Seek(centralDirectoryStart, SeekOrigin.Begin);

                        long numberOfEntries = 0;

                        ZipCentralDirectoryFileHeader header;

                        const int    prefixLength = 7;
                        const string prefix       = "assets/";
                        const string assetsPrefix = "assets/bin/";
                        Debug.Assert(prefixLength == prefix.Length);

                        while (ZipCentralDirectoryFileHeader.TryReadBlock(reader, out header))
                        {
                            if (header.CompressedSize != header.UncompressedSize)
                            {
                                // we only want uncompressed files
                            }
                            else
                            {
                                var fileName = Encoding.UTF8.GetString(header.Filename);
                                if (fileName.StartsWith(prefix))
                                {
                                    // ignore normal assets...
                                    if (fileName.StartsWith(assetsPrefix))
                                    {
                                        // Note: if you put bin directory in your StreamingAssets you will get false negative here
                                    }
                                    else
                                    {
                                        var relativePath = fileName.Substring(prefixLength - 1);
                                        var entry        = new PartInfo()
                                        {
                                            crc32  = header.Crc32,
                                            offset = header.RelativeOffsetOfLocalHeader, // this offset will need fixing later on
                                            size   = header.UncompressedSize
                                        };

                                        var index = paths.BinarySearch(relativePath, StringComparer.OrdinalIgnoreCase);
                                        if (index >= 0)
                                        {
                                            throw new System.InvalidOperationException("Paths duplicate! " + fileName);
                                        }

                                        paths.Insert(~index, relativePath);
                                        parts.Insert(~index, entry);
                                    }
                                }
                            }

                            numberOfEntries++;
                        }

                        if (numberOfEntries != expectedNumberOfEntries)
                        {
                            throw new ZipArchiveException("Number of entries does not match");
                        }
                    }
                    catch (EndOfStreamException ex)
                    {
                        throw new ZipArchiveException("CentralDirectoryInvalid", ex);
                    }

                    // now fix offsets
                    for (int i = 0; i < parts.Count; ++i)
                    {
                        var entry = parts[i];
                        stream.Seek(entry.offset, SeekOrigin.Begin);

                        if (!ZipLocalFileHeader.TrySkipBlock(reader))
                        {
                            throw new ZipArchiveException("Local file header corrupt");
                        }

                        entry.offset = stream.Position;

                        parts[i] = entry;
                    }
                }
        }
        private static void GetStreamingAssetsInfoFromJar(string apkPath, List <string> paths, List <PartInfo> parts)
        {
            using (var stream = File.OpenRead(apkPath))
                using (var reader = new BinaryReader(stream))
                {
                    if (!stream.CanRead)
                    {
                        throw new ArgumentException();
                    }
                    if (!stream.CanSeek)
                    {
                        throw new ArgumentException();
                    }

                    long expectedNumberOfEntries;
                    long centralDirectoryStart;
                    ZipArchiveUtils.ReadEndOfCentralDirectory(stream, reader, out expectedNumberOfEntries, out centralDirectoryStart);

                    try
                    {
                        stream.Seek(centralDirectoryStart, SeekOrigin.Begin);

                        long numberOfEntries = 0;

                        ZipCentralDirectoryFileHeader header;

                        const int    prefixLength = 7;
                        const string prefix       = "assets/";
                        const string assetsPrefix = "assets/bin/";
                        Debug.Assert(prefixLength == prefix.Length);

                        while (ZipCentralDirectoryFileHeader.TryReadBlock(reader, out header))
                        {
                            if (header.CompressedSize != header.UncompressedSize)
                            {
#if UNITY_ASSERTIONS
                                var fileName = Encoding.UTF8.GetString(header.Filename);
                                if (fileName.StartsWith(prefix) && !fileName.StartsWith(assetsPrefix))
                                {
                                    bool isStreamingAsset = true;
                                    AndroidIsCompressedFileStreamingAsset(fileName, ref isStreamingAsset);
                                    if (isStreamingAsset)
                                    {
                                        Debug.LogAssertionFormat("BetterStreamingAssets: file {0} is where Streaming Assets are put, but is compressed. " +
                                                                 "If this is a App Bundle build, see README for a possible workaround. " +
                                                                 "If this file is not a Streaming Asset (has been on purpose by hand or by another plug-in), implement " +
                                                                 "BetterStreamingAssets.AndroidIsCompressedFileStreamingAsset partial method to prevent this message from appearing again. ",
                                                                 fileName);
                                    }
                                }
#endif
                                // we only want uncompressed files
                            }
                            else
                            {
                                var fileName = Encoding.UTF8.GetString(header.Filename);

                                if (fileName.EndsWith("/"))
                                {
                                    // there's some strangeness when it comes to OBB: directories are listed as files
                                    // simply ignoring them should be enough
                                    Debug.Assert(header.UncompressedSize == 0);
                                }
                                else if (fileName.StartsWith(prefix))
                                {
                                    // ignore normal assets...
                                    if (fileName.StartsWith(assetsPrefix))
                                    {
                                        // Note: if you put bin directory in your StreamingAssets you will get false negative here
                                    }
                                    else
                                    {
                                        var relativePath = fileName.Substring(prefixLength - 1);
                                        var entry        = new PartInfo()
                                        {
                                            crc32  = header.Crc32,
                                            offset = header.RelativeOffsetOfLocalHeader, // this offset will need fixing later on
                                            size   = header.UncompressedSize
                                        };

                                        var index = paths.BinarySearch(relativePath, StringComparer.OrdinalIgnoreCase);
                                        if (index >= 0)
                                        {
                                            throw new System.InvalidOperationException("Paths duplicate! " + fileName);
                                        }

                                        paths.Insert(~index, relativePath);
                                        parts.Insert(~index, entry);
                                    }
                                }
                            }

                            numberOfEntries++;
                        }

                        if (numberOfEntries != expectedNumberOfEntries)
                        {
                            throw new ZipArchiveException("Number of entries does not match");
                        }
                    }
                    catch (EndOfStreamException ex)
                    {
                        throw new ZipArchiveException("CentralDirectoryInvalid", ex);
                    }

                    // now fix offsets
                    for (int i = 0; i < parts.Count; ++i)
                    {
                        var entry = parts[i];
                        stream.Seek(entry.offset, SeekOrigin.Begin);

                        if (!ZipLocalFileHeader.TrySkipBlock(reader))
                        {
                            throw new ZipArchiveException("Local file header corrupt");
                        }

                        entry.offset = stream.Position;

                        parts[i] = entry;
                    }
                }
        }
예제 #5
0
        /// <summary>
        /// Implements the Extract method which extracts a backup archive. A DataWriter must be already assigned and configured or an
        /// exception will be raised.
        /// </summary>
        public override void Extract(CancellationToken token)
        {
            // Initialize
            Close();
            Progress.FilePosition        = 0;
            Progress.RunningCompressed   = 0;
            Progress.RunningUncompressed = 0;
            Progress.Status        = ExtractionStatus.Running;
            Progress.LastException = null;
            ProgressEventArgs args;

            ZipEndOfCentralDirectoryRecord eocdRecord;

            try
            {
                // Read the End of Central Directory header
                eocdRecord = ReadEndOfCentralDirectory();

                // Reset the position to the very start of the archive
                Close();
                Progress.FilePosition        = 0;
                Progress.RunningCompressed   = 0;
                Progress.RunningUncompressed = 0;

                // Check for a spanned archive signature
                CheckForSpannedArchiveSignature();

                // Invoke event at start of extraction
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                while ((CurrentPartNumber != null) && (Progress.FilePosition < eocdRecord.TotalSize))
                {
                    // We have reached the start of the Central Directory, i.e. we're finished extracting
                    if ((CurrentPartNumber == (eocdRecord.CDDisk + 1)) && (Progress.FilePosition == eocdRecord.CDOffset))
                    {
                        break;
                    }

                    ZipLocalFileHeader fileHeader = ReadFileHeader();

                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    ProcessDataBlock(fileHeader, token);

                    args = new ProgressEventArgs(Progress);
                    OnProgressEvent(args);
                }
            }
            catch (OperationCanceledException cancelledException)
            {
                // The operation was cancelled. Set the state to Idle and reset the extraction.
                Close();
                Progress.FilePosition        = 0;
                Progress.RunningCompressed   = 0;
                Progress.RunningUncompressed = 0;
                Progress.Status        = ExtractionStatus.Idle;
                Progress.LastException = cancelledException;

                // Invoke an event notifying susbcribers about the cancelation.
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                return;
            }
            catch (Exception errorException)
            {
                // Any other exception. Close the option file and set the status to error.
                Close();
                Progress.Status        = ExtractionStatus.Error;
                Progress.LastException = errorException;

                // Invoke an event notifying of the error state
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                return;
            }

            // Invoke an event signaling the end of extraction
            Progress.Status = ExtractionStatus.Finished;
            args            = new ProgressEventArgs(Progress);
            OnProgressEvent(args);
        }