1use crate::archive::{RpfEntry, RpfEntryKind};
2
3#[derive(Debug, Clone)]
5pub struct DirNode {
6 pub name : String,
7 pub path : String,
8 pub files : Vec<FileRef>,
9 pub subdirs: Vec<DirNode>,
10}
11
12#[derive(Debug, Clone)]
14pub struct FileRef {
15 pub name : String,
16 pub path : String,
17 pub entry_index : usize, pub size : u32, pub mem_size : u32, pub is_resource : bool,
21}
22
23pub fn build_directory_tree(entries: &[RpfEntry]) -> DirNode {
25 if entries.is_empty() {
26 return DirNode { name: String::new(), path: String::new(), files: vec![], subdirs: vec![] };
27 }
28
29 let (root_name, start, count) = if let RpfEntryKind::Directory { entries_index, entries_count } = &entries[0].kind {
31 (entries[0].name.clone(), *entries_index as usize, *entries_count as usize)
32 } else {
33 return flat_root(entries);
35 };
36
37 let mut root = DirNode { name: root_name, path: String::new(), files: vec![], subdirs: vec![] };
38 populate(&mut root, entries, start, count, "");
39 root
40}
41
42pub fn list_all_files(node: &DirNode) -> Vec<&FileRef> {
44 let mut out = Vec::new();
45 collect(node, &mut out);
46 out
47}
48
49fn populate(dir: &mut DirNode, entries: &[RpfEntry], start: usize, count: usize, parent_path: &str) {
52 let end = (start + count).min(entries.len());
53 for i in start..end {
54 let entry = &entries[i];
55 match &entry.kind {
56 RpfEntryKind::Directory { entries_index, entries_count } => {
57 let sub_path = child_path(parent_path, &entry.name_lower);
58 let mut sub = DirNode { name: entry.name.clone(), path: sub_path.clone(), files: vec![], subdirs: vec![] };
59 populate(&mut sub, entries, *entries_index as usize, *entries_count as usize, &sub_path);
60 dir.subdirs.push(sub);
61 }
62 RpfEntryKind::BinaryFile { file_size, uncompressed_size, .. } => {
63 let path = child_path(parent_path, &entry.name_lower);
64 dir.files.push(FileRef {
65 name: entry.name.clone(), path, entry_index: i,
66 size: *file_size, mem_size: *uncompressed_size, is_resource: false,
67 });
68 }
69 RpfEntryKind::ResourceFile { file_size, system_flags, .. } => {
70 let path = child_path(parent_path, &entry.name_lower);
71 let mem_size = crate::archive::resource_size_from_flags(*system_flags) as u32;
72 dir.files.push(FileRef {
73 name: entry.name.clone(), path, entry_index: i,
74 size: *file_size, mem_size, is_resource: true,
75 });
76 }
77 }
78 }
79}
80
81fn flat_root(entries: &[RpfEntry]) -> DirNode {
82 let mut root = DirNode { name: String::new(), path: String::new(), files: vec![], subdirs: vec![] };
83 for (i, entry) in entries.iter().enumerate() {
84 match &entry.kind {
85 RpfEntryKind::BinaryFile { file_size, uncompressed_size, .. } => {
86 root.files.push(FileRef {
87 name: entry.name.clone(), path: entry.name_lower.clone(), entry_index: i,
88 size: *file_size, mem_size: *uncompressed_size, is_resource: false,
89 });
90 }
91 RpfEntryKind::ResourceFile { file_size, system_flags, .. } => {
92 let mem_size = crate::archive::resource_size_from_flags(*system_flags) as u32;
93 root.files.push(FileRef {
94 name: entry.name.clone(), path: entry.name_lower.clone(), entry_index: i,
95 size: *file_size, mem_size, is_resource: true,
96 });
97 }
98 _ => {}
99 }
100 }
101 root
102}
103
104fn collect<'a>(node: &'a DirNode, out: &mut Vec<&'a FileRef>) {
105 out.extend(node.files.iter());
106 for sub in &node.subdirs { collect(sub, out); }
107}
108
109fn child_path(parent: &str, name: &str) -> String {
110 if parent.is_empty() { name.to_string() } else { format!("{}/{}", parent, name) }
111}