smapi

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: MIT Imports: 12 Imported by: 0

README

go-smapi

Go


Reads SMAPI mod manifests and orders mods the way SMAPI's mod resolver does. Package smapi, module github.com/Rethunk-Tech/go-smapi.

A manifest reader that accepts what SMAPI accepts (comments, trailing commas, BOMs, single quotes, any key casing) and a resolver that returns mods in SMAPI's load order. Neither touches the filesystem, so both are safe to point at downloaded mods.

Quick start

go get github.com/Rethunk-Tech/go-smapi

A worked example and the contracts: HUMANS.md.

Features

  • Parse reads a manifest.json the way SMAPI's Json.NET reader does, with a size bound and no panics on hostile input.
  • Content packs list their framework as one required dependency, however the manifest declares it.
  • Dependency.Check and Manifest.CheckSMAPI answer met, unmet or unknown for a dependency or MinimumAPIVersion, so an unreadable version is never shown as met.
  • Resolve orders mods as SMAPI's mod resolver does and flags missing requirements and dependency loops.
  • NexusUpdateKey, CurseForgeUpdateKey and GitHubUpdateKey read update keys; the GitHub one refuses a repo that would move a URL's path.
  • RewriteDependencies adds or drops optional dependencies and keeps every other key.

Documentation

Doc Audience
HUMANS.md Running and using the package
AGENTS.md Internals, invariants, and why
CONTRIBUTING.md Process — commits, tests, docs
SECURITY.md Vulnerability disclosure
CHANGELOG.md Release notes

License

MIT — see LICENSE. © 2026 Rethunk.Tech, LLC.

Documentation

Overview

Package smapi reads SMAPI mod manifests and orders mods the way SMAPI's mod resolver does.

Parse reads a manifest.json as leniently as SMAPI's Json.NET reader. Resolve returns mods in SMAPI's load order. Mod ids (UniqueID) are case-insensitive and compared with FoldID.

Index

Constants

View Source
const FileName = "manifest.json"

FileName is the manifest file SMAPI looks for.

View Source
const MaxManifestSize = 4 << 20

MaxManifestSize is the largest manifest Parse reads. Real manifests are a few kilobytes; the bound keeps a crafted file from costing memory in proportion to its size.

Variables

View Source
var ErrTooLarge = errors.New("manifest is too large")

ErrTooLarge is returned by Parse for input over MaxManifestSize.

Functions

func CurseForgeUpdateKey

func CurseForgeUpdateKey(key string) (string, bool)

CurseForgeUpdateKey returns the project id of a "CurseForge:<id>" update key; the id is a positive number.

func FoldID

func FoldID(uniqueID string) string

FoldID is the key a UniqueID is compared by: SMAPI ids are case-insensitive and may carry stray spaces.

func GitHubUpdateKey

func GitHubUpdateKey(key string) (string, bool)

GitHubUpdateKey returns the "owner/repo" of a "GitHub:owner/repo" update key. A repo that would move the path of a URL built from it ("..", "o/r?x", "o/r/s") is refused.

func NexusUpdateKey

func NexusUpdateKey(key string) (int, bool)

NexusUpdateKey returns the mod page of a "Nexus:1234" or "Nexus:1234@subkey" update key.

func RewriteDependencies

func RewriteDependencies(raw []byte, want, drop []string) ([]byte, error)

RewriteDependencies returns manifest.json text with each UniqueID in want added as an optional dependency (unless it is already listed) and each optional dependency in drop that is not in want removed. Other keys, in any casing, are kept; the output is strict JSON, so comments in the input are lost.

Types

type Dependency

type Dependency struct {
	UniqueID string
	// MinimumVersion is the oldest version of the dependency that works; empty for any.
	MinimumVersion string
	// IsRequired is true when the manifest omits IsRequired, as in SMAPI.
	IsRequired bool
}

Dependency is one mod another mod needs.

func (Dependency) Check added in v0.2.0

func (d Dependency) Check(installed string) Status

Check compares an installed version of the dependency with MinimumVersion. It is Unknown when either version does not parse; an empty MinimumVersion is Met whatever is installed.

func (Dependency) SatisfiedBy

func (d Dependency) SatisfiedBy(installed string) bool

SatisfiedBy reports whether an installed version of the dependency meets MinimumVersion. It is false only when both versions parse and the installed one is older, so an unreadable version never blocks a mod.

type Manifest

type Manifest struct {
	Name        string
	Author      string
	Version     string
	UniqueID    string
	Description string
	UpdateKeys  []string
	// EntryDll is the C# mod's assembly file, relative to the mod folder; empty for content packs.
	EntryDll string
	// ContentPackFor is the framework's UniqueID when the manifest declares a ContentPackFor.
	ContentPackFor string
	// Dependencies lists Dependencies[] and, as a required entry, the ContentPackFor framework.
	Dependencies []Dependency
	// UpdateCautionMessage is the author's warning to show before updating to this version.
	UpdateCautionMessage string
	// MinimumGameVersion is the oldest game version the mod runs on, when it declares one.
	MinimumGameVersion string
	// MinimumAPIVersion is the oldest SMAPI the mod runs on, as written; empty when it declares none.
	MinimumAPIVersion string
	// DeleteOldVersion asks an updater to remove the previous version's files rather than overlay them.
	DeleteOldVersion bool
}

Manifest holds the manifest.json fields a mod manager reads.

func Parse

func Parse(b []byte) (Manifest, error)

Parse reads a manifest tolerating a BOM or UTF-16/32 text, comments, trailing commas, single quotes, unquoted names, curly quotes and any key casing. A manifest without a UniqueID is an error, since nothing can refer to that mod. Input over MaxManifestSize is ErrTooLarge.

func (Manifest) CheckSMAPI added in v0.2.0

func (m Manifest) CheckSMAPI(installed string) Status

CheckSMAPI compares the installed SMAPI version with MinimumAPIVersion, which SMAPI refuses to load a mod below. It is Unknown when either version does not parse; no MinimumAPIVersion is Met.

type Mod

type Mod struct {
	UniqueID string
	Name     string
	// Folder is the mod's folder; its name orders the walk, as SMAPI's scanner meets folders. Either OS separator is read.
	Folder string
	// Dependencies are in the order the manifest declares them. MinimumVersion is not read.
	Dependencies   []Dependency
	ContentPackFor string
}

Mod is the manifest fields the resolver needs.

type Row

type Row struct {
	// Position counts from 1.
	Position int
	UniqueID string
	Name     string
	// Required and Optional are the UniqueIDs the mod depends on, as declared.
	Required []string
	Optional []string
	// Dependents are the loaded mods that depend on this one, in folder order.
	Dependents []string
	// MissingRequired are required dependencies no listed mod provides.
	MissingRequired []string
	// Cycle is set on a mod that a dependency loop stopped, and on those depending on it.
	Cycle bool
}

Row is one mod in load order.

func Resolve

func Resolve(mods []Mod) []Row

Resolve returns mods in SMAPI's load order, which is ModResolver.ProcessDependencies (Pathoschild/SMAPI develop e5b8bf3, src/SMAPI/Framework/ModLoading/ModResolver.cs:221-391): it walks the mods in folder order and loads each mod's dependencies (required, optional, and the mod a content pack is for) just before it, depth first. Duplicate UniqueIDs keep the first. A mod that misses a required dependency, or that a dependency loop or a failed required dependency stops, is placed where SMAPI gives up on it. The walk visits each mod once, so a loop ends at the first mod that meets itself.

type Status added in v0.2.0

type Status int

Status is the outcome of comparing a version requirement with an installed version.

const (
	// Unknown means a version could not be ordered, so the requirement is neither met nor unmet.
	Unknown Status = iota
	Met
	Unmet
)

Jump to

Keyboard shortcuts

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