-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathBindFile.py
132 lines (105 loc) · 5.15 KB
/
BindFile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import re
import inspect
from pathlib import PurePath, Path, PureWindowsPath
from BLF import BLF
class KeyBind():
def __init__(self, key, name, page, contents = []):
if type(contents) == str: contents = [contents]
self.Key = key # actual key combo
self.Name = name # friendly name, ie, "Select All Pets"
self.Page = page # which tab the bind originated on
self.Contents = contents # a list of strings to '$$'-join to create the actual payload
# factory for PopulateBindFiles to use
def MakeFileKeyBind(self, contents):
if type(contents) == str: contents = [contents]
# changing self.Contents and reusing self over and over broke horribly in SoD.
# Therefore, we just make a new KeyBind object. Maybe investigate someday.
return KeyBind(self.Key, self.Name, self.Page, contents)
def GetKeyBindString(self):
payload = '$$'.join([i for i in self.Contents if i])
# remove any initial $$ if we snuck in here with it.
payload = re.sub(r'^\$\$', '', payload)
# and any doubled up '$$'
payload = re.sub(r'\$\$\$\$', '$$', payload)
return f'{self.Key} "{payload}"\n'
class BindFile():
def __init__(self, profile, pathbits:PurePath):
self.Profile = profile
self.BindsDir = profile.BindsDir()
self.GameBindsDir = profile.GameBindsDir()
self.Path = Path (self.BindsDir, pathbits)
self.GamePath = PureWindowsPath(self.GameBindsDir, pathbits)
self.KeyBinds = {}
def SetBind(self, keybind:KeyBind|str, name:str = '', page = None, contents:str|list = ''):
# we can either be called with a KeyBind, in which case we're golden, or with
# four strings, in which case we need to roll a KeyBind. Someday pick one scheme.
if isinstance(keybind, str):
if name and not contents: # got called as (key, contents), this is bad.
currframe = inspect.currentframe()
if currframe:
prevFrame = currframe.f_back
if prevFrame:
(filen, line, funcn, _, _) = inspect.getframeinfo(prevFrame)
raise(Exception(f"SetBind called old way from {filen}, {funcn}, {line} -- PROBABLY BROKEN"))
keybind = KeyBind(keybind, name, page, contents)
if not keybind.Key: return
self.KeyBinds[keybind.Key] = keybind
def BaseReset(self):
return f'{BLF()} {PureWindowsPath(self.GameBindsDir) / "reset.txt"}'
def BLF(self):
return f'{BLF()} {self.GamePath}'
def Write(self):
try:
self.Path.parent.mkdir(parents = True, exist_ok = True)
except Exception as e:
raise Exception(f"Can't make bindfile parent dirs {self.Path.parent} : {e}")
try:
self.Path.touch(exist_ok = True)
except Exception as e:
raise Exception(f"Can't instantiate bindfile {self}: {e}")
# duplicate citybinder's (modified) logic exactly
def getMainKey(testkey):
str = testkey or "UNBOUND"
str = str.upper()
str = re.sub(r'LSHIFT', '', str)
str = re.sub(r'RSHIFT', '', str)
str = re.sub(r'SHIFT', '', str)
str = re.sub(r'LCTRL', '', str)
str = re.sub(r'RCTRL', '', str)
str = re.sub(r'CTRL', '', str)
str = re.sub(r'LALT', '', str)
str = re.sub(r'RALT', '', str)
str = re.sub(r'ALT', '', str)
str = re.sub(r'\+', "", str)
if str == '':
rval = testkey
else:
rval = str
if testkey != str: rval = rval + " " + testkey
return rval
sortedKeyBinds = sorted(self.KeyBinds, key = getMainKey)
output = ''
for keybind in sortedKeyBinds:
kb = self.KeyBinds[keybind]
payload = kb.GetKeyBindString()
if len(payload) > 255:
raise Exception(f"Bind '{kb.Key}' from page '{kb.Page}' is {len(payload)} characters long - this will cause badness in-game!")
output = output + payload
if output:
try:
self.Path.write_text(output, newline = '\r\n')
except Exception as e:
raise Exception("Can't write to bindfile {self.Path}: {e}")
# delete the bindfile. THIS DOES NOT ASK FOR CONFIRMATION
def Delete(self):
if not self.Path:
raise Exception(f"Trying to delete a bindfile with empty Path")
# TODO do we actually want to check existence? We want to pass a bunch of possible
# files through here but they might not all be there, almost certainly some won't
if not self.Path.exists():
raise Exception(f"Trying to delete nonexistant bindfile {self}")
if not re.match(self.Profile.BindsDir(), str(self.Path)):
raise Exception(f"Trying to delete a bindfile {self} not in {self.Profile.BindsDir()}!")
# TODO do we need more sanity checking? Probably
#self.Path.unlink()
print(f"Would be deleting BindFile {self}")