mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-01-24 20:32:51 -05:00
install smokeapi native #61
This commit is contained in:
@@ -241,8 +241,26 @@ async fn uninstall_creamlinux(game: Game, app_handle: AppHandle) -> Result<(), S
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install SmokeAPI to a game
|
|
||||||
async fn install_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
async fn install_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
||||||
|
// Check if native or proton and route accordingly
|
||||||
|
if game.native {
|
||||||
|
install_smokeapi_native(game, app_handle).await
|
||||||
|
} else {
|
||||||
|
install_smokeapi_proton(game, app_handle).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn uninstall_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
||||||
|
// Check if native or proton and route accordingly
|
||||||
|
if game.native {
|
||||||
|
uninstall_smokeapi_native(game, app_handle).await
|
||||||
|
} else {
|
||||||
|
uninstall_smokeapi_proton(game, app_handle).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install SmokeAPI to a proton game
|
||||||
|
async fn install_smokeapi_proton(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
||||||
if game.native {
|
if game.native {
|
||||||
return Err("SmokeAPI can only be installed on Proton/Windows games".to_string());
|
return Err("SmokeAPI can only be installed on Proton/Windows games".to_string());
|
||||||
}
|
}
|
||||||
@@ -286,8 +304,8 @@ async fn install_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), Strin
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uninstall SmokeAPI from a game
|
// Uninstall SmokeAPI from a proton game
|
||||||
async fn uninstall_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
async fn uninstall_smokeapi_proton(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
||||||
if game.native {
|
if game.native {
|
||||||
return Err("SmokeAPI can only be uninstalled from Proton/Windows games".to_string());
|
return Err("SmokeAPI can only be uninstalled from Proton/Windows games".to_string());
|
||||||
}
|
}
|
||||||
@@ -329,6 +347,99 @@ async fn uninstall_smokeapi(game: Game, app_handle: AppHandle) -> Result<(), Str
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install SmokeAPI to a native Linux game
|
||||||
|
async fn install_smokeapi_native(
|
||||||
|
game: Game,
|
||||||
|
app_handle: AppHandle,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
|
||||||
|
info!("Installing SmokeAPI (native) for game: {}", game.title);
|
||||||
|
let game_title = game.title.clone();
|
||||||
|
|
||||||
|
emit_progress(
|
||||||
|
&app_handle,
|
||||||
|
&format!("Installing SmokeAPI for {}", game_title),
|
||||||
|
"Detecting game architecture...",
|
||||||
|
20.0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
emit_progress(
|
||||||
|
&app_handle,
|
||||||
|
&format!("Installing SmokeAPI for {}", game_title),
|
||||||
|
"Installing from cache...",
|
||||||
|
50.0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Install SmokeAPI for native Linux (empty string for api_files_str)
|
||||||
|
SmokeAPI::install_to_game(&game.path, "")
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to install SmokeAPI: {}", e))?;
|
||||||
|
|
||||||
|
// Update version manifest
|
||||||
|
let cached_versions = crate::cache::read_versions()?;
|
||||||
|
update_game_smokeapi_version(&game.path, cached_versions.smokeapi.latest)?;
|
||||||
|
|
||||||
|
emit_progress(
|
||||||
|
&app_handle,
|
||||||
|
&format!("Installation Completed: {}", game_title),
|
||||||
|
"SmokeAPI has been installed successfully!",
|
||||||
|
100.0,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("SmokeAPI (native) installation completed for: {}", game_title);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uninstall SmokeAPI from a native Linux game
|
||||||
|
async fn uninstall_smokeapi_native(game: Game, app_handle: AppHandle) -> Result<(), String> {
|
||||||
|
if !game.native {
|
||||||
|
return Err("This function is only for native Linux games".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let game_title = game.title.clone();
|
||||||
|
info!("Uninstalling SmokeAPI (native) from game: {}", game_title);
|
||||||
|
|
||||||
|
emit_progress(
|
||||||
|
&app_handle,
|
||||||
|
&format!("Uninstalling SmokeAPI from {}", game_title),
|
||||||
|
"Removing SmokeAPI files...",
|
||||||
|
50.0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Uninstall SmokeAPI (empty string for api_files_str)
|
||||||
|
SmokeAPI::uninstall_from_game(&game.path, "")
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to uninstall SmokeAPI: {}", e))?;
|
||||||
|
|
||||||
|
// Remove version from manifest
|
||||||
|
remove_smokeapi_version(&game.path)?;
|
||||||
|
|
||||||
|
emit_progress(
|
||||||
|
&app_handle,
|
||||||
|
&format!("Uninstallation Completed: {}", game_title),
|
||||||
|
"SmokeAPI has been removed successfully!",
|
||||||
|
100.0,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("SmokeAPI (native) uninstallation completed for: {}", game_title);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch DLC details from Steam API (simple version without progress)
|
// Fetch DLC details from Steam API (simple version without progress)
|
||||||
pub async fn fetch_dlc_details(app_id: &str) -> Result<Vec<DlcInfo>, String> {
|
pub async fn fetch_dlc_details(app_id: &str) -> Result<Vec<DlcInfo>, String> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl Unlocker for SmokeAPI {
|
|||||||
let mut archive =
|
let mut archive =
|
||||||
ZipArchive::new(file).map_err(|e| format!("Failed to read zip archive: {}", e))?;
|
ZipArchive::new(file).map_err(|e| format!("Failed to read zip archive: {}", e))?;
|
||||||
|
|
||||||
// Extract all DLL files
|
// Extract both DLL files (for Proton) and .so files (for native Linux)
|
||||||
for i in 0..archive.len() {
|
for i in 0..archive.len() {
|
||||||
let mut file = archive
|
let mut file = archive
|
||||||
.by_index(i)
|
.by_index(i)
|
||||||
@@ -102,8 +102,11 @@ impl Unlocker for SmokeAPI {
|
|||||||
|
|
||||||
let file_name = file.name();
|
let file_name = file.name();
|
||||||
|
|
||||||
// Only extract DLL files
|
// Extract DLL files for Proton and .so files for native Linux
|
||||||
if file_name.to_lowercase().ends_with(".dll") {
|
let should_extract = file_name.to_lowercase().ends_with(".dll")
|
||||||
|
|| file_name.to_lowercase().ends_with(".so");
|
||||||
|
|
||||||
|
if should_extract {
|
||||||
let output_path = version_dir.join(
|
let output_path = version_dir.join(
|
||||||
Path::new(file_name)
|
Path::new(file_name)
|
||||||
.file_name()
|
.file_name()
|
||||||
@@ -127,17 +130,56 @@ impl Unlocker for SmokeAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn install_to_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
async fn install_to_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
||||||
|
// Check if this is a native Linux game or Proton game
|
||||||
|
// Native games have empty api_files_str, Proton games have DLL paths
|
||||||
|
let is_native = api_files_str.is_empty();
|
||||||
|
|
||||||
|
if is_native {
|
||||||
|
Self::install_to_native_game(game_path).await
|
||||||
|
} else {
|
||||||
|
Self::install_to_proton_game(game_path, api_files_str).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn uninstall_from_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
||||||
|
// Check if this is a native Linux game or Proton game
|
||||||
|
let is_native = api_files_str.is_empty();
|
||||||
|
|
||||||
|
if is_native {
|
||||||
|
Self::uninstall_from_native_game(game_path).await
|
||||||
|
} else {
|
||||||
|
Self::uninstall_from_proton_game(game_path, api_files_str).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"SmokeAPI"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SmokeAPI {
|
||||||
|
/// Install SmokeAPI to a Proton/Windows game
|
||||||
|
async fn install_to_proton_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
||||||
// Parse api_files from the context string (comma-separated)
|
// Parse api_files from the context string (comma-separated)
|
||||||
let api_files: Vec<String> = api_files_str.split(',').map(|s| s.to_string()).collect();
|
let api_files: Vec<String> = api_files_str.split(',').map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Installing SmokeAPI to {} for {} API files",
|
"Installing SmokeAPI (Proton) to {} for {} API files",
|
||||||
game_path,
|
game_path,
|
||||||
api_files.len()
|
api_files.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get the cached SmokeAPI DLLs
|
// Get the cached SmokeAPI DLLs
|
||||||
let cached_dlls = crate::cache::list_smokeapi_dlls()?;
|
let cached_files = crate::cache::list_smokeapi_files()?;
|
||||||
|
if cached_files.is_empty() {
|
||||||
|
return Err("No SmokeAPI files found in cache".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached_dlls: Vec<_> = cached_files
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.extension().and_then(|e| e.to_str()) == Some("dll"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
if cached_dlls.is_empty() {
|
if cached_dlls.is_empty() {
|
||||||
return Err("No SmokeAPI DLLs found in cache".to_string());
|
return Err("No SmokeAPI DLLs found in cache".to_string());
|
||||||
}
|
}
|
||||||
@@ -195,15 +237,77 @@ impl Unlocker for SmokeAPI {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("SmokeAPI installation completed for: {}", game_path);
|
info!("SmokeAPI (Proton) installation completed for: {}", game_path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn uninstall_from_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
/// Install SmokeAPI to a native Linux game
|
||||||
|
async fn install_to_native_game(game_path: &str) -> Result<(), String> {
|
||||||
|
info!("Installing SmokeAPI (native) to {}", game_path);
|
||||||
|
|
||||||
|
// Detect game bitness
|
||||||
|
let bitness = crate::utils::bitness::detect_game_bitness(game_path)?;
|
||||||
|
info!("Detected game bitness: {:?}", bitness);
|
||||||
|
|
||||||
|
// Get the cached SmokeAPI files
|
||||||
|
let cached_files = crate::cache::list_smokeapi_files()?;
|
||||||
|
if cached_files.is_empty() {
|
||||||
|
return Err("No SmokeAPI files found in cache".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which .so file to use based on bitness
|
||||||
|
let target_so = match bitness {
|
||||||
|
crate::utils::bitness::Bitness::Bit32 => "libsmoke_api32.so",
|
||||||
|
crate::utils::bitness::Bitness::Bit64 => "libsmoke_api64.so",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find the matching .so file in cache
|
||||||
|
let matching_so = cached_files
|
||||||
|
.iter()
|
||||||
|
.find(|file| {
|
||||||
|
file.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
== target_so
|
||||||
|
})
|
||||||
|
.ok_or_else(|| format!("No matching {} found in cache", target_so))?;
|
||||||
|
|
||||||
|
let game_path_obj = Path::new(game_path);
|
||||||
|
|
||||||
|
// Look for libsteam_api.so in the game directory (scan up to depth 3)
|
||||||
|
let libsteam_path = Self::find_libsteam_api(game_path_obj)?;
|
||||||
|
|
||||||
|
info!("Found libsteam_api.so at: {}", libsteam_path.display());
|
||||||
|
|
||||||
|
// Create backup of original libsteam_api.so
|
||||||
|
let backup_path = libsteam_path.with_file_name("libsteam_api_o.so");
|
||||||
|
|
||||||
|
// Only backup if not already backed up
|
||||||
|
if !backup_path.exists() && libsteam_path.exists() {
|
||||||
|
fs::copy(&libsteam_path, &backup_path)
|
||||||
|
.map_err(|e| format!("Failed to backup libsteam_api.so: {}", e))?;
|
||||||
|
info!("Created backup: {}", backup_path.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace libsteam_api.so with SmokeAPI's libsmoke_api.so
|
||||||
|
fs::copy(matching_so, &libsteam_path)
|
||||||
|
.map_err(|e| format!("Failed to replace libsteam_api.so: {}", e))?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Replaced libsteam_api.so with {} (hook mode)",
|
||||||
|
target_so
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("SmokeAPI (native) installation completed for: {}", game_path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uninstall SmokeAPI from a Proton/Windows game
|
||||||
|
async fn uninstall_from_proton_game(game_path: &str, api_files_str: &str) -> Result<(), String> {
|
||||||
// Parse api_files from the context string (comma-separated)
|
// Parse api_files from the context string (comma-separated)
|
||||||
let api_files: Vec<String> = api_files_str.split(',').map(|s| s.to_string()).collect();
|
let api_files: Vec<String> = api_files_str.split(',').map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
info!("Uninstalling SmokeAPI from: {}", game_path);
|
info!("Uninstalling SmokeAPI (Proton) from: {}", game_path);
|
||||||
|
|
||||||
for api_file in &api_files {
|
for api_file in &api_files {
|
||||||
let api_path = Path::new(game_path).join(api_file);
|
let api_path = Path::new(game_path).join(api_file);
|
||||||
@@ -250,11 +354,79 @@ impl Unlocker for SmokeAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("SmokeAPI uninstallation completed for: {}", game_path);
|
info!("SmokeAPI (Proton) uninstallation completed for: {}", game_path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> &'static str {
|
/// Uninstall SmokeAPI from a native Linux game
|
||||||
"SmokeAPI"
|
async fn uninstall_from_native_game(game_path: &str) -> Result<(), String> {
|
||||||
|
info!("Uninstalling SmokeAPI (native) from: {}", game_path);
|
||||||
|
|
||||||
|
let game_path_obj = Path::new(game_path);
|
||||||
|
|
||||||
|
// Look for libsteam_api.so (which is actually our SmokeAPI now)
|
||||||
|
let libsteam_path = match Self::find_libsteam_api(game_path_obj) {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(_) => {
|
||||||
|
warn!("libsteam_api.so not found, nothing to uninstall");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Look for backup
|
||||||
|
let backup_path = libsteam_path.with_file_name("libsteam_api_o.so");
|
||||||
|
|
||||||
|
if backup_path.exists() {
|
||||||
|
// Remove the SmokeAPI version
|
||||||
|
if libsteam_path.exists() {
|
||||||
|
match fs::remove_file(&libsteam_path) {
|
||||||
|
Ok(_) => info!("Removed SmokeAPI version: {}", libsteam_path.display()),
|
||||||
|
Err(e) => warn!("Failed to remove SmokeAPI file: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the original file
|
||||||
|
match fs::rename(&backup_path, &libsteam_path) {
|
||||||
|
Ok(_) => info!("Restored original libsteam_api.so"),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to restore original file: {}", e);
|
||||||
|
// Try to copy instead if rename fails
|
||||||
|
if let Err(copy_err) = fs::copy(&backup_path, &libsteam_path)
|
||||||
|
.and_then(|_| fs::remove_file(&backup_path))
|
||||||
|
{
|
||||||
|
error!("Failed to copy backup file: {}", copy_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("No backup found (libsteam_api_o.so), cannot restore original");
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("SmokeAPI (native) uninstallation completed for: {}", game_path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find libsteam_api.so in the game directory
|
||||||
|
fn find_libsteam_api(game_path: &Path) -> Result<std::path::PathBuf, String> {
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
// Scan for libsteam_api.so (not too deep to keep it fast)
|
||||||
|
for entry in WalkDir::new(game_path)
|
||||||
|
.max_depth(3)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
{
|
||||||
|
let path = entry.path();
|
||||||
|
if !path.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = path.file_name().unwrap_or_default().to_string_lossy();
|
||||||
|
if filename == "libsteam_api.so" {
|
||||||
|
return Ok(path.to_path_buf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("libsteam_api.so not found in game directory".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user