package ziputils import java.nio.ByteBuffer import java.nio.ByteOrder /** * Represents a partial ZIP central directory file header. */ internal class CentralDirectoryFileHeader( val compressedSize: UInt, val uncompressedSize: UInt, val nameLength: UShort, val extraFieldLength: UShort, val commentLength: UShort, val disk: UShort, val localHeaderOffset: UInt, val fileName: String, val extraFieldRecords: List ) { val size: Int get() = SIZE + nameLength.toInt() + extraFieldLength.toInt() + commentLength.toInt() companion object { const val SIGNATURE = 0x02014b50U const val SIZE = 46 /** * Create `CentralDirectoryFileHeader` from raw byte data. * @throws InvalidDataException provided `ByteArray` is not a supported CEN. * @param data Raw byte data. * @param offset Skip first bytes in data array. * @return A `CentralDirectoryFileHeader`. */ @Throws(InvalidDataException::class) fun fromByteArray(data: ByteArray, offset: Int): CentralDirectoryFileHeader { if (data.size - offset < SIZE) { throw InvalidDataException("CEN must be at least 46 bytes") } val buf = ByteBuffer.wrap(data, offset, 46).order(ByteOrder.LITTLE_ENDIAN) if (buf.getInt().toUInt() != SIGNATURE) { throw InvalidSignatureException("Invalid signature") } val extraFieldRecords = mutableListOf() val nameLength = buf.getShort(offset + 28).toUShort() buf.position(offset + 20) val cen = CentralDirectoryFileHeader( compressedSize = buf.getInt().toUInt(), uncompressedSize = buf.getInt().toUInt(), nameLength = nameLength .also { buf.position(offset + 30) }, extraFieldLength = buf.getShort().toUShort(), commentLength = buf.getShort().toUShort(), disk = buf.getShort().toUShort() .also { buf.position(offset + 42) }, localHeaderOffset = buf.getInt().toUInt(), fileName = String(data.sliceArray(offset + SIZE.. 0) { val id = extraFieldsBuf.getShort().toUShort() val size = extraFieldsBuf.getShort().toUShort() extraFieldRecords.add(when (id) { Zip64ExtraFieldRecord.ID -> { Zip64ExtraFieldRecord( size, if (cen.uncompressedSize == 0xffffffffU) { extraFieldsBuf.getLong().toULong() } else null, if (cen.compressedSize == 0xffffffffU) { extraFieldsBuf.getLong().toULong() } else null, if (cen.localHeaderOffset == 0xffffffffU) { extraFieldsBuf.getLong().toULong() } else null, if (cen.disk == 0xffffU.toUShort()) { extraFieldsBuf.getInt().toUInt() } else null ) } else -> { extraFieldsBuf.position(extraFieldsBuf.position() + size.toInt()) ExtraFieldRecord(id, size) } }) } return cen } } }