mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-12 03:36:57 +08:00
Compare commits
No commits in common. "e17f8a4dd6fdab3eab7715fd5f6422f45adf9afd" and "9afbcd5bf7ad7c3b4199cebdb7b533ba834ad48b" have entirely different histories.
e17f8a4dd6
...
9afbcd5bf7
@ -1,648 +0,0 @@
|
|||||||
// build command: kotlinc -cp json-simple.jar -Xinline-classes -include-runtime -jvm-target=20 -d mkacl.jar mkacl.kt
|
|
||||||
// run command: java -cp "json-simple.jar:kotlin-stdlib.jar:mkacl.jar" MkaclKt
|
|
||||||
// java的内存blt确实麻烦,计算速度也比较慢,生成内容就没做验证了,反正真的慢
|
|
||||||
|
|
||||||
import org.json.simple.*
|
|
||||||
import java.nio.file.*
|
|
||||||
import java.nio.*
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.system.*
|
|
||||||
import kotlin.time.*
|
|
||||||
|
|
||||||
enum class ParserState {
|
|
||||||
WaitLock,
|
|
||||||
ReadLock,
|
|
||||||
WaitHole,
|
|
||||||
ReadHole,
|
|
||||||
ReadKey,
|
|
||||||
WaitPart,
|
|
||||||
NeedPart,
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias LockKey = ArrayList<String>
|
|
||||||
data class LockHole(val name: String, var keys: ArrayList<LockKey>)
|
|
||||||
data class Lock(val name: String, var holes: ArrayList<LockHole>)
|
|
||||||
data class CheatMap(val serial: String, val order: String)
|
|
||||||
data class CheatSet(val code: UInt, val sets: ByteArray)
|
|
||||||
data class CheatGrp(val serial: String, val grp: ArrayList<CheatSet>)
|
|
||||||
data class Entry(val id: UInt, val isKeys: Boolean, val strs: ArrayList<String>?, var keys: ArrayList<LockKey>? )
|
|
||||||
data class EntData(val id:UInt, val loc: UInt, val len: UInt)
|
|
||||||
data class AsData(val addr:UInt, val value: UInt);
|
|
||||||
|
|
||||||
val PassHole = arrayListOf("off", "text")
|
|
||||||
|
|
||||||
fun findIndex(t: ArrayList<UByte>, tr: ByteArray): Int {
|
|
||||||
val rlen = tr.size
|
|
||||||
val tlen = t.size
|
|
||||||
val limit = tlen - rlen
|
|
||||||
for (i in 0..<limit) {
|
|
||||||
if (t[i+rlen].toUInt() != 0u) continue;
|
|
||||||
|
|
||||||
var eq = true
|
|
||||||
for (j in 0..<rlen) {
|
|
||||||
if (t[i+j].toByte() != tr[j]) {
|
|
||||||
eq = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (eq) return i
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadList(): ArrayList<CheatMap> {
|
|
||||||
// 读取文件内容
|
|
||||||
val jsonContent = Files.readAllBytes( Paths.get("serial.json") )
|
|
||||||
val jsonText = String(jsonContent)
|
|
||||||
|
|
||||||
// 解析 JSON
|
|
||||||
val jso = JSONValue.parse(jsonText) as JSONObject
|
|
||||||
var list = ArrayList<CheatMap>()
|
|
||||||
for (entry in jso) {
|
|
||||||
val order = entry.key as String
|
|
||||||
val serial = entry.value as String
|
|
||||||
if (serial != "????") {
|
|
||||||
list.add(CheatMap(serial, order))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
fun transform( list: ArrayList<CheatMap> ): ArrayList<CheatGrp> {
|
|
||||||
var retval = ArrayList<CheatGrp>()
|
|
||||||
for (item in list) {
|
|
||||||
val file = "./gba/${item.order}.u8"
|
|
||||||
if (!Files.exists(Paths.get(file))) {
|
|
||||||
retval.add(CheatGrp(item.serial, ArrayList()))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val chtdata = Files.readAllBytes( Paths.get(file) );
|
|
||||||
if (chtdata.size > 0) {
|
|
||||||
var cheats = parse(chtdata, item.serial, item.order);
|
|
||||||
retval.add(CheatGrp(item.serial, cheats))
|
|
||||||
}
|
|
||||||
else retval.add(CheatGrp(item.serial, ArrayList()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun makeID( a:UInt, b: UInt): UInt = a or (b shl 16) // 令人窒息的代码!
|
|
||||||
|
|
||||||
inline fun align( n: UInt, base: UInt ): UInt = (n + base - 1u) and (base - 1u).inv()
|
|
||||||
|
|
||||||
fun isSpace(tr: ArrayList<Char>): Boolean {
|
|
||||||
var res = false
|
|
||||||
for (ch in tr) {
|
|
||||||
if (ch != ' ' && ch != '\t'){
|
|
||||||
res = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
fun parse(data: ByteArray, serial: String, order: String): ArrayList<CheatSet> {
|
|
||||||
var retval = ArrayList<CheatSet>()
|
|
||||||
var state = ParserState.WaitLock
|
|
||||||
var token = ArrayList<Char>()
|
|
||||||
var line = 1
|
|
||||||
var locks = ArrayList<Lock>()
|
|
||||||
var currlock: Lock? = null
|
|
||||||
var currhole: LockHole? = null
|
|
||||||
|
|
||||||
fun tokenStr(): String {
|
|
||||||
val retval = String( token.toCharArray() )
|
|
||||||
token.clear()
|
|
||||||
return retval
|
|
||||||
}
|
|
||||||
|
|
||||||
fun error(msg: String) {
|
|
||||||
println("Error: $msg at line $line in $order")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun incr( ch: Char ) {
|
|
||||||
when(ch) {
|
|
||||||
'[' -> {
|
|
||||||
if (state == ParserState.WaitLock || state == ParserState.ReadHole || state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.ReadLock
|
|
||||||
if (currlock != null) {
|
|
||||||
locks.add(currlock!!)
|
|
||||||
}
|
|
||||||
currlock = Lock("", ArrayList())
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else error("error occur [ on $state")
|
|
||||||
}
|
|
||||||
']' -> {
|
|
||||||
if (state == ParserState.ReadLock){
|
|
||||||
state = ParserState.WaitHole
|
|
||||||
val name = tokenStr()
|
|
||||||
if (name.lowercase() == "gameinfo"){
|
|
||||||
retval.add( lade(locks, serial, order) );
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
locks = ArrayList()
|
|
||||||
currhole = null
|
|
||||||
currlock = null
|
|
||||||
}
|
|
||||||
else if (name.length > 0) {
|
|
||||||
currlock = Lock(name, ArrayList())
|
|
||||||
currhole = null
|
|
||||||
}
|
|
||||||
else error("empty lock name in $order")
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else error("error occur ] on $state")
|
|
||||||
}
|
|
||||||
'\r', '\n' -> {
|
|
||||||
if (state == ParserState.WaitHole) {
|
|
||||||
state = ParserState.ReadHole
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadKey) {
|
|
||||||
val token = tokenStr()
|
|
||||||
var pass = false
|
|
||||||
if (currhole != null) {
|
|
||||||
if (token.length > 0) {
|
|
||||||
currhole!!.keys.get(currhole!!.keys.size - 1).add(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PassHole.contains(currhole!!.name.lowercase())) {
|
|
||||||
pass = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!pass) {
|
|
||||||
if (currlock != null) {
|
|
||||||
currlock!!.holes.add(currhole!!)
|
|
||||||
}
|
|
||||||
currhole = null
|
|
||||||
}
|
|
||||||
state = ParserState.ReadHole
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadLock) {
|
|
||||||
error("error occur newline on $state")
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadHole) {
|
|
||||||
if (token.size > 0 && isSpace(token)) {
|
|
||||||
error("error occur newline on $state")
|
|
||||||
}
|
|
||||||
else token.clear()
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {
|
|
||||||
state = ParserState.NeedPart
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
if (ch == '\n') ++line
|
|
||||||
}
|
|
||||||
'=' -> {
|
|
||||||
if (state == ParserState.ReadHole) {
|
|
||||||
state = ParserState.ReadKey
|
|
||||||
currhole = LockHole(tokenStr(), arrayListOf( LockKey() ))
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadLock) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else error("error occur = on $state")
|
|
||||||
}
|
|
||||||
',' -> {
|
|
||||||
if (state == ParserState.ReadKey) {
|
|
||||||
if (token.size > 0) {
|
|
||||||
currhole!!.keys.get(currhole!!.keys.size - 1).add(tokenStr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadHole) {
|
|
||||||
var t = currlock!!.holes.removeAt(currlock!!.holes.size - 1)
|
|
||||||
if (token.size > 0) t.keys.get(t.keys.size - 1).add(tokenStr())
|
|
||||||
currhole = t
|
|
||||||
state = ParserState.ReadKey
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadLock) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else error("error occur , on $state")
|
|
||||||
}
|
|
||||||
';' -> {
|
|
||||||
if (state == ParserState.ReadKey) {
|
|
||||||
if (token.size > 0) currhole!!.keys.get(currhole!!.keys.size - 1).add(tokenStr())
|
|
||||||
currhole!!.keys.add(LockKey())
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadLock) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadHole) {
|
|
||||||
var t = currlock!!.holes.removeAt(currlock!!.holes.size - 1)
|
|
||||||
if (token.size > 0) t.keys.get(t.keys.size - 1).add(tokenStr())
|
|
||||||
t.keys.add(LockKey())
|
|
||||||
currhole = t
|
|
||||||
state = ParserState.ReadKey
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else error("error occur ; on $state")
|
|
||||||
}
|
|
||||||
' ' -> {
|
|
||||||
if (state == ParserState.ReadLock || state == ParserState.ReadHole) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
if (state == ParserState.ReadLock || state == ParserState.ReadHole) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (state == ParserState.ReadKey) {
|
|
||||||
if ( "0123456789abcdefABCDEF".indexOf(ch) >= 0) {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
else if (currhole != null) {
|
|
||||||
if (currhole!!.name == "text") {
|
|
||||||
token.add(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else error("error occur $ch on $state")
|
|
||||||
}
|
|
||||||
else if (state == ParserState.WaitPart) {}
|
|
||||||
else if (state == ParserState.NeedPart) {
|
|
||||||
state = ParserState.WaitPart
|
|
||||||
}
|
|
||||||
else error("error occur $ch on $state")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun done() {
|
|
||||||
if (state != ParserState.WaitPart && state != ParserState.NeedPart) {
|
|
||||||
error("Unexpected end of file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val data = String(data, Charsets.UTF_8)
|
|
||||||
for (ch in data) {
|
|
||||||
incr(ch)
|
|
||||||
}
|
|
||||||
done()
|
|
||||||
|
|
||||||
return retval
|
|
||||||
}
|
|
||||||
|
|
||||||
fun assembleCheat( list: ArrayList<LockKey>, dup: TreeMap<UInt, UShort>, hole: UShort, order: String): ArrayList<UInt> {
|
|
||||||
fun fromTaddr(taddr: String): UInt {
|
|
||||||
val n = taddr.toInt(16)
|
|
||||||
if (n == 0 || n > 0x41fffff) {
|
|
||||||
println("get an invalid address $taddr in $order")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n <= 0x3ffff) {
|
|
||||||
return n.toUInt() or 0x2000000u
|
|
||||||
}
|
|
||||||
else if (0 == (n and 0xf000000)) {
|
|
||||||
return (n.toUInt() and 0xffffu) or 0x3000000u
|
|
||||||
}
|
|
||||||
else return n.toUInt()
|
|
||||||
}
|
|
||||||
var addrval = TreeMap<UInt, UInt>()
|
|
||||||
for (command in list) {
|
|
||||||
if (command.size == 0){
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val addr = fromTaddr(command[0])
|
|
||||||
var len = command.size - 1
|
|
||||||
for (pos in 0..<len) {
|
|
||||||
addrval[addr + pos.toUInt()] = command[pos + 1].toUInt(16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val ordered = ArrayList<AsData>()
|
|
||||||
ordered.ensureCapacity(addrval.size)
|
|
||||||
addrval.forEach {
|
|
||||||
ordered.add(AsData(it.key, it.value))
|
|
||||||
}
|
|
||||||
ordered.sortBy { it.addr }
|
|
||||||
|
|
||||||
var blocks = ArrayList<UInt>()
|
|
||||||
var curr = arrayOf(0u, 0u, 0u)
|
|
||||||
for (asd in ordered) {
|
|
||||||
dup[asd.addr] = asd.value.toUShort()
|
|
||||||
|
|
||||||
if (curr[2] == 0u) {
|
|
||||||
curr[0] = asd.addr
|
|
||||||
curr[1] = asd.value
|
|
||||||
curr[2] = 1u
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (curr[1] == asd.value && curr[0] + curr[2] == asd.addr) {
|
|
||||||
curr[2] += 1u
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
blocks.add(curr[0])
|
|
||||||
blocks.add( (curr[2] shl 8) or curr[1] )
|
|
||||||
curr[0] = asd.addr
|
|
||||||
curr[1] = asd.value
|
|
||||||
curr[2] = 1u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (curr[2] != 0u) {
|
|
||||||
blocks.add( curr[0] )
|
|
||||||
blocks.add( (curr[2] shl 8) or curr[1] )
|
|
||||||
}
|
|
||||||
|
|
||||||
return blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lade(locks: ArrayList<Lock>, serial: String, order: String): CheatSet {
|
|
||||||
var rootstr = ArrayList<String>()
|
|
||||||
var names = ArrayList<String>()
|
|
||||||
var enttable = ArrayList<Entry>()
|
|
||||||
|
|
||||||
for ((i, lock) in locks.withIndex()) {
|
|
||||||
rootstr.add(lock.name)
|
|
||||||
names.add(lock.name)
|
|
||||||
|
|
||||||
var holestr = ArrayList<String>()
|
|
||||||
var colname = lock.holes.size > 1;
|
|
||||||
for ((index, hole) in lock.holes.withIndex()) {
|
|
||||||
holestr.add(hole.name)
|
|
||||||
if (colname) names.add(hole.name)
|
|
||||||
val id = makeID(i.toUInt() + 1u, index.toUInt() + 1u)
|
|
||||||
enttable.add(Entry(id, true, null, hole.keys))
|
|
||||||
}
|
|
||||||
val id = makeID(i.toUInt() + 1u, 0u)
|
|
||||||
enttable.add(Entry(id, false, holestr, null))
|
|
||||||
}
|
|
||||||
enttable.add(Entry(0u, false, rootstr, null))
|
|
||||||
|
|
||||||
if (enttable.size > 0xffff) {
|
|
||||||
println("entry count is overflow in file $order")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
enttable.sortBy { it.id }
|
|
||||||
names.sortWith( comparator = { a, b -> if (a.length == b.length) { a.compareTo(b) } else { b.length - a.length } })
|
|
||||||
|
|
||||||
var strtable = ArrayList<UByte>()
|
|
||||||
var idxtable = ArrayList<UShort>()
|
|
||||||
var cmdtable = ArrayList<UInt>()
|
|
||||||
var strcache = HashMap<String, UShort>()
|
|
||||||
|
|
||||||
fun utpush( tr: ByteArray ): UShort {
|
|
||||||
val off = strtable.size
|
|
||||||
strtable.ensureCapacity(off + tr.size + 1)
|
|
||||||
strtable.addAll( arrayListOf(*tr.map { it.toUByte() }.toTypedArray()) )
|
|
||||||
strtable.add(0u.toUByte())
|
|
||||||
return off.toUShort()
|
|
||||||
}
|
|
||||||
fun utaddr( tr: String ): UShort {
|
|
||||||
if (strcache.containsKey(tr)) {
|
|
||||||
return strcache[tr]!!
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val codes = tr.toByteArray(Charsets.UTF_8)
|
|
||||||
var idx = findIndex(strtable, codes)
|
|
||||||
var off:UShort = if (idx < 0) { utpush(codes) } else { idx.toUShort() }
|
|
||||||
strcache[tr] = off
|
|
||||||
return off
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addString(list: ArrayList<String>): Array<UShort> {
|
|
||||||
var ret: Array<UShort> = Array<UShort>(list.size) {0u}
|
|
||||||
for ((i,s) in list.withIndex()) {
|
|
||||||
ret[i] = utaddr(s)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addStrIndex(list: Array<UShort>) : UShort {
|
|
||||||
if (list.size == 0) return 0u
|
|
||||||
|
|
||||||
if (strtable.size > 0xffff) {
|
|
||||||
println("String table overflow $order")
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = idxtable.size
|
|
||||||
idxtable.ensureCapacity(ret + list.size)
|
|
||||||
idxtable.addAll( list )
|
|
||||||
return (ret * 2).toUShort()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addCommand( list: ArrayList<UInt> ): UInt {
|
|
||||||
var ret = cmdtable.size
|
|
||||||
cmdtable.ensureCapacity( ret + list.size )
|
|
||||||
cmdtable.addAll( list )
|
|
||||||
return (ret * 4).toUInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
addString(names)
|
|
||||||
|
|
||||||
var entbytelist = ArrayList<EntData>()
|
|
||||||
var emptylist = ArrayList<String>()
|
|
||||||
var holedup = TreeMap<UInt, UShort>()
|
|
||||||
for (entry in enttable) {
|
|
||||||
val id = entry.id
|
|
||||||
if (entry.isKeys) {
|
|
||||||
val armbytecode = assembleCheat( entry.keys!!, holedup, (id and 0xffffu).toUShort(), order )
|
|
||||||
val location = addCommand( armbytecode )
|
|
||||||
entbytelist.add( EntData(id, location, armbytecode.size.toUInt()) )
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val idxlist = addString( if (id==0u) { entry.strs!! } else if (entry.strs!!.size > 1) { entry.strs!! } else { emptylist} )
|
|
||||||
val location = addStrIndex( idxlist )
|
|
||||||
entbytelist.add( EntData( id, location.toUInt(), idxlist.size.toUInt()) )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var code = 0u
|
|
||||||
for (ch in serial) {
|
|
||||||
code = (code shl 8) or (ch.code.toUInt() and 0xffu)
|
|
||||||
}
|
|
||||||
for (cmd in cmdtable) {
|
|
||||||
code = code xor cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
return CheatSet(code, pack(entbytelist, strtable, idxtable, cmdtable))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pack( entlist: ArrayList<EntData>, strlist: ArrayList<UByte>, idxlist: ArrayList<UShort>, cmdlist: ArrayList<UInt>): ByteArray {
|
|
||||||
val entrysize = 12
|
|
||||||
val strbase = 2 + entlist.size * entrysize
|
|
||||||
val idxbase = strbase + strlist.size
|
|
||||||
val cmdbase = idxbase + idxlist.size * 2
|
|
||||||
val datasize = cmdbase + cmdlist.size * 4
|
|
||||||
val size = align(datasize.toUInt(), 32u)
|
|
||||||
|
|
||||||
var ret = ByteArray(size.toInt())
|
|
||||||
var mem = ByteBuffer.wrap(ret)
|
|
||||||
mem.order(ByteOrder.LITTLE_ENDIAN)
|
|
||||||
mem.putShort( 0, entlist.size.toShort() )
|
|
||||||
|
|
||||||
for ((i, entry) in entlist.withIndex()) {
|
|
||||||
val offset = 2 + i * entrysize
|
|
||||||
val locbase = if (entry.id > 0xffffu) { cmdbase } else { idxbase}
|
|
||||||
mem.putInt(offset, entry.id.toInt())
|
|
||||||
mem.putInt(offset+4, locbase + entry.loc.toInt())
|
|
||||||
mem.putInt(offset+8, entry.len.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy
|
|
||||||
var mempos = strbase
|
|
||||||
strlist.forEach {
|
|
||||||
mem.put(mempos, it.toByte())
|
|
||||||
mempos += 1
|
|
||||||
}
|
|
||||||
idxlist.forEach {
|
|
||||||
mem.putShort(mempos, it.toShort())
|
|
||||||
mempos += 2
|
|
||||||
}
|
|
||||||
cmdlist.forEach {
|
|
||||||
mem.putInt(mempos, it.toInt())
|
|
||||||
mempos += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
fun format( cheats: ArrayList<CheatGrp> ): ByteArray {
|
|
||||||
cheats.sortBy { it.serial }
|
|
||||||
println("valid rom has ${cheats.size}")
|
|
||||||
|
|
||||||
var sers: ArrayList<Byte>? = null
|
|
||||||
var offs: ArrayList<Short>? = null
|
|
||||||
var chtc: Int? = null
|
|
||||||
var maxl: Int? = null
|
|
||||||
fun step1()
|
|
||||||
{
|
|
||||||
var item1 = ArrayList<Byte>()
|
|
||||||
var item2 = ArrayList<Short>()
|
|
||||||
var item3 = 0
|
|
||||||
var item4 = 0
|
|
||||||
var item5 = ""
|
|
||||||
var last = 0
|
|
||||||
for (c in cheats) {
|
|
||||||
val value = c.serial.substring(0, 3)
|
|
||||||
if (item5 != value) {
|
|
||||||
if (item2.size > 0 && item3 - last > item4) {
|
|
||||||
item4 = item3 - last
|
|
||||||
}
|
|
||||||
val bytes = value.toByteArray()
|
|
||||||
item1.add( bytes[0] )
|
|
||||||
item1.add( bytes[1] )
|
|
||||||
item1.add( bytes[2] )
|
|
||||||
item2.add( item3.toShort() )
|
|
||||||
last = item3
|
|
||||||
item5 = value
|
|
||||||
}
|
|
||||||
item3 += c.grp.size
|
|
||||||
}
|
|
||||||
if (item2.size > 0) {
|
|
||||||
if (item3 - last > item4) {
|
|
||||||
item4 = item3 - last
|
|
||||||
}
|
|
||||||
item2.add( item3.toShort() )
|
|
||||||
}
|
|
||||||
sers = item1
|
|
||||||
offs = item2
|
|
||||||
chtc = item3
|
|
||||||
maxl = item4
|
|
||||||
}
|
|
||||||
step1()
|
|
||||||
|
|
||||||
var chts: ArrayList<Int>? = null
|
|
||||||
var expanded: ArrayList<Byte>? = null
|
|
||||||
fun step2() {
|
|
||||||
var item1 = ArrayList<Int>()
|
|
||||||
var item2 = ArrayList<Byte>()
|
|
||||||
val item3 = align( (8 + sers!!.size + offs!!.size*2 + chtc!! * 8).toUInt(), 32u)
|
|
||||||
for (c in cheats) {
|
|
||||||
var value = Character.codePointAt(c.serial, 3) and 0xFF
|
|
||||||
for (cht in c.grp) {
|
|
||||||
val off = item3.toInt() + item2.size
|
|
||||||
item1.add( value or (off shl 3) )
|
|
||||||
item1.add( cht.code.toInt() )
|
|
||||||
item2.ensureCapacity( item2.size + cht.sets.size )
|
|
||||||
for (t in 0..<cht.sets.size) {
|
|
||||||
item2.add( cht.sets[t] )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chts = item1
|
|
||||||
expanded = item2
|
|
||||||
}
|
|
||||||
step2()
|
|
||||||
println("name: ${sers!!.size} cheats: $chtc maxl: $maxl")
|
|
||||||
|
|
||||||
val serialbase = 8
|
|
||||||
val offsetbase = serialbase + sers!!.size
|
|
||||||
val cheatbase = offsetbase + offs!!.size * 2
|
|
||||||
val cheatend = cheatbase + chts!!.size * 4
|
|
||||||
val expandbase = align(cheatend.toUInt(), 32u).toInt()
|
|
||||||
val total = expandbase + expanded!!.size
|
|
||||||
|
|
||||||
var retval = ByteArray(total)
|
|
||||||
retval[0] = Character.codePointAt("ACL", 0).toByte()
|
|
||||||
retval[1] = Character.codePointAt("ACL", 1).toByte()
|
|
||||||
retval[2] = Character.codePointAt("ACL", 2).toByte()
|
|
||||||
retval[3] = 1
|
|
||||||
|
|
||||||
var mem = ByteBuffer.wrap(retval, 4, total - 4)
|
|
||||||
mem.putShort( (sers!!.size / 3).toShort() )
|
|
||||||
mem.putShort( maxl!!.toShort() )
|
|
||||||
|
|
||||||
var mempos = serialbase
|
|
||||||
sers!!.forEach {
|
|
||||||
mem.put( mempos, it.toByte() )
|
|
||||||
mempos++
|
|
||||||
}
|
|
||||||
mempos = offsetbase
|
|
||||||
offs!!.forEach {
|
|
||||||
mem.putShort( mempos, it.toShort() )
|
|
||||||
mempos += 2
|
|
||||||
}
|
|
||||||
mempos = cheatbase
|
|
||||||
chts!!.forEach {
|
|
||||||
mem.put( mempos, it.toByte() )
|
|
||||||
mempos++
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
val tsrc = TimeSource.Monotonic
|
|
||||||
val start = tsrc.markNow()
|
|
||||||
val list = loadList()
|
|
||||||
val roms = transform( list );
|
|
||||||
println("all rom has ${roms.size} and time ${start.elapsedNow()}")
|
|
||||||
var idx = TreeMap<String, ArrayList<CheatSet>>()
|
|
||||||
roms.forEach {
|
|
||||||
if (it.grp.size == 0) return@forEach
|
|
||||||
|
|
||||||
if (idx.containsKey(it.serial) == false) {
|
|
||||||
idx[it.serial] = ArrayList<CheatSet>()
|
|
||||||
}
|
|
||||||
idx[it.serial]!!.addAll(it.grp)
|
|
||||||
}
|
|
||||||
|
|
||||||
var nlist = ArrayList<CheatGrp>()
|
|
||||||
idx.forEach {
|
|
||||||
nlist.add( CheatGrp(it.key, it.value) )
|
|
||||||
}
|
|
||||||
val content = format( nlist )
|
|
||||||
Files.write(Paths.get("gba.acl"), content)
|
|
||||||
}
|
|
@ -120,14 +120,9 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn readAll(allocator: Allocator, file: File) ![]u8 {
|
fn readAll(allocator: Allocator, file: File) ![]u8 {
|
||||||
//const file_size: usize = @intCast(try file.getEndPos());
|
const file_size: usize = @intCast(try file.getEndPos());
|
||||||
//const file_contents = try allocator.alloc(u8, file_size);
|
|
||||||
try file.seekFromEnd(0);
|
|
||||||
const file_size = try file.getPos();
|
|
||||||
const file_contents = try allocator.alloc(u8, file_size);
|
const file_contents = try allocator.alloc(u8, file_size);
|
||||||
|
|
||||||
try file.seekTo(0);
|
|
||||||
|
|
||||||
const readed = try file.readAll(file_contents);
|
const readed = try file.readAll(file_contents);
|
||||||
if (readed != file_size) {
|
if (readed != file_size) {
|
||||||
return error.EndOfStream;
|
return error.EndOfStream;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user