Homebrew Possibility of "game changes when I X" with DS emulators?

EpicGamer256893

Active Member
OP
Newcomer
Joined
Jul 24, 2020
Messages
29
Trophies
0
Location
my house
XP
159
Country
Australia
I've seen some videos around where someone is playing a game but every time they meet a certain criteria (dying, taking damge, collecting a coin, etc) it changes to a different game. Most of the videos are just different recordings of them playing different games that are then edited together but today I saw this video by Sonic Central showing off 'Alistar's Magic Box', a Mega Drive emulator that automatically switches between different Sonic games when a ring is collected or on a timer. Is something like this possible for Nintendo DS games?

(sorry I can't post links yet my account is still somewhat new)
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
Desmume has a Lua scripting option which can reach out and touch or read the "internal" memory on the DS. Should then be easy enough to trip a switch when you hit a score count, lives count, exp count... (basically anything that you could alter as a simple cheat, could even make something a cheat is not good at like counting if you have a tell -- this thing is only run when you die if then a game does not have a death counter or a lives* counter you can use instead) and get it to do something else, something else then could be loading another ROM or maybe switching to another emulator (possibly with attendant savestate).

*start with 10 lives and have no means of gaining another and then if you hit 5 lives you have died 5 times.

If you wanted to get absolutely ridiculous I can see a path to this in hardware, though it would be some rather custom work. That being you could combine ROMs and make a quasi flash cart reset to load another game also stuffed in the ROM, and also do a change on conditions being met, possibly intro skip/quasi savestate too.
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
Afraid I am not that familiar with the day to day use of lua in desmume (this is not really my thing, and if I do a challenge run it is usually within the game's own framework) but I reckon I can do an overview and otherwise point you in the right direction.

Finding memory location is basically just finding cheats
For the GBA but works the same on just about everything
https://web.archive.org/web/20080309104350/http://etk.scener.org/?op=tutorial

https://gamehacking.org/wiki/Hacking_NDS (if that is down do a search for enhacklopedia as that is the base document that various places mirror)

The more advanced ROM hackers and cheat makers will have a mental library of things devs have done in the past and things dev could do as a possibility (if I want stats then I tend to expect them to all be like stats, say every characters attack stat one after the other, or every character/weapon/spell/item having their own individual list one after the other or at set locations), possibly some tricks (if you want the super rare only available after a 40 hour side quest/grind sword then you can do that, however if item 1 is the rusty kitchen knife you start with, 2 is the first sword in the shop, 3 is the big sword, 4 is magic sword... then keep ticking up numbers for things in your inventory and eventually you will find the rare stuff, or alternatively you can probably buy 50 potions to learn where it is in memory, the only picked up once or twice in dungeons thing is most likely a few bytes away from that), how games tend to be put together (if I am looking for something in memory I will have a good idea of when it will be put into it, or at least of a list of good starting locations, which can help with certain things, and also can do tracing https://www.romhacking.net/documents/361/ ) but that just makes my life easier by virtue of it rather than strictly being necessary for most things.

Lua
http://wiki.desmume.org/index.php?title=Faq#What_is_this_Lua_stuff_I_see.3F
Lua is a general purpose programming language (some circles in game hacking have their own specific things, not my favourite thing to see but is what it is), seen in many things but game wise it is emulators that mostly we see it from (hacker and tool assisted speedrunner, though they can be different emulators).
https://www.lua.org/

The main reference most emulators will be built to mimic, which does include desmume, is the NES emulator FCEUX (it is also one of the best examples of a debugging emulator)
http://fceux.com/web/help/fceux.html?LuaScripting.html
Looking at
http://fceux.com/web/help/fceux.html?LuaFunctionsList.html
emu.loadrom(string filename)


Loads the ROM from the directory relative to the lua script or from the absolute path. Hence, the filename parameter can be absolute or relative path.


If the ROM can't be loaded, loads the most recent one.

savestate.load(object savestate)


Load the the given state. The argument is the result of savestate.create() and has been passed to savestate.save() at least once.


If this savestate is not persistent and not one of the predefined states, the state will be deleted after loading.

If it is matching in functions (or at least those ones) then to that end combine a basic memory read of the value you found with the cheat searching inside a basic IF ELSE (lua might use a different phrase but IF ELSE is what most will know it as in general programming) style loop* ( https://www.tutorialspoint.com/lua/lua_loops.htm seems like a reasonable start) and then those things for loading ROM and matching savestate (which you would make ahead of time to be on the right level with all the necessary preconditions). I don't know what you would have to flash up a message saying do this to proceed but there should be something somewhere in that, or at least something external you can call, or just have a list of things). Get the basics working and you can then start doing say random loads of things. Technically you don't need a savestate either as you could generate all the data necessary and inject it into memory (could even carry things, say lives, over from previous games if you want to make things follow through, though you can do that easily enough with cheats).

*I don't know how much programming you already know. We do have a general intro to such things https://gbatemp.net/threads/so-you-want-to-learn-to-program.371255/ but a more applicable to this might be
Anyway more is better but something like described above can be done with some basics, and if you grasp it here then when it comes time to learn programming for real you will have a leg up. If nothing else then for the sake of others playing along at home
Computers/processors have maybe three main functions.
1) Maths. Adding things, subtracting (in some cases), multiplication, division (the GBA notably lacks it in the CPU and has to be an extra function)... for various different styles of number (signed, unsigned, floating point, higher precision floating point, fixed point, grids/matrices/arrays, larger values, smaller values...), plus boolean logic, plus certain types of shuffling data around (shifts, rotates, floor, ceiling, masks though masks might also be boolean https://help.libreoffice.org/Calc/Mathematical_Functions https://www.osdata.com/topic/language/asm/shiftrot.htm ) or sorting it and a few other related things. Everything mentioned thus far is very useful in a lot of different situations and likely what you will spend a good chunk of your initial learning programming getting drilled on. Here then... basic maths like you might do in a calculator will probably do, probably not even trigonometry is necessary for this sort of thing though the more you know the more creative you can be with some of the things you do.
2) Housekeeping. There is only a limited amount of space to do things with so things need to get shuffled in and out of registers (the size of these being what determines the "bit" of a machine/OS, and when it says 32 bits it means 32 1s or 0s in a row, possibly only about 20 of them, unless it is the NES and in that case technically 3 but practically 2 of them and they are 8 bit). Higher level languages handle more of this for you at the cost of speed, Lua is a very high level language so will spare you most things.
3) Program flow. Maths and fiddling with numbers is fun and housekeeping is necessary. The good stuff is when you can do maths and then depending upon the results of said maths choose a different path to go down. Branches and jumps, https://xkcd.com/292/ , loops (you might want to do something over and over, or over and over until it is finished, or do something if something happens but something else if not) and various other words meaning much the same thing as all those then all being part of this.
This is also where a lot of code goes wrong and where a lot of exploits can happen.
To that end if you also want to learn the common errors in programming then it will help too (not least of all because you as the new to programming type will be making many of them yourself)
https://textexpander.com/blog/the-7...-errors-in-programming-and-how-to-avoid-them/
Also means you can have some fun but setting values beyond what the game might expect, quite good for challenges as well if you want to throw people's normal expectations/play styles off a bit)

A nice example of some of the fun that can be done here
https://www.romhacking.net/forum/index.php?topic=18717.0

A more basic example but for the DS
https://forums.desmume.org/viewtopic.php?id=10957
https://forums.desmume.org/viewtopic.php?id=11217

Beyond this everything is how creative you want to be.
Random game selection rather than set progression maybe, could even be a branching path depending upon performance.
Setting inventory
Setting a handicap (stats, maximum 3 ammo at any one time)
Maybe messing with controls (up is down, run jammed on, no shooting...)
Little randomiser maybe (might need to learn level editing basics for this one)
Jumping between one room in one game, another room in another game when you exist that first game's room, before jumping around some more... Or maybe just a quick and easy way to do different level order, repeat a level/segment, or maybe a boss rush. Can even do something silly like "beat your score from last time" which might be hard if they played to win last time and now have to beat that where a person that held back might have an easier time.
"Die 5 times", or maybe load savestate in choice locations and get them to die or do something special and die afterwards.
Finish exactly on/exactly with/...
Randomly load a savestate from a few seconds back just to mess with the player.
Combination of goals rather than simple score or whatever
If not a random savestate then random turbo mode, or random slowdown (messes with rhythm which in turn messes with players that play to it)
Force certain graphical layers off
... this could go on for a while. All relatively easy to do, give or take playing to the point in the game where you make the savestate (remember you have cheats and savestates of your own)

If you want to know how to do the hardware thing I speculated upon then that is a far greater project. The harder part would likely be merging games together and not having them interfere with each other, though the assembly is not likely to be anything other than tedious. If none of that or the previous thing makes sense or you can't ask sensible questions about what you might want to have to do from a general programming understanding of things (don't expect you to know specific header and file call methods of the DS, would expect you to speculate on their existence as a general thing and hurdle to overcome) then you have a long road ahead before you get anything there. Personally I could do it but would sooner grab a DS flash cart, reset option and savestates, and possibly just leave it as a manual thing.
 
  • Like
Reactions: EpicGamer256893

Hodgermccodger

New Member
Newbie
Joined
Jun 15, 2021
Messages
4
Trophies
0
Age
36
XP
39
Country
United Kingdom
I wondered if you had had any luck with this? I'm trying to do exactly the same thing using lua.

I've been able to get it to load a save state of the same game after reaching a certain point (e.g on health). However while I can get it to load a different ROM something is preventing the rest of the script working.
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
Any chance you can post the script either in a codebox or to something like pastebin and if you are still restricted on URLs tell us pastebin and this is the unique part of the URL?

I can't rule out some kind of problem with desmume and the lua breaking after a new game is loaded but I usually go with error with the script before thinking of anything there.
The functions on the fceux site don't mention the emulation needing to be started again after a ROM is loaded but in various programming scenarios then loading a whole new file does mean you want to do the whole frameadvance thing again if it is a new function.
 
  • Like
Reactions: Hodgermccodger

Hodgermccodger

New Member
Newbie
Joined
Jun 15, 2021
Messages
4
Trophies
0
Age
36
XP
39
Country
United Kingdom
Any chance you can post the script either in a codebox or to something like pastebin and if you are still restricted on URLs tell us pastebin and this is the unique part of the URL?

I can't rule out some kind of problem with desmume and the lua breaking after a new game is loaded but I usually go with error with the script before thinking of anything there.
The functions on the fceux site don't mention the emulation needing to be started again after a ROM is loaded but in various programming scenarios then loading a whole new file does mean you want to do the whole frameadvance thing again if it is a new function.

Firstly, thank you very much for your reply and help.

I've been using the lua console on Bizhawk, and to test out the code I've been trying to jump from a ROM of Mario Brothers to Ninja Gaiden.

The following code works and in Ninja Gaiden when your health reaches 8 you go to a save state within ninja gaiden

while ((memory.readbyte(0x0065)) ~= 8) do
emu.frameadvance()
end
savestate.load("Ninja Gaiden.QuickNes.Boss.State")

However if you try to load a different ROM and then a save state there are problems. First of all I tried emu.loadrom("Ninja Gaiden (USA).nes")- however this doesn't work at all- it doesn't open the ROM even as a single line of code. Indeed any of the emu. functions didn't seem to work- they produce a nil value error message, for example-
NLua.Exceptions.LuaScriptException: [string "main"]:6: attempt to call field 'pause' (a nil value)

I looked at the documentation and found that client.openrom might serve the same purpose. And it does work in so far as it opens the ROM, but it also produces an error message and the rest of the code doesn't work- e.g

client.openrom("Super Mario Bros. (World).nes")
while ((memory.readbyte(0x0756)) < 2) do
emu.frameadvance()
end
savestate.load("Super Mario Bros.QuickNes.Mario.State")

This should load Super Mario Bros, and then when he gets a fire flower it should end the loop and load the savestate. Instead it opens Super Mario Bros and then appears to stop working. The rest of the code works fine without the first line. The error message that appears is

NLua.Exceptions.LuaException: unprotected error in call to Lua API (0)

Ideally I was trying to create something like this-

client.openrom("Ninja Gaiden (USA).nes")
while ((memory.readbyte(0x0065)) ~= 14) do
emu.frameadvance()
end
client.openrom("Super Mario Bros. (World).nes")
savestate.load("Super Mario Bros.QuickNes.Mario.State")

In this case- Ninja Gaiden loads, you drop to 14 health and then a save state from Mario starts. This doesn't work at the moment, and I assume it's because of the lua API error. I would be very grateful for any thoughts on how to solve this. As may be quite evident I have very limited understanding of how this works, and I'm really just doing a lot of trial and error.
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
Afraid I have not played with bizhawk to know what goes or if it does anything different, and am not going to able to play with it here to test things right now.
It would also appear someone has come before you and done something kind of similar to what you want
https://github.com/alistairaitcheson/BizHawk-Shuffler
"that triggers game swaps when the player performs in-game actions (e.g. collecting a ring in Sonic games)"

But I would also also want to understand what went wrong here (not to mention that looks rather complicated as it is designed for more general uses)

http://tasvideos.org/Bizhawk/LuaFunctions.html then being what we are playing with here.
It would appear then in bizhawk that client.openrom takes the place of emu.openrom in the desumume/fceux world covered above. It does not give any guidance like the fceux one on what it expects for file location but if it works in part of that I will assume it still supports relative path at least.
https://www.twilio.com/blog/how-to-write-lua-scripts-for-video-games-with-the-bizhawk-emulator might also be worth looking at as a basic overview and it has some more ideas on what a script should look like, though does not do anything about changing out ROMs.

So your code
Code:
client.openrom("Ninja Gaiden (USA).nes")
   while ((memory.readbyte(0x0065)) ~= 14) do
   emu.frameadvance()
end
client.openrom("Super Mario Bros. (World).nes")
savestate.load("Super Mario Bros.QuickNes.Mario.State")

I am always wary of spaces and brackets in names of files and in programs in general but it appears to work in the first instance. For super mario then I note two "." in the name which could be something. I also assume it is in the same directory or path as Ninja Gaiden for this?
For my own sanity I tend to remove all spaces, extraneous . sections, brackets, square brackets... and just have short name and extension and in a folder that is low down on the drive without any spaces in the name either (by the time you end up in c:\documents and settings\some user name\documents\games\roms\nintendo entertainment system and famicom\nes\north america\" you have burned enough of the sometimes limited path lengths that lesser programs stumble on that a long name on top of that will cause it to break out in a sweat).

So dropping in health in Ninja Gaiden leads to a minigame, or maybe just the first of a challenge, where you want to get a fire flower in Mario?

Anyway my first thought is you have no emu.frameadvance() after Mario is loaded. Reading the stuff in the code section then in that it looks like the gameplay will only happen while the Ninja Gaiden memory check is running, after you fulfil its conditions, or indeed break from its conditions, it will stop emulating/running.
The thing I would try then being a simple emu.frameadvance()
Code:
client.openrom("Ninja Gaiden (USA).nes")
   while ((memory.readbyte(0x0065)) ~= 14) do
   emu.frameadvance()
end
client.openrom("Super Mario Bros. (World).nes")
savestate.load("Super Mario Bros.QuickNes.Mario.State")
emu.frameadvance()

I would also note in the Mario thing above I don't know what the memory looks like on the load of the ROM, or if client.openrom does a little memory reset. However you can cross that bridge afterwards when you try adding conditions to Mario.
 
  • Like
Reactions: Hodgermccodger

Hodgermccodger

New Member
Newbie
Joined
Jun 15, 2021
Messages
4
Trophies
0
Age
36
XP
39
Country
United Kingdom
Thank you again for your help.

I've tried your suggestion and also looked through the shuffler on github where they seem to use a similar script (albeit in a far more complex way- but the openrom, loadsavestate seems the same as what I am doing). Unfortunately I still seem to have the same problem of the script failing to work once I've opened the ROM.

I'm currently using this code- which registers what game you are playing, and if you're playing Ninja Gaiden loads a savestate when your health hits 14 or 13, and if you're playing Mario it loads Ninja Gaiden when you get a fire flower.

while true do
currentgame = gameinfo.getromname()
while currentgame == "Ninja Gaiden" do
if memory.readbyte(0x0065) == 14 then
savestate.load("Ninja Gaiden.QuickNes.Boss.State")
end
if memory.readbyte(0x0065) == 13 then
savestate.load("Ninja Gaiden.QuickNes.Boss.State")
end
emu.frameadvance();
end
while currentgame == "Super Mario Bros." do
if memory.readbyte(0x0756) == 2 then
client.openrom("Ninja Gaiden (USA).nes")
savestate.load("Ninja Gaiden.QuickNes.Boss.State")
print("the current game is " .. currentgame)
--savestate.load("Super Mario Bros.QuickNes.Mario.State")
end
emu.frameadvance();
end
emu.frameadvance();
end

This works well up until the point where it loads Ninja Gaiden once Mario gets a fire flower. It loads the ROM but it doesn't load the savestate. However if you refresh the script it continues to work- i.e it recognises the current game is Ninja Gaiden and loads the savestate when your health hits 14 or 13.

When it loads the ROM the error message appears
"NLua.Exceptions.LuaException: unprotected error in call to Lua API (0)"

This makes me think it is a problem with the path. I've put Bizhawk (or EmuHawk as the application seems to be called), lua scripts, save states and ROMs all in the same folder to minimize potential problems. I should note that I get similar error messages when I try to open the ROMs manually.

For example, if I just open the ROM (a .nes file) with EmuHawk I get the following error message
"System.UnauthorizedAccessException: Access to the path './ExternalTools' is denied."

When I open EmuHawk and try to open the ROM from there- I get the following error message
"Unhandled exception has occurred in a component in your application. If you click Continue, the application will ignore this error and attempt to continue.

The path is not of a legal form"

If I click continue here it appears to work fine. So I think this may be what is causing my script trouble. However I have no idea how to address it.

Any thoughts or recommendations would be very much appreciated.
 

Hodgermccodger

New Member
Newbie
Joined
Jun 15, 2021
Messages
4
Trophies
0
Age
36
XP
39
Country
United Kingdom
Thank you for this, it is the most recent version 2.6.2, so I'm hoping this is not an issue. It seems the external tools error message can be resolved by removing spaces from the file name. However this does not impact the other error messages, or the ability for the script to open the ROM and continue running. Looking at the comments it seems that one potential solution to the issue with the path is putting \\ rather than \ (e.g C:\\Users...\\NinjaGaiden). This sort of works- however it does produce a different error message.

System.InvalidOperationException: Can't have lua running in two host threads at a time.

Once you click okay on this error message- the emulator does load the ROM and go to the savestate, and the script continues running. So it almost works but for that error message.
 
Last edited by Hodgermccodger,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    Veho @ Veho: Firefox users be like "look at what they have to do to mimic a fraction of our power."