Last active
July 12, 2025 01:46
-
-
Save Skeeg/9b5a68f0d18c2df0b589274ca534363c to your computer and use it in GitHub Desktop.
batocera-config-bootstrap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Tooling to allow configuration of Batocera settings from files located on the boot partition. | |
# | |
# This can be invoked manually, or ran during the postshare.sh process to start a new system with a preferred configuration. | |
# If the postshare.sh script is used, the configurations will be applied on each boot. | |
# This can make it such that your preferred wifi network will be reapplied on every boot, | |
# So if you change networks as you travel, it would always reset to your /boot/bootstrap.batocera.conf.<named> setting upon reboot. | |
# | |
# Recommended usage is to place files directly on the boot partition after flashing a new image and place bootstrap files in that directory. | |
# | |
# An example can be found here: https://gist.github.com/Skeeg/9b5a68f0d18c2df0b589274ca534363c#file-batocera-bootstrapper-sh | |
# With a postshare.sh example here: https://gist.github.com/Skeeg/9b5a68f0d18c2df0b589274ca534363c#file-postshare-sh | |
# | |
# @skeeg on Batocera Discord. https://github.com/Skeeg | |
# | |
# 20241103 - Initial revision | |
# 20241109 - Update to not sort and clobber the batocera.conf file. | |
# Values in the base file will be updated in place, and new values will be placed in the User-generated Configurations section. | |
# | |
import re | |
import os | |
import sys | |
import xml.etree.ElementTree as ET | |
import xml.dom.minidom as minidom | |
def backup_file(file_to_backup): | |
# Determine the next available backup file number | |
counter = 1 | |
while os.path.exists(f"{file_to_backup}.bak{counter}"): | |
counter += 1 | |
backup_destination_file = f"{file_to_backup}.bak{counter}" | |
if os.path.exists(file_to_backup): | |
with open(file_to_backup, 'rb') as src_file: | |
with open(backup_destination_file, 'wb') as dst_file: | |
dst_file.write(src_file.read()) | |
print(f'Backed up {file_to_backup} to {backup_destination_file}.') | |
return backup_destination_file | |
def prettify_xml(elem): | |
"""Return a pretty-printed XML string for the Element.""" | |
rough_string = ET.tostring(elem, 'utf-8') | |
reparsed = minidom.parseString(rough_string) | |
pretty_string = reparsed.toprettyxml(indent=" ") | |
# Remove unnecessary blank lines | |
lines = pretty_string.split('\n') | |
non_empty_lines = [line for line in lines if line.strip()] | |
return '\n'.join(non_empty_lines) | |
def update_xml(source_file, target_file): | |
# Parse the XML files | |
tree1 = ET.parse(source_file) | |
tree2 = ET.parse(target_file) | |
root1 = tree1.getroot() | |
root2 = tree2.getroot() | |
# Create a dictionary to store key-value pairs from the source file | |
config1 = { | |
(child.attrib['name'], child.tag): child.attrib['value'] for child in root1 | |
} | |
# Drop any values from the target file that are defined in the source file. | |
for child in root2: | |
name = child.attrib['name'] | |
child_type = child.tag # e.g., 'bool', 'int', 'string' | |
if (name, child_type) in config1: | |
child.attrib['value'] = config1[(name, child_type)] | |
config1.pop((name, child_type))# Remove the updated key from the dictionary | |
# Merge remaining entries from the source file into the target file | |
for (name, child_type), value in config1.items(): | |
new_element = ET.Element(child_type) | |
new_element.attrib = {'name': name, 'value': value} | |
root2.append(new_element) | |
# Write the merged results to the target file as pretty-printed XML | |
pretty_xml_as_string = prettify_xml(root2) | |
with open(target_file, 'w', encoding='utf-8') as f: | |
f.write(pretty_xml_as_string) | |
print(f"Updated '{target_file}' with values from '{source_file}.") | |
def merge_keyvalues(source_file, target_file): | |
# Read the source file and target file | |
with open(source_file, 'r') as src_file: | |
source_lines = src_file.readlines() | |
filtered_lines = [line for line in source_lines if line.strip() and not line.strip().startswith('#')] | |
# Create empty map in case no target file exists | |
target_dict = {} | |
# Load existing target file data | |
if os.path.exists(target_file): | |
with open(target_file, 'r') as tgt_file: | |
for line in tgt_file: | |
if line.strip() and not line.strip().startswith('#'): | |
key, _, value = line.partition('=') | |
target_dict[key.strip()] = value.strip() | |
# Process the source file lines | |
for line in filtered_lines: | |
key, _, value = line.partition('=') | |
key = key.strip() | |
value = value.strip() | |
target_dict[key] = value | |
return target_dict | |
def update_keyvalue(source_file, target_file): | |
merged_keyvalues = merge_keyvalues(source_file, target_file) | |
# Read the target file lines | |
with open(target_file, 'r') as tgt: | |
target_lines = tgt.readlines() | |
# Dictionaries to track updated target lines | |
updated_lines = [] | |
target_keys = {} | |
# First pass: Detect and prepare target data for updates, preserving comments | |
for line in target_lines: | |
#matching on pretty much everything but # and | |
match = re.match(r'^(#+)?([\w\s\.\_\-\+\[\]\(\)\{\}\\\/\,\"\'\!\@\#\$\%\^\&\*\<\>\?\:\;\|]+)=(.*)', line.strip()) | |
if match: | |
comment, key, value = match.groups() | |
if key in merged_keyvalues: | |
# Update value and uncomment if necessary | |
new_value = merged_keyvalues[key] | |
updated_lines.append(f"{key}={new_value}\n") | |
target_keys[key] = True | |
else: | |
updated_lines.append(line) | |
target_keys[key] = False | |
else: | |
updated_lines.append(line) | |
# Append new entries from merged_keyvalues that were not in target_keys | |
for key, value in merged_keyvalues.items(): | |
if key not in target_keys: | |
updated_lines.append(f"{key}={value}\n") | |
# Write updated target file | |
with open(target_file, 'w') as tgt: | |
tgt.writelines(updated_lines) | |
print(f"Updated '{target_file}' with values from '{source_file}.") | |
if __name__ == "__main__": | |
if len(sys.argv) != 5: | |
print("Usage: batocera-bootstrap <data_structure> <directory path with bootstrap.\{filename\}* files> <conf file to update> <[True|False] for backup>") | |
print("Example for keyvalue: batocera-bootstrap keyvalue /boot /userdata/system/batocera.conf True") | |
print("Example for xml: batocera-bootstrap xml /boot /userdata/system/configs/emulationstation/es_settings.cfg True") | |
print("The destination base filename is used as the filter, so all /boot/bootstrap.batocera.conf* files in this example will be read") | |
sys.exit(1) | |
data_structure = str.lower(sys.argv[1]) | |
bootstrap_directory = sys.argv[2] | |
config_to_update = sys.argv[3] | |
config_backup = sys.argv[4] | |
if config_backup: | |
backup_file(config_to_update) | |
for file in sorted(os.listdir(bootstrap_directory)): | |
if file.startswith('bootstrap.' + os.path.basename(config_to_update)): | |
if data_structure == 'xml': | |
update_xml(os.path.join(bootstrap_directory, file), config_to_update) | |
elif data_structure == 'keyvalue': | |
update_keyvalue(os.path.join(bootstrap_directory, file), config_to_update) | |
print(f'Configuration bootstrapping complete.') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
if ! [[ -w "/boot" ]]; then | |
batocera-es-swissknife --remount | |
fi | |
REFRESH_GIST_FILES="true" | |
GIST_URL="https://gist.githubusercontent.com/Skeeg/9b5a68f0d18c2df0b589274ca534363c/raw" | |
for FILE in bootstrap.batocera.conf.cheats bootstrap.batocera.conf.common bootstrap.batocera.conf.h700 bootstrap.es_settings.cfg.common bootstrap.es_settings.cfg.h700 bootstrap.batocera.conf.compatibility.settings; | |
do | |
[[ "$REFRESH_GIST_FILES" == "true" ]] && [[ -f "/boot/$FILE" ]] && rm "/boot/$FILE" -v | |
[[ ! -f "/boot/$FILE" ]] && curl "$GIST_URL/$FILE" > "/boot/$FILE" | |
done | |
[[ "$REFRESH_GIST_FILES" == "true" ]] && curl "$GIST_URL/batocera-bootstrap" > /boot/tools/batocera-bootstrap | |
[[ "$REFRESH_GIST_FILES" == "true" ]] && curl "$GIST_URL/postshare.sh" > /boot/postshare.sh | |
chmod +x /boot/tools/batocera-bootstrap | |
chmod +x /boot/postshare.sh | |
resize2fs /dev/mmcblk0p4 | |
cat << 'EO1' >> /boot/postshare.sh | |
# Set SSH authorized keys | |
cat << 'EOF' > /userdata/system/.ssh/authorized_keys | |
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINXsKyrZY/qerpVULDHc+51dJD/eqzrjS02e2quotkCG | |
EOF | |
# Disable battery saver. These are on the read only partition, so removing them is a temporary, per boot change without doing an overlay | |
mv /usr/bin/battery-saver.sh /usr/bin/battery-saver.sh.bak | |
mv /etc/init.d/S96battery-saver-daemon /tmp/S96battery-saver-daemon | |
EO1 | |
cat << 'EOF' > /boot/bootstrap.batocera.conf.sensitive.personalized | |
global.netplay.nickname=UniqueNickname | |
global.retroachievements.password=YourPassword | |
global.retroachievements.username=YourUsername | |
wifi.key=YourPassword | |
wifi.ssid=YourSSID | |
system.hostname=KNULLI | |
EOF | |
cat << 'EOF' > /boot/bootstrap.es_settings.cfg.sensitive.personalized | |
<?xml version="1.0"?> | |
<config> | |
<string name="ScreenScraperPass" value="YourPassword" /> | |
<string name="ScreenScraperUser" value="YourUsername" /> | |
</config> | |
EOF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
global.retroarch.apply_cheats_after_load=true | |
global.retroarch.apply_cheats_after_toggle=true | |
global.retroarch.cheat_database_path="/userdata/cheats/cht" | |
amstradcpc.retroarch.cheat_database_path="/userdata/cheats/cht/Amstrad - GX4000" | |
atari2600.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - 2600" | |
atari800.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - 400-800-1200XL" | |
atari5200.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - 5200" | |
atari7800.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - 7800" | |
jaguar.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - Jaguar" | |
lynx.retroarch.cheat_database_path="/userdata/cheats/cht/Atari - Lynx" | |
colecovision.retroarch.cheat_database_path="/userdata/cheats/cht/Coleco - ColecoVision" | |
dos.retroarch.cheat_database_path="/userdata/cheats/cht/DOS | |
fbneo.retroarch.cheat_database_path="/userdata/cheats/cht/FBNeo - Arcade Games" | |
msx1.retroarch.cheat_database_path="/userdata/cheats/cht/Microsoft - MSX - MSX2 - MSX2P - MSX Turbo R" | |
msx2+.retroarch.cheat_database_path="/userdata/cheats/cht/Microsoft - MSX - MSX2 - MSX2P - MSX Turbo R" | |
msx2+.retroarch.cheat_database_path="/userdata/cheats/cht/Microsoft - MSX - MSX2 - MSX2P - MSX Turbo R" | |
msxturbor.retroarch.cheat_database_path="/userdata/cheats/cht/Microsoft - MSX - MSX2 - MSX2P - MSX Turbo R" | |
pcenginecd.retroarch.cheat_database_path="/userdata/cheats/cht/NEC - PC Engine CD - TurboGrafx-CD" | |
pcengine.retroarch.cheat_database_path="/userdata/cheats/cht/NEC - PC Engine SuperGrafx" | |
pcengine.retroarch.cheat_database_path="/userdata/cheats/cht/NEC - PC Engine - TurboGrafx 16" | |
gb.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Game Boy" | |
gba.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Game Boy Advance" | |
gbc.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Game Boy Color" | |
n64.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Nintendo 64" | |
nds.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Nintendo DS" | |
nes.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Nintendo Entertainment System" | |
snes.retroarch.cheat_database_path="/userdata/cheats/cht/Nintendo - Super Nintendo Entertainment System" | |
megadrive.retroarch.cheat_database_path="/userdata/cheats/cht/Sega - Mega Drive - Genesis" | |
saturn.retroarch.cheat_database_path="/userdata/cheats/cht/Sega - Saturn" | |
zxspectrum.retroarch.cheat_database_path="/userdata/cheats/cht/Sinclair - ZX Spectrum +3" | |
psx.retroarch.cheat_database_path="/userdata/cheats/cht/Sony - PlayStation" | |
thomson.retroarch.cheat_database_path="/userdata/cheats/cht/Thomson - MOTO" | |
tic80.retroarch.cheat_database_path="/userdata/cheats/cht/TIC-80 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
audio.bgmusic=0 | |
global.incrementalsavestates=0 | |
global.netplay_public_announce=1 | |
global.netplay.port=55435 | |
global.netplay=1 | |
global.retroachievements.challenge_indicators=0 | |
global.retroachievements.encore=0 | |
global.retroachievements.hardcore=0 | |
global.retroachievements.leaderboards=0 | |
global.retroachievements.richpresence=0 | |
global.retroachievements.screenshot=1 | |
global.retroachievements.sound=zelda-secret | |
global.retroachievements.verbose=1 | |
global.retroachievements=1 | |
global.retroarch.block_sram_overwrite=true | |
global.retroarch.menu_show_online_updater=true | |
global.retroarch.rewind_buffer_size = "134217728" | |
global.retroarch.rewind_buffer_size_step = "16" | |
global.retroarch.rewind_granularity=2 | |
global.retroarch.savefile_directory=/userdata/saves | |
global.retroarch.savestate_auto_index=true | |
global.retroarch.savestate_directory=/userdata/states | |
global.retroarch.savestate_file_compression=true | |
global.retroarch.savestate_max_keep=20 | |
global.retroarch.savestate_thumbnail_enable=true | |
global.retroarch.savestates_in_content_dir=false | |
global.retroarch.sort_savefiles_by_content_enable=true | |
global.retroarch.sort_savestates_by_content_enable=true | |
global.retroarchcore.fceumm_nospritelimit=enabled | |
global.retroarchcore.fceumm_sndquality=Very High | |
global.retroarchcore.fceumm_turbo_enable=Both | |
global.retroarchcore.pcsx_rearmed_neon_enhancement_enable=enabled | |
global.retroarchcore.pcsx_rearmed_neon_enhancement_no_main=enabled | |
global.retroarchcore.pcsx_rearmed_neon_enhancement_tex_adj=enabled | |
global.savestates=1 | |
global.toggle_fast_forward=1 | |
psx.core=pcsx_rearmed | |
psx.emulator=libretro | |
ScrollLoadMedias=0 | |
system.services=syncthing | |
system.timezone=America/Denver | |
wifi.enabled=1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
snes["Contra III - The Alien Wars (USA) (MSU1).sfc"].rewind=0 | |
snes["Donkey Kong Country 2 - Diddys Kong Quest (USA) (MSU1).sfc"].rewind=0 | |
snes["DuckTales SNES (MSU1).sfc"].rewind=0 | |
snes["Final Fantasy VI (USA) (MSU1).sfc"].rewind=0 | |
snes["Legend of Zelda, The - A Link to the Past (USA) (MSU1).sfc"].rewind=0 | |
snes["Mega Man 4 (USA) (MSU1).sfc"].rewind=0 | |
snes["Mega Man 7 (USA) (MSU1).sfc"].rewind=0 | |
snes["Mega Man X (USA) (Rev 1) (MSU1).sfc"].rewind=0 | |
snes["Mega Man X2 (USA) (MSU1).sfc"].rewind=0 | |
snes["Mega Man X3 (USA) (MSU1).sfc"].rewind=0 | |
snes["Mother 2 - Giygas Strikes Back! (USA) (MSU1) [n].sfc"].rewind=0 | |
snes["Rygar (USA) (Unl) (MSU1).sfc"].rewind=0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
global.retroarch.menu_driver=rgui | |
global.rewind=1 | |
n64.retroarch.rewind_enable=true | |
n64.retroarch.rewind_granularity=5 | |
psx.retroarch.rewind_enable=true | |
psx.retroarch.rewind_granularity=3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
display.brightness=75 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
display.brightness=50 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
display.brightness=50 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<config> | |
<bool name="InvertButtons" value="false" /> | |
<bool name="CheevosCheckIndexesAtStart" value="true" /> | |
<bool name="LocalArt" value="true" /> | |
<bool name="FavoritesFirst" value="true" /> | |
<bool name="NetPlayCheckIndexesAtStart" value="true" /> | |
<bool name="ScrapeBezel" value="true" /> | |
<bool name="ScrapeBoxBack" value="true" /> | |
<bool name="ScrapeFanart" value="true" /> | |
<bool name="ScrapeManual" value="true" /> | |
<bool name="ScrapeMap" value="true" /> | |
<bool name="ScrapeVideos" value="true" /> | |
<bool name="ScreenSaverControls" value="true" /> | |
<bool name="ScreenSaverMarquee" value="false" /> | |
<bool name="ShowManualIcon" value="true" /> | |
<bool name="ShowSaveStates" value="true" /> | |
<bool name="atari800.ungroup" value="true"/> | |
<bool name="audio.bgmusic" value="false" /> | |
<int name="recent.sort" value="7" /> | |
<string name="CollectionSystemsAuto" value="all,favorites,recent" /> | |
<string name="FolderViewMode" value="having multiple games"/> | |
<string name="HiddenSystems" value="imageviewer;odcommander;tools"/> | |
<string name="ScraperRegion" value="us" /> | |
<string name="ScraperImageSrc" value="mixrbv2" /> | |
<string name="ScrapperImageSrc" value="mixrbv2" /> | |
<string name="ScreenSaverBehavior" value="random video" /> | |
<string name="ScreenSaverDecorations" value="none" /> | |
<string name="ScreenSaverGameInfo" value="start & end" /> | |
<string name="ShowFlags" value="auto" /> | |
<string name="ThemeSet" value="es-theme-art-book-next" /> | |
</config> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<config> | |
<string name="FontScale" value="1.5" /> | |
<string name="ForceSmallScreen" value="true" /> | |
<string name="MenuFontScale" value="2.0" /> | |
</config> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<config> | |
<string name="FontScale" value="1.25" /> | |
<string name="ForceSmallScreen" value="true" /> | |
<string name="MenuFontScale" value="1.5" /> | |
</config> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<savestates> | |
<emulator name="libretro" autosave="true" incremental="true"> | |
<directory>/userdata/states/{{system}}</directory> | |
<file>{{romfilename}}.state{{slot}}</file> | |
<image>{{romfilename}}.state{{slot}}.png</image> | |
<autosave_file>{{romfilename}}.state.auto</autosave_file> | |
<autosave_image>{{romfilename}}.state.auto.png</autosave_image> | |
<!-- cores management --> | |
<!--<defaultCoreDirectory>/userdata/states/{{system}}</defaultCoreDirectory>--> | |
<!-- Sample core management | |
<core name="mesen" enabled="false"/> | |
<core name="fceumm" system="nes" directory="/userdata/states/{{system}}"/> | |
--> | |
</emulator> | |
<emulator name="bigpemu" firstslot="001" lastslot="999" autosave="false" incremental="false"> | |
<directory>/userdata/states/{{system}}/bigpemu</directory> | |
<file>{{romfilename}}_state{{slot2d}}.bigpstate</file> | |
<image>{{romfilename}}_state{{slot2d}}.png</image> | |
</emulator> | |
<emulator name="bizhawk" firstslot="0" lastslot="9" autosave="false" incremental="false"> | |
<directory>/userdata/states/{{system}}/bizhawk/sstates/{{core}}</directory> | |
<file>{{romfilename}}.QuickSave{{slot0}}.State</file> | |
<image>{{romfilename}}.QuickSave{{slot0}}.png</image> | |
</emulator> | |
<emulator name="dolphin" firstslot="1" lastslot="10"> | |
<directory>/userdata/states/{{system}}/dolphin</directory> | |
<file>{{romfilename}}.s{{slot2d}}</file> | |
<image>{{romfilename}}.s{{slot2d}}.png</image> | |
</emulator> | |
<emulator name="flycast" firstslot="1" lastslot="9"> | |
<directory>/userdata/states/{{system}}/flycast/sstates</directory> | |
<file>{{romfilename}}_{{slot0}}.state</file> | |
<image>{{romfilename}}_{{slot0}}.png</image> | |
</emulator> | |
<emulator name="jgenesis" firstslot="0" lastslot="9" autosave="false" incremental="false"> | |
<directory>/userdata/states/{{system}}/jgenesis/states</directory> | |
<file>{{romfilename}}_{{slot0}}.jst</file> | |
<image>{{romfilename}}_{{slot0}}.png</image> | |
</emulator> | |
<emulator name="pcsx2" firstslot="1" lastslot="10" autosave="true" incremental="true"> | |
<directory>/userdata/states/{{system}}/pcsx2</directory> | |
<file>{{romfilename}}.{{slot2d}}.p2s</file> | |
<image>{{romfilename}}.{{slot2d}}.p2s.png</image> | |
<autosave_file>{{romfilename}}.resume.p2s</autosave_file> | |
<autosave_image>{{romfilename}}.resume.p2s.png</autosave_image> | |
</emulator> | |
<emulator name="duckstation" firstslot="1" lastslot="10" autosave="true" incremental="true"> | |
<directory>/userdata/states/{{system}}/duckstation</directory> | |
<file>{{romfilename}}_{{slot2d}}.sav</file> | |
<image>{{romfilename}}_{{slot2d}}.png</image> | |
<autosave_file>{{romfilename}}_resume.sav</autosave_file> | |
<autosave_image>{{romfilename}}_resume.png</autosave_image> | |
</emulator> | |
<emulator name="openmsx" firstslot="0" lastslot="20" autosave="false" incremental="false"> | |
<directory>/userdata/states/{{system}}/openmsx</directory> | |
<file>{{romfilename}}_{{slot0}}.oms</file> | |
<image>{{romfilename}}_{{slot0}}.png</image> | |
</emulator> | |
<emulator name="ppsspp" firstslot="0" lastslot="4"> | |
<directory>/userdata/states/{{system}}/ppsspp</directory> | |
<file>{{romfilename}}_{{slot0}}.ppst</file> | |
<image>{{romfilename}}_{{slot0}}.jpg</image> | |
</emulator> | |
<emulator name="mupen64_notsupported" firstslot="0" lastslot="9"> | |
<directory>/userdata/states/{{system}}/mupen64</directory> | |
<file>{{romfilename}}.st{{slot0}}</file> | |
<image>{{romfilename}}.st{{slot0}}.png</image> | |
</emulator> | |
</savestates> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment