mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
Merge remote-tracking branch 'origin/master' into dev/bg3_support
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import mobase
|
||||
from PyQt6.QtCore import QDir, QFileInfo
|
||||
|
||||
from ..basic_features import BasicLocalSavegames
|
||||
from ..basic_game import BasicGame
|
||||
from ..steam_utils import find_steam_path
|
||||
|
||||
|
||||
# Lifted from https://github.com/ModOrganizer2/modorganizer-basic_games/blob/71dbb8c557d43cba9d290674a332e7ecd1650261/games/game_darkestdungeon.py
|
||||
class ArkhamCityModDataChecker(mobase.ModDataChecker):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.validDirNames = [
|
||||
"config",
|
||||
"cookedpcconsole",
|
||||
"localization",
|
||||
"movies",
|
||||
"moviesstereo",
|
||||
"splash",
|
||||
]
|
||||
|
||||
def dataLooksValid(
|
||||
self, filetree: mobase.IFileTree
|
||||
) -> mobase.ModDataChecker.CheckReturn:
|
||||
for entry in filetree:
|
||||
if not entry.isDir():
|
||||
continue
|
||||
if entry.name().casefold() in self.validDirNames:
|
||||
return mobase.ModDataChecker.VALID
|
||||
return mobase.ModDataChecker.INVALID
|
||||
|
||||
|
||||
class ArkhamCityGame(BasicGame):
|
||||
Name = "Batman: Arkham City Plugin"
|
||||
Author = "Paynamia"
|
||||
Version = "0.5.3"
|
||||
|
||||
GameName = "Batman: Arkham City"
|
||||
GameShortName = "batmanarkhamcity"
|
||||
GameNexusId = 372
|
||||
GameSteamId = 200260
|
||||
GameGogId = 1260066469
|
||||
GameEpicId = "Egret"
|
||||
GameBinary = "Binaries/Win32/BatmanAC.exe"
|
||||
GameLauncher = "Binaries/Win32/BmLauncher.exe"
|
||||
GameDataPath = "BmGame"
|
||||
GameDocumentsDirectory = (
|
||||
"%DOCUMENTS%/WB Games/Batman Arkham City GOTY/BmGame/Config"
|
||||
)
|
||||
GameIniFiles = ["UserEngine.ini", "UserGame.ini", "UserInput.ini"]
|
||||
GameSaveExtension = "sgd"
|
||||
|
||||
# This will only detect saves from the earliest-created Steam profile on the user's PC.
|
||||
def savesDirectory(self) -> QDir:
|
||||
docSaves = QDir(self.documentsDirectory().cleanPath("../../SaveData"))
|
||||
if self.is_steam():
|
||||
if (steamDir := find_steam_path()) is None:
|
||||
return docSaves
|
||||
for child in steamDir.joinpath("userdata").iterdir():
|
||||
if not child.is_dir() or child.name == "0":
|
||||
continue
|
||||
steamSaves = child.joinpath("200260", "remote")
|
||||
if steamSaves.is_dir():
|
||||
return QDir(str(steamSaves))
|
||||
else:
|
||||
return docSaves
|
||||
else:
|
||||
return docSaves
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._register_feature(ArkhamCityModDataChecker())
|
||||
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
return [
|
||||
mobase.ExecutableInfo(
|
||||
"Batman: Arkham City",
|
||||
QFileInfo(self.gameDirectory(), "Binaries/Win32/BatmanAC.exe"),
|
||||
),
|
||||
mobase.ExecutableInfo(
|
||||
"Arkham City Launcher",
|
||||
QFileInfo(self.gameDirectory(), "Binaries/Win32/BmLauncher.exe"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,153 @@
|
||||
import re
|
||||
|
||||
import mobase
|
||||
from mobase import FileTreeEntry, IFileTree, ModDataChecker
|
||||
|
||||
from ..basic_features import BasicModDataChecker
|
||||
from ..basic_game import BasicGame
|
||||
|
||||
_extention_pattern = re.compile("\\.(upk|umap|u|int|dll|exe)$", re.I)
|
||||
_mapslot_pattern = re.compile("^Mapslot\\d\\d?\\.umap$", re.I)
|
||||
|
||||
_mod_dirs = {
|
||||
"Binaries".casefold(): "/",
|
||||
"WillowGame".casefold(): "/",
|
||||
"CookedPC".casefold(): "WillowGame/",
|
||||
"Localization".casefold(): "WillowGame/",
|
||||
"Maps".casefold(): "WillowGame/CookedPC/",
|
||||
}
|
||||
|
||||
_slots_path = "WillowGame/CookedPC/Maps/MapSlots"
|
||||
|
||||
|
||||
def _check_filetree(
|
||||
filetree: IFileTree | FileTreeEntry, recurse: bool
|
||||
) -> ModDataChecker.CheckReturn:
|
||||
if filetree.isFile():
|
||||
if _extention_pattern.search(filetree.name()):
|
||||
if _mapslot_pattern.search(filetree.name()):
|
||||
return ModDataChecker.FIXABLE
|
||||
else:
|
||||
return ModDataChecker.INVALID
|
||||
elif recurse and isinstance(filetree, IFileTree):
|
||||
status = ModDataChecker.VALID
|
||||
for child in filetree:
|
||||
child_status = _check_filetree(child, True)
|
||||
if child_status is ModDataChecker.INVALID:
|
||||
return ModDataChecker.INVALID
|
||||
elif child_status is ModDataChecker.FIXABLE:
|
||||
status = ModDataChecker.FIXABLE
|
||||
return status
|
||||
|
||||
return ModDataChecker.VALID
|
||||
|
||||
|
||||
def _get_nest(filetree: IFileTree) -> IFileTree | None:
|
||||
children = tuple(filetree)
|
||||
if (
|
||||
len(children) == 1
|
||||
and children[0].isDir()
|
||||
and isinstance(children[0], IFileTree)
|
||||
and _mod_dirs.get(children[0].name().casefold()) is None
|
||||
):
|
||||
return children[0]
|
||||
return None
|
||||
|
||||
|
||||
def _get_slotstree(roottree: IFileTree) -> IFileTree:
|
||||
slotstree: IFileTree | None = roottree.find(_slots_path) # type: ignore
|
||||
if slotstree is None:
|
||||
slotstree = roottree.addDirectory(_slots_path)
|
||||
return slotstree
|
||||
|
||||
|
||||
def _fix_mapslots(filetree: IFileTree, roottree: IFileTree) -> None:
|
||||
for child in filetree:
|
||||
if child.isDir() and isinstance(child, IFileTree):
|
||||
_fix_mapslots(child, roottree)
|
||||
elif _mapslot_pattern.search(child.name()):
|
||||
child.moveTo(_get_slotstree(roottree))
|
||||
|
||||
|
||||
class Borderlands1ModDataChecker(BasicModDataChecker):
|
||||
def dataLooksValid(self, filetree: IFileTree) -> ModDataChecker.CheckReturn:
|
||||
parent = filetree.parent()
|
||||
if parent is not None:
|
||||
return self.dataLooksValid(parent)
|
||||
|
||||
status = ModDataChecker.VALID
|
||||
|
||||
nest = _get_nest(filetree)
|
||||
if nest is not None:
|
||||
status = ModDataChecker.FIXABLE
|
||||
filetree = nest
|
||||
|
||||
if _check_filetree(filetree, False) is ModDataChecker.INVALID:
|
||||
return ModDataChecker.INVALID
|
||||
|
||||
slotstree = filetree.find(_slots_path)
|
||||
if slotstree is not None and not slotstree.isDir():
|
||||
return ModDataChecker.INVALID
|
||||
|
||||
for child in filetree:
|
||||
if child.isDir():
|
||||
destination = _mod_dirs.get(child.name().casefold())
|
||||
if destination is None:
|
||||
child_status = _check_filetree(child, True)
|
||||
if child_status is ModDataChecker.INVALID:
|
||||
return ModDataChecker.INVALID
|
||||
elif child_status is ModDataChecker.FIXABLE:
|
||||
status = ModDataChecker.FIXABLE
|
||||
elif destination != "/":
|
||||
status = ModDataChecker.FIXABLE
|
||||
elif _mapslot_pattern.search(child.name()):
|
||||
status = ModDataChecker.FIXABLE
|
||||
elif _extention_pattern.search(child.name()):
|
||||
return ModDataChecker.INVALID
|
||||
return status
|
||||
|
||||
def fix(self, filetree: IFileTree) -> IFileTree:
|
||||
nest = _get_nest(filetree)
|
||||
if nest is not None:
|
||||
conflict: FileTreeEntry | None = None
|
||||
for child in tuple(nest):
|
||||
if not child.moveTo(filetree):
|
||||
conflict = child
|
||||
conflict.detach()
|
||||
filetree.remove(nest)
|
||||
if conflict is not None:
|
||||
conflict.moveTo(filetree)
|
||||
|
||||
for child in tuple(filetree):
|
||||
if child.isDir() and isinstance(child, IFileTree):
|
||||
destination = _mod_dirs.get(child.name().casefold())
|
||||
if destination is None:
|
||||
_fix_mapslots(child, filetree)
|
||||
elif destination != "/":
|
||||
filetree.move(child, destination)
|
||||
elif _mapslot_pattern.search(child.name()):
|
||||
child.moveTo(_get_slotstree(filetree))
|
||||
|
||||
return filetree
|
||||
|
||||
|
||||
class Borderlands1Game(BasicGame):
|
||||
Name = "Borderlands 1 Support Plugin"
|
||||
Author = "Miner Of Worlds, RedxYeti, mopioid"
|
||||
Version = "1.0.0"
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._register_feature(Borderlands1ModDataChecker())
|
||||
return True
|
||||
|
||||
GameName = "Borderlands"
|
||||
GameShortName = "Borderlands"
|
||||
GameNexusName = "Borderlands GOTY"
|
||||
GameSteamId = 8980
|
||||
GameBinary = "Binaries/Borderlands.exe"
|
||||
GameDataPath = "."
|
||||
GameSaveExtension = "sav"
|
||||
GameDocumentsDirectory = "%DOCUMENTS%/My Games/Borderlands/"
|
||||
GameSavesDirectory = "%GAME_DOCUMENTS%/savedata"
|
||||
GameIniFiles = "%GAME_DOCUMENTS%/WillowGame/Config"
|
||||
@@ -327,7 +327,7 @@ class Cyberpunk2077Game(BasicGame):
|
||||
organizer,
|
||||
archive=ModListFile(
|
||||
Path("archive/pc/mod/modlist.txt"),
|
||||
"archive/pc/mod/*",
|
||||
"archive/pc/mod/*.archive",
|
||||
reversed_priority=bool(self._get_setting("reverse_archive_load_order")),
|
||||
),
|
||||
redmod=ModListFile(
|
||||
|
||||
Reference in New Issue
Block a user