open static method

Implementation

static Future<UEpubArchive> open(UCachedByteSource source) async {
  final int length = source.length;
  if (length < 22) throw const UDocError(code: UDocErrorCode.corrupt, message: "Not a valid archive");
  final int tailLength = length < 66000 ? length : 66000;
  final Uint8List tail = await source.read(length - tailLength, tailLength);
  final UDocCursor cursor = UDocCursor(tail);
  final int eocd = cursor.lastIndexOf(const <int>[0x50, 0x4B, 0x05, 0x06]);
  if (eocd < 0) throw const UDocError(code: UDocErrorCode.corrupt, message: "Archive directory not found");
  int total = _readU16(tail, eocd + 10);
  int directorySize = _readU32(tail, eocd + 12);
  int directoryOffset = _readU32(tail, eocd + 16);
  final int locator = cursor.lastIndexOf(const <int>[0x50, 0x4B, 0x06, 0x07]);
  if (locator >= 0 && directoryOffset == 0xFFFFFFFF) {
    final int zip64Offset = _readU64(tail, locator + 8);
    if (zip64Offset >= 0 && zip64Offset < length) {
      final Uint8List header = await source.read(zip64Offset, 56);
      if (header.length >= 56 && header[0] == 0x50 && header[1] == 0x4B && header[2] == 0x06 && header[3] == 0x06) {
        total = _readU64(header, 32);
        directorySize = _readU64(header, 40);
        directoryOffset = _readU64(header, 48);
      }
    }
  }
  if (directoryOffset < 0 || directoryOffset >= length) throw const UDocError(code: UDocErrorCode.corrupt, message: "Archive directory is out of range");
  final int readSize = directorySize <= 0 || directoryOffset + directorySize > length ? length - directoryOffset : directorySize;
  final Uint8List directory = await source.read(directoryOffset, readSize);
  final Map<String, UEpubZipEntry> entries = <String, UEpubZipEntry>{};
  int position = 0;
  int count = 0;
  while (position + 46 <= directory.length && (total <= 0 || count < total + 8)) {
    if (!(directory[position] == 0x50 && directory[position + 1] == 0x4B && directory[position + 2] == 0x01 && directory[position + 3] == 0x02)) break;
    final int method = _readU16(directory, position + 10);
    int compressedSize = _readU32(directory, position + 20);
    int uncompressedSize = _readU32(directory, position + 24);
    final int nameLength = _readU16(directory, position + 28);
    final int extraLength = _readU16(directory, position + 30);
    final int commentLength = _readU16(directory, position + 32);
    int localOffset = _readU32(directory, position + 42);
    final int nameStart = position + 46;
    if (nameStart + nameLength > directory.length) break;
    final String name = utf8.decode(Uint8List.sublistView(directory, nameStart, nameStart + nameLength), allowMalformed: true);
    final int extraStart = nameStart + nameLength;
    if (compressedSize == 0xFFFFFFFF || uncompressedSize == 0xFFFFFFFF || localOffset == 0xFFFFFFFF) {
      int extraPosition = extraStart;
      while (extraPosition + 4 <= extraStart + extraLength && extraPosition + 4 <= directory.length) {
        final int headerId = _readU16(directory, extraPosition);
        final int size = _readU16(directory, extraPosition + 2);
        if (headerId == 0x0001) {
          int field = extraPosition + 4;
          if (uncompressedSize == 0xFFFFFFFF && field + 8 <= directory.length) {
            uncompressedSize = _readU64(directory, field);
            field += 8;
          }
          if (compressedSize == 0xFFFFFFFF && field + 8 <= directory.length) {
            compressedSize = _readU64(directory, field);
            field += 8;
          }
          if (localOffset == 0xFFFFFFFF && field + 8 <= directory.length) localOffset = _readU64(directory, field);
          break;
        }
        extraPosition += 4 + size;
      }
    }
    entries[name] = UEpubZipEntry(name: name, method: method, compressedSize: compressedSize, size: uncompressedSize, headerOffset: localOffset);
    position = extraStart + extraLength + commentLength;
    count++;
  }
  if (entries.isEmpty) throw const UDocError(code: UDocErrorCode.corrupt, message: "Archive has no entries");
  return UEpubArchive._(source, entries);
}