Skip to content

Commit

Permalink
Update to version 0.3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
xomachine committed Apr 9, 2018
2 parents ec8438d + 8740de2 commit 0ab0fdb
Show file tree
Hide file tree
Showing 29 changed files with 9,859 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ script:
- nim -v
- make tools
- touch signatures.txt
- make tests
- make precompile
cache:
directories:
- nim-master
Expand Down
70 changes: 44 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ MAKE ?= make
CACHEDIR ?= $(SRCDIR)
STEAMCLIENT ?= $(SRCDIR)/steamclient.so
SIGNATURESFILE ?= $(SRCDIR)/signatures.txt
SPECDIR ?= $(SRCDIR)
ORIGINAL_SPECFILE ?= steam_api_orig.spec
ORIGINAL_SPECPATH ?= $(SPECDIR)/$(ORIGINAL_SPECFILE)
SPECFILE ?= $(SPECDIR)/steam_api.spec
DLLPARSER ?= $(SRCDIR)/tools/dllparser
SIGSEARCH ?= $(SRCDIR)/tools/sigsearch
VERSIONSDIR ?= $(SRCDIR)/versions
Expand All @@ -28,29 +24,50 @@ else
LIB_POSTFIX =
NIMARCH = i386
endif
ifeq ($(NOTUNE), 1)
TUNEOPTS = --opt:none --passC:'-mtune=generic'
else
TUNEOPTS =
endif
DLL ?= $(SRCDIR)/steam_api$(LIB_POSTFIX).dll
OUTPUTDLL = $(DLL).so
NIMSRCS = $(wildcard $(SRCDIR)/genmacros/*.nim)
VERFILES = $(wildcard $(SRCDIR)/versions/*/libsteam_api.so)
WINLIBS = $(wildcard $(SRCDIR)/versions/*/steam_api.dll)
PREORIGS = $(WINLIBS:%.dll=%.orig_spec)
ORSPECS = $(wildcard $(SRCDIR)/versions/*/steam_api.orig_spec)
PRESPECS = $(ORSPECS:%.orig_spec=%.spec)
PRETARGETS = $(ORSPECS:%.orig_spec=%.dll.so)
INSTALLERSCRIPT = $(wildcard $(SRCDIR)/installer/*.py)
VERENDS = $(notdir $(VERFILES:%/libsteam_api.so=%))
VERAVAILABLE = $(notdir $(ORSPECS:%/steam_api.orig_spec=%))
INSTALLVERLIBS = $(foreach sf, $(VERENDS), $(INSTALLDATA) $(SRCDIR)/versions/$(sf)/libsteam_api.so $(DESTDIR)$(PREFIX)/share/SteamForwarder/versions/$(sf)/libsteam_api.so;)
INSTALLPRELIBS = $(foreach sf, $(VERAVAILABLE), $(INSTALLDATA) $(SRCDIR)/versions/$(sf)/steam_api.dll.so $(DESTDIR)$(PREFIX)/share/SteamForwarder/versions/$(sf)/steam_api.dll.so;)
INSTALLVERSPECS = $(foreach sf, $(VERAVAILABLE), $(INSTALLDATA) $(SRCDIR)/versions/$(sf)/steam_api.spec $(DESTDIR)$(PREFIX)/share/SteamForwarder/versions/$(sf)/steam_api.spec;)
INSTALLVERORSPECS = $(foreach sf, $(VERAVAILABLE), $(INSTALLDATA) $(SRCDIR)/versions/$(sf)/steam_api.orig_spec $(DESTDIR)$(PREFIX)/share/SteamForwarder/versions/$(sf)/steam_api.orig_spec;)

.PHONY: all tools clean fullclean install tests
.NOTPARALLEL:
.PHONY: all tools clean fullclean install precompile prespec preorig signatures
.SECONDARY: $(DLL:%.dll=%.spec)

all: $(OUTPUTDLL)

tools: $(SIGSEARCH) $(DLLPARSER)

tests:
cp tests/testspec.spec $(ORIGINAL_SPECPATH)
$(MAKE) -C $(SRCDIR) ORIGINAL_SPECPATH=$(ORIGINAL_SPECPATH) $(OUTPUTDLL)
precompile: $(PRETARGETS)

prespec: $(PRESPECS)

preorig: $(PREORIGS)

signatures: $(SIGNATURESFILE)

install: tools $(SIGNATURESFILE)
install: tools $(ORSPECS) $(PRESPECS) $(SIGNATURESFILE) $(PRETARGETS)
$(INSTALL) -t $(DESTDIR)$(PREFIX)/share/SteamForwarder/tools $(SIGSEARCH) \
$(DLLPARSER)
$(INSTALLVERLIBS)
$(INSTALLVERSPECS)
$(INSTALLPRELIBS)
$(INSTALLVERORSPECS)
$(INSTALL) $(SRCDIR)/sf_install \
$(DESTDIR)$(PREFIX)/share/SteamForwarder/sf_install
$(INSTALLDATA) -t $(DESTDIR)$(PREFIX)/share/SteamForwarder $(SIGNATURESFILE) \
Expand All @@ -66,33 +83,34 @@ install: tools $(SIGNATURESFILE)
$(DESTDIR)$(PREFIX)/bin/sf_install

$(SIGSEARCH):
$(MAKE) -C $(SRCDIR)/tools $(SIGSEARCH)
$(MAKE) -C $(SRCDIR)/tools TUNEOPTS="$(TUNEOPTS)" $(SIGSEARCH)

$(DLLPARSER):
$(MAKE) -C tools $(DLLPARSER)

$(OUTPUTDLL): $(SPECFILE) $(SIGNATURESFILE)
$(NIMC) c -d:specname=$(SPECFILE) -d:cdfile=$(SIGNATURESFILE) \
%.dll.so: %.spec $(NIMSRCS) $(SRCDIR)/steam_api.nims $(SRCDIR)/steam_api.nim $(SIGNATURESFILE)
$(NIMC) c -d:specname=$< -d:cdfile=$(SIGNATURESFILE) \
--passC:"-m$(ARCH)" --passL:"-m$(ARCH)" --cpu:$(NIMARCH) \
--nimcache:$(CACHEDIR)/nimcache -o:$(OUTPUTDLL) steam_api.nim
$(TUNEOPTS) \
--nimcache:`mktemp -d --tmpdir=$(CACHEDIR) nimcache.XXXX` -o:$@ \
steam_api.nim

$(SPECFILE): $(ORIGINAL_SPECPATH) $(DLLPARSER)
$(DLLPARSER) $(VERSIONSDIR) < $(ORIGINAL_SPECPATH) > $(SPECFILE)
%.spec: %.orig_spec | $(DLLPARSER)
$(DLLPARSER) $(VERSIONSDIR) < $< > $@

$(ORIGINAL_SPECPATH):
cd $(SPECDIR); \
$(WINEDUMP) spec $(DLL); \
$(RM) $(ORIGINAL_SPECFILE:%.spec=%_main.c) Makefile.in; \
$(MV) steam_api.spec "$@"
%.orig_spec: %.dll
cd "`dirname "$<"`"; \
$(WINEDUMP) spec "$<"; \
$(RM) $(@:%.orig_spec=%_main.c) Makefile.in; \
$(MV) $(@:%.orig_spec=%.spec) $@

$(SIGNATURESFILE): $(SIGSEARCH)
$(SIGNATURESFILE): | $(SIGSEARCH)
$(SIGSEARCH) $(STEAMCLIENT) > $(SIGNATURESFILE)

fullclean: clean
$(MAKE) -C tools clean
$(RM) $(SIGNATURESFILE)
$(RM) $(SIGNATURESFILE) $(PRETARGETS) $(PRESPECS)

clean:
$(RM) -r $(SRCDIR)/nimcache
$(RM) $(SCRDIR)/$(OUTPUTDLL) $(SPECFILE) \
$(ORIGINAL_SPECFILE) steam_api_main.c
$(RM) -r $(CACHEDIR)/nimcache.*
$(RM) $(SCRDIR)/$(OUTPUTDLL) steam_api_main.c
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,30 @@ For pre-2014 steam api it might be worthy to use

## Usage dependencies
* steamcmd (not required with experimental flag `--steamnative`)
* nim 0.18.0 or higher (to generate and compile a bunch of wrappers code)
* python3 (for installer script)
* wine-devel (including `winedump`, `winegcc` and headers)
* binutils (`libopcode.so` and `libbfd.so` in particular)
* make

Note that you may need to build `steam_api.dll.so` if the `sf_install` script
won't be able to find matching precompiled library. The script will try to do
the job for you, but it needs the build dependencies installed in your system to
succeed.

Addinional dependences to build:
## Build dependences
All usage dependencies plus

* nim compiler of version 0.18.0 or higher
* binutils (`libopcode.so` and `libbfd.so` in particular)
* binutils-devel (including `bfd.h` and `dis-asm.h`)

## Usage

This is a common usage scenario of SteamForwarder for users.
Some hints for experts can be found in the section below.

* Download redist.tar.bz2 from latest release of SteamForwarder from releases page
* Unpack it to the folder you want
* Open the terminal in the folder SteamForwarder was unpacked
* Download redist.tar.bz2 from latest release of SteamForwarder at the releases page.
* Unpack it to the folder you want.
* Open a terminal in the folder the `sf_install` script was unpacked (`share/SteamForwarder` or `bin`).
* Type `python3 sf_install --help` to learn command line options of installer tool.
* Launch your linux steam client if you decided to use `--steamnative` option, otherwise close the steam client if it is running (it may conflict with the steamcmd).
* Use `sf_install` to install your windows game. E.g. for Paladins it command might look like `python3 sf_install 444090`. The steam AppId of the game can be found in the url of the game page on the steam store site.
Expand Down
22 changes: 14 additions & 8 deletions genmacros/callback.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,10 @@ proc run(obj: ptr WrappedCallback, p: pointer) =
let originObj = obj.origin
asm """
mov %[obj], %%ecx
mov %%esp, %%edi
push %[p]
call %[mcall]
mov %%edi, %%esp
::[obj]"g"(`originObj`), [p]"g"(`p`), [mcall]"g"(`originRun`)
:"eax", "edi", "ecx", "cc"
:"eax", "ecx", "esp", "cc"
"""

proc run2(obj: ptr WrappedCallback, p: pointer, iofail: bool, scall: uint64) =
Expand All @@ -57,22 +55,30 @@ proc run2(obj: ptr WrappedCallback, p: pointer, iofail: bool, scall: uint64) =
let originObj = obj.origin
asm """
mov %[obj], %%ecx
mov %%esp, %%edi
push %[scall]
push %[iofail]
push %[p]
call %[originRun]
mov %%edi, %%esp
::[obj]"g"(`originObj`), [scall]"g"(`scall`), [iofail]"g"(`iofail`), [p]"g"(`p`), [originRun]"g"(`originRun`)
: "ecx", "edi", "eax", "cc"
: "ecx", "eax", "esp", "cc"
"""

proc getCallbackSizeBytes(obj: ptr WrappedCallback): int32 =
## Third CCallback virtual method. It does not need to call original
## getCallbackSizeBytes because it is used to obtain the actually passed to
## the linux side object.
trace("[%p]() = %d\n", obj, sizeof(WrappedCallback))
return sizeof(WrappedCallback).int32
trace("[%p]()\n", obj)
let originRun = (obj.origin.vtable + 8)[]
let originObj = obj.origin
asm """
mov %[obj], %%ecx
call %[mcall]
mov %%eax, %[result]
:[result]"=g"(`result`)
:[obj]"g"(`originObj`), [mcall]"g"(`originRun`)
:"eax", "ecx", "esp", "cc"
"""
trace(" = %d\n", result)
{.pop.}

var vtable = [
Expand Down
10 changes: 10 additions & 0 deletions genmacros/cdecls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ from wrapper import wrapIfNecessary
from generators import genTraceCall, genCall, genArgs
from wine import trace
from callback import wrap, unwrap, wrapToOrigin
from vtables import fastUnWrap
import macros

type Dummy = object
Expand Down Expand Up @@ -87,8 +88,17 @@ macro generateWineDecls*(specs: static[SpecFile]): untyped =
else:
let resultidt = newIdentNode("result")
# This one will be returned implicitly
let checkarg =
if s.nargs > 0:
let originarg = call[1]
let unwrappedarg = newIdentNode("unwrapped")
call[1] = unwrappedarg
quote do:
let `unwrappedarg` = fastUnWrap(`originarg`)
else: newEmptyNode()
decl.body = quote do:
`tracecall`
`checkarg`
`resultidt` = `call`
trace(" = %p\n", `resultidt`)
if not s.swap: # Objects with vtables are never being returned inmemory
Expand Down
9 changes: 9 additions & 0 deletions genmacros/vtables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type

proc wrapClass*(name: string, address: uint32): uint32
proc fastWrap*(address: uint32): uint32
proc fastUnWrap*(address: uint32): uint32
proc eachInt(k: string, a: seq[StackState], sink: NimNode): NimNode
{.compileTime.}

Expand Down Expand Up @@ -164,6 +165,14 @@ eachTable(vtables)
## The classes which already wrapped is stored in the `classAssociations`
var classAssociations = initTable[uint32, WrappedClass]()

proc fastUnWrap(address: uint32): uint32 =
## Checks if `address` already in classAssocations and returns the
## unwrapped object address if it is so or original `address` in other case
for k in classAssociations.keys():
if cast[uint32](classAssociations[k].addr) == address:
return k
return address

proc fastWrap(address: uint32): uint32 =
## Checks if `address` already in classAssocations and returns the
## wrapped object address if it is so or 0 in other case
Expand Down
69 changes: 69 additions & 0 deletions installer/dllsearch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from os.path import join, dirname, isfile, abspath
from os import walk, listdir, symlink
from subprocess import run
from tempfile import TemporaryDirectory

def find_steamapi_dll(installdir):
for root, dirs, files in walk(installdir):
if "steam_api.dll" in files:
return join(root, "steam_api.dll")
print("Can not find steam_api.dll inside the game directory! Probably, " +
"the game is incompatible with SteamForwarder")
exit(1)

def compare_specs(gamespec, precompiledspec):
with open(gamespec, "r") as gamespecfd:
with open(precompiledspec, "r") as prefd:
for line in gamespecfd.readlines():
if line.strip(" \n") == "" or line.startswith("#"):
continue
fname = line.split()
preline = ""
while preline.strip("\n ") == "" or preline.startswith("#"):
preline = prefd.readline()
prefname = preline.split()
if fname != prefname:
return False
return True

def esc(path):
return path.replace(" ", "\\ ")

def findMatchingLibrary(gamelocation):
if " " in gamelocation:
with TemporaryDirectory() as tmpdir:
newgamelocation = join(tmpdir, "thegame")
if " " in newgamelocation:
raise Exception("Can not handle the whitespaces in path. " +
"The temporary directory also contains whitespaces!")
symlink(gamelocation, newgamelocation)
return findMatchingLibrary(newgamelocation)
steamdll = find_steamapi_dll(gamelocation)
print("Found steam_api.dll: "+steamdll)
origspecfile = join(dirname(steamdll), "steam_api.orig_spec")
print("Specfile will be generated: "+origspecfile)
run(["make", "DLL="+esc(steamdll), esc(origspecfile)])
if not isfile(origspecfile):
raise Exception("Can not generate spec file for steam_api!" +
" Ensure that winedump exists in the system.")
versionlist = listdir("versions")
versionlist.sort()
for version in versionlist:
predir = join("versions", version)
prefile = join(predir, "steam_api.orig_spec")
if isfile(prefile) and compare_specs(origspecfile, prefile):
return abspath(predir)
run(["make", "DLL="+esc(steamdll), "clean"])
fixedspec = join(gamelocation, "steam_api.spec")
with TemporaryDirectory() as tmpdir:
run(["make", "DLL="+esc(steamdll),
"SPECDIR="+esc(gamelocation),
"SPECFILE="+esc(fixedspec),
"CACHEDIR="+esc(tmpdir),
esc(steamdll+".so")])
if not isfile(fixedspec):
raise Exception("Failed to compile steam_api.dll.so!")
with open(fixedspec, "r") as f:
return dirname(f.readline().strip("# \n"))


2 changes: 2 additions & 0 deletions installer/steam.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def getAppInfo(self):
while (len(read) == 0 or read[0] != '}') and (len(select([fd], [], [], 1.0)[0]) > 0):
read = fd.readline()
answer+=read
if answer.find("requesting...") >= 0:
return self.getAppInfo()
self.appinfo = parse_app_info(answer, self.appid)
return self.appinfo
def appUpdate(self):
Expand Down
2 changes: 1 addition & 1 deletion installer/steaminterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ def parse_app_info(steam_json, appid):
v['description'] = appinfos['name']
name = v['description']
appinfos['infos'][name] = LaunchInfo(v, installdir)
curlang = ""
if 'baselanguages' in appinfo['depots']:
langs = appinfo['depots']['baselanguages'].split(',')
curlang = ""
curlocale = getlocale()[0]
for lang in langs:
localename = normalize(lang).split('.')[0]
Expand Down
Loading

0 comments on commit 0ab0fdb

Please sign in to comment.