Skip to content

JayCanuck/gamelist-utils

Repository files navigation

gamelist-utils

NPM GitHub Actions Node.js Version

A comprehensive TypeScript toolbox for managing EmulationStation gamelist.xml romsets with CLI and programmatic API support.

Table of Contents

Features

  • 🎮 Comprehensive RomSet Management - Copy, filter, transform, and optimize EmulationStation romsets
  • 🖼️ Media Handling - Manage images, videos, thumbnails, and marquees with resize and conversion support
  • 🔄 Format Conversion - Convert between EmulationStation, RetroArch, SimpleMenu, muOS, and Onion formats
  • 🎯 Advanced Filtering - Include/exclude games by metadata, ID, or system with JSON filter files
  • 🚀 Performance Optimized - Minimal data transfer overhead, lazy-loading, and efficient file operations
  • 📦 Multi-System Support - Batch operations across multiple romset directories
  • 🔧 TypeScript API - Full programmatic access with type safety
  • Well Tested - Comprehensive test coverage with Vitest

Installation

Global Installation (CLI)

Install globally for command-line usage:

npm install -g gamelist-utils

NPX (No Installation)

Run directly without installing:

npx gamelist-utils [action] [options]

Local Installation (API)

Install as a project dependency for programmatic usage:

npm install gamelist-utils

Requirements: Node.js ≥ 22.0.0

Quick Start

# Backup your gamelist.xml
gamelist backup

# Copy a romset with filtering
gamelist copy --dest /path/to/destination --filter ./filters/top35.json

# Create a collection file
gamelist collection --name favorites --filter ./my-filter.json

# Optimize for low-end systems (remove videos and descriptions)
gamelist video --remove
gamelist simplify

# Handle multiple systems at once
gamelist backup --multi=gb,gbc,gba,nes,snes

RomSet Structure

This tool expects a specific romset directory structure, typically generated by Skraper:

system/
├── gamelist.xml
├── media/
│   ├── box2d/
│   ├── box3d/
│   ├── manual/
│   ├── mixed/
│   ├── screenshot/
│   ├── snap/
│   ├── title/
│   └── wheel/
└── [rom files - .zip, .bin, .iso, etc.]

Key Points:

  • Media files are organized by type in subdirectories under ./media
  • Each media file is named identically to its corresponding ROM (without file extension)
  • Custom Skraper mixes are available in the ./skraper directory

Configuration

Customize media directory names using environment variables or a .env file:

Variable Default Description
GAMELIST_MEDIA media Base media directory
GAMELIST_BOX2D box2d 2D box art subdirectory
GAMELIST_BOX3D box3d 3D box art subdirectory
GAMELIST_MIXED mixed Mixed image subdirectory
GAMELIST_SCREENSHOT screenshot Screenshot subdirectory
GAMELIST_SNAP snap Video snap subdirectory
GAMELIST_TITLE title Title screen subdirectory
GAMELIST_MARQUEE wheel Marquee/wheel subdirectory
GAMELIST_MANUAL manual Manual/PDF subdirectory

See the included .env example for reference.

CLI Actions

All actions support --help for detailed usage information.

Core Actions

Action Description
backup Creates a backup copy of gamelist.xml as gamelist.xml.bak
copy Copies romset to destination with optional transformations (optimized for slow media)
duplicates Scans for duplicate game IDs and potential name duplicates
collection Creates EmulationStation collection .cfg files with filter support
playlists Generates .m3u playlists for multi-disc games

Media Management

Action Description
image-type Updates game entries to use alternate image types with fallback support
image-resize Resizes images to desired dimensions with various object-fit strategies
thumbnail Adds/removes thumbnail entries and optionally generates thumbnail files
marquee Adds/removes marquee image entries and files
video Adds/removes video snap entries and files

Format Conversion

Action Description
simplify Removes descriptions and unused media to reduce file size and load time
simplemenu Converts romset to SimpleMenu format (OpenDingux)
retroarch Symlinks media files for RetroArch playlists
es-de Symlinks gamelist and media to EmulationStation-DE locations
muos Converts romset for muOS compatibility
onion Converts romset for Miyoo/Onion compatibility

Utilities

Action Description
lock Sets gamelist.xml as read-only (Windows only)
unlock Removes read-only attribute from gamelist.xml (Windows only)

Advanced Usage

Multiple RomSets

Use the -m / --multi option to run actions across multiple subdirectories:

# Process all subdirectories
gamelist lock --multi

# Process specific systems
gamelist backup --multi=gb,gbc,gba,nes,snes

# Copy multiple systems with filtering
gamelist copy --dest /backup --multi --filter ./filters/top35.json

Rom List Filters

Filter files use JSON format with include and exclude arrays. Supports exact matches or Contains suffix for partial matching.

Example: Name-based Filter

{
  "include": [
    { "name": "Duck Hunt" },
    { "nameContains": "Mario" }
  ]
}

Example: ID-based Multi-System Filter

{
  "include": [
    { "id": "14526", "system": "NEC PC Engine" },
    { "id": "59382", "system": "NEC PC Engine SuperGrafx" },
    { "id": "14543", "system": "NEC PC Engine" },
    { "id": "103574", "system": "NEC PC Engine CD-Rom" },
    { "id": "59796", "system": "NEC PC Engine" }
  ]
}

Filter Properties:

  • id - ScreenScraper game ID from the <game> tag
  • system - System name from the <provider> tag
  • Any game metadata field (e.g., name, genre, developer, publisher)
  • Add Contains suffix for partial matching (e.g., nameContains, genreContains)

Pre-made Filters: Top 35 game filters based on Wikipedia sales statistics are available in the ./filters directory.

API Usage

All CLI actions are available as programmatic APIs with full TypeScript support:

import { simplify, copy, collection } from 'gamelist-utils';

// Simplify a romset
await simplify.api(process.cwd(), { quiet: true });

// Copy with filtering
await copy.api('/source/path', {
  dest: '/destination/path',
  filter: './filters/top35.json',
  quiet: true
});

// Create a collection
await collection.api('/romset/path', {
  name: 'favorites',
  filter: './my-favorites.json'
});

API Features:

  • Lazy-loaded modules for minimal overhead
  • Promise-based async/await support
  • All CLI options available programmatically
  • Use quiet: true to suppress console output

Development

This project uses TypeScript with strict settings, Vitest for testing, and ESLint + Prettier for code quality.

Commands

npm run build         # Compile TypeScript to dist/
npm run lint          # Lint with ESLint + Prettier
npm run fix           # Auto-fix linting issues
npm run typecheck     # Type-check without emitting
npm test              # Run Vitest test suite
npm run test:coverage # Run tests with coverage report
npm run check         # Run all quality gates (lint + typecheck + test)

Tech Stack

  • Runtime: Node.js ≥ 22 (ESM)
  • Language: TypeScript 5.x with strict settings
  • Testing: Vitest with V8 coverage
  • Linting: ESLint 9 (flat config) + Prettier
  • CI: GitHub Actions

Contributing

Contributions are welcome! Please ensure all tests pass and code meets quality standards:

npm run check

License Information

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.