Hello everyone,
I'd like to build a gui as gauge on top of my flight simulator of choice (let's call it MySim.exe). Think head-up display.
As proof of concept I'd do a turn indicator:
The source value
- I've hunted down the value of interest using Cheat Engine
- The value is stored as "double value" in the first two "??" of following array of byte (AoB): ?? ?? ?? ?? ?? ?? ?? ?? ?? 31 BD 41 3A 3A C1 3F 28 F3 7B 62 4A 22 D5 BF
- I don't have a pointer, just the AoB shown above
- The value is <0 when turning left and >0 when turning right
What the script should do:
- Grab the value 10-30 times per second
- Convert the value to decimal format
- Round the value to 2 decimals
- Feed the value into one or two gui(s) that display(s) a bar. If the value is 0.00 +/- a tolerance interval of 0.1 no bar should be visible. If the value is positive, the bar should "grow" to the right depending on the value (cutoff at +10.00). If the value is negative, a bar should grow to the left (cutoff value at -10.00)
Any help with this would be greatly appreciated. The AoB part gives me the greatest headache, since I've never used AHK to read memory values.
Memory address value to GUI for flightsim gauge
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I've found a basic script to search for the AoB first and gave it a try. It includes RHCPs classMemory script found here: https://github.com/Kalamity/classMemory/blob/master/classMemory.ahk
It gives me following error: The program isn't running (not found) or you passed an incorrect program identifier parameter.
Here's the script I tried:
======================================================================================================================================
#include classMemory.ahk
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, Chara1
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
; I just put the pattern in an array so that it's a bit neater with 2 function calls
aPattern := ["?", "?", "?", "?", "?", "?", "?", "?", "?", 0xA6, 0x7E, 0x0B, 0x97, 0x75, 0xC2, 0x3F, 0x1E, 0x08, 0x33, 0x64, 0x2D, 0xA2, 0xD8, 0xBF]
address := mem.processPatternScan(,, aPattern*)
if address > 0
msgbox % "Found Pattern at: " address
else msgbox Pattern not found or error: %address%
address := mem.modulePatternScan(, aPattern*) ; assume its in the main process module. This will probably be quicker.
if address > 0
msgbox % "Found Pattern at: " address
else msgbox Pattern not found or error: %address%
return
======================================================================================================================================
It gives me following error: The program isn't running (not found) or you passed an incorrect program identifier parameter.
Here's the script I tried:
======================================================================================================================================
#include classMemory.ahk
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, Chara1
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
; I just put the pattern in an array so that it's a bit neater with 2 function calls
aPattern := ["?", "?", "?", "?", "?", "?", "?", "?", "?", 0xA6, 0x7E, 0x0B, 0x97, 0x75, 0xC2, 0x3F, 0x1E, 0x08, 0x33, 0x64, 0x2D, 0xA2, 0xD8, 0xBF]
address := mem.processPatternScan(,, aPattern*)
if address > 0
msgbox % "Found Pattern at: " address
else msgbox Pattern not found or error: %address%
address := mem.modulePatternScan(, aPattern*) ; assume its in the main process module. This will probably be quicker.
if address > 0
msgbox % "Found Pattern at: " address
else msgbox Pattern not found or error: %address%
return
======================================================================================================================================
Re: Memory address value to GUI for flightsim gauge
Try using " _ClassMemory.setSeDebugPrivilege()" before calling new.
E.g
E.g
Code: Select all
#include classMemory.ahk
if !A_IsAdmin
{
Run *RunAs "%A_ScriptFullPath%"
ExitApp
}
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
_ClassMemory.setSeDebugPrivilege()
WinGet, PID, PID, Chara1
if !PID
{
msgbox winget failed to find pid
ExitApp
}
mem := new _ClassMemory("ahk_pid " PID, "", hProcess)
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
msgbox A_LastError %A_LastError%
ExitApp
}
msgbox successful
return
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I had the wrong process. I've SetTitleMatchMode to 2 and now it finds the process and the AoB.
The next step is to extract numeric data from the first 8 "?" if the AoB... And feed it into a gui.
Does anyone have an idea how to best achieve this?
The next step is to extract numeric data from the first 8 "?" if the AoB... And feed it into a gui.
Does anyone have an idea how to best achieve this?
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I placed evan's ReadMemory.ahk library in C:\Program Files\AutoHotkey\lib (https://autohotkey.com/board/topic/33888-readmemory-function/)
===============================
ReadMemory(MADDRESS,PROGRAM)
{
winget, pid, PID, %PROGRAM%
VarSetCapacity(MVALUE,4,0)
ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)
Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)
return, result
}
===============================
Next, I tried to read the value using following script:
************************************************
SetFormat, IntegerFast, Hex
f1::
value := ReadMemory(0xA7868EDDE0, MySim.exe)
MsgBox, %value%
return
********************************************
I found the AoB, paused the game and looked at the memory address (A7868EDDE0) and it's value in Cheat Engine.
Cheat Engine info:
Address: A7868EDDE0
Value: 2D A7 4E D6 A7 68 22 40 as hex, 9.20 as double precision float.
If I convert the hex value to 40 22 68 A7 D6 4E A7 2D and feed it into a converter I get 9.204405495740128 in double precision float.
However, the script I use to read the value gives me 0x0. If I remove the SetFormat line I get a value of 0.
What am I doing wrong?
===============================
ReadMemory(MADDRESS,PROGRAM)
{
winget, pid, PID, %PROGRAM%
VarSetCapacity(MVALUE,4,0)
ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)
Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)
return, result
}
===============================
Next, I tried to read the value using following script:
************************************************
SetFormat, IntegerFast, Hex
f1::
value := ReadMemory(0xA7868EDDE0, MySim.exe)
MsgBox, %value%
return
********************************************
I found the AoB, paused the game and looked at the memory address (A7868EDDE0) and it's value in Cheat Engine.
Cheat Engine info:
Address: A7868EDDE0
Value: 2D A7 4E D6 A7 68 22 40 as hex, 9.20 as double precision float.
If I convert the hex value to 40 22 68 A7 D6 4E A7 2D and feed it into a converter I get 9.204405495740128 in double precision float.
However, the script I use to read the value gives me 0x0. If I remove the SetFormat line I get a value of 0.
What am I doing wrong?
Re: Memory address value to GUI for flightsim gauge
It's because you're trying to read a double (8 bytes) and that function reads 4 bytes from memory.
Try something like:
Try something like:
address := mem.modulePatternScan(, aPattern*)
msgbox % value := mem.read(address, "Double")
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
Thanks a lot, will try that as soon as I find some time!
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
Awesome, that worked! Many thanks!
I defined following variable
TurnInd := mem.read(address, "Double")
and placed it within a loop with 100 ms sleep and a tooltip. It updates nicely.
Now I'm reading up on how to best feed TurnInd into two progress bars: one should progress to the left, be red, and have a range of -0.25 to -10. The second bar should progress to the right, be green and have a range of 0.25 to 10. If TurnInd is between -0.25 and 0.25 no gui should be visible.
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
Awesome, it works just as planned! Many thanks RHCP, you da man!
I still have two questions:
1) Do you see a way to improve the script? Is there some weak spot that makes it slow or makes it use lots of CPU power?
2) The progress bar moves is steps of 1.0. Is there a way to make it reflect the true values of the variable more accurately?
Here's the current script:
==================================================================================
#include classMemory.ahk
SetTitleMatchMode, 2
SetTimer, TurnIndicator, 33
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, MySim
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
aPattern := ["?", "?", "?", "?", "?", "?", "?", "?", "?", 0xA6, 0x7E, 0x0B, 0x97, 0x75, 0xC2, 0x3F, 0x1E, 0x08, 0x33, 0x64, 0x2D, 0xA2, 0xD8, 0xBF]
address := mem.processPatternScan(,, aPattern*)
if address > 0
{
Gui, 1: Margin, 0, 0
Gui, 1: -Caption +ToolWindow ;-- no title, no taskbar icon
Gui, 1: Add,Progress, vTurnInd x-2 y-2 w150 h30 BackgroundBlack cGreen Range0-10, 0 ;Default range is 0 to 100
Gui, 1: +alwaysOnTop
Gui, 1: Show, x1280 y1350 NoActivate ; w150 h20, TurnInd, NoActivate
Gui, 2: Margin, 0, 0
Gui, 2: -Caption +ToolWindow ;-- no title, no taskbar icon
Gui, 2: Add,Progress, vTurnInd x-2 y-2 w150 h30 BackgroundRed cBlack Range-10-0, 0 ;Default range is 0 to 100
Gui, 2: +alwaysOnTop
Gui, 2: Show, x1090 y1350 NoActivate ; w150 h20, TurnInd, NoActivate
GoSub, TurnIndicator
}
else msgbox Pattern not found or error: %address%
return
TurnIndicator:
TurnInd := mem.read(address, "Double")
ToolTip, %TurnInd%
GuiControl 1: , TurnInd, %TurnInd% ;-- Update progress bar
GuiControl 2: , TurnInd, %TurnInd% ;-- Update progress bar
return
+Enter::Reload
+Esc::ExitApp
I still have two questions:
1) Do you see a way to improve the script? Is there some weak spot that makes it slow or makes it use lots of CPU power?
2) The progress bar moves is steps of 1.0. Is there a way to make it reflect the true values of the variable more accurately?
Here's the current script:
==================================================================================
#include classMemory.ahk
SetTitleMatchMode, 2
SetTimer, TurnIndicator, 33
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, MySim
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
aPattern := ["?", "?", "?", "?", "?", "?", "?", "?", "?", 0xA6, 0x7E, 0x0B, 0x97, 0x75, 0xC2, 0x3F, 0x1E, 0x08, 0x33, 0x64, 0x2D, 0xA2, 0xD8, 0xBF]
address := mem.processPatternScan(,, aPattern*)
if address > 0
{
Gui, 1: Margin, 0, 0
Gui, 1: -Caption +ToolWindow ;-- no title, no taskbar icon
Gui, 1: Add,Progress, vTurnInd x-2 y-2 w150 h30 BackgroundBlack cGreen Range0-10, 0 ;Default range is 0 to 100
Gui, 1: +alwaysOnTop
Gui, 1: Show, x1280 y1350 NoActivate ; w150 h20, TurnInd, NoActivate
Gui, 2: Margin, 0, 0
Gui, 2: -Caption +ToolWindow ;-- no title, no taskbar icon
Gui, 2: Add,Progress, vTurnInd x-2 y-2 w150 h30 BackgroundRed cBlack Range-10-0, 0 ;Default range is 0 to 100
Gui, 2: +alwaysOnTop
Gui, 2: Show, x1090 y1350 NoActivate ; w150 h20, TurnInd, NoActivate
GoSub, TurnIndicator
}
else msgbox Pattern not found or error: %address%
return
TurnIndicator:
TurnInd := mem.read(address, "Double")
ToolTip, %TurnInd%
GuiControl 1: , TurnInd, %TurnInd% ;-- Update progress bar
GuiControl 2: , TurnInd, %TurnInd% ;-- Update progress bar
return
+Enter::Reload
+Esc::ExitApp
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I found no way to make the bars progress in steps of 0.01 instead of 1, so I multiplied the TurnInd variable with 100 and adapted the progress bar range:
TurnIndicator:
TurnInd := (mem.read(address, "Double")*100)
TurnIndicator:
TurnInd := (mem.read(address, "Double")*100)
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I'm now working on a g meter that shows an overlay with numeric data rounded to one decimal just like in fighterplanes (e.g. 7.3 g). AoBs are inconvenient for this one, because they are very long and the address changes with every flight. Luckily I have good pointers:
https://autohotkey.com/board/topic/148210-need-help-to-read-multi-lvl-pointers/) with my Gui overlay script in AHK 1.1.27.00 64bit.
I only get the correct value when directing the mem.read to the currently valid dynamic address. Both msgbox % "base address methods seem to display the wrong base. The issues are marked in red in the following script.
As always, any help is much appreciated!
********************************************************************************************************
#include classMemory.ahk ; includes classMemory library found under ahk/libs
SetTitleMatchMode, 2
SetTimer, Update_gIndicator, 33 ; Gui updater
; Gui colors ==========================================================
Custom_C = EEAA99 ; Can be any RGB color.
Custom_W = 300
Custom_H = 20
; Read base address ===================================================
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, MySim
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
; the mem object can now be used to read/write data
msgbox % "base address: " mem.BaseAddress
. "`nBase address method2: " mem.getModuleBaseAddress() ; both methods return 140697963397120 as base address, which I think equals 7FF6CC210000 in hex. CE shows 0004F318 as base.
; Gui specs ==========================================================
Gui, +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, Color, %Custom_C%
Gui, Font, s16 ; Set a large font size (12-point).
Gui, Add, Text, vgInd cLime, xxxxxxxxxxxxxxxxx, y ; xs and ys serve to autosize window
WinSet, TransColor, %Custom_C% 200 ; Make Custom_C color invisible & Text translucent (150)
Gosub, Update_gIndicator ; Run first update immediately instead of waiting for the timer
Gui, Show, x800 y600 NoActivate
; Gui upadter ========================================================
Update_gIndicator:
;gInd := mem.read(0x1F1F4884180, "Double") ; Debugging; this is the final dynamic address; feeds the rounded correct value into the Gui
gInd := mem.read(mem.BaseAddress + 0x0004F318, "Double", 0x8, 0x630, 0x7D8, 0x18, 0x688) ; These are the base address and offsets as displayed in CE. Displays the wrong value (0).
GuiControl 1: , gInd, %gInd% ;-- Updates Gui
return
+Enter::Reload
+Esc::ExitApp
I merged RHCPs base address script (I only get the correct value when directing the mem.read to the currently valid dynamic address. Both msgbox % "base address methods seem to display the wrong base. The issues are marked in red in the following script.
As always, any help is much appreciated!
********************************************************************************************************
#include classMemory.ahk ; includes classMemory library found under ahk/libs
SetTitleMatchMode, 2
SetTimer, Update_gIndicator, 33 ; Gui updater
; Gui colors ==========================================================
Custom_C = EEAA99 ; Can be any RGB color.
Custom_W = 300
Custom_H = 20
; Read base address ===================================================
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
WinGet, PID, PID, MySim
mem := new _ClassMemory("ahk_pid " PID, "", hProcess) ; note tha quotes and space around AHK_Pid
if !IsObject(mem)
{
if (hProcess = "")
msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
else msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter.
ExitApp
}
; the mem object can now be used to read/write data
msgbox % "base address: " mem.BaseAddress
. "`nBase address method2: " mem.getModuleBaseAddress() ; both methods return 140697963397120 as base address, which I think equals 7FF6CC210000 in hex. CE shows 0004F318 as base.
; Gui specs ==========================================================
Gui, +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, Color, %Custom_C%
Gui, Font, s16 ; Set a large font size (12-point).
Gui, Add, Text, vgInd cLime, xxxxxxxxxxxxxxxxx, y ; xs and ys serve to autosize window
WinSet, TransColor, %Custom_C% 200 ; Make Custom_C color invisible & Text translucent (150)
Gosub, Update_gIndicator ; Run first update immediately instead of waiting for the timer
Gui, Show, x800 y600 NoActivate
; Gui upadter ========================================================
Update_gIndicator:
;gInd := mem.read(0x1F1F4884180, "Double") ; Debugging; this is the final dynamic address; feeds the rounded correct value into the Gui
gInd := mem.read(mem.BaseAddress + 0x0004F318, "Double", 0x8, 0x630, 0x7D8, 0x18, 0x688) ; These are the base address and offsets as displayed in CE. Displays the wrong value (0).
GuiControl 1: , gInd, %gInd% ;-- Updates Gui
return
+Enter::Reload
+Esc::ExitApp
Re: Memory address value to GUI for flightsim gauge
You need to find the address of "sceneman.dll", not the process for that pointer i.e.
Code: Select all
msgbox % "base address: " mem.BaseAddress
. "`nModleaddress: " mem.getModuleBaseAddress("sceneman.dll")
gInd := mem.read(mem.getModuleBaseAddress("sceneman.dll") + 0x0004F318, "Double", 0x8, 0x630, 0x7D8, 0x18, 0x688)
-
- Posts: 79
- Joined: 10 Mar 2019, 10:28
Re: Memory address value to GUI for flightsim gauge
I just tried it and it works like a charm! Thanks!RHCP wrote: ↑27 Mar 2019, 06:43You need to find the address of "sceneman.dll", not the process for that pointer i.e.Code: Select all
msgbox % "base address: " mem.BaseAddress . "`nModleaddress: " mem.getModuleBaseAddress("sceneman.dll") gInd := mem.read(mem.getModuleBaseAddress("sceneman.dll") + 0x0004F318, "Double", 0x8, 0x630, 0x7D8, 0x18, 0x688)
Let's see:
sideslip indicator....check!
g meter...............check!
This is so much fun I think I'll try an angle of attack (AoA) indicator next. With a g meter and an AoA indicator in place I'd be able to perform very informative flight tests, e.g. to determine the turn rate vs. angle of attack and find the sweet spot for turn performance.
Who is online
Users browsing this forum: Billykid and 211 guests