use anyhow::Context; use regex::bytes::Regex; use sha2::Digest; use std::{fs::File, io::Read, path::PathBuf}; #[cfg(any(target_os = "linux", target_os = "macos"))] fn get_steam_root() -> anyhow::Result { let mut base = dirs::home_dir().with_context(|| "Couldn't figure out your home directory")?; #[cfg(target_os="linux")] base.push(".steam/root"); #[cfg(target_os="macos")] base.push("Library/Application Support/Steam"); Ok(base) } #[cfg(target_os = "windows")] fn get_steam_root() -> anyhow::Result { use registry::{self, Security, Data}; let key = registry::Hive::LocalMachine.open(r"SOFTWARE\Wow6432Node\Valve\Steam", Security::Read)?; let value = match key.value("InstallPath")? { Data::String(s) => Some(s.to_string_lossy()), Data::ExpandString(s) => Some(s.to_string_lossy()), _ => None }.with_context(|| "Couldn't read the steam install path from registry")?; let mut base = PathBuf::from(value); Ok(base) } pub fn get_omori_path() -> anyhow::Result { let mut base = get_steam_root()?; base.push("steamapps/libraryfolders.vdf"); let mut buffer = Vec::new(); let mut fd = File::open(base)?; fd.read_to_end(&mut buffer)?; let data = String::from_utf8(buffer)?; let data = keyvalues_parser::Vdf::parse(&data.as_str())?; let data = data.value.get_obj().with_context(|| "Couldn't get the steam library libraryfolders object.")?; let keys = data.keys(); let mut omori_path: Option = None; for key in keys { println!("{:?}", key); let entry = data.get(key).with_context(|| "Couldn't read specific steam library libraryfolders object.")?; let entry = entry.get(0).with_context(|| "Couldn't read specific steam library libraryfolders object.")?; let entry = entry.get_obj().with_context(|| "libraryfolders parsing failed: Not an object")?; let apps = entry.get("apps").with_context(|| "libraryfolders parsing failed: No apps")?; let apps = apps.get(0).with_context(|| "libraryfolders parsing failed: No apps")?; let apps = apps.get_obj().with_context(|| "libraryfolders parsing failed: No apps")?; let apps: Vec<_> = apps.keys().collect(); let path = entry.get("path").with_context(|| "libraryfolders parsing failed: No path")?; let path = path.get(0).with_context(|| "libraryfolders parsing failed: No path")?; let path = path.get_str().with_context(|| "libraryfolders parsing failed: No path")?; if apps.iter().find(|x| (**x).eq("1150690")).is_some() { let mut base = PathBuf::from(path); #[cfg(not(target_os = "macos"))] base.push("steamapps/common/OMORI/www"); #[cfg(target_os = "macos")] base.push("steamapps/common/OMORI/OMORI.app/Contents/Resources/app.nw"); omori_path = Some(base); } } omori_path.with_context(|| "Failed to find omori") } const GAME_KEY_HASH: &[u8; 32] = include_bytes!("keyhash"); pub fn get_omori_key() -> anyhow::Result> { let mut base: PathBuf = get_steam_root()?; base.push("appcache/appinfo.vdf"); let mut buffer = Vec::new(); let mut fd = File::open(base)?; fd.read_to_end(&mut buffer)?; drop(fd); let re = Regex::new(r"--([0-9a-f]{32})")?; for (_, [key]) in re.captures_iter(&buffer).map(|c| c.extract()) { let hash = sha2::Sha256::digest(key); let mut equal = true; for i in 0..32 { if hash[i] != GAME_KEY_HASH[i] { equal = false; } } if equal { return Ok(key.to_vec()) } } Err(anyhow::anyhow!("Couldn't find any valid decryption key for OMORI in your Steam installation.")) }