mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-05 21:34: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 {
|
||||
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);
|
||||
|
||||
try file.seekTo(0);
|
||||
|
||||
const readed = try file.readAll(file_contents);
|
||||
if (readed != file_size) {
|
||||
return error.EndOfStream;
|
||||
|
Loading…
x
Reference in New Issue
Block a user