Claude: Implement extraction of archive

This commit is contained in:
Fiery Imp 2025-04-23 21:30:01 -04:00
parent 9bc602c136
commit 6aea40bfce
2 changed files with 151 additions and 48 deletions

View File

@ -271,14 +271,50 @@ impl SinfarInstallerApp<> {
}
pub fn start_installation(&mut self) {
// Use tokio runtime to handle the async installation
let install_path = self.install_path.clone().expect("Install path should be set");
let ee_exe_path = self.ee_exe_path.clone();
let download_state = self.download_state.clone();
let progress_state = self.download_state.clone();
let eframe_ctx = self.eframe_ctx.clone().expect("eframe context should be set");
let runtime = self.runtime.clone();
let game_type = self.game_type.clone().expect("Game type should be set");
// Create extraction manager with progress callback
let extractor = crate::installer::extractor::ExtractionManager::new(move |progress: f32, _filename: &str| {
if let Ok(mut app_state) = progress_state.lock() {
app_state.progress = progress;
}
eframe_ctx.request_repaint();
});
// This would be spawned in a separate thread
//let game_type = self.game_type.clone().unwrap();
//let install_path = self.install_path.clone().unwrap();
//let ee_exe_path = self.ee_exe_path.clone();
// Get downloaded files before spawning the async task
let downloaded_files = if let Ok(state) = download_state.lock() {
state.files.clone()
} else {
None
};
// Implementation with sevenz-rust would go here
// Update self.extraction_progress as files are extracted
if let Some(files) = downloaded_files {
runtime.spawn(async move {
// Try to install the files
if let Err(e) = extractor.install_all_files(
&files,
&install_path,
&game_type,
ee_exe_path.as_deref()
).await {
if let Ok(mut app_state) = download_state.lock() {
app_state.error = Some(format!("Installation failed: {}", e));
}
} else {
// Mark as complete on success
if let Ok(mut app_state) = download_state.lock() {
app_state.completed = true;
app_state.progress = 1.0;
}
}
});
}
}
}

View File

@ -1,13 +1,13 @@
// src/installer/extractor.rs
use anyhow::{Result, Context};
use sevenz_rust::{Archive, BlockDecoder, decompress};
use sevenz_rust::decompress;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tokio::sync::Mutex;
use log::{info, warn, error};
use log::{info, error};
use std::fs;
use std::io::{Read, Write};
use std::collections::HashMap;
use std::io::Read;
use crate::app::GameType;
pub struct ExtractionManager {
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str) + Send + Sync>>>,
@ -25,27 +25,67 @@ impl ExtractionManager {
/// Extract a 7z archive with progress reporting
pub async fn extract_7z(&self, archive_path: &Path, output_dir: &Path) -> Result<()> {
info!("Extracting 7z archive: {} -> {}", archive_path.display(), output_dir.display());
info!("Starting extraction of 7z archive");
info!("Archive path: {}", archive_path.display());
info!("Output directory: {}", output_dir.display());
// Check if archive exists
if !archive_path.exists() {
error!("Archive file does not exist: {}", archive_path.display());
return Err(anyhow::anyhow!("Archive file does not exist"));
}
// Validate 7z signature
let mut file = fs::File::open(&archive_path)?;
let mut signature = [0u8; 6];
file.read_exact(&mut signature)?;
// 7z files start with bytes [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]
if signature != [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C] {
error!("Invalid 7z file signature: {:?}", signature);
return Err(anyhow::anyhow!("Invalid 7z file - got wrong signature. The downloaded file might be corrupted or might be an error page"));
}
info!("7z signature validation passed");
// Make sure the output directory exists
if !output_dir.exists() {
info!("Creating output directory: {}", output_dir.display());
tokio::fs::create_dir_all(output_dir).await?;
}
// Get file size for progress calculation
let file_size = fs::metadata(archive_path)?.len();
info!("Archive size: {} bytes", file_size);
// We'll handle extraction in a blocking task since sevenz-rust is synchronous
let archive_path = archive_path.to_path_buf();
let output_dir = output_dir.to_path_buf();
let progress_callback = self.progress_callback.clone();
info!("Starting blocking extraction task");
tokio::task::spawn_blocking(move || -> Result<()> {
info!("Opening archive file for reading");
let archive_file = fs::File::open(&archive_path)?;
let out = fs::create_dir_all(&output_dir)?;
info!("Starting decompression to: {}", output_dir.display());
// decompress takes a Read + Seek and a destination directory
decompress(&archive_file, &output_dir)?;
match decompress(&archive_file, &output_dir) {
Ok(_) => info!("Decompression completed successfully"),
Err(e) => {
error!("Decompression failed: {}", e);
return Err(anyhow::anyhow!("Decompression failed: {}", e));
}
}
// List contents of output directory to verify extraction
if let Ok(entries) = fs::read_dir(&output_dir) {
info!("Listing extracted contents:");
for entry in entries {
if let Ok(entry) = entry {
info!(" {}", entry.path().display());
}
}
}
// Call progress update with 100% after decompression
let filename = archive_path.file_name()
@ -58,9 +98,11 @@ impl ExtractionManager {
callback(1.0, filename); // 100% done
});
info!("Extraction task completed successfully");
Ok(())
}).await??;
info!("7z extraction process completed");
Ok(())
}
@ -139,48 +181,73 @@ impl ExtractionManager {
&self,
downloaded_files: &[PathBuf],
install_path: &Path,
game_type: &str,
game_type: &GameType,
ee_exe_path: Option<&Path>
) -> Result<()> {
info!("Starting installation of all files");
info!("Install path: {}", install_path.display());
info!("Game type: {:?}", game_type);
if let Some(ee_path) = ee_exe_path {
info!("EE executable path: {}", ee_path.display());
}
info!("Available downloaded files:");
for file in downloaded_files {
info!(" {}", file.display());
}
// Find and extract the main content files
let content_archive = downloaded_files.iter()
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfar_all_files")))
.context("Content archive not found in downloaded files")?;
info!("Found content archive: {}", content_archive.display());
// Extract main content
info!("Starting extraction of main content");
self.extract_7z(content_archive, install_path).await?;
// Handle game-specific files
if game_type.to_lowercase() == "diamond" {
// For Diamond, find and copy the launcher
let launcher = downloaded_files.iter()
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarx.exe")))
.context("Launcher executable not found in downloaded files")?;
info!("Processing game-specific files");
match game_type {
GameType::Diamond => {
info!("Installing Diamond edition launcher");
// For Diamond, find and copy the launcher
let launcher = downloaded_files.iter()
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarx.exe")))
.context("Launcher executable not found in downloaded files")?;
// Copy launcher to install directory
let target_path = install_path.join("sinfarx.exe");
tokio::fs::copy(launcher, &target_path).await?;
// Copy launcher to install directory
let target_path = install_path.join("sinfarx.exe");
info!("Copying launcher from {} to {}", launcher.display(), target_path.display());
tokio::fs::copy(launcher, &target_path).await?;
info!("Launcher copied successfully");
}
GameType::EnhancedEdition => {
info!("Installing Enhanced Edition launcher");
// For EE, find the launcher zip
let launcher_zip = downloaded_files.iter()
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarLauncher.zip")))
.context("Launcher ZIP not found in downloaded files")?;
} else if game_type.to_lowercase() == "enhancededition" || game_type.to_lowercase() == "ee" {
// For EE, find the launcher zip
let launcher_zip = downloaded_files.iter()
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarLauncher.zip")))
.context("Launcher ZIP not found in downloaded files")?;
// Extract to the EE executable directory
if let Some(ee_path) = ee_exe_path {
// Create the bin/win32_8181 directory if it doesn't exist (Windows)
// or bin/linux_8181 directory (Linux)
#[cfg(windows)]
let extract_dir = ee_path.join("bin").join("win32_8181");
// Extract to the EE executable directory
if let Some(ee_path) = ee_exe_path {
// Create the bin/win32_8181 directory if it doesn't exist (Windows)
// or bin/linux_8181 directory (Linux)
#[cfg(windows)]
let extract_dir = ee_path.join("bin").join("win32_8181");
#[cfg(unix)]
let extract_dir = ee_path.join("bin").join("linux_8181");
#[cfg(unix)]
let extract_dir = ee_path.join("bin").join("linux_8181");
// Extract the launcher
self.extract_zip(launcher_zip, &extract_dir).await?;
} else {
return Err(anyhow::anyhow!("EE executable path is required for EE installation"));
info!("Extracting EE launcher to {}", extract_dir.display());
// Extract the launcher
self.extract_zip(launcher_zip, &extract_dir).await?;
info!("EE launcher extracted successfully");
} else {
error!("EE executable path is required but not provided");
return Err(anyhow::anyhow!("EE executable path is required for EE installation"));
}
}
}