helpers

package
v2.11.0-beta2 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2026 License: GPL-3.0 Imports: 42 Imported by: 0

Documentation

Index

Constants

View Source
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

View Source
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.

View Source
const MaxURLLength = 8192

MaxURLLength is the maximum allowed URL length for browser opening. This prevents resource exhaustion from malicious tokens with extremely long URLs.

View Source
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

View Source
var ErrUnclosedQuote = errors.New("unclosed quote in command string")
View Source
var GlobalLauncherCache = &LauncherCache{}

GlobalLauncherCache is the singleton instance used throughout the application.

View Source
var GlobalRegexCache = NewRegexCache()

GlobalRegexCache is the singleton instance used throughout the application

View Source
var ReURI = regexp.MustCompile(`^([a-zA-Z][a-zA-Z0-9+.-]*)://(.+)$`)

Functions

func AlphaMapKeys

func AlphaMapKeys[V any](m map[string]V) []string

func CachedCompile

func CachedCompile(pattern string) (*regexp.Regexp, error)

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

func CachedMustCompile(pattern string) *regexp.Regexp

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 ConfigDir

func ConfigDir(pl platforms.Platform) string

func Contains

func Contains[T comparable](xs []T, x T) bool

Contains returns true if slice contains value.

func CopyFile

func CopyFile(sourcePath, destPath string, perm ...os.FileMode) error

CopyFile copies a file from sourcePath to destPath. Optional perm parameter sets file permissions (uses 0644 if not specified).

func DataDir

func DataDir(pl platforms.Platform) string

func DecodeURIIfNeeded added in v2.7.0

func DecodeURIIfNeeded(uri string) string

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

func EnsureDirectories(pl platforms.Platform) error

EnsureDirectories creates the necessary directories for the application. This should be called early during startup, before InitLogging.

func EqualStringSlices added in v2.7.0

func EqualStringSlices(a, b []string) bool

EqualStringSlices compares two string slices for equality

func ExeDir

func ExeDir() string

func FilenameFromPath

func FilenameFromPath(p string) string

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 FreeDiskSpace added in v2.11.0

func FreeDiskSpace(path string) (uint64, error)

FreeDiskSpace returns the number of bytes available to the calling user on the filesystem containing the given path.

func GetAllLocalIPs

func GetAllLocalIPs() []string

GetAllLocalIPs returns all non-loopback private IPv4 addresses

func GetFileSize

func GetFileSize(filePath string) (int64, error)

func GetLocalIP

func GetLocalIP() string

func GetMd5Hash

func GetMd5Hash(filePath string) (string, error)

func GetPathName added in v2.8.0

func GetPathName(path string) string

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 GetSerialDeviceList() ([]string, error)

func GetUSBTopologyPath added in v2.9.0

func GetUSBTopologyPath(devicePath string) string

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

func HasUserDir() (string, bool)

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 InitLogging

func InitLogging(pl platforms.Platform, writers []io.Writer) error

func IsClockReliable added in v2.7.0

func IsClockReliable(t time.Time) bool

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 IsFalsey

func IsFalsey(s string) bool

func IsProcessRunning added in v2.7.0

func IsProcessRunning(proc *os.Process) bool

IsProcessRunning checks if a process is still running. Returns false if the process is nil or has terminated.

func IsTruthy

func IsTruthy(s string) bool

func IsValidExtension added in v2.7.0

func IsValidExtension(ext string) bool

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 IsZip

func IsZip(filePath string) bool

func ListZip

func ListZip(filePath string) ([]string, error)

ListZip returns a slice of all filenames in a zip file.

func LogWriter added in v2.8.0

func LogWriter() io.Writer

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 MaybeJSON

func MaybeJSON(data []byte) bool

func NormalizePathForComparison added in v2.7.0

func NormalizePathForComparison(path string) string

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

func OpenBrowser(url string) error

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

func PadNumber(num, width int) string

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

func PathHasPrefix(path, root string) bool

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 RandSeq

func RandSeq(n int) (string, error)

func RandomElem

func RandomElem[T any](xs []T) (T, error)

RandomElem picks and returns a random element from a slice.

func RandomInt added in v2.7.0

func RandomInt(maxVal int) (int, error)

RandomInt returns a random integer between 0 and maxVal-1 (inclusive).

func ResolveRelativePath added in v2.11.0

func ResolveRelativePath(path string) string

ResolveRelativePath resolves a path relative to ExeDir if it is not absolute. Absolute and empty paths are returned unchanged.

func SplitCommand added in v2.10.0

func SplitCommand(s string) ([]string, error)

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 TokensEqual

func TokensEqual(a, b *tokens.Token) bool

func ValidateBrowserURL added in v2.8.0

func ValidateBrowserURL(url string) error

ValidateBrowserURL checks if the URL has a valid scheme for browser opening. Only http:// and https:// URLs are accepted for security.

func WaitForInternet

func WaitForInternet(maxTries int) bool

func YesNoPrompt

func YesNoPrompt(label string, def bool) bool

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

type PathInfo struct {
	Path      string
	Filename  string
	Extension string
	Name      string
}

func GetPathInfo

func GetPathInfo(path string) PathInfo

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.

Directories

Path Synopsis
Package command provides an abstraction over exec.Command for testability.
Package command provides an abstraction over exec.Command for testability.
Package pathutil provides path resolution utilities with no dependencies on other Zaparoo packages.
Package pathutil provides path resolution utilities with no dependencies on other Zaparoo packages.
Package syncutil provides mutex primitives with optional deadlock detection.
Package syncutil provides mutex primitives with optional deadlock detection.

Jump to

Keyboard shortcuts

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