Ich weiß, dass es unnötig ist weil:
- Assault Cube eh schon eine built-in FPS Anzeige hat
- Es bei diesem Spiel nicht von Relevanz ist, die FPS zu messen
Falls jemand das Script aus Spaß ausprobiert: Erst Assault Cube starten, dann egal wann das Script ausführen.
(Interessant, was man nicht alles mit AHK machen kann..)
Code:
Code: Select all
;Pizzapizze's FPS Overplay für Assault Cube
;Version: 2 (17.01.2020)
#SingleInstance, force
OnExit, GuiClose
DetectHiddenWindows, On
global proc, ERR, bytes
bytes := []
proc := {name: "ac_client.exe", pid: 0, handle: 0, dsrBaseName: "OPENGL32.dll", dsrBaseAdr: 0, allocBase: 0, renderFuncBaseOffset: 0x32EA0}
ERR := {ERR_PROC_CONNECT: "Fehler beim Aufbau mit dem Zielprozess."}
Gui, 2:New, +HwndLoadingHWND, AC FPS Overlay
Gui, Font, s13
Gui, Add, Text, , Suche nach Spiel
Gui, Font, s20
Gui, Add, Text, vloadingDots w150
Gui, Show
Gui, 1:New, +toolwindow -caption +HwndOverlayHWND +alwaysontop
Gui, Color, 0x000000
Gui, Font, bold s16 cGreen
Gui, Add, Text, vfpsText w270, FPS here
;Gui, Show, w280 h150
WinSet, TransColor, 0x000000, ahk_id %OverlayHWND%
WinSet, ExStyle, +0x20, ahk_id %OverlayHWND%
SetTimer, UpdateDots, 50
while (!manageProcessStuff())
Sleep, 100
SetTimer, UpdateDots, off
GuiControl,2:, loadingDots, Gefunden!
WinActivate, % "ahk_exe " proc.name
WinGetPos, gx, gy,,, % "ahk_exe " proc.name
WinActivate, % "ahk_Id " LoadingHWND
WinGetPos, wx, wy,,, % "ahk_Id " LoadingHWND
stepX := (gx - wx) / 20, stepY := (gy - wy) / 20
Loop, 20
{
wx += stepX, wy += stepY
Gui, 2:Show, % "x" wx + stepX " y" wy + stepY
sleep, 1
}
Gui, 2:Destroy
Gui, 1:Show, w280 h150
WinActivate, % "ahk_exe " proc.name
proc.allocBase := allocMem()
;msgbox % hex(proc.allocBase)
setupFramesCounter()
framesBuffer := 0x00000000
clrbad := 0xd00000, clrmed := 0xd0d000, clrwell := 0x00d000, clrultra := 0x00d0d0
Loop
{
lastTimeStamp := newTimeStamp
lastFramesCount := crntFramesCount
DllCall("ReadProcessMemory", "UInt", proc.handle, "UPtr", proc.allocBase, "UPtr", &framesBuffer, "UInt", 4, "UPtr", 0, "int") ;get frames
newTimeStamp := ToTimeUnit(, A_MSec, A_Sec, A_Min, A_Hour)
crntFramesCount := NumGet(framesBuffer,, "UInt")
frameTime := ((newTimeStamp - lastTimeStamp)) / (crntFramesCount - lastFramesCount)
fps := (crntFramesCount - lastFramesCount) / ((newTimeStamp - lastTimeStamp) / 1000) ;get frames per second
;tooltip %lastTimeStamp% %newTimeStamp% %lastFramesCount% %crntFramesCount%
fpsString := "FPS: " Round(fps, 0) " (" Round(frameTime, 0) "ms)"
GuiControl,1:, fpsText, %fpsString%
crntFont := fps < 24 ? clrbad : fps < 46 ? clrmed : fps < 85 ? clrwell : clrultra
Gui, Font, c%crntFont%
GuiControl, Font, fpsText,
WinGetPos, wx, wy,,, % "ahk_exe " proc.name
WinMove, ahk_id %OverlayHWND%,, wx, % wy + 30
if (crntFramesCount > 0xFFFFFF)
{
DllCall("WriteProcessMemory", "UInt", proc.handle, "UPtr", proc.allocBase, "UInt*", 0, "UInt", 4, "UPtr", 0, "int")
newTimeStamp := 0
}
Sleep 300
}
return
UpdateDots:
dots .= "."
if dots = ....................
dots = .
GuiControl,2:, loadingDots, %dots%
return
manageProcessStuff() {
WinGet, pid, PID, % "ahk_exe " proc.name
if (!pid)
return false
proc.pid := pid
proc.handle := getHandle(proc.pid, 0x438)
proc.dsrBaseAdr := getModBase()
;msgbox % hex(proc.allocBase)
if (!proc.dsrBaseAdr)
{
displayError(ERR.ERR_PROC_CONNECT)
Return False
}
BASE_ADDRESS := proc.dsrBaseAdr + 0x1B39980
Return True
}
getModBase() {
hModules := 0 ;Modul Handle Pseudo Array.
szHModules := VarSetCapacity(hModules, 2048) ;Sicher stellen, dass alle handles reinpassen.
cbNeeded := 0 ;Hier wird später drinstehen, wie viel Platz ich tatsächlich gebraucht hätte.
strBaseName := ""
szStrBaseName := VarSetCapacity(strBaseName, 128)
funcWorked := DllCall("Psapi\EnumProcessModulesEx", "UInt", proc.handle, "UPtr", &hModules, "UInt", szHModules, "UPtr", &cbNeeded, "UInt", 0x1, "Char") ;Alle 32 oder 64bit handles (Base Addressen) des Zielprozesses erhalten und in einem "Array" speichern. 0x1=32bit
loop % NumGet(cbNeeded) / 8
{
;msgbox % "Base: " format("{:X}", NumGet(hModules, A_Index*8-8, "UPtr"))
funcWorked := DllCall("Psapi\GetModuleBaseNameA", "UInt", proc.handle, "UInt", NumGet(hModules, A_Index*8-8, "UPtr"), "UPtr", &strBaseName, "UInt", szStrBaseName, "int") ;Alle handles durchgehen und den Basename abfragen, und vergleichen.
if (StrGet(&strBaseName, szStrBaseName, "UTF-8") = proc.dsrBaseName)
{
Return NumGet(hModules, A_Index*8-8, "UPtr")
}
}
Return False
}
displayError(errText) {
MsgBox, 16, Win [%A_PtrSize% byte] - Fehler, %errText%
Return
}
allocMem() {
allocBase := DllCall("VirtualAllocEx", "UInt", proc.handle, "UInt", 0, "UInt", 1000, "UInt", 0x3000, "UInt", 0x40, "UInt")
return allocBase
}
setupFramesCounter() {
abc := 0
NumPut(proc.allocBase, abc, 0)
bytes[0] := 11
bytes[1] := 0xA1
bytes[2] := NumGet(abc, 0, "UChar")
bytes[3] := NumGet(abc, 1, "UChar")
bytes[4] := NumGet(abc, 2, "UChar")
bytes[5] := NumGet(abc, 3, "UChar")
bytes[6] := 0x40
bytes[7] := 0xA3
bytes[8] := NumGet(abc, 0, "UChar")
bytes[9] := NumGet(abc, 1, "UChar")
bytes[10] := NumGet(abc, 2, "UChar")
bytes[11] := NumGet(abc, 3, "UChar")
byteBuffer := 0x00
Loop, 5 ;Vorherige Bytes mitspeichern
{
DllCall("ReadProcessMemory", "UInt", proc.handle, "UPtr", proc.dsrBaseAdr + proc.renderFuncBaseOffset + A_Index - 1, "UPtr", &byteBuffer, "UInt", 1, "UPtr", 0, "int")
bytes[bytes[0] + A_Index] := NumGet(byteBuffer, 0, "UChar")
}
bytes[0] += 5
jmpNum := proc.dsrBaseAdr + proc.renderFuncBaseOffset - (proc.allocBase + bytes[0] + 6) ;referenzweet für JMP ausrechnen
NumPut(jmpNum, abc, 0)
bytes[bytes[0] + 1] := 0xE9 ;Jmp Near
bytes[bytes[0] + 2] := NumGet(abc, 0, "UChar")
bytes[bytes[0] + 3] := NumGet(abc, 1, "UChar")
bytes[bytes[0] + 4] := NumGet(abc, 2, "UChar")
bytes[bytes[0] + 5] := NumGet(abc, 3, "UChar")
bytes[0] += 5
Loop, % bytes[0] ;alle bytes in die allocated memory schreiben
{
DllCall("WriteProcessMemory", "UInt", proc.handle, "UPtr", proc.allocBase + 5 + (A_Index), "UChar*", bytes[A_Index], "UInt", 1, "UPtr", 0, "int")
}
NumPut(proc.allocBase - (proc.dsrBaseAdr + proc.renderFuncBaseOffset) + 1, abc, 0, "Int64") ;jmp ref
deBytes := []
deBytes[1] := 0xE9
deBytes[2] := NumGet(abc, 0, "UChar")
deBytes[3] := NumGet(abc, 1, "UChar")
deBytes[4] := NumGet(abc, 2, "UChar")
deBytes[5] := NumGet(abc, 3, "UChar")
Loop, 5 ;Detour einleiten; bytes überschreiben
{
DllCall("WriteProcessMemory", "UInt", proc.handle, "UPtr", proc.dsrBaseAdr + proc.renderFuncBaseOffset + A_Index - 1, "UChar*", deBytes[A_Index], "UInt", 1, "UPtr", 0, "int")
}
soundplay *0
}
cleanUp() {
if (bytes[12] !> 0)
return
Loop, 5 ;vorherige bytes wieder hineinschreiben
{
DllCall("WriteProcessMemory", "UInt", proc.handle, "UPtr", proc.dsrBaseAdr + proc.renderFuncBaseOffset + A_Index - 1, "UChar*", Bytes[11 + A_Index], "UInt", 1, "UPtr", 0, "int")
}
DllCall("VirtualFreeEx", "UInt", proc.handle, "UPtr", proc.allocBase, "UInt", 0, "UInt", 0x00008000, "Char")
}
hex(a) {
return format("0x{:X}", a)
}
ToTimeUnit(desiredTimeUnit := 1, timeMsec := 0, timeSec := 0, timeMin := 0, timeHr := 0) {
totalTimeMsec := timeMsec + (timeSec*1000) + (timeMin*60*1000) + (timeHr*60*60*1000)
totalTimeDesiredUnit := totalTimeMsec / desiredTimeUnit
Return totalTimeMsec
}
GetHandle(PID, ACCES_RIGHT) {
if (ACCES_RIGHT == "ReadWrite")
ACCES_RIGHT := 0x38
Return DllCall("OpenProcess", "UInt", ACCES_RIGHT, "Int", false, "UInt", PID, "UInt")
}
GuiClose:
cleanUp()
DllCAll("CloseHandle", "UInt", proc.handle)
ExitApp 0