Beispiel #1
0
    def write_json(self, params):
        """Write data into a json file."""
        self._check_required_params(["file", "data"], params, "write_json")

        # Get file
        filename = self._get_file(params["file"])

        # Create dir if necessary
        basedir = os.path.dirname(filename)
        if not os.path.exists(basedir):
            os.makedirs(basedir)

        merge = params.get("merge", True)

        if not os.path.exists(filename):
            # create an empty file
            with open(filename, "a+"):
                pass

        with open(filename, "r+" if merge else "w") as json_file:
            json_data = {}
            if merge:
                try:
                    json_data = json.load(json_file)
                except ValueError:
                    logger.error("Failed to parse JSON from file %s", filename)

            json_data = selective_merge(json_data, params.get("data", {}))
            json_file.seek(0)
            json_file.write(json.dumps(json_data, indent=2))
Beispiel #2
0
    def parse(self):
        """Converts the glxinfo output to class attributes"""
        if not self._output:
            logger.error("No available glxinfo output")
            return
        # Fix glxinfo output (Great, you saved one line by
        # combining display and screen)
        output = self._output.replace("  screen", "\nscreen")
        for line in output.split("\n"):
            if not line.strip():
                continue

            key, value = line.split(":", 1)
            key = key.replace(" string", "").replace(" ", "_")
            value = value.strip()

            if not value and key.startswith(("Extended_renderer_info", "Memory_info")):
                self._section = key[key.index("(") + 1:-1]
                setattr(self, self._section, Container())
                continue
            if self._section:
                if not key.startswith("____"):
                    self._section = None
                else:
                    setattr(getattr(self, self._section), key.strip("_").lower(), value)
                    continue
            self._attrs.add(key.lower())
            setattr(self, key.lower(), value)
Beispiel #3
0
    def prelaunch(self):
        super(winesteam, self).prelaunch()

        def check_shutdown(is_running, times=10):
            for x in range(1, times):
                time.sleep(1)
                if not is_running():
                    return True
        # Stop Wine Steam to prevent Wine prefix/version problems
        if is_running():
            logger.info("Waiting for Steam to shutdown...")
            self.shutdown()
            if not check_shutdown(is_running):
                logger.info("Wine Steam does not shut down, killing it...")
                kill()
                if not check_shutdown(is_running, 5):
                    logger.error("Failed to shut down Wine Steam :(")
                    return False
        # Stop Linux Steam
        from lutris.runners import steam
        if steam.is_running():
            logger.info("Waiting for Steam shutdown...")
            steam.shutdown()
            if not check_shutdown(steam.is_running):
                logger.info("Steam does not shut down, killing it...")
                steam.kill()
                if not check_shutdown(steam.is_running, 5):
                    logger.error("Failed to shut down Steam :(")
                    return False
        return True
Beispiel #4
0
def get_games(game_slugs=None, page=1):
    url = settings.SITE_URL + "/api/games"

    if int(page) > 1:
        url += "?page={}".format(page)

    response = http.Request(url, headers={'Content-Type': 'application/json'})
    if game_slugs:
        payload = json.dumps({'games': game_slugs, 'page': page}).encode('utf-8')
    else:
        payload = None
    response.get(data=payload)
    response_data = response.json

    if not response_data:
        logger.warning('Unable to get games from API')
        return None

    results = response_data.get('results', [])
    while response_data.get('next'):
        page_match = re.search(r'page=(\d+)', response_data['next'])
        if page_match:
            page = page_match.group(1)
        else:
            logger.error("No page found in %s", response_data['next'])
            break
        page_result = get_games(game_slugs=game_slugs, page=page)
        if not page_result:
            logger.warning("Unable to get response for page %s", page)
            break
        else:
            results += page_result

    return results
Beispiel #5
0
    def __init__(self, game_slug, file_id, file_meta):
        self.game_slug = game_slug
        self.id = file_id  # pylint: disable=invalid-name
        self.dest_file = None
        if isinstance(file_meta, dict):
            for field in ("url", "filename"):
                if field not in file_meta:
                    raise ScriptingError(
                        "missing field `%s` for file `%s`" % (field, file_id)
                    )
            self.url = file_meta["url"]
            self.filename = file_meta["filename"]
            self.referer = file_meta.get("referer")
            self.checksum = file_meta.get("checksum")
        else:
            self.url = file_meta
            self.filename = os.path.basename(file_meta)
            self.referer = None
            self.checksum = None

        if self.url.startswith(("$STEAM", "$WINESTEAM")):
            self.filename = self.url

        if self.url.startswith("/"):
            self.url = "file://" + self.url

        if not self.filename:
            logger.error("Couldn't find a filename for file %s in %s", file_id, file_meta)
            raise ScriptingError(
                "No filename provided for %s, please provide 'url' "
                "and 'filename' parameters in the script" % file_id
            )
        if self.uses_pga_cache(create=True):
            logger.debug("Using cache path %s", self.cache_path)
Beispiel #6
0
def get_outputs():
    """Return list of tuples containing output name and geometry."""
    outputs = []
    vid_modes = get_vidmodes()
    if not vid_modes:
        logger.error("xrandr didn't return anything")
        return []
    for line in vid_modes:
        parts = line.split()
        if len(parts) < 2:
            continue
        if parts[1] == 'connected':
            if len(parts) == 2:
                continue
            if parts[2] != 'primary':
                geom = parts[2]
                rotate = parts[3]
            else:
                geom = parts[3]
                rotate = parts[4]
            if geom.startswith('('):  # Screen turned off, no geometry
                continue
            if rotate.startswith('('):  # Screen not rotated, no need to include
                outputs.append((parts[0], geom, "normal"))
            else:
                if rotate in ("left", "right"):
                    geom_parts = geom.split('+')
                    x_y = geom_parts[0].split('x')
                    geom = "{}x{}+{}+{}".format(x_y[1], x_y[0], geom_parts[1], geom_parts[2])
                outputs.append((parts[0], geom, rotate))
    return outputs
Beispiel #7
0
def create_prefix(prefix, wine_path=None, arch='win32'):
    """Create a new Wine prefix."""
    logger.debug("Creating a %s prefix in %s", arch, prefix)

    # Avoid issue of 64bit Wine refusing to create win32 prefix
    # over an existing empty folder.
    if os.path.isdir(prefix) and not os.listdir(prefix):
        os.rmdir(prefix)

    if not wine_path:
        wine_path = wine().get_executable()

    wineboot_path = os.path.join(os.path.dirname(wine_path), 'wineboot')

    env = {
        'WINEARCH': arch,
        'WINEPREFIX': prefix
    }
    system.execute([wineboot_path], env=env)
    for i in range(20):
        time.sleep(.25)
        if os.path.exists(os.path.join(prefix, 'user.reg')):
            break
    if not os.path.exists(os.path.join(prefix, 'user.reg')):
        logger.error('No user.reg found after prefix creation. '
                     'Prefix might not be valid')
    logger.info('%s Prefix created in %s', arch, prefix)
    prefix_manager = WinePrefixManager(prefix)
    prefix_manager.setup_defaults()
Beispiel #8
0
def kill_pid(pid):
    try:
        int(pid)
    except ValueError:
        logger.error("Invalid pid %s")
        return
    execute(["kill", "-9", pid])
Beispiel #9
0
def execute(command, env=None, cwd=None, log_errors=False):
    """Execute a system command and return its results."""
    existing_env = os.environ.copy()
    if env:
        existing_env.update(env)
        logger.debug(' '.join('{}={}'.format(k, v) for k, v in env.iteritems()))
    logger.debug("Executing %s", ' '.join(command))

    # Piping stderr can cause slowness in the programs, use carefully
    # (especially when using regedit with wine)
    if log_errors:
        stderr_config = subprocess.PIPE
    else:
        stderr_config = None
    try:
        stdout, stderr = subprocess.Popen(command,
                                          shell=False,
                                          stdout=subprocess.PIPE,
                                          stderr=stderr_config,
                                          env=existing_env, cwd=cwd).communicate()
    except OSError as ex:
        logger.error('Could not run command %s: %s', command, ex)
        return
    if stderr and log_errors:
        logger.error(stderr)
    return stdout.strip()
Beispiel #10
0
def search_games(query):
    if not query:
        return []
    query = query.lower().strip()[:32]
    if query == "open source games":
        url = "/api/bundles/open-source"
    elif query == "free to play games":
        url = "/api/bundles/free-to-play"
    else:
        url = "/api/games?%s" % urllib.parse.urlencode({"search": query})
    response = http.Request(settings.SITE_URL + url, headers={"Content-Type": "application/json"})
    try:
        response.get()
    except http.HTTPError as ex:
        logger.error("Unable to get games from API: %s", ex)
        return None
    response_data = response.json
    if "bundles" in url:
        api_games = response_data.get("games", [])
    else:
        api_games = response_data.get("results", [])
    for index, game in enumerate(api_games, 1):
        game["id"] = index * -1
        game["installed"] = 1
        game["runner"] = None
        game["platform"] = None
        game["lastplayed"] = None
        game["installed_at"] = None
        game["playtime"] = None
    return api_games
Beispiel #11
0
def get_api_games(game_slugs=None, page="1", query_type="games", inject_aliases=False):
    """Return all games from the Lutris API matching the given game slugs"""
    response_data = get_game_api_page(game_slugs, page=page, query_type=query_type)
    if not response_data:
        return []
    results = response_data.get("results", [])
    while response_data.get("next"):
        page_match = re.search(r"page=(\d+)", response_data["next"])
        if page_match:
            next_page = page_match.group(1)
        else:
            logger.error("No page found in %s", response_data["next"])
            break
        response_data = get_game_api_page(game_slugs, page=next_page, query_type=query_type)
        if not response_data.get("results"):
            logger.warning("Unable to get response for page %s", next_page)
            break
        else:
            results += response_data.get("results")
    if game_slugs and inject_aliases:
        matched_games = []
        for game in results:
            for alias_slug in [alias["slug"] for alias in game.get("aliases", [])]:
                if alias_slug in game_slugs:
                    matched_games.append((alias_slug, game))
        for alias_slug, game in matched_games:
            game["slug"] = alias_slug
            results.append(game)
    return results
Beispiel #12
0
def get_overrides_env(overrides):
    """
    Output a string of dll overrides usable with WINEDLLOVERRIDES
    See: https://wiki.winehq.org/Wine_User%27s_Guide#WINEDLLOVERRIDES.3DDLL_Overrides
    """
    if not overrides:
        return ""
    override_buckets = OrderedDict(
        [("n,b", []), ("b,n", []), ("b", []), ("n", []), ("d", []), ("", [])]
    )
    for dll, value in overrides.items():
        if not value:
            value = ""
        value = value.replace(" ", "")
        value = value.replace("builtin", "b")
        value = value.replace("native", "n")
        value = value.replace("disabled", "")
        try:
            override_buckets[value].append(dll)
        except KeyError:
            logger.error("Invalid override value %s", value)
            continue

    override_strings = []
    for value, dlls in override_buckets.items():
        if not dlls:
            continue
        override_strings.append("{}={}".format(",".join(sorted(dlls)), value))
    return ";".join(override_strings)
Beispiel #13
0
    def on_done(self, _result, error):
        if error:
            logger.error("Download failed: %s", error)
            self.state = self.ERROR
            self.error = error
            if self.file_pointer:
                self.file_pointer.close()
                self.file_pointer = None
            return

        if self.state == self.CANCELLED:
            return

        logger.debug("Finished downloading %s", self.url)
        if not self.downloaded_size:
            logger.warning("Downloaded file is empty")

        if not self.full_size:
            self.progress_fraction = 1.0
            self.progress_percentage = 100
        self.state = self.COMPLETED
        self.file_pointer.close()
        self.file_pointer = None
        if self.callback:
            self.callback()
Beispiel #14
0
 def init_versions(manager):
     try:
         manager.DXVK_VERSIONS \
             = get_dxvk_versions(manager.base_name, manager.DXVK_TAGS_URL)
     except Exception as ex:  # pylint: disable= broad-except
         logger.error(ex)
     manager.DXVK_LATEST, manager.DXVK_PAST_RELEASES = manager.DXVK_VERSIONS[0], manager.DXVK_VERSIONS[1:9]
Beispiel #15
0
 def enable(self):
     """Enable DXVK for the current prefix"""
     if not system.path_exists(self.dxvk_path):
         logger.error(self.base_name.upper()+" %s is not available locally", self.version)
         return
     for system_dir, dxvk_arch, dll in self._iter_dxvk_dlls():
         self.enable_dxvk_dll(system_dir, dxvk_arch, dll)
Beispiel #16
0
def execute(command, env=None, cwd=None, log_errors=False, quiet=False):
    """Execute a system command and return its results."""
    existing_env = os.environ.copy()
    if env:
        existing_env.update(env)
        logger.debug(' '.join('{}={}'.format(k, v) for k, v in env.items()))
    if not quiet:
        logger.debug("Executing %s", ' '.join(command))

    # Piping stderr can cause slowness in the programs, use carefully
    # (especially when using regedit with wine)
    if log_errors:
        stderr_handler = subprocess.PIPE
        stderr_needs_closing = False
    else:
        stderr_handler = open(os.devnull, 'w')
        stderr_needs_closing = True
    try:
        stdout, stderr = subprocess.Popen(command,
                                          shell=False,
                                          stdout=subprocess.PIPE,
                                          stderr=stderr_handler,
                                          env=existing_env, cwd=cwd).communicate()
    except OSError as ex:
        logger.error('Could not run command %s: %s', command, ex)
        return
    finally:
        if stderr_needs_closing:
            stderr_handler.close()
    if stderr and log_errors:
        logger.error(stderr)
    return stdout.decode(errors='replace').strip()
Beispiel #17
0
 def get_gog_download_links(self):
     """Return a list of downloadable links for a GOG game"""
     gog_service = GogService()
     if not gog_service.is_available():
         logger.info("You are not connected to GOG")
         connect_gog()
     if not gog_service.is_available():
         raise UnavailableGame
     gog_installers = self.get_gog_installers(gog_service)
     if len(gog_installers) > 1:
         raise ScriptingError("Don't know how to deal with multiple installers yet.")
     try:
         installer = gog_installers[0]
     except IndexError:
         raise UnavailableGame
     download_links = []
     for game_file in installer.get('files', []):
         downlink = game_file.get("downlink")
         if not downlink:
             logger.error("No download information for %s", installer)
             continue
         download_info = gog_service.get_download_info(downlink)
         for field in ('checksum', 'downlink'):
             url = download_info[field]
             logger.info("Adding %s to download links", url)
             download_links.append(download_info[field])
     return download_links
Beispiel #18
0
def kill_pid(pid):
    try:
        int(pid)
    except ValueError:
        logger.error("Invalid pid %s")
        return
    execute(['kill', '-9', pid])
Beispiel #19
0
    def setup_x360ce(self, x360ce_path):
        if not os.path.isdir(x360ce_path):
            logger.error("%s is not a valid path for x360ce", x360ce_path)
            return
        mode = 'dumbxinputemu' if self.runner_config.get('dumbxinputemu') else 'x360ce'
        dll_files = ['xinput1_3.dll']
        if self.runner_config.get('x360ce-xinput9'):
            dll_files.append('xinput9_1_0.dll')

        for dll_file in dll_files:
            xinput_dest_path = os.path.join(x360ce_path, dll_file)
            xinput_arch = self.runner_config.get('xinput-arch') or self.wine_arch
            dll_path = os.path.join(datapath.get(), 'controllers/{}-{}'.format(mode, xinput_arch))
            if not os.path.exists(xinput_dest_path):
                source_file = dll_file if mode == 'dumbxinputemu' else 'xinput1_3.dll'
                shutil.copyfile(os.path.join(dll_path, source_file), xinput_dest_path)

        if mode == 'x360ce':
            if self.runner_config.get('x360ce-dinput'):
                dinput8_path = os.path.join(dll_path, 'dinput8.dll')
                dinput8_dest_path = os.path.join(x360ce_path, 'dinput8.dll')
                shutil.copyfile(dinput8_path, dinput8_dest_path)

            x360ce_config = X360ce()
            x360ce_config.populate_controllers()
            x360ce_config.write(os.path.join(x360ce_path, 'x360ce.ini'))
Beispiel #20
0
def get_pixbuf_for_game(game_slug, icon_type, is_installed=True):
    if icon_type.startswith("banner"):
        default_icon_path = os.path.join(datapath.get(), "media/default_banner.png")
        icon_path = resources.get_banner_path(game_slug)
    elif icon_type.startswith("icon"):
        default_icon_path = os.path.join(datapath.get(), "media/default_icon.png")
        icon_path = resources.get_icon_path(game_slug)
    else:
        logger.error("Invalid icon type '%s'", icon_type)
        return None

    size = IMAGE_SIZES[icon_type]

    pixbuf = get_pixbuf(icon_path, size, fallback=default_icon_path)
    if not is_installed:
        unavailable_game_overlay = os.path.join(datapath.get(), "media/unavailable.png")
        transparent_pixbuf = get_overlay(unavailable_game_overlay, size).copy()
        pixbuf.composite(
            transparent_pixbuf,
            0,
            0,
            size[0],
            size[1],
            0,
            0,
            1,
            1,
            GdkPixbuf.InterpType.NEAREST,
            100,
        )
        return transparent_pixbuf
    return pixbuf
Beispiel #21
0
def sync_with_lutris():
    desktop_games = {
        game['slug']: game
        for game in pga.get_games_where(runner='linux',
                                        installer_slug=INSTALLER_SLUG,
                                        installed=1)
    }
    seen = set()

    for name, appid, exe, args in get_games():
        slug = slugify(name) or slugify(appid)
        if not all([name, slug, appid]):
            logger.error("Failed to load desktop game \"{}\" (app: {}, slug: {})".format(name, appid, slug))
            continue
        else:
            logger.info("Found desktop game \"{}\" (app: {}, slug: {})".format(name, appid, slug))
        seen.add(slug)

        if slug not in desktop_games.keys():
            game_info = {
                'name': name,
                'slug': slug,
                'config_path': slug + '-' + INSTALLER_SLUG,
                'installer_slug': INSTALLER_SLUG,
                'exe': exe,
                'args': args
            }
            mark_as_installed(appid, 'linux', game_info)

    for slug in set(desktop_games.keys()).difference(seen):
        mark_as_uninstalled(desktop_games[slug])
Beispiel #22
0
    def get_runner_version(self, version=None):
        logger.info(
            "Getting runner information for %s%s",
            self.name,
            "(version: %s)" % version if version else "",
        )
        runner_info = self.get_runner_info()
        if not runner_info:
            logger.error("Failed to get runner information")
            return

        versions = runner_info.get("versions") or []
        arch = self.arch
        if version:
            if version.endswith("-i386") or version.endswith("-x86_64"):
                version, arch = version.rsplit("-", 1)
            versions = [v for v in versions if v["version"] == version]
        versions_for_arch = [v for v in versions if v["architecture"] == arch]
        if len(versions_for_arch) == 1:
            return versions_for_arch[0]

        if len(versions_for_arch) > 1:
            default_version = [v for v in versions_for_arch if v["default"] is True]
            if default_version:
                return default_version[0]
        elif len(versions) == 1 and system.LINUX_SYSTEM.is_64_bit:
            return versions[0]
        elif len(versions) > 1 and system.LINUX_SYSTEM.is_64_bit:
            default_version = [v for v in versions if v["default"] is True]
            if default_version:
                return default_version[0]
        # If we didn't find a proper version yet, return the first available.
        if len(versions_for_arch) >= 1:
            return versions_for_arch[0]
Beispiel #23
0
def fix_path_case(path):
    """Do a case insensitive check, return the real path with correct case."""
    if os.path.exists(path):
        return path
    parts = path.strip('/').split('/')
    current_path = "/"
    for part in parts:
        if not os.path.exists(current_path):
            return
        tested_path = os.path.join(current_path, part)
        if os.path.exists(tested_path):
            current_path = tested_path
            continue
        try:
            path_contents = os.listdir(current_path)
        except OSError:
            logger.error("Can't read contents of %s", current_path)
            path_contents = []
        for filename in path_contents:
            if filename.lower() == part.lower():
                current_path = os.path.join(current_path, filename)
                continue

    # Only return the path if we got the same number of elements
    if len(parts) == len(current_path.strip('/').split('/')):
        return current_path
Beispiel #24
0
def check_libs(all_components=False):
    """Checks that required libraries are installed on the system"""
    missing_libs = LINUX_SYSTEM.get_missing_libs()
    if all_components:
        components = LINUX_SYSTEM.requirements
    else:
        components = LINUX_SYSTEM.critical_requirements
    missing_vulkan_libs = []
    for req in components:
        for index, arch in enumerate(LINUX_SYSTEM.runtime_architectures):
            for lib in missing_libs[req][index]:
                if req == "VULKAN":
                    missing_vulkan_libs.append(arch)
                logger.error("%s %s missing (needed by %s)", arch, lib, req.lower())

    if missing_vulkan_libs:
        setting = "dismiss-missing-vulkan-library-warning"
        if settings.read_setting(setting) != "True":
            DontShowAgainDialog(
                setting,
                "Missing vulkan libraries",
                secondary_message="The Vulkan library for %s has not been found. "
                "This will prevent games using Vulkan (such as DXVK games) from running. "
                "To install it, please follow "
                "<a href='https://github.com/lutris/lutris/wiki/Installing-drivers'>"
                "the instructions on our Wiki</a>"
                % " and ".join(missing_vulkan_libs)
            )
Beispiel #25
0
 def get_glxinfo_output():
     """Return the glxinfo -B output"""
     try:
         return subprocess.check_output(["glxinfo", "-B"]).decode()
     except subprocess.CalledProcessError as ex:
         logger.error("glxinfo call failed: %s", ex)
         return ""
Beispiel #26
0
def get_overrides_env(overrides):
    """
    Output a string of dll overrides usable with WINEDLLOVERRIDES
    See: https://wiki.winehq.org/Wine_User%27s_Guide#WINEDLLOVERRIDES.3DDLL_Overrides
    """
    if not overrides:
        return ''
    override_buckets = OrderedDict([
        ('n,b', []),
        ('b,n', []),
        ('b', []),
        ('n', []),
        ('', [])
    ])
    for dll, value in overrides.items():
        if not value:
            value = ''
        value = value.replace(' ', '')
        value = value.replace('builtin', 'b')
        value = value.replace('native', 'n')
        value = value.replace('disabled', '')
        try:
            override_buckets[value].append(dll)
        except KeyError:
            logger.error('Invalid override value %s', value)
            continue

    override_strings = []
    for value, dlls in override_buckets.items():
        if not dlls:
            continue
        override_strings.append("{}={}".format(','.join(sorted(dlls)), value))
    return ';'.join(override_strings)
Beispiel #27
0
def vdf_parse(steam_config_file, config):
    """Parse a Steam config file and return the contents as a dict."""
    line = " "
    while line:
        try:
            line = steam_config_file.readline()
        except UnicodeDecodeError:
            logger.error("Error while reading Steam VDF file %s. Returning %s", steam_config_file, config)
            return config
        if not line or line.strip() == "}":
            return config
        while not line.strip().endswith("\""):
            nextline = steam_config_file.readline()
            if not nextline:
                break
            line = line[:-1] + nextline

        line_elements = line.strip().split("\"")
        if len(line_elements) == 3:
            key = line_elements[1]
            steam_config_file.readline()  # skip '{'
            config[key] = vdf_parse(steam_config_file, {})
        else:
            try:
                config[line_elements[1]] = line_elements[3]
            except IndexError:
                logger.error("Malformed config file: %s", line)
    return config
Beispiel #28
0
def get_game_api_page(game_ids, page="1", query_type="games"):
    """Read a single page of games from the API and return the response

    Args:
        game_ids (list): list of game IDs, the ID type is determined by `query_type`
        page (str): Page of results to get
        query_type (str): Type of the IDs in game_ids, by default 'games' queries
                          games by their Lutris slug. 'gogid' can also be used.
    """
    url = settings.SITE_URL + "/api/games"

    if int(page) > 1:
        url += "?page={}".format(page)

    response = http.Request(url, headers={"Content-Type": "application/json"})
    if game_ids:
        payload = json.dumps({query_type: game_ids, "page": page}).encode("utf-8")
    else:
        raise ValueError("No game id provided will fetch all games from the API")
    try:
        response.get(data=payload)
    except http.HTTPError as ex:
        logger.error("Unable to get games from API: %s", ex)
        return None
    response_data = response.json
    num_games = len(response_data.get("results"))
    if num_games:
        logger.debug("Loaded %s games from page %s", num_games, page)
    else:
        logger.debug("No game found for %s", ', '.join(game_ids))

    if not response_data:
        logger.warning("Unable to get games from API, status code: %s", response.status_code)
        return None
    return response_data
Beispiel #29
0
    def play(self):
        rompath = self.runner_config.get("rompath") or ""
        if not system.path_exists(rompath):
            logger.warning("BIOS path provided in %s doesn't exist", rompath)
            rompath = os.path.join(settings.RUNNER_DIR, "mess/bios")
        if not system.path_exists(rompath):
            logger.error("Couldn't find %s", rompath)
            return {"error": "NO_BIOS"}
        machine = self.game_config.get("machine")
        if not machine:
            return {"error": "INCOMPLETE_CONFIG"}
        rom = self.game_config.get("main_file") or ""
        if rom and not system.path_exists(rom):
            return {"error": "FILE_NOT_FOUND", "file": rom}
        device = self.game_config.get("device")
        command = [self.get_executable()]
        if self.runner_config.get("uimodekey"):
            command += ["-uimodekey", self.runner["uimodekey"]]

        command += ["-rompath", rompath, machine]
        if device:
            command.append("-" + device)
        if rom:
            command.append(rom)
        return {"command": command}
Beispiel #30
0
    def play(self):
        game_exe = self.game_exe
        arguments = self.game_config.get('args') or ''

        if not os.path.exists(game_exe):
            return {'error': 'FILE_NOT_FOUND', 'file': game_exe}

        launch_info = {}
        launch_info['env'] = self.get_env(full=False)

        if self.runner_config.get('xinput'):
            xinput_path = self.get_xinput_path()
            if xinput_path:
                logger.debug('Preloading %s', xinput_path)
                launch_info['ld_preload'] = self.get_xinput_path()
            else:
                logger.error('Missing koku-xinput-wine.so, Xinput won\'t be enabled')

        command = [self.get_executable()]
        if game_exe.endswith(".msi"):
            command.append('msiexec')
            command.append('/i')
        if game_exe.endswith('.lnk'):
            command.append('start')
            command.append('/unix')
        command.append(game_exe)

        if arguments:
            for arg in shlex.split(arguments):
                command.append(arg)
        launch_info['command'] = command
        return launch_info
Beispiel #31
0
 def load(self):
     """Load the user game library from the GOG API"""
     if self.is_loading:
         logger.warning("GOG games are already loading")
         return
     if not self.is_connected():
         logger.error("User not connected to GOG")
         return
     self.is_loading = True
     self.emit("service-games-load")
     games = [
         GOGGame.new_from_gog_game(game) for game in self.get_library()
     ]
     for game in games:
         game.save()
     self.match_games()
     self.is_loading = False
     self.emit("service-games-loaded")
     return games
Beispiel #32
0
 def enable_dxvk_dll(self, system_dir, dxvk_arch, dll):
     """Copies DXVK dlls to the appropriate destination"""
     # Copying DXVK's version
     dxvk_dll_path = os.path.join(self.dxvk_path, dxvk_arch, "%s.dll" % dll)
     if system.path_exists(dxvk_dll_path):
         wine_dll_path = os.path.join(system_dir, "%s.dll" % dll)
         logger.debug("Replacing %s/%s with DXVK version", system_dir, dll)
         if system.path_exists(wine_dll_path):
             if not self.is_dxvk_dll(wine_dll_path) and not os.path.islink(wine_dll_path):
                 # Backing up original version (may not be needed)
                 shutil.move(wine_dll_path, wine_dll_path + ".orig")
             else:
                 os.remove(wine_dll_path)
         try:
             os.symlink(dxvk_dll_path, wine_dll_path)
         except OSError:
             logger.error("Failed linking %s to %s", dxvk_dll_path, wine_dll_path)
     else:
         self.disable_dxvk_dll(system_dir, dxvk_arch, dll)
Beispiel #33
0
 def load(self):
     """Load the list of games"""
     if self.is_loading:
         logger.warning("EGS games are already loading")
         return
     self.is_loading = True
     try:
         library = self.get_library()
     except Exception as ex:  # pylint=disable:broad-except
         self.is_loading = False
         logger.error("Failed to load EGS library: %s", ex)
         return
     egs_games = []
     for game in library:
         egs_game = EGSGame.new_from_api(game)
         egs_game.save()
         egs_games.append(egs_game)
     self.is_loading = False
     return egs_games
Beispiel #34
0
 def prelaunch(self):
     def check_shutdown(is_running, times=10):
         for _ in range(1, times):
             time.sleep(1)
             if not is_running():
                 return True
     # If using primusrun, shutdown existing Steam first
     optimus = self.system_config.get('optimus')
     if optimus != 'off':
         if is_running():
             logger.info("Waiting for Steam shutdown...")
             shutdown()
             if not check_shutdown(is_running):
                 logger.info("Steam does not shut down, killing it...")
                 kill()
                 if not check_shutdown(is_running, 5):
                     logger.error("Failed to shut down Steam :(")
                     return False
     return True
Beispiel #35
0
    def download(self):
        """Download DXVK to the local cache"""
        if self.is_available():
            logger.warning("DXVK already available at %s", self.dxvk_path)

        dxvk_url = self.get_dxvk_download_url()
        if not dxvk_url:
            logger.warning("Could not find a release for DXVK %s",
                           self.version)
            return
        dxvk_archive_path = os.path.join(self.base_dir,
                                         os.path.basename(dxvk_url))
        download_file(dxvk_url, dxvk_archive_path, overwrite=True)
        if not system.path_exists(dxvk_archive_path) or not os.stat(
                dxvk_archive_path).st_size:
            logger.error("Failed to download DXVK %s", self.version)
            return
        extract_archive(dxvk_archive_path, self.dxvk_path, merge_single=True)
        os.remove(dxvk_archive_path)
Beispiel #36
0
    def get_unix_path(self, windows_path):
        windows_path = windows_path.replace("\\\\", "/")
        if not self.prefix_path:
            return
        drives_path = os.path.join(self.prefix_path, "dosdevices")
        if not system.path_exists(drives_path):
            return
        letter, relpath = windows_path.split(":", 1)
        relpath = relpath.strip("/")
        drive_link = os.path.join(drives_path, letter.lower() + ":")
        try:
            drive_path = os.readlink(drive_link)
        except FileNotFoundError:
            logger.error("Unable to read link for %s", drive_link)
            return

        if not os.path.isabs(drive_path):
            drive_path = os.path.join(drives_path, drive_path)
        return os.path.join(drive_path, relpath)
Beispiel #37
0
def _get_screen_saver_inhibitor():
    """Return the appropriate screen saver inhibitor instance.
    Returns None if the required interface isn't available."""
    desktop_environment = get_desktop_environment()
    if desktop_environment is DesktopEnvironment.MATE:
        name = "org.mate.ScreenSaver"
        path = "/"
    elif desktop_environment is DesktopEnvironment.XFCE:
        name = "org.xfce.ScreenSaver"
        path = "/"
    else:
        name = "org.freedesktop.ScreenSaver"
        path = "/org/freedesktop/ScreenSaver"
    interface = name
    try:
        return DBusScreenSaverInhibitor(name, path, interface)
    except GLib.Error as err:
        logger.error("Error during creation of DBusScreenSaverInhibitor: %s", err.message)
        return None
Beispiel #38
0
 def ensure_discord_connected(self):
     """Make sure we are actually connected before trying to send requests"""
     if not self.available():
         logger.debug("Discord Rich Presence not available due to lack of pypresence")
         return
     logger.debug("Ensuring connected.")
     if self.presence_connected:
         logger.debug("Already connected!")
     else:
         logger.debug("Creating Presence object.")
         self.rpc_client = PyPresence(self.client_id)
         try:
             logger.debug("Attempting to connect.")
             self.rpc_client.connect()
             self.presence_connected = True
         except Exception as e:
             logger.error("Unable to reach Discord.  Skipping update: %s", e)
             self.ensure_discord_disconnected()
     return self.presence_connected
Beispiel #39
0
    def get_runner_version(self, version=None):
        """Get the appropriate version for a runner

        Params:
            version (str): Optional version to lookup, will return this one if found

        Returns:
            dict: Dict containing version, architecture and url for the runner
        """
        logger.info(
            "Getting runner information for %s%s",
            self.name,
            " (version: %s)" % version if version else "",
        )
        request = Request("{}/api/runners/{}".format(settings.SITE_URL, self.name))
        runner_info = request.get().json
        if not runner_info:
            logger.error("Failed to get runner information")
            return

        versions = runner_info.get("versions") or []
        arch = system.LINUX_SYSTEM.arch
        if version:
            if version.endswith("-i386") or version.endswith("-x86_64"):
                version, arch = version.rsplit("-", 1)
            versions = [v for v in versions if v["version"] == version]
        versions_for_arch = [v for v in versions if v["architecture"] == arch]
        if len(versions_for_arch) == 1:
            return versions_for_arch[0]

        if len(versions_for_arch) > 1:
            default_version = [v for v in versions_for_arch if v["default"] is True]
            if default_version:
                return default_version[0]
        elif len(versions) == 1 and system.LINUX_SYSTEM.is_64_bit:
            return versions[0]
        elif len(versions) > 1 and system.LINUX_SYSTEM.is_64_bit:
            default_version = [v for v in versions if v["default"] is True]
            if default_version:
                return default_version[0]
        # If we didn't find a proper version yet, return the first available.
        if len(versions_for_arch) >= 1:
            return versions_for_arch[0]
Beispiel #40
0
def connect(username, password):
    """Connect to the Lutris API"""
    credentials = urllib.parse.urlencode(
        {"username": username, "password": password}
    ).encode("utf-8")
    login_url = settings.SITE_URL + "/api/accounts/token"
    try:
        request = urllib.request.urlopen(login_url, credentials, 10)
    except (socket.timeout, urllib.error.URLError) as ex:
        logger.error("Unable to connect to server (%s): %s", login_url, ex)
        return False
    response = json.loads(request.read().decode())
    if "token" in response:
        token = response["token"]
        with open(API_KEY_FILE_PATH, "w") as token_file:
            token_file.write(":".join((username, token)))
        get_user_info()
        return response["token"]
    return False
Beispiel #41
0
 def uninstall_runner_cli(self, runner_name):
     """
     uninstall the runner given in application file located in lutris/gui/application.py
     provided using lutris -u <runner>
     """
     try:
         runner_class = import_runner(runner_name)
         runner = runner_class()
     except InvalidRunner:
         logger.error("Failed to import Runner: %s", runner_name)
         return
     if not runner.is_installed():
         print(f"Runner '{runner_name}' is not installed.")
         return
     if runner.can_uninstall():
         runner.uninstall()
         print(f"'{runner_name}' has been uninstalled.")
     else:
         print(f"Runner '{runner_name}' cannot be uninstalled.")
Beispiel #42
0
    def __init__(self, game_id=None):
        super().__init__()
        self.id = game_id
        self.runner = None
        self.game_thread = None
        self.prelaunch_executor = None
        self.heartbeat = None
        self.config = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.exit_main_loop = False
        self.xboxdrv_thread = None
        game_data = pga.get_game_by_field(game_id, "id")
        self.slug = game_data.get("slug") or ""
        self.runner_name = game_data.get("runner") or ""
        self.directory = game_data.get("directory") or ""
        self.name = game_data.get("name") or ""

        self.is_installed = bool(game_data.get("installed"))
        self.platform = game_data.get("platform") or ""
        self.year = game_data.get("year") or ""
        self.lastplayed = game_data.get("lastplayed") or 0
        self.playtime = game_data.get("playtime") or 0.0
        self.game_config_id = game_data.get("configpath") or ""
        self.steamid = game_data.get("steamid") or ""
        self.has_custom_banner = bool(game_data.get("has_custom_banner"))
        self.has_custom_icon = bool(game_data.get("has_custom_icon"))

        self.load_config()
        self.game_runtime_config = {}
        self.resolution_changed = False
        self.compositor_disabled = False
        self.stop_compositor = self.start_compositor = ""
        self.original_outputs = None
        self.log_buffer = Gtk.TextBuffer()
        self.log_buffer.create_tag("warning", foreground="red")

        self.timer = Timer()
        try:
            self.playtime = float(game_data.get("playtime") or 0.0)
        except ValueError:
            logger.error("Invalid playtime value %s",
                         game_data.get("playtime"))
Beispiel #43
0
 def enable_dll(self, system_dir, arch, dll_path):
     """Copies dlls to the appropriate destination"""
     dll = os.path.basename(dll_path)
     if system.path_exists(dll_path):
         wine_dll_path = os.path.join(system_dir, dll)
         if system.path_exists(wine_dll_path):
             if not self.is_managed_dll(
                     wine_dll_path) and not os.path.islink(wine_dll_path):
                 # Backing up original version (may not be needed)
                 shutil.move(wine_dll_path, wine_dll_path + ".orig")
             else:
                 os.remove(wine_dll_path)
         try:
             os.symlink(dll_path, wine_dll_path)
         except OSError:
             logger.error("Failed linking %s to %s", dll_path,
                          wine_dll_path)
     else:
         self.disable_dll(system_dir, arch, dll)
Beispiel #44
0
 def update_discord_rich_presence(self):
     """Dispatch a request to Discord to update presence"""
     if int(time.time()) - self.rpc_interval < self.last_rpc:
         logger.debug("Not enough time since last RPC")
         return
     if self.rpc_enabled:
         self.last_rpc = int(time.time())
         if not self.connect():
             return
         try:
             state_text = "via %s" % self.runner_name if self.show_runner else "  "
             logger.info("Attempting to update Discord status: %s, %s",
                         self.game_name, state_text)
             self.rpc_client.update(details="Playing %s" % self.game_name,
                                    state=state_text,
                                    large_image="large_image",
                                    large_text="Using Lutris")
         except PyPresenceException as ex:
             logger.error("Unable to update Discord: %s", ex)
Beispiel #45
0
 def joy2key(self, config):
     """Run a joy2key thread."""
     if not system.find_executable('joy2key'):
         logger.error("joy2key is not installed")
         return
     win = "grep %s" % config['window']
     if 'notwindow' in config:
         win += ' | grep -v %s' % config['notwindow']
     wid = "xwininfo -root -tree | %s | awk '{print $1}'" % win
     buttons = config['buttons']
     axis = "Left Right Up Down"
     rcfile = os.path.expanduser("~/.joy2keyrc")
     rc_option = '-rcfile %s' % rcfile if os.path.exists(rcfile) else ''
     command = "sleep 5 "
     command += "&& joy2key $(%s) -X %s -buttons %s -axis %s" % (
         wid, rc_option, buttons, axis)
     joy2key_thread = LutrisThread(command)
     self.game_thread.attach_thread(joy2key_thread)
     joy2key_thread.start()
Beispiel #46
0
    def prelaunch(self):
        super(winesteam, self).prelaunch()

        def check_shutdown(is_running, times=10):
            for x in range(1, times + 1):
                time.sleep(1)
                if not is_running():
                    return True
        # Stop existing winesteam to prevent Wine prefix/version problems
        if is_running():
            logger.info("Waiting for Steam to shutdown...")
            self.shutdown()
            if not check_shutdown(is_running):
                logger.info("Wine Steam does not shut down, killing it...")
                kill()
                if not check_shutdown(is_running, 5):
                    logger.error("Failed to shut down Wine Steam :(")
                    return False
        return True
Beispiel #47
0
    def force_shutdown(self):
        """Forces a Steam shutdown, double checking its exit status and raising
        an error if it cannot be killed"""

        def has_steam_shutdown(times=10):
            for __ in range(1, times + 1):
                time.sleep(1)
                if not is_running():
                    return True

        # Stop existing winesteam to prevent Wine prefix/version problems
        if is_running():
            logger.info("Waiting for Steam to shutdown...")
            self.shutdown()
            if not has_steam_shutdown():
                logger.info("Forcing Steam shutdown")
                kill()
                if not has_steam_shutdown(5):
                    logger.error("Failed to shut down Wine Steam :(")
Beispiel #48
0
    def launch(self):
        """Request launching a game. The game may not be installed yet."""
        if not self.is_installed:
            raise RuntimeError("Tried to launch a game that isn't installed")
        self.load_config()  # Reload the config before launching it.

        if str(self.id) in LOG_BUFFERS:  # Reset game logs on each launch
            LOG_BUFFERS.pop(str(self.id))

        if not self.runner:
            dialogs.ErrorDialog(_("Invalid game configuration: Missing runner"))
            return
        if not self.is_launchable():
            logger.error("Game is not launchable")
            return
        self.state = self.STATE_LAUNCHING
        self.prelaunch_pids = system.get_running_pid_list()
        self.emit("game-start")
        jobs.AsyncCall(self.runner.prelaunch, self.configure_game)
Beispiel #49
0
 def download(self, slug: str, url: str) -> Optional[str]:
     """Downloads the banner if not present"""
     if not url:
         return
     cache_path = os.path.join(self.dest_path, self.get_filename(slug))
     if system.path_exists(cache_path, exclude_empty=True):
         return
     if system.path_exists(cache_path):
         cache_stats = os.stat(cache_path)
         # Empty files have a life time between 1 and 2 weeks, retry them after
         if time.time() - cache_stats.st_mtime < 3600 * 24 * random.choice(
                 range(7, 15)):
             return cache_path
         os.unlink(cache_path)
     try:
         return download_file(url, cache_path, raise_errors=True)
     except HTTPError as ex:
         logger.error(ex)
         return
Beispiel #50
0
def check_driver():
    """Report on the currently running driver"""
    driver_info = {}
    if drivers.is_nvidia():
        driver_info = drivers.get_nvidia_driver_info()
        # pylint: disable=logging-format-interpolation
        logger.info("Using {vendor} drivers {version} for {arch}".format(**driver_info["nvrm"]))
        gpus = drivers.get_nvidia_gpu_ids()
        for gpu_id in gpus:
            gpu_info = drivers.get_nvidia_gpu_info(gpu_id)
            logger.info("GPU: %s", gpu_info.get("Model"))
    elif LINUX_SYSTEM.glxinfo:
        # pylint: disable=no-member
        if hasattr(LINUX_SYSTEM.glxinfo, "GLX_MESA_query_renderer"):
            logger.info(
                "Running %s Mesa driver %s on %s",
                LINUX_SYSTEM.glxinfo.opengl_vendor,
                LINUX_SYSTEM.glxinfo.GLX_MESA_query_renderer.version,
                LINUX_SYSTEM.glxinfo.GLX_MESA_query_renderer.device,
            )
    else:
        logger.warning("glxinfo is not available on your system, unable to detect driver version")

    for card in drivers.get_gpus():
        # pylint: disable=logging-format-interpolation
        try:
            logger.info("GPU: {PCI_ID} {PCI_SUBSYS_ID} ({DRIVER} drivers)".format(**drivers.get_gpu_info(card)))
        except KeyError:
            logger.error("Unable to get GPU information from '%s'", card)

    if drivers.is_outdated():
        setting = "hide-outdated-nvidia-driver-warning"
        if settings.read_setting(setting) != "True":
            DontShowAgainDialog(
                setting,
                _("Your NVIDIA driver is outdated."),
                secondary_message=_("You are currently running driver %s which does not "
                                    "fully support all features for Vulkan and DXVK games.\n"
                                    "Please upgrade your driver as described in our "
                                    "<a href='https://github.com/lutris/lutris/wiki/Installing-drivers'>"
                                    "installation guide</a>") % driver_info["nvrm"]["version"],
            )
Beispiel #51
0
    def __init__(
        self,
        url,
        timeout=30,
        stop_request=None,
        headers=None,
        cookies=None,
    ):

        if not url:
            raise ValueError("An URL is required!")
        if url == "None":
            raise ValueError("You'd better stop that right now.")

        if url.startswith("//"):
            url = "https:" + url

        if url.startswith("/"):
            logger.error("Stop using relative URLs!: %s", url)
            url = SITE_URL + url

        self.url = url
        self.status_code = None
        self.content = b""
        self.timeout = timeout
        self.stop_request = stop_request
        self.buffer_size = 1024 * 1024  # Bytes
        self.total_size = None
        self.downloaded_size = 0
        self.headers = {"User-Agent": self.user_agent}
        self.response_headers = None
        self.info = None
        if headers is None:
            headers = {}
        if not isinstance(headers, dict):
            raise TypeError("HTTP headers needs to be a dict ({})".format(headers))
        self.headers.update(headers)
        if cookies:
            cookie_processor = urllib.request.HTTPCookieProcessor(cookies)
            self.opener = urllib.request.build_opener(cookie_processor)
        else:
            self.opener = None
Beispiel #52
0
    def __init__(self, game_id=None):
        super().__init__()
        self.id = game_id  # pylint: disable=invalid-name
        self.runner = None
        self.config = None

        # Load attributes from database
        game_data = pga.get_game_by_field(game_id, "id")
        self.slug = game_data.get("slug") or ""
        self.runner_name = game_data.get("runner") or ""
        self.directory = game_data.get("directory") or ""
        self.name = game_data.get("name") or ""

        self.game_config_id = game_data.get("configpath") or ""
        self.is_installed = bool(game_data.get("installed") and self.game_config_id)
        self.platform = game_data.get("platform") or ""
        self.year = game_data.get("year") or ""
        self.lastplayed = game_data.get("lastplayed") or 0
        self.steamid = game_data.get("steamid") or ""
        self.has_custom_banner = bool(game_data.get("has_custom_banner"))
        self.has_custom_icon = bool(game_data.get("has_custom_icon"))
        self.discord_presence = DiscordPresence()
        try:
            self.playtime = float(game_data.get("playtime") or 0.0)
        except ValueError:
            logger.error("Invalid playtime value %s", game_data.get("playtime"))
            self.playtime = 0.0

        if self.game_config_id:
            self.load_config()
        self.game_thread = None
        self.prelaunch_executor = None
        self.heartbeat = None
        self.killswitch = None
        self.state = self.STATE_IDLE
        self.game_runtime_config = {}
        self.resolution_changed = False
        self.compositor_disabled = False
        self.stop_compositor = self.start_compositor = ""
        self.original_outputs = None
        self._log_buffer = None
        self.timer = Timer()
Beispiel #53
0
def get_outputs():
    """Return list of namedtuples containing output 'name', 'geometry',
    'rotation' and whether it is the 'primary' display."""
    outputs = []
    vid_modes = _get_vidmodes()
    position = None
    rotate = None
    primary = None
    name = None
    if not vid_modes:
        logger.error("xrandr didn't return anything")
        return []
    for line in vid_modes:
        if "connected" in line:
            primary = "primary" in line
            if primary:
                name, _, _, geometry, rotate, *_ = line.split()
            else:
                name, _, geometry, rotate, *_ = line.split()
            if geometry.startswith("("):  # Screen turned off, no geometry
                continue
            if rotate.startswith("("):  # Screen not rotated, no need to include
                rotate = "normal"
            _, x_pos, y_pos = geometry.split("+")
            position = "{x_pos}x{y_pos}".format(x_pos=x_pos, y_pos=y_pos)
        elif "*" in line:
            mode, *framerates = line.split()
            for number in framerates:
                if "*" in number:
                    hertz = number[:-2]
                    outputs.append(
                        Output(
                            name=name,
                            mode=mode,
                            position=position,
                            rotation=rotate,
                            primary=primary,
                            rate=hertz,
                        )
                    )
                    break
    return outputs
Beispiel #54
0
def get_api_games(game_slugs=None, page="1", query_type="games"):
    """Return all games from the Lutris API matching the given game slugs"""
    response_data = get_game_api_page(game_slugs, page=page, query_type=query_type)
    if not response_data:
        return []
    results = response_data.get("results", [])
    while response_data.get("next"):
        page_match = re.search(r"page=(\d+)", response_data["next"])
        if page_match:
            next_page = page_match.group(1)
        else:
            logger.error("No page found in %s", response_data["next"])
            break
        response_data = get_game_api_page(game_slugs, page=next_page, query_type=query_type)
        if not response_data.get("results"):
            logger.warning("Unable to get response for page %s", next_page)
            break
        else:
            results += response_data.get("results")
    return results
Beispiel #55
0
def get_display_manager():
    """Return the appropriate display manager instance.
    Defaults to Mutter if available. This is the only one to support Wayland.
    """
    if DBUS_AVAILABLE:
        try:
            return MutterDisplayManager()
        except DBusException as ex:
            logger.debug("Mutter DBus service not reachable: %s", ex)
        except Exception as ex:  # pylint: disable=broad-except
            logger.exception(
                "Failed to instanciate MutterDisplayConfig. Please report with exception: %s",
                ex)
    else:
        logger.error(
            "DBus is not available, lutris was not properly installed.")
    try:
        return DisplayManager()
    except (GLib.Error, NoScreenDetected):
        return LegacyDisplayManager()
Beispiel #56
0
        def update_gui(result, error):
            if result:
                added_ids, updated_ids = result

                # sqlite limits the number of query parameters to 999, to
                # bypass that limitation, divide the query in chunks
                page_size = 999
                added_games = chain.from_iterable([
                    pga.get_games_where(
                        id__in=list(added_ids)[p * page_size:p * page_size +
                                               page_size])
                    for p in range(math.ceil(len(added_ids) / page_size))
                ])
                self.game_list += added_games
                self.view.populate_games(added_games)
                self.switch_splash_screen()
                GLib.idle_add(self.update_existing_games, added_ids,
                              updated_ids, True)
            else:
                logger.error("No results returned when syncing the library")
Beispiel #57
0
    def download(self):
        """Download DXVK to the local cache"""
        dxvk_url = self.base_url.format(self.version, self.version)
        if self.is_available():
            logger.warning("DXVK already available at %s", self.dxvk_path)

        dxvk_archive_path = os.path.join(self.base_dir, os.path.basename(dxvk_url))
        downloader = Downloader(dxvk_url, dxvk_archive_path)
        downloader.start()
        while downloader.check_progress() < 1:
            time.sleep(0.3)
        if not os.path.exists(dxvk_archive_path):
            logger.error("DXVK %s not downloaded")
            return
        if os.stat(dxvk_archive_path).st_size:
            extract_archive(dxvk_archive_path, self.dxvk_path, merge_single=True)
            os.remove(dxvk_archive_path)
        else:
            os.remove(dxvk_archive_path)
            raise UnavailableDXVKVersion("Failed to download DXVK %s" % self.version)
Beispiel #58
0
    def parse(self, line):
        """Parse a registry line, populating meta and subkeys"""
        if len(line) < 4:
            # Line is too short, nothing to parse
            return

        if line.startswith("#"):
            self.add_meta(line)
        elif line.startswith('"'):
            try:
                key, value = re.split(re.compile(r"(?<![^\\]\\\")="), line, maxsplit=1)
            except ValueError as ex:
                logger.error("Unable to parse line %s", line)
                logger.exception(ex)
                return
            key = key[1:-1]
            self.subkeys[key] = value
        elif line.startswith("@"):
            key, value = line.split("=", 1)
            self.subkeys["default"] = value
Beispiel #59
0
def get_pixbuf_for_game(game_slug, icon_type, is_installed=True):
    if icon_type in ("banner", "banner_small"):
        default_icon_path = DEFAULT_BANNER
        icon_path = datapath.get_banner_path(game_slug)
    elif icon_type in ("icon", "icon_small"):
        default_icon_path = DEFAULT_ICON
        icon_path = datapath.get_icon_path(game_slug)
    else:
        logger.error("Invalid icon type '%s'", icon_type)
        return

    size = IMAGE_SIZES[icon_type]

    pixbuf = get_pixbuf(icon_path, default_icon_path, size)
    if not is_installed:
        transparent_pixbuf = get_overlay(size).copy()
        pixbuf.composite(transparent_pixbuf, 0, 0, size[0], size[1], 0, 0, 1,
                         1, GdkPixbuf.InterpType.NEAREST, 100)
        return transparent_pixbuf
    return pixbuf
Beispiel #60
0
 def query_download_links(self, download):
     """Convert files from the GOG API to a format compatible with lutris installers"""
     download_links = []
     for game_file in download.get("files", []):
         downlink = game_file.get("downlink")
         if not downlink:
             logger.error("No download information for %s", game_file)
             continue
         download_info = self.get_download_info(downlink)
         for field in ('checksum', 'downlink'):
             download_links.append({
                 "name": download.get("name", ""),
                 "os": download.get("os", ""),
                 "type": download.get("type", ""),
                 "total_size": download.get("total_size", 0),
                 "id": str(game_file["id"]),
                 "url": download_info[field],
                 "filename": download_info[field + "_filename"]
             })
     return download_links