Pico OLED - display game images, metadata and system info on a 1.5" OLED screen project
-
Hi all,
I'm currently trying to write a python script that will drive an OLED display with various information about the current game launched and being run, including images which we've already got working. See here for progress on the code - we're using an RP2040 to receive data in over USB and then driving the screen via SPI for those interested. It will be open-source once completed so people can integrate it into their cabinet builds/whatever else: https://github.com/mackieks/pico_oled
I've managed to use runcommand-onstart.sh to get information about the system and game running and send that information to a temporary file to be used by my python script to fetch the relevant screenshot/wheel we want to show, but I'm wondering if it's possible to also extract the game metadata located in gamelist.xml using something like ElementTree, based on the current game being run: https://docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements. It's a little more complex as I'm not familiar with xml files and how to extract the information I want based on the current game running.
I'm trying to extract some lines of code from a .xml file between two points, and then store this text in several variables to be displayed to an OLED display (for a RetroPie project). Would anyone be able to help? I've been able to get the GameName stored into a variable, but I need to still figure out how to:
a) Iterate the XML file line by line and identify where in the .XML file a match is found (i.e what row) to the GameName variable stored - in this case <name>Virtua Tennis</name>
b) Search for first matching string between <name> and </name> and then extract everything below it until the next instance of </game> is found
c) Store each of these child nodes into separate variables to be used in my program
What I've written so far:import xml.etree.ElementTree as ET def get_game_metadata(): tempFileA = open('/tmp/retropie_oled.log', 'r', -1, "utf-8") gamemetadata = tempFileA.readlines() file.close() GameName = gamemetadata[0].split('/')[-1].rsplit('.',1)[0] # Get the current game name SystemName = gamemetadata[0].split('/')[4] # Get the current system name systemxml = "/opt/retropie/configs/all/emulationstation/gamelists/SystemName/gamelist.xml" tree = ET.parse (systemxml) root = tree.getroot() for game in root.findall('game'): print(game.tag, game.attrib) game_name = game.text('name') game_desc = game.text('desc') game_releasedate = game.text('releasedate') game_developer = game.text('developer') game_publisher = game.text('publisher') game_genre = game.text('genre')
Contents of retropie_oled.log:
/home/pi/RetroPie/roms/dreamcast/media/screenshot/Marvel vs. Capcom 2 (USA).png /home/pi/RetroPie/roms/dreamcast/media/wheel/Marvel vs. Capcom 2 (USA).png
Here's an example of the .xml file with contents I'm trying to extract, everything between </name> and </game> of current game name stored above in GameName variable (an exact string match).
<?xml version="1.0"?> <gameList> <game> <path>./Virtua Tennis (USA).cue</path> <name>Virtua Tennis</name> <image>~/.emulationstation/downloaded_images/dreamcast/Virtua Tennis (USA)-image.jpg</image> <releasedate>20000712T000000</releasedate> <developer>Hitmaker</developer> <publisher>Sega</publisher> <genre>Sports</genre> </game> <game> <path>./Jet Grind Radio (USA).gdi</path> <name>Jet Grind Radio</name> <desc>Tokyo-to, a city not unlike Tokyo, somewhere in Asia, in the near future. This is the story about the GG's, one of three rival teenage gangs who ride motorised inline skates and are tagging the streets with graffiti. There is a turf war going on between the gangs GG's, the Poison Jam, and the high-tech freaks, the Noise Tanks. The evil Rokkaku Corporation has the corrupt police in their grasp, and, headed by Captain Onishima, the cops are hell-bent on subduing the unruly teen protagonists. But there is light in the darkness: the underground DJ, "Professor K," and his Jet Set Radio station keep tabs on what is happening on the streets of Tokyo-to, and soon our teens will have something much darker than the police to worry about.</desc> <image>~/.emulationstation/downloaded_images/dreamcast/Jet Grind Radio (USA)-image.jpg</image> <releasedate>20001101T000000</releasedate> <developer>Smilebit</developer> <publisher>Sega</publisher> <genre>Action</genre> <playcount>1</playcount> <lastplayed>20220206T165741</lastplayed> </game> </gameList>
The end result I would like is an output like the below, based on extracting the information relating to Virtua Tennis (i.e the currently running game) from </name> to </game>. I then would be able to store each individual line into its own variable to be used in my program to send over to the OLED.
The problem with my current function is that it returns all of the child nodes related to <game>, whereas I want to only print the information related to the current running game (not all info within gamelist.xml).
<image>~/.emulationstation/downloaded_images/dreamcast/Virtua Tennis (USA)-image.jpg</image> <releasedate>20000712T000000</releasedate> <developer>Hitmaker</developer> <publisher>Sega</publisher> <genre>Sports</genre>
Any help would be appreciated! I tried referencing @mitu's excel script here since I think he is able to store the keys and their corresponding values, however I wasn't able to figure out how he's done it: https://gist.github.com/cmitu/73cf0783e9e1e4e02d8ce5c44f7a8984
-
Manually parsing an XML file is brittle and can lead to errors. It's better to use an XML parsing library/API.
Here's an example that, given a game name and a gamelist path, will print the description for it:#!/usr/bin/env python3 import xml.etree.ElementTree as et import xml.sax.saxutils as saxutils gamelist_path = '/home/pi/RetroPie/roms/arcade/gamelist.xml' game_name = '1945k 3' data = et.parse(gamelist_path) # Find all games with matching names game_list = data.findall(u".//game[name='{0}']".format(saxutils.escape(game_name))) # Get games matching given name for game in game_list: if game.find('desc') is not None: print('Description is:\n' + game.find('desc').text) if game.find('rating') is not None: print('Rating:\n' + game.find('rating').text)
-
Thanks a lot, this hopefully is exactly what I need.
Hopefully something like this will work well! I'm a bit of a python noob so always open to feedback.@mitu Out of interest, what is the u" doing before .// in game_list declaration? I couldn't find any information from here: https://docs.python.org/3/library/xml.etree.elementtree.html
def get_game_metadata(): tempFileA = open('/tmp/retropie_oled.log', 'r', -1, "utf-8") gamemetadata = tempFileA.readlines() file.close() game_name = gamemetadata[0].split('/')[-1].rsplit('.',1)[0] # Get the current game name system_name = gamemetadata[0].split('/')[4] # Get the current system name gamelist_path = '/opt/retropie/configs/all/emulationstation/gamelists/system_name/gamelist.xml' data = et.parse(gamelist_path) # Find all the games with matching names within gamelist_path game_list = data.findall(u".//game[name='{0}']".format(saxutils.escape(game_name))) for game in game_list: if game.find('desc') is not None: desc = str('Description is :\n' + game.find('desc').text) if game.find('rating') is not None: rating = str('Rating:\n' + game.find('rating').text) if game.find('releasedate') is not None: release_date = str ('Release Date:\n' + game.find('releasedate').text) if game.find('developer') is not None: developer = str ('Developer:\n' + game.find('developer').text) if game.find('publisher') is not None: publsiher = str ('Publisher:\n' + game.find('releasedate').text) if game.find('genre') is not None: genre = str ('Genre:\n' + game.find('genre').text)
-
@StonedEdge said in Pico OLED - display game images, metadata and system info on a 1.5" OLED screen project:
@mitu Out of interest, what is the u" doing before .// in game_list declaration?
Makes sure the string is interpreted as Unicode:
https://docs.python.org/2/tutorial/introduction.html#unicode-strings
-
Ah, makes sense. Thanks for your help!
We’ve got most of it working now. Just need to add a few tweaks to the code to write the metadata to the screen and make the info displayed selectable by the user. Thanks!
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.