208 lines
6.5 KiB
Kotlin
208 lines
6.5 KiB
Kotlin
/*
|
|
* Copyright 2023 dorkbox, llc
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package dorkbox.cabParser.structure
|
|
|
|
import dorkbox.bytes.LittleEndian
|
|
import dorkbox.cabParser.CabException
|
|
import dorkbox.cabParser.CorruptCabException
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.nio.charset.Charset
|
|
import java.util.*
|
|
|
|
class CabFileEntry {
|
|
/** uncompressed size of this file in bytes , 4bytes */
|
|
var cbFile: Long = 0
|
|
|
|
/** uncompressed offset of this file in the folder , 4bytes */
|
|
var offFolderStart: Long = 0
|
|
|
|
/** index into the CFFOLDER area , 2bytes */
|
|
var iFolder = 0
|
|
|
|
/** time/date stamp for this file , 2bytes */
|
|
var date = Date()
|
|
|
|
/** attribute flags for this file , 2bytes */
|
|
var attribs = 0
|
|
|
|
/** name of this file , 1*n bytes */
|
|
lateinit var name: String
|
|
|
|
var applicationData: Any? = null
|
|
|
|
@Throws(IOException::class, CabException::class)
|
|
fun read(input: InputStream) {
|
|
val arrayOfByte = ByteArray(256)
|
|
cbFile = LittleEndian.UInt_.from(input).toLong()
|
|
offFolderStart = LittleEndian.UInt_.from(input).toLong()
|
|
iFolder = LittleEndian.UShort_.from(input).toInt()
|
|
val timeA: Int = LittleEndian.UShort_.from(input).toInt()
|
|
val timeB: Int = LittleEndian.UShort_.from(input).toInt()
|
|
date = getDate(timeA, timeB)
|
|
attribs = LittleEndian.UShort_.from(input).toInt()
|
|
|
|
var i = 0
|
|
i = 0
|
|
while (i < arrayOfByte.size) {
|
|
val m = input.read()
|
|
if (m == -1) {
|
|
throw CorruptCabException("EOF reading cffile")
|
|
}
|
|
arrayOfByte[i] = m.toByte()
|
|
if (m == 0) {
|
|
break
|
|
}
|
|
i++
|
|
}
|
|
if (i >= arrayOfByte.size) {
|
|
throw CorruptCabException("cffile filename not null terminated")
|
|
}
|
|
name = if (attribs and NAME_IS_UTF == NAME_IS_UTF) {
|
|
readUtfString(arrayOfByte) ?: throw CorruptCabException("invalid name utf8 code")
|
|
}
|
|
else {
|
|
String(arrayOfByte, 0, i, US_ASCII).trim { it <= ' ' }
|
|
}
|
|
}
|
|
|
|
var isReadOnly: Boolean
|
|
get() = attribs and READONLY != 0
|
|
set(bool) {
|
|
if (bool) {
|
|
attribs = attribs or 1
|
|
return
|
|
}
|
|
attribs = attribs and -2
|
|
}
|
|
var isHidden: Boolean
|
|
get() = attribs and HIDDEN != 0
|
|
set(bool) {
|
|
if (bool) {
|
|
attribs = attribs or 2
|
|
return
|
|
}
|
|
attribs = attribs and -3
|
|
}
|
|
var isSystem: Boolean
|
|
get() = attribs and SYSTEM != 0
|
|
set(bool) {
|
|
if (bool) {
|
|
attribs = attribs or 4
|
|
return
|
|
}
|
|
attribs = attribs and -5
|
|
}
|
|
var isArchive: Boolean
|
|
get() = attribs and ARCHIVE != 0
|
|
set(bool) {
|
|
if (bool) {
|
|
attribs = attribs or 32
|
|
return
|
|
}
|
|
attribs = attribs and -33
|
|
}
|
|
var size: Long
|
|
get() = cbFile
|
|
set(size) {
|
|
cbFile = size.toInt().toLong()
|
|
}
|
|
|
|
private fun getDate(dateInfo: Int, timeInfo: Int): Date {
|
|
val i = dateInfo and 0x1F
|
|
val j = (dateInfo ushr 5) - 1 and 0xF
|
|
val k = (dateInfo ushr 9) + 80
|
|
val m = timeInfo and 0x1F shl 1
|
|
val n = timeInfo ushr 5 and 0x3F
|
|
val i1 = timeInfo ushr 11 and 0x1F
|
|
return Date(k, j, i, i1, n, m)
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return name
|
|
}
|
|
|
|
companion object {
|
|
val US_ASCII = Charset.forName("US-ASCII")
|
|
|
|
/** file is read-only (in HEX) */
|
|
const val READONLY = 0x01
|
|
|
|
/** file is hidden (in HEX) */
|
|
const val HIDDEN = 0x02
|
|
|
|
/** file is a system file (in HEX) */
|
|
const val SYSTEM = 0x04
|
|
|
|
/** file modified since last backup (in HEX) */
|
|
const val ARCHIVE = 0x20
|
|
|
|
/** szName[] contains UTF (in HEX) */
|
|
const val NAME_IS_UTF = 0x80
|
|
|
|
private fun readUtfString(stringBytes: ByteArray): String? {
|
|
var j = 0
|
|
var stringSize = 0
|
|
var k = 0
|
|
|
|
// count the size of the string
|
|
stringSize = 0
|
|
while (stringBytes[stringSize].toInt() != 0) {
|
|
stringSize++
|
|
}
|
|
val stringChars = CharArray(stringSize)
|
|
k = 0
|
|
while (stringBytes[j].toInt() != 0) {
|
|
var m = (stringBytes[j++].toInt() and 0xFF).toChar().code
|
|
if (m < 128) {
|
|
stringChars[k] = m.toChar()
|
|
}
|
|
else {
|
|
if (m < 192) {
|
|
return null
|
|
}
|
|
if (m < 224) {
|
|
stringChars[k] = (m and 0x1F shl 6).toChar()
|
|
m = (stringBytes[j++].toInt() and 0xFF).toChar().code
|
|
if (m < 128 || m > 191) {
|
|
return null
|
|
}
|
|
stringChars[k] = (stringChars[k].code or (m and 0x3F).toChar().code).toChar()
|
|
}
|
|
else if (m < 240) {
|
|
stringChars[k] = (m and 0xF shl 12).toChar()
|
|
m = (stringBytes[j++].toInt() and 0xFF).toChar().code
|
|
if (m < 128 || m > 191) {
|
|
return null
|
|
}
|
|
stringChars[k] = (stringChars[k].code or (m and 0x3F shl 6).toChar().code).toChar()
|
|
m = (stringBytes[j++].toInt() and 0xFF).toChar().code
|
|
if (m < 128 || m > 191) {
|
|
return null
|
|
}
|
|
stringChars[k] = (stringChars[k].code or (m and 0x3F).toChar().code).toChar()
|
|
}
|
|
else {
|
|
return null
|
|
}
|
|
}
|
|
k++
|
|
}
|
|
return String(stringChars, 0, k)
|
|
}
|
|
}
|
|
}
|