Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game Boy, Game Boy Color, and Game Boy Advance all showing as "Game Boy" #13

Open
XyberDAWG opened this issue Jan 17, 2021 · 2 comments

Comments

@XyberDAWG
Copy link

Shouldnt these three all show up as Integrations with different names, and different Lists in GOG?

@jshackles
Copy link
Owner

This is (currently) by design. Unfortunately, GOG Galaxy does not have platform integration IDs for either Game Boy Color, or Game Boy Advance. Just "Nintendo Game Boy".

https://galaxy-integrations-python-api.readthedocs.io/en/latest/platforms.html

There are currently efforts underway to include these platforms in the GOG Galaxy API

gogcom/galaxy-integrations-python-api#160

@manuth
Copy link

manuth commented Apr 18, 2021

@XyberDAWG I tinkered with the original RetroGOG plugin.py-file to include GB, GBC and GBA games all at once.
Sadly games which can't be found on GOGs gamesdb act quite weird (they show up as "Unknown Game") but apart from that, this workaround should work for you.

Just replace the content your RetroGOG "Nintendo Game Boy"'s plugin.py with following:

plugin.py
import asyncio
import subprocess
import sys
import json, urllib.request, os, os.path
import user_config
import datetime
import logging
import time
from collections import namedtuple
from typing import Any, Callable, Dict, List, NewType, Optional
from galaxy.api.consts import LicenseType, LocalGameState, Platform
from galaxy.api.plugin import Plugin, create_and_run_plugin
from galaxy.api.types import Achievement, Authentication, Game, LicenseInfo, LocalGame, GameTime

from version import __version__ as version

class Retroarch(Plugin):

    def __init__(self, reader, writer, token):
        super().__init__(Platform.NintendoGameBoy, version, reader, writer, token)
        self.internal_game_list: dict[str, tuple[str, Game]] = {}
        self.internal_game_list = None

        self.playlist_paths = {
            "gb": user_config.emu_path + "playlists/Nintendo - Game Boy.lpl",
            "gbc": user_config.emu_path + "playlists/Nintendo - Game Boy Color.lpl",
            "gba": user_config.emu_path + "playlists/Nintendo - Game Boy Advance.lpl"
        }

        self.proc = None
        self.game_run = ""

    @property
    def game_list(self):
        if self.internal_game_list is None:
            self.internal_game_list = {}
            self.update_game_list()
        return self.internal_game_list

    async def authenticate(self, stored_credentials = None):
        creds = {}
        creds["user"] = "RAUser"
        self.store_credentials(creds)
        return Authentication("RAUser", "Retroarch")

    async def pass_login_credentials(self, step, credentials, cookies):
        creds = {}
        creds["user"] = "RAUser"
        self.store_credentials(creds)
        return Authentication("RAUser", "Retroarch")

    async def get_owned_games(self):
        game_list = []
        if self.game_list is not None:
            self.update_game_list()

        for game_id in self.game_list:
            game_list.append(self.game_list[game_id][1])
        return game_list

    # Format helper for game names
    def format_game(self, game):
        game_return = game.rsplit(" (")[0]
        game_return = game_return.replace("'","")
        return game_return

    #Scans retroarch playlist for roms in rom_path and adds them to self.game_cache
    #as roms don't need to be installed, owned games and local games are the same and both run update_game_cache
    def update_game_list(self):
        game_list: dict[str, tuple[str, Game]] = {}

        for console_id in self.playlist_paths:
            playlist_path = self.playlist_paths[console_id]
            if os.path.isfile(playlist_path):
                with open(playlist_path) as playlist_data:
                    playlist = json.load(playlist_data)
                for game in playlist["items"]:
                    game_path = game["path"].split("#")[0]
                    if os.path.isfile(game_path):
                        game_title = self.format_game(game["label"])
                        game_id = game_title + " " + console_id.upper()
                        game_list[game_id] = (
                            game_path,
                            Game(
                                game_id,
                                game_title,
                                None,
                                LicenseInfo(LicenseType.SinglePurchase, None)))

        #adds games when added while running
        for game_id in game_list:
            if game_id not in self.game_list:
                self.game_list[game_id] = game_list[game_id]
                self.add_game(game_list[game_id][1])

        #removes games when removed while running
        for game_id in self.game_list:
            if game_id not in game_list:
                self.game_list.pop(game_id)
                self.remove_game(game_id)

    async def get_local_games(self):
        local_game_list = []
        for game_id in self.game_list:
            local_game_list.append(LocalGame(self.game_list[game_id][1].game_id, 1))
        return local_game_list

    # Only as placeholders so the launch game feature is recognized
    async def install_game(self, game_id):
        pass

    async def uninstall_game(self, game_id):
        pass

    def shutdown(self):
        pass

    #potentially give user more customization possibilities like starting in fullscreen etc
    async def launch_game(self, game_id: str):
        if game_id in self.game_list:
            game_entry = self.game_list[game_id]
            self.update_local_game_status(LocalGame(game_id, 2))
            self.game_run = game_id
            self.proc = subprocess.Popen(
                os.path.abspath(os.path.join(user_config.emu_path, "retroarch.exe")) +
                " -L \"" + os.path.abspath(os.path.join(user_config.emu_path, "cores", user_config.core) + "\" " +
                "\"" + os.path.abspath(game_entry[0]) + "\""))

    #imports retroarch playtime if existent. For this to work, activate "Save runtime log (aggregate)" in RetroArch settings -> Savings
    async def get_game_time(self, game_id: str, context: any):
        file_path = ""
        time = 0
        last_played = None

        game_entry = self.game_list[game_id]
        log_path = os.path.join(user_config.emu_path, "playlists", "logs", os.path.basename(game_entry[0]).rsplit("#")[0].rsplit(".", 1)[0] + ".lrtl")
        if os.path.isfile(log_path):
            with open(log_path) as log_data:
                log = json.load(log_data)
            last_played = datetime.datetime.timestamp(datetime.datetime.strptime(log["last_played"], '%Y-%m-%d %H:%M:%S'))
            runtime_span = datetime.datetime.strptime(log["runtime"], '%H:%M:%S')
            runtime = runtime_span.hour * 60 + runtime_span.minute
        return GameTime(game_id, time, last_played)

    #checks if game is (still) running, adjusts game_cache and game_time
    def tick(self):
        try:
            if self.proc.poll() is not None:
                self.update_local_game_status(LocalGame(self.game_run, 1))
                self.update_game_time(self.get_game_time(self.game_run, None))
                self.proc = None
        except AttributeError:
            pass

        self.update_game_list()
        self.get_local_games()

def main():
    create_and_run_plugin(Retroarch, sys.argv)

if __name__ == "__main__":
    main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants