1 2
6 7
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
Fortranm wrote:
http://tasvideos.org/userfiles/info/30702689816176331 I tried to reproduce the current any% run on lsnes since the SGB mode in VBA is probably not accurate enough for a glichy run like this(http://tasvideos.org/forum/viewtopic.php?t=13623). However, I couldn't get into the wall at the first place after copying the input. What am I doing wrong? SGB2 is used as the bios since SGB1 has inaccurate timing.
Not sure why it desyncs, but the clipping glitch is annoyingly precise, so using TAStudio would really help in this case.
Fortranm
He/Him
Editor, Experienced player (880)
Joined: 10/19/2013
Posts: 1122
fsvgm777 wrote:
Why didn't you use the combined bsnes/Gambatte core (which lets Gambatte handle the GB part)? bsnes's standard GB(C) emulator is garbage (and I do mean it).
Thanks for telling me this! I thought SGB was supposed to be emulated completely under SNES mode. Duh. I tried the very first clipping for both SGB mode and monochrome mode, and they work the same. The GBC version doesn't behave differently under SGB mode ether. Therefore the conclusion is that SGB mode does not affect the layout of a glitch hell in a rom.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
I was recently asked about this game in terms of "All Stages"/"100%" routing. Some discoveries as follows: 1. Secret doors in final boss stages count as boss defeated. Normal exits count as next level. 2. Stage ID addresses are 0x1510 (GBC) or 0x0510 (GB). 3. With respect to the above, this means if you can find a secret exit in the robot spear man stage (ID 29), you can trigger the credits. Beating it via normal exit OoB advances to ID 30 (Chapter 2 basement) So with that said, from the submission text:
From what I remember, using a goal door in this level warps you to Chapter 1, Story 2's level or something. TODO: Is this correct?
I can now say, sorry, me and mugg remembered it wrong. 4. If you tried the trick at the factory final stage (ID 49), you can advance to the Secret stage. If you managed to beat the secret stage, this occurs: Then, you're stuck forever. However, if you tried it AFTER beating the game once on the stage select, this happens instead: 5. Factory Final GBC OoB is mostly unbreakable blocks below the stage, and unreachable glitch blocks at the top. Even attempting to use the hammer NPC to get the bouncy status fails, since there's a ceiling of unbreakable blocks. In GB, the glitched blocks are much lower, thus a secret exit (and trigger credits) can easily be reached 6. The same situation almost occurred with ID 29, but luckily there's an NPC that allowed you to reach an opening. The closest normal exit I found in GBC was: https://cdn.discordapp.com/attachments/688394402472788033/689339903649120267/Wario_Land_II_USA_Europe_exit.bk2 And for 100%: https://cdn.discordapp.com/attachments/688394402472788033/689375423548882984/Wario_Land_II_USA_Europe_treasure__boss.bk2 This input file finds an OoB bonus door, then immediately goes to the boss room in the first room. While that is normally slower than using exit door, since getting the level select as fast as possible was apparently quicker to skip cutscenes, this may help. 7. I managed to find a workable OoB exit in the latest BizHawk version in GB for Chapter 2 To The Castle (ID 25): In GBC, this completely breaks apart, since the red blocks vanish shortly after touching them from the sides or above. 8. For both "Defeat the giant spear man" and "Go through the grand hall" (IDs 27, 28), I found OoB bonus rooms right near a exit in GBC. Need to test GB as well. 9. The original script only works for GBC. After messing around, it appears the layout is similar, but with different addresses and IDs. So I changed it (among other things) to include GB support: Download WL2Overlay.lua
Language: lua

local x, y, camx, camy, tref, ttype, ttypehigh, tcolor, warpdest local address = { gbc = { x = 0x153c, y = 0x153a, camx = 0x660, camy = 0x65f, tref = 0x704, warpdest = 0xa1, stageid = 0x1510 }, gb = { x = 0x053c, y = 0x053a, camx = 0x1652, camy = 0x1651, tref = 0x16EB, stageid = 0x0510 } } local tiles = { gbc = { [0x47bc] = "Switch platform", [0x49cb] = "Slideable slope (left)", --Press down to roll [0x495e] = "Slideable slope (right)", --Press down to roll [0x4a10] = "Platform", [0x4a33] = "Platform (NPC)", --NPCs can walk on them, you cannot [0x4a70] = "Breakable", --(ttype>0x4a70) and (ttype<0x4d9f) or (ttype==0x4a10) [0x4d9f] = "Breakable", [0x4da0] = "Breakable (NPC)", --throw npcs to destroy them [0x4e8a] = "Water", [0x4fbe] = "Minigame", --varies by stage [0x4ecd] = "Minigame", --varies by stage [0x4edb] = "Door", [0x4ef6] = "Boss door", --varies by stage [0x4f3a] = "Exit", [0x4f60] = "Secret exit", [0x4ffb] = "Switch" }, gb = { [0x47bc] = "Switch platform", [0x49cb] = "Slideable slope (left)", [0x495e] = "Slideable slope (right)", [0x4a10] = "Platform", [0x4a33] = "Platform (NPC)", --NPCs can walk on them, you cannot [0x4a70] = "Breakable", --(ttype>0x4a70) and (ttype<0x4d9f) or (ttype==0x4a10) [0x4d9f] = "Breakable", [0x4d95] = "Breakable (NPC)", --throw npcs to destroy them [0x4d99] = "Breakable (NPC)", --throw npcs to destroy them [0x4e83] = "Water", [0x4fb7] = "Minigame", [0x4ed4] = "Door", [0x4eef] = "Boss door", [0x4f33] = "Exit", [0x4f59] = "Secret exit", [0x4cef] = "Breakable (Invisible)", [0x4ff4] = "Switch", [0x50fb] = "Water" } } function bitswap (swappy) nib2 = bit.band(swappy,0xF) return (nib2*0x10+bit.rshift(swappy,4)) end function gettile (wx,wy) hix = bit.rshift(bit.band(0xFF00,wx),8) hiy = bit.rshift(bit.band(0xFF00,wy),8) lox = bit.band(0xFF,wx) loy = bit.band(0xFF,wy) ccea = bit.band(bitswap(bit.band(hiy,0x0F))+bit.band(bitswap(loy),0x0F)+0xA0,0xFF) cceb = bitswap(bit.band(hix,0x0F))+bit.band(bitswap(lox),0x0F) rawloc = ccea*0x100+cceb -- not the final location!!! can vary if above 0xa000 return (rawloc) -- return (bit.band(0x2000 + 0x100*math.floor(wy/16+1) + math.floor(wx/16),0x7FFF)) works for most space, not glitch rooms end function tileid (ntile) if (ntile >= 0xa000) then memory.usememorydomain('CartRAM') -- where normal level data is realloc = ntile - 0x8000 else memory.usememorydomain('System Bus') realloc = ntile end tlookup = memory.readbyte(realloc) memory.usememorydomain('ROM') local address = 0x7c002+tref+bit.band(tlookup*2,0xFF) local result = memory.read_u16_le(address) -- -- if result == 0x4f60 then -- if result == 0x4f3a then -- -- memory.write_u16_le(address,0x4f3a) -- memory.write_u16_le(address,0x4f60) -- end return result end local game_address = address.gbc local game_tiles = tiles.gbc while true do x = mainmemory.read_u16_be(game_address.x) -- position in level y = mainmemory.read_u16_be(game_address.y) camx = mainmemory.readbyte(game_address.camx) -- position relative to upper left camera edge camy = mainmemory.readbyte(game_address.camy) tref = mainmemory.read_u16_be(game_address.tref) -- warpdest = mainmemory.readbyte(game_address.warpdest) -- sector coordinates for a warp (??) -- 160x144 -- gui.drawText(3,130,string.format("%X",bit.rshift(warpdest,4))..' '..string.format("%X",bit.band(warpdest,0xF))) for i = -1,17,1 do for j = -1,17,1 do ttype = tileid(gettile(x-camx+15+16*i,y-camy+15+16*j)) ttypehigh = bit.band(ttype,0xFF00) if (ttype~=0x47ab) and (ttype~=0x49a7) and not ((ttype>=0x4e29) and (ttype<=0x4e39)) and not ((ttype>=0x5400) and (ttype<=0x54ff)) then --if (ttype~=0x47ab) and (ttype~=0x4cf3) and (ttype~=0x4cef) and (ttype~=0x4d03) and (ttype~=0x4cff) and (ttype~=0x4e29) and (ttype~=0x4e35) and (ttype~=0x4f3a) then -- if (ttype==0x4ecd) or (ttype==0x4edb) or (ttype==0x4f3a) or (ttype==0x4f60) then -- door, minigame, exit -- tcolor = 'GREEN' if game_tiles[ttype] ~= nil then if (game_tiles[ttype]=="Door") then --Regular door tcolor = 'BLACK' elseif (game_tiles[ttype]=="Boss door") then --Boss door tcolor = 'GREEN' elseif (game_tiles[ttype]=="Minigame") then -- Minigame tcolor = 'PURPLE' elseif (game_tiles[ttype]=="Exit") then -- exit tcolor = 'CYAN' elseif (game_tiles[ttype]=="Secret exit") then --secret exit tcolor = 'GOLD' elseif (game_tiles[ttype]=="Water") then -- water tcolor = 'BLUE' elseif (game_tiles[ttype]=="Platform") then -- platform tcolor = 'WHITE' elseif (game_tiles[ttype]=="Platform (NPC)") then -- platform tcolor = 'GREY' elseif (game_tiles[ttype]=="Slideable slope (left)") or (game_tiles[ttype]=="Slideable slope (right)") then -- platform tcolor = 'BROWN' elseif (ttype>0x4a70) and (ttype<0x4d9f) or (ttype==0x4a10) then -- breakable tcolor = 'PINK' elseif (game_tiles[ttype]=="Breakable (NPC)") then tcolor = "DEEPPINK" else -- solid or unknown tcolor = 'RED' end end gui.drawBox((camx-x)%16-8+16*i,(camy-y)%16-16+16*j,(camx-x)%16+7+16*i,(camy-y)%16+16*j-1,tcolor) end end end gui.drawText(3,3,string.format("%X",gettile(x,y-32))..' '..string.format("%X",tileid(gettile(x,y-32)))) gui.drawText(3,12,string.format("%X",gettile(x,y-16))..' '..string.format("%X",tileid(gettile(x,y-16)))) gui.drawText(3,21,string.format("%X",gettile(x,y))..' '..string.format("%X",tileid(gettile(x,y))),"BLACK","BLACK") gui.drawText(4,22,string.format("%X",gettile(x,y))..' '..string.format("%X",tileid(gettile(x,y)))) gui.drawText(3,31,"X:"..x.." Y:"..y,"BLACK","BLACK") gui.drawText(4,32,"X:"..x.." Y:"..y,"WHITE") emu.frameadvance() end
Original script: http://tasvideos.org/userfiles/info/16322306121342073 Thanks very much for Slamo for the initial version! 10. For floating OoB doors, you need swimming status to enter them 11. Last night, when exploring Factory Final (ID 49) on GB, the glitched area was solidish, and easily reachable: Attempting to record it from start of stage however, changed it into this: Which made the glitched area much harder to reach. I have no idea what changed the layout, since the 1st attempt was from a savestate, not start of stage.
TheKDX7
He/Him
Player (118)
Joined: 7/9/2011
Posts: 393
Location: Switzerland
Lots of very interesting things and funny to see how broken this game can be. Are you working on a 100% run or do you planning to do this on the futur ? And would it be better to run it on GBC or GB for faster execution ?
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
TheKDX7 wrote:
Lots of very interesting things and funny to see how broken this game can be. Are you working on a 100% run or do you planning to do this on the futur ? And would it be better to run it on GBC or GB for faster execution ?
GBC has a different OoB layout; for instance the glitched area in 2-2 in the current TAS still works in GB in BizHawk, but doesn't in GBC. However, certain tricks that do work in GBC (such as the one mentioned in the post above with bonus room leading to final boss room, and similar tricks in the 2 stages before it) can't seem to be done in GB (yet), or is slower. So might need to check every slow stage to see what is faster. Not helped by the fact it seems the OoB layout sometimes changes in the same room, in the same game version. I just edited the post from before, but it seems in GB Factory Final, the OoB can be either extremely easy to reach, or mostly out of reach like in GBC, and I have no idea what caused the change. Unrelated, but messing around while not recording changed all the bees into skull in 2-2: I can't seem to replicate it however.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
I resynced the current run up to the last boss: https://cdn.discordapp.com/attachments/280808167993245707/690749647098216458/Wario_Land_II_Resync.bk2 Here's a 819 frame improvement to the above due to a OoB exit in "Storm the castle: https://cdn.discordapp.com/attachments/280808167993245707/690857660412592158/Wario_Land_II_Resync.bk2 This is the exit used: I have no idea if it's console-able however. The TAS is also probably improveable, due to kinda forgetting some of the movement tricks. If anyone wants to help, please do. Edit: Now it improves even more; OoB used in 2-2, 2-3, 2-5. Stage 8; GBC OoB exit: https://cdn.discordapp.com/attachments/280808167993245707/691314394251919430/Wario_Land_II_8.bk2 Here's the same door, but causes a reset: https://cdn.discordapp.com/attachments/280808167993245707/691316866295267338/Wario_Land_II_8_reset.bk2 Not sure what made it work. Couldn't get it to work in GB so far. The upper OoB region is inaccessible; there's an entire row of warp tiles that causes the game to reset.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
While routing for 100%, I asked TiKevin83 to check for if the OoB in this game was emulated right by given them input files for any% improvement. An input file that did not soft reset the game synced back, but the one that did use a soft reset to alter OoB layout did not. Since the Spearman stage for 100% needs reset to reach the OoB exit after obtaining the treasure, I'm uncertain if the route works on console. I gave him the input file, but since he's busy, I decided to work on the OoB-less run instead. With that said, I rediscovered that from old posts, there's a 724 frame improvement to the previous TAS that does not use OoB exits. Given the links to the WIPs are dead, I decided to resync the current run (up to last boss, because diff rng ruined it) and see if the improvement could be refound: http://tasvideos.org/userfiles/info/63000414314992766 Edit: 879 frames improved, mostly from using inputs from the latest run, + wallclip savings mentioned by MUGG. https://youtu.be/-siFfVFZxUY http://tasvideos.org/userfiles/info/63043461467067961 Edit2: Improved 7 more frames by cancelling 1st dash jump instead of crouching in the giant spearman room with frostie Also the 4 frame rule between transitions also affect the minigame screens. Since the number for the end stage minigame is determined at start of minigame, this means it can only be changed once every 4 frames Edit 3: I investigated avoiding coins for the any%. Every time you have less than 50 coins you save 56 frames at the number guessing minigame since it just gives you a message "You have no coins" that can accept input sooner. Stage 1 (One Noisy Morning) 0 coins Stage 25 (To the Castle) 4 coins (3 from 1st screen, 1 from OoB) Stage 26 (Storm the Castle) 45 coins Stage 27 (Defeat the Giant Spearman) 1 coin (OoB) Stage 28 (Through the grand hall) 19 coins, assuming 0 from blocks Stage 29 (Kick em out!) varies, way more than 50 due to OoB https://docs.google.com/spreadsheets/d/1XevsUFWknCozzJ7IRKbHigPpSeJnk5FJFZm4lNmFnjM/edit?usp=sharing This means the main issue is stage 26. From the spreadsheet, I timed every screen in this stage. This was done on GBC instead of GB, since there was no lag in GBC, but the results apply as well. Compared to ignoring coins, it seems to be fastest is skip 4 coins at beginning, then avoid as much coins as possible reset of stage. This will give 25 coins. 0 + 4 + 25 + 1 + 19 = 49 coins by 2nd last stage. Last stage coins are unavoidable, so ignoring that. This takes an additional 49 frames for 2-2, but saves 56 frames per end level for the next 2 stages, so it's a gain.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
I wasn't going to post, but: https://cdn.discordapp.com/attachments/280806848909541376/750422414109704350/Wario_Land_II_100_GBC.bk2 I decided to compare it with the 100% TAS that currently exists, and discovered that the 1st room of 2-1 (Stage ID 25) was slower than the existing run by 1 frame. Due to the 4 frame rule, this doesn't affect the run since it ends up taking the same amount of time to fade out. However, when I tried to replace that particular room's input with the current published run's input to make it neater, I discovered the run the run desyncs next stage due to different RNG, despite everything else syncing: Left: The WIP I posted. Right: The same, except the 1st room of 2-1 has been changed. Given I even got a different coin drop amount, this suggest it's possible to manipulate silver coins along with the minigame results without losing too much time by doing different things slower (while still within the 4 frame window) on a previous stage. I do not know the RNG addresses for the game however, so the use of this might be limited to manual testing. Also I'm not changing anything before the robot spearman because I hate that boss. Also for future reference on how I approached the boss: See this antenna on top? It cycles through its animation every 12 frames or so. Note the fuel at start of cycle If the fuel counter goes down every frame, dont press anything unless you have to. If it doesn't, try pressing buttons on TAStudios. Note the fuel counter by the start of the next cycle In this case, it dropped by 3. Save. Then try other inputs & see if you can get the fuel counter lower at the same frame (eg. 252 instead of 253). Do this for every single cycle; it makes it more manageable to compare progress. Try to match the previous run's fuel in terms of it's antenna animation These are the "milestone" points I aim for +/- 2. The bottom right panel is in the middle of a cycle, but if the robot stops 1 pixel earlier I wont be able to hit it 3 times. The fuel for the top left in the previous run was 215 instead, but as long as you're +/- 2 from that you can make it Afterwards you need to manipulate it such that the last 4-10 units of fuel ALL deplete with no delay, or else you won't be able to charge early. Failing to do so allows the robot to replenish their HP When the robot is at the right hand side of the screen, you can move around a bit w/o affecting fuel drop rate. Be sure to position to somewhere close to the bottom left panel (1872 X or so) during this brief downtime
SoapAgent
He/Him
Joined: 4/2/2017
Posts: 13
Location: TASVideos.org, of course...
I suppose this could warrant a post. Basically, I discovered(?) that you can suspend dash "storage"/wall clip state by sliding (I didn't exactly discover it, since it's a known trick in WL3, but I also sincerely doubt that I was the first to find it in this game either). This means that it is possible to clip out of bounds in the far right of the second room of the Really Final Chapter. Due to how the level is laid out in ROM, this allows us to get inside the tile data for the 5th room and enter a door to the 6th, skipping a couple rooms along the way and eliminating the need to deal with the quite slow 4th room, saving 3 in-game seconds from mugg's TAS. User movie #637807299845550294 Link to video Another thing to note is that slide-buffering a clip makes finding wall clip positions significantly easier than fishing for a good lineup with the dashjump method (although maybe "easier" isn't what we want here since this method is not as direct or fast). It also allows clipping into spaces where there is not room to dashjump (an example that comes to mind is the tiny Wario room in 5-2 Storm the Castle, and I think possibly the top of the "bee bounce" room in 3-4). I also found a way to get a wall clip with only 1 tile of walking space, similar to Wario Land 3's turnaround wall clips used in N4 in the Any% route. I don't know if there is any reason to use a trick like this, but I still made a quick movie just in case: User movie #637807399158871261 And here is one last wall clip style that I don't think I've seen in this game yet — water clips (technically, they're water-buffered clips, since the actual clipping happens in the air still, but I'm just calling them what the WL3 community calls them): User movie #637807409719924707 I'd be curious to see how any of these could be used in a full level TAS, although the last two that I shared may be too specific of use cases. Who knows?
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
SMWAgent09AF wrote:
I'd be curious to see how any of these could be used in a full level TAS, although the last two that I shared may be too specific of use cases. Who knows?
Those are all amazing! In the GB 100% OoB run I'm making, almost every stage is basically 1st Room -> Bonus -> OoB exit A better way to easily clip out would definitely help. Additionally, some ideas for GBC in bounds: 1. "Kick em Out!": Instead of dropping down after the minigame, use the 1 tile clip method to get up? 2. "Storm the castle!": Is there any way to prevent backtracking after the minigame? 3. "Go down the cellar": No idea, but right now, the last room seems unbreakable. Not sure if there's a way to prevent getting crushed at the end.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
The following applies to gameboy. I found out that when you ground pound in certain areas below the stage, you can crash the game. I checked why, and it was because it jumped to 0xE200
4C62:  00        nop                 A:00 F:10 B:79 C:80 D:00 E:00 H:df L:8d LY:0f SP:dfe9  Cy:3776752168
4C63:  C4 00 E2  call nz, $E200      A:00 F:10 B:79 C:80 D:00 E:00 H:df L:8d LY:0f SP:dfe9  Cy:3776752170
E200:  00        nop                 A:00 F:10 B:79 C:80 D:00 E:00 H:df L:8d LY:0f SP:dfe7  Cy:3776752182
E201:  00        nop                 A:00 F:10 B:79 C:80 D:00 E:00 H:df L:8d LY:0f SP:dfe7  Cy:3776752184
0xC504, and thus 0xE504 is the treasure flags. So there's a small spot to manipulate for a credits warp. Specifically:
C503	Amount of times saved
C504	Treasure flags for stages 0-7
C505	Treasure flags for stages 8-15
C506	Treasure flags for stages 16-23
C507	Treasure flags for stages 24-31
C508	Treasure flags for stages 32-39
C509	Treasure flags for stages 40-47
C50A	Treasure flags for stages 48-49 (only bits 0,1 possible)
C50B	Coins bank (Displays decimal value as a hex value. eg. 40 coins in decimal becomes 0x40)
C50C	Coins bank
C50D	Coins bank
C50E	Coins overworld (Displays decimal value as a hex value. eg. 40 coins in decimal becomes 0x40)
C50F	Coins overworld
C510	Stage ID (Displays decimal value as a hex value. eg. Stage 10 in decimal becomes 0x10)
C511	
C512	Stage select flag (0 or 1)
C513	Puzzle flags for stages 0-7
C514	Puzzle flags for stages 8-15
C515	Puzzle flags for stages 16-23
C516	Puzzle flags for stages 24-31
C517	Puzzle flags for stages 32-39
C518	Puzzle flags for stages 40-47
C519	Puzzle flags for stages 48-49 (only bits 0,1 possible)
I couldn't figure out what to do to trigger the credits and avoid a crash however. I did find the following: 0xC004 - cutscene id 0 - none (skips cutscene) 1 - stage 0 (1-1) 2 - stage 5 (2-1) 3 - stage 10 (3-1) 4 - stage 15 (4-1) 5 - stage 20 (5-1) 6 - stage 25 (secret 2-1, you fell asleep) 7 - stage 30 (secret 2-1, you didnt fight snake) 8 - stage 35 (secret 3-1, you sunk the ship) 9 - stage 40 (mansion) 10 - stage 45 (factory) 11 - stage 50 (true final) 12 - crash 0xD6F3 - ending flag. If this is set to 1, and the ending id is a valid value, the ending is triggered at stage end. 0xD70A - ending id 0 - nothing 1 - normal 5-5 2 - secret 2-5 (you fell asleep) 3 - secret 3-5 (underwater) 4 - secret 5-5 (mansion) 5 - secret 5-5 (factory) 6 - true final 7 - reset game I know it's impractical time wise to set up, but it's a different route that uses ACE, so I'm still interested in it. Edit: 0xC000 to 0xFFFF area https://docs.google.com/spreadsheets/d/1x0ahgWoO948yNwCFYOYbA7bYH6OOarJgoSSVkF_hyB0/edit?usp=sharing most of it is undocumented however. Edit 2: ok
C503Number of times saved0x3Cinc aset a from 0 to 1; assumes a was 0
C504Treasure flags for stages 0-70xEAld [$D70A], aloads ending 1 to ending id
C505Treasure flags for stages 8-150x0A
C506Treasure flags for stages 16-230xD7
C507Treasure flags for stages 24-310x3Eld a, $0Bloads 0x0B to register A
C508Treasure flags for stages 32-390x0B
C509Treasure flags for stages 40-470x00nop
C50ATreasure flags for stages 48-49 (only bits 0,1 possible)0x00nop
C50BCoins bank0x00nop
C50CCoins bank0x18jr $05jump to C513 to avoid stage select opcode 0x01
C50DCoins bank0x05nop
C50ECoins overworld0x00nopSkipped
C50FCoins overworld0x00nopSkipped
C510Stage ID?VariesSkipped
C5110x00nopSkipped
C512Stage select flag0x01ld bc, _ _ _ _Skipped
C513Puzzle flags for stages 0-70xEAld [$D6D8], aChanges game state to 11
C514Puzzle flags for stages 8-150xD8
C515Puzzle flags for stages 16-230xD6
C516Puzzle flags for stages 24-31??Remaining bytes for avoiding crash
C517Puzzle flags for stages 32-39??
C518Puzzle flags for stages 40-47??
C519Puzzle flags for stages 48-49 (only bits 0,1 possible)??
I think C3 00 15 allowed me to survive a jump to WRAM. Additionally, if I can jump to C000, the area before, specifically C200, is related to the minigame. Maybe some way to get a payload there?
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
I am still working on a run for this. The current payload idea is
C503Number of times saved-VariesI'm not going to save/reset repeatedly; ignoring this.
C504Treasure flags for stages 0-70x3Eld a, $06loads 6 (real final chapter ending) in to register A
C505Treasure flags for stages 8-150x06
C506Treasure flags for stages 16-230xEAld [$D70A], aloads ending 6 to ending id
C507Treasure flags for stages 24-310x0A
C508Treasure flags for stages 32-390xD7
C509Treasure flags for stages 40-470x00nop
C50ATreasure flags for stages 48-49 (only bits 0,1 possible)0x00nop
C50BCoins bank0x00nop
C50CCoins bank0x18jr $05jump to C513 to avoid stage select opcode 0x01
C50DCoins bank0x05
C50ECoins overworld0x00nopSkipped
C50FCoins overworld0x00nopSkipped
C510Stage ID?VariesSkipped
C5110x00nopSkipped
C512Stage select flag0x01ld bc, _ _ _ _Skipped
C513Puzzle flags for stages 0-70xC2jp $4000jumps to somewhere not crash
C514Puzzle flags for stages 8-150x00
C515Puzzle flags for stages 16-230x04
C516Puzzle flags for stages 24-310x00nop
C517Puzzle flags for stages 32-390x00nop
C518Puzzle flags for stages 40-470x00nop
C519Puzzle flags for stages 48-49 (only bits 0,1 possible)0x00nop
This means the following stages are needed :
0One Noisy MorningTurn off the alarm clock!
1One Noisy MorningTurn off the giant faucet!
2One Noisy MorningLet the water out!
3One Noisy MorningGo down to the cellar
4One Noisy MorningDefeat the giant snake
5SS Tea CupReturn the hen to her nest
6SS Tea CupEscape from the woods!
7SS Tea CupGet in the Tea Cup
8SS Tea CupDrop the anchor!!
9SS Tea CupDefeat Bobo!!
10Maze WoodsGet to Maze Woods
15In TownStop that train!
16In TownUp on the rooftop!!
17In TownDown in the cellar
18In TownEscape from the factory!
19In TownAnyone for B-ball?
20Syrup CastleGet to the Castle!!
21Syrup CastleStorm the castle!!
22Syrup CastleDefeat four ducks!
23Syrup CastleFind the hidden door!!
25Invade Wario CastleTo the castle!!
26Invade Wario CastleStorm the castle!!
27Invade Wario CastleDefeat the giant spear man
28Invade Wario CastleGo through the grand hall
30Go to the cellar!Defeat the giant spear man
31Go to the cellar!Avoid the rocks!
32Go to the cellar!Stop that train!
33Go to the cellar!Find the exit!!
34Go to the cellar!Defeat the cave master!!
35Ruins at the Bottom of the seaEscape from the Tea Cup!
36Ruins at the Bottom of the seaDefeat the giant spear man
37Ruins at the Bottom of the seaInside the ruins!
38Ruins at the Bottom of the seaEscape fromt the ruins!
39Ruins at the Bottom of the seaCaptured Syrup!
I end the game at stage 28 below the stage, where a certain block when I swim at jumps to C000, then nops to C503. This sets the ending ID to 6, but more importantly, doesn't crash the game. I then need to beat the stage somehow, then the final ending plays. It would be around 7 times longer than beating the game normally (at least 33 stages, some repeats to unlock alt paths), but would be game end glitch. Notes: * Technically, I could've shorten the number of stages used by makign C503 be 0x3E, then shifting the payload up by 1 byte, allowing me to skip stages 32-39. Then get C509 to be 0xC3, have C50A be 0, and C50B to be 04, for the final jump, but that would need 62 (0x3E) saves, and 4000 coins. I save at least 33 times, so that leaves the run save/reset another 30 times. I would also need to manipulate multiple silver coins every single stage (they give 100, but I use 50 for treasure in some stages). * Right now, the path reaches the stage select screen using stage 39 Captured Syrup!. I could technically reach that using underground by beating stage 34 OoB, which if I haven't unlocked stage select yet, will take me to stage 35 Escape from the Tea Cup!. However, after unlocking stage select, I would be blocked from going back, since I never beaten stage 8 (Drop the anchor!!) * An alternate path, using the exact same payload above, would be to do what the any% does and unlock stage select using stage 29 (Kick 'em out!), then doing that again to unlock underground. Then beat stage 34 (Defeat the cave master!!) OoB to unlock underwater. This skips beating stage 4 (Defeat the giant snake) and stage 8 (Drop the anchor!!) twice, but at the cost of doing 2 extra stages (28, 29) + I'm forced to see stage select screen. But it also allows me to skip cutscenes. * The OoB area is harder to reach if you haven't unlocked stage select. A number of stages have the section 1 tile above reach, so I have to either reset, or find some innovative way to get up. This also meant the 100% GB input file wasn't as helpful as I thought.
Sand
He/Him
Player (143)
Joined: 6/26/2018
Posts: 175
Exploit code constructed out of treasure and puzzle flags, that's amazing :) It's not every day you have to think about the Hamming weight of your shellcode. I'm enjoying reading about this. If the contents of the a register are predictable, it may be possible to replace ld a, $06 (3e06) with add a, $XX (c6XX), where XX is the difference between 0x06 and the value of a. 0xc6 saves one bit relative to 0x3e, but whether it requires fewer treasures overall depends on what XX is.
jlun2 wrote:
The following applies to gameboy. I found out that when you ground pound in certain areas below the stage, you can crash the game. I checked why, and it was because it jumped to 0xE200. 0xC504, and thus 0xE504 is the treasure flags. So there's a small spot to manipulate for a credits warp.
I see why the contents of 0xe504 are the same as 0xc504—that's echo RAM. But how does the program counter get from 0xe200 to 0xe504? Is it all NOPs in between?
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
Sand wrote:
Exploit code constructed out of treasure and puzzle flags, that's amazing :) It's not every day you have to think about the Hamming weight of your shellcode. I'm enjoying reading about this. If the contents of the a register are predictable, it may be possible to replace ld a, $06 (3e06) with add a, $XX (c6XX), where XX is the difference between 0x06 and the value of a. 0xc6 saves one bit relative to 0x3e, but whether it requires fewer treasures overall depends on what XX is.
jlun2 wrote:
The following applies to gameboy. I found out that when you ground pound in certain areas below the stage, you can crash the game. I checked why, and it was because it jumped to 0xE200. 0xC504, and thus 0xE504 is the treasure flags. So there's a small spot to manipulate for a credits warp.
I see why the contents of 0xe504 are the same as 0xc504—that's echo RAM. But how does the program counter get from 0xe200 to 0xe504? Is it all NOPs in between?
Thanks for the comment! https://tasvideos.org/UserFiles/Info/638320581598112959 I think this was the input file that had it. I meant C200, sorry. There's a section in memory around there that seems to be based on the tiles in the treasure/number guessing minigame. I would love to somehow get 3E 06 to appear there, but I don't think I succeeded. I do not know if register A would be predictable, so I had 3E 06 just to at least get a run working first.
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
https://tasvideos.org/UserFiles/Info/638616165666042151 Link to video I wasn't able to replicate the trigger from my previous input file, so I found a new spot right below an exit tile that jumps to 0xC000 once you ground pound on it. The game then jumps back to the main loop, and I beat the stage with ending id set to 6. This plays the real final chapter ending credits after saving. This took 35 minutes, so it is completely unpractical for any%. It is also slower than just reaching stage 49 (Awaiting Syrup!) and beating it using an OoB exit to reach the real final stage, since that takes 22 stages, rather than 35+ here. It does execute arbitrary code via stage flags, so I hope someone finds a better way than beating half the game + actually beat the game at least once to jump to credits.
Sand
He/Him
Player (143)
Joined: 6/26/2018
Posts: 175
jlun2 wrote:
I wasn't able to replicate the trigger from my previous input file, so I found a new spot right below an exit tile that jumps to 0xC000 once you ground pound on it. The game then jumps back to the main loop, and I beat the stage with ending id set to 6. This plays the real final chapter ending credits after saving.
This is awesome! I'm impressed. The disassembly visualization in the video is great, too. An alternative to writing the instructions you need would be to find a place where those instructions already exist in the ROM, and jump to them. (Something like finding a "gadget" in return-oriented programming.) There is a sequence in the ROM at address 0x00264b0e that is tailor-made, ld a, 0x06; ld [0xd70a], a, followed by a ret:
264afa:  3e01    ld    a, 0x01
264afc:  1816    jr    0x16
264afe:  3e02    ld    a, 0x02
264b00:  1812    jr    0x12
264b02:  3e03    ld    a, 0x03
264b04:  180e    jr    0x0e
264b06:  3e04    ld    a, 0x04
264b08:  180a    jr    0x0a
264b0a:  3e05    ld    a, 0x05
264b0c:  1806    jr    0x06
264b0e:  3e06    ld    a, 0x06
264b10:  ea0ad7  ld    [0xd70a], a
264b13:  c9      ret
264b14:  ea0ad7  ld    [0xd70a], a
264b17:  cd9e06  call  0x069e
264b1a:  c9      ret
Instead of encoding your own ld a, 0x06; ld [0xd70a], a (5 bytes), you could do a call 0x4b0e (3 bytes). Or even jp 0x4b0e: maybe you'll get lucky and there will be a non-crashing return address on the stack for the ret to return to. However, I tried dumping memory at the time when the game starts executing 0xc000, and it looks like the above code is not mapped at that point. (At least I think—I don't know Game Boy architecture that well.) So this idea may be a dead end.
Post subject: Looking into root cause
Sand
He/Him
Player (143)
Joined: 6/26/2018
Posts: 175
I looked at an instruction trace of User movie #638616165666042151. I traced things back as far as where the game starts executing some data as instructions, until it hits an 0xff opcode (which is rst $38) at 0x4cb3. The code starting at 0x0038 is just NOPs, so execution falls through to the vblank interrupt handler at 0x0040. The vblank interrupt handler returns with a reti instruction, returning to the address 0x4cb4, which another 0xff rst $38 instruction, so the vblank interrupt handler gets called again, returns again, and continues at 0x4cb5. What we get is a long sequence of instructions, most of which are 0xff, which means that the code repeatedly invokes the vblank interrupt handler, punctuated by a few other instructions, until finally a long buffer of 0xff bytes ends at 0xc000. Stripping out the code of the jumping into and returning from the interrupt handler, what we have is this:
4CB3:  FF        rst $38             A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421866
4CB4:  FF        rst $38             A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:29 SP:dfe9  Cy:4465422036
4CB5:  FE FD     cp a, $FD           A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:2a SP:dfe9  Cy:4465422206
4CB7:  FF        rst $38             A:00 F:70 B:79 C:80 D:20 E:71 H:98 L:f0 LY:2a SP:dfe9  Cy:4465422210
4CB8:  5F        ld e, a             A:00 F:70 B:79 C:80 D:20 E:71 H:98 L:f0 LY:2b SP:dfe9  Cy:4465422380
4CB9:  CF        rst $08             A:00 F:70 B:79 C:80 D:20 E:00 H:98 L:f0 LY:2b SP:dfe9  Cy:4465422382
AFD8:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:2c SP:dfe9  Cy:4465422744
AFD9:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:2d SP:dfe9  Cy:4465422914
AFDA:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:2e SP:dfe9  Cy:4465423084
AFDB:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:2e SP:dfe9  Cy:4465423254
... long buffer of FF
BFFB:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3a SP:dfe9  Cy:4466128134
BFFC:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3b SP:dfe9  Cy:4466128304
BFFD:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3c SP:dfe9  Cy:4466128474
BFFE:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3c SP:dfe9  Cy:4466128644
BFFF:  FF        rst $38             A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3d SP:dfe9  Cy:4466128814
C000:  00        nop                 A:d7 F:20 B:f0 C:80 D:00 E:e0 H:af L:d7 LY:3e SP:dfe9  Cy:4466128984
The trace that leads to the first execution of 0xff rst $38 is strange. It looks like two separate pieces of code that have been mashed together. Here is the trace that leads to the execution of 0x4cb3:
4CA1:  2A        ld a, [hl+]         A:01 F:20 B:78 C:80 D:20 E:71 H:d6 L:3d LY:28 SP:dfe9  Cy:4465421818
4CA2:  6E        ld l, [hl]          A:20 F:20 B:78 C:80 D:20 E:71 H:d6 L:3e LY:28 SP:dfe9  Cy:4465421822
4CA3:  67        ld h, a             A:20 F:20 B:78 C:80 D:20 E:71 H:d6 L:71 LY:28 SP:dfe9  Cy:4465421826
4CA4:  7E        ld a, [hl]          A:20 F:20 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421828
4CA5:  E6 80     and a, $80          A:10 F:20 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421832
4CA7:  B0        or a, b             A:00 F:a0 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421836
4CA8:  77        ld [hl], a          A:78 F:00 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421838
4CA9:  07        rlca                A:78 F:00 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421842
4CAA:  0D        dec c               A:f0 F:00 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421844
4CAB:  09        add hl, bc          A:f0 F:60 B:78 C:7f D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421846
4CAC:  03        inc bc              A:f0 F:00 B:78 C:7f D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421850
4CAD:  0F        rrca                A:f0 F:00 B:78 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421854
4CAE:  A1        and a, c            A:78 F:00 B:78 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421856
4CAF:  04        inc b               A:00 F:a0 B:78 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421858
4CB0:  3F        ccf                 A:00 F:00 B:79 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421860
4CB1:  7F        ld a, a             A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421862
4CB2:  7F        ld a, a             A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421864
4CB3:  FF        rst $38             A:00 F:10 B:79 C:80 D:20 E:71 H:98 L:f0 LY:28 SP:dfe9  Cy:4465421866
The trace stops making sense as code at 0x4ca9 rlca. What it looks like, to me, is that the instructions before that point come from one part of the ROM, and the instructions after that come from a different part of the ROM. Here's the code that starts at 0x1f4ca1 in the ROM. Notice how it agrees with the above trace up through 0x1f4ca8 ld [hl], a, and disagrees thereafter:
1f4ca1  2a      ldi   a, [hl]
1f4ca2  6e      ld    l, [hl]
1f4ca3  67      ld    h, a
1f4ca4  7e      ld    a, [hl]
1f4ca5  e680    and   0x80
1f4ca7  b0      or    b
1f4ca8  77      ld    [hl], a
1f4ca9  e5      push  hl
1f4caa  cd181e  call  0x1e18
1f4cad  e1      pop   hl
1f4cae  cdd34d  call  0x4dd3
1f4cb1  fa79d6  ld    a, [0xd679]
And here is the code (actually looks more like data) that starts at 0x374ca1 in the ROM. Notice how it agrees with the above trace after 0x374ca9 rlca, but disagrees before that:
374ca1  0d      dec   c
374ca2  09      add   hl, bc
374ca3  0a      ld    a, [bc]
374ca4  0604    ld    b, 0x04
374ca6  04      inc   b
374ca7  84      add   h
374ca8  02      ld    [bc], a
374ca9  07      rlca
374caa  0d      dec   c
374cab  09      add   hl, bc
374cac  03      inc   bc
374cad  0f      rrca
374cae  a1      and   c
374caf  04      inc   b
374cb0  3f      ccf
374cb1  7f      ld    a, a
374cb2  7f      ld    a, a
374cb3  ff      rst   0x38
Again, I don't know much about the Game Boy architecture, but what it looks like to me is, the game had 0x1f4cxx mapped in, and while that code was executing, 0x374cxx got mapped in on top of it. Is that possible?
Skilled player (1744)
Joined: 9/17/2009
Posts: 4986
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
Thanks for checking this out!
However, I tried dumping memory at the time when the game starts executing 0xc000, and it looks like the above code is not mapped at that point. (At least I think—I don't know Game Boy architecture that well.) So this idea may be a dead end.
The trace stops making sense as code at 0x4ca9 rlca. What it looks like, to me, is that the instructions before that point come from one part of the ROM, and the instructions after that come from a different part of the ROM. Here's the code that starts at 0x1f4ca1 in the ROM. Notice how it agrees with the above trace up through 0x1f4ca8 ld [hl], a, and disagrees thereafter:
It's due to
4CA8:  77        ld [hl], a          A:78 F:00 B:78 C:80 D:20 E:71 H:20 L:71 LY:28 SP:dfe9  Cy:4465421838
https://retrocomputing.stackexchange.com/questions/11732/how-does-the-gameboys-memory-bank-switching-work
Switching to a ROM Bank < $20 Switching to banks $01-$1F is very simple. We only need to write our intended bank to $2000-$3FFF. Here we are switching to bank $05: ld $2000, $05 ; Now able to read data from bank $05
In this case, HL was $2071, so the memory right afterwards points to a different location after execution. That actually sounds like a good idea, if you could somehow get the rom bank where ld a, 0x06; ld [0xd70a], a is. The earlier parts of the memory before C500 changes from the minigames (the one where you guess the npc in the card, and the one you guess the number). I don't think I managed to get much out of it though. I recall at least 2 bytes, separate from each other, act as a timer that can have any value from 00 to FF, but I'm not sure if it would be useful. Another idea I had was get register A to shift right 6 times somehow using the minigame values, but I don't know if that is possible.
1 2
6 7