mirror of
https://gitea.tendokyu.moe/beerpsi/fsdecrypt.git
synced 2026-09-27 00:49:43 +03:00
feat: auto-extract base .app when only delta is provided
When no base VHD is found, fsdecrypt now also searches for the base
.app file ({game_id}_*_0.app) in the same directory, extracts its
VHD, and then merges the delta into it automatically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
371abf4e32
commit
6b06bc4e92
+66
-24
@@ -33,6 +33,8 @@ struct InputFile {
|
||||
/// Info about an extracted VHD, used for delta merge.
|
||||
struct ExtractedVhd {
|
||||
vhd_path: PathBuf,
|
||||
/// Original input .app file path (for resolving sibling files)
|
||||
input_path: PathBuf,
|
||||
sequence_number: u8,
|
||||
game_id: String,
|
||||
}
|
||||
@@ -367,6 +369,7 @@ fn main() -> Result<()> {
|
||||
std::str::from_utf8(&bootid.game_id)?.trim_end().to_string();
|
||||
extracted_vhds.push(ExtractedVhd {
|
||||
vhd_path,
|
||||
input_path: path.clone(),
|
||||
sequence_number: bootid.sequence_number,
|
||||
game_id,
|
||||
});
|
||||
@@ -406,45 +409,84 @@ fn main() -> Result<()> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If no base was extracted in this run, search the same directory for an existing one
|
||||
let found_base_path;
|
||||
// If no base was extracted in this run, search the same directory
|
||||
let found_base;
|
||||
let base = match base {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
// Look for a VHD matching "{game_id}_*_0.vhd" in the same directory as the delta
|
||||
let delta_dir = deltas[0]
|
||||
.vhd_path
|
||||
.input_path
|
||||
.parent()
|
||||
.unwrap_or_else(|| Path::new("."));
|
||||
let pattern_prefix = format!("{game_id}_");
|
||||
let pattern_suffix = "_0.vhd";
|
||||
|
||||
found_base_path = std::fs::read_dir(delta_dir)?
|
||||
// First, look for an existing base VHD ({game_id}_*_0.vhd)
|
||||
let existing_vhd = std::fs::read_dir(delta_dir)?
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.path())
|
||||
.find(|p| {
|
||||
let name = p.file_name().unwrap_or_default().to_string_lossy();
|
||||
name.starts_with(&pattern_prefix) && name.ends_with(pattern_suffix)
|
||||
name.starts_with(&pattern_prefix) && name.ends_with("_0.vhd")
|
||||
});
|
||||
|
||||
match &found_base_path {
|
||||
Some(path) => {
|
||||
println!("Found existing base VHD: {}", path.display());
|
||||
// Create a temporary ExtractedVhd to reference
|
||||
&ExtractedVhd {
|
||||
vhd_path: path.clone(),
|
||||
sequence_number: 0,
|
||||
game_id: game_id.clone(),
|
||||
if let Some(vhd_path) = existing_vhd {
|
||||
println!("Found existing base VHD: {}", vhd_path.display());
|
||||
found_base = ExtractedVhd {
|
||||
vhd_path,
|
||||
input_path: PathBuf::new(),
|
||||
sequence_number: 0,
|
||||
game_id: game_id.clone(),
|
||||
};
|
||||
&found_base
|
||||
} else {
|
||||
// Look for a base .app file ({game_id}_*_0.app) and extract it
|
||||
let base_app = std::fs::read_dir(delta_dir)?
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.path())
|
||||
.find(|p| {
|
||||
let name = p.file_name().unwrap_or_default().to_string_lossy();
|
||||
name.starts_with(&pattern_prefix) && name.ends_with("_0.app")
|
||||
});
|
||||
|
||||
match base_app {
|
||||
Some(app_path) => {
|
||||
println!(
|
||||
"Found base APP in same directory, extracting: {}",
|
||||
app_path.display()
|
||||
);
|
||||
// Read the bootid to get the sequence number
|
||||
let base_file = FscryptDecryptor::new(File::open(&app_path)?)
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
let base_seq = base_file.bootid.sequence_number;
|
||||
drop(base_file);
|
||||
|
||||
match extract_internal_vhd(&app_path, base_seq) {
|
||||
Ok(vhd_path) => {
|
||||
found_base = ExtractedVhd {
|
||||
vhd_path,
|
||||
input_path: app_path,
|
||||
sequence_number: 0,
|
||||
game_id: game_id.clone(),
|
||||
};
|
||||
&found_base
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"WARNING: Failed to extract base VHD: {e:#?}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
println!(
|
||||
"WARNING: Delta VHD(s) found for {game_id} but no base (seq=0) found."
|
||||
);
|
||||
println!(
|
||||
" Place the base .app or .vhd in the same directory."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
println!(
|
||||
"WARNING: Delta VHD(s) found for {game_id} but no base (seq=0) VHD found."
|
||||
);
|
||||
println!(
|
||||
" Provide the base .app file or place the base VHD in the same directory."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user