diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..806d434 --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# Python bytecode +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +.venv/ +venv/ +env/ +ENV/ +.conda/ +.python-version + +# Testing / type-check / tooling caches +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.pyre/ +.tox/ +.coverage +.coverage.* +.cache/ +nosetests.xml +coverage.xml +htmlcov/ + +# Build and packaging +build/ +dist/ +.eggs/ +*.egg-info/ +*.egg +pip-wheel-metadata/ +*.manifest +*.spec + +dist_upload/ +SeaLoader.exe +SeaLoader_Windows_x64.zip + +# Editors/IDE +.vscode/ +.idea/ +*.code-workspace + +# OS junk +.DS_Store +Thumbs.db + +# Local configuration +usersettings.ini + +# Logs +*.log diff --git a/__pycache__/steam_required_ids.cpython-310.pyc b/__pycache__/steam_required_ids.cpython-310.pyc deleted file mode 100644 index dd05ab8..0000000 Binary files a/__pycache__/steam_required_ids.cpython-310.pyc and /dev/null differ diff --git a/hrsys.png b/hrsys.png new file mode 100644 index 0000000..7d0fe76 Binary files /dev/null and b/hrsys.png differ diff --git a/hrsys_logo.svg b/hrsys_logo.svg deleted file mode 100644 index 60d90e6..0000000 --- a/hrsys_logo.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - SYSTEMS - - - - - - - - \ No newline at end of file diff --git a/sealoader_gui.py b/sealoader_gui.py index 88ac14f..71f02c0 100644 --- a/sealoader_gui.py +++ b/sealoader_gui.py @@ -3,6 +3,7 @@ import os import shutil import sys import threading +import webbrowser from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Tuple @@ -209,9 +210,23 @@ class SeaLoaderApp(tk.Tk): bottom.pack(fill=tk.X, padx=12, pady=(0, 12)) self.enable_btn = ttk.Button(bottom, text="Enable Matching Mods", command=self._on_enable) self.enable_btn.pack(side=tk.LEFT) + self.disable_all_btn = ttk.Button(bottom, text="Disable All Mods", command=self._on_disable_all) + self.disable_all_btn.pack(side=tk.LEFT, padx=(8, 0)) self.help_btn = ttk.Button(bottom, text="Help", command=self._on_help) self.help_btn.pack(side=tk.RIGHT) + # Footer attribution + footer = ttk.Frame(self, style="TFrame") + footer.pack(fill=tk.X, padx=12, pady=(0, 10)) + self._footer_icon = self._load_png_icon("hrsys.png", max_px=18) + if self._footer_icon is not None: + icon_lbl = ttk.Label(footer, image=self._footer_icon, style="TLabel") + icon_lbl.pack(side=tk.LEFT) + icon_lbl.bind("", lambda e: self._open_link("https://hudsonriggs.systems")) + link_lbl = ttk.Label(footer, text="Created by HudsonRiggs.Systems", style="TLabel", foreground=MUTED) + link_lbl.pack(side=tk.LEFT, padx=(6, 0)) + link_lbl.bind("", lambda e: self._open_link("https://hudsonriggs.systems")) + def _create_tree(self, parent, columns: List[str], ratios: List[float]): tree = ttk.Treeview(parent, columns=columns, show="headings", height=12) for col in columns: @@ -219,12 +234,10 @@ class SeaLoaderApp(tk.Tk): tree.column(col, width=50, anchor=tk.W, stretch=True) vsb = ttk.Scrollbar(parent, orient="vertical", command=tree.yview) - hsb = ttk.Scrollbar(parent, orient="horizontal", command=tree.xview) - tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) + tree.configure(yscrollcommand=vsb.set) tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) vsb.pack(side=tk.LEFT, fill=tk.Y) - hsb.pack(side=tk.BOTTOM, fill=tk.X) self._bind_auto_columns(parent, tree, ratios) return tree @@ -372,6 +385,59 @@ class SeaLoaderApp(tk.Tk): pass return Path(__file__).resolve().parent / name + def _open_link(self, url: str) -> None: + try: + webbrowser.open(url) + except Exception: + pass + + def _load_png_icon(self, filename: str, max_px: int = 18): + try: + png_path = self._resource_path(filename) + if not png_path.exists(): + return None + img = tk.PhotoImage(file=str(png_path)) + # Downscale if needed using integer subsample + sx = max(img.width() // max_px, 1) + sy = max(img.height() // max_px, 1) + scale = max(sx, sy) + if scale > 1: + img = img.subsample(scale, scale) + return img + except Exception: + return None + + def _on_disable_all(self) -> None: + confirm = messagebox.askyesno("SeaLoader", "Disable all mods in usersettings.ini?") + if not confirm: + return + try: + self._disable_all_mods() + except Exception as exc: + messagebox.showerror("SeaLoader", f"Failed to disable all mods: {exc}") + return + self._load_installed_mods() + messagebox.showinfo("SeaLoader", "All mods disabled.") + + def _disable_all_mods(self) -> None: + parser = configparser.ConfigParser() + parser.optionxform = str + parser.read(self.ini_path, encoding="utf-8") + section = "LoadOrder" + if section not in parser: + raise ValueError("[LoadOrder] section not found in usersettings.ini") + shutil.copyfile(self.ini_path, self.ini_path + ".bak") + for key, value in list(parser[section].items()): + if not key.lower().startswith("mod"): + continue + parts = value.split(",") + if not parts: + continue + left_token = parts[0].strip() + parser[section][key] = f"{left_token},False" + with open(self.ini_path, "w", encoding="utf-8") as f: + parser.write(f) + def main() -> None: ini_path = DEFAULT_INI_PATH diff --git a/steam_required_ids.py b/steam_required_ids.py index f558b06..5151c79 100644 --- a/steam_required_ids.py +++ b/steam_required_ids.py @@ -128,10 +128,10 @@ def extract_required_item_ids(url: str) -> List[str]: html = fetch_page(url) found_ids = extract_required_item_ids_from_html(html) - # Remove the current page's ID if present + # Ensure the current page's ID is included (user wants main mod too) current_id = parse_main_item_id(url) - if current_id and current_id in found_ids: - found_ids.remove(current_id) + if current_id: + found_ids.add(current_id) return sorted(found_ids, key=int)