Skip to content

Commit

Permalink
Compat: Initial support for Steam compatibility tools (`compatibility…
Browse files Browse the repository at this point in the history
…tools.d`) (#300)
  • Loading branch information
tkashkin committed Oct 24, 2020
1 parent 602ca47 commit 307fcbc
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/data/compat/CompatTool.vala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace GameHub.Data.Compat

Compat.Tools.Wine.Wine.detect();
Compat.Tools.Proton.Proton.detect();
Compat.Tools.SteamCompatTool.detect();

foreach(var tool in compat_tools)
{
Expand Down
251 changes: 251 additions & 0 deletions src/data/compat/tools/SteamCompatTool.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
This file is part of GameHub.
Copyright (C) 2018-2019 Anatoliy Kashkin
GameHub is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GameHub is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GameHub. If not, see <https://www.gnu.org/licenses/>.
*/

using Gee;

using GameHub.Utils;

using GameHub.Data.Runnables;
using GameHub.Data.Runnables.Tasks.Install;
using GameHub.Data.Runnables.Tasks.Run;

using GameHub.Data.Sources.Steam;

using GameHub.Data.Compat.Tools.Wine;

namespace GameHub.Data.Compat.Tools
{
public class SteamCompatTool: CompatTool, CompatToolTraits.Run
{
public File? directory { protected get; protected construct set; }

public SteamCompatTool(File directory, string? name = null)
{
var _name = name;
if(_name == null)
{
_name = directory.get_basename();
}
var executable_name = _name;
var run_cmd = get_run_command(directory);
if(run_cmd != null && run_cmd.length > 0)
{
executable_name = Utils.replace_prefix(run_cmd[0], "/", "");
}
var executable = directory.get_child(executable_name);
Object(
tool: "steamct",
id: Utils.md5(executable.get_path()),
name: _name,
icon: "source-steam-symbolic",
directory: directory,
executable: executable
);
}

public SteamCompatTool.from_db(Sqlite.Statement s)
{
File? directory = null;

var info = DB.Tables.CompatTools.INFO.get(s);
var info_node = Parser.parse_json(info);
if(info_node != null && info_node.get_node_type() == Json.NodeType.OBJECT)
{
var info_obj = info_node.get_object();
if(info_obj.has_member("directory"))
{
directory = FS.file(info_obj.get_string_member("directory"));
}
}

Object(
tool: "steamct",
id: DB.Tables.CompatTools.ID.get(s),
name: DB.Tables.CompatTools.NAME.get(s),
icon: "source-steam-symbolic",
directory: directory,
executable: FS.file(DB.Tables.CompatTools.EXECUTABLE.get(s)),
info: DB.Tables.CompatTools.INFO.get(s),
options: DB.Tables.CompatTools.OPTIONS.get(s)
);
}

public bool can_run(Traits.SupportsCompatTools runnable)
{
return runnable is Game;
}

public async void run(Traits.SupportsCompatTools runnable)
{
if(!can_run(runnable)) return;

var dir = directory ?? executable.get_parent();
var run_cmd = get_run_command(dir);

string[] cmd = {};
foreach(var arg in run_cmd)
{
if(cmd.length == 0)
{
cmd += dir.get_path() + arg;
}
else
{
cmd += arg;
}
}
cmd += runnable.executable.get_path();

var task = runnable.prepare_exec_task(cmd);

runnable.cast<Sources.GOG.GOGGame>(game => task.env_var("GOG_GAME_ID", game.id));

yield task.sync_thread();
}

private static string[]? get_run_command(File directory)
{
var root_node = Parser.parse_vdf_file(directory.get_path(), "toolmanifest.vdf");
var manifest = Parser.json_object(root_node, {"manifest"});
if(manifest != null)
{
return manifest.get_string_member("commandline").split(" ");
}
return null;
}

construct
{
try
{
string? _version = null;
if(FileUtils.get_contents(executable.get_parent().get_child("version").get_path(), out _version))
{
version = _version.strip();
}
}
catch(Error e)
{
warning("[SteamCompatTool.construct] Failed to get version: %s", e.message);
}
}

public override void save()
{
Utils.thread("SteamCompatTool.save", () => {
var info_node = new Json.Node(Json.NodeType.OBJECT);
var info_obj = new Json.Object();

if(directory != null && directory.query_exists())
{
info_obj.set_string_member("directory", directory.get_path());
}

info_node.set_object(info_obj);
info = Json.to_string(info_node, false);

DB.Tables.CompatTools.add(this);
});
}

private const string[] STEAM_COMPATTOOLS_PATHS = {"~/.local/share/Steam/compatibilitytools.d"};

private static ArrayList<SteamCompatTool>? steamct_tools = null;

public static ArrayList<SteamCompatTool> detect()
{
if(steamct_tools != null) return steamct_tools;

steamct_tools = new ArrayList<SteamCompatTool>();

var db_tools = (ArrayList<SteamCompatTool>) DB.Tables.CompatTools.get_all("steamct");
if(db_tools != null)
{
foreach(var tool in db_tools)
{
add_tool(tool);
}
}

foreach(var path in STEAM_COMPATTOOLS_PATHS)
{
var compattools_dir = FS.file(path);
if(compattools_dir != null && compattools_dir.query_exists())
{
try
{
FileInfo? finfo = null;
var enumerator = compattools_dir.enumerate_children("standard::*", FileQueryInfoFlags.NONE);
while((finfo = enumerator.next_file()) != null)
{
var dir = compattools_dir.get_child(finfo.get_name());
var toolmanifest = dir.get_child("toolmanifest.vdf");
if(toolmanifest != null && toolmanifest.query_exists())
{
add_tool_from_directory(dir);
}
}
}
catch(Error e)
{
warning("[SteamCompatTool.detect] %s", e.message);
}
}
}

return steamct_tools;
}

public static bool is_tool_added(File directory)
{
foreach(var existing_tool in steamct_tools)
{
if(existing_tool.directory.equal(directory)) return true;
}
return false;
}

public static void add_tool(SteamCompatTool tool)
{
if(!is_tool_added(tool.directory))
{
steamct_tools.add(tool);
Compat.add_tool(tool);
}
}

public static void add_tool_from_directory(File directory, string? name = null)
{
if(!is_tool_added(directory))
{
var proton_executable = directory.get_child("proton");
if(proton_executable != null && proton_executable.query_exists())
{
Proton.Proton.add_proton_version_from_file(proton_executable);
}
else
{
var new_tool = new SteamCompatTool(directory, name);
new_tool.save();
steamct_tools.add(new_tool);
Compat.add_tool(new_tool);
}
}
}
}
}
3 changes: 1 addition & 2 deletions src/data/compat/tools/proton/Proton.vala
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ namespace GameHub.Data.Compat.Tools.Proton

public Proton.from_db(Sqlite.Statement s)
{
var executable = FS.file(DB.Tables.CompatTools.EXECUTABLE.get(s));
Object(
tool: "proton",
id: DB.Tables.CompatTools.ID.get(s),
name: DB.Tables.CompatTools.NAME.get(s),
icon: "source-steam-symbolic",
executable: executable,
executable: FS.file(DB.Tables.CompatTools.EXECUTABLE.get(s)),
info: DB.Tables.CompatTools.INFO.get(s),
options: DB.Tables.CompatTools.OPTIONS.get(s)
);
Expand Down
3 changes: 1 addition & 2 deletions src/data/compat/tools/wine/Wine.vala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ namespace GameHub.Data.Compat.Tools.Wine

public Wine.from_db(Sqlite.Statement s)
{
var executable = FS.file(DB.Tables.CompatTools.EXECUTABLE.get(s));
File? wineserver_executable = null;

var info = DB.Tables.CompatTools.INFO.get(s);
Expand All @@ -74,7 +73,7 @@ namespace GameHub.Data.Compat.Tools.Wine
id: DB.Tables.CompatTools.ID.get(s),
name: DB.Tables.CompatTools.NAME.get(s),
icon: "tool-wine-symbolic",
executable: executable,
executable: FS.file(DB.Tables.CompatTools.EXECUTABLE.get(s)),
wineserver_executable: wineserver_executable,
info: info,
options: DB.Tables.CompatTools.OPTIONS.get(s)
Expand Down
3 changes: 3 additions & 0 deletions src/data/db/tables/CompatTools.vala
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ namespace GameHub.Data.DB.Tables
case "proton":
tools.add(new Tools.Proton.Proton.from_db(s));
break;
case "steamct":
tools.add(new Tools.SteamCompatTool.from_db(s));
break;
}
}
return tools;
Expand Down
3 changes: 3 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ sources = [

'data/compat/tools/proton/Proton.vala',

'data/compat/tools/SteamCompatTool.vala',

#'data/compat/tools/CustomScript.vala',
#'data/compat/tools/Innoextract.vala',
#'data/compat/tools/WineWrap.vala',
Expand Down Expand Up @@ -205,6 +207,7 @@ sources = [
'ui/widgets/compat/CompatToolsList.vala',
'ui/widgets/compat/tabs/Wine.vala',
'ui/widgets/compat/tabs/Proton.vala',
'ui/widgets/compat/tabs/SteamCompatTools.vala',

'ui/widgets/settings/SettingsSidebar.vala',
'ui/widgets/settings/SettingsGroup.vala',
Expand Down
8 changes: 8 additions & 0 deletions src/ui/widgets/compat/CompatToolsList.vala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ namespace GameHub.UI.Widgets.Compat
this.foreach(w => w.destroy());
add_tab(new Tabs.Wine(runnable, mode));
add_tab(new Tabs.Proton(runnable, mode));

if(mode == CompatToolsList.Mode.RUN)
{
add_tab(new Tabs.SteamCompatTools(runnable, mode));
}
}

private void add_tool()
Expand All @@ -85,6 +90,9 @@ namespace GameHub.UI.Widgets.Compat
{
public string title { get; construct set; }

public Traits.SupportsCompatTools? runnable { get; construct; default = null; }
public CompatToolsList.Mode mode { get; construct; default = CompatToolsList.Mode.RUN; }

protected ListBox tools_list;
private Box tool_options;

Expand Down
Loading

0 comments on commit 307fcbc

Please sign in to comment.