1use std::path::{Path, PathBuf};
2
3use crate::{GameInfoProvider, PackFile};
4
5pub struct DummyVpk;
8
9impl PackFile for DummyVpk {
10 fn open<P: AsRef<Path>>(_path: P) -> Option<Self> {
11 None
13 }
14
15 fn has_entry(&self, _path: &str) -> bool {
16 false
17 }
18
19 fn read_entry(&self, _path: &str) -> Option<Vec<u8>> {
20 None
21 }
22}
23
24pub struct SimpleGameInfo;
26
27impl GameInfoProvider for SimpleGameInfo {
28 fn get_search_paths<P: AsRef<Path>>(path: P) -> Option<Vec<(String, String)>> {
29 let content = std::fs::read_to_string(&path).ok()?;
30 let mut paths = Vec::new();
31 let mut in_search_paths = false;
32
33 for line in content.lines() {
36 let line = line.trim().to_lowercase();
37
38 if line.contains("\"searchpaths\"") || line.contains("searchpaths") {
39 in_search_paths = true;
40 continue;
41 }
42
43 if in_search_paths {
44 if line == "}" {
45 break;
46 }
47
48 if line == "{" || line.is_empty() || line.starts_with("//") {
49 continue;
50 }
51
52 let parts: Vec<&str> = line
53 .trim()
54 .splitn(2, char::is_whitespace)
55 .map(|s| s.trim_start().trim_matches('"'))
56 .collect();
57
58 if parts.len() >= 2 {
59 paths.push((parts[0].to_string(), parts[1].to_string()));
60 }
61 }
62 }
63
64 if paths.is_empty() {
65 None
66 } else {
67 Some(paths)
68 }
69 }
70}
71
72pub struct P2GameInfo;
76
77impl GameInfoProvider for P2GameInfo {
78 fn get_search_paths<P: AsRef<Path>>(path: P) -> Option<Vec<(String, String)>> {
79 let mut paths = vec![];
80
81 let game_path_str = path.as_ref()
83 .ancestors()
84 .nth(2)
85 .map(|p| p.to_string_lossy().into_owned())
86 .unwrap_or_default();
87 let game_path = PathBuf::from(&game_path_str);
88
89 for idx in 1..99 {
90 let dlc_name = format!("portal2_dlc{}/", idx);
91 let dlc_path = game_path.join(&dlc_name);
92 if !dlc_path.exists() {
93 break
94 }
95 paths.push(("game".to_string(), dlc_name));
97 }
98
99 paths.reverse();
100 paths.extend(SimpleGameInfo::get_search_paths(&path)?);
101
102 Some(paths)
103 }
104}
105
106