Skip to content

Commit

Permalink
Started work on #47:
Browse files Browse the repository at this point in the history
* GamesDB refactoring
* Game tags
* Tags import from GOG
* FiltersPopover (just basic UI now, not hooked to anything)
  • Loading branch information
tkashkin committed Aug 27, 2018
1 parent d8c36a3 commit c4e0f3f
Show file tree
Hide file tree
Showing 17 changed files with 447 additions and 124 deletions.
2 changes: 2 additions & 0 deletions src/data/Game.vala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace GameHub.Data
return platform in platforms;
}

public ArrayList<GamesDB.Tables.Tags.Tag> tags { get; protected set; default = new ArrayList<GamesDB.Tables.Tags.Tag>(GamesDB.Tables.Tags.Tag.is_equal); }

public bool is_installable { get; protected set; default = false; }

public File executable { get; protected set; }
Expand Down
324 changes: 240 additions & 84 deletions src/data/GamesDB.vala
Original file line number Diff line number Diff line change
Expand Up @@ -46,94 +46,224 @@ namespace GameHub.Data

db.exec("DROP TABLE IF EXISTS `unsupported_games`");

if(db.prepare_v2("SELECT `platforms` FROM `games`", -1, out s) != Sqlite.OK)
{
db.exec("DROP TABLE `games`");
}

// current db format

db.exec("CREATE TABLE IF NOT EXISTS `games`(
`source` string not null,
`id` string not null,
`name` string not null,
`icon` string,
`image` string,
`install_path` string,
`platforms` string,
`info` string,
`info_detailed` string,
PRIMARY KEY(`source`, `id`))");
Tables.Games.init(db);
Tables.Tags.init(db);

db.exec("CREATE TABLE IF NOT EXISTS `merges`(`merge` string not null, PRIMARY KEY(`merge`))");
}

public enum GAMES
public class Tables
{
SOURCE, ID, NAME, ICON, IMAGE, INSTALL_PATH, PLATFORMS, INFO, INFO_DETAILED;

public int column()
public abstract class DBTable
{
switch(this)
public class Field
{
case GAMES.SOURCE: return 0;
case GAMES.ID: return 1;
case GAMES.NAME: return 2;
case GAMES.ICON: return 3;
case GAMES.IMAGE: return 4;
case GAMES.INSTALL_PATH: return 5;
case GAMES.PLATFORMS: return 6;
case GAMES.INFO: return 7;
case GAMES.INFO_DETAILED: return 8;
public int column = 0;
public int column_for_bind = 0;
public Field(int col)
{
column = col;
column_for_bind = col + 1;
}

public int bind(Statement s, string? str)
{
if(str == null) return bind_null(s);
return s.bind_text(column_for_bind, str);
}
public int bind_int(Statement s, int? i)
{
if(i == null) return bind_null(s);
return s.bind_int(column_for_bind, i);
}
public int bind_int64(Statement s, int64? i)
{
if(i == null) return bind_null(s);
return s.bind_int64(column_for_bind, i);
}
public int bind_value(Statement s, Sqlite.Value? v)
{
if(v == null) return bind_null(s);
return s.bind_value(column_for_bind, v);
}
public int bind_null(Statement s)
{
return s.bind_null(column_for_bind);
}

public string? get(Statement s)
{
return s.column_text(column);
}
public int? get_int(Statement s)
{
return s.column_int(column);
}
public int64? get_int64(Statement s)
{
return s.column_int64(column);
}
public unowned Sqlite.Value? get_value(Statement s)
{
return s.column_value(column);
}
}
assert_not_reached();
}

public int column_for_bind()
{
return column() + 1;
protected static DBTable.Field f(int col)
{
return new DBTable.Field(col);
}
}

public int bind(Statement s, string? str)
{
if(str == null) return bind_null(s);
return s.bind_text(column_for_bind(), str);
}
public int bind_int(Statement s, int? i)
{
if(i == null) return bind_null(s);
return s.bind_int(column_for_bind(), i);
}
public int bind_int64(Statement s, int64? i)
{
if(i == null) return bind_null(s);
return s.bind_int64(column_for_bind(), i);
}
public int bind_value(Statement s, Sqlite.Value? v)
public class Games: DBTable
{
if(v == null) return bind_null(s);
return s.bind_value(column_for_bind(), v);
}
public int bind_null(Statement s)
{
return s.bind_null(column_for_bind());
}
public static DBTable.Field SOURCE;
public static DBTable.Field ID;
public static DBTable.Field NAME;
public static DBTable.Field ICON;
public static DBTable.Field IMAGE;
public static DBTable.Field INSTALL_PATH;
public static DBTable.Field PLATFORMS;
public static DBTable.Field INFO;
public static DBTable.Field INFO_DETAILED;
public static DBTable.Field TAGS;

public static void init(Database? db) requires (db != null)
{
Statement s;
if(db.prepare_v2("SELECT `platforms` FROM `games`", -1, out s) != Sqlite.OK)
{
db.exec("DROP TABLE `games`");
}

public string? get(Statement s)
{
return s.column_text(column());
}
public int? get_int(Statement s)
{
return s.column_int(column());
}
public int64? get_int64(Statement s)
{
return s.column_int64(column());
db.exec("CREATE TABLE IF NOT EXISTS `games`(
`source` string not null,
`id` string not null,
`name` string not null,
`icon` string,
`image` string,
`install_path` string,
`platforms` string,
`info` string,
`info_detailed` string,
`tags` string,
PRIMARY KEY(`source`, `id`))");

if(db.prepare_v2("SELECT `tags` FROM `games`", -1, out s) != Sqlite.OK)
{
db.exec("ALTER TABLE `games` ADD `tags` string");
}

SOURCE = f(0);
ID = f(1);
NAME = f(2);
ICON = f(3);
IMAGE = f(4);
INSTALL_PATH = f(5);
PLATFORMS = f(6);
INFO = f(7);
INFO_DETAILED = f(8);
TAGS = f(9);
}
}
public unowned Sqlite.Value? get_value(Statement s)

public class Tags: DBTable
{
return s.column_value(column());
public static DBTable.Field ID;
public static DBTable.Field NAME;
public static DBTable.Field ICON;

public static ArrayList<Tag> TAGS;

public class Tag: Object
{
public const string BUILTIN_PREFIX = "builtin:";

public enum Builtin
{
FAVORITES, HIDDEN;

public string id()
{
switch(this)
{
case Builtin.FAVORITES: return "favorites";
case Builtin.HIDDEN: return "hidden";
}
assert_not_reached();
}

public string name()
{
switch(this)
{
case Builtin.FAVORITES: return _("Favorites");
case Builtin.HIDDEN: return _("Hidden");
}
assert_not_reached();
}

public string icon()
{
switch(this)
{
case Builtin.FAVORITES: return "user-bookmarks-symbolic";
case Builtin.HIDDEN: return "window-close-symbolic";
}
assert_not_reached();
}
}

public string? id { get; construct set; }
public string? name { get; construct set; }
public string icon { get; construct; }

public Tag(string? id, string? name, string icon="tag-symbolic")
{
Object(id: id, name: name, icon: icon);
}
public Tag.from_db(Statement s)
{
this(ID.get(s), NAME.get(s), ICON.get(s));
}
public Tag.from_builtin(Builtin t)
{
this(BUILTIN_PREFIX + t.id(), t.name(), t.icon());
}

public static bool is_equal(Tag first, Tag second)
{
return first == second || first.id == second.id;
}
}

public static void init(Database? db) requires (db != null)
{
db.exec("CREATE TABLE IF NOT EXISTS `tags`(`id` string, `name` string, `icon` string, PRIMARY KEY(`id`))");

ID = f(0);
NAME = f(1);
ICON = f(2);

TAGS = new ArrayList<Tag>(Tag.is_equal);
TAGS.add(new Tag.from_builtin(Tag.Builtin.FAVORITES));
TAGS.add(new Tag.from_builtin(Tag.Builtin.HIDDEN));

foreach(var t in TAGS)
{
GamesDB.get_instance().add_tag(t);
}

Statement s;
int res = db.prepare_v2("SELECT * FROM `tags`", -1, out s);
while((res = s.step()) == Sqlite.ROW)
{
var tag = new Tag.from_db(s);
if(!TAGS.contains(tag)) TAGS.add(tag);
}
}
}
}

Expand All @@ -143,8 +273,8 @@ namespace GameHub.Data

Statement s;
int res = db.prepare_v2("INSERT OR REPLACE INTO `games`
(`source`, `id`, `name`, `icon`, `image`, `install_path`, `platforms`, `info`, `info_detailed`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", -1, out s);
(`source`, `id`, `name`, `icon`, `image`, `install_path`, `platforms`, `info`, `info_detailed`, `tags`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", -1, out s);

assert(res == Sqlite.OK);

Expand All @@ -155,20 +285,46 @@ namespace GameHub.Data
platforms += p.id();
}

GAMES.SOURCE.bind(s, game.source.id);
GAMES.ID.bind(s, game.id);
GAMES.NAME.bind(s, game.name);
GAMES.ICON.bind(s, game.icon);
GAMES.IMAGE.bind(s, game.image);
GAMES.INSTALL_PATH.bind(s, game.install_dir == null || !game.install_dir.query_exists() ? null : game.install_dir.get_path());
GAMES.PLATFORMS.bind(s, platforms);
GAMES.INFO.bind(s, game.info);
GAMES.INFO_DETAILED.bind(s, game.info_detailed);
var tags = "";
foreach(var t in game.tags)
{
if(tags.length > 0) tags += ",";
tags += t.id;
}

Tables.Games.SOURCE.bind(s, game.source.id);
Tables.Games.ID.bind(s, game.id);
Tables.Games.NAME.bind(s, game.name);
Tables.Games.ICON.bind(s, game.icon);
Tables.Games.IMAGE.bind(s, game.image);
Tables.Games.INSTALL_PATH.bind(s, game.install_dir == null || !game.install_dir.query_exists() ? null : game.install_dir.get_path());
Tables.Games.PLATFORMS.bind(s, platforms);
Tables.Games.INFO.bind(s, game.info);
Tables.Games.INFO_DETAILED.bind(s, game.info_detailed);
Tables.Games.TAGS.bind(s, tags);

res = s.step();

return res == Sqlite.DONE;
}

public bool add_tag(Tables.Tags.Tag tag) requires (db != null)
{
Statement s;
int res = db.prepare_v2("INSERT OR REPLACE INTO `tags` (`id`, `name`, `icon`) VALUES (?, ?, ?)", -1, out s);

assert(res == Sqlite.OK);

Tables.Tags.ID.bind(s, tag.id);
Tables.Tags.NAME.bind(s, tag.name);
Tables.Tags.ICON.bind(s, tag.icon);

res = s.step();

if(!Tables.Tags.TAGS.contains(tag)) Tables.Tags.TAGS.add(tag);

return res == Sqlite.DONE;
}

public bool merge(Game first, Game second) requires (db != null)
{
Expand Down Expand Up @@ -257,7 +413,7 @@ namespace GameHub.Data

if((res = st.step()) == Sqlite.ROW)
{
var s = GameSource.by_id(GAMES.SOURCE.get(st));
var s = GameSource.by_id(Tables.Games.SOURCE.get(st));

if(s is Steam)
{
Expand Down Expand Up @@ -297,7 +453,7 @@ namespace GameHub.Data

while((res = st.step()) == Sqlite.ROW)
{
var s = GameSource.by_id(GAMES.SOURCE.get(st));
var s = GameSource.by_id(Tables.Games.SOURCE.get(st));

if(s is Steam)
{
Expand Down
Loading

0 comments on commit c4e0f3f

Please sign in to comment.