Post subject: Presenting: Smart RAM Search!
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
Download the latest version here! Quick instructions As per DarkKobold's request, I'm including a basic guide at the top of this post. These are just bare-bones instructions to get you started. The full technical description and detailed instructions are after all these purdy pictures. Introduction If you're anything like me, you've encountered some difficulties with emulators' native RAM Search functions. Although the built-in RAM Search offers precision and comparative ease when you know just what to look for, oftentimes the RAM address you seek is somewhat ill-defined. Everyone has experience narrowing down a RAM address until the search has to be reset or, even worse, making some small mistake that cannot be undone. That is why FatRatKnight and I are proud to present Smart RAM Searching. This Lua script aims to make RAM searching intuitive, internally intelligent, and externally idiot-proof. The mathematical premise is simple if you have some vector algebra under your belt. We take a user's mouse movements with each frame as the input vector, let's call it v. This vector and vectors representing all RAM addresses (denote them by w) for each frame are then detrended (their average value subtracted) and normalized (divided by their magnitude). The program then takes the dot product between the normalized input vector and the normalized RAM address vectors. Its output is a list of RAM addresses that maximize the magnitude of this dot product, since
v.w=|v|*|w|*cos(theta)
   =cos(theta)             (for unit vectors)
where the period denotes the dot product. The closer cos^2(theta) is to 1, the closer the user's input is to being just a multiple of the RAM address's value. Incidentally, this method also minimizes the "residual sum of squares" (the sum of the squares of the differences between the two vectors). Why use this program? This program has several advantages over the default RAM Search function: • It is insensitive to the address's range. It does not matter whether the address's value varies between 0 and 10, 197 and 235, or 0 and 256. As long as the user faithfully tracks its contour, the script will pick out the best match. • It is insensitive to sign differences. This is especially useful for tracking things like the y coordinate. Oftentimes, the y coordinate decreases with increasing height. • It is relatively insensitive to small human errors. Because the user's input is qualitative, small errors (such as misplacing the mouse for a single frame) will not appreciably affect the program's output. Note that the same applies to the RAM address itself, which may (for whatever reason) fluctuate in a manner inconsistent with what the user perceives. This program also has some disadvantages compared with the default RAM Search function: • It cannot find RAM addresses whose values are constant. This is because the vectors are detrended. On the other hand, if the value is constant, why would you even attempt to use this script instead of using the default RAM Search or, better yet, just writing the value down? • It offers no real advantages if the value in question is known. For example, if enemy HP is displayed on the screen, that value can simply be input into RAM Search to efficiently find its address. • It is slow. In its current implementation, this script typically takes a few seconds to execute a RAM search in FCEUX if the user's input is about 100 frames. That is because it must compare the user's input with every RAM address for every frame. The script is best used with FCEUX or Game Boy games in VBA, both of which have comparatively less RAM than other systems. FatRatKnight and I are working on some pre-filtering options that will make this program run much faster and allow it to be used efficiently in other emulators. Current features • A vertical bar to accept user input. Clicking the bar while frame advancing generates the input vector. • Shift-clicking to move the bar. • Resize boxes above and below the bar. • GUI output that displays top-candidate RAM addresses, their "score" (cos2(theta)), and their current value. • Cross-compatibility with several emulators. The script has been tested to work in VBA, FCEUX, and SNES9x. It is probably also compatible with Gens. • Excluded mirror values. Many RAM addresses have mirrored values. Including them wastes processing power and clutters up the GUI output. • Pretty rainbow colors in the input bar. (Does not work in FCEUX.) Features to be implemented Because FatRatKnight and I are working on different aspects of the script, I will split the improvements into technical and GUI categories. Technical improvements: • Pre-filtering. This currently falls under two categories: - Change pre-filtering. This would be similar to the default RAM Search's change count feature. It would allow the user to exclude addresses that have or haven't changed, according to what they expect from the address they're searching for. - Dot product pre-filtering. This would work exactly like the program's basic function except that addresses that aren't a sufficiently good match would be strictly excluded from future searches. This could be set by a number of different methods: keep the top x% of matches, keep the top n matches, or keep only matches whose score is above a certain threshold. • Downsampling. This means recording the RAM addresses only once every n frames. • Derivatives. Sometimes you do not know whether the value you are looking for is a function or its derivative. An example of this (and one of the things that inspired me to work on this script) is the hilly terrain in the racing game Skitchin': Is it stored as a height for graphical purposes or as a slope, which affects the player's acceleration directly? • Time lag. Sometimes there is a delay between what the user sees on the screen and what the RAM address is doing internally. In these instances, we want to compare the two vectors offset by some time delay (with a normalization factor thrown in). This is known as the cross-correlation. It is very computationally intensive, however, and cannot reasonably be implemented without pre-filtering. Also, the cross-correlation is most quickly computed by taking the discrete Fourier transform of the vectors, although it may only be faster to do so if there is an especially large time lag. • Words and other series of bytes. Sometimes we're not looking for a single byte. Endianness should also be considered. • Wrap-around. For periodic variables (e.g., the position of fire bars in Super Mario Bros. or the progression of the music), the value can jump from at or near 255 back to 0 (or vice versa). The user would have to set a threshold below which wrap-around would be applied. • Signed values. Currently, all values are presumed unsigned. • Expanded compatibility. This script only works with 8- and 16-bit consoles, as we do not yet know the memory map of other consoles. • An improved sorting algorithm. For ease of programming and getting this script published promptly, an insertion sort was implemented. This should be changed to a merge sort or other sort that executes in O(n*log(n)). GUI improvements: • Graphical tie-in of the above-mentioned improvements. • Options to change the vertical input bar into a horizontal bar. A circle could also be used. • A menu system that will complement keyboard shortcuts for features. • Arrows signifying the resize buttons. • Context-sensitive cursor icons. • Text boxes for user input. • Customizable output, including less or more information and fewer or more matches. For long lists of matches, the user should also be able to scroll through results. • Continuing to gather user input after the cursor leaves the bar. • Manual override of game system and/or screen resolution. GBx, SGB, and GBA games all use different screen resolutions and there seems to be no easy way to differentiate between which game is which in Lua. The same goes for PSX games, which use several different resolutions. Sega games may use different resolutions and they also have different memory maps depending on the system, according to marzojr. The user should be able to toggle between pre-set screen resolutions. • Fix the slowdown that occurs when results are displayed in Gens (and other emulators?). We also wish to further break the script up into smaller functions for ease of editing. How to use the script Although the script is designed to be very intuitive, there are a few basic instructions. First, download all the scripts posted below and place them in the same directory. Run the script SmartRAMSearch.lua in the emulator of your choice (preferably FCEUX or VBA). Either immediately or after frame advancing once, a green rectangle (the user input bar) should appear. This bar can be moved by holding the shift key and then clicking and dragging it. It can also be resized by clicking and dragging the boxes above and below it. Do something in the game that will affect the RAM address you are looking for in a predictable way. (It is suggested you make a movie and work from that.) While the RAM address is changing, click and hold the bar and then frame advance. Continue to frame advance while the mouse is still held. As you frame advance, move the mouse up and down with the contour you think the RAM address's value is following. For example, if you wish to know the RAM address corresponding to Mario's height in Super Mario Bros., just follow him with your cursor while you execute a jump. Input will not be accepted if your cursor leaves the bar. Once you have collected data for many frames (a few dozen to a few hundred), press the spacebar to have the program calculate the closest-matching RAM addresses. To display these addresses, press the "home" key. The first column contains the RAM addresses, sorted by how closely they matched with your input, the second column contains their "score", which is just cos^2(theta) times 10,000, and the third column contains their current values. What we need from you • Your encouragement. • Your constructive criticism. • Your suggestions. • Your success stories. If this topic stagnates, then we will assume there is no demand for this script and stop updating it. The scripts As mentioned above, you should download these scripts to the same directory. Open the game of your choice and run "SmartRAMSearch.lua". Download BasicFunctions.lua
Language: lua

function boolto10(boole) if boole then return 1 else return 0 end end
Download VectorOps.lua
Language: lua

function getaverage(vector) local total=0 local n=0 for k,v in pairs(vector) do total=total+v n=n+1 end local average=total/n return average end function detrend(vector) local average=getaverage(vector) for k,v in pairs(vector) do vector[k]=v-average end return vector end function dotproduct(v1,v2) local total=0 for k,v in pairs(v1) do total=total+v*v2[k] end return total end function getmag(vector) local magsquared=dotproduct(vector,vector) local magnitude=math.sqrt(magsquared) return magnitude end function normalize(vector) local magnitude=getmag(vector) if magnitude>0 then for k,v in pairs(vector) do vector[k]=v/magnitude end end return vector end
Download Sorts.lua
Language: lua

--This function executes a selection sort on a vector, finding the best N values and organizing them into a table. --Note that the computation time is O(n^2). For efficiency, consider merge sort, whose speed is O(n*log(n)). --The advantage of selection sort is that it's easy to program and is probably still quite efficient as long as N is small. function selectionsort(vector,N) local sorted={} for i=1,N do sorted[i]={-1,0} for k,v in pairs(vector) do if v>sorted[i][2] then sorted[i]={k,v} end end vector[sorted[i][1]]=0 end return sorted end --Insertion sort provided by FatRatKnight. SortedTable={} function InsertVal(v) local n=#SortedTable+1 while n>1 do if v<SortedTable[n-1] then break end SortedTable[n]=SortedTable[n-1] n=n-1 end SortedTable[n]=v end
Download YummyButton.lua
Language: lua

--[[ Welcome to my Buttons script! To start, put in the following lines: require("thisScript") NewBtn{x= 1, y= 1, fn= function() ((Various code here)) end} And all of a sudden, you have this bluish button on the top left corner of the emulator window, once you run the script. Use the mouse to click the button. The code you put in will happen. Now, there are ways to configure these buttons to your desires. For one thing, make as many NewBtn objects as you like. This script will keep track of 'em. For another, change that x and y up there if you don't like the NewBtn being stuck in the top-left corner. Finally, there's various options you can use to tweak your NewBtn desires. REQUIRED: x - X position. Where is the button horizontally? y - Y position. Where do we put it vertically? fn - Function. The button had better do something!! Optional: text - Cute text to display on button. May size button automatically. x1 - Identical to the otherwise required x. If both present, x has priority x2 - The right side of the button y1 - Identical to y. y1 can take y's place, but if you make both, y is used y2 - The bottom side of the button width - Width of button. Would interfere with x2, but x2 takes priority. height- Height of button. Use if you don't feel like using y2. color - General color scheme of the button. This affects all aspects of color. MBC - Main Border Color. "Main" is when your mouse isn't over the button. MFC - Main Fill Color. ... I use "Main" for lack of better words... SBC - Sub Border Color. "Sub" is used when you're mousing over the button. SFC - Sub Fill Color. Anyone have better words for me? tag - Spare variable. Included functions: draw - Draws the button. Don't bother using it, it's automatically called. recolor- Modifies the color scheme. Takes in one value depicting base color. move - Moves the button so that the top-left corner matches the x,y input. While it is packaged for simple use as is, the more savvy programmers may want to read on. I pass the button object to the clicked button function as the only parameter. I can't think of any real way of passing variables you want for it, but I added the "tag" variable for this purpose. Technically, after the button is constructed, you can add new parameters to the button object itself, but the "tag" variable seemed like a convenient thing to add to the constructor. In addition, I put the HandleBtns function in gui.register by default, but go ahead and call it in another function and replace the gui.register callback. This function will return whatever the button function returns, so if it is in your interest to have the button function return something, it will do so through _FRK_Btn.HandleBtns. The NewBtn constructor returns the button object it created as a table for you to mess around with, should you have a reason to. Maybe you want to move it around or change its colors, who knows. Finally, _FRK_Btn.MB holds all my buttons. You can, in the middle of creating new buttons, set another variable (MB2, perhaps?), set _FRK_Btn.MB = {}, then create more buttons. You'll have two different sets of buttons which you can switch between, but you'll have to make your own functions to do the switch. But if you're not experienced in lua programming, none of the previous four paragraphs are of any interest to you. The simple use of these buttons are good enough for many uses. ]]-- _FRK_Btn= {} local EMUCHWID= 6 local EMUCHHGT= 8 if stylus then EMUCHWID= 6; EMUCHHGT= 8 elseif snes9x then EMUCHWID= 4; EMUCHHGT= 6 elseif VBA then EMUCHWID= 4; EMUCHHGT= 6 end _FRK_Btn.ClickType= "leftclick" _FRK_Btn.MB= {} keys, lastkeys= {}, {} function UpdateKeys() lastkeys= keys; keys= input.get() end function Press(k) return keys[k] and not lastkeys[k] end --***************************************************************************** function _FRK_Btn.FindBtn() --***************************************************************************** if (keys.xmouse == lastkeys.xmouse) and (keys.ymouse == lastkeys.ymouse) then return end local X,Y= keys.xmouse, keys.ymouse for i= 1, #_FRK_Btn.MB do -- linear scan, darn it! local n= _FRK_Btn.MB[i] if (Y >= n.y1) and (Y <= n.y2) and (X >= n.x1) and (X <= n.x2) then return i end end return -1 end --***************************************************************************** function _FRK_Btn.ClickBtn(id) --***************************************************************************** if id and _FRK_Btn.MB[id] then return _FRK_Btn.MB[id]:fn() end end --***************************************************************************** function _FRK_Btn.DrawUnsel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.MFC, B.MBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.DrawSel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.ShowBtns() --***************************************************************************** B= _FRK_Btn.MB for i= 1, #B do B[i]:draw() end end --***************************************************************************** function _FRK_Btn.Recolor(B,color) --***************************************************************************** -- Does not like strings. Still will accept them, however. local t= type(color) if t == "string" then -- Uh, no. No tricky color stuff. Sorry. B.MBC= Cns.color B.SBC= Cns.color B.MFC= 0x00000040 B.SFC= 0xC0C0C0C0 --Oh, not a string! Better be table or number, please. else if t == "table" then color= 0 local q= color.r or color[1] if q then color= bit.lshift(q,24) end q= color.g or color[2] if q then color= bit.bor(color,bit.lshift(q,16)) end q= color.b or color[3] if q then color= bit.bor(color,bit.lshift(q, 8)) end q= color.a or color[4] if q then color= bit.bor(color,q) else color= bit.bor(color,0xFF) end end B.MBC= color B.MFC= bit.rshift(bit.band(color,0xFEFEFEFE),1) B.SBC= bit.bor(color+bit.band(0xFFFFFFFF-color,0xFEFEFEFE)/2,0xFF) B.SFC= color end end --***************************************************************************** function _FRK_Btn.Move(B, x,y) --***************************************************************************** -- Moves the whole button for you! if x then local XDiff= x - B.x1 B.x1= x B.x2= B.x2 + XDiff B.xt= B.xt + XDiff end if y then local YDiff= y - B.y1 B.y1= y B.y2= B.y2 + YDiff B.yt= B.yt + YDiff end end --***************************************************************************** function _FRK_Btn.NewBtn(Cns) --***************************************************************************** -- the button constructor! --Insert universal function pointers while creating a new table. local NewTbl= { draw= _FRK_Btn.DrawUnsel, recolor= _FRK_Btn.Recolor, move= _FRK_Btn.Move } --Insert presumed information. NewTbl.fn= Cns.fn NewTbl.x1= Cns.x or Cns.x1 NewTbl.y1= Cns.y or Cns.y1 NewTbl.tag= Cns.tag --Check if user defined the right side. if Cns.x2 then NewTbl.x2= math.max(NewTbl.x1,Cns.x2) NewTbl.x1= math.min(NewTbl.x1,Cns.x2) elseif Cns.width then NewTbl.x2= NewTbl.x1 + Cns.width end --Check if user defined the bottom side. if Cns.y2 then NewTbl.y2= math.max(NewTbl.y1,Cns.y2) NewTbl.y1= math.min(NewTbl.y1,Cns.y2) elseif Cns.height then NewTbl.y2= NewTbl.y1 + Cns.height end --Look for text. Do tricky stuff based on where to put the darn text or --how to resize the button. if Cns.text then NewTbl.text= tostring(Cns.text) local len= #NewTbl.text * EMUCHWID if NewTbl.x2 then NewTbl.xt= math.floor((NewTbl.x1 + NewTbl.x2 - len)/2) else NewTbl.x2= NewTbl.x1 + len + 3 NewTbl.xt= NewTbl.x1 + 2 end if NewTbl.y2 then NewTbl.yt= math.floor((NewTbl.y1 + NewTbl.y2 - 8)/2) else NewTbl.y2= NewTbl.y1 + EMUCHHGT + 4 NewTbl.yt= NewTbl.y1 + 2 end else NewTbl.text= "" NewTbl.xt= NewTbl.x1 NewTbl.yt= NewTbl.y1 if not NewTbl.x2 then NewTbl.x2= NewTbl.x1 + 8 end if not NewTbl.y2 then NewTbl.y2= NewTbl.y1 + 8 end end --Mess with colors! if Cns.color then NewTbl:recolor(Cns.color) else NewTbl.MBC= Cns.MBC or 0x0020FFC0 NewTbl.MFC= Cns.MFC or 0x00000040 NewTbl.SBC= Cns.SBC or 0x0080FFFF NewTbl.SFC= Cns.SFC or 0x2000C0C0 end --Finally add the button to our MB array. local index= #(_FRK_Btn.MB)+1 _FRK_Btn.MB[index]= NewTbl --Here's the button object in case you want to handle it yourself. return NewTbl end local ID= -1 --***************************************************************************** function _FRK_Btn.HandleBtns() --***************************************************************************** UpdateKeys() local id= _FRK_Btn.FindBtn() if id then if ID ~= -1 then _FRK_Btn.MB[ID].draw= _FRK_Btn.DrawUnsel end if id ~= -1 then _FRK_Btn.MB[id].draw= _FRK_Btn.DrawSel end ID= id end _FRK_Btn.ShowBtns() if Press(_FRK_Btn.ClickType) and (ID ~= -1) then return _FRK_Btn.ClickBtn(ID) end end --============================================================================= if not FRK_NoAuto then --============================================================================= gui.register(_FRK_Btn.HandleBtns) NewBtn= _FRK_Btn.NewBtn end
Download bit.lua
Language: lua

--[[--------------- LuaBit v0.4 ------------------- a bitwise operation lib for lua. http://luaforge.net/projects/bit/ How to use: ------------------- bit.bnot(n) -- bitwise not (~n) bit.band(m, n) -- bitwise and (m & n) bit.bor(m, n) -- bitwise or (m | n) bit.bxor(m, n) -- bitwise xor (m ^ n) bit.brshift(n, bits) -- right shift (n >> bits) bit.blshift(n, bits) -- left shift (n << bits) bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>) Please note that bit.brshift and bit.blshift only support number within 32 bits. 2 utility functions are provided too: bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence) -- high bits first bit.tonumb(bit_tbl) -- convert a bit table into a number ------------------- Under the MIT license. copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com) --]]--------------- do ------------------------ -- bit lib implementions local function check_int(n) -- checking not float if(n - math.floor(n) > 0) then error("trying to use bitwise operation on non-integer!") end end local function to_bits(n) check_int(n) if(n < 0) then -- negative return to_bits(bit.bnot(math.abs(n)) + 1) end -- to bits table local tbl = {} local cnt = 1 while (n > 0) do local last = math.mod(n,2) if(last == 1) then tbl[cnt] = 1 else tbl[cnt] = 0 end n = (n-last)/2 cnt = cnt + 1 end return tbl end local function tbl_to_number(tbl) local n = table.getn(tbl) local rslt = 0 local power = 1 for i = 1, n do rslt = rslt + tbl[i]*power power = power*2 end return rslt end local function expand(tbl_m, tbl_n) local big = {} local small = {} if(table.getn(tbl_m) > table.getn(tbl_n)) then big = tbl_m small = tbl_n else big = tbl_n small = tbl_m end -- expand small for i = table.getn(small) + 1, table.getn(big) do small[i] = 0 end end local function bit_or(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i]== 0 and tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_and(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i]== 0 or tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_not(n) local tbl = to_bits(n) local size = math.max(table.getn(tbl), 32) for i = 1, size do if(tbl[i] == 1) then tbl[i] = 0 else tbl[i] = 1 end end return tbl_to_number(tbl) end local function bit_xor(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) for i = 1, rslt do if(tbl_m[i] ~= tbl_n[i]) then tbl[i] = 1 else tbl[i] = 0 end end --table.foreach(tbl, print) return tbl_to_number(tbl) end local function bit_rshift(n, bits) check_int(n) local high_bit = 0 if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 high_bit = 2147483648 -- 0x80000000 end for i=1, bits do n = n/2 n = bit_or(math.floor(n), high_bit) end return math.floor(n) end -- logic rightshift assures zero filling shift local function bit_logic_rshift(n, bits) check_int(n) if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 end for i=1, bits do n = n/2 end return math.floor(n) end local function bit_lshift(n, bits) check_int(n) if(n < 0) then -- negative n = bit_not(math.abs(n)) + 1 end for i=1, bits do n = n*2 end return bit_and(n, 4294967295) -- 0xFFFFFFFF end local function bit_xor2(m, n) local rhs = bit_or(bit_not(m), bit_not(n)) local lhs = bit_or(m, n) local rslt = bit_and(lhs, rhs) return rslt end -------------------- -- bit lib interface bit = { -- bit operations bnot = bit_not, band = bit_and, bor = bit_or, bxor = bit_xor, rshift = bit_rshift, lshift = bit_lshift, bxor2 = bit_xor2, blogic_rshift = bit_logic_rshift, -- utility func tobits = to_bits, tonumb = tbl_to_number, } end --[[ for i = 1, 100 do for j = 1, 100 do if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then error("bit.xor failed.") end end end --]]
Download SmartRAMSearch.lua
Language: lua

--Certain parameters are emulator-specific. --In particular, we need to know the range of relevant RAM addresses and place the bar where it is visible within the window. local function getsystem() --These are the default values if the emulator isn't specified. Watch out! It will take a very long time to execute and you may scroll the bar off the screen! byterange={ {0x000000,0x1FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=480,640 if FCEU then --NES byterange={ {0x0000,0x07FF}, {0x2000,0x2008}, {0x4000,0x7FFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=239,255 elseif vba then --GBx byterange={ {0x8000,0xDFFF}, {0xFF80,0xFFFF}} L,U,R,D= 140,10,149,109 SCRBOTTOM,SCRRIGHT=143,159 elseif snes9x then --SNES byterange={ {0x7E0000,0x7FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=222,255 elseif gens then --Genesis (and similar) byterange={ {0xFF0000,0xFFFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=223,319 emu=gens dofile("bit.lua") elseif pcsx then --PlayStation byterange={ {0x000000,0x1FFFFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=480,640 elseif stylus then --DS print("Compatibility pending...") else --Emulator not recognized print("Error: What emulator are you using?") end end --Initialize this stuff... dofile("BasicFunctions.lua") dofile("VectorOps.lua") dofile("Sorts.lua") getsystem() local mindex=0 range={} for i=1,#byterange do for j=byterange[i][1],byterange[i][2] do range[mindex]=j mindex=mindex+1 end end --range=0x7FFF addresses={} for i = 1,#range do addresses[range[i]]={} end X={} costhetasq={} --Converts a hue angle to RGB. Normalized to the range [0,255]. local function huetoRGB(hueangle,alpha) local R,G,B=0,0,0 if not(alpha) then alpha=0x7F end --Alpha defaults to 0x7F. hueangle=hueangle%360 local huefraction=hueangle/60-math.floor(hueangle/60) --This is a sawtooth function if hueangle>=0 and hueangle<60 then R,G,B=1,huefraction,0 elseif hueangle>=60 and hueangle<120 then R,G,B=1-huefraction,1,0 elseif hueangle>=120 and hueangle<180 then R,G,B=0,1,huefraction elseif hueangle>=180 and hueangle<240 then R,G,B=0,1-huefraction,1 elseif hueangle>=240 and hueangle<300 then R,G,B=huefraction,0,1 elseif hueangle>=300 and hueangle<360 then R,G,B=1,0,1-huefraction end R,G,B=math.floor(0xFF*R),math.floor(0xFF*G),math.floor(0xFF*B) return {R,G,B,alpha} --Output is in table form. end --############################################################################# --############################################################################# local BestTable= false local function CalcNow() for i,v in pairs(addresses) do addresses[i]=detrend(addresses[i]) addresses[i]=normalize(addresses[i]) end X=detrend(X) X=normalize(X) for i,v in pairs(addresses) do costhetasq[i]=dotproduct(X,addresses[i])^2 end BestTable=selectionsort(costhetasq,20) for i=1,#BestTable do BestTable[i][3]=memory.readbyte(BestTable[i][1]) end end local function GetData(UserInput) for i,v in pairs(addresses) do addresses[i][emu.framecount()]=memory.readbyte(i) end X[emu.framecount()]=UserInput end local function NullFn() end -- The "Do Nothing" function. --############################################################################# --############################################################################# --***************************************************************************** local function InBox(Lf,Up,Rt,Dn , x,y) --***************************************************************************** -- Just seeing if the chosen pixel resides within the chosen boundaries. -- Does not like it if the "box" you specify has reversed x- or y-axis. return (x >= Lf) and (x <= Rt) and (y >= Up) and (y <= Dn) end --***************************************************************************** local Draw= {} --***************************************************************************** --Coordinates is the top-left pixel of the 3x5 digit. --Used for drawing compact, colored numbers. local Px,Li= gui.pixel, gui.line Draw[0]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ### end Draw[1]= function(x,y,c) -- # Li(x ,y+4,x+2,y+4,c)-- ## Li(x+1,y ,x+1,y+3,c)-- # Px(x ,y+1,c) -- # end -- ### Draw[2]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+3,x+2,y+1,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[3]= function(x,y,c) -- ### Li(x ,y ,x+1,y ,c)-- # Li(x ,y+2,x+1,y+2,c)-- ### Li(x ,y+4,x+1,y+4,c)-- # Li(x+2,y ,x+2,y+4,c)-- ### end Draw[4]= function(x,y,c) -- # # Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y+2,c) -- # end -- # Draw[5]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[6]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x ,y+4,c)-- ### Li(x+2,y+2,x+2,y+4,c)-- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end -- ### Draw[7]= function(x,y,c) -- # Li(x ,y ,x+1,y ,c)-- ## Li(x+2,y ,x+1,y+4,c)-- # end -- # Draw[8]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y ,c) -- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end Draw[9]= function(x,y,c) -- ### Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x+1,y ,c) -- ### Px(x+1,y+2,c) end Draw[10]=function(x,y,c) -- # Li(x ,y+1,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+4,c)-- # # Px(x+1,y ,c) -- ### Px(x+1,y+3,c) -- # # end Draw[11]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+1,y ,x+2,y+1,c)-- ## Li(x+1,y+4,x+2,y+3,c)-- # # Px(x+1,y+2,c) -- ## end Draw[12]=function(x,y,c) -- # Li(x ,y+1,x ,y+3,c)-- # # Li(x+1,y ,x+2,y+1,c)-- # Li(x+1,y+4,x+2,y+3,c)-- # # end -- # Draw[13]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+3,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ## end Draw[14]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Li(x+1,y+4,x+2,y+4,c)-- # Px(x+1,y+2,c) -- ### end Draw[15]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Px(x+1,y+2,c) -- # end -- # --***************************************************************************** local function __DN_AnyBase(right, y, Number, c, bkgnd, div) --***************************************************************************** -- Works with any base from 2 to 16. Paints the input number. -- Returns the x position where it would paint another digit. -- It only works with integers. Rounds fractions toward zero. if div < 2 then return end -- Prevents the function from never returning. local Digit= {} local Negative= false if Number < 0 then Number= -Number Negative= true end Number= math.floor(Number) c= c or "white" bkgnd= bkgnd or "clear" local i= 0 if Number < 1 then Digit[1]= 0 i= 1 end while (Number >= 1) do i= i+1 Digit[i]= Number % div Number= math.floor(Number/div) end if Negative then i= i+1 end local x= right - i*4 gui.box(x+1, y-1, right+1, y+5,bkgnd,bkgnd) i= 1 while Draw[Digit[i]] do Draw[Digit[i]](right-2,y,c) right= right-4 i=i+1 end if Negative then gui.line(right, y+2,right-2,y+2,c) right= right-4 end return right end --############################################################################# --############################################################################# --Let's construct buttons! Yay! local Flag_DrawRainbow= false -- Bah, hate flags. Any workarounds? local ValFindFn, MoveMe FRK_NoAuto= true dofile("YummyButton.lua") --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local InputBar= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= 5, y1= 15, x2= 14, y2= 99, MBC= 0x00FF00FF, MFC= 0, SBC= 0x7FFF7FFF, SFC= 0, ------------------------------------------------------------------------------- fn= function(B) ------------------------------------------------------------------------------- if keys.shift then return MoveMe end return ValFindFn end } local function rt(B) return B.tag end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxUp= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y1-15, x2= InputBar.x2+5, y2= InputBar.y1+5, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxUp.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, -InputBar.y1) --Don't put beyond screen edge! DeltaY= math.min(DeltaY, InputBar.y2-InputBar.y1-10) InputBar.y1= InputBar.y1+DeltaY StretchBoxUp.y1= StretchBoxUp.y1+DeltaY StretchBoxUp.y2= StretchBoxUp.y2+DeltaY end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxDn= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y2-5, x2= InputBar.x2+5, y2= InputBar.y2+15, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxDn.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, InputBar.y1-InputBar.y2+10) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) --Screen edge InputBar.y2= InputBar.y2+DeltaY StretchBoxDn.y1= StretchBoxDn.y1+DeltaY StretchBoxDn.y2= StretchBoxDn.y2+DeltaY end --***************************************************************************** ValFindFn= function() --***************************************************************************** Flag_DrawRainbow= true end --***************************************************************************** MoveMe= function() --***************************************************************************** local DeltaX= keys.xmouse - lastkeys.xmouse local DeltaY= keys.ymouse - lastkeys.ymouse DeltaX= math.min(DeltaX, SCRRIGHT - InputBar.x2) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) DeltaX= math.max(DeltaX, -InputBar.x1) DeltaY= math.max(DeltaY, -InputBar.y1) InputBar:move(InputBar.x1+DeltaX, InputBar.y1+DeltaY) StretchBoxUp:move(StretchBoxUp.x1+DeltaX, StretchBoxUp.y1+DeltaY) StretchBoxDn:move(StretchBoxDn.x1+DeltaX, StretchBoxDn.y1+DeltaY) end ------------------------------------------------------------------------------- InputBar.GetVal= function(B,k) ------------------------------------------------------------------------------- local h= B.y2 - B.y1 local n= k.ymouse - B.y1 return (1 - n/h) * 1024 -- The multiplication is fairly arbitrary end ------------------------------------------------------------------------------- InputBar.DrawSel= function(B) --Override standard button code ------------------------------------------------------------------------------- gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) -- May as well box ourself Flag_DrawRainbow= true -- Would rather make only one call, not thousands. end ------------------------------------------------------------------------------- InputBar.DrawRainbow= function(B,y) ------------------------------------------------------------------------------- y=B.y1*boolto10(y<=B.y1) + y*boolto10(B.y1<y and y<=B.y2) + B.y2*boolto10(B.y2<y) for i=y,B.y2 do local hueangle=240 - 240/(B.y1-B.y2)*(i-B.y2) local RGBTable=huetoRGB(hueangle,0x7F) gui.line(B.x1,i,B.x2,i,RGBTable) end gui.line(B.x1,y,B.x2,y,"white") end ------------------------------------------------------------------------------- InputBar.MouseHit= function(B,k) ------------------------------------------------------------------------------- if InBox(B.x1,B.y1,B.x2,B.y2, k.xmouse,k.ymouse) then return InputBar:GetVal(k) end return nil end --############################################################################# --############################################################################# local ShowTable= false --***************************************************************************** local function PaintStat(T,y) --***************************************************************************** gui.box(2,y-1,70,y+5,0x000000FF,0x000000FF) local temp= T[1] -- Address for i= 0, 3 do local n= bit.band(bit.rshift(temp,i*4),0xF) Draw[n](16-4*i, y, -1) end temp= T[2]*10000 -- Certainty local color= "red" if temp >= 900 then color= "white" elseif temp >= 800 then color= "green" elseif temp >= 700 then color= "yellow" end __DN_AnyBase(44, y, temp, color, 0, 10) temp= memory.readbyte(T[1]) -- Memory value __DN_AnyBase(68, y, temp, "white", 0, 10) end --############################################################################# --############################################################################# --Keyboard (and indirectly, location where mouse is stored) --local keys, lastkeys= input.get(), input.get() --local function UpdateKeys() lastkeys= keys; keys= input.get() end --local function Press(k) return keys[k] and (not lastkeys[k]) end -- Functions are supplied by YummyButtons.lua. Neato. local KF= {} --***************************************************************************** local function KeyFunctions() --***************************************************************************** for key,v in pairs(keys) do if not lastkeys[key] then if KF[key] then KF[key]() end end end end ------------------------------------------------------------------------------- KF["space"]= function() -- The CALCULATE NOW function. ------------------------------------------------------------------------------- CalcNow() end ------------------------------------------------------------------------------- KF["home"]= function() -- Display toggle ------------------------------------------------------------------------------- ShowTable= not ShowTable end --############################################################################# --############################################################################# local Val= nil --***************************************************************************** local function ControlHUD() --***************************************************************************** gui.box(L,U,R,D,0,0x00FF00FF) -- See if we click the all-important box if InBox(L,U,R,D , keys.xmouse , keys.ymouse) then gui.line(L,keys.ymouse,R,keys.ymouse, -1) local h= D - U -- Get height of box local n= (keys.ymouse - U) -- Figure out where mouse is relative to box Val= (1 - n/h) * 999 -- Linear scoring of mouse position. gui.text(200,210,math.floor(Val)) else Val= nil end if keys.shift and keys.leftclick then L= L + keys.xmouse - lastkeys.xmouse R= R + keys.xmouse - lastkeys.xmouse U= U + keys.ymouse - lastkeys.ymouse D= D + keys.ymouse - lastkeys.ymouse end end local HoldFn --***************************************************************************** local function HandleGui() --***************************************************************************** --Main handler. Things to do, yes. local Fn= _FRK_Btn.HandleBtns() HoldFn= Fn or HoldFn if not keys.leftclick then HoldFn= nil end if HoldFn then HoldFn() end KeyFunctions() -- Maybe display list of results. This is only a glorified print() so far. -- There are planned alterations to do... if ShowTable and BestTable then for i= 1, #BestTable do PaintStat(BestTable[i],9*i) end end -- Paint the rainbow! if Flag_DrawRainbow then InputBar:DrawRainbow(keys.ymouse) Flag_DrawRainbow= false end end gui.register(HandleGui) --***************************************************************************** local function CheckImmediateInput() --***************************************************************************** local ImmediateInput= input.get() if ImmediateInput.leftclick and not ImmediateInput.shift then return InputBar:MouseHit(ImmediateInput) end end --***************************************************************************** local function HandleBefore() --***************************************************************************** --Handle frame-advancey stuff. local Val= CheckImmediateInput() if Val then GetData(Val) end end emu.registerbefore(HandleBefore)
A little extra thanks Although I have tried to emphasize that this is a joint project, I would still like to extend my thanks to FatRatKnight, who kindly volunteered his time to create the GUI. I do not have the programming know-how to efficiently make a GUI, so this project would have gone nowhere without him. Even though I am in charge of the technical aspects of the program, I still feel FatRatKnight has created most of its true "content". If anything in the scripts seems to be programmed elegantly, chances are good that FatRatKnight wrote it.
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
Moderators: This is not just a copy of the first post. I am including this code here as a record of earlier versions of the script. As the script is updated, I will add their code to this topic as well as editing the original post. I will delete this message after we create our next version of the script. Download Version 1.0 here! Version 1.0 Current features • A vertical bar to accept user input. Clicking the bar while frame advancing generates the input vector. • Shift-clicking to move the bar. • Resize boxes above and below the bar. • GUI output that displays top-candidate RAM addresses, their "score" (cos2(theta)), and their current value. • Cross-compatibility with several emulators. The script has been tested to work in VBA, FCEUX, and SNES9x. It is probably also compatible with Gens. • Excluded mirror values. Many RAM addresses have mirrored values. Including them wastes processing power and clutters up the GUI output. • Pretty rainbow colors in the input bar. (Does not work in FCEUX.) Features to be implemented Because FatRatKnight and I are working on different aspects of the script, I will split the improvements into technical and GUI categories. Technical improvements: • Pre-filtering. This currently falls under two categories: - Change pre-filtering. This would be similar to the default RAM Search's change count feature. It would allow the user to exclude addresses that have or haven't changed, according to what they expect from the address they're searching for. - Dot product pre-filtering. This would work exactly like the program's basic function except that addresses that aren't a sufficiently good match would be strictly excluded from future searches. This could be set by a number of different methods: keep the top x% of matches, keep the top n matches, or keep only matches whose score is above a certain threshold. • Downsampling. This means recording the RAM addresses only once every n frames. • Derivatives. Sometimes you do not know whether the value you are looking for is a function or its derivative. An example of this (and one of the things that inspired me to work on this script) is the hilly terrain in the racing game Skitchin': Is it stored as a height for graphical purposes or as a slope, which affects the player's acceleration directly? • Time lag. Sometimes there is a delay between what the user sees on the screen and what the RAM address is doing internally. In these instances, we want to compare the two vectors offset by some time delay (with a normalization factor thrown in). This is known as the cross-correlation. It is very computationally intensive, however, and cannot reasonably be implemented without pre-filtering. Also, the cross-correlation is most quickly computed by taking the discrete Fourier transform of the vectors, although it may only be faster to do so if there is an especially large time lag. • Words and other series of bytes. Sometimes we're not looking for a single byte. Endianness should also be considered. • Wrap-around. For periodic variables (e.g., the position of fire bars in Super Mario Bros. or the progression of the music), the value can jump from at or near 255 back to 0 (or vice versa). The user would have to set a threshold below which wrap-around would be applied. • Signed values. Currently, all values are presumed unsigned. • Expanded compatibility. This script only works with 8- and 16-bit consoles, as we do not yet know the memory map of other consoles. • An improved sorting algorithm. For ease of programming and getting this script published promptly, an insertion sort was implemented. This should be changed to a merge sort or other sort that executes in O(n*log(n)). GUI improvements: • Graphical tie-in of the above-mentioned improvements. • Options to change the vertical input bar into a horizontal bar. A circle could also be used. • A menu system that will complement keyboard shortcuts for features. • Arrows signifying the resize buttons. • Context-sensitive cursor icons. • Text boxes for user input. • Customizable output, including less or more information and fewer or more matches. For long lists of matches, the user should also be able to scroll through results. • Continuing to gather user input after the cursor leaves the bar. We also wish to further break the script up into smaller functions for ease of editing. The scripts Download BasicFunctions.lua
Language: lua

local function boolto10(boole) if boole then return 1 else return 0 end end
Download VectorOps.lua
Language: lua

local function getaverage(vector) local total=0 local n=0 for k,v in pairs(vector) do total=total+v n=n+1 end local average=total/n return average end local function detrend(vector) local average=getaverage(vector) for k,v in pairs(vector) do vector[k]=v-average end return vector end local function dotproduct(v1,v2) local total=0 for k,v in pairs(v1) do total=total+v*v2[k] end return total end local function getmag(vector) local magsquared=dotproduct(vector,vector) local magnitude=math.sqrt(magsquared) return magnitude end local function normalize(vector) local magnitude=getmag(vector) if magnitude>0 then for k,v in pairs(vector) do vector[k]=v/magnitude end end return vector end
Download Sorts.lua
Language: lua

--This function executes a selection sort on a vector, finding the best N values and organizing them into a table. --Note that the computation time is O(n^2). For efficiency, consider merge sort, whose speed is O(n*log(n)). --The advantage of selection sort is that it's easy to program and is probably still quite efficient as long as N is small. local function selectionsort(vector,N) local sorted={} for i=1,N do sorted[i]={-1,0} for k,v in pairs(vector) do if v>sorted[i][2] then sorted[i]={k,v} end end vector[sorted[i][1]]=0 end return sorted end --Insertion sort provided by FatRatKnight. SortedTable={} function InsertVal(v) local n=#SortedTable+1 while n>1 do if v<SortedTable[n-1] then break end SortedTable[n]=SortedTable[n-1] n=n-1 end SortedTable[n]=v end
Download YummyButton.lua
Language: lua

--[[ Welcome to my Buttons script! To start, put in the following lines: require("thisScript") NewBtn{x= 1, y= 1, fn= function() ((Various code here)) end} And all of a sudden, you have this bluish button on the top left corner of the emulator window, once you run the script. Use the mouse to click the button. The code you put in will happen. Now, there are ways to configure these buttons to your desires. For one thing, make as many NewBtn objects as you like. This script will keep track of 'em. For another, change that x and y up there if you don't like the NewBtn being stuck in the top-left corner. Finally, there's various options you can use to tweak your NewBtn desires. REQUIRED: x - X position. Where is the button horizontally? y - Y position. Where do we put it vertically? fn - Function. The button had better do something!! Optional: text - Cute text to display on button. May size button automatically. x1 - Identical to the otherwise required x. If both present, x has priority x2 - The right side of the button y1 - Identical to y. y1 can take y's place, but if you make both, y is used y2 - The bottom side of the button width - Width of button. Would interfere with x2, but x2 takes priority. height- Height of button. Use if you don't feel like using y2. color - General color scheme of the button. This affects all aspects of color. MBC - Main Border Color. "Main" is when your mouse isn't over the button. MFC - Main Fill Color. ... I use "Main" for lack of better words... SBC - Sub Border Color. "Sub" is used when you're mousing over the button. SFC - Sub Fill Color. Anyone have better words for me? tag - Spare variable. Included functions: draw - Draws the button. Don't bother using it, it's automatically called. recolor- Modifies the color scheme. Takes in one value depicting base color. move - Moves the button so that the top-left corner matches the x,y input. While it is packaged for simple use as is, the more savvy programmers may want to read on. I pass the button object to the clicked button function as the only parameter. I can't think of any real way of passing variables you want for it, but I added the "tag" variable for this purpose. Technically, after the button is constructed, you can add new parameters to the button object itself, but the "tag" variable seemed like a convenient thing to add to the constructor. In addition, I put the HandleBtns function in gui.register by default, but go ahead and call it in another function and replace the gui.register callback. This function will return whatever the button function returns, so if it is in your interest to have the button function return something, it will do so through _FRK_Btn.HandleBtns. The NewBtn constructor returns the button object it created as a table for you to mess around with, should you have a reason to. Maybe you want to move it around or change its colors, who knows. Finally, _FRK_Btn.MB holds all my buttons. You can, in the middle of creating new buttons, set another variable (MB2, perhaps?), set _FRK_Btn.MB = {}, then create more buttons. You'll have two different sets of buttons which you can switch between, but you'll have to make your own functions to do the switch. But if you're not experienced in lua programming, none of the previous four paragraphs are of any interest to you. The simple use of these buttons are good enough for many uses. ]]-- _FRK_Btn= {} local EMUCHWID= 6 local EMUCHHGT= 8 if stylus then EMUCHWID= 6; EMUCHHGT= 8 elseif snes9x then EMUCHWID= 4; EMUCHHGT= 6 elseif VBA then EMUCHWID= 4; EMUCHHGT= 6 end _FRK_Btn.ClickType= "leftclick" _FRK_Btn.MB= {} keys, lastkeys= {}, {} function UpdateKeys() lastkeys= keys; keys= input.get() end function Press(k) return keys[k] and not lastkeys[k] end --***************************************************************************** function _FRK_Btn.FindBtn() --***************************************************************************** if (keys.xmouse == lastkeys.xmouse) and (keys.ymouse == lastkeys.ymouse) then return end local X,Y= keys.xmouse, keys.ymouse for i= 1, #_FRK_Btn.MB do -- linear scan, darn it! local n= _FRK_Btn.MB[i] if (Y >= n.y1) and (Y <= n.y2) and (X >= n.x1) and (X <= n.x2) then return i end end return -1 end --***************************************************************************** function _FRK_Btn.ClickBtn(id) --***************************************************************************** if id and _FRK_Btn.MB[id] then return _FRK_Btn.MB[id]:fn() end end --***************************************************************************** function _FRK_Btn.DrawUnsel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.MFC, B.MBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.DrawSel(B) --***************************************************************************** gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) gui.text(B.xt, B.yt, B.text) end --***************************************************************************** function _FRK_Btn.ShowBtns() --***************************************************************************** B= _FRK_Btn.MB for i= 1, #B do B[i]:draw() end end --***************************************************************************** function _FRK_Btn.Recolor(B,color) --***************************************************************************** -- Does not like strings. Still will accept them, however. local t= type(color) if t == "string" then -- Uh, no. No tricky color stuff. Sorry. B.MBC= Cns.color B.SBC= Cns.color B.MFC= 0x00000040 B.SFC= 0xC0C0C0C0 --Oh, not a string! Better be table or number, please. else if t == "table" then color= 0 local q= color.r or color[1] if q then color= bit.lshift(q,24) end q= color.g or color[2] if q then color= bit.bor(color,bit.lshift(q,16)) end q= color.b or color[3] if q then color= bit.bor(color,bit.lshift(q, 8)) end q= color.a or color[4] if q then color= bit.bor(color,q) else color= bit.bor(color,0xFF) end end B.MBC= color B.MFC= bit.rshift(bit.band(color,0xFEFEFEFE),1) B.SBC= bit.bor(color+bit.band(0xFFFFFFFF-color,0xFEFEFEFE)/2,0xFF) B.SFC= color end end --***************************************************************************** function _FRK_Btn.Move(B, x,y) --***************************************************************************** -- Moves the whole button for you! if x then local XDiff= x - B.x1 B.x1= x B.x2= B.x2 + XDiff B.xt= B.xt + XDiff end if y then local YDiff= y - B.y1 B.y1= y B.y2= B.y2 + YDiff B.yt= B.yt + YDiff end end --***************************************************************************** function _FRK_Btn.NewBtn(Cns) --***************************************************************************** -- the button constructor! --Insert universal function pointers while creating a new table. local NewTbl= { draw= _FRK_Btn.DrawUnsel, recolor= _FRK_Btn.Recolor, move= _FRK_Btn.Move } --Insert presumed information. NewTbl.fn= Cns.fn NewTbl.x1= Cns.x or Cns.x1 NewTbl.y1= Cns.y or Cns.y1 NewTbl.tag= Cns.tag --Check if user defined the right side. if Cns.x2 then NewTbl.x2= math.max(NewTbl.x1,Cns.x2) NewTbl.x1= math.min(NewTbl.x1,Cns.x2) elseif Cns.width then NewTbl.x2= NewTbl.x1 + Cns.width end --Check if user defined the bottom side. if Cns.y2 then NewTbl.y2= math.max(NewTbl.y1,Cns.y2) NewTbl.y1= math.min(NewTbl.y1,Cns.y2) elseif Cns.height then NewTbl.y2= NewTbl.y1 + Cns.height end --Look for text. Do tricky stuff based on where to put the darn text or --how to resize the button. if Cns.text then NewTbl.text= tostring(Cns.text) local len= #NewTbl.text * EMUCHWID if NewTbl.x2 then NewTbl.xt= math.floor((NewTbl.x1 + NewTbl.x2 - len)/2) else NewTbl.x2= NewTbl.x1 + len + 3 NewTbl.xt= NewTbl.x1 + 2 end if NewTbl.y2 then NewTbl.yt= math.floor((NewTbl.y1 + NewTbl.y2 - 8)/2) else NewTbl.y2= NewTbl.y1 + EMUCHHGT + 4 NewTbl.yt= NewTbl.y1 + 2 end else NewTbl.text= "" NewTbl.xt= NewTbl.x1 NewTbl.yt= NewTbl.y1 if not NewTbl.x2 then NewTbl.x2= NewTbl.x1 + 8 end if not NewTbl.y2 then NewTbl.y2= NewTbl.y1 + 8 end end --Mess with colors! if Cns.color then NewTbl:recolor(Cns.color) else NewTbl.MBC= Cns.MBC or 0x0020FFC0 NewTbl.MFC= Cns.MFC or 0x00000040 NewTbl.SBC= Cns.SBC or 0x0080FFFF NewTbl.SFC= Cns.SFC or 0x2000C0C0 end --Finally add the button to our MB array. local index= #(_FRK_Btn.MB)+1 _FRK_Btn.MB[index]= NewTbl --Here's the button object in case you want to handle it yourself. return NewTbl end local ID= -1 --***************************************************************************** function _FRK_Btn.HandleBtns() --***************************************************************************** UpdateKeys() local id= _FRK_Btn.FindBtn() if id then if ID ~= -1 then _FRK_Btn.MB[ID].draw= _FRK_Btn.DrawUnsel end if id ~= -1 then _FRK_Btn.MB[id].draw= _FRK_Btn.DrawSel end ID= id end _FRK_Btn.ShowBtns() if Press(_FRK_Btn.ClickType) and (ID ~= -1) then return _FRK_Btn.ClickBtn(ID) end end --============================================================================= if not FRK_NoAuto then --============================================================================= gui.register(_FRK_Btn.HandleBtns) NewBtn= _FRK_Btn.NewBtn end
Download SmartRAMSearch.lua
Language: lua

dofile("BasicFunctions.lua") dofile("VectorOps.lua") dofile("Sorts.lua") --Certain parameters are emulator-specific. --In particular, we need to know the range of relevant RAM addresses and place the bar where it is visible within the window. local function getsystem() if FCEU then --NES byterange={ {0x0000,0x07FF}, {0x2000,0x2008}, {0x4000,0x7FFF}} L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=239,255 elseif vba then --GBx byterange={ {0x8000,0xDFFF}, {0xFF80,0xFFFF}} L,U,R,D= 140,10,149,109 SCRBOTTOM,SCRRIGHT=143,159 elseif snes9x then --SNES byterange={ {0x7E0000,0x7FFFFF}} L,U,R,D= 180,50,189,149 elseif gens then --Genesis (and similar) byterange={ {0x000000,0x3FFFFF}} elseif stylus then --DS print("Compatibility pending...") else --Emulator not recognized print("Error: What emulator are you using?") end end --Initialize this stuff... getsystem() local mindex=0 range={} for i=1,#byterange do for j=byterange[i][1],byterange[i][2] do range[mindex]=j mindex=mindex+1 end end --range=0x7FFF addresses={} for i = 1,#range do addresses[range[i]]={} end X={} costhetasq={} --Converts a hue angle to RGB. Normalized to the range [0,255]. local function huetoRGB(hueangle,alpha) local R,G,B=0,0,0 if not(alpha) then alpha=0x7F end --Alpha defaults to 0x7F. hueangle=hueangle%360 local huefraction=hueangle/60-math.floor(hueangle/60) --This is a sawtooth function if hueangle>=0 and hueangle<60 then R,G,B=1,huefraction,0 elseif hueangle>=60 and hueangle<120 then R,G,B=1-huefraction,1,0 elseif hueangle>=120 and hueangle<180 then R,G,B=0,1,huefraction elseif hueangle>=180 and hueangle<240 then R,G,B=0,1-huefraction,1 elseif hueangle>=240 and hueangle<300 then R,G,B=huefraction,0,1 elseif hueangle>=300 and hueangle<360 then R,G,B=1,0,1-huefraction end R,G,B=math.floor(0xFF*R),math.floor(0xFF*G),math.floor(0xFF*B) return {R,G,B,alpha} --Output is in table form. end --############################################################################# --############################################################################# local BestTable= false local function CalcNow() for i,v in pairs(addresses) do addresses[i]=detrend(addresses[i]) addresses[i]=normalize(addresses[i]) end X=detrend(X) X=normalize(X) for i,v in pairs(addresses) do costhetasq[i]=dotproduct(X,addresses[i])^2 end BestTable=selectionsort(costhetasq,20) for i=1,#BestTable do BestTable[i][3]=memory.readbyte(BestTable[i][1]) end end local function GetData(UserInput) for i,v in pairs(addresses) do addresses[i][emu.framecount()]=memory.readbyte(i) end X[emu.framecount()]=UserInput end local function NullFn() end -- The "Do Nothing" function. --############################################################################# --############################################################################# --***************************************************************************** local function InBox(Lf,Up,Rt,Dn , x,y) --***************************************************************************** -- Just seeing if the chosen pixel resides within the chosen boundaries. -- Does not like it if the "box" you specify has reversed x- or y-axis. return (x >= Lf) and (x <= Rt) and (y >= Up) and (y <= Dn) end --***************************************************************************** local Draw= {} --***************************************************************************** --Coordinates is the top-left pixel of the 3x5 digit. --Used for drawing compact, colored numbers. local Px,Li= gui.pixel, gui.line Draw[0]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ### end Draw[1]= function(x,y,c) -- # Li(x ,y+4,x+2,y+4,c)-- ## Li(x+1,y ,x+1,y+3,c)-- # Px(x ,y+1,c) -- # end -- ### Draw[2]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+3,x+2,y+1,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[3]= function(x,y,c) -- ### Li(x ,y ,x+1,y ,c)-- # Li(x ,y+2,x+1,y+2,c)-- ### Li(x ,y+4,x+1,y+4,c)-- # Li(x+2,y ,x+2,y+4,c)-- ### end Draw[4]= function(x,y,c) -- # # Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y+2,c) -- # end -- # Draw[5]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x ,y+2,c) -- ### Px(x+2,y+2,c) end Draw[6]= function(x,y,c) -- ### Li(x ,y ,x+2,y ,c)-- # Li(x ,y+1,x ,y+4,c)-- ### Li(x+2,y+2,x+2,y+4,c)-- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end -- ### Draw[7]= function(x,y,c) -- # Li(x ,y ,x+1,y ,c)-- ## Li(x+2,y ,x+1,y+4,c)-- # end -- # Draw[8]= function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y ,x+2,y+4,c)-- ### Px(x+1,y ,c) -- # # Px(x+1,y+2,c) -- ### Px(x+1,y+4,c) end Draw[9]= function(x,y,c) -- ### Li(x ,y ,x ,y+2,c)-- # # Li(x+2,y ,x+2,y+3,c)-- ### Li(x ,y+4,x+2,y+4,c)-- # Px(x+1,y ,c) -- ### Px(x+1,y+2,c) end Draw[10]=function(x,y,c) -- # Li(x ,y+1,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+4,c)-- # # Px(x+1,y ,c) -- ### Px(x+1,y+3,c) -- # # end Draw[11]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+1,y ,x+2,y+1,c)-- ## Li(x+1,y+4,x+2,y+3,c)-- # # Px(x+1,y+2,c) -- ## end Draw[12]=function(x,y,c) -- # Li(x ,y+1,x ,y+3,c)-- # # Li(x+1,y ,x+2,y+1,c)-- # Li(x+1,y+4,x+2,y+3,c)-- # # end -- # Draw[13]=function(x,y,c) -- ## Li(x ,y ,x ,y+4,c)-- # # Li(x+2,y+1,x+2,y+3,c)-- # # Px(x+1,y ,c) -- # # Px(x+1,y+4,c) -- ## end Draw[14]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Li(x+1,y+4,x+2,y+4,c)-- # Px(x+1,y+2,c) -- ### end Draw[15]=function(x,y,c) -- ### Li(x ,y ,x ,y+4,c)-- # Li(x+1,y ,x+2,y ,c)-- ## Px(x+1,y+2,c) -- # end -- # --***************************************************************************** local function __DN_AnyBase(right, y, Number, c, bkgnd, div) --***************************************************************************** -- Works with any base from 2 to 16. Paints the input number. -- Returns the x position where it would paint another digit. -- It only works with integers. Rounds fractions toward zero. if div < 2 then return end -- Prevents the function from never returning. local Digit= {} local Negative= false if Number < 0 then Number= -Number Negative= true end Number= math.floor(Number) c= c or "white" bkgnd= bkgnd or "clear" local i= 0 if Number < 1 then Digit[1]= 0 i= 1 end while (Number >= 1) do i= i+1 Digit[i]= Number % div Number= math.floor(Number/div) end if Negative then i= i+1 end local x= right - i*4 gui.box(x+1, y-1, right+1, y+5,bkgnd,bkgnd) i= 1 while Draw[Digit[i]] do Draw[Digit[i]](right-2,y,c) right= right-4 i=i+1 end if Negative then gui.line(right, y+2,right-2,y+2,c) right= right-4 end return right end --############################################################################# --############################################################################# --Let's construct buttons! Yay! local Flag_DrawRainbow= false -- Bah, hate flags. Any workarounds? local ValFindFn, MoveMe FRK_NoAuto= true dofile("YummyButton.lua") --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local InputBar= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= 5, y1= 15, x2= 14, y2= 99, MBC= 0x00FF00FF, MFC= 0, SBC= 0x7FFF7FFF, SFC= 0, ------------------------------------------------------------------------------- fn= function(B) ------------------------------------------------------------------------------- if keys.shift then return MoveMe end return ValFindFn end } local function rt(B) return B.tag end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxUp= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y1-15, x2= InputBar.x2+5, y2= InputBar.y1+5, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxUp.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, -InputBar.y1) --Don't put beyond screen edge! DeltaY= math.min(DeltaY, InputBar.y2-InputBar.y1-10) InputBar.y1= InputBar.y1+DeltaY StretchBoxUp.y1= StretchBoxUp.y1+DeltaY StretchBoxUp.y2= StretchBoxUp.y2+DeltaY end --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local StretchBoxDn= _FRK_Btn.NewBtn{ --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x1= InputBar.x1-5, y1= InputBar.y2-5, x2= InputBar.x2+5, y2= InputBar.y2+15, MBC= "#FFFFFF40", MFC= 0, SBC= "#FFFFFFC0", SFC= 0, fn= function(B) return B.tag end } ------------------------------------------------------------------------------- StretchBoxDn.tag= function() ------------------------------------------------------------------------------- local DeltaY= keys.ymouse - lastkeys.ymouse DeltaY= math.max(DeltaY, InputBar.y1-InputBar.y2+10) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) --Screen edge InputBar.y2= InputBar.y2+DeltaY StretchBoxDn.y1= StretchBoxDn.y1+DeltaY StretchBoxDn.y2= StretchBoxDn.y2+DeltaY end --***************************************************************************** ValFindFn= function() --***************************************************************************** Flag_DrawRainbow= true end --***************************************************************************** MoveMe= function() --***************************************************************************** local DeltaX= keys.xmouse - lastkeys.xmouse local DeltaY= keys.ymouse - lastkeys.ymouse DeltaX= math.min(DeltaX, SCRRIGHT - InputBar.x2) DeltaY= math.min(DeltaY, SCRBOTTOM - InputBar.y2) DeltaX= math.max(DeltaX, -InputBar.x1) DeltaY= math.max(DeltaY, -InputBar.y1) InputBar:move(InputBar.x1+DeltaX, InputBar.y1+DeltaY) StretchBoxUp:move(StretchBoxUp.x1+DeltaX, StretchBoxUp.y1+DeltaY) StretchBoxDn:move(StretchBoxDn.x1+DeltaX, StretchBoxDn.y1+DeltaY) end ------------------------------------------------------------------------------- InputBar.GetVal= function(B,k) ------------------------------------------------------------------------------- local h= B.y2 - B.y1 local n= k.ymouse - B.y1 return (1 - n/h) * 1024 -- The multiplication is fairly arbitrary end ------------------------------------------------------------------------------- InputBar.DrawSel= function(B) --Override standard button code ------------------------------------------------------------------------------- gui.box(B.x1, B.y1, B.x2, B.y2, B.SFC, B.SBC) -- May as well box ourself Flag_DrawRainbow= true -- Would rather make only one call, not thousands. end ------------------------------------------------------------------------------- InputBar.DrawRainbow= function(B,y) ------------------------------------------------------------------------------- y=B.y1*boolto10(y<=B.y1) + y*boolto10(B.y1<y and y<=B.y2) + B.y2*boolto10(B.y2<y) for i=y,B.y2 do local hueangle=240 - 240/(B.y1-B.y2)*(i-B.y2) local RGBTable=huetoRGB(hueangle,0x7F) gui.line(B.x1,i,B.x2,i,RGBTable) end gui.line(B.x1,y,B.x2,y,"white") end ------------------------------------------------------------------------------- InputBar.MouseHit= function(B,k) ------------------------------------------------------------------------------- if InBox(B.x1,B.y1,B.x2,B.y2, k.xmouse,k.ymouse) then return InputBar:GetVal(k) end return nil end --############################################################################# --############################################################################# local ShowTable= false --***************************************************************************** local function PaintStat(T,y) --***************************************************************************** gui.box(2,y-1,70,y+5,0x000000FF,0x000000FF) local temp= T[1] -- Address for i= 0, 3 do local n= bit.band(bit.rshift(temp,i*4),0xF) Draw[n](16-4*i, y, -1) end temp= T[2]*10000 -- Certainty local color= "red" if temp >= 900 then color= "white" elseif temp >= 800 then color= "green" elseif temp >= 700 then color= "yellow" end __DN_AnyBase(44, y, temp, color, 0, 10) temp= memory.readbyte(T[1]) -- Memory value __DN_AnyBase(68, y, temp, "white", 0, 10) end --############################################################################# --############################################################################# --Keyboard (and indirectly, location where mouse is stored) --local keys, lastkeys= input.get(), input.get() --local function UpdateKeys() lastkeys= keys; keys= input.get() end --local function Press(k) return keys[k] and (not lastkeys[k]) end -- Functions are supplied by YummyButtons.lua. Neato. local KF= {} --***************************************************************************** local function KeyFunctions() --***************************************************************************** for key,v in pairs(keys) do if not lastkeys[key] then if KF[key] then KF[key]() end end end end ------------------------------------------------------------------------------- KF["space"]= function() -- The CALCULATE NOW function. ------------------------------------------------------------------------------- CalcNow() end ------------------------------------------------------------------------------- KF["home"]= function() -- Display toggle ------------------------------------------------------------------------------- ShowTable= not ShowTable end --############################################################################# --############################################################################# local Val= nil --***************************************************************************** local function ControlHUD() --***************************************************************************** gui.box(L,U,R,D,0,0x00FF00FF) -- See if we click the all-important box if InBox(L,U,R,D , keys.xmouse , keys.ymouse) then gui.line(L,keys.ymouse,R,keys.ymouse, -1) local h= D - U -- Get height of box local n= (keys.ymouse - U) -- Figure out where mouse is relative to box Val= (1 - n/h) * 999 -- Linear scoring of mouse position. gui.text(200,210,math.floor(Val)) else Val= nil end if keys.shift and keys.leftclick then L= L + keys.xmouse - lastkeys.xmouse R= R + keys.xmouse - lastkeys.xmouse U= U + keys.ymouse - lastkeys.ymouse D= D + keys.ymouse - lastkeys.ymouse end end local HoldFn --***************************************************************************** local function HandleGui() --***************************************************************************** --Main handler. Things to do, yes. local Fn= _FRK_Btn.HandleBtns() HoldFn= Fn or HoldFn if not keys.leftclick then HoldFn= nil end if HoldFn then HoldFn() end KeyFunctions() -- Maybe display list of results. This is only a glorified print() so far. -- There are planned alterations to do... if ShowTable and BestTable then for i= 1, #BestTable do PaintStat(BestTable[i],9*i) end end -- Paint the rainbow! if Flag_DrawRainbow then InputBar:DrawRainbow(keys.ymouse) Flag_DrawRainbow= false end end gui.register(HandleGui) --***************************************************************************** local function CheckImmediateInput() --***************************************************************************** local ImmediateInput= input.get() if ImmediateInput.leftclick and not ImmediateInput.shift then return InputBar:MouseHit(ImmediateInput) end end --***************************************************************************** local function HandleBefore() --***************************************************************************** --Handle frame-advancey stuff. local Val= CheckImmediateInput() if Val then GetData(Val) end end emu.registerbefore(HandleBefore)
Editor, Experienced Forum User, Skilled player (1176)
Joined: 9/27/2008
Posts: 1072
I'm pretty sure I still missed a few things. Feel free to report glitches! ... That is, if you want to try it out, anyway. Basically, run the script. A bar should show up. Click some spot on it and, with the left mouse button still held down, frame advance. Be sure to go up or down as you think the value you're looking for goes up or down as well. It does decent estimations, so no need to know exact values this way. The core was easy. The gui was messy. It's pretty processor intensive, and there's certainly a few things that still need to be done (I consider it to be mostly beta at this point). Still, it was indeed a joint project between us two, me and Bobo the King. So now you have confirmation, as if you needed it.
Editor, Experienced Forum User, Skilled player (1384)
Joined: 7/9/2010
Posts: 1312
Glitchy TAS of 2015NES TASer of 2014Speedy TAS of 2014NES TAS of 2013
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
Favorite animal: STOCK Gt(ROSA)26Sortm1.1(rtTA,EGFP)Nagy Grm7Tg(SMN2)89Ahmb Smn1tm1Msd Tg(SMN2*delta7)4299Ahmb Tg(tetO-SMN2,-luc)#aAhmb/J YouTube Twitch
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
TASeditor wrote:
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
You are correct. Finding Mario's height with this function is overkill-- I only mentioned it as a simple example. Try to think of a RAM value that isn't so obvious As for CPU-cycled RNGs, I'm afraid I don't understand the question.
Editor, Experienced Forum User, Skilled player (1384)
Joined: 7/9/2010
Posts: 1312
Glitchy TAS of 2015NES TASer of 2014Speedy TAS of 2014NES TAS of 2013
Bobo the King wrote:
TASeditor wrote:
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
You are correct. Finding Mario's height with this function is overkill-- I only mentioned it as a simple example. Try to think of a RAM value that isn't so obvious As for CPU-cycled RNGs, I'm afraid I don't understand the question.
I mean RNGs, that change value by a event e.g. enemy is shooting, so this means the value could be constant.
Favorite animal: STOCK Gt(ROSA)26Sortm1.1(rtTA,EGFP)Nagy Grm7Tg(SMN2)89Ahmb Smn1tm1Msd Tg(SMN2*delta7)4299Ahmb Tg(tetO-SMN2,-luc)#aAhmb/J YouTube Twitch
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
TASeditor wrote:
Bobo the King wrote:
TASeditor wrote:
Is this really needed? The example with Super Mario Bros. height position listen like a lot of work, compared with the normal RAM Search (just a few points to register by saying, with help of the graphical expression of the RAM Mario either moves up or down, value is greater or less.) Does it work for CPU-cycled RNGs, because it's not possible to find RAM Adresses, which are constant?
You are correct. Finding Mario's height with this function is overkill-- I only mentioned it as a simple example. Try to think of a RAM value that isn't so obvious As for CPU-cycled RNGs, I'm afraid I don't understand the question.
I mean RNGs, that change value by a event e.g. enemy is shooting, so this means the value could be constant.
It might be possible to find such an RNG with this script. You would hold the mouse at a certain level until an enemy fires (assuming you know what triggers the RNG change), then move it to an arbitrary point and advance a few more frames. I think the change counter in the default RAM search would also do the trick.
Editor, Experienced Forum User, Skilled player (1799)
Joined: 6/15/2005
Posts: 3153
Glitchy TAS of 2013Gameboy TAS of 2013PSX TASer of 2010
I got an error beginning with:
...\side\snes9x v1.43\smartramsearch\SmartRAMSearch.lua:1: module 'BasicFunctions.lua' not found: no field package.preload['BasicFunctions.lua'] no file '.\BasicFunctions\lua.lua' ...
Even though I have all the files ("BasicFunctions.lua", "SmartRAMSearch.lua", "Sorts.lua", "VectorOps.lua", "YummyButton.lua") in one folder labelled "smartramsearch". I tested both versions of Snes9x, as well as FCEUX 2.1.4 and VBA v22.
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
FractalFusion wrote:
I got an error beginning with:
...\side\snes9x v1.43\smartramsearch\SmartRAMSearch.lua:1: module 'BasicFunctions.lua' not found: no field package.preload['BasicFunctions.lua'] no file '.\BasicFunctions\lua.lua' ...
Even though I have all the files ("BasicFunctions.lua", "SmartRAMSearch.lua", "Sorts.lua", "VectorOps.lua", "YummyButton.lua") in one folder labelled "smartramsearch". I tested both versions of Snes9x, as well as FCEUX 2.1.4 and VBA v22.
Interesting. It worked on my computer just before I submitted it. You can delete the require("BasicFunctions.lua") statement at the top of the SmartRAMSearch.lua script and manually copy in the boolto10 function. Tell me if that gives you a new error with the second "require" statement. If so, just copy in the rest of the files manually. Any thoughts, FatRatKnight?
Editor, Experienced Forum User, Skilled player (1176)
Joined: 9/27/2008
Posts: 1072
Bobo the King wrote:
Any thoughts, FatRatKnight?
For whatever reason, the require keyword wasn't cooperative at one point when I was trying to get YummyButtons.lua in there, so I went with dofile instead. I actually don't know a lot of details about require, though. Just that loading a lua file is one of the things it can do.
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
The scripts have been updated to use "dofile" instead of "require". I still have no idea what the underlying problem is, since I have a copy of the script with "require" at the top running right now.
Active player, Experienced Forum User (287)
Joined: 2/28/2006
Posts: 2275
Location: Milky Way -> Earth -> Brazil
New systems TAS of 2009
This little video might help the people (like myself) who couldn't understand how the script works: http://www.youtube.com/watch?v=QFR4ee_u3SY
"Genuine self-esteem, however, consists not of causeless feelings, but of certain knowledge about yourself. It rests on the conviction that you — by your choices, effort and actions — have made yourself into the kind of person able to deal with reality. It is the conviction — based on the evidence of your own volitional functioning — that you are fundamentally able to succeed in life and, therefore, are deserving of that success." - Onkar Ghate
Bisqwit wrote:
Drama, too long, didn't read, lol.
Post subject: on-screen + compat. error
Experienced Forum User
Joined: 3/18/2006
Posts: 971
Location: Great Britain
Sorry for my stupid questions :D "Move the box to where you want it" Does this mean it will only work for things that are on-screen? Let's say for example that mario's 'timer' is very difficult to find using traditional ram searches. Could I use this tool to find the address? And how about critical hits in Final fantasy? byte range 0x000000,0x1FFFFF
Post subject: Re: on-screen + compat. error
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
antd wrote:
Sorry for my stupid questions :D "Move the box to where you want it" Does this mean it will only work for things that are on-screen? Let's say for example that mario's 'timer' is very difficult to find using traditional ram searches. Could I use this tool to find the address? And how about critical hits in Final fantasy? byte range 0x000000,0x1FFFFF
You can use this script to find any non-constant RAM address you want! The position and size of the bar have absolutely nothing to do with the program's results. I only used Mario jumping as an example because it is particularly easy to track pixel by pixel. (However, because it's so easy to see what's going on with Mario's height, it isn't particularly interesting to use the script.) Your screenshot had me worried for a moment, but then I noticed you were pointing out its incompatibility with pcsx-rr. You may manually insert the lines
Language: lua

elseif pcsx then --PlayStation byterange={ {0x000000,0x1FFFFF}}
into the SmartRAMSearch.lua code under the "getsystem" function to get you started. I'll also need to know the native resolution of the game. A brief check online indicates that the resolution varies from game to game, so you should also insert the lines
Language: lua

L,U,R,D= 180,50,189,149 SCRBOTTOM,SCRRIGHT=640,480
which may allow you to scroll the bar off the screen in some games, so be careful! I'll make all those changes myself and update the code and download link shortly. Edit: Changes complete! Be forewarned: 2 MB of RAM to be copied every frame is positively massive. Expect it to take about ten seconds to advance a single frame. Do not press the spacebar unless you're ready to press ctrl-alt-del as well. However, if you could confirm that the basic program is working properly, I'd much appreciate it. True compatibility with PCSX will come when we implement pre-filtering. I also could use the byte range for Genesis's RAM.
Experienced Forum User
Joined: 3/18/2006
Posts: 971
Location: Great Britain
Thanks. All features work, including displaying the values on screen, etc. But as you said, uses the ram! :p
Post subject: Re: on-screen + compat. error
marzojr
He/Him
Experienced Forum User, Experienced player (912)
Joined: 9/29/2008
Posts: 964
Location: 🇫🇷 France
Sega TASer of 2014Sega TAS of 2014Sega TASer of 2010Sega TAS of 2010
Bobo the King wrote:
I also could use the byte range for Genesis's RAM.
Genesis RAM goes from 0xFF0000 to 0xFFFFFF. With Sega CD, there is also "program RAM" at 0x20000 to 0x3FFFF and "work RAM" at 0x200000 to 0x23FFFF. I have no idea about the 32X.
Marzo Junior
Post subject: Re: on-screen + compat. error
Experienced Forum User, Player (73)
Joined: 8/5/2007
Posts: 860
marzojr wrote:
Bobo the King wrote:
I also could use the byte range for Genesis's RAM.
Genesis RAM goes from 0xFF0000 to 0xFFFFFF. With Sega CD, there is also "program RAM" at 0x20000 to 0x3FFFF and "work RAM" at 0x200000 to 0x23FFFF. I have no idea about the 32X.
Thanks, marzojr! Compatibility with Gens has now been implemented! It took entirely too long, however. Gens seems to be the black sheep of the emulators I've tested so far. It doesn't have the bit module pre-loaded, it doesn't have emu=gens (easy to fix, but still annoying), and it only checks its default directory for required lua scripts, not the directory of the parent script. As such, if you plan on using this script in Gens, copy all scripts to Gens's default directory, which should be the same directory that the emulator is in. For the bit module, I downloaded a Lua-native programmed bit module from here. This module doesn't have such extensive features as the official release (for example, its bit.band, bit.bor, and bit.bxor functions only take two arguments; I could fix it easily, but since we never use those functions with more than two arguments, I'll leave well enough alone), so it is only loaded if the emulator is Gens. I also noted some slowdown when displaying results. I don't know what's causing it off the top of my head. FatRatKnight is implementing his own custom font and display functions for standardization and because some emulators' Lua GUI functions look really ugly. For emulators like Gens and VBA, I think the default font looks decent, so it may be worth checking whether the slowdown can be stopped by using its native output. Finally, this affects nothing, but I will mention it for full disclosure. Because I have too many copies of these scripts floating around, sometimes changes are made to one copy but not the "official" copy. As such, if you have downloaded the script, you may have noticed that SmartRAMSearch.lua doesn't match the copy I presented in the forum post. The copy you have is the one without the functions broken up into other Lua scripts. In fact, if you do break up the functions into other Lua scripts, SmartRAMSearch.lua won't even run properly! That is because all those other scripts need to declare global functions, not local functions. I have fixed all this and now what you see is exactly what you get.