mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-16 13:34:06 +09:00
a tily updates to the savegame format handling -- read the SAVE_FORMAT.md
This commit is contained in:
@@ -94,7 +94,7 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
}
|
}
|
||||||
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
||||||
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
|
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
|
||||||
addFile(disk, world); skimmer.appendEntryOnly(world)
|
addFile(disk, world); skimmer.appendEntry(world)
|
||||||
|
|
||||||
WriteSavegame.saveProgress += 1f
|
WriteSavegame.saveProgress += 1f
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
val entryContent = EntryFile(chunkBytes)
|
val entryContent = EntryFile(chunkBytes)
|
||||||
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
|
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
|
||||||
// "W1L0-92,15"
|
// "W1L0-92,15"
|
||||||
addFile(disk, entry); skimmer.appendEntryOnly(entry)
|
addFile(disk, entry); skimmer.appendEntry(entry)
|
||||||
|
|
||||||
WriteSavegame.saveProgress += chunkProgressMultiplier
|
WriteSavegame.saveProgress += chunkProgressMultiplier
|
||||||
chunksWrote += 1
|
chunksWrote += 1
|
||||||
@@ -134,7 +134,7 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
|
|
||||||
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
||||||
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)
|
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)
|
||||||
addFile(disk, actor); skimmer.appendEntryOnly(actor)
|
addFile(disk, actor); skimmer.appendEntry(actor)
|
||||||
|
|
||||||
WriteSavegame.saveProgress += actorProgressMultiplier
|
WriteSavegame.saveProgress += actorProgressMultiplier
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
|
|
||||||
disk.saveKind = VDSaveKind.WORLD_DATA
|
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)
|
skimmer.setSaveKind(VDSaveKind.WORLD_DATA)
|
||||||
|
|||||||
@@ -168,11 +168,15 @@ removefile:
|
|||||||
skipRead(entrySize) // skips rest of the entry's actual contents
|
skipRead(entrySize) // skips rest of the entry's actual contents
|
||||||
|
|
||||||
if (typeFlag > 0) {
|
if (typeFlag > 0) {
|
||||||
|
if (entryToOffsetTable[entryID] != null)
|
||||||
|
debugPrintln("[DiskSkimmer] ... overwriting the entry $entryID previously found at offset:${entryToOffsetTable[entryID]} with offset:$offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})")
|
||||||
|
else
|
||||||
|
debugPrintln("[DiskSkimmer] ... successfully read the entry $entryID at offset:$offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})")
|
||||||
|
|
||||||
entryToOffsetTable[entryID] = offset
|
entryToOffsetTable[entryID] = offset
|
||||||
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, getSaveKind())})")
|
debugPrintln("[DiskSkimmer] ... discarding entry $entryID at offset:$offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +196,8 @@ removefile:
|
|||||||
* @return DiskEntry if the entry exists on the disk, `null` otherwise.
|
* @return DiskEntry if the entry exists on the disk, `null` otherwise.
|
||||||
*/
|
*/
|
||||||
fun requestFile(entryID: EntryID): DiskEntry? {
|
fun requestFile(entryID: EntryID): DiskEntry? {
|
||||||
|
if (entryID == 0L) throw UnsupportedOperationException("Peeking at the root entry is an undefined behaviour for the Terrarum Savegame format.")
|
||||||
|
|
||||||
if (!initialised) throw IllegalStateException("File entries not built! Initialise the Skimmer by executing rebuild()")
|
if (!initialised) throw IllegalStateException("File entries not built! Initialise the Skimmer by executing rebuild()")
|
||||||
|
|
||||||
entryToOffsetTable[entryID].let { offset ->
|
entryToOffsetTable[entryID].let { offset ->
|
||||||
@@ -323,9 +329,9 @@ removefile:
|
|||||||
fa.write(crc.toBigEndian())
|
fa.write(crc.toBigEndian())
|
||||||
}
|
}
|
||||||
|
|
||||||
private val modifiedDirectories = TreeSet<DiskEntry>()
|
//private val modifiedDirectories = TreeSet<DiskEntry>()
|
||||||
|
|
||||||
fun rewriteDirectories() {
|
/*fun rewriteDirectories() {
|
||||||
modifiedDirectories.forEach {
|
modifiedDirectories.forEach {
|
||||||
invalidateEntry(it.entryID)
|
invalidateEntry(it.entryID)
|
||||||
|
|
||||||
@@ -336,7 +342,7 @@ removefile:
|
|||||||
entryToOffsetTable[it.entryID] = appendAt + 8
|
entryToOffsetTable[it.entryID] = appendAt + 8
|
||||||
it.serialize().forEach { fa.writeByte(it.toInt()) }
|
it.serialize().forEach { fa.writeByte(it.toInt()) }
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fun setSaveMode(bits: Int) {
|
fun setSaveMode(bits: Int) {
|
||||||
fa.seek(49L)
|
fa.seek(49L)
|
||||||
@@ -409,7 +415,7 @@ removefile:
|
|||||||
// THESE ARE METHODS TO SUPPORT ON-LINE MODIFICATION //
|
// THESE ARE METHODS TO SUPPORT ON-LINE MODIFICATION //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
fun appendEntryOnly(entry: DiskEntry) {
|
/*fun appendEntryOnly(entry: DiskEntry) {
|
||||||
val parentDir = requestFile(entry.parentEntryID)!!
|
val parentDir = requestFile(entry.parentEntryID)!!
|
||||||
val id = entry.entryID
|
val id = entry.entryID
|
||||||
|
|
||||||
@@ -426,19 +432,19 @@ removefile:
|
|||||||
// append new file
|
// append new file
|
||||||
entryToOffsetTable[id] = appendAt + 8
|
entryToOffsetTable[id] = appendAt + 8
|
||||||
entry.serialize().forEach { fa.writeByte(it.toInt()) }
|
entry.serialize().forEach { fa.writeByte(it.toInt()) }
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fun appendEntry(entry: DiskEntry) {
|
fun appendEntry(entry: DiskEntry) {
|
||||||
val parentDir = requestFile(entry.parentEntryID)!!
|
// val parentDir = requestFile(entry.parentEntryID)!!
|
||||||
val id = entry.entryID
|
val id = entry.entryID
|
||||||
val parent = entry.parentEntryID
|
// val parent = entry.parentEntryID
|
||||||
|
|
||||||
// add the entry to its parent directory if there was none
|
// add the entry to its parent directory if there was none
|
||||||
val dirContent = (parentDir.contents as EntryDirectory)
|
// val dirContent = (parentDir.contents as EntryDirectory)
|
||||||
if (!dirContent.contains(id)) dirContent.add(id)
|
// if (!dirContent.contains(id)) dirContent.add(id)
|
||||||
|
|
||||||
invalidateEntry(parent)
|
// invalidateEntry(parent)
|
||||||
invalidateEntry(id)
|
// invalidateEntry(id)
|
||||||
|
|
||||||
val appendAt = fa.length()
|
val appendAt = fa.length()
|
||||||
fa.seek(appendAt)
|
fa.seek(appendAt)
|
||||||
@@ -447,27 +453,28 @@ removefile:
|
|||||||
entryToOffsetTable[id] = appendAt + 8
|
entryToOffsetTable[id] = appendAt + 8
|
||||||
entry.serialize().forEach { fa.writeByte(it.toInt()) }
|
entry.serialize().forEach { fa.writeByte(it.toInt()) }
|
||||||
// append modified directory
|
// append modified directory
|
||||||
entryToOffsetTable[parent] = fa.filePointer + 8
|
// entryToOffsetTable[parent] = fa.filePointer + 8
|
||||||
parentDir.serialize().forEach { fa.writeByte(it.toInt()) }
|
// parentDir.serialize().forEach { fa.writeByte(it.toInt()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteEntry(id: EntryID) {
|
fun deleteEntry(id: EntryID) {
|
||||||
val entry = requestFile(id)!!
|
val entry = requestFile(id)!!
|
||||||
val parentDir = requestFile(entry.parentEntryID)!!
|
// val parentDir = requestFile(entry.parentEntryID)!!
|
||||||
val parent = entry.parentEntryID
|
// val parent = entry.parentEntryID
|
||||||
|
|
||||||
invalidateEntry(parent)
|
// invalidateEntry(parent)
|
||||||
|
invalidateEntry(id)
|
||||||
|
|
||||||
// remove the entry
|
// remove the entry
|
||||||
val dirContent = (parentDir.contents as EntryDirectory)
|
// val dirContent = (parentDir.contents as EntryDirectory)
|
||||||
dirContent.remove(id)
|
// dirContent.remove(id)
|
||||||
|
|
||||||
val appendAt = fa.length()
|
val appendAt = fa.length()
|
||||||
fa.seek(appendAt)
|
fa.seek(appendAt)
|
||||||
|
|
||||||
// append modified directory
|
// append modified directory
|
||||||
entryToOffsetTable[id] = appendAt + 8
|
// entryToOffsetTable[id] = appendAt + 8
|
||||||
parentDir.serialize().forEach { fa.writeByte(it.toInt()) }
|
// parentDir.serialize().forEach { fa.writeByte(it.toInt()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun appendEntries(entries: List<DiskEntry>) = entries.forEach { appendEntry(it) }
|
fun appendEntries(entries: List<DiskEntry>) = entries.forEach { appendEntry(it) }
|
||||||
|
|||||||
@@ -80,3 +80,9 @@ Making `inventory` transient is impossible as it would render Storage Chests unu
|
|||||||
1. Allow multiple players share the same world
|
1. Allow multiple players share the same world
|
||||||
2. Make multiplayer possible
|
2. Make multiplayer possible
|
||||||
3. Make Players distributable (like VRChat avatars)
|
3. Make Players distributable (like VRChat avatars)
|
||||||
|
|
||||||
|
## Quirks with Terrarum's custom TVDA implementation
|
||||||
|
|
||||||
|
- Subdirectory is not allowed -- every file must be on the root directory
|
||||||
|
- The entry for the root directory is there (only to satisfy the format constraints), but it's basically meaningless -- contents of the entry are undefined
|
||||||
|
- Programmers are encouraged to scan the entire disk to get the file listings
|
||||||
|
|||||||
Reference in New Issue
Block a user