core

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyProfileOverrides added in v1.1.0

func ApplyProfileOverrides(game *domain.Game, profile *domain.Profile) error

ApplyProfileOverrides writes a profile's configuration overrides to the game install directory. Each key in profile.Overrides is a path relative to game.InstallPath; the value is written as file content. Used on deploy and profile switch so INI tweaks and other overrides are applied. Paths that escape the game install directory (e.g. ../../../etc/passwd) are rejected to prevent abuse.

func CompareVersions

func CompareVersions(v1, v2 string) int

CompareVersions compares two version strings Returns: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2

func DetectModName added in v1.1.0

func DetectModName(extractedPath, archiveFilename string) string

DetectModName determines a display name for an imported mod. It checks for a single top-level directory in the extracted content, falling back to the archive basename if not found.

func IsNewerVersion

func IsNewerVersion(currentVersion, newVersion string) bool

IsNewerVersion returns true if newVersion is newer than currentVersion

Types

type BatchOptions added in v1.1.0

type BatchOptions struct {
	Hooks       *ResolvedHooks // Hooks to run during batch operation
	HookRunner  *HookRunner    // Runner for executing hooks
	HookContext HookContext    // Base context for hooks (mod-specific fields added per-mod)
	Force       bool           // If true, bypass before_* hook failures
}

BatchOptions configures batch install/uninstall operations

type BatchResult added in v1.1.0

type BatchResult struct {
	Installed   []InstalledModResult   // Successfully installed mods (for InstallBatch)
	Uninstalled []UninstalledModResult // Successfully uninstalled mods (for UninstallBatch)
	Skipped     []SkippedMod           // Mods skipped due to hook failure or error
	Errors      []error                // Non-fatal errors (after_* hook failures)
}

BatchResult contains the results of a batch install/uninstall operation

type Conflict added in v1.1.0

type Conflict struct {
	RelativePath    string
	CurrentSourceID string
	CurrentModID    string
}

Conflict represents a file that would be overwritten by installing a mod

type DependencyResolver

type DependencyResolver struct{}

DependencyResolver resolves mod dependencies and detects cycles

func NewDependencyResolver

func NewDependencyResolver() *DependencyResolver

NewDependencyResolver creates a new dependency resolver

func (*DependencyResolver) GetDependencyTree

func (r *DependencyResolver) GetDependencyTree(mod *domain.Mod, modMap map[string]*domain.Mod) ([]domain.Mod, error)

GetDependencyTree returns all dependencies for a mod (including transitive)

func (*DependencyResolver) Resolve

func (r *DependencyResolver) Resolve(mods []domain.Mod) ([]domain.Mod, error)

Resolve returns mods in dependency order (dependencies first) Returns ErrDependencyLoop if a circular dependency is detected

func (*DependencyResolver) ValidateDependencies

func (r *DependencyResolver) ValidateDependencies(mods []domain.Mod) error

ValidateDependencies checks if all dependencies are satisfied

type DownloadModResult added in v1.1.0

type DownloadModResult struct {
	FilesExtracted int    // Number of files extracted
	Checksum       string // MD5 hash of downloaded archive
}

DownloadModResult contains the outcome of downloading a mod file

type DownloadProgress

type DownloadProgress struct {
	TotalBytes int64   // Total size in bytes (0 if unknown)
	Downloaded int64   // Bytes downloaded so far
	Percentage float64 // Completion percentage (0-100)
}

DownloadProgress represents the current state of a download

type DownloadResult added in v1.1.0

type DownloadResult struct {
	Path     string // Final file path
	Size     int64  // Bytes downloaded
	Checksum string // MD5 hash of downloaded file
}

DownloadResult contains the outcome of a download

type Downloader

type Downloader struct {
	// contains filtered or unexported fields
}

Downloader handles HTTP file downloads with progress tracking

func NewDownloader

func NewDownloader(httpClient *http.Client) *Downloader

NewDownloader creates a new Downloader with the given HTTP client If httpClient is nil, http.DefaultClient is used

func (*Downloader) Download

func (d *Downloader) Download(ctx context.Context, url, destPath string, progressFn ProgressFunc) (*DownloadResult, error)

Download fetches a file from the URL and saves it to destPath, with retries on transient failures (exponential backoff). Progress updates are sent to the optional progressFn callback.

type Extractor

type Extractor struct{}

Extractor handles archive extraction for mod files

func NewExtractor

func NewExtractor() *Extractor

NewExtractor creates a new Extractor

func (*Extractor) CanExtract

func (e *Extractor) CanExtract(filename string) bool

CanExtract returns true if the extractor can handle the given filename

func (*Extractor) DetectFormat

func (e *Extractor) DetectFormat(filename string) string

DetectFormat returns the archive format based on filename extension

func (*Extractor) Extract

func (e *Extractor) Extract(archivePath, destDir string) error

Extract extracts an archive to the destination directory Supports .zip (native), .7z and .rar (via system 7z command)

type HookContext added in v1.1.0

type HookContext struct {
	GameID     string
	GamePath   string
	ModPath    string
	ModID      string // Empty for *_all hooks
	ModName    string // Empty for *_all hooks
	ModVersion string // Empty for *_all hooks
	HookName   string // e.g., "install.before_all"
}

HookContext provides environment information for hook scripts

type HookResult added in v1.1.0

type HookResult struct {
	Stdout   string
	Stderr   string
	ExitCode int
}

HookResult contains the output from running a hook

type HookRunner added in v1.1.0

type HookRunner struct {
	// contains filtered or unexported fields
}

HookRunner executes hook scripts with timeout and environment

func NewHookRunner added in v1.1.0

func NewHookRunner(timeout time.Duration) *HookRunner

NewHookRunner creates a new hook runner with the given timeout

func (*HookRunner) Run added in v1.1.0

func (r *HookRunner) Run(ctx context.Context, scriptPath string, hc HookContext) (*HookResult, error)

Run executes a hook script and returns its output

type ImportOptions added in v1.1.0

type ImportOptions struct {
	SourceID    string // Explicit source (empty = auto-detect or "local")
	ModID       string // Explicit mod ID (empty = auto-detect or generate)
	ProfileName string // Target profile
}

ImportOptions configures the import operation

type ImportResult added in v1.1.0

type ImportResult struct {
	Mod            *domain.Mod
	FilesExtracted int
	LinkedSource   string // "nexusmods", "local", etc.
	AutoDetected   bool   // true if source/ID was parsed from filename
}

ImportResult contains the outcome of importing a local mod

type Importer added in v1.1.0

type Importer struct {
	// contains filtered or unexported fields
}

Importer handles importing mods from local archive files

func NewImporter added in v1.1.0

func NewImporter(cache *cache.Cache) *Importer

NewImporter creates a new Importer

func (*Importer) FindDuplicateMod added in v1.1.0

func (i *Importer) FindDuplicateMod(modName string, installedMods []domain.InstalledMod) *domain.InstalledMod

findDuplicateMod checks if a mod with similar name already exists (for duplicate prevention)

func (*Importer) Import added in v1.1.0

func (i *Importer) Import(ctx context.Context, archivePath string, game *domain.Game, opts ImportOptions) (result *ImportResult, err error)

Import imports a mod from a local archive file

func (*Importer) ScanModPath added in v1.1.0

func (i *Importer) ScanModPath(ctx context.Context, game *domain.Game, installedMods []domain.InstalledMod, opts ScanOptions) ([]ScanResult, error)

ScanModPath scans the game's mod_path for untracked mods

type InstalledModResult added in v1.1.0

type InstalledModResult struct {
	domain.Mod
}

InstalledModResult is a successfully installed mod (wraps domain.Mod for batch result)

type Installer

type Installer struct {
	// contains filtered or unexported fields
}

Installer handles mod installation and uninstallation

func NewInstaller

func NewInstaller(cache *cache.Cache, linker linker.Linker, database *db.DB) *Installer

NewInstaller creates a new installer The db parameter is optional - if nil, file tracking is disabled

func (*Installer) GetConflicts added in v1.1.0

func (i *Installer) GetConflicts(ctx context.Context, game *domain.Game, mod *domain.Mod, profileName string) ([]Conflict, error)

GetConflicts checks if installing a mod would overwrite files from other mods. Returns conflicts for files owned by OTHER mods (not the mod being installed).

func (*Installer) GetDeployedFiles

func (i *Installer) GetDeployedFiles(game *domain.Game, mod *domain.Mod) ([]string, error)

GetDeployedFiles returns the list of files deployed for a mod

func (*Installer) Install

func (i *Installer) Install(ctx context.Context, game *domain.Game, mod *domain.Mod, profileName string) error

Install deploys a mod to the game directory. If DB tracking is enabled and a SaveDeployedFile fails, only the file that failed to track is rolled back so the filesystem stays consistent with the database (previously deployed+tracked files are left in place).

func (*Installer) InstallBatch added in v1.1.0

func (i *Installer) InstallBatch(ctx context.Context, game *domain.Game, mods []*domain.Mod, versions []string, profileName string, opts BatchOptions) (*BatchResult, error)

InstallBatch installs multiple mods with hook support Hook behavior: - install.before_all: If fails, return error immediately (unless Force) - install.before_each: If fails, skip that mod, continue others - install.after_each: If fails, warn (add to Errors), continue - install.after_all: If fails, warn (add to Errors)

func (*Installer) IsInstalled

func (i *Installer) IsInstalled(game *domain.Game, mod *domain.Mod) (bool, error)

IsInstalled checks if a mod is currently deployed. Returns true only if every cached file is deployed (partial installs report as not installed).

func (*Installer) Uninstall

func (i *Installer) Uninstall(ctx context.Context, game *domain.Game, mod *domain.Mod, profileName string) error

Uninstall removes a mod from the game directory

func (*Installer) UninstallBatch added in v1.1.0

func (i *Installer) UninstallBatch(ctx context.Context, game *domain.Game, mods []*domain.InstalledMod, profileName string, opts BatchOptions) (*BatchResult, error)

UninstallBatch uninstalls multiple mods with hook support Hook behavior: - uninstall.before_all: If fails, return error immediately (unless Force) - uninstall.before_each: If fails, skip that mod, continue others - uninstall.after_each: If fails, warn (add to Errors), continue - uninstall.after_all: If fails, warn (add to Errors)

type ParsedFilename added in v1.1.0

type ParsedFilename struct {
	ModID    string // NexusMods mod ID
	Version  string // Mod version (normalized)
	BaseName string // Mod name portion before the ID
}

ParsedFilename contains extracted info from a NexusMods-style filename

func ParseNexusModsFilename added in v1.1.0

func ParseNexusModsFilename(filename string) *ParsedFilename

ParseNexusModsFilename attempts to extract mod ID and version from a NexusMods-style filename like "SkyUI-12604-5-2SE.zip". Returns nil if the filename doesn't match the expected pattern.

type ProfileManager

type ProfileManager struct {
	// contains filtered or unexported fields
}

ProfileManager handles profile CRUD operations and switching

func NewProfileManager

func NewProfileManager(configDir string, database *db.DB, cache *cache.Cache, lnk linker.Linker) *ProfileManager

NewProfileManager creates a new profile manager

func (*ProfileManager) AddMod

func (pm *ProfileManager) AddMod(gameID, profileName string, mod domain.ModReference) error

AddMod adds a mod reference to a profile

func (*ProfileManager) Create

func (pm *ProfileManager) Create(gameID, name string) (*domain.Profile, error)

Create creates a new profile for a game

func (*ProfileManager) Delete

func (pm *ProfileManager) Delete(gameID, name string) error

Delete removes a profile

func (*ProfileManager) Export

func (pm *ProfileManager) Export(gameID, profileName string) ([]byte, error)

Export exports a profile to a portable format

func (*ProfileManager) Get

func (pm *ProfileManager) Get(gameID, name string) (*domain.Profile, error)

Get retrieves a specific profile

func (*ProfileManager) GetDefault

func (pm *ProfileManager) GetDefault(gameID string) (*domain.Profile, error)

GetDefault returns the default profile for a game

func (*ProfileManager) Import

func (pm *ProfileManager) Import(data []byte) (*domain.Profile, error)

Import imports a profile from portable format

func (*ProfileManager) ImportWithOptions

func (pm *ProfileManager) ImportWithOptions(data []byte, force bool) (*domain.Profile, error)

ImportWithOptions imports a profile with optional force overwrite

func (*ProfileManager) List

func (pm *ProfileManager) List(gameID string) ([]*domain.Profile, error)

List returns all profiles for a game

func (*ProfileManager) ParseProfile

func (pm *ProfileManager) ParseProfile(data []byte) (*domain.Profile, error)

ParseProfile parses profile data without saving (for preview)

func (*ProfileManager) RemoveMod

func (pm *ProfileManager) RemoveMod(gameID, profileName, sourceID, modID string) error

RemoveMod removes a mod reference from a profile

func (*ProfileManager) ReorderMods

func (pm *ProfileManager) ReorderMods(gameID, profileName string, mods []domain.ModReference) error

ReorderMods updates the load order of mods in a profile

func (*ProfileManager) SetDefault

func (pm *ProfileManager) SetDefault(gameID, name string) error

SetDefault sets a profile as the default for a game

func (*ProfileManager) Switch

func (pm *ProfileManager) Switch(ctx context.Context, game *domain.Game, newProfileName string) error

Switch switches to a different profile, undeploying the current profile's mods and deploying the new profile's mods. It fails fast on any error and rolls back to the previous state (game dir and default profile) so the system is never left in a mixed old/new state.

func (*ProfileManager) UpsertMod added in v0.7.5

func (pm *ProfileManager) UpsertMod(gameID, profileName string, mod domain.ModReference) error

UpsertMod adds or updates a mod reference in a profile. If the mod exists, it updates Version and FileIDs while preserving position. If the mod doesn't exist, it appends to the end. This is the preferred method for install/update operations.

type ProgressFunc

type ProgressFunc func(DownloadProgress)

ProgressFunc is called periodically during download with progress updates

type ResolvedHooks added in v1.1.0

type ResolvedHooks struct {
	Install   domain.HookConfig
	Uninstall domain.HookConfig
}

ResolvedHooks contains the final merged hooks for an operation

func ResolveHooks added in v1.1.0

func ResolveHooks(game *domain.Game, profile *domain.Profile) *ResolvedHooks

ResolveHooks merges game-level hooks with profile-level overrides

type ScanOptions added in v1.1.0

type ScanOptions struct {
	ProfileName string
	DryRun      bool // If true, don't actually import, just report what would be done
}

ScanOptions configures the scan operation

type ScanResult added in v1.1.0

type ScanResult struct {
	FilePath       string      // Original path in mod_path
	FileName       string      // Base filename
	Mod            *domain.Mod // Detected/created mod info
	MatchedSource  string      // "curseforge", "nexusmods", or "local"
	AlreadyTracked bool        // True if already in lmm database
	Error          error       // Any error during processing
}

ScanResult contains the outcome of scanning a single mod file

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the main orchestrator for mod management operations

func NewService

func NewService(cfg ServiceConfig) (*Service, error)

NewService creates a new core service instance

func (*Service) AddGame

func (s *Service) AddGame(game *domain.Game) error

AddGame adds a new game configuration

func (*Service) Cache

func (s *Service) Cache() *cache.Cache

Cache returns the default cache manager

func (*Service) Close

func (s *Service) Close() error

Close releases resources held by the service

func (*Service) ConfigDir

func (s *Service) ConfigDir() string

ConfigDir returns the configuration directory

func (*Service) DB

func (s *Service) DB() *db.DB

DB returns the database

func (*Service) DeleteSourceToken

func (s *Service) DeleteSourceToken(sourceID string) error

DeleteSourceToken removes an API token for a source

func (*Service) DownloadMod

func (s *Service) DownloadMod(ctx context.Context, sourceID string, game *domain.Game, mod *domain.Mod, file *domain.DownloadableFile, progressFn ProgressFunc) (result *DownloadModResult, err error)

DownloadMod downloads a mod file, extracts it, and stores it in the cache Returns the download result including files extracted and checksum. Multiple files from the same mod can be downloaded to the same cache location.

func (*Service) GetDefaultLinkMethod

func (s *Service) GetDefaultLinkMethod() domain.LinkMethod

GetDefaultLinkMethod returns the default link method from config

func (*Service) GetDependencies added in v1.1.0

func (s *Service) GetDependencies(ctx context.Context, sourceID string, mod *domain.Mod) ([]domain.ModReference, error)

GetDependencies returns dependencies for a mod from the specified source

func (*Service) GetDownloadURL

func (s *Service) GetDownloadURL(ctx context.Context, sourceID string, mod *domain.Mod, fileID string) (string, error)

GetDownloadURL gets the download URL for a specific mod file

func (*Service) GetGame

func (s *Service) GetGame(gameID string) (*domain.Game, error)

GetGame retrieves a game by ID

func (*Service) GetGameCache

func (s *Service) GetGameCache(game *domain.Game) *cache.Cache

GetGameCache returns a cache manager for the specified game. Uses the game's cache_path if configured (game-scoped: paths omit gameID), otherwise the global cache.

func (*Service) GetGameCachePath

func (s *Service) GetGameCachePath(game *domain.Game) string

GetGameCachePath returns the effective cache path for a game. Uses the game's cache_path if configured, otherwise falls back to global cache.

func (*Service) GetGameLinkMethod

func (s *Service) GetGameLinkMethod(game *domain.Game) domain.LinkMethod

GetGameLinkMethod returns the effective link method for a game. Uses the game's explicit setting if configured, otherwise falls back to global default.

func (*Service) GetInstalledMod

func (s *Service) GetInstalledMod(sourceID, modID, gameID, profileName string) (*domain.InstalledMod, error)

GetInstalledMod retrieves a single installed mod

func (*Service) GetInstalledMods

func (s *Service) GetInstalledMods(gameID, profileName string) ([]domain.InstalledMod, error)

GetInstalledMods returns all installed mods for a game/profile (DB order: installed_at).

func (*Service) GetInstalledModsInProfileOrder added in v1.1.0

func (s *Service) GetInstalledModsInProfileOrder(gameID, profileName string) ([]domain.InstalledMod, error)

GetInstalledModsInProfileOrder returns installed mods in profile load order (first = lowest priority). Mods not present in the profile are omitted. Use this for deploy/switch so deployment order matches load order.

func (*Service) GetInstaller added in v1.1.0

func (s *Service) GetInstaller(game *domain.Game) *Installer

GetInstaller returns an Installer configured for the given game

func (*Service) GetLinker

func (s *Service) GetLinker(method domain.LinkMethod) linker.Linker

GetLinker returns a linker for the given method

func (*Service) GetMod

func (s *Service) GetMod(ctx context.Context, sourceID, gameID, modID string) (*domain.Mod, error)

GetMod retrieves a specific mod from a source

func (*Service) GetModFiles

func (s *Service) GetModFiles(ctx context.Context, sourceID string, mod *domain.Mod) ([]domain.DownloadableFile, error)

GetModFiles retrieves available download files for a mod

func (*Service) GetSource

func (s *Service) GetSource(id string) (source.ModSource, error)

GetSource retrieves a source by ID

func (*Service) GetSourceToken

func (s *Service) GetSourceToken(sourceID string) (*db.StoredToken, error)

GetSourceToken retrieves an API token for a source

func (*Service) IsSourceAuthenticated

func (s *Service) IsSourceAuthenticated(sourceID string) bool

IsSourceAuthenticated checks if a source has a stored API token

func (*Service) ListGames

func (s *Service) ListGames() []*domain.Game

ListGames returns all configured games

func (*Service) ListSources

func (s *Service) ListSources() []source.ModSource

ListSources returns all registered sources

func (*Service) RegisterSource

func (s *Service) RegisterSource(src source.ModSource)

RegisterSource adds a mod source to the registry

func (*Service) Registry

func (s *Service) Registry() *source.Registry

Registry returns the source registry

func (*Service) RollbackModVersion

func (s *Service) RollbackModVersion(sourceID, modID, gameID, profileName string) error

RollbackModVersion reverts a mod to its previous version

func (*Service) SaveSourceToken

func (s *Service) SaveSourceToken(sourceID, apiKey string) error

SaveSourceToken saves an API token for a source

func (*Service) SearchMods

func (s *Service) SearchMods(ctx context.Context, sourceID, gameID, query string, category string, tags []string) ([]domain.Mod, error)

SearchMods searches for mods in a source

func (*Service) SetModFileIDs added in v0.7.3

func (s *Service) SetModFileIDs(sourceID, modID, gameID, profileName string, fileIDs []string) error

SetModFileIDs updates the file IDs for an installed mod

func (*Service) SetModLinkMethod

func (s *Service) SetModLinkMethod(sourceID, modID, gameID, profileName string, linkMethod domain.LinkMethod) error

SetModLinkMethod sets the deployment method for an installed mod

func (*Service) SetModUpdatePolicy

func (s *Service) SetModUpdatePolicy(sourceID, modID, gameID, profileName string, policy domain.UpdatePolicy) error

SetModUpdatePolicy sets the update policy for an installed mod

func (*Service) UpdateModVersion

func (s *Service) UpdateModVersion(sourceID, modID, gameID, profileName, newVersion string) error

UpdateModVersion updates the version of an installed mod, preserving the previous version for rollback

type ServiceConfig

type ServiceConfig struct {
	ConfigDir string // Directory for configuration files
	DataDir   string // Directory for database and persistent data
	CacheDir  string // Directory for mod file cache
}

ServiceConfig holds configuration for the core service

type SkippedMod added in v1.1.0

type SkippedMod struct {
	Mod    *domain.Mod
	Reason string
}

SkippedMod represents a mod that was skipped during batch operation

type UninstalledModResult added in v1.1.0

type UninstalledModResult struct {
	domain.Mod
}

UninstalledModResult is a successfully uninstalled mod (wraps domain.Mod for batch result)

type Updater

type Updater struct {
	// contains filtered or unexported fields
}

Updater checks for and applies mod updates

func NewUpdater

func NewUpdater(registry *source.Registry) *Updater

NewUpdater creates a new updater

func (*Updater) CheckUpdates

func (u *Updater) CheckUpdates(ctx context.Context, installed []domain.InstalledMod) ([]domain.Update, error)

CheckUpdates checks for available updates for installed mods

func (*Updater) GetAutoUpdateMods

func (u *Updater) GetAutoUpdateMods(installed []domain.InstalledMod) []domain.InstalledMod

GetAutoUpdateMods filters installed mods to those with auto-update enabled

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL