mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-11 02:54:04 +09:00
adding 'kind flag' to the savegame format so the file can be determined if it contains player or world data
This commit is contained in:
@@ -195,7 +195,7 @@ class SavegameCracker(
|
|||||||
it.entries.toSortedMap().forEach { (i, entry) ->
|
it.entries.toSortedMap().forEach { (i, entry) ->
|
||||||
if (i != 0L) println(
|
if (i != 0L) println(
|
||||||
ccNoun + i.toString(10).padStart(11, ' ') + " " +
|
ccNoun + i.toString(10).padStart(11, ' ') + " " +
|
||||||
ccNoun2 + (diskIDtoReadableFilename(entry.entryID) + cc0).padEnd(40) { if (it == 0) ' ' else '.' } +
|
ccNoun2 + (diskIDtoReadableFilename(entry.entryID, it.saveKind) + cc0).padEnd(40) { if (it == 0) ' ' else '.' } +
|
||||||
ccConst + " " + entry.contents.getSizePure() + " bytes"
|
ccConst + " " + entry.contents.getSizePure() + " bytes"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class WorldSavingThread(
|
|||||||
override fun save() {
|
override fun save() {
|
||||||
|
|
||||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||||
|
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||||
|
|
||||||
if (hasThumbnail) {
|
if (hasThumbnail) {
|
||||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||||
@@ -224,6 +225,7 @@ class PlayerSavingThread(
|
|||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||||
|
disk.saveKind = VDSaveKind.PLAYER_DATA
|
||||||
disk.capacity = 0L
|
disk.capacity = 0L
|
||||||
|
|
||||||
WriteSavegame.saveProgress = 0f
|
WriteSavegame.saveProgress = 0f
|
||||||
|
|||||||
@@ -140,9 +140,12 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||||
|
|
||||||
skimmer.rewriteDirectories()
|
skimmer.rewriteDirectories()
|
||||||
skimmer.injectDiskCRC(disk.hashCode())
|
skimmer.injectDiskCRC(disk.hashCode())
|
||||||
skimmer.setSaveMode(1 + 2 * isAuto.toInt())
|
skimmer.setSaveMode(1 + 2 * isAuto.toInt())
|
||||||
|
skimmer.setSaveKind(VDSaveKind.WORLD_DATA)
|
||||||
|
|
||||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||||
|
|
||||||
|
|||||||
@@ -169,10 +169,10 @@ removefile:
|
|||||||
|
|
||||||
if (typeFlag > 0) {
|
if (typeFlag > 0) {
|
||||||
entryToOffsetTable[entryID] = offset
|
entryToOffsetTable[entryID] = offset
|
||||||
debugPrintln("[DiskSkimmer] ... successfully read the entry $entryID at offset $offset (name: ${diskIDtoReadableFilename(entryID)})")
|
debugPrintln("[DiskSkimmer] ... successfully read the entry $entryID at offset $offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
debugPrintln("[DiskSkimmer] ... discarding entry $entryID at offset $offset (name: ${diskIDtoReadableFilename(entryID)})")
|
debugPrintln("[DiskSkimmer] ... discarding entry $entryID at offset $offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,11 +343,21 @@ removefile:
|
|||||||
fa.writeByte(bits)
|
fa.writeByte(bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setSaveKind(bits: Int) {
|
||||||
|
fa.seek(50L)
|
||||||
|
fa.writeByte(bits)
|
||||||
|
}
|
||||||
|
|
||||||
fun getSaveMode(): Int {
|
fun getSaveMode(): Int {
|
||||||
fa.seek(49L)
|
fa.seek(49L)
|
||||||
return fa.read()
|
return fa.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSaveKind(): Int {
|
||||||
|
fa.seek(50L)
|
||||||
|
return fa.read()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getDiskName(charset: Charset): String {
|
override fun getDiskName(charset: Charset): String {
|
||||||
val bytes = ByteArray(268)
|
val bytes = ByteArray(268)
|
||||||
fa.seek(10L)
|
fa.seek(10L)
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ object VDUtil {
|
|||||||
|
|
||||||
val crcMsg =
|
val crcMsg =
|
||||||
"CRC failed: stored value is ${entryCRC.toHex()}, but calculated value is ${calculatedCRC.toHex()}\n" +
|
"CRC failed: stored value is ${entryCRC.toHex()}, but calculated value is ${calculatedCRC.toHex()}\n" +
|
||||||
"at file \"${diskIDtoReadableFilename(diskEntry.entryID)}\" (entry ID ${diskEntry.entryID})"
|
"at file \"${diskIDtoReadableFilename(diskEntry.entryID, vdisk.saveKind)}\" (entry ID ${diskEntry.entryID})"
|
||||||
|
|
||||||
if (calculatedCRC != entryCRC) {
|
if (calculatedCRC != entryCRC) {
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ object VDUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e: ArrayIndexOutOfBoundsException) {
|
catch (e: ArrayIndexOutOfBoundsException) {
|
||||||
System.err.println("An error occurred while reading a file (entryID: $entryID (${diskIDtoReadableFilename(entryID)}), typeFlag: $entryTypeFlag)")
|
System.err.println("An error occurred while reading a file (entryID: $entryID (${diskIDtoReadableFilename(entryID, vdisk.saveKind)}), typeFlag: $entryTypeFlag)")
|
||||||
System.err.println("Stack trace:")
|
System.err.println("Stack trace:")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package net.torvald.terrarum.savegame
|
package net.torvald.terrarum.savegame
|
||||||
|
|
||||||
import net.torvald.terrarum.App.printdbg
|
import net.torvald.terrarum.App.printdbg
|
||||||
|
import net.torvald.terrarum.savegame.VDSaveKind.PLAYER_DATA
|
||||||
|
import net.torvald.terrarum.savegame.VDSaveKind.WORLD_DATA
|
||||||
import net.torvald.terrarum.serialise.Common
|
import net.torvald.terrarum.serialise.Common
|
||||||
import net.torvald.terrarum.serialise.toUint
|
import net.torvald.terrarum.serialise.toUint
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -66,12 +68,18 @@ Version 254 is a customised version of TEVD tailored to be used as a savegame fo
|
|||||||
4. for elems on list: update crc with the elem (crc = calculateCRC(crc, elem))
|
4. for elems on list: update crc with the elem (crc = calculateCRC(crc, elem))
|
||||||
Int8 Version
|
Int8 Version
|
||||||
Int8 0xFE
|
Int8 0xFE
|
||||||
|
< BEGIN extraInfoBytes >
|
||||||
Int8 Disk properties flag 1
|
Int8 Disk properties flag 1
|
||||||
0th bit: readonly
|
0th bit: readonly
|
||||||
Int8 Save type
|
Int8 Save type
|
||||||
0th bit: unset - full save; set - quick save
|
0th bit: unset - full save; set - quick save
|
||||||
1st bit: set - generated by autosave
|
1st bit: set - generated by autosave
|
||||||
Int8[14] Extra info bytes
|
Int8 Kind of the Save file
|
||||||
|
0: Undefined (old version of the game?)
|
||||||
|
1: Player Data
|
||||||
|
2: World Data
|
||||||
|
Int8[13] Extra info bytes
|
||||||
|
< END extraInfoBytes >
|
||||||
Unit8[236] Rest of the long disk name (268 bytes total)
|
Unit8[236] Rest of the long disk name (268 bytes total)
|
||||||
|
|
||||||
(Header size: 300 bytes)
|
(Header size: 300 bytes)
|
||||||
@@ -141,6 +149,9 @@ class VirtualDisk(
|
|||||||
var saveMode: Int
|
var saveMode: Int
|
||||||
set(value) { extraInfoBytes[1] = value.toByte() }
|
set(value) { extraInfoBytes[1] = value.toByte() }
|
||||||
get() = extraInfoBytes[1].toUint()
|
get() = extraInfoBytes[1].toUint()
|
||||||
|
var saveKind: Int
|
||||||
|
set(value) { extraInfoBytes[2] = value.toByte() }
|
||||||
|
get() = extraInfoBytes[2].toUint()
|
||||||
override fun getDiskName(charset: Charset) = diskName.toCanonicalString(charset)
|
override fun getDiskName(charset: Charset) = diskName.toCanonicalString(charset)
|
||||||
val root: DiskEntry
|
val root: DiskEntry
|
||||||
get() = entries[0]!!
|
get() = entries[0]!!
|
||||||
@@ -234,6 +245,12 @@ class VirtualDisk(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object VDSaveKind {
|
||||||
|
const val UNDEFINED = 0
|
||||||
|
const val PLAYER_DATA = 1
|
||||||
|
const val WORLD_DATA = 2
|
||||||
|
}
|
||||||
|
|
||||||
object VDFileID {
|
object VDFileID {
|
||||||
const val ROOT = 0L
|
const val ROOT = 0L
|
||||||
const val SAVEGAMEINFO = -1L
|
const val SAVEGAMEINFO = -1L
|
||||||
@@ -245,11 +262,21 @@ object VDFileID {
|
|||||||
const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L
|
const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L
|
||||||
}
|
}
|
||||||
|
|
||||||
fun diskIDtoReadableFilename(id: EntryID): String = when (id) {
|
fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) {
|
||||||
VDFileID.ROOT -> "root"
|
VDFileID.ROOT -> "root"
|
||||||
VDFileID.SAVEGAMEINFO -> "savegameinfo.json"
|
VDFileID.SAVEGAMEINFO -> "savegameinfo.json"
|
||||||
VDFileID.THUMBNAIL, VDFileID.SPRITEDEF -> "thumbnail.tga.gz (world)/spritedef (player)"
|
VDFileID.THUMBNAIL, VDFileID.SPRITEDEF ->
|
||||||
VDFileID.SPRITEDEF_GLOW -> "spritedef-glow (player)"
|
if (saveKind == PLAYER_DATA)
|
||||||
|
"spritedef"
|
||||||
|
else if (saveKind == WORLD_DATA)
|
||||||
|
"thumbnail.tga.gz"
|
||||||
|
else
|
||||||
|
"thumbnail.tga.gz (world)/spritedef (player)"
|
||||||
|
VDFileID.SPRITEDEF_GLOW ->
|
||||||
|
if (saveKind == PLAYER_DATA)
|
||||||
|
"spritedef-glow"
|
||||||
|
else
|
||||||
|
"file #$id"
|
||||||
VDFileID.LOADORDER -> "loadOrder.txt"
|
VDFileID.LOADORDER -> "loadOrder.txt"
|
||||||
// -16L -> "blockcodex.json.gz"
|
// -16L -> "blockcodex.json.gz"
|
||||||
// -17L -> "itemcodex.json.gz"
|
// -17L -> "itemcodex.json.gz"
|
||||||
@@ -259,7 +286,11 @@ fun diskIDtoReadableFilename(id: EntryID): String = when (id) {
|
|||||||
// -1024L -> "apocryphas.json.gz"
|
// -1024L -> "apocryphas.json.gz"
|
||||||
VDFileID.BODYPART_TO_ENTRY_MAP -> "bodypart-to-entry.map"
|
VDFileID.BODYPART_TO_ENTRY_MAP -> "bodypart-to-entry.map"
|
||||||
VDFileID.BODYPARTGLOW_TO_ENTRY_MAP -> "bodypartglow-to-entry.map"
|
VDFileID.BODYPARTGLOW_TO_ENTRY_MAP -> "bodypartglow-to-entry.map"
|
||||||
in 1..65535 -> "bodypart #$id.tga.gz (player)"
|
in 1..65535 ->
|
||||||
|
if (saveKind == PLAYER_DATA)
|
||||||
|
"bodypart #$id.tga.gz"
|
||||||
|
else
|
||||||
|
"file #$id"
|
||||||
in 1048576..2147483647 -> "actor #$id.json"
|
in 1048576..2147483647 -> "actor #$id.json"
|
||||||
in 0x0000_0001_0000_0000L..0x0000_FFFF_FFFF_FFFFL ->
|
in 0x0000_0001_0000_0000L..0x0000_FFFF_FFFF_FFFFL ->
|
||||||
"World${id.ushr(32)}-L${id.and(0xFF00_0000).ushr(24)}-C${id.and(0xFFFFFF)}.gz"
|
"World${id.ushr(32)}-L${id.and(0xFF00_0000).ushr(24)}-C${id.and(0xFFFFFF)}.gz"
|
||||||
@@ -324,7 +355,7 @@ class DiskEntry(
|
|||||||
|
|
||||||
override fun equals(other: Any?) = if (other == null) false else this.hashCode() == other.hashCode()
|
override fun equals(other: Any?) = if (other == null) false else this.hashCode() == other.hashCode()
|
||||||
|
|
||||||
override fun toString() = "DiskEntry(name: ${diskIDtoReadableFilename(entryID)}, ID: $entryID, parent: $parentEntryID, type: ${contents.getTypeFlag()}, contents size: ${contents.getSizeEntry()}, crc: ${hashCode().toHex()})"
|
override fun toString() = "DiskEntry(name: ${diskIDtoReadableFilename(entryID, null)}, ID: $entryID, parent: $parentEntryID, type: ${contents.getTypeFlag()}, contents size: ${contents.getSizeEntry()}, crc: ${hashCode().toHex()})"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
|||||||
if (vdisk != null) {
|
if (vdisk != null) {
|
||||||
val entry = currentDirectoryEntries!![rowIndex - 1]
|
val entry = currentDirectoryEntries!![rowIndex - 1]
|
||||||
return when(columnIndex) {
|
return when(columnIndex) {
|
||||||
0 -> diskIDtoReadableFilename(entry.entryID)
|
0 -> diskIDtoReadableFilename(entry.entryID, vdisk?.saveKind)
|
||||||
1 -> Instant.ofEpochSecond(entry.modificationDate).
|
1 -> Instant.ofEpochSecond(entry.modificationDate).
|
||||||
atZone(TimeZone.getDefault().toZoneId()).
|
atZone(TimeZone.getDefault().toZoneId()).
|
||||||
format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||||
@@ -623,7 +623,7 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
|||||||
private fun updateDiskInfo() {
|
private fun updateDiskInfo() {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
directoryHierarchy.forEach {
|
directoryHierarchy.forEach {
|
||||||
sb.append(diskIDtoReadableFilename(it))
|
sb.append(diskIDtoReadableFilename(it, vdisk?.saveKind))
|
||||||
sb.append('/')
|
sb.append('/')
|
||||||
}
|
}
|
||||||
sb.dropLast(1)
|
sb.dropLast(1)
|
||||||
@@ -647,7 +647,7 @@ Write protected: ${disk.isReadOnly.toEnglish()}"""
|
|||||||
|
|
||||||
|
|
||||||
private fun getFileInfoText(file: DiskEntry): String {
|
private fun getFileInfoText(file: DiskEntry): String {
|
||||||
return """Name: ${diskIDtoReadableFilename(file.entryID)}
|
return """Name: ${diskIDtoReadableFilename(file.entryID, vdisk?.saveKind)}
|
||||||
Size: ${file.getEffectiveSize()}
|
Size: ${file.getEffectiveSize()}
|
||||||
Type: ${DiskEntry.getTypeString(file.contents)}
|
Type: ${DiskEntry.getTypeString(file.contents)}
|
||||||
CRC: ${file.hashCode().toHex()}
|
CRC: ${file.hashCode().toHex()}
|
||||||
|
|||||||
Reference in New Issue
Block a user