diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ef00467 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,44 @@ +name: Build Basic Games Plugin + +on: + push: + branches: [master] + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + runs-on: windows-2022 + steps: + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set environmental variables + shell: bash + run: | + echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + path: build + + - name: Configure Basic Games Plugin build + working-directory: ${{ github.workspace }}/build + shell: pwsh + run: | + cmake --preset vs2022-windows "-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install" "-DVCPKG_MANIFEST_FEATURES=standalone" + + - name: Build Basic Games Plugin + working-directory: ${{ github.workspace }}/build + run: cmake --build vsbuild --config RelWithDebInfo + + - name: Install Basic Games Plugin + working-directory: ${{ github.workspace }}/build + run: cmake --install vsbuild --config RelWithDebInfo + + - name: Upload Basic Games Plugin artifact + uses: actions/upload-artifact@master + with: + name: basic_games + path: ${{ github.workspace }}/install/bin/plugins diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8070c..288c2a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,8 @@ cmake_minimum_required(VERSION 3.16) -if(DEFINED DEPENDENCIES_DIR) - include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/mo2.cmake) -else() - include(${CMAKE_CURRENT_LIST_DIR}/../cmake_common/mo2.cmake) -endif() - project(basic_games LANGUAGES NONE) + +find_package(mo2-cmake CONFIG REQUIRED) + add_custom_target(basic_games ALL) -mo2_configure_python(basic_games - MODULE - TRANSLATIONS OFF) +mo2_configure_python(basic_games MODULE TRANSLATIONS OFF) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..acc6d97 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,17 @@ +{ + "configurePresets": [ + { + "binaryDir": "${sourceDir}/vsbuild", + "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "generator": "Visual Studio 17 2022", + "name": "vs2022-windows" + } + ], + "buildPresets": [ + { + "name": "vs2022-windows", + "configurePreset": "vs2022-windows" + } + ], + "version": 4 +} diff --git a/games/game_oblivion_remaster.py b/games/game_oblivion_remaster.py index 84731ad..218bb04 100644 --- a/games/game_oblivion_remaster.py +++ b/games/game_oblivion_remaster.py @@ -13,11 +13,9 @@ import mobase from ..basic_features import BasicGameSaveGameInfo from ..basic_game import BasicGame -from .oblivion_remaster.constants import PLUGIN_NAME +from .oblivion_remaster.constants import DEFAULT_UE4SS_MODS, PLUGIN_NAME, UE4SSModInfo from .oblivion_remaster.paks.widget import PaksTabWidget -from .oblivion_remaster.ue4ss.widget import UE4SSModInfo, UE4SSTabWidget - -DEFAULT_UE4SS_MODS = ["BPML_GenericFunctions", "BPModLoaderMod"] +from .oblivion_remaster.ue4ss.widget import UE4SSTabWidget def getLootPath() -> Path | None: @@ -286,11 +284,11 @@ class OblivionRemasteredGame( if not ue4ss_mods_txt.exists(): with open(ue4ss_mods_txt.absoluteFilePath(), "w") as mods_txt: for mod in DEFAULT_UE4SS_MODS: - mods_txt.write(f"{mod} : 1\n") + mods_txt.write(f"{mod['mod_name']} : 1\n") if not ue4ss_mods_json.exists(): mods_data: list[UE4SSModInfo] = [] for mod in DEFAULT_UE4SS_MODS: - mods_data.append({"mod_name": mod, "mod_enabled": True}) + mods_data.append({"mod_name": mod["mod_name"], "mod_enabled": True}) with open(ue4ss_mods_json.absoluteFilePath(), "w") as mods_json: mods_json.write(json.dumps(mods_data, indent=4)) diff --git a/games/oblivion_remaster/constants.py b/games/oblivion_remaster/constants.py index bce8063..656e296 100644 --- a/games/oblivion_remaster/constants.py +++ b/games/oblivion_remaster/constants.py @@ -1 +1,14 @@ +from typing import TypedDict + PLUGIN_NAME = "Oblivion Remastered Support Plugin" + + +class UE4SSModInfo(TypedDict): + mod_name: str + mod_enabled: bool + + +DEFAULT_UE4SS_MODS: list[UE4SSModInfo] = [ + {"mod_name": "BPML_GenericFunctions", "mod_enabled": True}, + {"mod_name": "BPModLoaderMod", "mod_enabled": True}, +] diff --git a/games/oblivion_remaster/mod_data_content.py b/games/oblivion_remaster/mod_data_content.py index 32fb282..2ee7e5f 100644 --- a/games/oblivion_remaster/mod_data_content.py +++ b/games/oblivion_remaster/mod_data_content.py @@ -82,8 +82,14 @@ class OblivionRemasteredDataContent(mobase.ModDataContent): for paks_entry in entry: if isinstance(paks_entry, mobase.IFileTree): if paks_entry.name().casefold() == "~mods": - if paks_entry.find("MagicLoader"): - contents.add(Content.MAGIC_LOADER) + for mods_entry in paks_entry: + if isinstance(mods_entry, mobase.IFileTree): + if ( + "magicloader" + in mods_entry.name().casefold() + ): + contents.add(Content.MAGIC_LOADER) + break if paks_entry.name().casefold() == "logicmods": contents.add(Content.UE4SS) case "movies": diff --git a/games/oblivion_remaster/paks/model.py b/games/oblivion_remaster/paks/model.py index 3b60d2f..c43cec0 100644 --- a/games/oblivion_remaster/paks/model.py +++ b/games/oblivion_remaster/paks/model.py @@ -235,7 +235,7 @@ class PaksModel(QAbstractItemModel): ) ) - index = 9999 + index = 8999 for row, pak in new_paks.items(): current_dir = QDir(pak[2]) parent_dir = QDir(pak[2]) diff --git a/games/oblivion_remaster/paks/widget.py b/games/oblivion_remaster/paks/widget.py index 472413a..efc3038 100644 --- a/games/oblivion_remaster/paks/widget.py +++ b/games/oblivion_remaster/paks/widget.py @@ -1,4 +1,3 @@ -import re from functools import cmp_to_key from pathlib import Path from typing import cast @@ -69,7 +68,7 @@ class PaksTabWidget(QWidget): def write_pak_files(self): for index, pak in sorted(self._model.paks.items()): - name, _, current_path, target_path = pak + _, _, current_path, target_path = pak if current_path and current_path != target_path: path_dir = Path(current_path) target_dir = Path(target_path) @@ -77,30 +76,23 @@ class PaksTabWidget(QWidget): target_dir.mkdir(parents=True, exist_ok=True) if path_dir.exists(): for pak_file in path_dir.glob("*.pak"): - match = re.match(r"^(\d{4}_)?(.*)", pak_file.stem) - if not match: - continue - match_name = ( - match.group(2) if match.group(2) else match.group(1) + ucas_file = pak_file.with_suffix(".ucas") + utoc_file = pak_file.with_suffix(".utoc") + for file in (pak_file, ucas_file, utoc_file): + if not file.exists(): + continue + try: + file.rename(target_dir.joinpath(file.name)) + except FileExistsError: + pass + data = self._model.paks[index] + self._model.paks[index] = ( + data[0], + data[1], + data[3], + data[3], ) - if match_name == name: - ucas_file = pak_file.with_suffix(".ucas") - utoc_file = pak_file.with_suffix(".utoc") - for file in (pak_file, ucas_file, utoc_file): - if not file.exists(): - continue - try: - file.rename(target_dir.joinpath(file.name)) - except FileExistsError: - pass - data = self._model.paks[index] - self._model.paks[index] = ( - data[0], - data[1], - data[3], - data[3], - ) - break + break if not list(path_dir.iterdir()): path_dir.rmdir() @@ -140,7 +132,7 @@ class PaksTabWidget(QWidget): if isinstance(pak_mods, mobase.IFileTree): for entry in pak_mods: if is_directory(entry): - if entry.name().casefold() == "magicloader": + if "magicloader" in entry.name().casefold(): continue for sub_entry in entry: if ( @@ -179,7 +171,7 @@ class PaksTabWidget(QWidget): QDir.Filter.Dirs | QDir.Filter.Files | QDir.Filter.NoDotAndDotDot ): if entry.isDir(): - if entry.completeBaseName().casefold() == "magicloader": + if "magicloader" in entry.completeBaseName().casefold(): continue for sub_entry in QDir(entry.absoluteFilePath()).entryInfoList( QDir.Filter.Files @@ -207,7 +199,7 @@ class PaksTabWidget(QWidget): sorted_paks = dict(sorted(paks.items(), key=cmp_to_key(pak_sort))) shaken_paks: list[str] = self._shake_paks(sorted_paks) final_paks: dict[str, tuple[str, str, str]] = {} - pak_index = 9999 + pak_index = 8999 for pak in shaken_paks: target_dir = pak_paths[pak][1] + "/" + str(pak_index).zfill(4) final_paks[pak] = (pak_source[pak], pak_paths[pak][0], target_dir) diff --git a/games/oblivion_remaster/ue4ss/model.py b/games/oblivion_remaster/ue4ss/model.py index 9caf7c5..a3804fe 100644 --- a/games/oblivion_remaster/ue4ss/model.py +++ b/games/oblivion_remaster/ue4ss/model.py @@ -1,4 +1,5 @@ import json +from json import JSONDecodeError from typing import Any, Iterable from PyQt6.QtCore import ( @@ -13,6 +14,8 @@ from PyQt6.QtWidgets import QWidget import mobase +from ..constants import DEFAULT_UE4SS_MODS + class UE4SSListModel(QStringListModel): def __init__(self, parent: QWidget | None, organizer: mobase.IOrganizer): @@ -26,7 +29,10 @@ class UE4SSListModel(QStringListModel): mods_json = QFileInfo(profile.absoluteFilePath("mods.json")) if mods_json.exists(): with open(mods_json.absoluteFilePath(), "r") as json_file: - mod_data = json.load(json_file) + try: + mod_data = json.load(json_file) + except JSONDecodeError: + mod_data = DEFAULT_UE4SS_MODS for mod in mod_data: if mod["mod_enabled"]: self._checked_items.add(mod["mod_name"]) @@ -37,7 +43,10 @@ class UE4SSListModel(QStringListModel): mod_list: dict[str, bool] = {} if mods_json.exists(): with open(mods_json.absoluteFilePath(), "r") as json_file: - mod_data = json.load(json_file) + try: + mod_data = json.load(json_file) + except JSONDecodeError: + mod_data = DEFAULT_UE4SS_MODS for mod in mod_data: mod_list[mod["mod_name"]] = mod["mod_enabled"] for i in range(self.rowCount()): diff --git a/games/oblivion_remaster/ue4ss/widget.py b/games/oblivion_remaster/ue4ss/widget.py index 59900d1..246b7fa 100644 --- a/games/oblivion_remaster/ue4ss/widget.py +++ b/games/oblivion_remaster/ue4ss/widget.py @@ -1,22 +1,18 @@ import json from functools import cmp_to_key +from json import JSONDecodeError from pathlib import Path -from typing import TypedDict from PyQt6.QtCore import QDir, QFileInfo, Qt from PyQt6.QtWidgets import QGridLayout, QWidget import mobase +from ..constants import DEFAULT_UE4SS_MODS, UE4SSModInfo from .model import UE4SSListModel from .view import UE4SSView -class UE4SSModInfo(TypedDict): - mod_name: str - mod_enabled: bool - - class UE4SSTabWidget(QWidget): def __init__(self, parent: QWidget | None, organizer: mobase.IOrganizer): super().__init__(parent) @@ -155,7 +151,10 @@ class UE4SSTabWidget(QWidget): mods_list: list[str] = [] if mods_json.exists() and mods_json.isFile(): with open(mods_json.absoluteFilePath(), "r") as json_file: - mods = json.load(json_file) + try: + mods = json.load(json_file) + except JSONDecodeError: + mods = DEFAULT_UE4SS_MODS for mod in mods: if mod["mod_enabled"]: mods_list.append(mod["mod_name"]) diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..a27b7c5 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,17 @@ +{ + "features": { + "standalone": { + "description": "Build Standalone.", + "dependencies": [ + "mo2-cmake" + ] + } + }, + "vcpkg-configuration": { + "default-registry": { + "kind": "git", + "repository": "https://github.com/ModOrganizer2/vcpkg-registry", + "baseline": "69db61b22147b14ad66fd05cf798d855f6d68c12" + } + } +}