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 @@
-
-
\ 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)