#!/usr/bin/python2.7

#script to launch Wine with the correct environment

import filecmp
import json
import os
import shutil
import struct
import subprocess
import sys
import tarfile

PFX="Proton: "

def log(msg):
    sys.stdout.write(PFX + msg + os.linesep)

def run_wine(args):
    if lfile == None:
        subprocess.call(args, env=env)
    else:
        subprocess.call(args, env=env, stdout=lfile, stderr=subprocess.STDOUT)

def makedirs(path):
    try:
        os.makedirs(path)
    except:
        #already exists
        pass

if not ("STEAM_COMPAT_DATA_PATH" in os.environ):
    log("No compat data path?")
    sys.exit(1)

if "STEAM_COMPAT_CONFIG" in os.environ:
    config_opts = os.environ["STEAM_COMPAT_CONFIG"].split(",")
else:
    config_opts = []

if "PROTON_USE_DXVK" in os.environ:
    config_opts.append("dxvk")

basedir = os.path.dirname(sys.argv[0])
bindir = basedir + "/dist/bin/"
libdir = basedir + "/dist/lib"
lib64dir = basedir + "/dist/lib64"
wine_path = bindir + "/wine64"

#extract if needed
if not os.path.exists(basedir + "/dist") or \
        not os.path.exists(basedir + "/dist/version") or \
        not filecmp.cmp(basedir + "/version", basedir + "/dist/version"):
    if os.path.exists(basedir + "/dist"):
        shutil.rmtree(basedir + "/dist")
    tar = tarfile.open(basedir + "/proton_dist.tar.gz", mode="r:gz")
    tar.extractall(path=basedir + "/dist")
    tar.close()
    shutil.copy(basedir + "/version", basedir + "/dist/")

env = dict(os.environ)

env["WINEDEBUG"] = "-all"
lfile_path = None

#env["WINEDEBUG"] = "+timestamp,+tid,+seh"
#lfile_path = env["HOME"] + "/steam-" + env["SteamGameId"] + ".log"

if lfile_path is None:
    lfile = open("/dev/null", "w")
else:
    if os.path.exists(lfile_path):
        os.remove(lfile_path)
    lfile = open(lfile_path, "w")

if "LD_LIBRARY_PATH" in os.environ:
    env["LD_LIBRARY_PATH"] = lib64dir + ":" + libdir + ":" + env["LD_LIBRARY_PATH"]
else:
    env["LD_LIBRARY_PATH"] = lib64dir + ":" + libdir

env["WINEDLLPATH"] = lib64dir + "/wine:" + libdir + "/wine"

if "PATH" in os.environ:
    env["PATH"] = bindir + ":" + env["PATH"]
else:
    env["PATH"] = bindir

if not os.path.isdir(basedir + "/dist/share/default_pfx"):
    #make default prefix
    env["WINEPREFIX"] = basedir + "/dist/share/default_pfx"
    run_wine([wine_path, "wineboot"])
    run_wine([bindir + "/wineserver", "-w"])

prefix = os.environ["STEAM_COMPAT_DATA_PATH"] + "/pfx/"
env["WINEPREFIX"] = prefix

if not os.path.isdir(prefix):
    #copy default prefix into place
    shutil.copytree(basedir + "/dist/share/default_pfx", prefix, symlinks=True)

#copy steam files into place
steamdir = env["HOME"] + "/.steam/root/legacycompat/"
dst = prefix + "/drive_c/Program Files (x86)/"
makedirs(dst + "Steam")
filestocopy = ["steamclient.dll",
        "steamclient64.dll",
        "Steam.dll"]
for f in filestocopy:
    if os.path.isfile(steamdir + f):
        shutil.copy(steamdir + f, dst + "Steam/" + f)

#copy openvr files into place
dst = prefix + "/drive_c/vrclient/bin/"
makedirs(dst)
shutil.copy(basedir + "/dist/lib/wine/fakedlls/vrclient.dll", dst)
shutil.copy(basedir + "/dist/lib64/wine/fakedlls/vrclient_x64.dll", dst)

#parse linux openvr config and present it in win32 format to the app.
#logic from openvr's CVRPathRegistry_Public::GetPaths

#check environment for overrides
vr_runtime = None
if "VR_OVERRIDE" in env:
    vr_runtime = env["VR_OVERRIDE"]
    env.pop("VR_OVERRIDE")

vr_config = None
if "VR_CONFIG_PATH" in env:
    vr_config = env["VR_CONFIG_PATH"]
    env.pop("VR_CONFIG_PATH")

vr_log = None
if "VR_LOG_PATH" in env:
    vr_log = env["VR_LOG_PATH"]
    env.pop("VR_LOG_PATH")

#load from json if needed
if vr_runtime is None or \
        vr_config is None or \
        vr_log is None:
    try:
        if "XDG_CONFIG_HOME" in env:
            path = env["XDG_CONFIG_HOME"]
        else:
            path = env["HOME"] + "/.config"
        path = path + "/openvr/openvrpaths.vrpath"

        j = json.load(open(path, "r"))

        if vr_runtime is None:
            vr_runtime = j["runtime"][0]

        if vr_config is None:
            vr_config = j["config"][0]

        if vr_log is None:
            vr_log = j["log"][0]
    except:
        pass

makedirs(prefix + "/drive_c/users/steamuser/Local Settings/Application Data/openvr")

#remove existing file
vrpaths_name = prefix + "/drive_c/users/steamuser/Local Settings/Application Data/openvr/openvrpaths.vrpath"
if os.path.exists(vrpaths_name):
    os.remove(vrpaths_name)

#dump new file
if not vr_runtime is None:
    try:
        env["PROTON_VR_RUNTIME"] = vr_runtime

        j = { "runtime": [ "C:\\vrclient\\", "C:\\vrclient" ] }

        if not vr_config is None:
            win_vr_config = subprocess.check_output([wine_path, "winepath", "-w", vr_config], env=env, stderr=open("/dev/null", "w"))
            j["config"] = [ win_vr_config.strip() ]

        if not vr_log is None:
            win_vr_log = subprocess.check_output([wine_path, "winepath", "-w", vr_log], env=env, stderr=open("/dev/null", "w"))
            j["log"] = [ win_vr_log.strip() ]

        j["version"] = 1
        j["jsonid"] = "vrpathreg"

        json.dump(j, open(vrpaths_name, "w"), indent=2)
    except:
        pass

def make_dxvk_links(dll_dir, link_dir):
    if os.path.lexists(link_dir + "/d3d11.dll"):
        os.remove(link_dir + "/d3d11.dll")
    if os.path.lexists(link_dir + "/dxgi.dll"):
        os.remove(link_dir + "/dxgi.dll")
    os.symlink(dll_dir + "/d3d11.dll", link_dir + "/d3d11.dll")
    os.symlink(dll_dir + "/dxgi.dll", link_dir + "/dxgi.dll")

if "dxvk" in config_opts:
    make_dxvk_links(basedir + "/dist/lib64/wine/dxvk/",
        prefix + "drive_c/windows/system32")
    make_dxvk_links(basedir + "/dist/lib/wine/dxvk/",
        prefix + "drive_c/windows/syswow64")
    env["WINEDLLOVERRIDES"] = "dxgi,d3d11=n"
else:
    make_dxvk_links(basedir + "/dist/lib64/wine/",
        prefix + "drive_c/windows/system32")
    make_dxvk_links(basedir + "/dist/lib/wine/",
        prefix + "drive_c/windows/syswow64")

ARCH_UNKNOWN=0
ARCH_I386=1
ARCH_X86_64=2
def determine_architecture(path):
    #algorithm from file's msdos magic file
    with open(path, "rb") as f:
        magic = f.read(2)
        if magic != "MZ":
            return ARCH_UNKNOWN
        f.seek(0x18)
        reloc = struct.unpack('<H', f.read(2))[0]
        if reloc < 0x40:
            #DOS
            return ARCH_I386
        f.seek(0x3c)
        pe_offs = struct.unpack('<L', f.read(4))[0]
        f.seek(pe_offs)
        magic = f.read(4)
        if magic != "PE\0\0":
            return ARCH_UNKNOWN
        f.seek(pe_offs + 4)
        arch = struct.unpack('<H', f.read(2))[0]
        if arch == 0x8664:
            return ARCH_X86_64
        if arch == 0x014c:
            return ARCH_I386
        return ARCH_UNKNOWN

#determine mode
if sys.argv[1] == "run":
    #start target app
    if "PROTON_DUMP_DEBUG_COMMAND" in env:
        f = open("/tmp/proton_dbg_cmd", "w")
        f.write("#!/bin/bash\n\n")
        f.write("cd \"" + os.getcwd() + "\"\n")
        f.write("SteamGameId=\"" + env["SteamGameId"] + "\" \\\n")
        f.write("\tSteamAppId=\"" + env["SteamAppId"] + "\" \\\n")
        f.write("\tPATH=\"" + env["PATH"] + "\" \\\n")
        f.write("\tWINEDEBUG=-all\\\n")
        f.write("\tWINEDLLPATH=\"" + env["WINEDLLPATH"] + "\" \\\n")
        f.write("\tLD_LIBRARY_PATH=\"" + env["LD_LIBRARY_PATH"] + "\" \\\n")
        f.write("\tWINEPREFIX=\"" + env["WINEPREFIX"] + "\" \\\n")
        if "PROTON_VR_RUNTIME" in env:
            f.write("\tPROTON_VR_RUNTIME=\"" + env["PROTON_VR_RUNTIME"] + "\" \\\n")
        if "WINEDLLOVERRIDES" in env:
            f.write("\tWINEDLLOVERRIDES=\"" + env["WINEDLLOVERRIDES"] + "\" \\\n")
        arch = determine_architecture(sys.argv[2])
        if arch == ARCH_X86_64:
            f.write("\t\"" + bindir + "wine64\" winedbg")
        else:
            f.write("\t\"" + bindir + "wine\" winedbg")
        for arg in sys.argv[2:]:
            f.write(" \"" + arg + "\"")
        f.write("\n")
        f.close()
    else:
        run_wine([wine_path] + sys.argv[2:])
elif sys.argv[1] == "translatepath":
    #get windows path equivalent
    path = subprocess.check_output([wine_path, "winepath", "-w", sys.argv[2]], env=env, stderr=open("/dev/null", "w"))
    stdout.write(path)
else:
    stdout.write("Need a verb.")
    sys.exit(1)

sys.exit(0)