mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Initial parser for butlerdb
This commit is contained in:
Generated
+2
@@ -1092,6 +1092,8 @@ dependencies = [
|
|||||||
"failure",
|
"failure",
|
||||||
"fl2rust",
|
"fl2rust",
|
||||||
"fltk",
|
"fltk",
|
||||||
|
"nom 7.0.0",
|
||||||
|
"nom_locate",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde 1.0.130",
|
"serde 1.0.130",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
+10
-8
@@ -5,16 +5,18 @@ edition = "2018"
|
|||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[dependencies]
|
[dependencies]
|
||||||
steam_shortcuts_util = "1.1.4"
|
steam_shortcuts_util = "^1.1.4"
|
||||||
steamgriddb_api = "0.2,0"
|
steamgriddb_api = "^0.2,0"
|
||||||
serde = { version = "1.0.130", features = ["derive"] }
|
serde = { version = "^1.0.130", features = ["derive"] }
|
||||||
serde_json = "1.0.68"
|
serde_json = "^1.0.68"
|
||||||
tokio = { version = "1.11.0", features = ["full"] }
|
tokio = { version = "^1.11.0", features = ["full"] }
|
||||||
reqwest = { version = "0.11.4", features = ["default"] }
|
reqwest = { version = "^0.11.4", features = ["default"] }
|
||||||
config = "0.11.0"
|
config = "^0.11.0"
|
||||||
failure = "0.1.8"
|
failure = "^0.1.8"
|
||||||
#https://fltk-rs.github.io/fltk-rs/
|
#https://fltk-rs.github.io/fltk-rs/
|
||||||
fltk = { version = "^1.2", features = ["fltk-bundled"], optional = true }
|
fltk = { version = "^1.2", features = ["fltk-bundled"], optional = true }
|
||||||
|
nom = "^7.0.*"
|
||||||
|
nom_locate = "^3.0.*"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
fl2rust = { version = "0.4", optional = true }
|
fl2rust = { version = "0.4", optional = true }
|
||||||
|
|||||||
@@ -6,5 +6,8 @@ debug= false
|
|||||||
|
|
||||||
[steam]
|
[steam]
|
||||||
|
|
||||||
|
[itch]
|
||||||
|
enabled = false
|
||||||
|
|
||||||
[steamgrid_db]
|
[steamgrid_db]
|
||||||
enabled = true
|
enabled = true
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use nom::{
|
||||||
|
bytes::complete::{tag, take_until},
|
||||||
|
multi::many0,
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct DbPaths<'a> {
|
||||||
|
pub(crate) base_path: &'a str,
|
||||||
|
pub(crate) path: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_butler_db<'a>(content: &'a [u8]) -> nom::IResult<&[u8], Vec<DbPaths<'a>>> {
|
||||||
|
many0(parse_path)(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_path<'a>(i: &'a [u8]) -> nom::IResult<&[u8], DbPaths<'a>> {
|
||||||
|
let prefix = "{\"basePath\":\"";
|
||||||
|
let suffix = "\",\"totalSize\"";
|
||||||
|
let (i, _taken) = take_until(prefix)(i)?;
|
||||||
|
let (i, _taken) = tag(prefix)(i)?;
|
||||||
|
let (i, base_path) = take_until(suffix)(i)?;
|
||||||
|
|
||||||
|
let prefix = ":[{\"path\":\"";
|
||||||
|
let suffix = "\",\"depth";
|
||||||
|
let (i, _taken) = take_until(prefix)(i)?;
|
||||||
|
let (i, _taken) = tag(prefix)(i)?;
|
||||||
|
let (i, path) = take_until(suffix)(i)?;
|
||||||
|
|
||||||
|
IResult::Ok((
|
||||||
|
i,
|
||||||
|
DbPaths {
|
||||||
|
base_path: std::str::from_utf8(base_path).unwrap(),
|
||||||
|
path: std::str::from_utf8(path).unwrap(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_itch_butler_db_test() {
|
||||||
|
let content = include_bytes!("../testdata/itch/butler.db-wal");
|
||||||
|
let result = parse_butler_db(content);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let (_r, paths) = result.unwrap();
|
||||||
|
assert_eq!(paths.len(), 6);
|
||||||
|
|
||||||
|
assert_eq!(paths[0].base_path, "/home/philip/.config/itch/apps/islands");
|
||||||
|
assert_eq!(paths[0].path, "Islands_Linux.x86_64");
|
||||||
|
assert_eq!(
|
||||||
|
paths[1].base_path,
|
||||||
|
"/home/philip/.config/itch/apps/night-in-the-woods"
|
||||||
|
);
|
||||||
|
assert_eq!(paths[1].path, "Night in the Woods.x86_64");
|
||||||
|
assert_eq!(paths[2].base_path, "/home/philip/.config/itch/apps/islands");
|
||||||
|
assert_eq!(paths[2].path, "Islands_Linux.x86_64");
|
||||||
|
assert_eq!(
|
||||||
|
paths[3].base_path,
|
||||||
|
"/home/philip/.config/itch/apps/overland"
|
||||||
|
);
|
||||||
|
assert_eq!(paths[3].path, "Overland.x86_64");
|
||||||
|
assert_eq!(
|
||||||
|
paths[4].base_path,
|
||||||
|
"/home/philip/.config/itch/apps/night-in-the-woods"
|
||||||
|
);
|
||||||
|
assert_eq!(paths[4].path, "Night in the Woods.x86_64");
|
||||||
|
assert_eq!(paths[5].base_path, "/home/philip/.config/itch/apps/islands");
|
||||||
|
assert_eq!(paths[5].path, "Islands_Linux.x86_64");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
||||||
|
pub struct ItchGame {
|
||||||
|
pub install_path: String,
|
||||||
|
pub executable: String,
|
||||||
|
pub title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ItchGame> for ShortcutOwned {
|
||||||
|
fn from(game: ItchGame) -> Self {
|
||||||
|
let exe = Path::new(&game.install_path).join(&game.executable);
|
||||||
|
let exe = exe.to_str().unwrap().to_string();
|
||||||
|
let shortcut = Shortcut::new(
|
||||||
|
0,
|
||||||
|
game.title.as_str(),
|
||||||
|
exe.as_str(),
|
||||||
|
&game.install_path,
|
||||||
|
exe.as_str(),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut owned_shortcut = shortcut.to_owned();
|
||||||
|
owned_shortcut.tags.push("Itch".to_owned());
|
||||||
|
owned_shortcut.tags.push("Ready TO Play".to_owned());
|
||||||
|
owned_shortcut.tags.push("Installed".to_owned());
|
||||||
|
|
||||||
|
owned_shortcut
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
use super::butler_db_parser::*;
|
||||||
|
use super::{ItchGame, ItchSettings};
|
||||||
|
use crate::platform::Platform;
|
||||||
|
use failure::*;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub struct ItchPlatform {
|
||||||
|
settings: ItchSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItchPlatform {
|
||||||
|
pub fn new(settings: ItchSettings) -> ItchPlatform {
|
||||||
|
ItchPlatform { settings }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform<ItchGame, ItchErrors> for ItchPlatform {
|
||||||
|
fn enabled(&self) -> bool {
|
||||||
|
self.settings.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"Itch"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shortcuts(&self) -> Result<Vec<ItchGame>, ItchErrors> {
|
||||||
|
let itch_location = self.settings.location.clone();
|
||||||
|
let itch_location = itch_location.unwrap_or_else(get_default_location);
|
||||||
|
|
||||||
|
let itch_db_location = Path::new(&itch_location).join("db").join("butler.db-wal");
|
||||||
|
if !itch_db_location.exists() {
|
||||||
|
return Err(ItchErrors::PathNotFound {
|
||||||
|
path: itch_db_location.to_str().unwrap().to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let shortcut_bytes = std::fs::read(&itch_db_location).unwrap();
|
||||||
|
|
||||||
|
let res = match parse_butler_db(&shortcut_bytes) {
|
||||||
|
Ok((_, shortcuts)) => Ok(shortcuts),
|
||||||
|
Err(e) => Err(ItchErrors::ParseError {
|
||||||
|
error: e.to_string(),
|
||||||
|
path: itch_db_location.to_str().unwrap().to_string(),
|
||||||
|
}),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let res = res
|
||||||
|
.iter()
|
||||||
|
.map(|paths| ItchGame {
|
||||||
|
install_path: paths.base_path.to_owned(),
|
||||||
|
executable: paths.path.to_owned(),
|
||||||
|
title: paths.path.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn get_default_location() -> String {
|
||||||
|
//If we don't have a home drive we have to just die
|
||||||
|
let home = std::env::var("HOME").expect("Expected a home variable to be defined");
|
||||||
|
format!("{}/.config/itch/", home)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn get_default_location() -> String {
|
||||||
|
let key = "PROGRAMFILES(X86)";
|
||||||
|
let program_files = env::var(key).expect("Expected a program files variable to be defined");
|
||||||
|
format!("{}//Itch//", program_files)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum ItchErrors {
|
||||||
|
#[fail(
|
||||||
|
display = "Itch path: {} could not be found. Try to specify a different path for the Itch.",
|
||||||
|
path
|
||||||
|
)]
|
||||||
|
PathNotFound { path: String },
|
||||||
|
|
||||||
|
#[fail(display = "Could not read Itch db at {} error: {}", path, error)]
|
||||||
|
ReadDirError { path: String, error: std::io::Error },
|
||||||
|
|
||||||
|
#[fail(display = "Could not parse Itch db at {} error: {}", path, error)]
|
||||||
|
ParseError { path: String, error: String },
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
mod settings;
|
||||||
|
mod itch_game;
|
||||||
|
mod itch_platform;
|
||||||
|
mod butler_db_parser;
|
||||||
|
|
||||||
|
pub use settings::*;
|
||||||
|
pub use itch_game::*;
|
||||||
|
pub use itch_platform::*;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct ItchSettings {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub location: Option<String>,
|
||||||
|
}
|
||||||
+10
-1
@@ -1,6 +1,10 @@
|
|||||||
use crate::steamgriddb::{download_images, CachedSearch};
|
use crate::{
|
||||||
|
itch::ItchPlatform,
|
||||||
|
steamgriddb::{download_images, CachedSearch},
|
||||||
|
};
|
||||||
use std::{fs::File, io::Write, path::Path};
|
use std::{fs::File, io::Write, path::Path};
|
||||||
mod egs;
|
mod egs;
|
||||||
|
mod itch;
|
||||||
mod legendary;
|
mod legendary;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod settings;
|
mod settings;
|
||||||
@@ -84,6 +88,11 @@ async fn run_sync() -> Result<(), Box<dyn Error>> {
|
|||||||
&mut new_user_shortcuts,
|
&mut new_user_shortcuts,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
update_platform_shortcuts(
|
||||||
|
&ItchPlatform::new(settings.itch.clone()),
|
||||||
|
&mut new_user_shortcuts,
|
||||||
|
);
|
||||||
|
|
||||||
let shortcuts = new_user_shortcuts.iter().map(|f| f.borrow()).collect();
|
let shortcuts = new_user_shortcuts.iter().map(|f| f.borrow()).collect();
|
||||||
|
|
||||||
save_shortcuts(&shortcuts, Path::new(&shortcut_info.path));
|
save_shortcuts(&shortcuts, Path::new(&shortcut_info.path));
|
||||||
|
|||||||
+2
-1
@@ -1,4 +1,4 @@
|
|||||||
use crate::{egs::EpicGamesLauncherSettings, legendary::LegendarySettings, steam::SteamSettings, steamgriddb::SteamGridDbSettings};
|
use crate::{egs::EpicGamesLauncherSettings, itch::ItchSettings, legendary::LegendarySettings, steam::SteamSettings, steamgriddb::SteamGridDbSettings};
|
||||||
|
|
||||||
use config::{Config, ConfigError, Environment, File};
|
use config::{Config, ConfigError, Environment, File};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -9,6 +9,7 @@ pub struct Settings {
|
|||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub epic_games: EpicGamesLauncherSettings,
|
pub epic_games: EpicGamesLauncherSettings,
|
||||||
pub legendary: LegendarySettings,
|
pub legendary: LegendarySettings,
|
||||||
|
pub itch: ItchSettings,
|
||||||
pub steamgrid_db: SteamGridDbSettings,
|
pub steamgrid_db: SteamGridDbSettings,
|
||||||
pub steam: SteamSettings
|
pub steam: SteamSettings
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user