RetroPie forum home
    • Recent
    • Tags
    • Popular
    • Home
    • Docs
    • Register
    • Login
    Please do not post a support request without first reading and following the advice in https://retropie.org.uk/forum/topic/3/read-this-first

    Pico OLED - display game images, metadata and system info on a 1.5" OLED screen project

    Scheduled Pinned Locked Moved Help and Support
    gamelist.xml
    5 Posts 2 Posters 514 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • StonedEdgeS
      StonedEdge
      last edited by StonedEdge

      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

      Check out my build logs below:
      Wii SPii (Wii in a GBA SP Shell)
      https://bitbuilt.net/forums/index.php?threads/the-wii-spii.3347/
      Retro Lite CM4
      https://bitbuilt.net/forums/index.php?threads/retro-lite-cm4-a-stonededge-and-dmcke5-collaboration.3934/

      1 Reply Last reply Reply Quote 0
      • mituM
        mitu Global Moderator
        last edited by

        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)
        
        
        StonedEdgeS 1 Reply Last reply Reply Quote 1
        • StonedEdgeS
          StonedEdge @mitu
          last edited by StonedEdge

          @mitu

          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)
          

          Check out my build logs below:
          Wii SPii (Wii in a GBA SP Shell)
          https://bitbuilt.net/forums/index.php?threads/the-wii-spii.3347/
          Retro Lite CM4
          https://bitbuilt.net/forums/index.php?threads/retro-lite-cm4-a-stonededge-and-dmcke5-collaboration.3934/

          1 Reply Last reply Reply Quote 0
          • mituM
            mitu Global Moderator
            last edited by

            @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

            StonedEdgeS 1 Reply Last reply Reply Quote 0
            • StonedEdgeS
              StonedEdge @mitu
              last edited by

              @mitu

              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!

              alt text
              alt text

              Check out my build logs below:
              Wii SPii (Wii in a GBA SP Shell)
              https://bitbuilt.net/forums/index.php?threads/the-wii-spii.3347/
              Retro Lite CM4
              https://bitbuilt.net/forums/index.php?threads/retro-lite-cm4-a-stonededge-and-dmcke5-collaboration.3934/

              1 Reply Last reply Reply Quote 0
              • First post
                Last post

              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.