public string Play()
        {
            List <PlayerInfo> players = profile.PlayerData;

            Screen[] all = Screen.AllScreens;

            string backupDir  = GameManager.Instance.GetBackupFolder(this.userGame.Game);
            string binFolder  = Path.GetDirectoryName(userGame.ExePath);
            string rootFolder = ReplaceCaseInsensitive(binFolder, gen.BinariesFolder, "");

            int  gamePadId = 0;
            bool first     = true;

            hidetaskbar = false;

            bool keyboard = false;

            if (gen.SupportsKeyboard)
            {
                // make sure the keyboard player is the last to be started,
                // so it will get the focus by default
                KeyboardPlayer player = (KeyboardPlayer)profile.Options["KeyboardPlayer"];
                if (player.Value != -1)
                {
                    keyboard = true;
                    List <PlayerInfo> newPlayers = new List <PlayerInfo>();

                    for (int i = 0; i < players.Count; i++)
                    {
                        PlayerInfo p = players[i];
                        if (i == player.Value)
                        {
                            continue;
                        }

                        newPlayers.Add(p);
                    }
                    newPlayers.Add(players[player.Value]);
                    players = newPlayers;
                }
            }

            for (int i = 0; i < players.Count; i++)
            {
                PlayerInfo player = players[i];
                if (i > 0 && gen.KillMutex.Length > 0)
                {
                    PlayerInfo before = players[i - 1];
                    for (;;)
                    {
                        if (exited > 0)
                        {
                            return("");
                        }
                        Thread.Sleep(100);
                        if (before.ProcessData.KilledMutexes)
                        {
                            break;
                        }
                    }
                }

                Rectangle playerBounds = player.MonitorBounds;

                // find the monitor that has this screen
                Screen owner = null;
                for (int j = 0; j < all.Length; j++)
                {
                    Screen s = all[j];
                    if (s.Bounds.Contains(playerBounds))
                    {
                        owner = s;
                        break;
                    }
                }

                int  width        = playerBounds.Width;
                int  height       = playerBounds.Height;
                bool isFullscreen = false;

                if (owner != null)
                {
                    Rectangle ob = owner.Bounds;
                    if (playerBounds.X == ob.X &&
                        playerBounds.Y == ob.Y &&
                        playerBounds.Width == ob.Width &&
                        playerBounds.Height == ob.Height)
                    {
                        isFullscreen = true;
                    }
                }

                GenericContext context = gen.CreateContext(profile, player, this);
                context.PlayerID         = i;
                context.IsFullscreen     = isFullscreen;
                context.IsKeyboardPlayer = keyboard && i == players.Count - 1;
                gen.PrePlay(context);

                player.IsKeyboardPlayer = context.IsKeyboardPlayer;

                string saveFile = context.SavePath;
                if (gen.SaveType != SaveType.None && first)
                {
                    GameManager.Instance.BeginBackup(gen);
                    GameManager.Instance.BackupFile(gen, saveFile);
                }

                // symlink the game folder
                // find out the folder that contains the game executable
                string root = GetRootFolder(gen.BinariesFolder);

                string linkFolder = Path.Combine(backupDir, "Instance" + i);
                Directory.CreateDirectory(linkFolder);
                int exitCode;
                CmdUtil.LinkDirectories(rootFolder, linkFolder, out exitCode, root.ToLower());

                string linkBin = Path.Combine(linkFolder, gen.BinariesFolder);

                if (!string.IsNullOrEmpty(gen.BinariesFolder))
                {
                    // this needs fixing, if there are several folder to the exe and they have important files inside, this won't work! TODO
                    Directory.CreateDirectory(linkBin);
                    CmdUtil.LinkDirectories(binFolder, linkBin, out exitCode);
                }

                string exePath = Path.Combine(linkBin, this.userGame.Game.ExecutableName);

                if (context.SymlinkExe)
                {
                    CmdUtil.LinkFiles(binFolder, linkBin, out exitCode, "xinput", "ncoop");
                }
                else
                {
                    CmdUtil.LinkFiles(binFolder, linkBin, out exitCode, "xinput", "ncoop", Path.GetFileNameWithoutExtension(gen.ExecutableName.ToLower()));
                    File.Copy(userGame.ExePath, exePath, true);
                }

                // some games have save files inside their game folder, so we need to access them inside the loop
                this.data[Folder.GameFolder.ToString()] = linkFolder;

                IniFile file = new IniFile(saveFile);

                switch (context.SaveType)
                {
                case SaveType.INI:
                    for (int j = 0; j < context.ModifySave.Length; j++)
                    {
                        SaveInfo save = context.ModifySave[j];
                        if (save is IniSaveInfo)
                        {
                            IniSaveInfo ini = (IniSaveInfo)save;
                            file.IniWriteValue(ini.Section, ini.Key, ini.Value);
                        }
                    }
                    break;
                }

                string startArgs = context.StartArguments;

                if (context.CustomXinput)
                {
                    byte[] xdata = null;
                    if (context.IsKeyboardPlayer)
                    {
                        // TODO: need to make an xinput that answers to no gamepad?
                        xdata = Properties.Resources.xinput4;
                    }
                    else
                    {
                        switch (gamePadId)
                        {
                        case 0:
                            xdata = Properties.Resources.xinput1;
                            break;

                        case 1:
                            xdata = Properties.Resources.xinput2;
                            break;

                        case 2:
                            xdata = Properties.Resources.xinput3;
                            break;

                        case 3:
                        default:
                            xdata = Properties.Resources.xinput4;
                            break;
                        }
                        gamePadId++;
                    }

                    using (Stream str = File.OpenWrite(Path.Combine(linkBin, "xinput1_3.dll")))
                    {
                        str.Write(xdata, 0, xdata.Length);
                    }

                    string ncoopIni = Path.Combine(linkBin, "ncoop.ini");

                    using (Stream str = File.OpenWrite(ncoopIni))
                    {
                        byte[] ini = Properties.Resources.ncoop;
                        str.Write(ini, 0, ini.Length);
                    }

                    IniFile x360 = new IniFile(ncoopIni);
                    x360.IniWriteValue("Options", "HookWindows", context.HookWindows.ToString(CultureInfo.InvariantCulture));
                    x360.IniWriteValue("Options", "HookGameWindow", context.HookGameWindow.ToString(CultureInfo.InvariantCulture));
                    x360.IniWriteValue("Options", "HookNeeded", context.HookNeeded.ToString(CultureInfo.InvariantCulture));
                }

                Process proc;
                if (context.NeedsSteamEmulation)
                {
                    string steamEmu = GameManager.Instance.ExtractSteamEmu();
                    if (string.IsNullOrEmpty(steamEmu))
                    {
                        return("Extraction of SmartSteamEmu failed!");
                    }

                    string emuExe = Path.Combine(steamEmu, "SmartSteamLoader.exe");

                    string  emuIni = Path.Combine(steamEmu, "SmartSteamEmu.ini");
                    IniFile emu    = new IniFile(emuIni);

                    emu.IniWriteValue("Launcher", "Target", exePath);
                    emu.IniWriteValue("Launcher", "StartIn", Path.GetDirectoryName(exePath));
                    emu.IniWriteValue("Launcher", "CommandLine", startArgs);
                    emu.IniWriteValue("Launcher", "SteamClientPath", Path.Combine(steamEmu, "SmartSteamEmu.dll"));
                    emu.IniWriteValue("Launcher", "SteamClientPath64", Path.Combine(steamEmu, "SmartSteamEmu64.dll"));
                    emu.IniWriteValue("Launcher", "InjectDll", "0");
                    emu.IniWriteValue("SmartSteamEmu", "AppId", context.SteamID);

                    if (context.KillMutex.Length > 0)
                    {
                        // to kill the mutexes we need to orphanize the process
                        proc = ProcessUtil.RunOrphanProcess(emuExe);
                    }
                    else
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo();
                        startInfo.FileName = emuExe;
                        proc = Process.Start(startInfo);
                    }

                    player.SteamEmu = true;
                }
                else
                {
                    if (context.KillMutex.Length > 0)
                    {
                        proc = Process.GetProcessById(StartGameUtil.StartGame(exePath, startArgs));
                    }
                    else
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo();
                        startInfo.FileName         = exePath;
                        startInfo.WindowStyle      = ProcessWindowStyle.Hidden;
                        startInfo.Arguments        = startArgs;
                        startInfo.UseShellExecute  = true;
                        startInfo.WorkingDirectory = Path.GetDirectoryName(exePath);
                        proc = Process.Start(startInfo);
                    }

                    if (proc == null)
                    {
                        for (int times = 0; times < 200; times++)
                        {
                            Thread.Sleep(50);

                            Process[] procs        = Process.GetProcesses();
                            string    proceName    = Path.GetFileNameWithoutExtension(context.ExecutableName).ToLower();
                            string    launcherName = Path.GetFileNameWithoutExtension(context.LauncherExe).ToLower();

                            for (int j = 0; j < procs.Length; j++)
                            {
                                Process p      = procs[j];
                                string  lowerP = p.ProcessName.ToLower();
                                if (((lowerP == proceName) || lowerP == launcherName) &&
                                    !attached.Contains(p))
                                {
                                    attached.Add(p);
                                    proc = p;
                                    break;
                                }
                            }

                            if (proc != null)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        attached.Add(proc);
                    }
                }

                ProcessData data = new ProcessData(proc);

                data.Position      = new Point(playerBounds.X, playerBounds.Y);
                data.Size          = new Size(playerBounds.Width, playerBounds.Height);
                data.KilledMutexes = context.KillMutex.Length == 0;
                player.ProcessData = data;

                if (first)
                {
                    if (context.HideTaskbar)
                    {
                        hidetaskbar = true;
                    }
                }

                first = false;
            }

            //if (hidetaskbar)
            //{
            //    User32.HideTaskbar();
            //}

            return(string.Empty);
        }
        public string Play()
        {
            List <PlayerInfo> players = profile.PlayerData;

            Screen[] all = Screen.AllScreens;

            string backupDir  = GameManager.Instance.GetBackupFolder(this.userGame.Game);
            string binFolder  = Path.GetDirectoryName(userGame.ExePath);
            string rootFolder = ReplaceCaseInsensitive(binFolder, gen.BinariesFolder, "");

            int gamePadId = 0;

            for (int i = 0; i < players.Count; i++)
            {
                PlayerInfo player = players[i];
                if (i > 0 && gen.KillMutex.Length > 0)
                {
                    PlayerInfo before = players[i - 1];
                    for (;;)
                    {
                        if (exited > 0)
                        {
                            return("");
                        }
                        Thread.Sleep(100);
                        if (before.screenData.KilledMutexes)
                        {
                            break;
                        }
                    }
                }

                Rectangle playerBounds = player.monitorBounds;

                // find the monitor that has this screen
                Screen owner = null;
                for (int j = 0; j < all.Length; j++)
                {
                    Screen s = all[j];
                    if (s.Bounds.Contains(playerBounds))
                    {
                        owner = s;
                        break;
                    }
                }

                int  width        = playerBounds.Width;
                int  height       = playerBounds.Height;
                bool isFullscreen = false;

                if (owner != null)
                {
                    Rectangle ob = owner.Bounds;
                    if (playerBounds.X == ob.X &&
                        playerBounds.Y == ob.Y &&
                        playerBounds.Width == ob.Width &&
                        playerBounds.Height == ob.Height)
                    {
                        isFullscreen = true;
                    }
                }

                engine.SetValue("Id", i.ToString(CultureInfo.InvariantCulture));
                engine.SetValue("Width", width.ToString(CultureInfo.InvariantCulture));
                engine.SetValue("Height", height.ToString(CultureInfo.InvariantCulture));
                engine.SetValue("IsFullscreen", isFullscreen);

                // symlink the game folder
                // find out the folder that contains the game executable
                string root = GetRootFolder(gen.BinariesFolder);

                string linkFolder = Path.Combine(backupDir, "Instance" + i);
                Directory.CreateDirectory(linkFolder);
                int exitCode;
                CmdUtil.LinkDirectories(rootFolder, linkFolder, out exitCode, root.ToLower());

                string linkBin = Path.Combine(linkFolder, gen.BinariesFolder);

                if (!string.IsNullOrEmpty(gen.BinariesFolder))
                {
                    // this needs fixing, if there are several folder to the exe and they have important files inside, this won't work! TODO
                    Directory.CreateDirectory(linkBin);
                    CmdUtil.LinkDirectories(binFolder, linkBin, out exitCode);
                }

                string exePath = Path.Combine(linkBin, this.userGame.Game.ExecutableName);

                if (gen.SymlinkExe)
                {
                    CmdUtil.LinkFiles(binFolder, linkBin, out exitCode, "xinput");
                }
                else
                {
                    CmdUtil.LinkFiles(binFolder, linkBin, out exitCode, "xinput", Path.GetFileNameWithoutExtension(gen.ExecutableName.ToLower()));
                    File.Copy(userGame.ExePath, exePath, true);
                }


                // some games have save files inside their game folder, so we need to access them inside the loop
                this.data[NucleusFolderEnum.GameFolder.ToString()] = linkFolder;

                string  saveFile = ProcessPath(gen.SavePath);
                IniFile file     = new IniFile(saveFile);

                switch (gen.SaveType)
                {
                case GenericGameSaveType.INI:
                    foreach (var modPair in gen.ModifySave)
                    {
                        string   key  = modPair.Key;
                        string[] keys = key.Split('/');

                        engine.Execute(modPair.Value);
                        string val = engine.GetCompletionValue().ToString();

                        file.IniWriteValue(keys[0], keys[1], val);
                    }
                    break;
                }

                string startArgs = gen.StartArguments;

                byte[] xdata = null;

                if (gen.SupportsKeyboard && i == (int)profile.Options["KeyboardPlayer"])
                {
                    engine.SetValue("Keyboard", true);

                    // need to make an xinput that answers to no gamepad?
                    xdata = Properties.Resources.xinput4;
                }
                else
                {
                    engine.SetValue("Keyboard", false);

                    switch (gamePadId)
                    {
                    case 0:
                        xdata = Properties.Resources.xinput1;
                        break;

                    case 1:
                        xdata = Properties.Resources.xinput2;
                        break;

                    case 2:
                        xdata = Properties.Resources.xinput3;
                        break;

                    case 3:
                        xdata = Properties.Resources.xinput4;
                        break;

                    default:
                        xdata = Properties.Resources.xinput4;
                        break;
                    }
                    gamePadId++;
                }

                using (Stream str = File.OpenWrite(Path.Combine(linkBin, "xinput1_3.dll")))
                {
                    str.Write(xdata, 0, xdata.Length);
                }

                if (!string.IsNullOrEmpty(startArgs))
                {
                    startArgs = engine.Execute(startArgs).GetCompletionValue().AsString();
                }

                if (gen.NeedsSteamEmulation)
                {
                    string steamEmu = GameManager.Instance.ExtractSteamEmu();
                    string emuExe   = Path.Combine(steamEmu, "SmartSteamLoader.exe");

                    string  emuIni = Path.Combine(steamEmu, "SmartSteamEmu.ini");
                    IniFile emu    = new IniFile(emuIni);

                    emu.IniWriteValue("Launcher", "Target", exePath);
                    emu.IniWriteValue("Launcher", "StartIn", Path.GetDirectoryName(exePath));
                    emu.IniWriteValue("Launcher", "CommandLine", startArgs);
                    emu.IniWriteValue("Launcher", "SteamClientPath", Path.Combine(steamEmu, "SmartSteamEmu.dll"));
                    emu.IniWriteValue("Launcher", "SteamClientPath64", Path.Combine(steamEmu, "SmartSteamEmu64.dll"));
                    emu.IniWriteValue("Launcher", "InjectDll", "0");
                    emu.IniWriteValue("SmartSteamEmu", "AppId", gen.SteamID);

                    Process proc;
                    if (gen.KillMutex.Length > 0)
                    {
                        // to kill the mutexes we need to orphanize the process
                        proc = ProcessUtil.RunOrphanProcess(emuExe);
                    }
                    else
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo();
                        startInfo.FileName = emuExe;
                        proc = Process.Start(startInfo);
                    }

                    player.Process  = proc;
                    player.SteamEmu = true;
                }
                else
                {
                    Process proc;
                    if (gen.KillMutex.Length > 0)
                    {
                        proc = Process.GetProcessById(StartGameUtil.StartGame(exePath, startArgs));
                    }
                    else
                    {
                        ProcessStartInfo startInfo = new ProcessStartInfo();
                        startInfo.FileName         = exePath;
                        startInfo.WindowStyle      = ProcessWindowStyle.Hidden;
                        startInfo.Arguments        = startArgs;
                        startInfo.UseShellExecute  = true;
                        startInfo.WorkingDirectory = Path.GetDirectoryName(exePath);
                        proc = Process.Start(startInfo);
                    }
                    if (proc == null)
                    {
                        for (int times = 0; times < 200; times++)
                        {
                            Thread.Sleep(50);

                            Process[] procs        = Process.GetProcesses();
                            string    proceName    = Path.GetFileNameWithoutExtension(gen.ExecutableName).ToLower();
                            string    launcherName = Path.GetFileNameWithoutExtension(gen.LauncherExe).ToLower();

                            for (int j = 0; j < procs.Length; j++)
                            {
                                Process p      = procs[j];
                                string  lowerP = p.ProcessName.ToLower();
                                if (((lowerP == proceName) || lowerP == launcherName) &&
                                    !attached.Contains(p))
                                {
                                    attached.Add(p);
                                    proc = p;
                                    break;
                                }
                            }

                            if (proc != null)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        attached.Add(proc);
                    }
                    player.Process = proc;
                }

                ScreenData data = new ScreenData();
                data.Position      = new Point(playerBounds.X, playerBounds.Y);
                data.Size          = new Size(playerBounds.Width, playerBounds.Height);
                data.KilledMutexes = gen.KillMutex.Length == 0;
                player.screenData  = data;
            }

            return(string.Empty);
        }