231 lines
7.9 KiB
Python
Executable File
231 lines
7.9 KiB
Python
Executable File
# Helper tool to quickly convert a string representation of either a hexadecimal or decimal
|
|
# u32 into the corresponding set of collision flags as defined in gtaArchetype.h
|
|
#
|
|
# Author: Richard Archibald
|
|
# Started: 20/1/12
|
|
|
|
from __future__ import with_statement
|
|
|
|
# Python libraries needed:
|
|
import re
|
|
import traceback
|
|
import functools
|
|
from Tkinter import *
|
|
|
|
|
|
#-----------------------------------------------------
|
|
# Local function definitions
|
|
#-----------------------------------------------------
|
|
def parseLine(line):
|
|
"""
|
|
Decides what is on a line in the enum definition. Skips any whitespace or comment
|
|
lines.
|
|
"""
|
|
|
|
# Skip any line which doesn't meet expectations (e.g. a comment or whitespace).
|
|
if re.match('^\ *GTA_.*=\ BIT([0-9]*)', line) and not re.match('^.*//', line):
|
|
# Split enum def lines into the name and value.
|
|
tokens = line.split("=")
|
|
value = tokens[0].strip(",").strip() # Get rid of any remaining white-space too!
|
|
key = int(tokens[1].split("(")[1].strip().split(")")[0].strip())
|
|
return (key, value)
|
|
|
|
def readEnumNames():
|
|
"""
|
|
Populate a dictionary with the data in gtaArchetype.h so that
|
|
we can match include flag values to the enum value in C++ code.
|
|
"""
|
|
|
|
enum_dict = {}
|
|
with open("x:/gta5/src/dev_ng/game/physics/gtaArchetype.h", "r") as enumDefFile:
|
|
# Read until we hit the enum definition.
|
|
for line in enumDefFile:
|
|
line = line.strip()
|
|
if line.find("namespace ArchetypeFlags")!=-1: break
|
|
|
|
# Skip lines until we hit the actual enum definition.
|
|
for line in enumDefFile:
|
|
line = line.strip()
|
|
if line.find("enum")!=-1: break
|
|
for line in enumDefFile:
|
|
line = line.strip()
|
|
if line.find("{")!=-1: break
|
|
|
|
# Add definitions to the dictionary until we reach the end tag.
|
|
for line in enumDefFile:
|
|
line = line.strip()
|
|
if line.find("};")!=-1: break
|
|
returnValue = parseLine(line)
|
|
if returnValue:
|
|
key = returnValue[0]
|
|
value = returnValue[1]
|
|
enum_dict[key] = value
|
|
|
|
return enum_dict
|
|
|
|
|
|
#--------------------------------
|
|
class exceptionDecorator(object):
|
|
#--------------------------------
|
|
"""Used to pop-up a little exception window when bad things happen."""
|
|
|
|
def __init__(self, fn):
|
|
self.fn = fn
|
|
|
|
def __get__(self, obj, objtype):
|
|
"""Support instance methods."""
|
|
return functools.partial(self.__call__, obj)
|
|
|
|
def __call__(self, *args):
|
|
try:
|
|
self.fn(*args)
|
|
except:
|
|
root = Tk()
|
|
root.title("UNHANDLED EXCEPTION:")
|
|
def close_and_quit():
|
|
Tk().quit()
|
|
root.protocol("WM_DELETE_WINDOW", close_and_quit)
|
|
# Get screen width and height
|
|
screen_width = root.winfo_screenwidth()
|
|
screen_height = root.winfo_screenheight()
|
|
# Size and position window near task bar.
|
|
wx = screen_width/2 - 300
|
|
wy = screen_height/2 - 100
|
|
root.geometry('+%d+%d' % (wx, wy))
|
|
e_type = sys.exc_info()[1]
|
|
e_text = "\"%s\"\n\n%s" % (e_type, traceback.format_exc())
|
|
msg = Text(root)
|
|
msg.insert(INSERT, e_text)
|
|
msg.pack()
|
|
closebutton = Button(root, text="Close.", command=close_and_quit)
|
|
closebutton.pack()
|
|
root.mainloop()
|
|
Tk().quit()
|
|
finally: # Close any files or whatever.
|
|
pass
|
|
|
|
|
|
#---------
|
|
class App:
|
|
#---------
|
|
@exceptionDecorator
|
|
def __init__(self):
|
|
self.root = Tk()
|
|
self.root.title("GTA collision flag helper tool")
|
|
|
|
# Get screen width and height
|
|
screen_width = self.root.winfo_screenwidth()
|
|
screen_height = self.root.winfo_screenheight()
|
|
|
|
# Size and position window near task bar.
|
|
window_w = 300
|
|
window_h = 490
|
|
wx = 100
|
|
wy = screen_height - window_h - 100
|
|
self.root.geometry('%dx%d+%d+%d' % (window_w, window_h, wx, wy))
|
|
|
|
# Layout of frames:
|
|
# ---------------------- frmMain
|
|
# | |----------------- |
|
|
# | |frmH1 | |
|
|
# | |----------------- |
|
|
# | |----------------- |
|
|
# | |frmH2 | |
|
|
# | |----------------- |
|
|
# | ... |
|
|
# ----------------------
|
|
self.frmMain = Frame(self.root)
|
|
self.frmMain.pack()
|
|
#
|
|
self.frmH1 = Frame(self.frmMain)
|
|
self.frmH1.pack(side=TOP)
|
|
#
|
|
self.frmH2 = Frame(self.frmMain)
|
|
self.frmH2.pack(side=TOP)
|
|
#
|
|
self.frmH3 = Frame(self.frmMain)
|
|
self.frmH3.pack(side=TOP)
|
|
#
|
|
self.frmH4 = Frame(self.frmMain, bd=3, relief=SUNKEN)
|
|
self.frmH4.pack(side=BOTTOM)
|
|
|
|
# Create the widgets.
|
|
self.status = Label(self.frmH1, text='Enter collision flags (use 0x for hex):')
|
|
self.status.pack(side=LEFT)
|
|
#
|
|
self.col_flags = Entry(self.frmH1)
|
|
self.col_flags.bind('<Return>', lambda e: self.lookup_flags())
|
|
self.col_flags.bind('<Escape>', lambda e: self.exit_app())
|
|
self.col_flags.pack(side=RIGHT)
|
|
#
|
|
self.msg = Label(self.frmH2, text='', width=window_w)
|
|
self.msg.pack(side=LEFT)
|
|
#
|
|
self.okButton = Button(self.frmH3, text="Ok.", command=self.lookup_flags, \
|
|
default=ACTIVE, state=ACTIVE)
|
|
self.okButton.pack(side=LEFT)
|
|
#
|
|
self.cancelButton = Button(self.frmH3, text="Cancel.", command=self.exit_app)
|
|
self.cancelButton.pack(side=RIGHT)
|
|
# The results area.
|
|
self.results = Label(self.frmH4, text='', width=75)
|
|
self.results.pack(side=BOTTOM)
|
|
|
|
# Run the lookup function as part of initialisation.
|
|
self.lookup_flags()
|
|
|
|
@exceptionDecorator
|
|
def exit_app(self):
|
|
self.root.destroy()
|
|
self.root.quit()
|
|
|
|
@exceptionDecorator
|
|
def lookup_flags(self):
|
|
"""Check the user defined flag value against the enums and display the appropriate flags."""
|
|
self.enum_dict = readEnumNames()
|
|
self.msg.config(text='')
|
|
user_value = 0
|
|
user_flags = self.col_flags.get()
|
|
if user_flags == '':
|
|
user_flags = '0'
|
|
flag_text = ''
|
|
|
|
class OutOfRangeError(Exception):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
try:
|
|
if re.match('^0x', user_flags):
|
|
user_value = long(user_flags, 16)
|
|
self.msg.config(text='Converting from hex...', fg="darkgreen")
|
|
else:
|
|
user_value = long(user_flags, 10)
|
|
self.msg.config(text='Converting from decimal...', fg="darkgreen")
|
|
if user_value >= long(1<<32): raise OutOfRangeError(user_value)
|
|
for k in self.enum_dict:
|
|
if (long(1<<k) & user_value) > 0:
|
|
flag_text += "%s\n" % self.enum_dict[k]
|
|
else:
|
|
flag_text += "---\n"
|
|
self.results.config(text=flag_text)
|
|
except ValueError:
|
|
# Catch bad user input data and inform them what they are doing wrong.
|
|
self.msg.config(text=sys.exc_info()[1], fg="red")
|
|
except OutOfRangeError:
|
|
# Value user entered was bigger than a u32.
|
|
self.msg.config(text="ERROR: value entered is greater than max for u32!", fg="red")
|
|
|
|
@exceptionDecorator
|
|
def run(self):
|
|
self.root.mainloop()
|
|
|
|
|
|
#-----------------------------------------------------
|
|
# Main
|
|
#-----------------------------------------------------
|
|
if __name__ == "__main__":
|
|
app = App()
|
|
app.run()
|