/// <summary> /// Reads the contents of the archive's Central Directory and returns an ArchiveInformation record with the /// CompressedSize and UncompressedSize fields populated from the contents of the Central Directory file header /// records. /// </summary> /// <param name="eocdRecord">The End of Central Directory record which gives us information about the Central Directory's location</param> /// <returns>The pre-populated ArchiveInformation record</returns> /// <exception cref="InvalidArchiveException"></exception> private ArchiveInformation ReadCentralDirectoryContents(ZipEndOfCentralDirectoryRecord eocdRecord) { // -- Get a new info record ArchiveInformation info = new ArchiveInformation(); info.UncompressedSize = 0; info.CompressedSize = 0; // -- Open the correct archive part and seek to the first Central Directory record Open(eocdRecord.CDDisk + 1); InputStream.Seek((long)eocdRecord.CDOffset, SeekOrigin.Begin); // -- Loop all entries for (int i = 0; i < eocdRecord.NumFilesInCD; i++) { ZipCentralDirectoryFileHeader cdHeader = new ZipCentralDirectoryFileHeader(); cdHeader.Signature = ReadULong(); if (cdHeader.Signature != BitConverter.ToUInt32(new byte[] { 0x50, 0x4b, 0x01, 0x02 }, 0)) { throw new InvalidArchiveException(String.Format(Language.ResourceManager.GetString("ERR_FORMAT_INVALID_CD_HEADER_AT_POSITION"), CurrentPartNumber, InputStream.Position - 4)); } cdHeader.VersionMadeBy = ReadUShort(); cdHeader.VersionToExtract = ReadUShort(); cdHeader.Flags = ReadUShort(); cdHeader.CompressionMethod = ReadUShort(); cdHeader.LastModTime = ReadUShort(); cdHeader.LastModDate = ReadUShort(); cdHeader.CRC32 = ReadULong(); cdHeader.CompressedSize = ReadULong(); cdHeader.UncompressedSize = ReadULong(); cdHeader.FileNameLength = ReadUShort(); cdHeader.ExtraFieldLength = ReadUShort(); cdHeader.FileCommentLength = ReadUShort(); cdHeader.DiskNumberStart = ReadUShort(); cdHeader.InternalFileAttributes = ReadUShort(); cdHeader.ExternalFileAttributes = ReadULong(); cdHeader.RelativeOffset = ReadULong(); cdHeader.Filename = ReadUtf8String(cdHeader.FileNameLength); cdHeader.Comment = ""; if (cdHeader.FileCommentLength > 0) { cdHeader.Comment = ReadUtf8String(cdHeader.FileCommentLength); } info.CompressedSize += cdHeader.CompressedSize; info.UncompressedSize += cdHeader.UncompressedSize; } return(info); }
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; } } }