ModMgr: I can load class by name; dropped Groovy script support, coding must go to JAR

This commit is contained in:
minjaesong
2018-05-09 05:34:39 +09:00
parent d70aabc1b4
commit f0907d9737
26 changed files with 383 additions and 73 deletions

View File

@@ -6,6 +6,7 @@ import net.torvald.terrarum.utils.CSVFetcher
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.itemproperties.ItemID
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.utils.JsonFetcher
import org.apache.commons.csv.CSVFormat
@@ -182,12 +183,14 @@ object ModMgr {
@JvmStatic operator fun invoke(module: String) {
val csv = CSVFetcher.readFromModule(module, itemPath + "itemid.csv")
csv.forEach {
val filename = it["filename"].toString()
val script = getFile(module, itemPath + filename).readText()
val className = it["classname"].toString()
val itemID = it["id"].toInt()
groovyEngine.eval(script)
ItemCodex[itemID] = groovyInvocable.invokeFunction("invoke", itemID) as GameItem
val loadedClass = Class.forName(className)
val loadedClassConstructor = loadedClass.getConstructor(ItemID::class.java)
val loadedClassInstance = loadedClassConstructor.newInstance(itemID)
ItemCodex[itemID] = loadedClassInstance as GameItem
}
}
}

View File

@@ -0,0 +1,40 @@
package net.torvald.terrarum.audio
/**
* Mixes spacial audio sources to multiple channels
*
*
* Channels and their mapping:
*
* Notation: (front/side/rear.subwoofer/top-front)
* Plugs: G-Front (green), K-Rear (black), Y-Centre/Subwoofer (yellow), E-Side (grey)
* e.g. E-RC,NULL means GREY jack outputs REAR-CENTRE to its left and nothing to its right channel.
*
* = Headphones: Binaural
* = Stereo ---------- (2/0/0.0/0): G-FL,FR
* = Quadraphonic ---- (2/0/2.0/0): G-FL,FR; K-RL,RR
* = 4.0 ------------- (3/0/1.0/0): G-FL,FR; Y-FC,RC
* = 5.1 ------------- (3/0/2.1/0): G-FL,FR; Y-FC,SW; K-RL,RR
* = 6.1 ------------- (3/0/3.1/0): G-FL,FR; Y-FC,SW; K-RL,RR, E-RC,RC
* = 7.1 ------------- (3/2/2.1/0): G-FL,FR; Y-FC,SW; K-RL,RR, E-SL,SR
* = Dolby Atmos 5.1.2 (3/0/2.1/2): G-FL,FR; Y-FC,SW; K-RL,RR, E-TL,TR
*
*
* Channel uses:
*
* - Front and centre: usual thingies
* - Rear: weather/ambient if 4.0, channel phantoming otherwise
* - Top/Side: weather/ambient
* - Side: extreme pan for front channels
* - Centre: interface/UI
*
* * If both side and rear speakers are not there, play weather/ambient to the stereo speakers but NOT TO THE CENTRE
* * For non-existent speakers, use channel phantoming
*
* Note: 5.1.2 does NOT output Dolby-compatible signals.
*/
object SpatialAudioMixer {
const val centreQuotient = 0.7071f
}

View File

@@ -119,7 +119,7 @@ open class ActorHumanoid(
/** how long the walk button has down, in frames */
internal var walkCounterX = 0
internal var walkCounterY = 0
@Transient private val MAX_JUMP_LENGTH = 31 // manages "heaviness" of the jump control. Higher = heavier
@Transient private val MAX_JUMP_LENGTH = 25 // manages "heaviness" of the jump control. Higher = heavier
private var readonly_totalX = 0.0
private var readonly_totalY = 0.0

View File

@@ -34,7 +34,7 @@ object PlayerBuilderSigrid {
p.actorValue[AVKey.SPEEDBUFF] = 1.0
p.actorValue[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE
p.actorValue[AVKey.ACCELBUFF] = 1.0
p.actorValue[AVKey.JUMPPOWER] = 10.0
p.actorValue[AVKey.JUMPPOWER] = 13.0
p.actorValue[AVKey.BASEMASS] = 80.0
p.actorValue[AVKey.SCALEBUFF] = 1.0 // Constant 1.0 for player, meant to be used by random mobs

View File

@@ -1,22 +1,27 @@
package net.torvald.terrarum.virtualcomputer.tvd
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
/**
* ByteArray that can hold larger than 4 GiB of Data.
* ByteArray that can hold larger than 2 GiB of Data.
*
* Works kind of like Bank Switching of old game console's cartridges which does same thing.
*
* Created by Minjaesong on 2017-04-12.
*/
class ByteArray64(val size: Long) {
private val bankSize: Int = 1 shl 30 // 2^30 Bytes, or 1 GiB
companion object {
val bankSize: Int = 8192
}
private val data: Array<ByteArray>
init {
if (size <= 0)
if (size < 0)
throw IllegalArgumentException("Invalid array size!")
val requiredBanks: Int = 1 + ((size - 1) / bankSize).toInt()
@@ -94,8 +99,9 @@ class ByteArray64(val size: Long) {
fun forEach(consumer: (Byte) -> Unit) = iterator().forEach { consumer(it) }
fun forEachInt32(consumer: (Int) -> Unit) = iteratorChoppedToInt().forEach { consumer(it) }
fun forEachBanks(consumer: (ByteArray) -> Unit) = data.forEach(consumer)
fun sliceArray(range: LongRange): ByteArray64 {
fun sliceArray64(range: LongRange): ByteArray64 {
val newarr = ByteArray64(range.last - range.first + 1)
range.forEach { index ->
newarr[index - range.first] = this[index]
@@ -103,6 +109,14 @@ class ByteArray64(val size: Long) {
return newarr
}
fun sliceArray(range: IntRange): ByteArray {
val newarr = ByteArray(range.last - range.first + 1)
range.forEach { index ->
newarr[index - range.first] = this[index.toLong()]
}
return newarr
}
fun toByteArray(): ByteArray {
if (this.size > Integer.MAX_VALUE - 8) // according to OpenJDK; the size itself is VM-dependent
throw TypeCastException("Impossible cast; too large to fit")
@@ -125,4 +139,19 @@ class ByteArray64(val size: Long) {
fos.close()
}
}
}
class ByteArray64InputStream(val byteArray64: ByteArray64): InputStream() {
private var readCounter = 0L
override fun read(): Int {
readCounter += 1
return try {
byteArray64[readCounter - 1].toUint()
}
catch (e: ArrayIndexOutOfBoundsException) {
-1
}
}
}

View File

@@ -0,0 +1,102 @@
package net.torvald.terrarum.virtualcomputer.tvd
import java.io.File
import java.io.FileInputStream
/**
* Creates entry-to-offset tables to allow streaming from the disk, without storing whole VD file to the memory.
*
* Created by minjaesong on 2017-11-17.
*/
class DiskSkimmer(diskFile: File) {
class EntryOffsetPair(val entryID: Int, val offset: Long)
val entryToOffsetTable = ArrayList<EntryOffsetPair>()
init {
val fis = FileInputStream(diskFile)
var currentPosition = fis.skip(47) // skip disk header
fun skipRead(bytes: Long) {
currentPosition += fis.skip(bytes)
}
/**
* Reads a byte and adds up the position var
*/
fun readByte(): Byte {
currentPosition++
val read = fis.read()
if (read < 0) throw InternalError("Unexpectedly reached EOF")
return read.toByte()
}
/**
* Reads specific bytes to the buffer and adds up the position var
*/
fun readBytes(buffer: ByteArray): Int {
val readStatus = fis.read(buffer)
currentPosition += readStatus
return readStatus
}
fun readIntBig(): Int {
val buffer = ByteArray(4)
val readStatus = readBytes(buffer)
if (readStatus != 4) throw InternalError("Unexpected error -- EOF reached? (expected 4, got $readStatus)")
return buffer.toIntBig()
}
fun readInt48(): Long {
val buffer = ByteArray(6)
val readStatus = readBytes(buffer)
if (readStatus != 6) throw InternalError("Unexpected error -- EOF reached? (expected 6, got $readStatus)")
return buffer.toInt48()
}
while (true) {
val entryID = readIntBig()
// footer
if (entryID == 0xFEFEFEFE.toInt()) break
// fill up table
entryToOffsetTable.add(EntryOffsetPair(entryID, currentPosition))
skipRead(4) // skip entryID of parent
val entryType = readByte()
skipRead(256 + 6 + 6 + 4) // skips rest of the header
// figure out the entry size so that we can skip
val entrySize: Long = when(entryType) {
0x01.toByte() -> readInt48()
0x11.toByte() -> readInt48() + 6 // size of compressed payload + 6 (header elem for uncompressed size)
0x02.toByte() -> readIntBig().shl(16).toLong() * 4 - 2 // #entris is 2 bytes, we read 4 bytes, so we subtract 2
0x03.toByte() -> 4 // symlink
else -> throw InternalError("Unknown entry type: ${entryType.toUint()}")
}
skipRead(entrySize) // skips rest of the entry's actual contents
}
}
private fun ByteArray.toIntBig(): Int {
return this[0].toUint().shl(24) or this[1].toUint().shl(16) or
this[2].toUint().shl(8) or this[2].toUint()
}
private fun ByteArray.toInt48(): Long {
return this[0].toUlong().shl(56) or this[1].toUlong().shl(48) or
this[2].toUlong().shl(40) or this[3].toUlong().shl(32) or
this[4].toUlong().shl(24) or this[5].toUlong().shl(16) or
this[6].toUlong().shl(8) or this[7].toUlong()
}
}

View File

@@ -4,6 +4,10 @@ import java.io.*
import java.nio.charset.Charset
import java.util.*
import java.util.logging.Level
import java.util.zip.DeflaterOutputStream
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import java.util.zip.InflaterOutputStream
import javax.naming.OperationNotSupportedException
import kotlin.collections.ArrayList
@@ -41,7 +45,7 @@ object VDUtil {
unsanitisedHierarchy.removeAt(0)
// removes tail slash
if (unsanitisedHierarchy.size > 0 &&
unsanitisedHierarchy[unsanitisedHierarchy.lastIndex].isEmpty())
unsanitisedHierarchy[unsanitisedHierarchy.lastIndex].isEmpty())
unsanitisedHierarchy.removeAt(unsanitisedHierarchy.lastIndex)
unsanitisedHierarchy.forEach {
@@ -116,12 +120,12 @@ object VDUtil {
if (magicMismatch(VirtualDisk.MAGIC, inbytes.sliceArray(0L..3L).toByteArray()))
if (magicMismatch(VirtualDisk.MAGIC, inbytes.sliceArray64(0L..3L).toByteArray()))
throw RuntimeException("Invalid Virtual Disk file!")
val diskSize = inbytes.sliceArray(4L..9L).toInt48Big()
val diskName = inbytes.sliceArray(10L..10L + 31)
val diskCRC = inbytes.sliceArray(10L + 32..10L + 32 + 3).toIntBig() // to check with completed vdisk
val diskSize = inbytes.sliceArray64(4L..9L).toInt48Big()
val diskName = inbytes.sliceArray64(10L..10L + 31)
val diskCRC = inbytes.sliceArray64(10L + 32..10L + 32 + 3).toIntBig() // to check with completed vdisk
val diskSpecVersion = inbytes[10L + 32 + 4]
@@ -133,30 +137,31 @@ object VDUtil {
//println("[VDUtil] currentUnixtime = $currentUnixtime")
var entryOffset = VirtualDisk.HEADER_SIZE
while (!Arrays.equals(inbytes.sliceArray(entryOffset..entryOffset + 3).toByteArray(), VirtualDisk.FOOTER_START_MARK)) {
// not footer, entries
while (!Arrays.equals(inbytes.sliceArray64(entryOffset..entryOffset + 3).toByteArray(), VirtualDisk.FOOTER_START_MARK)) {
//println("[VDUtil] entryOffset = $entryOffset")
// read and prepare all the shits
val entryID = inbytes.sliceArray(entryOffset..entryOffset + 3).toIntBig()
val entryParentID = inbytes.sliceArray(entryOffset + 4..entryOffset + 7).toIntBig()
val entryID = inbytes.sliceArray64(entryOffset..entryOffset + 3).toIntBig()
val entryParentID = inbytes.sliceArray64(entryOffset + 4..entryOffset + 7).toIntBig()
val entryTypeFlag = inbytes[entryOffset + 8]
val entryFileName = inbytes.sliceArray(entryOffset + 9..entryOffset + 9 + 255).toByteArray()
val entryCreationTime = inbytes.sliceArray(entryOffset + 265..entryOffset + 270).toInt48Big()
val entryModifyTime = inbytes.sliceArray(entryOffset + 271..entryOffset + 276).toInt48Big()
val entryCRC = inbytes.sliceArray(entryOffset + 277..entryOffset + 280).toIntBig() // to check with completed entry
val entryFileName = inbytes.sliceArray64(entryOffset + 9..entryOffset + 9 + 255).toByteArray()
val entryCreationTime = inbytes.sliceArray64(entryOffset + 265..entryOffset + 270).toInt48Big()
val entryModifyTime = inbytes.sliceArray64(entryOffset + 271..entryOffset + 276).toInt48Big()
val entryCRC = inbytes.sliceArray64(entryOffset + 277..entryOffset + 280).toIntBig() // to check with completed entry
val entryData = when (entryTypeFlag) {
DiskEntry.NORMAL_FILE -> {
val filesize = inbytes.sliceArray(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 5).toInt48Big()
val filesize = inbytes.sliceArray64(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 5).toInt48Big()
//println("[VDUtil] --> is file; filesize = $filesize")
inbytes.sliceArray(entryOffset + DiskEntry.HEADER_SIZE + 6..entryOffset + DiskEntry.HEADER_SIZE + 5 + filesize)
inbytes.sliceArray64(entryOffset + DiskEntry.HEADER_SIZE + 6..entryOffset + DiskEntry.HEADER_SIZE + 5 + filesize)
}
DiskEntry.DIRECTORY -> {
val entryCount = inbytes.sliceArray(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 1).toShortBig()
val entryCount = inbytes.sliceArray64(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 1).toShortBig()
//println("[VDUtil] --> is directory; entryCount = $entryCount")
inbytes.sliceArray(entryOffset + DiskEntry.HEADER_SIZE + 2..entryOffset + DiskEntry.HEADER_SIZE + 1 + entryCount * 4)
inbytes.sliceArray64(entryOffset + DiskEntry.HEADER_SIZE + 2..entryOffset + DiskEntry.HEADER_SIZE + 1 + entryCount * 4)
}
DiskEntry.SYMLINK -> {
inbytes.sliceArray(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 3)
inbytes.sliceArray64(entryOffset + DiskEntry.HEADER_SIZE..entryOffset + DiskEntry.HEADER_SIZE + 3)
}
else -> throw RuntimeException("Unknown entry with type $entryTypeFlag at entryOffset $entryOffset")
}
@@ -165,9 +170,10 @@ object VDUtil {
// update entryOffset so that we can fetch next entry in the binary
entryOffset += DiskEntry.HEADER_SIZE + entryData.size + when (entryTypeFlag) {
DiskEntry.NORMAL_FILE -> 6
DiskEntry.DIRECTORY -> 2
DiskEntry.SYMLINK -> 0
DiskEntry.COMPRESSED_FILE -> 12 // PLEASE DO REFER TO Spec.md
DiskEntry.NORMAL_FILE -> 6 // PLEASE DO REFER TO Spec.md
DiskEntry.DIRECTORY -> 2 // PLEASE DO REFER TO Spec.md
DiskEntry.SYMLINK -> 0 // PLEASE DO REFER TO Spec.md
else -> throw RuntimeException("Unknown entry with type $entryTypeFlag")
}
@@ -185,7 +191,7 @@ object VDUtil {
else if (entryTypeFlag == DiskEntry.DIRECTORY) {
val entryList = ArrayList<EntryID>()
(0..entryData.size / 4 - 1).forEach {
entryList.add(entryData.sliceArray(4 * it..4 * it + 3).toIntBig())
entryList.add(entryData.sliceArray64(4 * it..4 * it + 3).toIntBig())
}
EntryDirectory(entryList)
@@ -202,7 +208,7 @@ object VDUtil {
val calculatedCRC = diskEntry.hashCode()
val crcMsg = "CRC failed: expected ${entryCRC.toHex()}, got ${calculatedCRC.toHex()}\n" +
"at file \"${diskEntry.getFilenameString(charset)}\" (entry ID ${diskEntry.entryID})"
"at file \"${diskEntry.getFilenameString(charset)}\" (entry ID ${diskEntry.entryID})"
if (calculatedCRC != entryCRC) {
if (crcWarnLevel == Level.SEVERE)
@@ -215,6 +221,15 @@ object VDUtil {
// add entry to disk
vdisk.entries[entryID] = diskEntry
}
// entries ends, footers are to be read
run {
entryOffset += 4 // skip footer marker
val footerSize = inbytes.size - entryOffset - VirtualDisk.EOF_MARK.size
if (footerSize > 0) {
vdisk.__internalSetFooter__(inbytes.sliceArray64(entryOffset..entryOffset + footerSize - 1))
}
}
// check CRC of disk
@@ -235,6 +250,11 @@ object VDUtil {
}
fun isFile(disk: VirtualDisk, entryID: EntryID) = disk.entries[entryID]?.contents is EntryFile
fun isCompressedFile(disk: VirtualDisk, entryID: EntryID) = disk.entries[entryID]?.contents is EntryFileCompressed
fun isDirectory(disk: VirtualDisk, entryID: EntryID) = disk.entries[entryID]?.contents is EntryDirectory
fun isSymlink(disk: VirtualDisk, entryID: EntryID) = disk.entries[entryID]?.contents is EntrySymlink
/**
* Get list of entries of directory.
*/
@@ -317,12 +337,12 @@ object VDUtil {
*/
private fun DiskEntry.getAsNormalFile(disk: VirtualDisk): EntryFile =
this.contents as? EntryFile ?:
if (this.contents is EntryDirectory)
throw RuntimeException("this is directory")
else if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsNormalFile(disk)
else
throw RuntimeException("Unknown entry type")
if (this.contents is EntryDirectory)
throw RuntimeException("this is directory")
else if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsNormalFile(disk)
else
throw RuntimeException("Unknown entry type")
/**
* SYNOPSIS disk.getFile("bin/msh.lua")!!.first.getAsNormalFile(disk)
*
@@ -330,12 +350,12 @@ object VDUtil {
*/
private fun DiskEntry.getAsDirectory(disk: VirtualDisk): EntryDirectory =
this.contents as? EntryDirectory ?:
if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsDirectory(disk)
else if (this.contents is EntryFile)
throw RuntimeException("this is not directory")
else
throw RuntimeException("Unknown entry type")
if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsDirectory(disk)
else if (this.contents is EntryFile)
throw RuntimeException("this is not directory")
else
throw RuntimeException("Unknown entry type")
/**
* Search for the file and returns a instance of normal file.
@@ -441,14 +461,28 @@ object VDUtil {
* Add file to the specified directory.
* The file will get new EntryID and its ParentID will be overwritten.
*/
fun addFile(disk: VirtualDisk, parentPath: VDPath, file: DiskEntry) {
fun addFile(disk: VirtualDisk, parentPath: VDPath, file: DiskEntry, compress: Boolean = false) {
val targetDirID = getFile(disk, parentPath)!!.entryID
return addFile(disk, targetDirID, file)
return addFile(disk, targetDirID, file, compress)
}
fun randomBase62(length: Int): String {
val glyphs = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
val sb = StringBuilder()
kotlin.repeat(length) {
sb.append(glyphs[(Math.random() * glyphs.length).toInt()])
}
return sb.toString()
}
/**
* Add file to the specified directory. ParentID of the file will be overwritten.
*
* @param compressTheFile Used to compress un-compressed file. Will be ignored if the entry is Dir, Symlink or EntryFileCompressed
*/
fun addFile(disk: VirtualDisk, directoryID: EntryID, file: DiskEntry) {
fun addFile(disk: VirtualDisk, directoryID: EntryID, file: DiskEntry, compressTheFile: Boolean = false) {
disk.checkReadOnly()
disk.checkCapacity(file.serialisedSize)
@@ -457,8 +491,53 @@ object VDUtil {
file.entryID = disk.generateUniqueID()
// add record to the directory
getAsDirectory(disk, directoryID).add(file.entryID)
// add entry on the disk
disk.entries[file.entryID] = file
// DEFLATE fat boy if marked as
if (compressTheFile && file.contents is EntryFile) {
val filename = "./tmp_" + randomBase62(10)
// dump the deflated bytes to disk
file.contents.bytes.forEachBanks {
val fos = BufferedOutputStream(FileOutputStream(filename))
val deflater = DeflaterOutputStream(fos, true)
deflater.write(it)
deflater.flush()
deflater.close()
}
// read back deflated bytes untouched and store it
val tempFile = File(filename)
val tempFileFIS = FileInputStream(tempFile)
val readBytes = ByteArray64(tempFile.length())
var c = 0L
while (true) {
val r = tempFileFIS.read()
if (r == -1) break
else readBytes[c] = r.toByte()
c++
}
tempFileFIS.close()
val newContent = EntryFileCompressed(file.contents.bytes.size, readBytes)
val newEntry = DiskEntry(
file.entryID, file.parentEntryID, file.filename, file.creationDate, file.modificationDate,
newContent
)
disk.entries[file.entryID] = newEntry
}
// just the add the boy to the house
else {
disk.entries[file.entryID] = file
}
// make this boy recognise his new parent
file.parentEntryID = directoryID
}
@@ -599,7 +678,20 @@ object VDUtil {
*/
fun exportFile(entryFile: EntryFile, outfile: File) {
outfile.createNewFile()
outfile.writeBytes64(entryFile.bytes)
if (entryFile is EntryFileCompressed) {
entryFile.bytes.forEachBanks {
val fos = FileOutputStream(outfile)
val inflater = InflaterOutputStream(fos)
inflater.write(it)
inflater.flush()
inflater.close()
}
}
else {
outfile.writeBytes64(entryFile.bytes)
}
}
fun exportDirRecurse(disk: VirtualDisk, parentDir: EntryID, outfile: File, charset: Charset) {
@@ -851,6 +943,7 @@ object VDUtil {
}
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
fun magicMismatch(magic: ByteArray, array: ByteArray): Boolean {
return !Arrays.equals(array, magic)
}

View File

@@ -3,8 +3,9 @@ package net.torvald.terrarum.virtualcomputer.tvd
import java.io.IOException
import java.nio.charset.Charset
import java.util.*
import java.util.function.Consumer
import java.util.zip.CRC32
import kotlin.experimental.and
import kotlin.experimental.or
/**
* Created by minjaesong on 2017-03-31.
@@ -12,23 +13,33 @@ import java.util.zip.CRC32
typealias EntryID = Int
val specversion = 0x02.toByte()
val specversion = 0x03.toByte()
class VirtualDisk(
/** capacity of 0 makes the disk read-only */
var capacity: Long,
var diskName: ByteArray = ByteArray(NAME_LENGTH)
var diskName: ByteArray = ByteArray(NAME_LENGTH),
footer: ByteArray64 = ByteArray64(8) // default to mandatory 8-byte footer
) {
var footerBytes: ByteArray64 = footer
private set
val entries = HashMap<EntryID, DiskEntry>()
val isReadOnly: Boolean
get() = capacity == 0L
var isReadOnly: Boolean
set(value) { footerBytes[0] = (footerBytes[0] and 0xFE.toByte()) or value.toBit() }
get() = capacity == 0L || (footerBytes.size > 0 && footerBytes[0].and(1) == 1.toByte())
fun getDiskNameString(charset: Charset) = String(diskName, charset)
val root: DiskEntry
get() = entries[0]!!
private fun Boolean.toBit() = if (this) 1.toByte() else 0.toByte()
internal fun __internalSetFooter__(footer: ByteArray64) {
footerBytes = footer
}
private fun serializeEntriesOnly(): ByteArray64 {
val bufferList = ArrayList<Byte>()
val bufferList = ArrayList<Byte>() // FIXME this part would take up excessive memory for large files
entries.forEach {
val serialised = it.value.serialize()
serialised.forEach { bufferList.add(it) }
@@ -41,7 +52,7 @@ class VirtualDisk(
fun serialize(): AppendableByteBuffer {
val entriesBuffer = serializeEntriesOnly()
val buffer = AppendableByteBuffer(HEADER_SIZE + entriesBuffer.size + FOOTER_SIZE)
val buffer = AppendableByteBuffer(HEADER_SIZE + entriesBuffer.size + FOOTER_SIZE + footerBytes.size)
val crc = hashCode().toBigEndian()
buffer.put(MAGIC)
@@ -51,6 +62,7 @@ class VirtualDisk(
buffer.put(specversion)
buffer.put(entriesBuffer)
buffer.put(FOOTER_START_MARK)
buffer.put(footerBytes)
buffer.put(EOF_MARK)
return buffer
@@ -122,6 +134,8 @@ class DiskEntry(
val NORMAL_FILE = 1.toByte()
val DIRECTORY = 2.toByte()
val SYMLINK = 3.toByte()
val COMPRESSED_FILE = 0x11.toByte()
private fun DiskEntryContent.getTypeFlag() =
if (this is EntryFile) NORMAL_FILE
else if (this is EntryDirectory) DIRECTORY
@@ -168,7 +182,12 @@ interface DiskEntryContent {
fun getSizePure(): Long
fun getSizeEntry(): Long
}
class EntryFile(var bytes: ByteArray64) : DiskEntryContent {
/**
* Do not retrieve bytes directly from this! Use VDUtil.retrieveFile(DiskEntry)
* And besides, the bytes could be compressed.
*/
open class EntryFile(internal var bytes: ByteArray64) : DiskEntryContent {
override fun getSizePure() = bytes.size
override fun getSizeEntry() = getSizePure() + 6
@@ -183,6 +202,21 @@ class EntryFile(var bytes: ByteArray64) : DiskEntryContent {
return buffer
}
}
class EntryFileCompressed(internal var uncompressedSize: Long, bytes: ByteArray64) : EntryFile(bytes) {
override fun getSizePure() = bytes.size
override fun getSizeEntry() = getSizePure() + 12
/* No new blank file for the compressed */
override fun serialize(): AppendableByteBuffer {
val buffer = AppendableByteBuffer(getSizeEntry())
buffer.put(getSizePure().toInt48())
buffer.put(uncompressedSize.toInt48())
buffer.put(bytes)
return buffer
}
}
class EntryDirectory(private val entries: ArrayList<EntryID> = ArrayList<EntryID>()) : DiskEntryContent {
override fun getSizePure() = entries.size * 4L