-
-
Save odie5533/f638611e6fda0d846b0969b4dfcb11b7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# Form implementation generated from reading ui file 'mainwindow.ui' | |
# | |
# Created by: PyQt5 UI code generator 5.9 | |
# | |
# WARNING! All changes made in this file will be lost! | |
from PyQt5 import QtCore, QtGui, QtWidgets | |
class Ui_MainWindow(object): | |
def setupUi(self, MainWindow): | |
MainWindow.setObjectName("MainWindow") | |
MainWindow.setEnabled(True) | |
MainWindow.resize(380, 110) | |
self.centralWidget = QtWidgets.QWidget(MainWindow) | |
self.centralWidget.setObjectName("centralWidget") | |
self.frame = QtWidgets.QFrame(self.centralWidget) | |
self.frame.setGeometry(QtCore.QRect(0, 0, 380, 110)) | |
palette = QtGui.QPalette() | |
brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush) | |
brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush) | |
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush) | |
brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush) | |
brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush) | |
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush) | |
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush) | |
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush) | |
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) | |
brush.setStyle(QtCore.Qt.SolidPattern) | |
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush) | |
self.frame.setPalette(palette) | |
self.frame.setAutoFillBackground(True) | |
self.frame.setObjectName("frame") | |
self.labelAchievement = QtWidgets.QLabel(self.frame) | |
self.labelAchievement.setGeometry(QtCore.QRect(110, 20, 261, 51)) | |
font = QtGui.QFont() | |
font.setPointSize(12) | |
font.setBold(True) | |
font.setWeight(75) | |
self.labelAchievement.setFont(font) | |
self.labelAchievement.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) | |
self.labelAchievement.setWordWrap(True) | |
self.labelAchievement.setObjectName("labelAchievement") | |
self.labelDescription = QtWidgets.QLabel(self.frame) | |
self.labelDescription.setGeometry(QtCore.QRect(110, 58, 261, 41)) | |
font = QtGui.QFont() | |
font.setPointSize(10) | |
self.labelDescription.setFont(font) | |
self.labelDescription.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) | |
self.labelDescription.setWordWrap(True) | |
self.labelDescription.setObjectName("labelDescription") | |
self.label = QtWidgets.QLabel(self.frame) | |
self.label.setGeometry(QtCore.QRect(10, 10, 90, 90)) | |
self.label.setText("") | |
self.label.setPixmap(QtGui.QPixmap("Youngling.png")) | |
self.label.setScaledContents(True) | |
self.label.setObjectName("label") | |
MainWindow.setCentralWidget(self.centralWidget) | |
self.retranslateUi(MainWindow) | |
QtCore.QMetaObject.connectSlotsByName(MainWindow) | |
def retranslateUi(self, MainWindow): | |
_translate = QtCore.QCoreApplication.translate | |
MainWindow.setWindowTitle(_translate("MainWindow", "RetroAchievements")) | |
self.labelAchievement.setText(_translate("MainWindow", "Achievement")) | |
self.labelDescription.setText(_translate("MainWindow", "Description")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes, win32ui, win32process, win32api, win32event, win32gui, win32con | |
from ctypes import wintypes | |
import datetime, sys, signal | |
from PyQt5 import QtWidgets, QtCore | |
from PyQt5.QtWidgets import QDesktopWidget | |
from mainwindow import Ui_MainWindow | |
PROCESS_ALL_ACCESS = 0x1F0FFF | |
ReadProcessMemory = ctypes.WinDLL('kernel32',use_last_error=True).ReadProcessMemory | |
ReadProcessMemory.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPVOID, | |
ctypes.c_size_t,ctypes.POINTER(ctypes.c_size_t)] | |
ReadProcessMemory.restype = wintypes.BOOL | |
SwEp1RacerAchievements = { | |
"DETECTION": { | |
"windowName": "Episode I Racer" | |
}, | |
"FINISHED_A_RACE": {"achieved":False, | |
"name":"Youngling", | |
"desc":"You finished your first race!", | |
"requirements":{ | |
"core": {}, | |
"alt1": {1:{ | |
"size": "string7", | |
"memory": 0xE2C805, # ADDRESS_TALLY_STRING | |
"cmp": "=", | |
"value": b"Total: " | |
}}, | |
"alt2": {1:{ | |
"size": "string7", | |
"memory": 0xE2C384, # ADDRESS_RESULTS_STRING, | |
"cmp": "=", | |
"value": b"Results" | |
}} | |
}}, | |
"FIRSTPLACE_RACE1": {"achieved":False, "name":"Boonta Training Course Veteran", "desc":"First place in Boonta Training Course!", | |
"requirements":{ | |
"core": {1:{ | |
"size": "byte", | |
"memory": 0xEA02B0, # ADDRESS_RACE_SELECT, | |
"cmp": "=", | |
"value": 0 | |
}, | |
2:{ | |
"size": "byte", | |
"memory": 0xE29C1C, # ADDRESS_PLACE, | |
"cmp": "=", | |
"value": 1 | |
} | |
}, | |
"alt1": {1:{ | |
"size": "string7", | |
"memory": 0xE2C805, # ADDRESS_TALLY_STRING | |
"cmp": "=", | |
"value": b"Total: " | |
}}, | |
"alt2": {1:{ | |
"size": "string7", | |
"memory": 0xE2C384, # ADDRESS_RESULTS_STRING, | |
"cmp": "=", | |
"value": b"Results" | |
}} | |
}}, | |
"FIRSTPLACE_RACE2": {"achieved":False, "name":"Mon Gazza Speedway Veteran", "desc":"First place in Mon Gazza Speedway!", | |
"requirements":{ | |
"core": {1:{ | |
"size": "byte", | |
"memory": 0xEA02B0, # ADDRESS_RACE_SELECT, | |
"cmp": "=", | |
"value": 16 | |
}, | |
2:{ | |
"size": "byte", | |
"memory": 0xE29C1C, # ADDRESS_PLACE, | |
"cmp": "=", | |
"value": 1 | |
} | |
}, | |
"alt1": {1:{ | |
"size": "string7", | |
"memory": 0xE2C805, # ADDRESS_TALLY_STRING | |
"cmp": "=", | |
"value": b"Total: " | |
}}, | |
"alt2": {1:{ | |
"size": "string7", | |
"memory": 0xE2C384, # ADDRESS_RESULTS_STRING, | |
"cmp": "=", | |
"value": b"Results" | |
}} | |
}}, | |
} | |
def readByte(process, address): | |
buffer = ctypes.create_string_buffer(1) | |
bytes_read = ctypes.c_size_t() | |
ReadProcessMemory(process.handle, address, buffer, 1, ctypes.byref(bytes_read)) | |
last_err = ctypes.get_last_error() | |
if last_err: | |
print("Error readByte:", last_err) | |
return None | |
return buffer.raw[0] | |
def readString(process, address, length): | |
buffer = ctypes.create_string_buffer(length) | |
bytes_read = ctypes.c_size_t() | |
ReadProcessMemory(process.handle, address, buffer, length, ctypes.byref(bytes_read)) | |
last_err = ctypes.get_last_error() | |
if last_err: | |
print("Error readString:", last_err) | |
return None | |
i = buffer.raw.find(b'\x00') | |
if i == -1: | |
return buffer.raw | |
return buffer.raw[:i] | |
class PCRAApp(QtWidgets.QMainWindow, Ui_MainWindow): | |
def __init__(self, parent=None): | |
super(PCRAApp, self).__init__(parent) | |
self.setupUi(self) | |
signal.signal(signal.SIGINT, self.sigint_handler) | |
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowTransparentForInput) | |
self.check_timer = None | |
self.swep1timer = None | |
self.should_close = False | |
ag = QDesktopWidget().availableGeometry() | |
x = ag.width() - self.width() - 20 | |
y = ag.height() - self.height() - 20 | |
self.move(x, y) | |
#self.unlockAchievement("FINISHED_A_RACE", SwEp1RacerAchievements["FINISHED_A_RACE"]) | |
self.check_games() | |
def sigint_handler(self, *args): | |
self.should_close = True | |
self.close() | |
QtCore.QCoreApplication.instance().quit() | |
def printWindowStyles(self): | |
print("WindowStyle 0x%X" % self.getWindowStyle()) | |
print("WindowStyleEx 0x%X" % self.getWindowStyleEx()) | |
def hwnd(self): | |
#win32ui.FindWindow(None, self.windowTitle()).GetSafeHwnd() | |
return self.winId() | |
def getWindowStyleEx(self): | |
return win32gui.GetWindowLong(self.hwnd(), win32con.GWL_EXSTYLE ) | |
def getWindowStyle(self): | |
return win32gui.GetWindowLong(self.hwnd(), win32con.GWL_STYLE ) | |
"""def make_transparent(self): | |
lExStyle = self.getWindowStyleEx() | |
lExStyle = lExStyle | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT | |
lExStyle = lExStyle & ~(win32con.WS_EX_DLGMODALFRAME | win32con.WS_EX_CLIENTEDGE | win32con.WS_EX_STATICEDGE) | |
print("lExStyle", lExStyle) | |
win32gui.SetWindowLong(self.hwnd(), win32con.GWL_EXSTYLE, lExStyle ) | |
lStyle = self.getWindowStyle() | |
lStyle = lStyle & ~(win32con.WS_CAPTION | win32con.WS_THICKFRAME | win32con.WS_MINIMIZE | win32con.WS_MAXIMIZE | win32con.WS_SYSMENU) | |
print("lstyle", lStyle) | |
win32gui.SetWindowLong(self.hwnd(), win32con.GWL_STYLE, lStyle ) | |
#self.setFixedSize(self.width()-16, self.height()-38) | |
win32gui.SetWindowPos(self.hwnd(), win32con.HWND_TOP, 0, 0, self.width(), self.height(), win32con.SWP_NOMOVE) | |
#SetLayeredWindowAttributes(self.hwnd(), win32api.RGB(255, 255, 255), 0, win32con.LWA_COLORKEY)""" | |
def unlockAchievement(self, ach_name, ach): | |
if ach['achieved'] == False: | |
ach['achieved'] = True | |
msg = "Unlocking achievement: " + ach['name'] + " - " + ach['desc'] | |
print(msg) | |
self.labelAchievement.setText(ach['name']) | |
self.labelDescription.setText(ach['desc']) | |
self.printWindowStyles() | |
self.show() | |
self.printWindowStyles() | |
#self.plainTextEdit.appendPlainText(msg) | |
#vsb = self.plainTextEdit.verticalScrollBar() | |
#vsb.setValue(vsb.maximum()) | |
def swep1racer_parsetime(self, string): | |
if string.count(":") == 2: | |
x = datetime.datetime.strptime(string,'%H:%M:%S.%f') | |
if string.count(":") == 1: | |
x = datetime.datetime.strptime(string,'%M:%S.%f') | |
if string.count(":") == 0: | |
x = datetime.datetime.strptime(string,'%S.%f') | |
return datetime.timedelta(hours=x.hour, | |
minutes=x.minute, | |
seconds=x.second, | |
microseconds=x.microsecond).total_seconds() | |
def testRequirement(self, proc, req): | |
if req["size"].startswith("string"): | |
s_len = int(req["size"][6:]) | |
return readString(proc, req["memory"], s_len) == req["value"] | |
if req["size"] == "byte": | |
return readByte(proc, req["memory"]) == req["value"] | |
def testReqGroup(self, proc, group): | |
if len(group) == 0: | |
return True | |
reqResults = [self.testRequirement(proc, group[i]) for i in group] | |
return all(reqResults) | |
def checkAchievement(self, proc, ach_name, ach): | |
if "requirements" not in ach: | |
return | |
reqs = ach["requirements"] | |
altRes = [self.testReqGroup(proc, reqs[grp]) for grp in reqs if grp.startswith("alt")] | |
if self.testReqGroup(proc, reqs["core"]): | |
if len(altRes) == 0 or any(altRes): | |
self.unlockAchievement(ach_name, ach) | |
def checkAchievements(self, proc, achievements): | |
for ach_name, ach in achievements.items(): | |
self.checkAchievement(proc, ach_name, ach) | |
def check_swep1racer(self): | |
if self.should_close: | |
return | |
if win32event.WaitForSingleObject(self.swep1process, 0) == 0: | |
self.swep1timer = None | |
return | |
self.checkAchievements(self.swep1process, SwEp1RacerAchievements) | |
self.swep1timer = QtCore.QTimer() | |
self.swep1timer.timeout.connect(self.check_swep1racer) | |
self.swep1timer.start(200) | |
def is_swep1racer_running(self): | |
try: | |
HWND = win32ui.FindWindow(None, "Episode I Racer").GetSafeHwnd() | |
PID = win32process.GetWindowThreadProcessId(HWND)[1] | |
return win32api.OpenProcess(PROCESS_ALL_ACCESS, 0, PID) | |
except win32ui.error: | |
return False | |
def check_games(self): | |
if self.should_close: | |
return | |
if self.swep1timer is None: | |
self.swep1process = self.is_swep1racer_running() | |
if self.swep1process != False: | |
self.check_swep1racer() | |
self.check_timer = QtCore.QTimer() | |
self.check_timer.timeout.connect(self.check_games) | |
self.check_timer.start(500) | |
def closeEvent(self, event): | |
self.should_close = True | |
def main(): | |
app = QtWidgets.QApplication(sys.argv) | |
form = PCRAApp() | |
sys.exit(app.exec_()) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment