From d48a34658ddbd66660dce09db05ec96bd3844f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 10 Jul 2024 21:47:38 +0200 Subject: [PATCH 1/3] Move to VCPKG for mo2-cmake. --- CMakeLists.txt | 13 ++++--------- CMakePresets.json | 17 +++++++++++++++++ vcpkg.json | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 CMakePresets.json create mode 100644 vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8070c7..288c2a4d 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 00000000..acc6d975 --- /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/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..2c96d07b --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,15 @@ +{ + "features": { + "standalone": { + "description": "Build Standalone.", + "dependencies": ["mo2-cmake"] + } + }, + "vcpkg-configuration": { + "default-registry": { + "kind": "git", + "repository": "https://github.com/ModOrganizer2/vcpkg-registry", + "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60" + } + } +} From 8e7d1bce6518137ddb068b227c407d6ab4825c89 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 27 May 2025 08:21:04 +0200 Subject: [PATCH 2/3] Add build action to CI. --- .github/workflows/build.yml | 44 +++++++++++++++++++++++++++++++++++++ vcpkg.json | 6 +++-- 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..ef004674 --- /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/vcpkg.json b/vcpkg.json index 2c96d07b..a27b7c57 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,14 +2,16 @@ "features": { "standalone": { "description": "Build Standalone.", - "dependencies": ["mo2-cmake"] + "dependencies": [ + "mo2-cmake" + ] } }, "vcpkg-configuration": { "default-registry": { "kind": "git", "repository": "https://github.com/ModOrganizer2/vcpkg-registry", - "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60" + "baseline": "69db61b22147b14ad66fd05cf798d855f6d68c12" } } } From a59a78cf75a22ec72c0de8e55932b5b8edec1798 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Wed, 28 May 2025 00:50:24 -0500 Subject: [PATCH 3/3] Oblivion Remastered updates (#184) * Oblivion Remastered updates - Handle invalid JSON - Remove outdated pak sorting code - Start from 8999 for pak directories - Leaves space for early loaders - Update MagicLoader handling for more dir names --- games/game_oblivion_remaster.py | 10 ++--- games/oblivion_remaster/constants.py | 13 ++++++ games/oblivion_remaster/mod_data_content.py | 10 ++++- games/oblivion_remaster/paks/model.py | 2 +- games/oblivion_remaster/paks/widget.py | 48 +++++++++------------ games/oblivion_remaster/ue4ss/model.py | 13 +++++- games/oblivion_remaster/ue4ss/widget.py | 13 +++--- 7 files changed, 63 insertions(+), 46 deletions(-) diff --git a/games/game_oblivion_remaster.py b/games/game_oblivion_remaster.py index 84731ad3..218bb048 100644 --- a/games/game_oblivion_remaster.py +++ b/games/game_oblivion_remaster.py @@ -13,11 +13,9 @@ 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 @@ def write_default_mods(self, profile: QDir): 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 bce80634..656e296d 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 32fb282b..2ee7e5f4 100644 --- a/games/oblivion_remaster/mod_data_content.py +++ b/games/oblivion_remaster/mod_data_content.py @@ -82,8 +82,14 @@ def getContentsFor(self, filetree: mobase.IFileTree) -> list[int]: 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 3b60d2f0..c43cec0c 100644 --- a/games/oblivion_remaster/paks/model.py +++ b/games/oblivion_remaster/paks/model.py @@ -235,7 +235,7 @@ def dropMimeData( ) ) - 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 472413ad..efc30386 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 @@ def write_paks_list(self): 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 @@ def write_pak_files(self): 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 @@ def _parse_pak_files(self): 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 @@ def _parse_pak_files(self): 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 @@ def _parse_pak_files(self): 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 9caf7c51..a3804fe7 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 @@ import mobase +from ..constants import DEFAULT_UE4SS_MODS + class UE4SSListModel(QStringListModel): def __init__(self, parent: QWidget | None, organizer: mobase.IOrganizer): @@ -26,7 +29,10 @@ def _init_mod_states(self): 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 @@ def _set_mod_states(self): 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 59900d1f..246b7fa0 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 @@ def sort_mods(self, mod_a: str, mod_b: str) -> int: 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"])