Documentation
¶
Index ¶
- Constants
- Variables
- func AlphaMapKeys[V any](m map[string]V) []string
- func CachedCompile(pattern string) (*regexp.Regexp, error)
- func CachedMustCompile(pattern string) *regexp.Regexp
- func CalculateMemoryLimit() int64
- func ConfigDir(pl platforms.Platform) string
- func ConfigureMemoryLimit()
- func Contains[T comparable](xs []T, x T) bool
- func CopyFile(sourcePath, destPath string, perm ...os.FileMode) error
- func DataDir(pl platforms.Platform) string
- func DecodeURIIfNeeded(uri string) string
- func EnsureDirectories(pl platforms.Platform) error
- func EqualStringSlices(a, b []string) bool
- func ExeDir() string
- func FilenameFromPath(p string) string
- func FindLauncher(cfg *config.Instance, pl platforms.Platform, path string) (platforms.Launcher, error)
- func GetAllLocalIPs() []string
- func GetFileSize(filePath string) (int64, error)
- func GetLocalIP() string
- func GetMd5Hash(filePath string) (string, error)
- func GetPathName(path string) string
- func GetSerialDeviceList() ([]string, error)
- func GetUSBTopologyPath(devicePath string) string
- func HasUserDir() (string, bool)
- func InitLogging(pl platforms.Platform, writers []io.Writer) error
- func IsClockReliable(t time.Time) bool
- func IsFalsey(s string) bool
- func IsProcessRunning(proc *os.Process) bool
- func IsTruthy(s string) bool
- func IsValidExtension(ext string) bool
- func IsZip(filePath string) bool
- func ListZip(filePath string) ([]string, error)
- func LogWriter() io.Writer
- func MapKeys[K comparable, V any](m map[K]V) []K
- func MatchSystemFile(cfg *config.Instance, pl platforms.Platform, systemID string, path string) bool
- func MaybeJSON(data []byte) bool
- func NormalizePathForComparison(path string) string
- func OpenBrowser(url string) error
- func PadNumber(num, width int) string
- func ParseCustomLaunchers(pl platforms.Platform, customLaunchers []config.LaunchersCustom) []platforms.Launcher
- func PathHasPrefix(path, root string) bool
- func PathIsLauncher(cfg *config.Instance, pl platforms.Platform, l *platforms.Launcher, ...) bool
- func PathToLaunchers(cfg *config.Instance, pl platforms.Platform, path string) []platforms.Launcher
- func PlayConfiguredSound(player audio.Player, path string, enabled bool, defaultSound []byte, ...)
- func RandSeq(n int) (string, error)
- func RandomElem[T any](xs []T) (T, error)
- func RandomInt(maxVal int) (int, error)
- func RestoreMemoryLimit()
- func SplitCommand(s string) ([]string, error)
- func SuspendMemoryLimit()
- func TokensEqual(a, b *tokens.Token) bool
- func ValidateBrowserURL(url string) error
- func WaitForInternet(maxTries int) bool
- func YesNoPrompt(label string, def bool) bool
- type LauncherCache
- func (lc *LauncherCache) GetAllLaunchers() []platforms.Launcher
- func (lc *LauncherCache) GetLauncherByID(id string) *platforms.Launcher
- func (lc *LauncherCache) GetLaunchersBySystem(systemID string) []platforms.Launcher
- func (lc *LauncherCache) Initialize(pl platforms.Platform, cfg *config.Instance)
- func (lc *LauncherCache) InitializeFromSlice(launchers []platforms.Launcher)
- func (lc *LauncherCache) Refresh(pl platforms.Platform, cfg *config.Instance)
- func (lc *LauncherCache) ToRelativePath(rootDirs []string, systemID string, path string) string
- type LauncherMatcher
- type PathInfo
- type RegexCache
- type SleepWakeMonitor
Constants ¶
const ( // ClockSourceSystem indicates the timestamp came from a system clock that appeared reliable. // This could be NTP, RTC, or manually set - we don't distinguish at record creation time. ClockSourceSystem = "system" // ClockSourceEpoch indicates the timestamp came from an unreliable clock (year < 2024). // Common on MiSTer devices that boot without RTC and haven't synced NTP yet. ClockSourceEpoch = "epoch" // ClockSourceHealed indicates the timestamp was mathematically reconstructed. // Original timestamp was unreliable, but was later corrected using: // TrueTimestamp = TrueBootTime + MonotonicOffset ClockSourceHealed = "healed" )
ClockSource values indicate how a timestamp was determined
const MaxResponseBodySize = 1 << 20 // 1 MiB
MaxResponseBodySize is the default maximum number of bytes to read from an HTTP response body. Use with io.LimitReader to prevent memory exhaustion from malicious or misconfigured servers. 1MB covers legitimate API responses.
const MaxURLLength = 8192
MaxURLLength is the maximum allowed URL length for browser opening. This prevents resource exhaustion from malicious tokens with extremely long URLs.
const ( // MinReliableYear is the earliest year considered valid for the system clock. // Zaparoo Core v2 was released in 2024 - any earlier date indicates an unset clock. MinReliableYear = 2024 )
Variables ¶
var ErrUnclosedQuote = errors.New("unclosed quote in command string")
var GlobalLauncherCache = &LauncherCache{}
GlobalLauncherCache is the singleton instance used throughout the application.
var GlobalRegexCache = NewRegexCache()
GlobalRegexCache is the singleton instance used throughout the application
var ReURI = regexp.MustCompile(`^([a-zA-Z][a-zA-Z0-9+.-]*)://(.+)$`)
Functions ¶
func AlphaMapKeys ¶
func CachedCompile ¶
CachedCompile compiles and caches a dynamic regex pattern. Returns error if pattern is invalid. Only use for runtime patterns - for static patterns, use package-level vars instead.
func CachedMustCompile ¶
CachedMustCompile compiles and caches a dynamic regex pattern. Panics if pattern is invalid. Only use for runtime patterns - for static patterns, use package-level vars instead.
func CalculateMemoryLimit ¶ added in v2.10.0
func CalculateMemoryLimit() int64
CalculateMemoryLimit returns a GOMEMLIMIT value based on system memory: 10% of total RAM, clamped to [32MB, 128MB]. Returns -1 if system memory cannot be read.
func ConfigureMemoryLimit ¶ added in v2.10.0
func ConfigureMemoryLimit()
ConfigureMemoryLimit sets GOMEMLIMIT based on available system memory to keep idle RSS low on memory-constrained devices like MiSTer (492MB total). The soft limit makes the GC more aggressive about returning memory to the OS after transient spikes (e.g., media indexing). It does NOT hard-cap memory — the runtime will exceed the limit if the live heap requires it.
Skipped when the GOMEMLIMIT environment variable is already set, so users can override with their own value.
func Contains ¶
func Contains[T comparable](xs []T, x T) bool
Contains returns true if slice contains value.
func CopyFile ¶
CopyFile copies a file from sourcePath to destPath. Optional perm parameter sets file permissions (uses 0644 if not specified).
func DecodeURIIfNeeded ¶ added in v2.7.0
DecodeURIIfNeeded applies URL decoding to URIs based on their scheme - Zaparoo custom schemes (steam://, kodi-*://, etc.): uses virtualpath.ParseVirtualPathStr for full decoding - Standard web schemes (http://, https://): decodes path component only - Other schemes: returns as-is (no decoding) Returns the original URI on decoding errors (graceful fallback) Uses manual parsing to handle malformed URLs gracefully
func EnsureDirectories ¶ added in v2.7.0
EnsureDirectories creates the necessary directories for the application. This should be called early during startup, before InitLogging.
func EqualStringSlices ¶ added in v2.7.0
EqualStringSlices compares two string slices for equality
func FilenameFromPath ¶
func FindLauncher ¶
func FindLauncher( cfg *config.Instance, pl platforms.Platform, path string, ) (platforms.Launcher, error)
FindLauncher takes a path and tries to find the best possible match for a launcher, taking into account specificity and allowlist restrictions.
func GetAllLocalIPs ¶
func GetAllLocalIPs() []string
GetAllLocalIPs returns all non-loopback private IPv4 addresses
func GetFileSize ¶
func GetLocalIP ¶
func GetLocalIP() string
func GetMd5Hash ¶
func GetPathName ¶ added in v2.8.0
GetPathName returns just the name portion (filename without extension) from a path. This is a convenience wrapper around GetPathInfo for use with platforms.DoLaunch.
func GetSerialDeviceList ¶
func GetUSBTopologyPath ¶ added in v2.9.0
GetUSBTopologyPath resolves a device path (e.g., /dev/ttyUSB0) to its USB port topology path (e.g., "1-2.3.1"). This path is stable across reboots as long as the device stays in the same physical USB port.
Returns empty string if topology cannot be determined (e.g., in Docker, non-USB device, or missing /sys filesystem).
func HasUserDir ¶
HasUserDir checks if a "user" directory exists next to the Zaparoo binary and returns true and the absolute path to it. This directory is used as a parent for all platform directories if it exists, for a portable install. The result is cached after the first call for better performance. This function is safe for concurrent use.
func IsClockReliable ¶ added in v2.7.0
IsClockReliable checks if the system clock appears to be set correctly. Returns false if the clock is clearly wrong (e.g., year < 2024). This handles MiSTer's lack of RTC chip - clock often resets to epoch on boot.
func IsProcessRunning ¶ added in v2.7.0
IsProcessRunning checks if a process is still running. Returns false if the process is nil or has terminated.
func IsValidExtension ¶ added in v2.7.0
IsValidExtension checks if a file extension contains only valid characters Valid extensions contain only alphanumeric characters (and the leading dot) Examples: ".zip" ✓, ".tar" ✓, ".mp3" ✓, ".other thing" ✗, ".file-name" ✗
func LogWriter ¶ added in v2.8.0
LogWriter returns the underlying io.Writer used by the logger. This is useful for adding additional writers (e.g., telemetry) after initialization.
func MapKeys ¶
func MapKeys[K comparable, V any](m map[K]V) []K
MapKeys returns a list of all keys in a map.
func MatchSystemFile ¶
func MatchSystemFile( cfg *config.Instance, pl platforms.Platform, systemID string, path string, ) bool
MatchSystemFile returns true if a given path is for a given system.
func NormalizePathForComparison ¶ added in v2.7.0
NormalizePathForComparison normalizes a path for cross-platform case-insensitive comparison. Converts to forward slashes and lowercases for consistent matching across all platforms. This handles paths from databases (forward slashes) vs filepath.Join (OS-specific slashes), and ensures case-insensitive matching works for FAT32/exFAT filesystems on all platforms.
func OpenBrowser ¶ added in v2.8.0
OpenBrowser opens the given URL in the default web browser using xdg-open. This is a fire-and-forget operation - the browser process is started but not waited on. Only http:// and https:// URLs are accepted for security.
func PadNumber ¶ added in v2.7.0
PadNumber formats a number with leading zeros to the specified width. Examples:
- PadNumber(5, 2) → "05"
- PadNumber(42, 4) → "0042"
- PadNumber(123, 2) → "123"
func ParseCustomLaunchers ¶
func ParseCustomLaunchers( pl platforms.Platform, customLaunchers []config.LaunchersCustom, ) []platforms.Launcher
ParseCustomLaunchers converts user-configured custom launchers into platform launchers. When a custom launcher has an empty Execute field, the returned launcher's Launch function will be nil — the platform is expected to fill in its own default launch mechanism (e.g., ES API on Batocera).
func PathHasPrefix ¶ added in v2.7.0
PathHasPrefix checks if path is within root directory, handling separator boundaries correctly. This avoids the prefix bug where "c:/roms2/game.bin" would incorrectly match root "c:/roms".
func PathIsLauncher ¶
func PathIsLauncher( cfg *config.Instance, pl platforms.Platform, l *platforms.Launcher, path string, ) bool
PathIsLauncher returns true if a given path matches against any of the criteria defined in a launcher.
func PathToLaunchers ¶
func PathToLaunchers( cfg *config.Instance, pl platforms.Platform, path string, ) []platforms.Launcher
PathToLaunchers is a reverse lookup to match a given path against all possible launchers in a platform. Returns all matched launchers.
func PlayConfiguredSound ¶ added in v2.7.0
func PlayConfiguredSound(player audio.Player, path string, enabled bool, defaultSound []byte, soundName string)
PlayConfiguredSound plays a sound based on configuration. Custom sound files fall back to the embedded default on error.
func RandomElem ¶
RandomElem picks and returns a random element from a slice.
func RandomInt ¶ added in v2.7.0
RandomInt returns a random integer between 0 and maxVal-1 (inclusive).
func RestoreMemoryLimit ¶ added in v2.10.0
func RestoreMemoryLimit()
RestoreMemoryLimit re-applies the calculated GOMEMLIMIT after a memory-intensive operation completes.
func SplitCommand ¶ added in v2.10.0
SplitCommand splits a command string into a slice of arguments, respecting double quotes and single quotes for grouping. Quotes are stripped from the output. This is used instead of shell invocation (sh -c) to avoid shell injection vulnerabilities.
There is no escape character. ZapScript's ^ escape handles escaping before text reaches this function. Use single quotes to include literal double quotes and vice versa. Use quote doubling to include a literal quote inside a same-type quoted group (e.g. “some “”arg”).
Rules:
- Unquoted whitespace (space, tab) separates arguments
- Double-quoted strings group content; all characters inside are literal
- Single-quoted strings group content; all characters inside are literal
- Inside double quotes, "" produces a literal "
- Inside single quotes, two consecutive ' produce a literal '
- All other characters (including backslash) are literal
- Empty quoted strings produce an empty argument ("")
- Unclosed quotes return an error
func SuspendMemoryLimit ¶ added in v2.10.0
func SuspendMemoryLimit()
SuspendMemoryLimit disables GOMEMLIMIT for memory-intensive operations like media indexing where speed matters more than RSS.
func TokensEqual ¶
func ValidateBrowserURL ¶ added in v2.8.0
ValidateBrowserURL checks if the URL has a valid scheme for browser opening. Only http:// and https:// URLs are accepted for security.
func WaitForInternet ¶
func YesNoPrompt ¶
Types ¶
type LauncherCache ¶
type LauncherCache struct {
// contains filtered or unexported fields
}
LauncherCache provides fast O(1) launcher lookups by system ID. This replaces the expensive O(n*m) pl.Launchers() calls in hot paths.
func (*LauncherCache) GetAllLaunchers ¶
func (lc *LauncherCache) GetAllLaunchers() []platforms.Launcher
GetAllLaunchers returns all cached launchers.
func (*LauncherCache) GetLauncherByID ¶ added in v2.10.0
func (lc *LauncherCache) GetLauncherByID(id string) *platforms.Launcher
GetLauncherByID finds a launcher by its unique ID. Returns nil if no launcher with the given ID is found.
func (*LauncherCache) GetLaunchersBySystem ¶
func (lc *LauncherCache) GetLaunchersBySystem(systemID string) []platforms.Launcher
GetLaunchersBySystem returns all launchers for a specific system ID. Returns nil if no launchers found for the system.
func (*LauncherCache) Initialize ¶
func (lc *LauncherCache) Initialize(pl platforms.Platform, cfg *config.Instance)
Initialize builds the launcher cache from platform launchers. This should be called once at startup after custom launchers are loaded.
func (*LauncherCache) InitializeFromSlice ¶ added in v2.10.0
func (lc *LauncherCache) InitializeFromSlice(launchers []platforms.Launcher)
InitializeFromSlice builds the launcher cache from a pre-built slice of launchers. This is useful for testing or when launchers are already available.
func (*LauncherCache) Refresh ¶
func (lc *LauncherCache) Refresh(pl platforms.Platform, cfg *config.Instance)
Refresh rebuilds the cache with updated launcher data. This can be called via API to refresh the cache without restarting.
func (*LauncherCache) ToRelativePath ¶ added in v2.10.0
func (lc *LauncherCache) ToRelativePath( rootDirs []string, systemID string, path string, ) string
ToRelativePath converts an absolute media path to a relative path with the system ID as the first component. It strips the matching rootDir+folder prefix and replaces it with the systemID.
Example: "/mnt/games/SNES/USA/game.sfc" with systemID "snes" becomes "snes/USA/game.sfc".
Returns the original path unchanged if it is a URI or no prefix matches.
type LauncherMatcher ¶ added in v2.10.0
type LauncherMatcher struct {
// contains filtered or unexported fields
}
LauncherMatcher provides optimized path matching with pre-normalized paths. All data is immutable after construction, making it safe for concurrent use from fastwalk callbacks.
func NewLauncherMatcher ¶ added in v2.10.0
func NewLauncherMatcher(cfg *config.Instance, pl platforms.Platform) *LauncherMatcher
NewLauncherMatcher creates a matcher with pre-normalized root dirs and folder paths. Call once before a file walk and reuse for all files to avoid redundant normalization.
func (*LauncherMatcher) MatchSystemFile ¶ added in v2.10.0
func (m *LauncherMatcher) MatchSystemFile(systemID, path string) bool
MatchSystemFile returns true if path matches a launcher for the given system. The file path is normalized once and compared against pre-normalized roots.
type PathInfo ¶
func GetPathInfo ¶
type RegexCache ¶
type RegexCache struct {
// contains filtered or unexported fields
}
RegexCache provides thread-safe caching of compiled regular expressions to avoid repeated compilation overhead for dynamic/runtime patterns.
When to Use ¶
Use CachedCompile for DYNAMIC patterns only:
- Patterns from user configuration (config files, databases)
- Patterns from external sources (CSV files, APIs)
- Patterns that vary at runtime
For STATIC patterns (constant strings), use package-level variables instead:
var myPattern = regexp.MustCompile(`pattern`) // ✅ Static pattern re := helpers.CachedMustCompile(`pattern`) // ❌ Don't use for static
Package-level vars provide:
- Zero runtime overhead (no map lookups)
- Compile-time validation (errors caught at startup)
- Better code organization (patterns visible at top of file)
func NewRegexCache ¶
func NewRegexCache() *RegexCache
NewRegexCache creates a new RegexCache instance
func (*RegexCache) Clear ¶
func (rc *RegexCache) Clear()
Clear removes all cached patterns (useful for testing or memory management)
func (*RegexCache) Compile ¶
func (rc *RegexCache) Compile(pattern string) (*regexp.Regexp, error)
Compile compiles a regex pattern and caches it for future use. If the pattern is already cached, returns the cached version. Returns an error if the pattern cannot be compiled.
func (*RegexCache) MustCompile ¶
func (rc *RegexCache) MustCompile(pattern string) *regexp.Regexp
MustCompile compiles a regex pattern and caches it for future use. If the pattern is already cached, returns the cached version. Panics if the pattern cannot be compiled (same behavior as regexp.MustCompile).
func (*RegexCache) Size ¶
func (rc *RegexCache) Size() int
Size returns the number of cached patterns
type SleepWakeMonitor ¶ added in v2.8.0
type SleepWakeMonitor struct {
// contains filtered or unexported fields
}
SleepWakeMonitor detects system sleep/wake events by monitoring wall clock jumps. When the system sleeps, the monotonic clock pauses but wall clock continues. On wake, the wall clock will have jumped forward significantly more than expected.
Usage:
monitor := NewSleepWakeMonitor(5 * time.Second)
for {
if monitor.Check() {
// Handle wake from sleep
}
time.Sleep(1 * time.Second)
}
func NewSleepWakeMonitor ¶ added in v2.8.0
func NewSleepWakeMonitor(threshold time.Duration) *SleepWakeMonitor
NewSleepWakeMonitor creates a monitor that detects time jumps exceeding threshold. Recommended threshold: 5 seconds for fast polling loops (allows for normal scheduling delays), or match the polling interval for slower loops.
func (*SleepWakeMonitor) Check ¶ added in v2.8.0
func (m *SleepWakeMonitor) Check() bool
Check returns true if a wake-from-sleep was likely detected since last check. Should be called regularly (e.g., every 100ms-1s) from a polling loop. Thread-safe for concurrent access.
func (*SleepWakeMonitor) Reset ¶ added in v2.8.0
func (m *SleepWakeMonitor) Reset()
Reset resets the monitor's last check time to now. Useful after handling a wake event to prevent false positives, or when resuming monitoring after a pause.
func (*SleepWakeMonitor) SetLastCheckForTesting ¶ added in v2.8.0
func (m *SleepWakeMonitor) SetLastCheckForTesting(t time.Time)
SetLastCheckForTesting sets the last check time for testing purposes. This allows tests to simulate time jumps without accessing private fields.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package command provides an abstraction over exec.Command for testability.
|
Package command provides an abstraction over exec.Command for testability. |
|
Package syncutil provides mutex primitives with optional deadlock detection.
|
Package syncutil provides mutex primitives with optional deadlock detection. |