Partial move of stuff to commonMain
This commit is contained in:
parent
f0bd8d5170
commit
852632e053
|
@ -14,6 +14,10 @@ kotlin {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||||
}
|
}
|
||||||
|
languageSettings {
|
||||||
|
enableLanguageFeature('InlineClasses')
|
||||||
|
useExperimentalAnnotation('kotlin.ExperimentalUnsignedTypes')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -47,6 +51,10 @@ kotlin {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
||||||
}
|
}
|
||||||
|
languageSettings {
|
||||||
|
enableLanguageFeature('InlineClasses')
|
||||||
|
useExperimentalAnnotation('kotlin.ExperimentalUnsignedTypes')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
jsTest {
|
jsTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.memory
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.util.UInt24
|
import com.smallhacker.util.UInt24
|
||||||
import com.smallhacker.disbrowser.util.joinBytes
|
import com.smallhacker.util.joinBytes
|
||||||
import com.smallhacker.disbrowser.util.joinNullableBytes
|
import com.smallhacker.util.joinNullableBytes
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.util.toUInt24
|
||||||
|
|
||||||
interface MemorySpace {
|
interface MemorySpace {
|
||||||
val size: UInt
|
val size: UInt
|
||||||
|
@ -22,7 +22,8 @@ object EmptyMemorySpace : ValidMemorySpace {
|
||||||
fun MemorySpace.asSequence(): Sequence<UByte?> = (0u until size).asSequence().map { this[it] }
|
fun MemorySpace.asSequence(): Sequence<UByte?> = (0u until size).asSequence().map { this[it] }
|
||||||
fun MemorySpace.getWord(address: UInt): UShort? = joinNullableBytes(this[address], this[address + 1u])?.toUShort()
|
fun MemorySpace.getWord(address: UInt): UShort? = joinNullableBytes(this[address], this[address + 1u])?.toUShort()
|
||||||
fun MemorySpace.getLong(address: UInt): UInt24? = joinNullableBytes(this[address], this[address + 1u], this[address + 2u])?.toUInt24()
|
fun MemorySpace.getLong(address: UInt): UInt24? = joinNullableBytes(this[address], this[address + 1u], this[address + 2u])?.toUInt24()
|
||||||
fun MemorySpace.range(start: UInt, length: UInt): MemorySpace = MemoryRange(this, start, length)
|
fun MemorySpace.range(start: UInt, length: UInt): MemorySpace =
|
||||||
|
MemoryRange(this, start, length)
|
||||||
fun MemorySpace.range(start: SnesAddress, length: UInt): MemorySpace = range(start.value.toUInt(), length)
|
fun MemorySpace.range(start: SnesAddress, length: UInt): MemorySpace = range(start.value.toUInt(), length)
|
||||||
fun MemorySpace.validate(): ValidMemorySpace? {
|
fun MemorySpace.validate(): ValidMemorySpace? {
|
||||||
if (asSequence().any { it == null }) {
|
if (asSequence().any { it == null }) {
|
||||||
|
@ -34,7 +35,11 @@ fun MemorySpace.validate(): ValidMemorySpace? {
|
||||||
fun ValidMemorySpace.asSequence(): Sequence<UByte> = (0u until size).asSequence().map { this[it] }
|
fun ValidMemorySpace.asSequence(): Sequence<UByte> = (0u until size).asSequence().map { this[it] }
|
||||||
fun ValidMemorySpace.getWord(address: UInt): UShort = joinBytes(this[address], this[address + 1u]).toUShort()
|
fun ValidMemorySpace.getWord(address: UInt): UShort = joinBytes(this[address], this[address + 1u]).toUShort()
|
||||||
fun ValidMemorySpace.getLong(address: UInt): UInt24 = joinBytes(this[address], this[address + 1u], this[address + 2u]).toUInt24()
|
fun ValidMemorySpace.getLong(address: UInt): UInt24 = joinBytes(this[address], this[address + 1u], this[address + 2u]).toUInt24()
|
||||||
fun ValidMemorySpace.range(start: UInt, length: UInt): ValidMemorySpace = MemoryRange(this, start, length).validate()!!
|
fun ValidMemorySpace.range(start: UInt, length: UInt): ValidMemorySpace = MemoryRange(
|
||||||
|
this,
|
||||||
|
start,
|
||||||
|
length
|
||||||
|
).validate()!!
|
||||||
fun ValidMemorySpace.range(start: SnesAddress, length: UInt): ValidMemorySpace = range(start.value.toUInt(), length).validate()!!
|
fun ValidMemorySpace.range(start: SnesAddress, length: UInt): ValidMemorySpace = range(start.value.toUInt(), length).validate()!!
|
||||||
|
|
||||||
class ArrayMemorySpace(private val bytes: UByteArray) : MemorySpace {
|
class ArrayMemorySpace(private val bytes: UByteArray) : MemorySpace {
|
||||||
|
@ -52,20 +57,22 @@ class UnreadableMemory(override val size: UInt) : MemorySpace {
|
||||||
override fun get(address: UInt): UByte? = null
|
override fun get(address: UInt): UByte? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MemoryRange(private val parent: MemorySpace, private val start: UInt, override val size: UInt) : MemorySpace {
|
private class MemoryRange(private val parent: MemorySpace, private val start: UInt, override val size: UInt) :
|
||||||
|
MemorySpace {
|
||||||
override fun get(address: UInt) = if (address < size) parent[start + address] else null
|
override fun get(address: UInt) = if (address < size) parent[start + address] else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ValidatedMemorySpace(private val parent: MemorySpace) : ValidMemorySpace {
|
private class ValidatedMemorySpace(private val parent: MemorySpace) :
|
||||||
|
ValidMemorySpace {
|
||||||
override val size get() = parent.size
|
override val size get() = parent.size
|
||||||
override fun get(address: UInt) = parent[address]!!
|
override fun get(address: UInt) = parent[address]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReindexedMemorySpace(
|
private class ReindexedMemorySpace(
|
||||||
private val parent: MemorySpace,
|
private val parent: MemorySpace,
|
||||||
override val size: UInt,
|
override val size: UInt,
|
||||||
private val mapper: (UInt) -> UInt
|
private val mapper: (UInt) -> UInt
|
||||||
) : MemorySpace {
|
) : MemorySpace {
|
||||||
override fun get(address: UInt): UByte? {
|
override fun get(address: UInt): UByte? {
|
||||||
if (address >= size) {
|
if (address >= size) {
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.smallhacker.disbrowser.memory
|
||||||
|
|
||||||
|
import com.smallhacker.util.UInt24
|
||||||
|
import com.smallhacker.util.toHex
|
||||||
|
import com.smallhacker.util.toIntOrNull
|
||||||
|
import com.smallhacker.util.toUInt24
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
|
||||||
|
operator fun plus(offset: Int) = SnesAddress(value + offset)
|
||||||
|
operator fun minus(offset: Int) = SnesAddress(value - offset)
|
||||||
|
operator fun inc() = plus(1)
|
||||||
|
operator fun dec() = minus(1)
|
||||||
|
|
||||||
|
override fun toString(): String = toFormattedString()
|
||||||
|
fun toFormattedString(): String = "$${(value shr 16).toUByte().toHex()}:${value.toUShort().toHex()}"
|
||||||
|
fun toSimpleString(): String = value.toHex()
|
||||||
|
|
||||||
|
fun withinBank(value: UShort): SnesAddress = SnesAddress((this.value and 0xFF_0000u) or value.toUInt24())
|
||||||
|
|
||||||
|
override fun compareTo(other: SnesAddress) = value.toUInt().compareTo(other.value.toUInt())
|
||||||
|
|
||||||
|
infix fun distanceTo(other: SnesAddress) = abs(value.toInt() - other.value.toInt()).toUInt()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(address: String): SnesAddress? = toIntOrNull(address, 16)
|
||||||
|
?.let { SnesAddress(it.toUInt24()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24())
|
|
@ -1,16 +1,21 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.memory
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.datatype.MutableRangeMap
|
import com.smallhacker.util.MutableRangeMap
|
||||||
import com.smallhacker.disbrowser.datatype.NaiveRangeMap
|
import com.smallhacker.util.NaiveRangeMap
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.util.toUInt24
|
||||||
|
|
||||||
abstract class SnesMemory: MemorySpace {
|
abstract class SnesMemory: MemorySpace {
|
||||||
override val size = 0x100_0000u
|
override val size = 0x100_0000u
|
||||||
private val areas: MutableRangeMap<UInt, UIntRange, MapperEntry> = NaiveRangeMap()
|
private val areas: MutableRangeMap<UInt, UIntRange, MapperEntry> =
|
||||||
|
NaiveRangeMap()
|
||||||
|
|
||||||
protected fun add(start: UInt, canonicalStart: UInt, memorySpace: MemorySpace) {
|
protected fun add(start: UInt, canonicalStart: UInt, memorySpace: MemorySpace) {
|
||||||
val range = start until (start + memorySpace.size)
|
val range = start until (start + memorySpace.size)
|
||||||
areas[range] = MapperEntry(start, SnesAddress(canonicalStart.toUInt24()), memorySpace)
|
areas[range] = MapperEntry(
|
||||||
|
start,
|
||||||
|
SnesAddress(canonicalStart.toUInt24()),
|
||||||
|
memorySpace
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(address: UInt): UByte? {
|
override fun get(address: UInt): UByte? {
|
|
@ -1,4 +1,4 @@
|
||||||
package com.smallhacker.disbrowser
|
package com.smallhacker.util
|
||||||
|
|
||||||
interface ImmStack<E>: Iterable<E> {
|
interface ImmStack<E>: Iterable<E> {
|
||||||
fun isEmpty(): Boolean
|
fun isEmpty(): Boolean
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.smallhacker.util
|
||||||
|
|
||||||
|
fun toIntOrNull(s: String, radix: Int = 10) = try {
|
||||||
|
s.toInt(radix)
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
null
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.smallhacker.disbrowser.util
|
package com.smallhacker.util
|
||||||
|
|
||||||
// The Kotlin standard library does not contain a counterpart to Java's ArrayDeque<E>, so let's implement a simplistic
|
// The Kotlin standard library does not contain a counterpart to Java's ArrayDeque<E>, so let's implement a simplistic
|
||||||
// one ourselves for portability.
|
// one ourselves for portability.
|
|
@ -1,4 +1,4 @@
|
||||||
package com.smallhacker.disbrowser.util
|
package com.smallhacker.util
|
||||||
|
|
||||||
typealias MultiMap<K, V> = Map<K, List<V>>
|
typealias MultiMap<K, V> = Map<K, List<V>>
|
||||||
typealias MutableMultiMap<K, V> = MutableMap<K, MutableList<V>>
|
typealias MutableMultiMap<K, V> = MutableMap<K, MutableList<V>>
|
||||||
|
@ -6,5 +6,5 @@ typealias MutableMultiMap<K, V> = MutableMap<K, MutableList<V>>
|
||||||
fun <K, V> mutableMultiMap(): MutableMultiMap<K, V> = HashMap()
|
fun <K, V> mutableMultiMap(): MutableMultiMap<K, V> = HashMap()
|
||||||
|
|
||||||
fun <K, V> MutableMultiMap<K, V>.putSingle(key: K, value: V) {
|
fun <K, V> MutableMultiMap<K, V>.putSingle(key: K, value: V) {
|
||||||
computeIfAbsent(key) { ArrayList() }.add(value)
|
getOrPut(key) { ArrayList() }.add(value)
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
package com.smallhacker.disbrowser.datatype
|
package com.smallhacker.util
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.util.asReverseSequence
|
|
||||||
|
|
||||||
interface RangeMap<K : Comparable<K>, R : ClosedRange<K>, V : Any> {
|
interface RangeMap<K : Comparable<K>, R : ClosedRange<K>, V : Any> {
|
||||||
operator fun get(key: K): V?
|
operator fun get(key: K): V?
|
||||||
|
@ -11,6 +9,7 @@ interface MutableRangeMap<K : Comparable<K>, R : ClosedRange<K>, V: Any> : Range
|
||||||
operator fun set(keyRange: R, value: V): RangeMap<K, R, V>
|
operator fun set(keyRange: R, value: V): RangeMap<K, R, V>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just an oversimplified implementation for now. Can be heavily optimized if needed.
|
||||||
class NaiveRangeMap<K : Comparable<K>, R : ClosedRange<K>, V: Any> : MutableRangeMap<K, R, V> {
|
class NaiveRangeMap<K : Comparable<K>, R : ClosedRange<K>, V: Any> : MutableRangeMap<K, R, V> {
|
||||||
private val entries = ArrayList<Pair<R, V>>()
|
private val entries = ArrayList<Pair<R, V>>()
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.smallhacker.util
|
||||||
|
|
||||||
|
inline class UInt24(private val data: UInt) {
|
||||||
|
fun toUInt() = data and 0x00FF_FFFFu
|
||||||
|
fun toUShort() = toUInt().toUShort()
|
||||||
|
fun toUByte() = toUInt().toUByte()
|
||||||
|
fun toInt() = toUInt().toInt()
|
||||||
|
fun toShort() = toUShort().toShort()
|
||||||
|
fun toByte() = toUByte().toByte()
|
||||||
|
|
||||||
|
infix fun and(v: UInt24) = (data and v.data).toUInt24()
|
||||||
|
infix fun and(v: UInt) = (data and v).toUInt24()
|
||||||
|
infix fun or(v: UInt24) = (data or v.data).toUInt24()
|
||||||
|
infix fun or(v: UInt) = (data or v).toUInt24()
|
||||||
|
infix fun shl(v: Int) = (data shl v).toUInt24()
|
||||||
|
infix fun shr(v: Int) = (toUInt() shr v).toUInt24()
|
||||||
|
|
||||||
|
operator fun plus(v: UInt24) = (toUInt() + v.toUInt()).toUInt24()
|
||||||
|
operator fun plus(v: UInt) = (toUInt() + v).toUInt24()
|
||||||
|
operator fun plus(v: Int) = (toInt() + v).toUInt24()
|
||||||
|
operator fun minus(v: UInt24) = (toUInt() - v.toUInt()).toUInt24()
|
||||||
|
operator fun minus(v: UInt) = (toUInt() - v).toUInt24()
|
||||||
|
operator fun minus(v: Int) = (toInt() - v).toUInt24()
|
||||||
|
|
||||||
|
override fun toString() = data.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UInt.toUInt24() = UInt24(this and 0x00FF_FFFFu)
|
||||||
|
fun UShort.toUInt24() = this.toUInt().toUInt24()
|
||||||
|
fun UByte.toUInt24() = this.toUInt().toUInt24()
|
||||||
|
fun Int.toUInt24() = this.toUInt().toUInt24()
|
||||||
|
fun Short.toUInt24() = this.toUInt().toUInt24()
|
||||||
|
fun Byte.toUInt24() = this.toUInt().toUInt24()
|
|
@ -1,4 +1,4 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.util
|
||||||
|
|
||||||
inline class VagueNumber(private val valueAndCertainty: ULong) {
|
inline class VagueNumber(private val valueAndCertainty: ULong) {
|
||||||
private constructor(value: UInt, certainty: UInt) : this(value.toULong() or (certainty.toULong() shl 32))
|
private constructor(value: UInt, certainty: UInt) : this(value.toULong() or (certainty.toULong() shl 32))
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.smallhacker.util
|
||||||
|
|
||||||
|
fun toHex(value: UInt, digits: UInt): String {
|
||||||
|
if (digits == 0u) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val hex = value.toLong().toString(16)
|
||||||
|
return hex.padStart(digits.toInt(), '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UInt.toHex() = toHex(this, 8u)
|
||||||
|
fun UInt24.toHex() = toHex(toUInt(), 6u)
|
||||||
|
fun UShort.toHex() = toHex(toUInt(), 4u)
|
||||||
|
fun UByte.toHex() = toHex(toUInt(), 2u)
|
||||||
|
|
||||||
|
fun joinBytes(vararg bytes: UByte) = bytes
|
||||||
|
.asSequence()
|
||||||
|
.mapIndexed { index, v -> v.toUInt() shl (index * 8) }
|
||||||
|
.reduce { a, b -> a or b }
|
||||||
|
|
||||||
|
fun joinNullableBytes(vararg bytes: UByte?): UInt? {
|
||||||
|
if (bytes.any { it == null }) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
.asSequence()
|
||||||
|
.mapIndexed { index, v -> v!!.toUInt() shl (index * 8) }
|
||||||
|
.reduce { a, b -> a or b }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> List<T>.asReverseSequence(): Sequence<T> =
|
||||||
|
((size - 1) downTo 0).asSequence().map { this[it] }
|
||||||
|
|
||||||
|
fun <K, V> MutableMap<K, V>.removeIf(condition: (K, V) -> Boolean) {
|
||||||
|
this.asSequence()
|
||||||
|
.filter { condition(it.key, it.value) }
|
||||||
|
.map { it.key }
|
||||||
|
.toList()
|
||||||
|
.forEach { remove(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hashOf(vararg values: Any?): Int {
|
||||||
|
return if (values.isEmpty()) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
values.asSequence()
|
||||||
|
.map { it.hashCode() }
|
||||||
|
.reduce { acc, v -> (acc * 31) + v }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,13 @@
|
||||||
package com.smallhacker.disbrowser
|
package com.smallhacker.disbrowser
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.*
|
import com.smallhacker.disbrowser.asm.CodeUnit
|
||||||
|
import com.smallhacker.disbrowser.asm.Disassembly
|
||||||
|
import com.smallhacker.disbrowser.asm.print
|
||||||
import com.smallhacker.disbrowser.game.Game
|
import com.smallhacker.disbrowser.game.Game
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
import kotlin.collections.asSequence
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
class Grid {
|
class Grid {
|
||||||
private val arrowCells = HashMap<Pair<Int, Int>, HtmlNode?>()
|
private val arrowCells = HashMap<Pair<Int, Int>, HtmlNode?>()
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package com.smallhacker.disbrowser
|
package com.smallhacker.disbrowser
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.*
|
import com.smallhacker.disbrowser.asm.*
|
||||||
import com.smallhacker.disbrowser.game.Game
|
|
||||||
import com.smallhacker.disbrowser.disassembler.Disassembler
|
import com.smallhacker.disbrowser.disassembler.Disassembler
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.game.Game
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
import com.smallhacker.disbrowser.memory.address
|
||||||
|
import com.smallhacker.disbrowser.memory.getWord
|
||||||
|
import com.smallhacker.util.VagueNumber
|
||||||
|
import com.smallhacker.util.toUInt24
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
private val RESET_VECTOR_LOCATION = address(0x00_FFFC)
|
private val RESET_VECTOR_LOCATION = address(0x00_FFFC)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
|
||||||
class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
class Disassembly(lines: List<CodeUnit>) : Iterable<CodeUnit> {
|
||||||
override fun iterator() = lineList.iterator() as Iterator<CodeUnit>
|
override fun iterator() = lineList.iterator() as Iterator<CodeUnit>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.game.GameData
|
import com.smallhacker.disbrowser.game.GameData
|
||||||
import com.smallhacker.disbrowser.util.*
|
import com.smallhacker.disbrowser.game.get
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesMemory
|
||||||
|
import com.smallhacker.disbrowser.memory.ValidMemorySpace
|
||||||
|
import com.smallhacker.disbrowser.memory.asSequence
|
||||||
|
import com.smallhacker.util.joinBytes
|
||||||
|
import com.smallhacker.util.toHex
|
||||||
|
import com.smallhacker.util.toUInt24
|
||||||
|
|
||||||
interface CodeUnit {
|
interface CodeUnit {
|
||||||
val address: SnesAddress?
|
val address: SnesAddress?
|
||||||
|
@ -25,7 +32,7 @@ interface CodeUnit {
|
||||||
|
|
||||||
fun bytesToString(): String {
|
fun bytesToString(): String {
|
||||||
return bytes.asSequence()
|
return bytes.asSequence()
|
||||||
.map { toHex(it.toUInt(), 1u) }
|
.map { it.toHex() }
|
||||||
.joinToString(" ")
|
.joinToString(" ")
|
||||||
.padEnd(11, ' ')
|
.padEnd(11, ' ')
|
||||||
}
|
}
|
||||||
|
@ -55,35 +62,35 @@ interface CodeUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataBlock(
|
class DataBlock(
|
||||||
override val opcode: Opcode,
|
override val opcode: Opcode,
|
||||||
override val bytes: ValidMemorySpace,
|
override val bytes: ValidMemorySpace,
|
||||||
override val address: SnesAddress?,
|
override val address: SnesAddress?,
|
||||||
override val indicativeAddress: SnesAddress,
|
override val indicativeAddress: SnesAddress,
|
||||||
override val sortedAddress: SnesAddress,
|
override val sortedAddress: SnesAddress,
|
||||||
override val relativeAddress: SnesAddress,
|
override val relativeAddress: SnesAddress,
|
||||||
override val linkedState: State?,
|
override val linkedState: State?,
|
||||||
override val memory: SnesMemory,
|
override val memory: SnesMemory,
|
||||||
override val certainty: Certainty
|
override val certainty: Certainty
|
||||||
) : CodeUnit {
|
) : CodeUnit {
|
||||||
constructor(
|
constructor(
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
bytes: ValidMemorySpace,
|
bytes: ValidMemorySpace,
|
||||||
indicativeAddress: SnesAddress,
|
indicativeAddress: SnesAddress,
|
||||||
sortedAddress: SnesAddress,
|
sortedAddress: SnesAddress,
|
||||||
relativeAddress: SnesAddress,
|
relativeAddress: SnesAddress,
|
||||||
linkedState: State?,
|
linkedState: State?,
|
||||||
memory: SnesMemory,
|
memory: SnesMemory,
|
||||||
certainty: Certainty
|
certainty: Certainty
|
||||||
) : this(opcode, bytes, null, indicativeAddress, sortedAddress, relativeAddress, linkedState, memory, certainty)
|
) : this(opcode, bytes, null, indicativeAddress, sortedAddress, relativeAddress, linkedState, memory, certainty)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
bytes: ValidMemorySpace,
|
bytes: ValidMemorySpace,
|
||||||
address: SnesAddress,
|
address: SnesAddress,
|
||||||
relativeAddress: SnesAddress,
|
relativeAddress: SnesAddress,
|
||||||
linkedState: State?,
|
linkedState: State?,
|
||||||
memory: SnesMemory,
|
memory: SnesMemory,
|
||||||
certainty: Certainty
|
certainty: Certainty
|
||||||
) : this(opcode, bytes, address, address, address, relativeAddress, linkedState, memory, certainty)
|
) : this(opcode, bytes, address, address, address, relativeAddress, linkedState, memory, certainty)
|
||||||
|
|
||||||
override val nextSortedAddress: SnesAddress
|
override val nextSortedAddress: SnesAddress
|
||||||
|
@ -109,11 +116,11 @@ interface Instruction : CodeUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MutableInstruction(
|
class MutableInstruction(
|
||||||
override val bytes: ValidMemorySpace,
|
override val bytes: ValidMemorySpace,
|
||||||
override val opcode: Opcode,
|
override val opcode: Opcode,
|
||||||
override val preState: State,
|
override val preState: State,
|
||||||
override var continuation: Continuation,
|
override var continuation: Continuation,
|
||||||
override var certainty: Certainty
|
override var certainty: Certainty
|
||||||
) : Instruction {
|
) : Instruction {
|
||||||
override val memory = preState.memory
|
override val memory = preState.memory
|
||||||
override val address: SnesAddress get() = preState.address
|
override val address: SnesAddress get() = preState.address
|
||||||
|
@ -174,7 +181,9 @@ fun CodeUnit.print(gameData: GameData? = null): PrintedCodeUnit {
|
||||||
val secondaryMnemonic = mnemonic.alternativeName
|
val secondaryMnemonic = mnemonic.alternativeName
|
||||||
|
|
||||||
var suffix = lengthSuffix
|
var suffix = lengthSuffix
|
||||||
var operands = gameData?.let { opcode.mode.printWithLabel(this, it) }
|
val referencedAddress = opcode.mode.referencedAddress(this)
|
||||||
|
val operandLabel = gameData?.get(referencedAddress)?.label
|
||||||
|
var operands = opcode.mode.printWithLabel(this, operandLabel)
|
||||||
if (operands == null) {
|
if (operands == null) {
|
||||||
operands = opcode.mode.printRaw(this)
|
operands = opcode.mode.printRaw(this)
|
||||||
suffix = null
|
suffix = null
|
||||||
|
@ -187,7 +196,7 @@ fun CodeUnit.print(gameData: GameData? = null): PrintedCodeUnit {
|
||||||
val bytes = bytesToString()
|
val bytes = bytesToString()
|
||||||
|
|
||||||
val labelAddress = if (opcode.mode.canHaveLabel) {
|
val labelAddress = if (opcode.mode.canHaveLabel) {
|
||||||
opcode.mode.referencedAddress(this)?.let {
|
referencedAddress?.let {
|
||||||
memory.toCanonical(it)
|
memory.toCanonical(it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.game.GameData
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
import com.smallhacker.disbrowser.util.*
|
import com.smallhacker.disbrowser.memory.SnesMemory
|
||||||
|
import com.smallhacker.util.UInt24
|
||||||
|
import com.smallhacker.util.toHex
|
||||||
|
|
||||||
interface Mode {
|
interface Mode {
|
||||||
val dataMode get() = false
|
val dataMode get() = false
|
||||||
val showLengthSuffix get() = true
|
val showLengthSuffix get() = true
|
||||||
val canHaveLabel: Boolean
|
val canHaveLabel: Boolean
|
||||||
fun operandLength(state: State): UInt?
|
fun operandLength(state: State): UInt?
|
||||||
fun printWithLabel(ins: CodeUnit, gameData: GameData): String? = referencedAddress(ins)?.let { gameData[it]?.label }
|
fun printWithLabel(ins: CodeUnit, label: String?): String? = label
|
||||||
fun printRaw(ins: CodeUnit): String
|
fun printRaw(ins: CodeUnit): String
|
||||||
fun referencedAddress(ins: CodeUnit): SnesAddress?
|
fun referencedAddress(ins: CodeUnit): SnesAddress?
|
||||||
}
|
}
|
||||||
|
@ -31,7 +33,7 @@ private abstract class RawWrappedMode(
|
||||||
private val suffix: String = ""
|
private val suffix: String = ""
|
||||||
) : Mode by parent {
|
) : Mode by parent {
|
||||||
override val canHaveLabel = false
|
override val canHaveLabel = false
|
||||||
override fun printWithLabel(ins: CodeUnit, gameData: GameData): String? = printRaw(ins)
|
override fun printWithLabel(ins: CodeUnit, label: String?): String? = null
|
||||||
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ private abstract class WrappedMode(
|
||||||
private val prefix: String = "",
|
private val prefix: String = "",
|
||||||
private val suffix: String = ""
|
private val suffix: String = ""
|
||||||
) : Mode by parent {
|
) : Mode by parent {
|
||||||
override fun printWithLabel(ins: CodeUnit, gameData: GameData): String? = parent.printWithLabel(ins, gameData)?.let { prefix + it + suffix }
|
override fun printWithLabel(ins: CodeUnit, label: String?): String? = parent.printWithLabel(ins, label)?.let { prefix + it + suffix }
|
||||||
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
override fun printRaw(ins: CodeUnit) = prefix + parent.printRaw(ins) + suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ abstract class MultiMode(private val fallback: Mode, private vararg val options:
|
||||||
|
|
||||||
override fun operandLength(state: State) = get(state) { operandLength(state) }
|
override fun operandLength(state: State) = get(state) { operandLength(state) }
|
||||||
|
|
||||||
override fun printWithLabel(ins: CodeUnit, gameData: GameData): String? = get(ins.preState) { printWithLabel(ins, gameData) }
|
override fun printWithLabel(ins: CodeUnit, label: String?): String? = get(ins.preState) { printWithLabel(ins, label) }
|
||||||
|
|
||||||
override fun printRaw(ins: CodeUnit) = get(ins.preState) { printRaw(ins) }
|
override fun printRaw(ins: CodeUnit) = get(ins.preState) { printRaw(ins) }
|
||||||
|
|
||||||
|
@ -113,17 +115,17 @@ private abstract class DataValueMode(private val length: UInt, protected val typ
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataByteMode(type: DataValueType) : DataValueMode(1u, type) {
|
private class DataByteMode(type: DataValueType) : DataValueMode(1u, type) {
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.byte)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.byte.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.byte, ins.preState, ins.memory)
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.byte, ins.preState, ins.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataWordMode(type: DataValueType) : DataValueMode(2u, type) {
|
private class DataWordMode(type: DataValueType) : DataValueMode(2u, type) {
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.word.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.word, ins.preState, ins.memory)
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.word, ins.preState, ins.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataLongMode(type: DataValueType) : DataValueMode(3u, type) {
|
private class DataLongMode(type: DataValueType) : DataValueMode(3u, type) {
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.long)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.long.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.long, ins.preState, ins.memory)
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = type.resolve(ins.long, ins.preState, ins.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +141,7 @@ object Immediate8 : Mode {
|
||||||
override val canHaveLabel = false
|
override val canHaveLabel = false
|
||||||
override val showLengthSuffix = false
|
override val showLengthSuffix = false
|
||||||
override fun operandLength(state: State) = 1u
|
override fun operandLength(state: State) = 1u
|
||||||
override fun printRaw(ins: CodeUnit) = "#$" + toHex(ins.byte)
|
override fun printRaw(ins: CodeUnit) = "#$" + ins.byte.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,35 +149,35 @@ object Immediate16 : Mode {
|
||||||
override val canHaveLabel = false
|
override val canHaveLabel = false
|
||||||
override val showLengthSuffix = false
|
override val showLengthSuffix = false
|
||||||
override fun operandLength(state: State) = 2u
|
override fun operandLength(state: State) = 2u
|
||||||
override fun printRaw(ins: CodeUnit) = "#$" + toHex(ins.word)
|
override fun printRaw(ins: CodeUnit) = "#$" + ins.word.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
override fun referencedAddress(ins: CodeUnit): SnesAddress? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
object Direct : Mode {
|
object Direct : Mode {
|
||||||
override val canHaveLabel = true
|
override val canHaveLabel = true
|
||||||
override fun operandLength(state: State) = 1u
|
override fun operandLength(state: State) = 1u
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.byte)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.byte.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveDirectPage(ins.byte)
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveDirectPage(ins.byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Absolute : Mode {
|
object Absolute : Mode {
|
||||||
override val canHaveLabel = true
|
override val canHaveLabel = true
|
||||||
override fun operandLength(state: State) = 2u
|
override fun operandLength(state: State) = 2u
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.word.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteData(ins.word)
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteData(ins.word)
|
||||||
}
|
}
|
||||||
|
|
||||||
object AbsoluteCode : Mode {
|
object AbsoluteCode : Mode {
|
||||||
override val canHaveLabel = true
|
override val canHaveLabel = true
|
||||||
override fun operandLength(state: State) = 2u
|
override fun operandLength(state: State) = 2u
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.word)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.word.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteCode(ins.word)
|
override fun referencedAddress(ins: CodeUnit) = ins.preState?.resolveAbsoluteCode(ins.word)
|
||||||
}
|
}
|
||||||
|
|
||||||
object AbsoluteLong : Mode {
|
object AbsoluteLong : Mode {
|
||||||
override val canHaveLabel = true
|
override val canHaveLabel = true
|
||||||
override fun operandLength(state: State) = 3u
|
override fun operandLength(state: State) = 3u
|
||||||
override fun printRaw(ins: CodeUnit) = "$" + toHex(ins.long)
|
override fun printRaw(ins: CodeUnit) = "$" + ins.long.toHex()
|
||||||
override fun referencedAddress(ins: CodeUnit) = ins.memory.toCanonical(SnesAddress(ins.long))
|
override fun referencedAddress(ins: CodeUnit) = ins.memory.toCanonical(SnesAddress(ins.long))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
|
||||||
class Segment (val start: SnesAddress, val end: SegmentEnd, val instructions: List<Instruction>)
|
class Segment (val start: SnesAddress, val end: SegmentEnd, val instructions: List<Instruction>)
|
||||||
|
|
||||||
class SegmentEnd(val address: SnesAddress, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: SnesAddress? = null, val returning: Boolean = false)
|
class SegmentEnd(val address: SnesAddress, val local: List<State> = emptyList(), val remote: List<State> = emptyList(), val returnAddress: SnesAddress? = null, val returning: Boolean = false)
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator
|
|
||||||
import com.fasterxml.jackson.annotation.JsonValue
|
|
||||||
import com.smallhacker.disbrowser.util.UInt24
|
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
|
||||||
import com.smallhacker.disbrowser.util.tryParseInt
|
|
||||||
|
|
||||||
data class SnesAddress(val value: UInt24) : Comparable<SnesAddress> {
|
|
||||||
operator fun plus(offset: Int) = SnesAddress(value + offset)
|
|
||||||
operator fun minus(offset: Int) = SnesAddress(value - offset)
|
|
||||||
operator fun inc() = plus(1)
|
|
||||||
operator fun dec() = minus(1)
|
|
||||||
|
|
||||||
override fun toString(): String = toFormattedString()
|
|
||||||
fun toFormattedString(): String = String.format("$%02x:%04x", (value shr 16).toInt(), (value and 0xFFFFu).toInt())
|
|
||||||
@JsonValue
|
|
||||||
fun toSimpleString(): String = String.format("%06x", value.toInt())
|
|
||||||
|
|
||||||
fun withinBank(value: UShort): SnesAddress = SnesAddress((this.value and 0xFF_0000u) or value.toUInt24())
|
|
||||||
|
|
||||||
override fun compareTo(other: SnesAddress) = value.toUInt().compareTo(other.value.toUInt())
|
|
||||||
|
|
||||||
infix fun distanceTo(other: SnesAddress) = Math.abs(value.toInt() - other.value.toInt()).toUInt()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
@JsonCreator
|
|
||||||
fun parse(address: String): SnesAddress? = tryParseInt(address, 16)
|
|
||||||
?.let { SnesAddress(it.toUInt24()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun address(snesAddress: Int) = SnesAddress(snesAddress.toUInt24())
|
|
|
@ -1,9 +1,12 @@
|
||||||
package com.smallhacker.disbrowser.asm
|
package com.smallhacker.disbrowser.asm
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.ImmStack
|
import com.smallhacker.util.ImmStack
|
||||||
import com.smallhacker.disbrowser.game.GameData
|
import com.smallhacker.disbrowser.game.GameData
|
||||||
import com.smallhacker.disbrowser.immStack
|
import com.smallhacker.util.immStack
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesMemory
|
||||||
|
import com.smallhacker.util.VagueNumber
|
||||||
|
import com.smallhacker.util.toUInt24
|
||||||
|
|
||||||
data class State(val origin: Instruction? = null, val memory: SnesMemory, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack(), val gameData: GameData) {
|
data class State(val origin: Instruction? = null, val memory: SnesMemory, val address: SnesAddress, val flags: VagueNumber = VagueNumber(), val stack: ImmStack<VagueNumber> = immStack(), val gameData: GameData) {
|
||||||
val m: Boolean? get() = flags.getBoolean(0x20u)
|
val m: Boolean? get() = flags.getBoolean(0x20u)
|
||||||
|
|
|
@ -2,12 +2,14 @@ package com.smallhacker.disbrowser.disassembler
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.*
|
import com.smallhacker.disbrowser.asm.*
|
||||||
import com.smallhacker.disbrowser.game.*
|
import com.smallhacker.disbrowser.game.*
|
||||||
import com.smallhacker.disbrowser.util.LifoQueue
|
import com.smallhacker.disbrowser.memory.*
|
||||||
import com.smallhacker.disbrowser.util.mutableMultiMap
|
import com.smallhacker.util.LifoQueue
|
||||||
import com.smallhacker.disbrowser.util.putSingle
|
import com.smallhacker.util.mutableMultiMap
|
||||||
|
import com.smallhacker.util.putSingle
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
|
||||||
object Disassembler {
|
object Disassembler {
|
||||||
fun disassemble(initialState: State, gameData: GameData, global: Boolean): Disassembly {
|
fun disassemble(initialState: State, gameData: GameData, global: Boolean): Disassembly {
|
||||||
val seen = HashSet<SnesAddress>()
|
val seen = HashSet<SnesAddress>()
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package com.smallhacker.disbrowser.game
|
package com.smallhacker.disbrowser.game
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.SnesMemory
|
import com.smallhacker.disbrowser.memory.SnesMemory
|
||||||
import com.smallhacker.disbrowser.util.JsonFile
|
import com.smallhacker.disbrowser.util.JsonFile
|
||||||
|
|
||||||
class Game(
|
class Game(
|
||||||
val id: String,
|
val id: String,
|
||||||
val memory: SnesMemory,
|
val memory: SnesMemory,
|
||||||
val gameData: GameData,
|
val gameData: GameData,
|
||||||
private val gameDataFile: JsonFile<GameData>
|
private val gameDataFile: JsonFile<GameData>
|
||||||
) {
|
) {
|
||||||
fun saveGameData() {
|
fun saveGameData() {
|
||||||
gameData.cleanUp()
|
gameData.cleanUp()
|
||||||
|
|
|
@ -6,9 +6,10 @@ import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
|
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||||
import com.smallhacker.disbrowser.asm.*
|
import com.smallhacker.disbrowser.asm.*
|
||||||
import com.smallhacker.disbrowser.util.joinNullableBytes
|
import com.smallhacker.disbrowser.memory.*
|
||||||
import com.smallhacker.disbrowser.util.removeIf
|
import com.smallhacker.util.joinNullableBytes
|
||||||
import com.smallhacker.disbrowser.util.toUInt24
|
import com.smallhacker.util.removeIf
|
||||||
|
import com.smallhacker.util.toUInt24
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class GameData {
|
class GameData {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.smallhacker.disbrowser.game
|
package com.smallhacker.disbrowser.game
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.asm.SnesMemory
|
import com.smallhacker.disbrowser.memory.SnesMemory
|
||||||
import com.smallhacker.disbrowser.util.jsonFile
|
import com.smallhacker.disbrowser.util.jsonFile
|
||||||
import org.glassfish.jersey.server.ResourceConfig
|
import org.glassfish.jersey.server.ResourceConfig
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package com.smallhacker.disbrowser.resource
|
package com.smallhacker.disbrowser.resource
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.*
|
import com.smallhacker.disbrowser.*
|
||||||
import com.smallhacker.disbrowser.asm.SnesAddress
|
import com.smallhacker.util.VagueNumber
|
||||||
import com.smallhacker.disbrowser.asm.VagueNumber
|
|
||||||
import com.smallhacker.disbrowser.game.getGameSource
|
import com.smallhacker.disbrowser.game.getGameSource
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.ws.rs.GET
|
import javax.ws.rs.GET
|
||||||
import javax.ws.rs.Path
|
import javax.ws.rs.Path
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package com.smallhacker.disbrowser.resource
|
package com.smallhacker.disbrowser.resource
|
||||||
|
|
||||||
import com.smallhacker.disbrowser.Service
|
import com.smallhacker.disbrowser.Service
|
||||||
import com.smallhacker.disbrowser.asm.SnesAddress
|
|
||||||
import com.smallhacker.disbrowser.asm.MetadataLine
|
import com.smallhacker.disbrowser.asm.MetadataLine
|
||||||
import com.smallhacker.disbrowser.game.getGameSource
|
import com.smallhacker.disbrowser.game.getGameSource
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
import javax.ws.rs.Consumes
|
import javax.ws.rs.Consumes
|
||||||
import javax.ws.rs.POST
|
import javax.ws.rs.POST
|
||||||
import javax.ws.rs.Path
|
import javax.ws.rs.Path
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.smallhacker.disbrowser.util
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.*
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
|
||||||
|
typealias KSerializer<T> = T.(gen: JsonGenerator, serializers: SerializerProvider) -> Unit
|
||||||
|
typealias KDeserializer<T> = JsonParser.(ctxt: DeserializationContext) -> T
|
||||||
|
typealias KKeyDeserializer<T> = String.(ctxt: DeserializationContext) -> T
|
||||||
|
|
||||||
|
interface JsonTypeMapper<T> {
|
||||||
|
val type: Class<T>
|
||||||
|
fun serialize(value: T, gen: JsonGenerator, serializers: SerializerProvider)
|
||||||
|
fun deserialize(p: JsonParser, ctxt: DeserializationContext): T
|
||||||
|
fun keyDeserialize(value: String, ctxt: DeserializationContext): T
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> jsonTypeMapper(
|
||||||
|
noinline serializer: KSerializer<T>,
|
||||||
|
noinline deserializer: KDeserializer<T>,
|
||||||
|
noinline keyDeserializer: KKeyDeserializer<T>
|
||||||
|
) =
|
||||||
|
object : JsonTypeMapper<T> {
|
||||||
|
override val type: Class<T> = T::class.java
|
||||||
|
|
||||||
|
override fun serialize(value: T, gen: JsonGenerator, serializers: SerializerProvider) =
|
||||||
|
serializer(value, gen, serializers)
|
||||||
|
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
|
||||||
|
deserializer(p, ctxt)
|
||||||
|
|
||||||
|
override fun keyDeserialize(value: String, ctxt: DeserializationContext) =
|
||||||
|
keyDeserializer(value, ctxt)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> ObjectMapper.addMapper(
|
||||||
|
noinline serializer: KSerializer<T>,
|
||||||
|
noinline deserializer: KDeserializer<T>,
|
||||||
|
noinline keyDeserializer: KKeyDeserializer<T>
|
||||||
|
) = addMapper(jsonTypeMapper(serializer, deserializer, keyDeserializer))
|
||||||
|
|
||||||
|
fun <T : Any> ObjectMapper.addMapper(mapper: JsonTypeMapper<T>) =
|
||||||
|
registerModule(object : SimpleModule() {
|
||||||
|
init {
|
||||||
|
val serializer = object : JsonSerializer<T>() {
|
||||||
|
override fun serialize(value: T, gen: JsonGenerator, serializers: SerializerProvider) =
|
||||||
|
mapper.serialize(value, gen, serializers)
|
||||||
|
}
|
||||||
|
val deserializer = object : JsonDeserializer<T>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext) =
|
||||||
|
mapper.deserialize(p, ctxt)
|
||||||
|
}
|
||||||
|
val keyDeserializer = object : KeyDeserializer() {
|
||||||
|
override fun deserializeKey(key: String, ctxt: DeserializationContext) =
|
||||||
|
mapper.keyDeserialize(key, ctxt)
|
||||||
|
|
||||||
|
}
|
||||||
|
addSerializer(mapper.type, serializer)
|
||||||
|
addDeserializer(mapper.type, deserializer)
|
||||||
|
addKeySerializer(mapper.type, serializer)
|
||||||
|
addKeyDeserializer(mapper.type, keyDeserializer)
|
||||||
|
}
|
||||||
|
})!!
|
|
@ -1,57 +0,0 @@
|
||||||
package com.smallhacker.disbrowser.util
|
|
||||||
|
|
||||||
abstract class UType(val bytes: Int, val name: String) {
|
|
||||||
val mask = (1 shl (bytes * 8)) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
class UVal<U : UType>(value: Int, val type: U) : Comparable<UVal<U>> {
|
|
||||||
val value = value and type.mask
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = equalsBy(other, { value }, { type })
|
|
||||||
|
|
||||||
override fun toString() = "${type.name}(${toHex(true)})"
|
|
||||||
|
|
||||||
fun toHex(prefix: Boolean = false): String {
|
|
||||||
val digits = 2 * type.bytes
|
|
||||||
val start = if (prefix) "0x" else ""
|
|
||||||
val pattern = "%0${digits}x"
|
|
||||||
return start + String.format(pattern, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compareTo(other: UVal<U>): Int = Integer.compare(value, other.value)
|
|
||||||
|
|
||||||
override fun hashCode() = hashOf(value, type)
|
|
||||||
|
|
||||||
//fun value(value: Int) = UVal(value, type)
|
|
||||||
//fun mutate(mutator: (Int) -> Int) = UVal(mutator(value), type)
|
|
||||||
|
|
||||||
object U1 : UType(1, "OldUByte")
|
|
||||||
object U2 : UType(2, "OldUWord")
|
|
||||||
object U3 : UType(3, "OldULong")
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified U: UVal<*>> U.value(value: Int): U = UVal(value, type) as U
|
|
||||||
inline infix fun <reified U: UVal<*>> U.mutate(mutator: (Int) -> Int): U = value(mutator(value))
|
|
||||||
|
|
||||||
typealias OldUByte = UVal<UVal.U1>
|
|
||||||
typealias OldUWord = UVal<UVal.U2>
|
|
||||||
typealias OldULong = UVal<UVal.U3>
|
|
||||||
|
|
||||||
fun UVal<*>.toByte() = uByte(value)
|
|
||||||
fun UVal<*>.toWord() = uWord(value)
|
|
||||||
fun UVal<*>.toLong() = uLong(value)
|
|
||||||
|
|
||||||
private val UBYTE_CACHE = Array(256) { OldUByte(it, UVal.U1) }
|
|
||||||
|
|
||||||
fun uByte(value: Int): OldUByte = UBYTE_CACHE[value and 0xFF]
|
|
||||||
fun uWord(value: Int): OldUWord = UVal(value, UVal.U2)
|
|
||||||
fun uLong(value: Int): OldULong = UVal(value, UVal.U3)
|
|
||||||
|
|
||||||
inline infix fun <reified U: UVal<V>, V: UType> U.left(count: Int): U = mutate { it shl count }
|
|
||||||
inline infix fun <reified U: UVal<V>, V: UType> U.right(count: Int): U = mutate { it ushr count }
|
|
||||||
inline infix fun <reified U: UVal<V>, V: UType> U.and(other: U): U = mutate { it and other.value }
|
|
||||||
inline infix fun <reified U: UVal<V>, V: UType> U.or(other: U): U = mutate { it or other.value }
|
|
||||||
inline operator fun <reified U: UVal<*>> U.not(): U = mutate { it.inv() }
|
|
||||||
|
|
||||||
inline infix operator fun <reified U: UVal<V>, V: UType> U.plus(other: U): U = mutate { it + other.value }
|
|
||||||
inline infix operator fun <reified U: UVal<V>, V: UType> U.minus(other: U): U = mutate { it - other.value }
|
|
|
@ -3,10 +3,15 @@ package com.smallhacker.disbrowser.util
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.smallhacker.disbrowser.memory.SnesAddress
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
val jsonMapper: ObjectMapper by lazy {
|
val jsonMapper: ObjectMapper by lazy {
|
||||||
jacksonObjectMapper()
|
jacksonObjectMapper()
|
||||||
|
.addMapper(
|
||||||
|
{ gen, _ -> gen.writeString(this.toSimpleString()) },
|
||||||
|
{ SnesAddress.parse(text)!! },
|
||||||
|
{ SnesAddress.parse(this)!! })
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JsonFile<T> {
|
interface JsonFile<T> {
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
package com.smallhacker.disbrowser.util
|
|
||||||
|
|
||||||
inline fun <reified T> T.equalsBy(other: Any?, vararg values: T.() -> Any?) = when (other) {
|
|
||||||
!is T -> false
|
|
||||||
else -> values.asSequence()
|
|
||||||
.map { it(this) to it(other) }
|
|
||||||
.all { it.first == it.second }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun tryParseInt(s: String, radix: Int = 10): Int? {
|
|
||||||
return try {
|
|
||||||
Integer.parseInt(s, radix)
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toHex(value: UInt, bytes: UInt): String {
|
|
||||||
if (bytes == 0u) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
val digits = 2u * bytes
|
|
||||||
val pattern = "%0${digits}x"
|
|
||||||
return String.format(pattern, value.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toHex(value: UInt24) = toHex(value.toUInt(), 3u)
|
|
||||||
fun toHex(value: UShort) = toHex(value.toUInt(), 2u)
|
|
||||||
fun toHex(value: UByte) = toHex(value.toUInt(), 1u)
|
|
||||||
|
|
||||||
fun joinBytes(vararg bytes: UByte) = bytes
|
|
||||||
.asSequence()
|
|
||||||
.mapIndexed { index, v -> v.toUInt() shl (index * 8) }
|
|
||||||
.reduce { a, b -> a or b }
|
|
||||||
|
|
||||||
fun joinNullableBytes(vararg bytes: UByte?): UInt? {
|
|
||||||
if (bytes.any { it == null }) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes
|
|
||||||
.asSequence()
|
|
||||||
.mapIndexed { index, v -> v!!.toUInt() shl (index * 8) }
|
|
||||||
.reduce { a, b -> a or b }
|
|
||||||
}
|
|
||||||
|
|
||||||
inline class UInt24(private val data: UInt) {
|
|
||||||
fun toUInt() = data and 0x00FF_FFFFu
|
|
||||||
fun toUShort() = toUInt().toUShort()
|
|
||||||
fun toUByte() = toUInt().toUByte()
|
|
||||||
fun toInt() = toUInt().toInt()
|
|
||||||
fun toShort() = toUShort().toShort()
|
|
||||||
fun toByte() = toUByte().toByte()
|
|
||||||
|
|
||||||
infix fun and(v: UInt24) = (data and v.data).toUInt24()
|
|
||||||
infix fun and(v: UInt) = (data and v).toUInt24()
|
|
||||||
infix fun or(v: UInt24) = (data or v.data).toUInt24()
|
|
||||||
infix fun or(v: UInt) = (data or v).toUInt24()
|
|
||||||
infix fun shl(v: Int) = (data shl v).toUInt24()
|
|
||||||
infix fun shr(v: Int) = (toUInt() shr v).toUInt24()
|
|
||||||
|
|
||||||
operator fun plus(v: UInt24) = (toUInt() + v.toUInt()).toUInt24()
|
|
||||||
operator fun plus(v: UInt) = (toUInt() + v).toUInt24()
|
|
||||||
operator fun plus(v: Int) = (toInt() + v).toUInt24()
|
|
||||||
operator fun minus(v: UInt24) = (toUInt() - v.toUInt()).toUInt24()
|
|
||||||
operator fun minus(v: UInt) = (toUInt() - v).toUInt24()
|
|
||||||
operator fun minus(v: Int) = (toInt() - v).toUInt24()
|
|
||||||
|
|
||||||
override fun toString() = data.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun UInt.toUInt24() = UInt24(this and 0x00FF_FFFFu)
|
|
||||||
fun UShort.toUInt24() = this.toUInt().toUInt24()
|
|
||||||
fun UByte.toUInt24() = this.toUInt().toUInt24()
|
|
||||||
fun Int.toUInt24() = this.toUInt().toUInt24()
|
|
||||||
fun Short.toUInt24() = this.toUInt().toUInt24()
|
|
||||||
fun Byte.toUInt24() = this.toUInt().toUInt24()
|
|
||||||
|
|
||||||
fun <T> List<T>.asReverseSequence(): Sequence<T> =
|
|
||||||
((size - 1) downTo 0).asSequence().map { this[it] }
|
|
||||||
|
|
||||||
fun <K, V> MutableMap<K, V>.removeIf(condition: (K, V) -> Boolean) {
|
|
||||||
this.asSequence()
|
|
||||||
.filter { condition(it.key, it.value) }
|
|
||||||
.map { it.key }
|
|
||||||
.toList()
|
|
||||||
.forEach { remove(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hashOf(vararg values: Any?): Int {
|
|
||||||
return if (values.isEmpty()) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
values.asSequence()
|
|
||||||
.map { it.hashCode() }
|
|
||||||
.reduce { acc, v -> (acc * 31) + v }
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue