• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Development: Lua? In MY emulator?

knizz

192
Posts
16
Years
    • Seen Oct 28, 2020
    It's likelier than you think!

    I forked VBA-M a few days ago to add scripting support and it just reached alpha level.
    You can download/star/subscribe the project at https://github.com/dkreuter/VBA-M

    You'll have to compile it yourself, and I only have a linux machine to work with, so I don't know if it even works with windows.

    Anyway, here are some of the uses I imagine:
    • ROM-Hackers could write their gameplay in a proper programming language instead of pokescript.
    • ROM-Hackers could add features like multiplayer.
      1. Hacker X wants to know to something about offset Y but can't find where Y is used.
      2. X writes a script that triggers when Y is read and uploads it to a server
      3. Somewhere else, the lua-enabled emulator downloads that script and lets it interact with the game.
      4. When the script triggers it uploads a savegame to the server for X to analyze.

    I personally want to use it to get traces as nice html.

    Feedback? Bug-reports? Questions? Suggestions?
     

    knizz

    192
    Posts
    16
    Years
    • Seen Oct 28, 2020
    UPDATE: The building process for linux users should be fully automated now.

    Here's a test-file you can use with my emulator. Put it in the top folder of the project and name it… "pokemon.lua" maybe. Launch the emulator with 'vbam -l firered.gba' in the top folder, press F11, type 'require "pokemon"'. Resume emulation with Ctrl+D. You should see lots of debug output now. (PROFIT!)
    Code:
    -- Usage:
    --  require "pokemon"
    --  Ctrl+D
    
    require "bkpt"
    bit = require "bit"
    stack = require "stack"
    stack.over = {}
    
    mute_c3 = false
    
    function pwrite(x)
        io.write(string.format("%-80s", x))
        printtrace()
    end
    
    function c3_init_enter()
        print("\n======== RESET C3 ========\n")
        mute_c3 = true
    end
    
    function c3_init_leave()
        mute_c3 = false
    end
    
    function c3watch_trigger(self, a, b, c)
        if mute_c3 then return end
        local space = ""
        for i=1,self.id do space = space.."            " end
        if a == 0 then
            pwrite(space..string.format("#%02x ", self.id) .. ref(c))
        elseif a == 4 then
            pwrite(space..string.format("#%02x ", self.id) .. (c==1 and "ON" or "OFF"))
        end
    end
    
    function dp01watch_trigger(self, a, b, c)
        local space = ""
        for i=1,self.id do space = space.."       " end
        pwrite("DP01 ==== "..space..ref(c))
    end
    
    in_script = false
    function script_enter() in_script = true  end
    function script_leave() in_script = false end
    function script_env_trigger(self, a, b, c)
        -- a = offset
        -- b = old value
        -- c = new value
        if a >= 0x74 then
            return
        end
        if a == 0x5C and c ~= 0x0815F9B4 then
            print("!!!!!!!!!!!!!!!!!!!!!!!!!!!")
            print(string.format("New script command table @ %08x", c))
        end
        if not in_script then
            print(string.format("untracked change 0x%08x+%02x %x->%x", self.start, a, b, c))
            printtrace()
        end
    end
    function script_desc()
        local p = gba.reg(4)
        return string.format("scriptenv %08x @ %08x ", p, gba.mem32(p+8))
    end
    function script_cmd()
        print(script_desc().."cmd: "..ref(gba.reg(1)))
    end
    function script_env_init()
        in_script = true
        pwrite(string.format("scriptenv %08x @ reset", gba.reg(4)))
    end
    function script_status_normal()
        in_script = true
        pwrite(string.format("scriptenv %08x @ %08x LAUNCH", gba.reg(0), gba.reg(1)))
    end
    function oamt_spawn_from_uns_2()
        pwrite(string.format("OAMt from UNS 2 %08x", gba.reg(0)))
    end
    function oamt_spawn_from_uns_1()
        pwrite(string.format("OAMt from UNS 1 %08x", gba.reg(0)))
    end
    
    -- -- -- -- -- -- -- --
    
    function watch_array(start, count, size, trigger)
        for i=0,count do
            local nw = {}
            nw.id = i
            nw.start = start + size*i
            nw.stop =  start + size*(i+1)
            nw.trigger = trigger
            table.insert(watch, nw)
        end
        gba.oldapi("bpw", string.format("0x%08x", start), string.format("%d", count*size))
    end
    
    function watch_simple(pos, size, msg)
        function pp(self, a, b, c)
            pwrite(string.format("%s: %08x+%x: ", msg, self.start, a)..ref(b).." -> "..ref(c))
        end
        local nw = {}
        nw.start = pos
        nw.stop = pos + size
        nw.trigger = pp
        table.insert(watch, nw)
        gba.oldapi("bpw", string.format("0x%08x", pos), string.format("%d", size))
    end
    
    function regmodpoint(addr, reg, value)
        function modder()
            gba.reg(reg, value)
        end
        table.insert(bkpts.inactive, addr)
        bkpts.callback[addr] = modder
    end
    
    -- -- -- -- -- -- -- --
    
    function install()
        bkpts:add(0x080773BC, c3_init_enter)
        bkpts:add(0x0807740C, c3_init_leave)
        bkpts:add(0x08069804, script_enter)
        bkpts:add(0x0806986E, script_cmd)
        bkpts:add(0x0806987C, script_leave)
        bkpts:add(0x080697AC, script_env_init)
        bkpts:add(0x080697E2, script_leave)
        bkpts:add(0x080697E8, script_status_normal)
        bkpts:add(0x080697F0, script_leave)
        bkpts:add(0x08006F8C, oamt_spawn_from_uns_1)
        bkpts:add(0x08006FE0, oamt_spawn_from_uns_2)
    
        watch_array(0x03005090, 16, 0x28, c3watch_trigger)
        watch_array(0x03000eb0,  2, 0x78, script_env_trigger)
        watch_array(0x03004FE0, 4, 4, dp01watch_trigger)
        watch_simple(0x03005020, 4, "map loader")
        -- watch_simple(0x030030F0, 8)
        -- watch_simple(0x02023D74, 4)
        -- watch_simple(0x03004F84, 4)
    
        add_tracepoint(0x08035AC0, "                               ------------AC0", 1)
        -- add_tracepoint(0x0800D8B0, "load dp01 buffer", 1)
        -- add_tracepoint(0x08014068, "", 0)
        -- add_tracepoint(0x08012408, "c1_battle_dp01_bx_r0", 0)
    
        bkpts:enable()
    
        -- You probably don't have this file.
        -- loadnames("firered.map")
    end
    
    -- -- -- -- -- -- -- --
    
    names = {}
    
    function ref(addr)
        local addr = bit.band(addr, bit.bnot(1))
        return names[addr] or string.format("%08x", addr)
    end
    
    function loadnames(fname)
        for line in io.lines(fname) do
            k, v = string.match(line, ":(%x+)       (.+)")
            if k ~= nil then
                names[tonumber(k, 16)+0x08000000] = v
            end
        end
    end
    
    -- -- -- -- -- -- -- --
    
    function introskipper()
        gba.mem32(0x080EC5D0, 0x0800C301)
        gba.mem32(0x0800C4C4, 0x08056645)
        gba.mem32(0x080EC5D0, 0x0800C301)
        gba.mem32(0x0800C4C4, 0x08056645) -- Player Name: Kamon
        gba.mem32(0x08054A68, 0x68204C3C)
        gba.mem32(0x08054A6C, 0xE0014900)
        gba.mem32(0x08054A70, 0x081C5797)
        gba.mem16(0x08054A7E, 0x4E34)
        gba.mem16(0x08054B3A, 0x0000)
        gba.mem16(0x08054B3C, 0x0000)
    end
    
    -- -- -- -- -- -- -- --
    
    install()
    introskipper()

    I can't really explain all of it right now. I hope you can figure it out from the lua-files.
     
    Last edited:

    Shiny Quagsire

    I'm Still Alive, Elsewhere
    697
    Posts
    14
    Years
  • Epic!

    I can see so much potential with this. If the Lua can be triggered on a RAM write, you could use a writebytetooffset in a pokescript to trigger a lua script, do some stuff, then return!

    As for networking, that might take a bit of work for stuff like a DPPt style underground, but I can see some REALLY good but simple uses for networking: Mystery Gifts. Use an online distribution system (or codes based on an online thingy) to get certain items or event pokemon. Or, a GTS to trade pokemon with other ROM-Hack-players. OR, an online saving system where you can view your progress... With networking, the possibilities are endless!

    Although, I must request one feature if it's not already existant: Linking (wireless or wired) from a Lua script. While it's possible manually, it's annoyingly difficult. If we could have link connections from lua, we could have a wifi battle system, or a wifi trading system in all the Pokemon Games!
     
    Back
    Top