Claude: Implement extraction of archive
This commit is contained in:
parent
9bc602c136
commit
6aea40bfce
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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,19 +181,37 @@ 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" {
|
||||
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")))
|
||||
@ -159,9 +219,12 @@ impl ExtractionManager {
|
||||
|
||||
// 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?;
|
||||
|
||||
} else if game_type.to_lowercase() == "enhancededition" || game_type.to_lowercase() == "ee" {
|
||||
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")))
|
||||
@ -177,12 +240,16 @@ impl ExtractionManager {
|
||||
#[cfg(unix)]
|
||||
let extract_dir = ee_path.join("bin").join("linux_8181");
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("All files installed successfully");
|
||||
Ok(())
|
||||
|
Loading…
x
Reference in New Issue
Block a user