esapi

package
v2.18.0-beta.2 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: GPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package esapi provides types and helpers for reading EmulationStation gamelist.xml files.

EmulationStation (ES) has been forked many times; each fork and scraper tool differs in which fields it writes and how it formats media paths. The Game and Folder structs here cover the superset of all known fields. Unknown elements are silently ignored by encoding/xml.

Path format conventions

All path-based media fields (image, thumbnail, video, etc.) accept three path formats, which ES resolves at runtime:

  • Absolute: /home/pi/.emulationstation/downloaded_images/snes/game.png
  • System-relative: ./media/images/game.png (relative to system ROM folder)
  • Home-relative: ~/.emulationstation/downloaded_images/snes/game.png

ES will try to write paths as system-relative or home-relative when saving so that installations remain portable across machines.

Fork / version landscape

This file comments each media field with observed differences across:

  • Aloshi (original ES, ~2014)
  • RetroPie fork (RetroPie/EmulationStation)
  • Batocera fork (batocera-linux/batocera-emulationstation, MetaData.cpp)
  • ES-DE (EmulationStation Desktop Edition)
  • AmberELEC / EmuELEC forks
  • Recalbox fork
  • Skyscraper scraper (muldjord/skyscraper) output
  • ARRM scraper output
  • Pegasus frontend (compatible reader)

Index

Constants

View Source
const (
	ESDateFormat = "20060102T150405"
	// MaxGameListXMLSize bounds the bytes read from one gamelist.xml. The file
	// is decoded as a stream, so this guards against a single oversized element
	// rather than sizing a buffer; MaxGameListEntries is the limit a large but
	// ordinary library reaches first.
	MaxGameListXMLSize = 128 << 20
	// MaxGameListXMLSizeConstrained is the limit for platforms that report
	// ResourceConstrained. Decoded entries stay in memory for the whole scrape,
	// and a list near MaxGameListXMLSize costs hundreds of MB — more than a
	// MiSTer has, on top of the indexes a scrape already builds. Such a device
	// is told its file is too large rather than being pushed into swapless
	// thrashing; its artwork can still be imported from media folders.
	MaxGameListXMLSizeConstrained = 16 << 20
	MaxGameListEntries            = 100_000
	MaxGameListXMLDepth           = 64
)

ESDateFormat is the strftime-style format EmulationStation uses for releasedate and lastplayed: "%Y%m%dT%H%M%S". In Go's time package this translates to the layout below. Some scrapers omit the time component and write only the date portion ("19950311T000000").

Variables

View Source
var (
	ErrGameListTooLarge     = errors.New("gamelist.xml exceeds size limit")
	ErrGameListTooManyItems = errors.New("gamelist.xml exceeds entry limit")
	ErrGameListTooDeep      = errors.New("gamelist.xml exceeds XML depth limit")
	// ErrGameListInvalidRoot covers every way a file fails to be a gameList
	// document at all: no root element, a different root, or content outside
	// it. Each wraps this sentinel with the detail, so a caller reporting the
	// failure to a user has one condition to name.
	ErrGameListInvalidRoot = errors.New("file is not an EmulationStation game list")
)
View Source
var ErrInvalidRunningGameResponse = errors.New("invalid running game response")

Functions

func APIEmuKill

func APIEmuKill() error

func APILaunch

func APILaunch(path string) error

func APINotify

func APINotify(msg string) error

func APIRequest

func APIRequest(path, body string, timeout time.Duration) ([]byte, error)

func FormatESDate added in v2.12.0

func FormatESDate(t time.Time) string

FormatESDate formats a time.Time into the EmulationStation datetime string format ("YYYYMMDDTHHMMSS") used by releasedate and lastplayed fields.

func IsAvailable

func IsAvailable() bool

IsAvailable checks if the EmulationStation API server is running

func ParseESDate added in v2.12.0

func ParseESDate(s string) (time.Time, error)

ParseESDate parses an EmulationStation datetime string into a time.Time. ES stores dates as "YYYYMMDDTHHMMSS" (e.g. "19950311T000000") using ESDateFormat. Zone-less ES timestamps are treated as UTC (time.Parse with a layout that has no timezone produces a time with UTC location). Callers that need local-time semantics must convert the result with time.In or time.ParseInLocation. Returns the zero time and an error if the string is empty or malformed.

func ParseRating added in v2.12.0

func ParseRating(s string) (float64, error)

ParseRating parses an ES rating string (a float between "0" and "1") into a float64. Returns 0 and an error if the string is empty, malformed, or outside [0, 1].

func ValidateGameListXML added in v2.17.0

func ValidateGameListXML(data []byte) error

ValidateGameListXML enforces shared size, depth, root, and entry limits.

Types

type Folder added in v2.12.0

type Folder struct {
	XMLName xml.Name `xml:"folder"`

	// Path is the subfolder path, typically relative to the system ROM folder.
	Path string `xml:"path"`

	Name string `xml:"name,omitempty"`
	Desc string `xml:"desc,omitempty"`

	// Image and Thumbnail follow the same fork/path differences as in Game.
	Image     string `xml:"image,omitempty"`
	Thumbnail string `xml:"thumbnail,omitempty"`

	// Some forks (Batocera, ES-DE) also support video and marquee on folders.
	Video   string `xml:"video,omitempty"`
	Marquee string `xml:"marquee,omitempty"`
}

Folder represents a <folder> entry in the gamelist. Folders support a smaller set of metadata than games. Most path-based media fields follow the same fork differences as in Game — see Game field comments.

type Game

type Game struct {
	XMLName             xml.Name `xml:"game"`
	Players             string   `xml:"players,omitempty"`
	Name                string   `xml:"name,omitempty"`
	SortName            string   `xml:"sortname,omitempty"`
	Desc                string   `xml:"desc,omitempty"`
	Image               string   `xml:"image,omitempty"`
	Thumbnail           string   `xml:"thumbnail,omitempty"`
	Video               string   `xml:"video,omitempty"`
	Marquee             string   `xml:"marquee,omitempty"`
	Wheel               string   `xml:"wheel,omitempty"`
	FanArt              string   `xml:"fanart,omitempty"`
	TitleShot           string   `xml:"titleshot,omitempty"`
	Manual              string   `xml:"manual,omitempty"`
	Magazine            string   `xml:"magazine,omitempty"`
	Map                 string   `xml:"map,omitempty"`
	Genre               string   `xml:"genre,omitempty"`
	Cartridge           string   `xml:"cartridge,omitempty"`
	BoxBack             string   `xml:"boxback,omitempty"`
	Mix                 string   `xml:"mix,omitempty"`
	Rating              string   `xml:"rating,omitempty"`
	ReleaseDate         string   `xml:"releasedate,omitempty"`
	ArcadeSystemName    string   `xml:"arcadesystemname,omitempty"`
	Path                string   `xml:"path"`
	Publisher           string   `xml:"publisher,omitempty"`
	LastPlayed          string   `xml:"lastplayed,omitempty"`
	Developer           string   `xml:"developer,omitempty"`
	Bezel               string   `xml:"bezel,omitempty"`
	Tags                string   `xml:"tags,omitempty"`
	ScreenScraperIDAttr string   `xml:"id,attr,omitempty"`
	Emulator            string   `xml:"emulator,omitempty"`
	Core                string   `xml:"core,omitempty"`
	Lang                string   `xml:"lang,omitempty"`
	Region              string   `xml:"region,omitempty"`
	Source              string   `xml:"source,omitempty"`
	CRC32               string   `xml:"crc32,omitempty"`
	MD5                 string   `xml:"md5,omitempty"`
	MultiDisk           string   `xml:"multidisk,omitempty"`
	CheevosHash         string   `xml:"cheevosHash,omitempty"`
	Genres              string   `xml:"genres,omitempty"`
	SourceAttr          string   `xml:"source,attr,omitempty"` //nolint:revive // attr and el
	ParentIDAttr        string   `xml:"parentid,attr,omitempty"`
	Screenshot          string   `xml:"screenshot,omitempty"`
	TitleScreen         string   `xml:"titlescreen,omitempty"`
	Boxart2D            string   `xml:"boxart2d,omitempty"`
	Family              string   `xml:"family,omitempty"`
	Boxart3D            string   `xml:"boxart3d,omitempty"`
	PlayCount           int      `xml:"playcount,omitempty"`
	ScreenScraperID     int      `xml:"id,omitempty"` //nolint:revive // attr and el
	CheevosID           int      `xml:"cheevosId,omitempty"`
	GameTime            int      `xml:"gametime,omitempty"`
	Favorite            bool     `xml:"favorite,omitempty"`
	Hidden              bool     `xml:"hidden,omitempty"`
	KidGame             bool     `xml:"kidgame,omitempty"`
}

Game represents a single <game> entry in the gamelist.xml.

Fields are marked omitempty so that re-marshalling preserves sparseness; ES itself omits fields whose value matches the type default.

Media path field differences (20+ documented cases)

Each path-type field below carries a comment block describing known fork/scraper differences. The short codes used are:

[Aloshi]   — original Aloshi/EmulationStation (master branch)
[RPI]      — RetroPie fork
[Batocera] — Batocera fork (MetaData.cpp defines the canonical tag names)
[ES-DE]    — EmulationStation Desktop Edition
[AmberELEC]— AmberELEC / EmuELEC forks
[Recalbox] — Recalbox fork
[Sky]      — Skyscraper scraper output
[ARRM]     — ARRM scraper output

func (*Game) UnmarshalXML added in v2.18.0

func (g *Game) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML accepts the ZapScraper box2d alias while keeping boxart2d as the canonical field and serialized tag. An explicit canonical value wins.

type GameList

type GameList struct {
	XMLName xml.Name `xml:"gameList"`
	Games   []Game   `xml:"game"`
	Folders []Folder `xml:"folder"`
	// Skipped counts <game> and <folder> entries dropped because one of their
	// typed fields held a value that does not parse, such as a non-numeric
	// <playcount>. The rest of the document is still decoded.
	Skipped int `xml:"-"`
}

GameList is the root element of an EmulationStation gamelist.xml file. It may contain any mix of <game> and <folder> children.

func ParseGameListXML added in v2.17.0

func ParseGameListXML(data []byte) (GameList, error)

ParseGameListXML validates and decodes a full gamelist.xml document.

func ReadGameListXML

func ReadGameListXML(path string) (GameList, error)

ReadGameListXML opens and decodes a full EmulationStation gamelist.xml file.

func ReadGameListXMLFS added in v2.17.0

func ReadGameListXMLFS(fs afero.Fs, path string) (GameList, error)

ReadGameListXMLFS decodes a full gamelist.xml through the supplied filesystem.

func ReadGameListXMLLimitFS added in v2.18.0

func ReadGameListXMLLimitFS(fs afero.Fs, path string, maxBytes int64) (GameList, error)

ReadGameListXMLLimitFS decodes a full gamelist.xml, reading at most maxBytes. Callers on memory-constrained platforms pass MaxGameListXMLSizeConstrained so an oversized list is refused with a clear reason instead of being decoded into memory the device does not have.

type GameListTooLargeError added in v2.18.0

type GameListTooLargeError struct {
	Limit int64
}

GameListTooLargeError reports the limit that a gamelist.xml exceeded, which differs by platform. It satisfies errors.Is(err, ErrGameListTooLarge).

func (*GameListTooLargeError) Error added in v2.18.0

func (e *GameListTooLargeError) Error() string

func (*GameListTooLargeError) Unwrap added in v2.18.0

func (*GameListTooLargeError) Unwrap() error

type GameReference added in v2.17.0

type GameReference struct {
	Name string `xml:"name"`
	Path string `xml:"path"`
}

GameReference is the lightweight gamelist subset needed during media discovery.

func ReadGameReferencesXML added in v2.17.0

func ReadGameReferencesXML(path string) ([]GameReference, error)

ReadGameReferencesXML decodes only names and paths needed during discovery.

func ReadGameReferencesXMLFS added in v2.17.0

func ReadGameReferencesXMLFS(fs afero.Fs, path string) ([]GameReference, error)

ReadGameReferencesXMLFS decodes lightweight game references through the supplied filesystem.

type RunningGameResponse

type RunningGameResponse struct {
	ID          string `json:"id"`
	Path        string `json:"path"`
	Name        string `json:"name"`
	SystemName  string `json:"systemName"`
	Desc        string `json:"desc"`
	Image       string `json:"image"`
	Video       string `json:"video"`
	Marquee     string `json:"marquee"`
	Thumbnail   string `json:"thumbnail"`
	Rating      string `json:"rating"`
	ReleaseDate string `json:"releaseDate"`
	Developer   string `json:"developer"`
	Genre       string `json:"genre"`
	Genres      string `json:"genres"`
	Players     string `json:"players"`
	Favorite    string `json:"favorite"`
	KidGame     string `json:"kidgame"`
	LastPlayed  string `json:"lastplayed"`
	CRC32       string `json:"crc32"`
	MD5         string `json:"md5"`
	GameTime    string `json:"gametime"`
	Lang        string `json:"lang"`
	CheevosHash string `json:"cheevosHash"`
}

func APIRunningGame

func APIRunningGame() (RunningGameResponse, bool, error)

Jump to

Keyboard shortcuts

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