mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 05:44:11 +08:00
Compare commits
2 Commits
9afbcd5bf7
...
e17f8a4dd6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e17f8a4dd6 | ||
![]() |
dbb4c28ffe |
648
tools/cheat-builder/make-acl.kt
Normal file
648
tools/cheat-builder/make-acl.kt
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
// 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,9 +120,14 @@ 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