disbrowser/src/main/java/com/smallhacker/disbrowser/game/GameData.kt

134 lines
4.7 KiB
Kotlin
Raw Normal View History

package com.smallhacker.disbrowser.game
2019-01-07 18:19:37 +00:00
2019-01-12 02:16:50 +00:00
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonSubTypes
2019-01-11 03:19:08 +00:00
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
2019-01-12 02:16:50 +00:00
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.smallhacker.disbrowser.asm.*
2019-01-12 02:16:50 +00:00
import com.smallhacker.disbrowser.util.joinNullableBytes
2019-01-13 03:38:59 +00:00
import com.smallhacker.disbrowser.util.removeIf
2019-01-11 03:19:08 +00:00
import com.smallhacker.disbrowser.util.toUInt24
2019-01-11 05:09:12 +00:00
import java.util.*
2019-01-07 18:19:37 +00:00
class GameData {
2019-01-11 16:35:35 +00:00
@JsonProperty
val name: String
@JsonProperty
val path: String
@JsonProperty
private val metadata: MutableMap<SnesAddress, MetadataLine>
2019-01-07 18:19:37 +00:00
constructor(name: String, path: String) {
this.name = name
this.path = path
this.metadata = TreeMap()
2019-01-11 05:09:12 +00:00
}
2019-01-11 03:19:08 +00:00
@JsonCreator
private constructor(@JsonProperty name: String, @JsonProperty path: String, @JsonProperty metadata: Map<SnesAddress, MetadataLine>) {
this.name = name
this.path = path
this.metadata = TreeMap(metadata)
2019-01-11 03:19:08 +00:00
}
operator fun set(address: SnesAddress, line: MetadataLine?): GameData {
2019-01-11 05:09:12 +00:00
if (line == null) {
metadata.remove(address)
2019-01-11 05:09:12 +00:00
} else {
metadata[address] = line
2019-01-11 05:09:12 +00:00
}
2019-01-07 18:19:37 +00:00
return this
}
2019-01-11 16:35:35 +00:00
operator fun get(address: SnesAddress): MetadataLine? {
return metadata[address]
2019-01-07 18:19:37 +00:00
}
2019-01-11 03:19:08 +00:00
operator fun contains(address: SnesAddress) = metadata[address] != null
2019-01-11 05:09:12 +00:00
2019-01-11 16:35:35 +00:00
fun getOrCreate(address: SnesAddress): MetadataLine {
2019-01-11 03:19:08 +00:00
val line = this[address]
if (line != null) {
return line
}
2019-01-11 05:09:12 +00:00
val newLine = MetadataLine()
this[address] = newLine
2019-01-11 03:19:08 +00:00
return newLine
}
2019-01-13 03:38:59 +00:00
fun cleanUp() {
metadata.removeIf { _, v -> v.isEmpty() }
2019-01-13 03:38:59 +00:00
}
2019-01-07 18:19:37 +00:00
}
2019-01-11 03:19:08 +00:00
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "flagType")
@JsonSubTypes(
Type(value = NonReturningRoutine::class, name = "NonReturningRoutine"),
Type(value = JmpIndirectLongInterleavedTable::class, name = "JmpIndirectLongInterleavedTable"),
Type(value = JslTableRoutine::class, name = "JslTableRoutine")
)
2019-01-07 18:19:37 +00:00
interface InstructionFlag
2019-01-11 03:19:08 +00:00
object NonReturningRoutine : InstructionFlag {
override fun toString() = "NonReturningRoutine"
}
class JmpIndirectLongInterleavedTable @JsonCreator constructor(
2019-01-11 16:35:35 +00:00
@field:JsonProperty @JsonProperty private val start: SnesAddress,
2019-01-11 03:19:08 +00:00
@field:JsonProperty @JsonProperty private val entries: Int
) : InstructionFlag {
private val uEntries get() = entries.toUInt()
2019-01-07 18:19:37 +00:00
2019-01-12 02:16:50 +00:00
fun readTable(data: MemorySpace): Sequence<SnesAddress?> {
return (0 until entries)
2019-01-07 18:19:37 +00:00
.asSequence()
2019-01-12 02:16:50 +00:00
.map { start + it }
.map { address -> joinNullableBytes(data[address], data[address + entries], data[address + entries + entries])?.toUInt24() }
.map { pointer -> pointer?.let { SnesAddress(it) } }
2019-01-11 03:19:08 +00:00
}
fun generateCode(jumpInstruction: Instruction): Sequence<DataBlock> {
2019-01-12 02:16:50 +00:00
val table = jumpInstruction.preState.memory.deinterleave(uEntries,
start.value.toUInt(),
(start + entries).value.toUInt(),
(start + (2 * uEntries.toInt())).value.toUInt()
).validate() ?: return emptySequence()
2019-01-11 03:19:08 +00:00
return (0u until uEntries)
.asSequence()
.map { index -> index * 3u }
.map { offset ->
val target = table.getLong(offset)
2019-01-07 18:19:37 +00:00
2019-01-11 03:19:08 +00:00
DataBlock(
2019-01-13 03:38:59 +00:00
Opcode.CODE_POINTER_LONG,
2019-01-11 03:19:08 +00:00
table.range(offset, 3u),
jumpInstruction.postState.address + offset.toInt(),
jumpInstruction.relativeAddress,
jumpInstruction.opcode.mutate(jumpInstruction)
2019-01-11 16:35:35 +00:00
.mutateAddress { SnesAddress(target) }
2019-01-13 03:38:59 +00:00
.withOrigin(jumpInstruction),
jumpInstruction.memory
2019-01-11 03:19:08 +00:00
)
}
2019-01-07 18:19:37 +00:00
}
2019-01-11 03:19:08 +00:00
override fun toString() = "JmpIndirectLongInterleavedTable($start, $entries)"
2019-01-07 18:19:37 +00:00
}
2019-01-11 03:19:08 +00:00
class JslTableRoutine @JsonCreator constructor(
@field:JsonProperty @JsonProperty private val entries: Int
) : InstructionFlag {
2019-01-12 02:16:50 +00:00
fun readTable(postJsr: State): Sequence<SnesAddress?> {
val data = postJsr.memory
return (0 until entries)
2019-01-07 18:19:37 +00:00
.asSequence()
2019-01-12 02:16:50 +00:00
.map { postJsr.address + (it * 3) }
.map { address -> joinNullableBytes(data[address], data[address + 1], data[address + 2])?.toUInt24() }
.map { pointer -> pointer?.let { SnesAddress(it) } }
2019-01-07 18:19:37 +00:00
}
2019-01-11 03:19:08 +00:00
override fun toString() = "JslTableRoutine($entries)"
2019-01-07 18:19:37 +00:00
}