use std::path::Path;
use std::process::Command;
use uxn_tal_defined::v1::{EmulatorLauncher, ProtocolParseResult};
pub fn get_cached_canonical_orca_rom() -> Result<(std::path::PathBuf, std::path::PathBuf), String> {
use uxn_tal_common::hash_url;
use uxn_tal_defined::consts::CANONICAL_ORCA;
let url = CANONICAL_ORCA;
let roms_dir =
crate::paths::uxntal_roms_get_path().ok_or("Failed to get uxntal roms directory")?;
let cache_dir = roms_dir.join(format!("{}", hash_url(url)));
let orca_rom = cache_dir.join("orca.rom");
if !orca_rom.exists() {
return Err(format!(
"orca.rom not found in cache dir: {}",
orca_rom.display()
));
}
let metadata =
std::fs::metadata(&orca_rom).map_err(|e| format!("orca.rom metadata error: {e}"))?;
if metadata.len() == 0 {
return Err(format!(
"orca.rom is empty in cache dir: {}",
orca_rom.display()
));
}
Ok((orca_rom, cache_dir))
}
pub fn get_workspace_canonical_orca_rom() -> Result<(std::path::PathBuf, std::path::PathBuf), String>
{
let roms_dir = std::path::PathBuf::from("roms");
let orca_rom = roms_dir.join("orca.rom");
if !orca_rom.exists() {
return Err(format!(
"orca.rom not found in workspace roms dir: {}",
orca_rom.display()
));
}
let metadata =
std::fs::metadata(&orca_rom).map_err(|e| format!("orca.rom metadata error: {e}"))?;
if metadata.len() == 0 {
return Err(format!(
"orca.rom is empty in workspace roms dir: {}",
orca_rom.display()
));
}
Ok((orca_rom, roms_dir))
}
pub fn resolve_canonical_orca_rom() -> Result<(std::path::PathBuf, std::path::PathBuf), String> {
if let Ok(pair) = get_cached_canonical_orca_rom() {
return Ok(pair);
}
use uxn_tal_common::cache::RomEntryResolver;
use uxn_tal_defined::consts::CANONICAL_ORCA;
let entry_resolver = crate::util::RealRomEntryResolver;
let (tal_path, cache_dir) = entry_resolver
.resolve_entry_and_cache_dir(CANONICAL_ORCA)
.map_err(|e| format!("Failed to resolve canonical orca: {e}"))?;
let orca_rom = cache_dir.join("orca.rom");
if !orca_rom.exists() {
let prev_dir =
std::env::current_dir().map_err(|e| format!("Failed to get current dir: {e}"))?;
let set_dir = std::env::set_current_dir(&cache_dir);
let rom_bytes = match set_dir {
Ok(_) => {
let result = crate::assemble_file(&tal_path)
.map_err(|e| format!("Failed to assemble canonical orca.tal: {e}"));
let _ = std::env::set_current_dir(&prev_dir);
result?
}
Err(e) => {
return Err(format!("Failed to set current dir to cache dir: {e}"));
}
};
std::fs::write(&orca_rom, &rom_bytes)
.map_err(|e| format!("Failed to write canonical orca.rom: {e}"))?;
}
let metadata =
std::fs::metadata(&orca_rom).map_err(|e| format!("orca.rom metadata error: {e}"))?;
if metadata.len() == 0 {
return Err(format!(
"orca.rom is empty in cache dir: {}",
orca_rom.display()
));
}
Ok((orca_rom, cache_dir))
}
pub fn handle_orca_mode(
result: &ProtocolParseResult,
user_rom_path: &str,
mapper: &dyn EmulatorLauncher,
emulator_path: &Path,
working_dir: Option<&Path>,
) -> Result<Command, Box<dyn std::error::Error>> {
let (canonical_orca_rom, _canonical_cache_dir) = resolve_canonical_orca_rom()?;
let mut cmd = mapper.build_command(
result,
&canonical_orca_rom.display().to_string(),
emulator_path,
working_dir,
);
cmd.arg(user_rom_path);
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
Ok(cmd)
}
pub fn handle_orca_file_with_protocol(
result: &ProtocolParseResult,
canon_input_p: &Path,
verbose: bool,
) -> Result<(), String> {
use uxn_tal_defined::get_emulator_launcher;
use uxn_tal_defined::v1::ProtocolVarVar;
let (mapper, emulator_path) =
get_emulator_launcher(result).ok_or("Failed to get emulator launcher")?;
let orca_dir = canon_input_p.parent().unwrap_or_else(|| Path::new("."));
let rel_orca = match canon_input_p.strip_prefix(orca_dir) {
Ok(rel) => rel.display().to_string(),
Err(_) => canon_input_p.display().to_string(),
};
if let Some(ProtocolVarVar::Bool(true)) = result.proto_vars.get("orca") {
let cmd = handle_orca_mode(
result,
&rel_orca,
mapper.as_ref(),
&emulator_path,
Some(orca_dir),
)
.map_err(|e| format!("Failed to setup orca mode: {}", e))?;
crate::emulator_utils::spawn_emulator_with_timeout(
mapper.as_ref(),
result,
cmd,
&emulator_path,
verbose,
)?;
} else {
let mut cmd = mapper.build_command(result, &rel_orca, &emulator_path, Some(orca_dir));
cmd.current_dir(orca_dir);
crate::emulator_utils::spawn_emulator_with_timeout(
mapper.as_ref(),
result,
cmd,
&emulator_path,
verbose,
)?;
}
Ok(())
}