Joined: 2/5/2011
Posts: 9
When I first started with JPC-RR I used CheatEngine to look at values, but wondered how I could get those addresses CE found into a lua script JPC could understand. So I made a small tutorial with lots of pictures for newbs like me. If you have a better method please share it. Ok so for this example I'm using D/Generation and CheatEngine. Any game will work but I'm not familiar with things other than CheatEngine. Your mem hacker of choice will work if it has the same capabilities. First off we start the game and start CE. Attach CE to java.exe. For this example I'm going to be using the Y position of the main char. I'm going to assume you know how to find stuff in CE or whatever. If not, what I did was start with an unknown value, ran down the screen a few pixels and scanned for an increased value, repeated that a few times, then ran up the screen and scanned for decreased values. Here's what I came up with after enough iterations. 54 hits? That's too many. Well, we know the game is running in mode 13 320x200 because we're old and bitter, so surely the Y position of the player can't be over 200. Let's clamp the scan to "smaller than 200". (Jokes aside. if you can't eyeball the resolution a good thing to do is build or get a debug version of Dosbox and put an interrupt breakpoint on 10h, then look at Ralf Brown's interrupt list and sift through the calls to the VGA to determine what is happening. Thankfully 90% of DOS games are just straight mode 13 320x200. Also this is about a one screen game. If it's a scrolling game, use your intuition about how the value is stored of course). 8 hits. That's more like it. So double click the first value and change it. Hopefully this is the Y position of our character. If it is, the character should snap across the screen. If it's not, it will be one of the others. Ok, it turns out it was the first value. Don't you love it when things just work out? So... now we know where the player Y position is in the memory of the JPC-RR process running. How are we going to translate that to something practical? JPC doesn't see memory the same way that CheatEngine does. It has its own internal RAM. Let's assume you installed JPC-RR to "C:\JPC-RR\". Open notepad and write jpcrr.ram_dump("ram.dump", true) print("Dumped") and save it as "dump.lua" into "C:\JPC-RR\lua\". To be honest I don't remember why the "true" is there and I can't be bothered to read the docs. So just do it! Or don't and write "false" and see what happens. Live on the edge! So the script is in the default location now, let's run it. JPC has a lua window you can't get rid of, so bring it up and type "dump.lua" into it and click run. If all goes well it should say "Dumped" in the window. If not make sure the script is where JPC wants it. Now we have a binary dump of JPC's RAM called "ram.dump" inside the "JPC-RR\lua\" folder. There's no question that what we found in CheatEngine is inside that RAM dump, but how do we find it? Easy... maybe. Go back to CheatEngine and rightclick on the address you determined the player Y position was. Select "Browse this memory region" Hmm there's a lot of crap here. We need something unique that we can match up to the RAM dump. All those 00's won't do, so scroll up one row. Ah there's some unique characters! Select as many unique characters as you can *while still containing the value you are interested in* and copy them. In this example, the value we're interested in is the very last byte, 0x27. (of course you can select whatever you want as long as you know where your target byte lies in it and it's unique enough). So now if all is well we have a string of hexadecimal characters copied to the clipboard, which is hopefully unique and not repeated elsewhere. Open up the ram.dump file in your favourite hex editor and search for the hex string that you've got copied. Great! Only 1 match in the entire file. We've established that this is the string in CheatEngine, and CE was looking at JPC-RR's memory albeit in a virtual space. So if we can see the same string in the dump of JPC's RAM, we're looking at it exactly how JPC sees it! Good stuff. Since our target value was the byte on the end, the player's Y position, let's find out what that is. Put your cursor over it and find out that it's at offset 0x2B0D8. (This will be a diff address for you likely). So the detective work is done! We know that JPC, internally, sees the player's Y position in its RAM as that very address. Let's confirm this. Open notepad and write ypos = jpcrr.read_byte(0x2B0D8) print(ypos) and save it as whatever you want as long as it's in the lua directory. When you run it, it should print the Y position. And that's that! It was a simple tutorial but hopefully it can help someone.
Emulator Coder, Skilled player (1142)
Joined: 5/1/2010
Posts: 1217
viren wrote:
So... now we know where the player Y position is in the memory of the JPC-RR process running. How are we going to translate that to something practical? JPC doesn't see memory the same way that CheatEngine does. It has its own internal RAM. Let's assume you installed JPC-RR to "C:\JPC-RR\". Open notepad and write jpcrr.ram_dump("ram.dump", true) print("Dumped") and save it as "dump.lua" into "C:\JPC-RR\lua\". To be honest I don't remember why the "true" is there and I can't be bothered to read the docs. So just do it! Or don't and write "false" and see what happens. Live on the edge! So the script is in the default location now, let's run it. JPC has a lua window you can't get rid of, so bring it up and type "dump.lua" into it and click run. If all goes well it should say "Dumped" in the window. If not make sure the script is where JPC wants it. Now we have a binary dump of JPC's RAM called "ram.dump" inside the "JPC-RR\lua\" folder. There's no question that what we found in CheatEngine is inside that RAM dump, but how do we find it? Easy... maybe.
Easier Way: Snapshot -> Ram Dump -> Binary (or Hexadecimal). Then you are prompted for the filename to save as. At least some Java implementation have a bug here: Dragging from Snapshot to Binary/Hexadecimal might not work. Click on snapshot and ram dump to open the (sub)menus (or point at correct menu entry and hit <ENTER>). Oh, BTW, the "true" there selects binary mode. Also, the JPC-RR physical memory is not in one chunk in OS memory (chunk is 4096 bytes), so you might need to search for sequence ending in correct value and sequence starting with the correct value. That is: If the memory address is near the beginning of the chunk, searching for string ending in the correct value won't find it. You need to search for string starting with the correct value (to avoid hitting chunk boundaries). If in the other hand, the address is near the end of the chunk, the situation is vice versa: You need to search for string ending in the correct value. Of course, since chunks are 4096 bytes, most addresses are not near either the start or the end of the chunk. For these cases, either string starting or ending at correct value works. The case in post above is such case: The y-position was at 0x2B0D8, which is over 200 bytes from start of the chunk (and much further from the end). Oh, and here is slightly better memory watch script (draws the values on screen): Download dgeneration.lua
Language: lua

on_redraw = function() -- Make some empty space to right, so we don't have to write over game graphics. -- The first parameter (1) controls when the empty space will appear. 1 => Screen only, 2 => Dump only -- 3 => Both. -- The second parameter (160) is size of the empty space. In this case, 160 pixels wide (20 characters). jpcrr.hud.right_gap(1, 160); -- The x-position to place texts at. Usually this will be 640 ingame, but in case it isn't... pos = jpcrr.vga_resolution(); -- Write some text. -- 1st parameter (1): When the empty space will appear (same meanings as first argument of right_gap). -- 2nd parameter (pos): x-position to write the text to. -- 3nd parameter (0): y-position to write the text to. -- 4th parameter ("ypos=" .. jpcrr.read_byte(0x0x2B0D8)): The text to write. -- 5th parameter (false): If true, linefeed in text causes line change in printed text. -- 6th parameter (255): Foreground red (0-255). -- 7th parameter (255): Foreground green (0-255). -- 8th parameter (255): Foreground blue (0-255). -- 9th parameter (255): Foreground alpha (0-255). -- 10th parameter (0): Background red (0-255). -- 11th parameter (0): Background green (0-255). -- 12th parameter (0): Background blue (0-255). -- 13th parameter (0): Background alpha (0-255). jpcrr.hud.chargen(1, pos, 0, "ypos=" .. jpcrr.read_byte(0x2B0D8), false, 255, 255, 255, 255, 0, 0, 0, 0); -- Write another text (in this case, number of current frame). The text height is 16, so write it at -- y-position 0 + 16 = 16. jpcrr.hud.chargen(1, pos, 16, "frame=" .. jpcrr.frame_number(), false, 255, 255, 255, 255, 0, 0, 0, 0); end -- Register on_redraw to run on each frame received. jpcrr.register_redraw_function(on_redraw); -- Wait for events. while true do jpcrr.wait_event() end
Joined: 2/5/2011
Posts: 9
Thanks for that, especially about the chunks. Edit: It's 7am here and I completely misinterpreted what you said. Edited out the dumb stuff :)
Joined: 2/5/2011
Posts: 9
If that's the memory watch that came with the latest JPC-RR I tried it and it crashed a lot, which is why I tried to find another way. It seemed to narrow down values very well but once I tried to watch something everything got very unstable. I tried it a few times and the same deal. Is this known or maybe it's just on my end? Edit againnn: Ok reading the source it obviously isn't. But it's still worth it to bring up the mem watch script that comes with JPC.