Last active
February 16, 2019 19:38
-
-
Save Sanqui/5295479 to your computer and use it in GitHub Desktop.
A Lua script for corrupting Gameboy and Gameboy Advance games live. Supply to VBA-RR. Consult your doctor before use.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- ░█▀▄░█▀█░█▀█░█▄█░░░░█░░░█░█░█▀█ | |
-- ░█▀▄░█░█░█░█░█░█░░░░█░░░█░█░█▀█ | |
-- ░▀▀░░▀▀▀░▀▀▀░▀░▀░▀░░▀▀▀░▀▀▀░▀░▀ | |
-- | |
-- | |
-- Fair warning: this code is about as bad as the things it does to games. | |
-- GBA support experimental and frankly quite boring. | |
--Original NES Braidulator by Antony Lavelle 2009 got_wot@hotmail.com http://www.the-exp.net | |
--Change these settings to adjust options | |
--Which key you would like to function as the "rewind key" | |
local rewindKey = 'W' -- Hold this to go back in time! | |
-- Keys. Feel free to change them to something convenient, because I have a weird keyboard layout | |
randomkey = "A" -- Just your standard fare. One random WRAM byte. | |
plusminuskey = "S" -- +/- on one random WRAM byte. Probably the safest you can do which still has some effect. | |
powerkey = "M" -- Power corruption! 16 random bytes. The most fun key you can press. | |
highkey = "K" -- On GB, random byte from HRAM, may have drastic consequences. On GBA, power corruption on IRAM | |
vramkey = "Q" -- Messes up vram (graphics). Purely visual - totally safe. | |
freezekey = "B" -- Freezes a random byte in memory indefinedly. May have totally different effects from regular corruption. | |
unfreezekey = "N" -- Unfreezes every frozen byte. This also happens automatically when rewinding. | |
savetileskey = "I" -- Save current tiles. | |
restoretileskey = "O" -- Restore saved tiles. Purely visual, can be used to copy tiles from game to game etc. | |
wrammap = "L" -- Display an interactive memory map, one pixel per byte. | |
-- Opacity = value. Blue = byte changed this frame. Green = HRAM. | |
-- Left: +1 Right: -1 Middle: set to $FF | |
-- Warning: SLOW! PARTLY BROKEN FOR GBA! | |
rewindon = "T" -- Switches rewinding on and off. May save some 15%, depending on your computer. | |
debugkey = "`" -- Displays some boring CPU info. | |
--How much rewind power would you like? (The higher the number the further back in time you can go, but more computer memory is used up) | |
--Do not set to 0! | |
local saveMax = 2000; | |
funtimes = 0; | |
txttime = 0; | |
--The stuff below is for more advanced users, enter at your own peril! | |
print("Boom loaded!") | |
function rainbowcolor(ri) | |
freq = 0.15 | |
r = math.sin(freq*ri) * 127 + 128 | |
g = math.sin(freq*ri+2) * 127 + 128 | |
b = math.sin(freq*ri+4) * 127 + 128 | |
return {r, g, b} | |
end | |
function randmemory(from, to) -- gets a random memory offset, avoiding the stack | |
if gb then | |
while (true) do | |
bang = math.random(from, to) | |
if bang < sp or bang > stack then | |
break | |
end | |
end | |
return bang | |
else | |
return math.random(from, to) | |
end | |
end | |
frozen = {} | |
crashi = 0 | |
vram = {} | |
wram = {} | |
prevwram = {} | |
s = {0xff, 0xff, 0x00, 0x00, 0xbb, 0xbb, 0x8b, 0x8b, 0x68, 0x68, 0x8b, 0x8b, 0xeb, 0xeb, 0x18, 0x18} | |
stack = -1 | |
-- dumb format for speed/convenience | |
invalid_opcodes = {[0xd3]= true, [0xdb]= true, [0xdd]= true, [0xe3]= true, [0xe4]= true, [0xeb]= true, [0xec]= true, [0xed]= true, [0xfd]= true, [0xf4]= true} | |
local saveArray = {};--the Array in which the save states are stored | |
local saveCount = 1;--used for finding which array position to cycle through | |
local save; -- the variable used for storing the save state | |
local rewindCount = 0;--this stops you looping back around the array if theres nothing at the end | |
local savePreventBuffer = 1;--Used for more control over when save states will be saved, not really used in this version much. | |
local saveFrequency = 10; -- look at me mom, I'm editing LIVE! | |
local timer = -1; | |
local rewind = true | |
while (true) do | |
keys = input.get(); | |
timer = timer + 1; | |
savePreventBuffer = savePreventBuffer-1; | |
if savePreventBuffer==0 then | |
savePreventBuffer = 1; | |
end; | |
keys = input.get(); | |
if keys[rewindKey] then | |
savePreventBuffer = 5; | |
if rewindCount==0 then | |
--makes sure you can't go back too far could also include other things in here, left empty for now. | |
else | |
savestate.load(saveArray[saveCount]); | |
saveCount = saveCount-1; | |
rewindCount = rewindCount-1; | |
if saveCount==0 then | |
saveCount = saveMax-1; | |
end; | |
end; | |
local HUDMATH = rewindCount / saveMax;--Making the rewind time a percentage. | |
gui.text(1,0, string.format("%.2f%%", HUDMATH * 100));--Displaying the time onscreen. | |
if math.fmod(timer, 30) <= 15 then | |
gui.text(138,8*2, "<<"); -- picky, picky | |
end; | |
end; | |
if rewind and savePreventBuffer==1 and math.fmod(timer, saveFrequency) == 0 then | |
-- gui.text(80,15,""); | |
saveCount=saveCount+1; | |
if saveCount==saveMax then | |
saveCount = 1; | |
end | |
rewindCount = rewindCount+1; | |
if rewindCount==saveMax-1 then | |
rewindCount = saveMax-2; | |
end; | |
save = savestate.create(); | |
savestate.save(save); | |
saveArray[saveCount] = save; | |
end; | |
if math.fmod(timer, 32) == 0 then | |
header = memory.readbyte(0x133) -- last byte of logo on GB | |
if header == 0x3e then | |
gb = true | |
gba = false | |
else | |
gb = false | |
gba = true | |
end | |
end | |
if gb then | |
pc = memory.getregister('pc') | |
op = memory.readbyte(pc) | |
is_invalid = invalid_opcodes[op] | |
sp = memory.getregister('sp') | |
if crashi <= 1 then | |
if stack == -1 and sp < 0xe000 and sp >= 0xc000 then | |
stack = (math.ceil(sp / 0x10) * 0x10)-1 | |
print(string.format("sp=%04X, stack detected at %04X", sp, stack)) | |
elseif sp > 0xFF00 then | |
print("Stack detected in HRAM, restart?") | |
stack = -1 | |
end | |
end | |
--if sp <= | |
end | |
if keys[debugkey] then | |
if gb then | |
debugtext = string.format("pc=%04X sp=%04X", pc, sp) | |
gui.text(160-(string.len(debugtext)*4),0, debugtext, 'green'); | |
debugtext = string.format("opcode %02X", op) | |
gui.text(160-(string.len(debugtext)*4),8, debugtext, 'green'); | |
else | |
debugtext = string.format("GBA") | |
gui.text(240-(string.len(debugtext)*4),0, debugtext, 'green'); | |
end | |
end | |
if gb then | |
if pc >= 0x8000 or sp < 0x8000 or is_invalid or pc == 0x39 then | |
crashi = crashi + 1 | |
if crashi >= 2 then | |
gui.text(1,8, crashi, 'red'); | |
if crashi > 10 then info = "Crashed" else info = "Clinical death" end | |
crashtext = string.format(info.." pc=%04X sp=%04X", pc, sp) | |
gui.text(160-(string.len(crashtext)*4),0, crashtext, 'red'); | |
crashtext = "" | |
if is_invalid then | |
crashtext = string.format("Invalid opcode %02X", op) | |
elseif pc == 0x39 then | |
crashtext = string.format("Stuck in rst 38", op) | |
end | |
gui.text(160-(string.len(crashtext)*4),8, crashtext, 'red'); | |
end | |
else | |
crashi = 0 | |
end | |
-- print(string.format('%04X', memory.getregister('pc'))) | |
end | |
if keys[randomkey] then | |
gui.text(1,137, "Random bytes"); -- picky, picky | |
if gb then | |
bang1 = randmemory(0x9800, 0xFEFF) | |
elseif gba then | |
bang1 = randmemory(0x2000000, 0x0203FFFF) | |
end | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
--print(string.format("%04X => %02X", bang1, bang2)); | |
end; | |
if keys[plusminuskey] then | |
if gb then | |
bang1 = randmemory(0x9800, 0xFEFF) | |
elseif gba then | |
bang1 = randmemory(0x2000000, 0x0203FFFF) | |
end | |
bang2 = AND(memory.readbyte(bang1) + math.random(-1, 1), 0xFF); | |
memory.writebyte(bang1, bang2); | |
gui.text(1,137, string.format("%04X +/- 1", bang1)); -- picky, picky | |
--print(string.format("%04X => %02X", bang1, bang2)); | |
end; | |
-- KABLOOEY | |
if keys[powerkey] then | |
gui.text(1,137, "Power corruption!", rainbowcolor(timer)); -- picky, picky | |
for i = 0, 0xF do | |
if gb then | |
bang1 = randmemory(0xA000, 0xFEFF) | |
elseif gba then | |
--if math.random(1) then | |
bang1 = randmemory(0x2000000, 0x0203FFFF) | |
--else | |
-- bang1 = randmemory(0x3000000, 0x0303FFFF) | |
--end | |
end | |
bang2 = math.random(0x00, 0xFF); | |
memory.writebyte(bang1, bang2); | |
--print(string.format("%04X => %02X", bang1, bang2)); | |
end; | |
end; | |
-- KABLOOEY | |
if keys[highkey] then | |
if gb then | |
gui.text(1,137, "HRAM corruption!"); -- picky, picky | |
bang1 = randmemory(0xFF8C, 0xFFFE) | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
end | |
if gba then | |
gui.text(1,137, "Power corruption INT!", rainbowcolor(timer)); -- picky, picky | |
for i = 0, 0xF do | |
bang1 = randmemory(0x3000000, 0x0300FFFF) | |
bang2 = math.random(0x00, 0xFF); | |
memory.writebyte(bang1, bang2); | |
end; | |
end | |
end; | |
if keys[vramkey] then | |
rand = math.random(0, 16) | |
if gb then | |
if rand >= 3 then | |
gui.text(1,137, "Trashing VRAM"); | |
bang1 = math.random(0x8000, 0x9FFF) | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
--elseif rand >= 3 then -- I hate you vba | |
-- gui.text(1,137, "Trashing palettes"); | |
-- bang1 = math.random(0, 0xFF); | |
-- bang2 = math.random(0, 0xFF); | |
-- memory.writebyte(0xFF68, bang1); | |
-- memory.writebyte(0xFF69, bang2); | |
elseif rand >= 1 then | |
gui.text(1,137, "Mixing up tiles"); | |
bang1 = math.random(0x8000, 0x9700) | |
tile = memory.readbyterange(bang1, 0x10) | |
bang2 = math.random(0x8000, 0x9700) | |
for i,byte in pairs(tile) do | |
memory.writebyte(bang2+i, byte) | |
end | |
memory.writebyte(bang1, bang2); | |
else | |
gui.text(1,137, "Trashing OAM"); | |
bang1 = math.random(0xFE00, 0xFE90) | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
end | |
rand2 = math.random(0, 10000) | |
if rand2 == 0 then | |
bang = math.random(0x8000, 0x9700) | |
bang = AND(bang, 0xfff0) | |
for i,byte in pairs(s) do | |
memory.writebyte(bang-1+i, byte) | |
end | |
gui.text(1,137, "4"); | |
end | |
elseif gba then | |
for i = 0, 0x8 do -- larger screen, larger corruption | |
if rand >= 3 then | |
gui.text(1,137, "Trashing VRAM"); | |
bang1 = math.random(0x6000000, 0x6017fff) | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
else | |
gui.text(1,137, "Trashing palettes"); | |
bang1 = math.random(0x5000000, 0x5000400) | |
bang2 = math.random(0, 0xFF); | |
memory.writebyte(bang1, bang2); | |
end | |
end | |
end | |
--print(string.format("%04X => %02X", bang1, bang2)); | |
end; | |
if keys[freezekey] then | |
gui.text(1,137, "Freeze"); -- picky, picky | |
for i = 0, 0xF do | |
if gb then | |
bang1 = randmemory(0xC000, 0xDFFF) | |
elseif gba then | |
bang1 = randmemory(0x2000000, 0x0203FFFF) | |
end | |
--bang2 = math.random(0x00, 0xFF); | |
frozen[bang1] = memory.readbyte(bang1); | |
--print(string.format("%04X => %02X", bang1, bang2)); | |
end; | |
end; | |
if keys[unfreezekey] or keys[rewindKey] then | |
gui.text(1,137, "Unfreeze") | |
frozen = {} | |
end | |
if keys[savetileskey] then | |
if gb then | |
gui.text(1,137, "Saved tiles"); -- picky, picky | |
vram = memory.readbyterange(0x8000, 0x1700) | |
end | |
end; | |
if keys[restoretileskey] then | |
if gb then | |
gui.text(1,137, "Restored tiles") | |
for i,v in pairs(vram) do | |
memory.writebyte(0x8000-1+i, v); | |
end | |
end | |
end | |
if keys[rewindon] and not lastkeys[rewindon] then rewind = not rewind end | |
if keys[rewindon] then | |
if rewind then text = "Rewind ON!" else text = "Rewind OFF!" end | |
gui.text(1,137, text) | |
end | |
if keys[wrammap] and not lastkeys[wrammap] then showwram = not showwram; end | |
if showwram then | |
--gui.text(1,137, "Live WRAM") | |
if gb then | |
wram = memory.readbyterange(0xC000, 0x2000) | |
for k,v in pairs(memory.readbyterange(0xFF00, 0xFF)) do wram[0x2000+k] = v end | |
width = 64 | |
elseif gba then | |
wram = memory.readbyterange(0x2000000, 0x3FFFF) | |
width = 64*3 | |
end | |
for i,v in pairs(wram) do | |
--memory.writebyte(0x8000-1+i, v); | |
if prevwram[i] ~= wram[i] then | |
color = {0, 0, 255, v} | |
elseif frozen[0xC000+i-1] then | |
color = {0, 255, 255, v} | |
elseif i > 0x2000 then | |
color = {0, 255, 0, v} | |
else | |
color = {255, 0, 0, v} | |
end | |
gui.pixel((i-1)%width, math.floor((i-1)/width), color) | |
end | |
prevwram = wram | |
xmouse, ymouse, left, right, middle = keys['xmouse'], keys['ymouse'], keys['leftclick'], keys['rightclick'], keys['middleclick'] | |
if left or right or middle and xmouse <= width then | |
offset = 0xC000 + (xmouse%width) + (ymouse*width) | |
if left then | |
memory.writebyte(offset, AND(memory.readbyte(offset) + 1, 0xFF)) | |
elseif right then | |
memory.writebyte(offset, AND(memory.readbyte(offset) - 1, 0xFF)) | |
else | |
memory.writebyte(offset, 0xff) | |
end | |
end | |
end | |
for i,v in pairs(frozen) do | |
memory.writebyte(i, v); | |
end | |
lastkeys = keys | |
emu.frameadvance(); | |
end; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment