Initial implementations
This commit is contained in:
93
src/main/kotlin/ziputils/CentralDirectoryFileHeader.kt
Normal file
93
src/main/kotlin/ziputils/CentralDirectoryFileHeader.kt
Normal file
@@ -0,0 +1,93 @@
|
||||
package ziputils
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
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.
|
||||
*/
|
||||
@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<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
|
||||
}
|
||||
}
|
||||
}
|
25
src/main/kotlin/ziputils/EndOfCentralDirectoryLocator.kt
Normal file
25
src/main/kotlin/ziputils/EndOfCentralDirectoryLocator.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
package ziputils
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
internal class EndOfCentralDirectoryLocator(
|
||||
val endOfCentralDirectory64Offset: ULong
|
||||
) {
|
||||
companion object {
|
||||
const val SIGNATURE = 0x07064b50U
|
||||
const val SIZE = 20
|
||||
@Throws(InvalidDataException::class)
|
||||
fun fromByteArray(data: ByteArray, offset: Int): EndOfCentralDirectoryLocator {
|
||||
if (data.size - offset < SIZE) {
|
||||
throw InvalidDataException("EOCD64 locator must be at least 20 bytes")
|
||||
}
|
||||
val buf = ByteBuffer.wrap(data, offset, SIZE).order(ByteOrder.LITTLE_ENDIAN)
|
||||
if (buf.getInt().toUInt() != SIGNATURE) {
|
||||
throw InvalidSignatureException("Invalid signature")
|
||||
}
|
||||
buf.position(offset + 8)
|
||||
return EndOfCentralDirectoryLocator(buf.getLong().toULong())
|
||||
}
|
||||
}
|
||||
}
|
38
src/main/kotlin/ziputils/EndOfCentralDirectoryRecord.kt
Normal file
38
src/main/kotlin/ziputils/EndOfCentralDirectoryRecord.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package ziputils
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
/**
|
||||
* Partial End of Central Directory record class.
|
||||
* Only supports data required by the backup tool.
|
||||
*/
|
||||
internal class EndOfCentralDirectoryRecord(
|
||||
val centralDirectoryOffset: UInt
|
||||
) {
|
||||
fun eocd64Required(): Boolean =
|
||||
centralDirectoryOffset == 0xffffffffU
|
||||
|
||||
companion object {
|
||||
const val SIGNATURE = 0x06054b50U
|
||||
const val SIZE = 22
|
||||
/**
|
||||
* Create EndOfCentralDirectoryRecord from raw byte data.
|
||||
* @throws InvalidDataException provided ByteArray is not a supported EOCD64.
|
||||
*/
|
||||
@Throws(InvalidDataException::class)
|
||||
fun fromByteArray(data: ByteArray, offset: Int): EndOfCentralDirectoryRecord {
|
||||
if (data.size - offset < SIZE) {
|
||||
throw InvalidDataException("EOCD must be at least 22 bytes")
|
||||
}
|
||||
val buf = ByteBuffer.wrap(data, offset, SIZE).order(ByteOrder.LITTLE_ENDIAN)
|
||||
if (buf.getInt().toUInt() != SIGNATURE) {
|
||||
throw InvalidSignatureException("Invalid signature")
|
||||
}
|
||||
buf.position(offset + 16)
|
||||
return EndOfCentralDirectoryRecord(
|
||||
centralDirectoryOffset = buf.getInt().toUInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
35
src/main/kotlin/ziputils/EndOfCentralDirectoryRecord64.kt
Normal file
35
src/main/kotlin/ziputils/EndOfCentralDirectoryRecord64.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
package ziputils
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
/**
|
||||
* Partial End of Central Directory record (ZIP64) class.
|
||||
* Only supports data required by the backup tool.
|
||||
*/
|
||||
internal class EndOfCentralDirectoryRecord64(
|
||||
val centralDirectoryOffset: ULong
|
||||
) {
|
||||
companion object {
|
||||
const val SIGNATURE = 0x06064b50U
|
||||
const val SIZE = 56
|
||||
/**
|
||||
* Create EndOfCentralDirectoryRecord64 from raw byte data.
|
||||
* @throws InvalidDataException provided ByteArray is not a supported EOCD.
|
||||
*/
|
||||
@Throws(InvalidDataException::class)
|
||||
fun fromByteArray(data: ByteArray, offset: Int): EndOfCentralDirectoryRecord64 {
|
||||
if (data.size - offset < SIZE) {
|
||||
throw InvalidDataException("EOCD64 must be at least 56 bytes")
|
||||
}
|
||||
val buf = ByteBuffer.wrap(data, offset, SIZE).order(ByteOrder.LITTLE_ENDIAN)
|
||||
if (buf.getInt().toUInt() != SIGNATURE) {
|
||||
throw InvalidSignatureException("Invalid signature")
|
||||
}
|
||||
buf.position(offset + 48)
|
||||
return EndOfCentralDirectoryRecord64(
|
||||
centralDirectoryOffset = buf.getLong().toULong()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
4
src/main/kotlin/ziputils/Exceptions.kt
Normal file
4
src/main/kotlin/ziputils/Exceptions.kt
Normal file
@@ -0,0 +1,4 @@
|
||||
package ziputils
|
||||
|
||||
class InvalidDataException(message: String): Exception(message)
|
||||
class InvalidSignatureException(message: String): Exception(message)
|
6
src/main/kotlin/ziputils/ExtraFieldRecord.kt
Normal file
6
src/main/kotlin/ziputils/ExtraFieldRecord.kt
Normal file
@@ -0,0 +1,6 @@
|
||||
package ziputils
|
||||
|
||||
internal open class ExtraFieldRecord(
|
||||
val id: UShort,
|
||||
val size: UShort
|
||||
)
|
6
src/main/kotlin/ziputils/README.md
Normal file
6
src/main/kotlin/ziputils/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# ZipUtils
|
||||
|
||||
These are **internal** utility classes for reading zip file metadata. They only implement what is required for the
|
||||
backup tool, and no more.
|
||||
|
||||
Specifically, we are looking at the [ZIP v6.3.9 specification](https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT).
|
13
src/main/kotlin/ziputils/Zip64ExtraFieldRecord.kt
Normal file
13
src/main/kotlin/ziputils/Zip64ExtraFieldRecord.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package ziputils
|
||||
|
||||
internal class Zip64ExtraFieldRecord(
|
||||
size: UShort,
|
||||
val uncompressedSize: ULong?,
|
||||
val compressedSize: ULong?,
|
||||
val localHeaderOffset: ULong?,
|
||||
val disk: UInt?
|
||||
): ExtraFieldRecord(ID, size) {
|
||||
companion object {
|
||||
const val ID: UShort = 0x0001U
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user