Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# top-most EditorConfig file
root = true

[*]
end_of_line = lf
insert_final_newline = true


# 4 space indentation
[*.py]
charset = utf-8
indent_style = space
indent_size = 4
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,5 @@ ENV/
.ropeproject

.*.sw*

.idea
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ For when the [Regular Bridge] [SteamBridge] is too... *sketchy*.

Installation includes an executable Python script named steamfootbridge.

~~Run **steamfootbridge setup** to configure the Wine Steam installation~~ Actually a no-op
Run **steamfootbridge setup** to configure the Wine Steam installation
right now.

Run **steamfootbridge download (appid)** to download the application corresponding to (appid)
Expand Down
33 changes: 20 additions & 13 deletions bin/steamfootbridge
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#!/usr/bin/env python3

import argparse, os, sys
import argparse
import sys
import steamfootbridge

def error(message):

def print_error(message):
print("{}: {}".format(steamfootbridge.__app_name__, message))
exit(1)

def help():

def print_help():
print("Command: \t \t \t Description:")
print("\t-v, --version or version : Prints the version of steamfootbridge to the screen")
print("\t-h, --help or help : Prints this screen")
Expand All @@ -16,22 +19,25 @@ def help():
print("\tsetup {steamid} : Set up a new Wineprefix for the specified app")
exit(0)

def version():

def print_version():
print("{} {}".format(steamfootbridge.__app_name__, steamfootbridge.__version__))
exit(0)


class CLI:
def create_parser(self, command, description):
return argparse.ArgumentParser(prog="{} {}".format(steamfootbridge.__app_name__, command),
description=description)
return argparse.ArgumentParser(
prog="{} {}".format(steamfootbridge.__app_name__, command),
description=description
)

def download(self, args):
parser = self.create_parser("download", "Download an application inside Wine Steam")
parser.add_argument("appid", type=int, help="the Steam application's AppID")
args = parser.parse_args(args)
steamfootbridge.download.do(args.appid)


def execute(self, args):
parser = self.create_parser("execute", "Execute an application inside Wine Steam")
parser.add_argument("appid", type=int, help="the Steam application's AppID")
Expand All @@ -43,22 +49,23 @@ class CLI:
args = parser.parse_args(args)
steamfootbridge.setup.do()


if len(sys.argv) < 2:
error("Need a command")
print_error("Need a command")

cmd = sys.argv[1].lower()
cli = CLI()

if cmd == "--help" or cmd == "-h" or cmd == "help":
help()
elif cmd == "--version" or cmd == "-v" or cmd == "version":
version()
if cmd in ("--help", "-h", "help"):
print_help()
elif cmd in ("--version", "-v", "version"):
print_version()
elif cmd == "download":
cli.download(sys.argv[2:])
elif cmd == "execute":
cli.execute(sys.argv[2:])
elif cmd == "setup":
cli.setup(sys.argv[2:])
else:
error("Unknown or unrecognized command: {}".format(cmd))
print_error("Unknown or unrecognized command: {}".format(cmd))

13 changes: 7 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from setuptools import setup

setup(name='steamfootbridge',
version='0.0.1',
packages=['steamfootbridge'],
scripts=['bin/steamfootbridge'],
install_requires=[
setup(
name='steamfootbridge',
version='0.0.1',
packages=['steamfootbridge'],
scripts=['bin/steamfootbridge'],
install_requires=[
'steamodd',
],
],
)
147 changes: 81 additions & 66 deletions steamfootbridge/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# SteamFootBridge
# Copyright (c) 2016 Bryan DeGrendel

import os, re, shutil, string, subprocess, tempfile
import os
import re
import shutil
import subprocess
import tempfile

# NOTE: No explict \"s around the path. They're necessary when running as an actual shell to keep
# the shell from parsing the slash and whatnot, but are actually stripped out when passed
Expand All @@ -13,69 +17,80 @@
__wine_steam_user_directories__ = "/userdata"
__wine_steam_userconfig_file__ = "/config/localconfig.vdf"


STEAM_EXE_REGEX = re.compile(r'"SteamExe"="(.*)"')
STEAM_PATH_REGEX = re.compile(r'"SteamPath"="(.*)"')


class Configuration:
def __init__(self):
pass

def __enter__(self):
self._temp_directory = tempfile.mkdtemp("steamfootbridge")
self._read_registry_keys()
self._userid = self._determine_current_user()
return self

def __exit__(self, exception_type, exception_value, traceback):
shutil.rmtree(self._temp_directory)

def get_wine_steam_windows_executable(self):
return self._wine_steam_windows_executable

def get_wine_steam_windows_path(self):
return self._wine_steam_windows_path

def get_wine_steam_path(self):
return self._wine_steam_path

def get_current_user(self):
return self._userid

def get_wine_steam_userconfig_filename(self):
return "{}{}/{}{}".format(self.get_wine_steam_path(), __wine_steam_user_directories__,
self._userid, __wine_steam_userconfig_file__)

def _determine_current_user(self):
directories = os.listdir(self._wine_steam_path.decode() + __wine_steam_user_directories__)
if len(directories) == 0:
raise StandardException("No users found! Have you logged into Wine Steam?")
elif len(directories) > 1:
raise StandardException("Mulitple users found! This will evenatually be handled correctly")
else:
return directories[0]


# TODO: This is awfully slow, and it's /probably/ fine to just get the Wine Prefix and do a
# direct search through user.reg - and probably considerably faster.
# TODO: Should handle not finding keys or regedit error or whatever
# TODO: It's probably a good idea to cache this if sticking with regedit, and re-read on request
# or if the path isn't valid
# TODO: Should check to make sure executable actually exists, and path is a directory
def _read_registry_keys(self):
subprocess.call(["regedit", "-E",
"{}/{}".format(self._temp_directory, __wine_registry_dump_file__),
__wine_registry_steam_key__])
self._wine_steam_windows_path = None
self._wine_steam_windows_executable = None
with open("{}/{}".format(self._temp_directory, __wine_registry_dump_file__)) as f:
for line in f:
result = re.search("\"SteamExe\"=\"(.*)\"", line)
if result:
self._wine_steam_windows_executable = result.group(1)

result = re.search("\"SteamPath\"=\"(.*)\"", line)
if result:
self._wine_steam_windows_path = result.group(1)
if self._wine_steam_windows_path == None:
raise StandardException("Unable to determine the SteamPath")
if self._wine_steam_windows_executable == None:
raise StandardException("Unable to determine the SteamExe")
self._wine_steam_path = subprocess.check_output(["winepath",
self._wine_steam_windows_path]).strip()
def __init__(self):
pass

def __enter__(self):
self._temp_directory = tempfile.mkdtemp("steamfootbridge")
self._read_registry_keys()
self._userid = self._determine_current_user()
return self

def __exit__(self, exception_type, exception_value, traceback):
shutil.rmtree(self._temp_directory)

def get_wine_steam_windows_executable(self):
return self._wine_steam_windows_executable

def get_wine_steam_windows_path(self):
return self._wine_steam_windows_path

def get_wine_steam_path(self):
return self._wine_steam_path

def get_current_user(self):
return self._userid

def get_wine_steam_userconfig_filename(self):
return "{}{}/{}{}".format(
self.get_wine_steam_path(), __wine_steam_user_directories__, self._userid, __wine_steam_userconfig_file__
)

def _determine_current_user(self):
directories = os.listdir(self._wine_steam_path + __wine_steam_user_directories__)
if len(directories) == 0:
raise Exception("No users found! Have you logged into Wine Steam?")
elif len(directories) > 1:
raise Exception("Mulitple users found! This will evenatually be handled correctly")
else:
return directories[0]

# TODO: This is awfully slow, and it's /probably/ fine to just get the Wine Prefix and do a
# direct search through user.reg - and probably considerably faster.
# TODO: Should handle not finding keys or regedit error or whatever
# TODO: It's probably a good idea to cache this if sticking with regedit, and re-read on request
# or if the path isn't valid
# TODO: Should check to make sure executable actually exists, and path is a directory
def _read_registry_keys(self):
subprocess.call([
"regedit", "-E", "{}/{}".format(
self._temp_directory, __wine_registry_dump_file__
),
__wine_registry_steam_key__
])
self._wine_steam_windows_path = None
self._wine_steam_windows_executable = None
with open("{}/{}".format(self._temp_directory, __wine_registry_dump_file__), encoding='utf-16') as f:
for line in f:
result = STEAM_EXE_REGEX.search(line)
if result:
self._wine_steam_windows_executable = result.group(1)

result = STEAM_PATH_REGEX.search(line)
if result:
self._wine_steam_windows_path = result.group(1)
if self._wine_steam_windows_path is None:
raise Exception("Unable to determine the SteamPath")

if self._wine_steam_windows_executable is None:
raise Exception("Unable to determine the SteamExe")

self._wine_steam_path = subprocess.check_output(
["winepath", self._wine_steam_windows_path]
).strip().decode('utf-8')
21 changes: 11 additions & 10 deletions steamfootbridge/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

from . import config


def do(appid):
print("Downloading {}".format(appid))
with config.Configuration() as c:
# NOTE: It appears that generated shortcuts are little more than
# C:\\Windows\command\start.exe steam://rungameid/<appid>
# Which I'm 90% sure is simply redirecting to Steam.exe steam://rungameid/<appid>
# - possibly with quotation marks. If so, we probably don't need anything beyond this.
# TODO: In the future, SteamFootBridge will cache the list of possible applications and their
# states. Starting a download like this should trigger a refresh.
subprocess.Popen(["wine", c.get_wine_steam_windows_executable(), "-silent",
"steam://install/{}".format(appid)])
print("Downloading {}".format(appid))
with config.Configuration() as c:
# NOTE: It appears that generated shortcuts are little more than
# C:\\Windows\command\start.exe steam://rungameid/<appid>
# Which I'm 90% sure is simply redirecting to Steam.exe steam://rungameid/<appid>
# - possibly with quotation marks. If so, we probably don't need anything beyond this.
# TODO: In the future, SteamFootBridge will cache the list of possible applications and their
# states. Starting a download like this should trigger a refresh.
subprocess.Popen(["wine", c.get_wine_steam_windows_executable(), "-silent",
"steam://install/{}".format(appid)])
12 changes: 7 additions & 5 deletions steamfootbridge/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

from . import config


def do(appid):
print("Executing appid {}".format(appid))
with config.Configuration() as c:
# TODO: Should we explicitly start Steam if it isn't already running?
subprocess.Popen(["wine", c.get_wine_steam_windows_executable(), "-silent", "-applaunch",
str(appid)])
print("Executing appid {}".format(appid))
with config.Configuration() as c:
# TODO: Should we explicitly start Steam if it isn't already running?
subprocess.Popen([
"wine", c.get_wine_steam_windows_executable(), "-silent", "-applaunch", str(appid)
])
46 changes: 26 additions & 20 deletions steamfootbridge/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import steam


# TODO: Does capitalization mater?
__root_userconfig_key__ = 'UserLocalConfigStore'
__friends_userconfig_key__ = 'friends'
Expand All @@ -15,35 +16,40 @@
# TODO: Option to disable setting friends autologin
# TODO: Option to disable setting overlay disabled
# TODO: Option to avoid touching the userconfig at all


def do():
with config.Configuration() as c:
path = c.get_wine_steam_userconfig_filename()
print("Reading {}".format(path))
with config.Configuration() as c:
path = c.get_wine_steam_userconfig_filename()
print("Reading {}".format(path))

with open(path, 'r') as f:
userconfig = steam.vdf.load(f)
with open(path, mode='r', encoding='utf-16') as f:
userconfig = steam.vdf.load(f)

_base_setup_userconfig(userconfig)
_set_disable_friends_auto_login(userconfig)
_set_disable_overlay(userconfig)
_base_setup_userconfig(userconfig)
_set_disable_friends_auto_login(userconfig)
_set_disable_overlay(userconfig)

print("Writing updated {}".format(path))
with open(path, mode='w', encoding='utf-16') as f:
f.write(steam.vdf.dumps(userconfig).decode('utf-16').lstrip('\n'))

print("Writing updated {}".format(path))
with open(path, 'w') as f:
steam.vdf.dump(userconfig, f)

def _base_setup_userconfig(userconfig):
if not __root_userconfig_key__ in userconfig:
userconfig[__root_userconfig_key__] = {}
root = userconfig[__root_userconfig_key__]
if __root_userconfig_key__ not in userconfig:
userconfig[__root_userconfig_key__] = {}
root = userconfig[__root_userconfig_key__]

if __friends_userconfig_key__ not in root:
root[__friends_userconfig_key__] = {}

if not __friends_userconfig_key__ in root:
root[__friends_userconfig_key__] = {}
if __system_userconfig_key__ not in root:
root[__system_userconfig_key__] = {}

if not __system_userconfig_key__ in root:
root[__system_userconfig_key__] = {}

def _set_disable_friends_auto_login(userconfig):
userconfig[__root_userconfig_key__][__friends_userconfig_key__][__autologin_friends_key__] = '0'
userconfig[__root_userconfig_key__][__friends_userconfig_key__][__autologin_friends_key__] = '0'


def _set_disable_overlay(userconfig):
userconfig[__root_userconfig_key__][__system_userconfig_key__][__enable_game_overlay_key__] = '0'
userconfig[__root_userconfig_key__][__system_userconfig_key__][__enable_game_overlay_key__] = '0'