Push-Over, for SNES
This script has the following features:
- Level select.
- Show the passcode for the current level.
- Show the passcode using a sprite sheet image to draw the digits (instead of built-in text rendering). Requires latest SVN version; but the script also has a fallback code for text rendering.
- Automatically takes screenshots of all levels. Since some levels take a bit longer to load, the script detects when the main character is at a specific animation frame, so he looks the same in all screenshots.
- Bruteforce passcode testing. It will likely take 10 or 20 hours to bruteforce all passcodes (nope, I have not left it running for so long). Fun fact: for each passcode, the game also accepts the same passcode added by 2.
- Since it is impossible to call emu.frameadvance() in a button handler function, this script implements a work around.
Language: lua
NUMBERS_PNG = "SNES_Push-Over_numbers.png"
MAINMENU_SAVESTATE = "SNES_Push-Over_MainMenu.State"
PASSCODE_BASE = 0x001100
TIME_TICK = 0x000303
TIME_SECOND = 0x000304
PASSCODES = {
00512,
01536,
01024,
03072,
03584,
02560,
02048,
06144,
06656,
07680,
07168,
05122,
05634,
04610,
04098,
12290,
12802,
13826,
13314,
15362,
15878,
14854,
14342,
10246,
10758,
11782,
11270,
09222,
09734,
08718,
08206,
24590,
25102,
26126,
25614,
27662,
28174,
27150,
26638,
30734,
31246,
32270,
31758,
29726,
30238,
29214,
28702,
20510,
21022,
22046,
21534,
23582,
24094,
23070,
22558,
18494,
19006,
20030,
19518,
17470,
17982,
16958,
16510,
16511,
17023,
18047,
17535,
19583,
20095,
19071,
18559,
22655,
23167,
24191,
23679,
21631,
22143,
21247,
20735,
28927,
29439,
30463,
29951,
31999,
32511,
31487,
30975,
26879,
27647,
28671,
28159,
26111,
26623,
25599,
25087,
08703,
09215,
10239,
09727,
44543
}
function input_passcode(passcode)
for index = 8, 0, -2 do
local digit = passcode % 10
passcode = (passcode-digit) / 10
mainmemory.write_u16_le(PASSCODE_BASE + index, digit)
end
end
function get_passcode_digit(index)
return mainmemory.read_u16_le(PASSCODE_BASE + 2 * index) % 10
end
function draw_passcode()
for i = 0, 4, 1 do
local value = get_passcode_digit(i)
if gui.drawImageRegion then
gui.drawImageRegion(NUMBERS_PNG, 0, value * 16, 16, 16, 128 - 40 + i * 16, -1)
else
gui.drawText(128 - 40 + i * 16, 0, value, 0xFFFFFFFF, 16)
end
end
end
function reset()
joypad.set({Reset = true})
end
function start()
joypad.set({Start = true}, 1)
end
function wait_until(domain, address, byte_value)
memory.usememorydomain(domain)
while memory.readbyte(address) ~= byte_value do
emu.frameadvance()
end
memory.usememorydomain("WRAM")
end
function wait_until_tick_equals_to(value)
wait_until("WRAM", TIME_TICK, value)
end
function wait_main_character_is_getting_out()
wait_until("VRAM", 0xC08A, 195)
end
function is_incorrect_code_screen()
local BASE = 0x0022D0
local text = "incorrect code"
for i = 1, 14, 1 do
local ascii = string.byte(text, i)
local value = mainmemory.readbyte(BASE + i*2)
if ascii ~= 32 then
if value ~= ascii then
return false
end
end
end
return true
end
function go_to_main_screen()
reset()
client.unpause()
input_passcode(00000)
client.speedmode(400)
while mainmemory.readbyte(PASSCODE_BASE + 4) ~= 5 do
start()
emu.frameadvance()
emu.frameadvance()
end
emu.frameadvance()
client.speedmode(100)
savestate.save(MAINMENU_SAVESTATE)
client.pause()
end
function go_to_level(level_number)
savestate.load(MAINMENU_SAVESTATE)
client.unpause()
input_passcode(PASSCODES[level_number])
start()
mainmemory.writebyte(TIME_TICK, 33)
client.speedmode(400)
wait_until_tick_equals_to(0)
wait_main_character_is_getting_out()
client.speedmode(100)
client.pause()
end
function take_screenshots_of_all_levels()
for level_number = 1, 100, 1 do
go_to_level(level_number)
client.unpause()
for i = 0, 32, 1 do
gui.addmessage("")
end
if forms.ischecked(CHCK_SHOWPASSCODE) then
draw_passcode()
emu.frameadvance()
end
client.screenshot(string.format("Push-Over_level_%02d_passcode_%05d.png", level_number, PASSCODES[level_number]))
end
end
function bruteforce_all_passcodes()
client.speedmode(800)
for passcode = 0, 99999, 1 do
local passcode_string = string.format("%05d", passcode)
savestate.load(MAINMENU_SAVESTATE)
client.unpause()
input_passcode(passcode)
gui.addmessage(passcode_string)
start()
mainmemory.writebyte(TIME_TICK, 33)
wait_until_tick_equals_to(0)
if not is_incorrect_code_screen() then
print(passcode_string)
end
end
client.speedmode(100)
client.pause()
end
function button_mainmenu_click()
SHOULD_GO_TO_LEVEL = -1
client.unpause()
end
function button_goToLevel_click()
local level_number = forms.gettext(TEXT_LEVELNUM)
level_number = tonumber(level_number)
if level_number == nil then return; end
level_number = math.floor(level_number)
if level_number <= 0 or level_number > 100 then return; end
SHOULD_GO_TO_LEVEL = level_number
client.unpause()
end
function button_previousLevel_click()
local level_number = forms.gettext(TEXT_LEVELNUM)
forms.settext(TEXT_LEVELNUM, level_number - 1)
button_goToLevel_click()
end
function button_nextLevel_click()
local level_number = forms.gettext(TEXT_LEVELNUM)
forms.settext(TEXT_LEVELNUM, level_number + 1)
button_goToLevel_click()
end
function button_screenshots_click()
SHOULD_GO_TO_LEVEL = -2
client.unpause()
end
function button_findpasscodes_click()
SHOULD_GO_TO_LEVEL = -3
client.unpause()
end
FORM = forms.newform(128, 240, "Push-Over")
BUTT_MAINMENU = forms.button(FORM, "Initialize Save State", button_mainmenu_click, 0, 0, 120, 20)
LABL_LEVELNUM = forms.label(FORM, "Level:", 0, 30, 40, 20, false)
TEXT_LEVELNUM = forms.textbox(FORM, "1", 40, 20, "UNSIGNED", 40, 30, false, true)
BUTT_GOTOLEVEL = forms.button(FORM, "Go to level", button_goToLevel_click, 80, 30, 40, 20)
BUTT_PREVLEVEL = forms.button(FORM, "< Prev", button_previousLevel_click, 0, 50, 60, 20)
BUTT_NEXTLEVEL = forms.button(FORM, "Next >", button_nextLevel_click, 60, 50, 60, 20)
CHCK_SHOWPASSCODE = forms.checkbox(FORM, "Show passcode", 0, 80)
BUTT_SCREENSHOTS = forms.button(FORM, "Take screenshots of all levels", button_screenshots_click, 0, 110, 120, 40)
BUTT_FINDPASSCODES = forms.button(FORM, "Bruteforce passcodes (very slow!)", button_findpasscodes_click, 0, 150, 120, 40)
SHOULD_GO_TO_LEVEL = nil
while true do
if SHOULD_GO_TO_LEVEL ~= nil then
if SHOULD_GO_TO_LEVEL == -1 then
go_to_main_screen()
elseif SHOULD_GO_TO_LEVEL == -2 then
take_screenshots_of_all_levels()
elseif SHOULD_GO_TO_LEVEL == -3 then
bruteforce_all_passcodes()
elseif SHOULD_GO_TO_LEVEL > 0 and SHOULD_GO_TO_LEVEL <= 100 then
go_to_level(SHOULD_GO_TO_LEVEL)
end
SHOULD_GO_TO_LEVEL = nil
end
if forms.ischecked(CHCK_SHOWPASSCODE) then
draw_passcode()
end
emu.frameadvance()
end
Moderators Note:
The PNG Text Data was removed due to breaking the forums' layout. Please refrain from posting long single line strings.