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);
}