This repository has been archived on 2024-02-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
teamcity-executors-test-task/src/main/kotlin/ziputils/CentralDirectoryFileHeader.kt
Gleb Koval 694b8d4d28
All checks were successful
Test Workflow / Lint and test library (push) Successful in 17m11s
Publish Workflow / Publish library (push) Successful in 8m49s
CLI implementation & linting (#2)
Implement a basic CLI interface & add linting to the project.

Reviewed-on: #2
2023-12-30 06:36:49 +00:00

119 lines
4.7 KiB
Kotlin

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<ExtraFieldRecord>,
) {
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 <offset> 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 InvalidDataException("Invalid signature")
}
val extraFieldRecords = mutableListOf<ExtraFieldRecord>()
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..<offset + SIZE + nameLength.toInt())),
extraFieldRecords = extraFieldRecords,
)
if (data.size - offset < cen.size) {
throw InvalidDataException("CEN is too short")
}
// Parse extra field records
val extraFieldsBuf =
ByteBuffer.wrap(
data,
offset + SIZE + cen.nameLength.toInt(),
cen.extraFieldLength.toInt(),
).order(ByteOrder.LITTLE_ENDIAN)
while (extraFieldsBuf.remaining() > 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
}
}
}