How to Use Cheat Engine with PCSX2 v1.7+
Credit: Fobes (Git Pull Request - Core: Export Recompiler Offsets #5531)
General Overview
To put it simply, you basically assign EEmem (A) as a pointer and use the PS2 Memory Address as an Offset (B) to the EEmem Pointer.
Porting Example
Imagine for a moment that our cheat address is 2033CD68 in PCSX2 v1.6
PCSX2x64.EEmem would be assigned as a pointer,
0x33CD68 would be assigned as the first offset
Code:
Dereferencing<process name>.EEmem (example pcsx2x64-avx2-dev.EEmem ) in CheatEngine will give you the address to use as a base pointer.
~ Fobes
A)
B)
The images are provided as reference. The actual address for EEmem (7FF200000000) will change and is the whole reason for needing to go about doing things this way
Furthermore, the provided address (0x33CD68) would typically be written as 0x2033CD68 however with the recent changes to PCSX2 , prefixing our offsets with 0x2 is not necessary.
IMPORTANT NOTE:
As with PCSX2 v1.6 and previous releases, the same problem of applying patches to certain regions of memory will still result in no affect. This is due to how virtual memory is compiled , stored and cached.
Cheat Engine : PCSX2 Scripting Tutorial
Assigning EEMem as a Symbol
This took me a little bit of time to figure out but should make things much easier on everybody. Basically we are going to define a word as an address to be accessible in cheat engine. We can do this in a few simple steps

- Open Cheat Engine and open the memory viewer
- Press CTRL+A to open the Script Assembler
- Its here that we will define "PCSX2x64.EEmem" as a symbol "EEmem" so that we can always access it by creating a new address in our table with EEMem as the address.
- Paste the following into your script entry window and then save it to your table. We do not need to run this script ever, it simply sits there and upon attaching to a said process it will assign an address to the symbol
- Go ahead and launch pcsx2, attach and add a new address with the Address of "EEmem"
From here you just assign a pointer with the offset value to your address in question (PS2 Format without "0x20")
Code:
{$lua}
[ENABLE]
-- Register EEmem Symbol --
local EEmem = getAddress("PCSX2x64.eemem")
registerSymbol("EEmem", PS2Mem, true)
[DISABLE]
unregisterSymbol("EEmem")
Assigning PS2mem as a Symbol
Having EEmem accessible as an address is pretty slick , but thanks to the PCSX2 dev team we were already able to access that with "PCSX2x64.eemem" as we discussed at the beginning. So while it might be cool that we can access the pointer with less characters , we havent really simplified anything and some might view that as a waste of time. However , there may be instances where one might want simple quick access to EEmem and having direct access to PS2Memory might not always be beneficial. This next part will walk you through creating a symbol to access PS2Memory all the time, just like EEmem.
- Open Cheat Engine and open the memory viewer
- Press CTRL+A to open the Script Assembler
- Paste the following into your script window and save it to your table.
You can get direct access to PS2Memory by using the address "PS2mem", offsets can be accessed by adding the offset to the address like so "PS2mem+033CD68"
Code:
{$lua}
[ENABLE]
-- Register PS2mem Symbol --
local PS2mem = getAddress("PCSX2x64.eemem")
PS2mem = readPointer(PS2mem)
registerSymbol("PS2mem", PS2mem, true)
[DISABLE]
unregisterSymbol("PS2mem")
ESSENTIAL FUNCTIONS
The following functions will prove to be very helpful in creating your PCSX2 Cheat Engine table. For instance, with PCSX2 it is very hard to deal with pointers , classes and their fields / methods.
Say you have a pointer to an Actor Object. To gain access to the structure, you will need to read 4 bytes from the pointer and then take that value and apply it to the PS2EEmem base.
Cheat Engine has no way built in methods to normalize this, so using cheat engine as you normally would to navigate pointers ... simply put will not work.
I have created the following functions as somewhat of a bandaid to this issue. In this way , you can automate the process ahead of time with a script and just activate it to gain access to said fields and methods.
Function List:
- GetEEmem
- ResolveAddress
- GetPS2Address
- GetPS2AddrFromPointer
- GetPS2AddrFromPointerChain
Code:
{$lua}
-- Globals
PCSX2_VER="pcsx2-qtx64-avx2.exe"
PCSX2_EEMEM="pcsx2-qtx64-avx2.eemem"
-- Pointers
RAW_PlayerPointer = 0x20440C38
PlayerPointer = 0x440C38
-- Offsets
coordsX = 0x1C
coordsY = 0x20
coordsZ = 0x24
TeamID = 0xC8
Health = 0x1044
-- Resolves input RAW PS2 address
-- example input: 0x20440C38
-- example output: 0x440C38
function ResolveAddress(RAW_Address)
return RAW_Address - 0x20000000
end
-- Gets EEMem Address
function GetEEMem()
return readPointer(getAddress(PCSX2_EEMEM))
end
-- Resolves RAW PS2 Address relative to EEMem offset
-- input must be RAW PS2 Offset i.e [ 0x20440C38 ] remove the 0x20
-- Alternatively call Resolve Offset and input everything as RAW PS2 format
function GetPS2Address(offset)
local base = GetEEMem()
local result = base + offset
return result
end
-- Gets pointer address by reading 4bytes from input
-- returns result + eemem
function GetPS2AddrFromPointer(Pointer)
local base = GetEEMem()
local value = readInteger(Pointer)
local result = base + value
return result
end
-- Get address by navigating pointer chain
-- input Base Address must be in shorthand RAW PS2 Format, this gets resolved
function GetPS2AddrFromPointerChain(BaseAddress, Offsets)
local base = GetPS2AddrFromPointer(GetPS2Address(BaseAddress))
for k,v in pairs(Offsets) do
base = GetPS2AddrFromPointer(base + v)
end
return base
end
ATTACH ON LOAD
Code:
-- NightFyre Cheat Engine Script Framework --
---------------------------------------------
-- Establish Window Title --
PCSX2VERSION="PCSX2 v1.7.2912"
getAutoAttachList().add('PCSX2x64.exe')
MainForm.Caption = string.format('PCSX2::CheatTable - %s', PCSX2VERSION)
-- Remove Scan Panel --
MainForm.Panel5.visible = false
-- Set Dark Mode --
GetAddressList().Control[0].BackgroundColor=0x545454
-- Attach to Process + Generate PS2mem Symbol--
-- you may need to adjust the process name depending on the name of your executable
if not openProcess('PCSX2x64.exe')
then sleep(250)
if getOpenedProcessID()== 0
then getAutoAttachList().add('PCSX2x64.exe') sleep(250)
local PS2mem = getAddress("PCSX2x64.eemem")
PS2mem = readPointer(PS2mem)
registerSymbol("PS2mem", PS2mem, true)
end
end
--- This is not functional in its present state.
--- Essentially all it does it abort if PCSX2 is not found to be running when the table is loaded.
--- If you want to use it all you have to do is wrap "attach to Process" as a function that either returns true or false.
--- I originally had it wrapped this way but got tired of it as I debug trainers and internal menus quite frequently.
--- I could have just ripped out the skeleton code but I've left it for however long so I will leave it here for you to do what yo please with it
---
-- Search for Process --
-- if ISPCSX2Running() == false then
-- return
-- end
Example Cheat Engine Table
Game: Sly Cooper and the Thievius Raccoonus
Offset Credits: Sly Cooper Modding Community
Table Author: NightFyre
The following code provides as an example of a game utilizing all of the scripts contained above. This is how most CheatTable makers for PC games are accustomed to utilizing Cheat Engine. The above provided scripts simplify the process in accessing Playstation 2 Memory Variables while the following should give you an understanding on how to properly put everything together to get a decent table going.
Special thanks to the Sly Cooper Modding Community. Their efforts made it possible for me to provide this simple example.
I would also like to express some gratitude towards PeaceBeUponYou, for without his help and patience I would have had a much tougher time creating these scripts
Code:
---------------------------------------------
-- PCSX2 Cheat Engine Script Framework --
---------------------------------------------
PCSX2_VER="pcsx2x64.exe" -- Module
PCSX2_EEMEM="pcsx2x64.eemem" -- EEMainMemory Module
-- Pointers
GameStatePointer = 0x2623C0 -- RAW PS2 Offset
WorldStatePointer = 0x2623C4 -- RAW PS2 Offset
-- Classes
local GameState = {
GameStateFlags = 0x0,
nCheckSum = 0x4,
Unknown = 0x8,
GlobalPlayTimer = 0xC,
WorldSaves = 0x10,
CurrentWorldID = 0x19d8,
CurrentLevelID = 0x19dC,
LivesCount = 0x19E0,
CharmsCount = 0x19E4,
CoinsCount = 0x19E8,
SettingsFlags = 0x19EC,
UnlockedThiefMoves = 0x19F0,
UnlockCutscenes = 0x19F4,
GameCompletionFlags = 0x19F8,
LastThiefMove = 0x19FC
}
local WorldState = {
LevelSaves = 0x0,
KeysCount = 0x438,
VaultsCount = 0x43C,
MTSCount = 0x440,
WorldPlayTimer = 0x444,
WorldStateFlags = 0x448
}
-- Establish Window Title
TITLE="PCSX2::SlyCooper(v1.0.0)"
MainForm.Caption = string.format('%s - Sly Cooper Modding Community', TITLE)
-- Remove Scan Panel
MainForm.Panel5.visible = false
-- Set Dark Mode
GetAddressList().Control[0].BackgroundColor=0x101010
-- Get Process Path
function GetEXEFilePath(addr,pid)
local mods=enumModules(pid)
for k,v in pairs(mods) do
if v.Address==addr then
return v.PathToFile
end
end
end
-- Attach Process
function AttachProcess(name)
if not (openProcess(name) and readInteger(name))then
registerSymbol(name,true)
end
if not getOpenedProcessID() then
messageDialog('PCSX2 1.7 Process Not Found.',1)
error('PCSX2 NOT RUNNING')
end
local FilePath=GetEXEFilePath(getAddressSafe(name),getOpenedProcessID())
if not FilePath then
messageDialog("WRONG PROCESS - PCSX2 ERROR",0)
error("Process not found.")
end
end
-- Gets EEMem Address
function GetEEMem()
return readPointer(getAddress(PCSX2_EEMEM))
end
-- Resolves RAW PS2 Address relative to EEMem offset
-- input must be RAW PS2 Offset i.e [ 0x20440C38 ] remove the 0x20
-- Alternatively call Resolve Offset and input everything as RAW PS2 format
function GetPS2Address(offset)
local base = GetEEMem() -- EEmem Address
local result = base + offset
return result
end
-- Gets pointer address by reading 4bytes from input
-- returns result + eemem
function GetPS2AddrFromPointer(Pointer)
local base = GetEEMem() -- EEmem Address
local value = readInteger(Pointer) -- EEmem Offset
local result = base + value
return result
end
-- Get address by navigating pointer chain
-- input Base Address must be in shorthand RAW PS2 Format, this gets resolved
function GetPS2AddrFromPointerChain(BaseAddress, Offsets)
local base = GetPS2AddrFromPointer(GetPS2Address(BaseAddress))
for k,v in pairs(Offsets) do
base = GetPS2AddrFromPointer(base + v)
end
return base
end
-- Attach Cheat Engine to PCSX2 Process
AttachProcess(PCSX2_VER)
-- Get PCSX2 Module
local dwGameBase = getAddress(PCSX2_VER)
-- Get EEMem
local EEmem = getAddress(PCSX2_EEMEM)
local EEModule = GetEEMem()
-- Get GameState Pointer
local GameStateBase = GetPS2AddrFromPointer(GetPS2Address(GameStatePointer))
for k,v in pairs(GameState) do
registerSymbol(tostring(k), GameStateBase + v)
end
registerSymbol("GameState", GameStateBase)
local WorldStateBase = GetPS2AddrFromPointer(GetPS2Address(WorldStatePointer))
for k,v in pairs(WorldState) do
registerSymbol(tostring(k), WorldStateBase + v)
end
registerSymbol("WorldState", WorldStateBase)
registerSymbol("dwGameBase", dwGameBase, true)
registerSymbol("EEmem", EEmem, true)
registerSymbol("PS2Mem", EEModule, true)
TOOLS - Cheat Table
- Attach to process (+optional attach on load)
- Show / Hide Address Panel
- PCSX2 Module Base
- EEmem Base
Code:
<?xml version="1.0" encoding="utf-8"?>
<CheatTable>
<CheatEntries>
<CheatEntry>
<ID>387240</ID>
<Description>"[PCSX2x64]"</Description>
<Options moHideChildren="1"/>
<LastState Value="" Activated="1" RealAddress="00000000"/>
<Color>FF51FF</Color>
<GroupHeader>1</GroupHeader>
<CheatEntries>
<CheatEntry>
<ID>386836</ID>
<Description>"PCSX2 moduleBase"</Description>
<LastState Value="00905A4D" RealAddress="7FF71C720000"/>
<ShowAsHex>1</ShowAsHex>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>4 Bytes</VariableType>
<Address>pcsx2x64.exe</Address>
</CheatEntry>
<CheatEntry>
<ID>386837</ID>
<Description>"EEMainMemory"</Description>
<ShowAsHex>1</ShowAsHex>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>4 Bytes</VariableType>
<Address>PCSX2x64.EEmem</Address>
<Offsets>
<Offset>0</Offset>
</Offsets>
</CheatEntry>
</CheatEntries>
</CheatEntry>
<CheatEntry>
<ID>386480</ID>
<Description>"DEBUG"</Description>
<Options moHideChildren="1"/>
<LastState Value="" Activated="1" RealAddress="00000000"/>
<Color>00FFFF</Color>
<GroupHeader>1</GroupHeader>
<CheatEntries>
<CheatEntry>
<ID>386609</ID>
<Description>"SHOW ADDRESS PANEL"</Description>
<LastState/>
<Color>00FFFF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>[ENABLE]
{$lua}
MainForm.Panel5.visible = true
[DISABLE]
{$lua}
MainForm.Panel5.visible = false
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>356</ID>
<Description>"ADD NEW ADDRESS"</Description>
<LastState/>
<Color>00FFFF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
AddressList.createMemoryRecord()
[ENABLE]
[DISABLE]
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>386610</ID>
<Description>"LUA ENGINE"</Description>
<LastState/>
<Color>00FFFF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
print('')
[ENABLE]
[DISABLE]
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>386547</ID>
<Description>"DARK MODE"</Description>
<LastState/>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
[ENABLE]
GetAddressList().Control[0].BackgroundColor=0x545454
--getMainForm().color=0x545454
[DISABLE]
GetAddressList().Control[0].BackgroundColor=0xFFFFFF
--getMainForm().color=0xf0f0f0
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>386684</ID>
<Description>"ATTACH PCSX2 [1.6.0]"</Description>
<LastState/>
<Color>4080FF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
[ENABLE]
-- Attach to Process --
if not openProcess('pcsx2.exe')
then sleep(250)
if getOpenedProcessID()==0
then getAutoAttachList().add('pcsx2.exe') sleep(250)
end
end
--NOT USED
[DISABLE]
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>387225</ID>
<Description>"ATTACH PCSX2dis [1.5]"</Description>
<LastState/>
<Color>4080FF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
[ENABLE]
-- Attach to Process --
if not openProcess('pcsx2dis.exe')
then sleep(250)
if getOpenedProcessID()==0
then getAutoAttachList().add('pcsx2dis.exe') sleep(250)
end
end
--NOT USED
[DISABLE]
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>386726</ID>
<Description>"ATTACH PCSX2x64 [v1.7]"</Description>
<LastState/>
<Color>4080FF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
[ENABLE]
-- Attach to Process --
if not openProcess('pcsx2x64.exe')
then sleep(250)
if getOpenedProcessID()==0
then getAutoAttachList().add('pcsx2x64.exe') sleep(250)
end
end
--NOT USED
[DISABLE]
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>387235</ID>
<Description>"ATTACH PCSX2x64-avx2 [v1.7]"</Description>
<LastState/>
<Color>4080FF</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{$lua}
[ENABLE]
-- Attach to Process --
if not openProcess('pcsx2x64-avx2.exe')
then sleep(250)
if getOpenedProcessID()==0
then getAutoAttachList().add('pcsx2x64-avx2.exe') sleep(250)
end
end
--NOT USED
[DISABLE]
</AssemblerScript>
</CheatEntry>
</CheatEntries>
</CheatEntry>
</CheatEntries>
</CheatTable>