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) {
|
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");
|
||||||
// This would be spawned in a separate thread
|
let ee_exe_path = self.ee_exe_path.clone();
|
||||||
//let game_type = self.game_type.clone().unwrap();
|
let download_state = self.download_state.clone();
|
||||||
//let install_path = self.install_path.clone().unwrap();
|
let progress_state = self.download_state.clone();
|
||||||
//let ee_exe_path = self.ee_exe_path.clone();
|
let eframe_ctx = self.eframe_ctx.clone().expect("eframe context should be set");
|
||||||
|
let runtime = self.runtime.clone();
|
||||||
// Implementation with sevenz-rust would go here
|
let game_type = self.game_type.clone().expect("Game type should be set");
|
||||||
// Update self.extraction_progress as files are extracted
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get downloaded files before spawning the async task
|
||||||
|
let downloaded_files = if let Ok(state) = download_state.lock() {
|
||||||
|
state.files.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
// src/installer/extractor.rs
|
||||||
use anyhow::{Result, Context};
|
use anyhow::{Result, Context};
|
||||||
use sevenz_rust::{Archive, BlockDecoder, decompress};
|
use sevenz_rust::decompress;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use log::{info, warn, error};
|
use log::{info, error};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{Read, Write};
|
use std::io::Read;
|
||||||
use std::collections::HashMap;
|
use crate::app::GameType;
|
||||||
|
|
||||||
pub struct ExtractionManager {
|
pub struct ExtractionManager {
|
||||||
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str) + Send + Sync>>>,
|
progress_callback: Arc<Mutex<Box<dyn Fn(f32, &str) + Send + Sync>>>,
|
||||||
@ -25,27 +25,67 @@ impl ExtractionManager {
|
|||||||
|
|
||||||
/// Extract a 7z archive with progress reporting
|
/// Extract a 7z archive with progress reporting
|
||||||
pub async fn extract_7z(&self, archive_path: &Path, output_dir: &Path) -> Result<()> {
|
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
|
// Make sure the output directory exists
|
||||||
if !output_dir.exists() {
|
if !output_dir.exists() {
|
||||||
|
info!("Creating output directory: {}", output_dir.display());
|
||||||
tokio::fs::create_dir_all(output_dir).await?;
|
tokio::fs::create_dir_all(output_dir).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get file size for progress calculation
|
// Get file size for progress calculation
|
||||||
let file_size = fs::metadata(archive_path)?.len();
|
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
|
// We'll handle extraction in a blocking task since sevenz-rust is synchronous
|
||||||
let archive_path = archive_path.to_path_buf();
|
let archive_path = archive_path.to_path_buf();
|
||||||
let output_dir = output_dir.to_path_buf();
|
let output_dir = output_dir.to_path_buf();
|
||||||
let progress_callback = self.progress_callback.clone();
|
let progress_callback = self.progress_callback.clone();
|
||||||
|
|
||||||
|
info!("Starting blocking extraction task");
|
||||||
tokio::task::spawn_blocking(move || -> Result<()> {
|
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||||
|
info!("Opening archive file for reading");
|
||||||
let archive_file = fs::File::open(&archive_path)?;
|
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 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
|
// Call progress update with 100% after decompression
|
||||||
let filename = archive_path.file_name()
|
let filename = archive_path.file_name()
|
||||||
@ -58,9 +98,11 @@ impl ExtractionManager {
|
|||||||
callback(1.0, filename); // 100% done
|
callback(1.0, filename); // 100% done
|
||||||
});
|
});
|
||||||
|
|
||||||
|
info!("Extraction task completed successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}).await??;
|
}).await??;
|
||||||
|
|
||||||
|
info!("7z extraction process completed");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,53 +176,78 @@ impl ExtractionManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles extraction of all downloaded files to their appropriate locations
|
/// Handles extraction of all downloaded files to their appropriate locations
|
||||||
pub async fn install_all_files(
|
pub async fn install_all_files(
|
||||||
&self,
|
&self,
|
||||||
downloaded_files: &[PathBuf],
|
downloaded_files: &[PathBuf],
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
game_type: &str,
|
game_type: &GameType,
|
||||||
ee_exe_path: Option<&Path>
|
ee_exe_path: Option<&Path>
|
||||||
) -> Result<()> {
|
) -> 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
|
// Find and extract the main content files
|
||||||
let content_archive = downloaded_files.iter()
|
let content_archive = downloaded_files.iter()
|
||||||
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfar_all_files")))
|
.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")?;
|
.context("Content archive not found in downloaded files")?;
|
||||||
|
|
||||||
|
info!("Found content archive: {}", content_archive.display());
|
||||||
|
|
||||||
// Extract main content
|
// Extract main content
|
||||||
|
info!("Starting extraction of main content");
|
||||||
self.extract_7z(content_archive, install_path).await?;
|
self.extract_7z(content_archive, install_path).await?;
|
||||||
|
|
||||||
// Handle game-specific files
|
// Handle game-specific files
|
||||||
if game_type.to_lowercase() == "diamond" {
|
info!("Processing game-specific files");
|
||||||
// For Diamond, find and copy the launcher
|
match game_type {
|
||||||
let launcher = downloaded_files.iter()
|
GameType::Diamond => {
|
||||||
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarx.exe")))
|
info!("Installing Diamond edition launcher");
|
||||||
.context("Launcher executable not found in downloaded files")?;
|
// For Diamond, find and copy the launcher
|
||||||
|
let launcher = downloaded_files.iter()
|
||||||
// Copy launcher to install directory
|
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarx.exe")))
|
||||||
let target_path = install_path.join("sinfarx.exe");
|
.context("Launcher executable not found in downloaded files")?;
|
||||||
tokio::fs::copy(launcher, &target_path).await?;
|
|
||||||
|
// Copy launcher to install directory
|
||||||
} else if game_type.to_lowercase() == "enhancededition" || game_type.to_lowercase() == "ee" {
|
let target_path = install_path.join("sinfarx.exe");
|
||||||
// For EE, find the launcher zip
|
info!("Copying launcher from {} to {}", launcher.display(), target_path.display());
|
||||||
let launcher_zip = downloaded_files.iter()
|
tokio::fs::copy(launcher, &target_path).await?;
|
||||||
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarLauncher.zip")))
|
info!("Launcher copied successfully");
|
||||||
.context("Launcher ZIP not found in downloaded files")?;
|
}
|
||||||
|
GameType::EnhancedEdition => {
|
||||||
// Extract to the EE executable directory
|
info!("Installing Enhanced Edition launcher");
|
||||||
if let Some(ee_path) = ee_exe_path {
|
// For EE, find the launcher zip
|
||||||
// Create the bin/win32_8181 directory if it doesn't exist (Windows)
|
let launcher_zip = downloaded_files.iter()
|
||||||
// or bin/linux_8181 directory (Linux)
|
.find(|p| p.file_name().map_or(false, |f| f.to_str().unwrap_or("").contains("sinfarLauncher.zip")))
|
||||||
#[cfg(windows)]
|
.context("Launcher ZIP not found in downloaded files")?;
|
||||||
let extract_dir = ee_path.join("bin").join("win32_8181");
|
|
||||||
|
// Extract to the EE executable directory
|
||||||
#[cfg(unix)]
|
if let Some(ee_path) = ee_exe_path {
|
||||||
let extract_dir = ee_path.join("bin").join("linux_8181");
|
// Create the bin/win32_8181 directory if it doesn't exist (Windows)
|
||||||
|
// or bin/linux_8181 directory (Linux)
|
||||||
// Extract the launcher
|
#[cfg(windows)]
|
||||||
self.extract_zip(launcher_zip, &extract_dir).await?;
|
let extract_dir = ee_path.join("bin").join("win32_8181");
|
||||||
} else {
|
|
||||||
return Err(anyhow::anyhow!("EE executable path is required for EE installation"));
|
#[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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user