Skip to content

Commit eb3e1e9

Browse files
authored
Add Borderlands 1 support plugin (#159)
1 parent a8613c4 commit eb3e1e9

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

games/game_borderlands1.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import re
2+
3+
import mobase
4+
from mobase import FileTreeEntry, IFileTree, ModDataChecker
5+
6+
from ..basic_features import BasicModDataChecker
7+
from ..basic_game import BasicGame
8+
9+
_extention_pattern = re.compile("\\.(upk|umap|u|int|dll|exe)$", re.I)
10+
_mapslot_pattern = re.compile("^Mapslot\\d\\d?\\.umap$", re.I)
11+
12+
_mod_dirs = {
13+
"Binaries".casefold(): "/",
14+
"WillowGame".casefold(): "/",
15+
"CookedPC".casefold(): "WillowGame/",
16+
"Localization".casefold(): "WillowGame/",
17+
"Maps".casefold(): "WillowGame/CookedPC/",
18+
}
19+
20+
_slots_path = "WillowGame/CookedPC/Maps/MapSlots"
21+
22+
23+
def _check_filetree(
24+
filetree: IFileTree | FileTreeEntry, recurse: bool
25+
) -> ModDataChecker.CheckReturn:
26+
if filetree.isFile():
27+
if _extention_pattern.search(filetree.name()):
28+
if _mapslot_pattern.search(filetree.name()):
29+
return ModDataChecker.FIXABLE
30+
else:
31+
return ModDataChecker.INVALID
32+
elif recurse and isinstance(filetree, IFileTree):
33+
status = ModDataChecker.VALID
34+
for child in filetree:
35+
child_status = _check_filetree(child, True)
36+
if child_status is ModDataChecker.INVALID:
37+
return ModDataChecker.INVALID
38+
elif child_status is ModDataChecker.FIXABLE:
39+
status = ModDataChecker.FIXABLE
40+
return status
41+
42+
return ModDataChecker.VALID
43+
44+
45+
def _get_nest(filetree: IFileTree) -> IFileTree | None:
46+
children = tuple(filetree)
47+
if (
48+
len(children) == 1
49+
and children[0].isDir()
50+
and isinstance(children[0], IFileTree)
51+
and _mod_dirs.get(children[0].name().casefold()) is None
52+
):
53+
return children[0]
54+
return None
55+
56+
57+
def _get_slotstree(roottree: IFileTree) -> IFileTree:
58+
slotstree: IFileTree | None = roottree.find(_slots_path) # type: ignore
59+
if slotstree is None:
60+
slotstree = roottree.addDirectory(_slots_path)
61+
return slotstree
62+
63+
64+
def _fix_mapslots(filetree: IFileTree, roottree: IFileTree) -> None:
65+
for child in filetree:
66+
if child.isDir() and isinstance(child, IFileTree):
67+
_fix_mapslots(child, roottree)
68+
elif _mapslot_pattern.search(child.name()):
69+
child.moveTo(_get_slotstree(roottree))
70+
71+
72+
class Borderlands1ModDataChecker(BasicModDataChecker):
73+
def dataLooksValid(self, filetree: IFileTree) -> ModDataChecker.CheckReturn:
74+
parent = filetree.parent()
75+
if parent is not None:
76+
return self.dataLooksValid(parent)
77+
78+
status = ModDataChecker.VALID
79+
80+
nest = _get_nest(filetree)
81+
if nest is not None:
82+
status = ModDataChecker.FIXABLE
83+
filetree = nest
84+
85+
if _check_filetree(filetree, False) is ModDataChecker.INVALID:
86+
return ModDataChecker.INVALID
87+
88+
slotstree = filetree.find(_slots_path)
89+
if slotstree is not None and not slotstree.isDir():
90+
return ModDataChecker.INVALID
91+
92+
for child in filetree:
93+
if child.isDir():
94+
destination = _mod_dirs.get(child.name().casefold())
95+
if destination is None:
96+
child_status = _check_filetree(child, True)
97+
if child_status is ModDataChecker.INVALID:
98+
return ModDataChecker.INVALID
99+
elif child_status is ModDataChecker.FIXABLE:
100+
status = ModDataChecker.FIXABLE
101+
elif destination != "/":
102+
status = ModDataChecker.FIXABLE
103+
elif _mapslot_pattern.search(child.name()):
104+
status = ModDataChecker.FIXABLE
105+
elif _extention_pattern.search(child.name()):
106+
return ModDataChecker.INVALID
107+
return status
108+
109+
def fix(self, filetree: IFileTree) -> IFileTree:
110+
nest = _get_nest(filetree)
111+
if nest is not None:
112+
conflict: FileTreeEntry | None = None
113+
for child in tuple(nest):
114+
if not child.moveTo(filetree):
115+
conflict = child
116+
conflict.detach()
117+
filetree.remove(nest)
118+
if conflict is not None:
119+
conflict.moveTo(filetree)
120+
121+
for child in tuple(filetree):
122+
if child.isDir() and isinstance(child, IFileTree):
123+
destination = _mod_dirs.get(child.name().casefold())
124+
if destination is None:
125+
_fix_mapslots(child, filetree)
126+
elif destination != "/":
127+
filetree.move(child, destination)
128+
elif _mapslot_pattern.search(child.name()):
129+
child.moveTo(_get_slotstree(filetree))
130+
131+
return filetree
132+
133+
134+
class Borderlands1Game(BasicGame):
135+
Name = "Borderlands 1 Support Plugin"
136+
Author = "Miner Of Worlds, RedxYeti, mopioid"
137+
Version = "1.0.0"
138+
139+
def init(self, organizer: mobase.IOrganizer) -> bool:
140+
super().init(organizer)
141+
self._register_feature(Borderlands1ModDataChecker())
142+
return True
143+
144+
GameName = "Borderlands"
145+
GameShortName = "Borderlands"
146+
GameNexusName = "Borderlands GOTY"
147+
GameSteamId = 8980
148+
GameBinary = "Binaries/Borderlands.exe"
149+
GameDataPath = "."
150+
GameSaveExtension = "sav"
151+
GameDocumentsDirectory = "%DOCUMENTS%/My Games/Borderlands/"
152+
GameSavesDirectory = "%GAME_DOCUMENTS%/savedata"
153+
GameIniFiles = "%GAME_DOCUMENTS%/WillowGame/Config"

0 commit comments

Comments
 (0)