launcher/utils/
texture_loader.rs1use eframe::egui;
2use std::path::{Path, PathBuf};
3
4pub fn load_texture_robust(
10 ctx: &egui::Context,
11 path: &Path,
12 name: &str,
13) -> Result<egui::TextureHandle, Box<dyn std::error::Error>> {
14 let actual_path = find_texture_file(path).ok_or_else(|| {
16 std::io::Error::new(
17 std::io::ErrorKind::NotFound,
18 format!("Texture not found: {}", path.display()),
19 )
20 })?;
21
22 let file_data = std::fs::read(&actual_path).map_err(|e| {
24 eprintln!("Failed to read texture file {}: {e}", actual_path.display());
25 e
26 })?;
27
28 let image_result = image::load_from_memory(&file_data).or_else(|_| {
30 let format = if actual_path
31 .extension()
32 .is_some_and(|e| e.eq_ignore_ascii_case("tga"))
33 {
34 image::ImageFormat::Tga
35 } else {
36 image::ImageFormat::from_path(&actual_path).unwrap_or(image::ImageFormat::Tga)
37 };
38 image::load_from_memory_with_format(&file_data, format)
39 });
40
41 let color_image = image_result
42 .map(|img| {
43 let size = [img.width() as usize, img.height() as usize];
44 let pixels = img.to_rgba8();
45 egui::ColorImage::from_rgba_unmultiplied(size, pixels.as_flat_samples().as_slice())
46 })
47 .map_err(|e| {
48 eprintln!("Failed to decode texture {}: {e}", actual_path.display());
49 e
50 })?;
51
52 Ok(ctx.load_texture(name, color_image, egui::TextureOptions::LINEAR))
53}
54
55#[must_use]
57pub fn find_texture_file(target_path: &Path) -> Option<PathBuf> {
58 let target_filename = target_path
59 .file_name()
60 .and_then(|n| n.to_str())?
61 .to_lowercase();
62
63 if let Some(parent) = target_path.parent()
65 && parent.exists()
66 {
67 let mut candidates = Vec::new();
68 collect_candidates_recursive(parent, &target_filename, &mut candidates);
69
70 if !candidates.is_empty() {
71 candidates.sort_by_key(|b| std::cmp::Reverse(b.file_size));
73 return Some(candidates[0].path.clone());
74 }
75 }
76
77 None
78}
79
80struct FileCandidate {
81 path: PathBuf,
82 file_size: u64,
83}
84
85fn collect_candidates_recursive(
86 dir: &Path,
87 target_filename: &str,
88 candidates: &mut Vec<FileCandidate>,
89) {
90 if let Ok(entries) = std::fs::read_dir(dir) {
91 for entry in entries.flatten() {
92 let path = entry.path();
93 if path.is_dir() {
94 collect_candidates_recursive(&path, target_filename, candidates);
95 } else if path.is_file()
96 && let Some(filename) = path.file_name().and_then(|n| n.to_str())
97 && filename.to_lowercase() == target_filename
98 && let Ok(metadata) = path.metadata()
99 {
100 candidates.push(FileCandidate {
101 path,
102 file_size: metadata.len(),
103 });
104 }
105 }
106 }
107}