添加go版本的代码,测试比aot后的C#要慢,大概是2*native

This commit is contained in:
a92126 2025-03-20 16:33:03 +08:00
parent 91548c067e
commit 4d952f6a82

View File

@ -0,0 +1,672 @@
package main;
import (
"fmt"
"os"
"encoding/json"
"encoding/binary"
"strings"
"bytes"
"sort"
)
type Serial struct {
order string
serial string
}
type Packed struct {
code uint32
data []byte
}
type Cheat struct {
serial string
cheats []Packed
}
type LockKey = []string
type LockHole struct {
name string
keys []LockKey
}
type Lock struct {
name string
holes []LockHole
}
type EntData struct {
id uint32
t uint
strs [](*string)
cmds *[]LockKey
}
const (
WaitLock = iota
ReadLock
WaitHole
ReadHole
ReadKey
WaitPart
NeedPart
)
const (
EntNames = iota
EntCmds
)
func align(n uint32, basic uint32) uint32 {
ext := n % basic
if ext == 0 {
return n
} else {
return n + basic - ext
}
}
func find_index(t []byte, tr []byte) int {
rlen, tlen := len(tr), len(t)
limit := tlen - rlen
for i := 0; i < limit; i++ {
if t[i+rlen] != 0 {
continue
}
eq := true
for j := 0; j < rlen; j++ {
if tr[j] != t[i+j] {
eq = false
break
}
}
if eq {
return i
}
}
return -1
}
func assemble_cheat(list *[]LockKey, dup map[uint32]uint16, hole uint16) []uint32 {
var atoi = func(t string) uint32 {
var n uint32
fmt.Sscanf(t, "%x", &n)
return n
}
var from_taddr = func(taddr string) uint32 {
n := atoi(taddr)
if n == 0 || n > 0x41fffff {
panic(fmt.Sprintf("invalid taddr %s", taddr))
}
if n <= 0x3ffff {
return n | 0x2000000
}else if 0 == (n&0xf000000) {
return (n & 0xffff) | 0x3000000
}else { return n }
}
addrval := make(map[uint32]uint32, 0)
for _, command := range *list {
if len(command) == 0 {
continue
}
addr := from_taddr(command[0])
len := len(command) - 1
for i := 0; i < len; i++ {
addrval[addr + uint32(i)] = atoi(command[i+1])
}
}
ordered := make([][2]uint32, 0)
for addr, val := range addrval {
ordered = append(ordered, [2]uint32{addr, val})
}
sort.Slice(ordered, func(i, j int) bool {
return ordered[i][0] < ordered[j][0]
})
blocks, curr := make([]uint32, 0), [3]uint32{0, 0, 0}
for _, addrval := range ordered {
addr, value := addrval[0], addrval[1]
dup[addr] = hole
if 0 == curr[2] {
curr = [3]uint32{addr, value, 1}
}else if curr[1] == value && curr[0] + curr[2] == addr {
curr[2]++
}else {
blocks = append(blocks, curr[0], curr[2]<<8 | curr[1])
curr = [3]uint32{addr, value, 1}
}
}
if curr[2] > 0 {
blocks = append(blocks, curr[0], curr[2]<<8 | curr[1])
}
return blocks
}
func loadlist() ([]Serial, error) {
bytes, err := os.ReadFile("./serial.json")
if err != nil {
fmt.Println(err)
return nil, err
}
var jsonData map[string]string
err = json.Unmarshal(bytes, &jsonData)
if err != nil {
fmt.Println(err)
return nil, err
}
var serials []Serial
for k, v := range jsonData {
if v != "????" {
serials = append(serials, Serial{order: k, serial: v})
}
}
return serials, nil
}
func lade(locks []Lock, serial string, order string) Packed {
rootstr, names := make([]*string, 0), make([]*string, 0)
enttable := make([]EntData, 0)
var make_id = func(a uint32, b uint32) uint32 {
return a | b<<16
};
for i, lock := range locks {
rootstr = append(rootstr, &lock.name)
names = append(names, &lock.name)
holestr := make([]*string, 0)
colname := len(lock.holes) > 1
for idx, hole := range lock.holes {
holestr = append(holestr, &hole.name)
if colname {
names = append(names, &hole.name)
}
id := make_id(uint32(i+1), uint32(idx+1))
enttable = append( enttable, EntData{
id, EntCmds, make([]*string, 0), &hole.keys,
})
}
enttable = append(enttable, EntData{
make_id(uint32(i+1), 0),
EntNames, holestr, nil,
})
}
enttable = append(enttable, EntData{
make_id(0, 0), EntNames, rootstr, nil,
})
if len(enttable) > 0xffff {
panic("too many entries")
}
sort.Slice(enttable, func(i, j int) bool {
return enttable[i].id < enttable[j].id
})
sort.Slice(names, func(i, j int) bool {
if len(*names[i]) == len(*names[j]) {
return *names[i] < *names[j]
}else {
return len(*names[j]) < len(*names[i])
}
})
strtable, idxtable, cmdtable := make([]byte, 0), make([]uint16, 0), make([]uint32, 0)
strcache := make(map[string]uint16, 0)
var ut_push = func(tr []byte) uint16 {
off := len(strtable)
for _, v := range tr {
strtable = append(strtable, v)
}
strtable = append(strtable, 0)
return uint16(off)
}
var ut_addr = func(tr string) uint16 {
if v, ok := strcache[tr]; ok {
return v
}
idx := find_index(strtable, []byte(tr))
var off uint16
if idx < 0 { off = ut_push([]byte(tr)) } else { off = uint16(idx) }
//uint16(idx < 0 ? ut_push([]byte(tr)) : idx)
strcache[tr] = off
return off
}
var add_string = func(list []*string) []uint16 {
retval := make([]uint16, len(list))
for i, v := range list {
retval[i] = ut_addr(*v)
}
return retval
}
var add_str_index = func(list []uint16) uint16 {
if len(list) == 0 {
return 0
}
if len(strtable) > 0xffff {
panic("too many strings")
}
retval := uint16(len(idxtable))
for _, v := range list {
idxtable = append(idxtable, v)
}
return retval*2
}
var add_command = func(list []uint32) uint32 {
retval := len(cmdtable)
for _, v := range list {
cmdtable = append(cmdtable, v)
}
return uint32(retval*4)
}
add_string(names)
entbytelist, emptylist, holedup := make([][3]uint32, 0), make([]*string, 0), make(map[uint32]uint16, 0)
for _, entry := range enttable {
if entry.t == EntNames {
var idxlist []uint16
if entry.id == 0 {
idxlist = add_string( entry.strs )
}else if len(entry.strs) > 1 {
idxlist = add_string( entry.strs )
}else {
idxlist = add_string( emptylist )
}
location := add_str_index(idxlist)
entbytelist = append(entbytelist, [3]uint32{
entry.id, uint32(location), uint32(len(idxlist)),
})
} else {
armbytecode := assemble_cheat( entry.cmds, holedup, uint16(entry.id & 0xffff) )
location := add_command(armbytecode)
entbytelist = append(entbytelist, [3]uint32{
entry.id, uint32(location), uint32(len(armbytecode)),
})
}
}
var code uint32 = 0
for _, c := range []byte(serial) {
code = (code << 8) | uint32(c & 0xff)
}
for _, cmd := range cmdtable {
code = code ^ cmd
}
return Packed{code, pack(entbytelist, strtable, idxtable, cmdtable) }
}
func pack(entlist [][3]uint32, strlist []byte, idxlist []uint16, cmdlist []uint32) []byte {
entrysize := 12
strbase := 2 + len(entlist) * entrysize
idxbase := strbase + len(strlist)
cmdbase := idxbase + len(idxlist) * 2
nonalign_size := cmdbase + len(cmdlist) * 4
size := align(uint32(nonalign_size), 32)
result := make([]byte, size)
binary.Encode(result, binary.LittleEndian, uint32(len(entlist)))
for i, entry := range entlist {
offset := i * entrysize + 2
locbase := idxbase
if entry[0] > 0xffff {
locbase = cmdbase
}
binary.Encode(result[offset:], binary.LittleEndian, entry[0])
binary.Encode(result[offset+4:], binary.LittleEndian, entry[1] + uint32(locbase))
binary.Encode(result[offset+8:], binary.LittleEndian, entry[2])
}
copy(result[strbase:], strlist)
for i, v := range idxlist {
binary.Encode(result[idxbase + i*2:], binary.LittleEndian, v)
}
for i, v := range cmdlist {
binary.Encode(result[cmdbase + i*4:], binary.LittleEndian, v)
}
return result
}
func parse(datas []byte, serial string, order string) ([]Packed, error) {
var retval []Packed
var token []byte
state, line, locks := WaitLock, 1, make([]Lock, 0)
var currlock *Lock
var currhole *LockHole
var token_str = func() string {
var r = string( token );
token = make([]byte, 0)
return r;
};
var error = func(msg string) {
panicmsg := fmt.Sprintf("error occur %s on line %d", msg, line)
panic(panicmsg)
};
// incr
var incr = func(ch byte) {
switch ch {
case '[':
if state == WaitLock || state == ReadHole || state == NeedPart {
state = ReadLock;
if currlock != nil {
locks = append(locks, *currlock );
}
currlock = &Lock{"", make([]LockHole, 0)};
}else if state == WaitPart {
}else { error(fmt.Sprintf("error occur [ on %d", state) ) }
case ']':
if state == ReadLock {
state = WaitHole
name := token_str()
if strings.ToLower(name) == "gameinfo" {
retval = append(retval, lade(locks, serial, order))
state = WaitPart
locks = make([]Lock, 0)
currlock = nil
currhole = nil
}else if len(name) > 0 {
currlock = &Lock{name, make([]LockHole, 0)}
currhole = nil
}else { error(fmt.Sprintf("empty lock name in %s", order) ) }
}else if state == WaitPart {
}else if state == NeedPart{
state = WaitPart
}else { error(fmt.Sprintf("error occur ] on %d", state) ) }
case '\r', '\n':
if state == WaitHole {
state = ReadHole
}else if state == ReadKey {
t, pass := token_str(), false
if currhole != nil {
if len(t) > 0 {
idx := len(currhole.keys) - 1
currhole.keys[idx] = append(currhole.keys[idx], t)
}
t = strings.ToLower(currhole.name)
if t == "text" || t == "off" {
pass = true
}
}
if !pass {
if currlock != nil {
if currhole == nil { error("unreachable for pass") }
currlock.holes = append(currlock.holes, *currhole)
//LockHole{name: currhole.name, keys: currhole.keys})
}
currhole = nil
}
state = ReadHole
}else if state == ReadLock {
error(fmt.Sprintf("error occur newline on %d", state))
}else if state == ReadHole {
if len(token) > 0 && bytes.ContainsFunc(token, func(r rune) bool { return r != ' ' && r != '\t' }) {
error(fmt.Sprintf("error occur space on %d", state))
} else {
token = make([]byte, 0)
}
}else if state == WaitPart {
state = NeedPart
} else {}
if ch == '\n' { line++ }
case '=':
if state == ReadHole {
state = ReadKey
currhole = &LockHole{name: token_str(), keys: make([]LockKey, 1)}
}else if state == ReadLock {
token = append(token, ch)
}else if state == WaitPart {
}else if state == NeedPart {
state = WaitPart
}else { error(fmt.Sprintf("error occur = on %d", state)) }
case ',':
if state == ReadKey {
if currhole == nil { error("unreachable for currhole") }
if len(token) > 0 {
idx := len(currhole.keys) - 1
currhole.keys[idx] = append(currhole.keys[idx], token_str())
}
}else if state == ReadHole {
if currlock == nil { error("unreachable for currlock") }
idx := len(currlock.holes) - 1
tmp := currlock.holes[idx]
currlock.holes = currlock.holes[:idx]
if len(token) > 0 {
pos := len(tmp.keys) - 1
tmp.keys[pos] = append(tmp.keys[pos], token_str())
}
currhole = &LockHole{name: tmp.name, keys: tmp.keys}
state = ReadKey
}else if state == ReadLock {
token = append(token, ch)
}else if state == WaitPart {
}else if state == NeedPart {
state = WaitPart
}else { error(fmt.Sprintf("error occur , on %d", state)) }
case ';':
if state == ReadKey {
if currhole == nil { error("unreachable for currhole") }
if len(token) > 0 {
idx := len(currhole.keys) - 1
currhole.keys[idx] = append(currhole.keys[idx], token_str())
}
if currhole != nil {
currhole.keys = append(currhole.keys, make([]string, 0))
}
}else if state == ReadLock {
token = append(token, ch)
}else if state == ReadHole {
if currlock == nil { error("unreachable for currlock") }
idx := len(currlock.holes) - 1
tmp := currlock.holes[idx]
currlock.holes = currlock.holes[:idx]
if len(token) > 0 {
pos := len(tmp.keys) - 1
tmp.keys[pos] = append(tmp.keys[pos], token_str())
}
tmp.keys = append(tmp.keys, make([]string, 0))
currhole = &LockHole{name: tmp.name, keys: tmp.keys}
state = ReadKey
}else if state == WaitPart {
}else if state == NeedPart {
state = WaitPart
}else { error(fmt.Sprintf("error occur ; on %d", state)) }
case ' ':
if state == ReadLock || state == ReadHole {
token = append(token, ch)
}else if state == NeedPart {
state = WaitPart
}else {}
default:
if state == ReadLock || state == ReadHole {
token = append(token, ch)
}else if state == ReadKey {
if strings.Contains("0123456789abcdefABCDEF", string(ch)) {
token = append(token, ch)
} else if currhole != nil {
if strings.ToLower(currhole.name) == "text" {
token = append(token, ch)
}
} else { error(fmt.Sprintf("error occur %c on %d", ch, state)) }
}else if state == WaitPart {
}else if state == NeedPart {
state = WaitPart
} else { error(fmt.Sprintf("error occur %c on %d", ch, state))}
}
}
// done
var done = func() {
if state != WaitPart && state != NeedPart {
error(fmt.Sprintf("unexpected end of file %d in %s", state, order))
}
}
for _, ch := range datas {
incr(ch)
}
done()
return retval, nil
}
func transform(list []Serial) []Cheat {
var retval []Cheat
for _, game := range list {
file := fmt.Sprintf("./gba/%s.u8", game.order)
if _, err := os.Stat(file); os.IsNotExist(err) {
// fmt.Println(file, " not found")
retval = append( retval, Cheat{ game.serial, make([]Packed, 0) } )
continue
}
bytes, err := os.ReadFile(file)
if err != nil || len(bytes) == 0 {
retval = append( retval, Cheat{ game.serial, make([]Packed, 0) } )
} else {
cheats, err := parse(bytes, game.serial, game.order)
if err != nil {
fmt.Println(err)
retval = append( retval, Cheat{ game.serial, make([]Packed, 0) } )
} else {
retval = append( retval, Cheat{ game.serial, cheats } )
}
}
}
return retval
}
func format( cheats []Cheat ) []byte {
sort.Slice( cheats, func(i, j int) bool {
return cheats[i].serial < cheats[j].serial
})
fmt.Println("valid rom has", len(cheats))
var step1 = func() ([]byte, []uint16, uint32, uint32) {
item1, item2 := make([]byte, 0), make([]uint16, 0)
item3, item4, item5, last := uint32(0), uint32(0), "", uint32(0)
for _, ch := range cheats {
serial, cheat := ch.serial, ch.cheats
val := serial[0:3]
if item5 != val {
if len(item2) > 0 && item3 - last > item4 {
item4 = item3 - last
}
item1 = append(item1, []byte(val)...)
item2 = append(item2, uint16(item3))
last = item3
item5 = val
}
item3 += uint32(len(cheat))
}
if len(item2) > 0 {
if item3 - last > item4 {
item4 = item3 - last
}
item2 = append(item2, uint16(item3))
}
return item1, item2, item3, item4
}
sers, offs, chtc, maxl := step1()
var step2 = func() ([]uint32, []byte) {
item1, item2 := make([]uint32, 0), make([]byte, 0)
item3 := align(uint32(8 + len(sers) + len(offs)*2 + int(chtc) * 8), 32)
for _, ch := range cheats {
serial, cheat := ch.serial, ch.cheats
val := serial[3]
for _, cht := range cheat {
id, bin := cht.code, cht.data
off := item3 + uint32(len(item2))
item1 = append(item1, uint32(val) | (off<<3), id)
item2 = append(item2, bin...)
}
}
return item1, item2
}
chts, expanded := step2()
fmt.Printf("name: %d cheats: %d maxl: %d", len(sers), chtc, maxl)
serialbase := 8
offsetbase := serialbase + len(sers)
cheatbase := offsetbase + len(offs) * 2
cheatend := cheatbase + len(chts) * 4
expandbase := align(uint32(cheatend), 32)
total := int(expandbase) + len(expanded)
retval := make([]byte, total)
retval[0] = 'A'
retval[1] = 'C'
retval[2] = 'L'
retval[3] = 1
binary.Encode(retval[4:], binary.LittleEndian, uint16(len(sers)/3))
binary.Encode(retval[6:], binary.LittleEndian, uint16(maxl))
copy(retval[serialbase:], sers)
for i, off := range offs {
binary.Encode(retval[offsetbase+i*2:], binary.LittleEndian, off)
}
for i, ch := range chts {
binary.Encode(retval[cheatbase+i*4:], binary.LittleEndian, ch)
}
copy(retval[cheatbase:], expanded)
return retval
}
func main() {
list, err := loadlist()
if err != nil {
fmt.Println(err)
return
}
roms := transform(list)
fmt.Println("all rom has", len(roms))
idx := make(map[string][]Packed, 0)
for _, rom := range roms {
if len(rom.cheats) == 0 {
continue
}
if _, ok := idx[rom.serial]; !ok {
idx[rom.serial] = make([]Packed, 0)
}
idx[rom.serial] = append(idx[rom.serial], rom.cheats...)
}
cheats := make([]Cheat, 0)
for serial, chts := range idx {
cheats = append(cheats, Cheat{ serial, chts })
}
content := format( cheats )
os.WriteFile("gba.acl", content, 0644)
}