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:
@@ -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>,
|
||||
}
|
||||
Reference in New Issue
Block a user