emu_runner/contexts/
fceux.rs1use camino::Utf8PathBuf;
2use crate::{EmulatorContext, Error};
3use crate::includes::copy_if_different;
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct FceuxContext {
7 pub config: Option<Utf8PathBuf>,
8 pub movie: Option<Utf8PathBuf>,
9 pub lua: Option<Utf8PathBuf>,
10 pub rom: Option<Utf8PathBuf>,
11
12 pub ppu_mode: Option<bool>,
16 pub working_dir: Utf8PathBuf,
17}
18impl EmulatorContext for FceuxContext {
19 fn cmd_name(&self) -> String {
20 #[cfg(target_family = "unix")]
21 {
22 match self.determine_executable() {
23 Some(exe) if exe == "fceux" => "./fceux".into(),
24 Some(_) => "wine".into(),
25 None => "./fceux".into(),
26 }
27 }
28
29 #[cfg(target_family = "windows")]
30 {
31 match self.determine_executable() {
32 Some(exe) => exe,
33 None => "fceux.exe".into()
34 }
35 }
36 }
37
38 fn args(&self) -> Vec<String> {
39 let mut args = Vec::with_capacity(5);
40
41 #[cfg(target_family = "unix")]
42 {
43 if self.cmd_name() == "wine" {
44 args.push(self.determine_executable().unwrap());
45 }
46 }
47
48 match self.determine_executable() {
49 Some(exe) => match exe.as_str() {
50 "fceux.exe" | "fceux64.exe" => {
51 if let Some(config) = self.config.as_ref() {
52 args.push("-cfg".into());
53 args.push(config.to_string());
54 }
55 if let Some(movie) = self.movie.as_ref() {
56 args.push("-playmovie".into());
57 args.push(movie.to_string());
58 }
59 if let Some(lua) = self.lua.as_ref() {
60 args.push("-lua".into());
61 args.push(lua.to_string());
62 }
63 },
64 "fceux" | "qfceux.exe" => {
65 if let Some(movie) = self.movie.as_ref() {
66 args.push("--playmov".into());
67 args.push(movie.to_string());
68 }
69 if let Some(lua) = self.lua.as_ref() {
70 args.push("--loadlua".into());
71 args.push(lua.to_string());
72 }
73 if let Some(ppu_mode) = self.ppu_mode.as_ref() {
74 args.push("--newppu".into());
75 args.push(if *ppu_mode { "1".into() } else { "0".into() });
76 }
77 },
78 _ => ()
79 },
80 None => ()
81 }
82
83 if let Some(rom) = self.rom.as_ref() {
84 args.push(rom.to_string());
85 }
86
87 args
88 }
89
90 fn env(&self) -> Vec<(String, String)> {
91 let mut vars = vec![];
92
93 #[cfg(target_family = "unix")]
94 {
95 if self.cmd_name() == "wine" {
96 let mut prefix = self.working_dir();
97 prefix.push(".wine/");
98
99 vars.push(("WINEPREFIX".into(), prefix.to_string()));
100 }
101 }
102
103 let mut home = self.working_dir();
104 home.push(".fceux/");
105 vars.push(("HOME".into(), home.to_string()));
106
107 vars
108 }
109
110 fn prepare(&mut self) -> Result<(), Error> {
111 #[cfg(target_family = "windows")]
118 {
119 if cmd_name == "./fceux" {
120 return Err(Error::IncompatibleOSVersion);
121 }
122 }
123
124 if let Some(config) = self.config.as_ref() {
125 if !config.is_file() {
132 return Err(Error::MissingConfig(config.clone()));
133 }
134
135 if let Some(config) = self.config.as_ref() {
136 if !config.is_file() {
137 return Err(Error::MissingConfig(config.clone()));
138 }
139
140 if let Some(exe) = self.determine_executable() {
141 let mut dest = self.working_dir();
142 if exe == "fceux" {
143 dest.push(".fceux/");
144 if !dest.is_dir() {
145 std::fs::create_dir_all(&dest)?;
146 }
147 dest.push("fceux.cfg");
148
149 copy_if_different(&std::fs::read(config)?, dest)?;
150 } else if exe == "qfceux.exe" {
151 dest.push("fceux.cfg");
152
153 copy_if_different(&std::fs::read(config)?, dest)?;
154 } else if !config.is_absolute() {
155 return Err(Error::AbsolutePathFailed);
156 }
157 }
158 }
159 }
160 if let Some(movie) = self.movie.as_ref() {
161 if !movie.is_file() {
162 return Err(Error::MissingMovie(movie.clone()));
163 }
164 if !movie.is_absolute() {
165 return Err(Error::AbsolutePathFailed);
166 }
167 }
168 if let Some(lua) = self.lua.as_ref() {
169 if !lua.is_file() {
170 return Err(Error::MissingLua(lua.clone()));
171 }
172 if !lua.is_absolute() {
173 return Err(Error::AbsolutePathFailed);
174 }
175 }
176 if let Some(rom) = self.rom.as_ref() {
177 if !rom.is_file() {
178 return Err(Error::MissingRom(rom.clone()));
179 }
180 if !rom.is_absolute() {
181 return Err(Error::AbsolutePathFailed);
182 }
183 }
184
185 Ok(())
186 }
187
188 fn working_dir(&self) -> Utf8PathBuf {
189 self.working_dir.clone()
190 }
191}
192impl FceuxContext {
193 pub fn new<P: Into<Utf8PathBuf>>(working_dir: P) -> Result<Self, Error> {
198 let mut working_dir = working_dir.into();
199 if working_dir.is_file() {
200 working_dir.pop();
201 }
202
203 working_dir = working_dir.canonicalize_utf8().unwrap_or(working_dir);
204
205 if working_dir.is_file() || !working_dir.exists() {
206 return Err(Error::MissingExecutable(working_dir));
207 }
208
209 let mut found = false;
210 for exe in ["fceux.exe", "fceux64.exe", "qfceux.exe", "fceux"] {
211 let mut path = working_dir.clone();
212 path.push(exe);
213
214 if path.is_file() {
215 found = true;
216 break;
217 }
218 }
219 if !found {
220 let mut path = working_dir.clone();
221 path.push("fceux");
222 return Err(Error::MissingExecutable(path));
223 }
224
225 Ok(Self {
226 config: None,
227 movie: None,
228 lua: None,
229 rom: None,
230 ppu_mode: None,
231 working_dir,
232 })
233 }
234
235 pub fn with_config<P: Into<Utf8PathBuf>>(self, config: P) -> Self {
236 let config = config.into();
237 Self {
238 config: Some(config.canonicalize_utf8().unwrap_or_else(|_| config)),
239 ..self
240 }
241 }
242
243 pub fn with_movie<P: Into<Utf8PathBuf>>(self, movie: P) -> Self {
244 let movie = movie.into();
245 Self {
246 movie: Some(movie.canonicalize_utf8().unwrap_or_else(|_| movie)),
247 ..self
248 }
249 }
250
251 pub fn with_lua<P: Into<Utf8PathBuf>>(self, lua: P) -> Self {
252 let lua = lua.into();
253 Self {
254 lua: Some(lua.canonicalize_utf8().unwrap_or_else(|_| lua)),
255 ..self
256 }
257 }
258
259 pub fn with_rom<P: Into<Utf8PathBuf>>(self, rom: P) -> Self {
260 let rom = rom.into();
261 Self {
262 rom: Some(rom.canonicalize_utf8().unwrap_or_else(|_| rom)),
263 ..self
264 }
265 }
266
267 pub fn with_ppu_mode(self, ppu_mode: bool) -> Self {
268 Self {
269 ppu_mode: Some(ppu_mode),
270 ..self
271 }
272 }
273
274 pub fn determine_executable(&self) -> Option<String> {
275 let mut path = self.working_dir();
276 path.push("fceux");
277 if path.is_file() {
278 return Some("fceux".into())
279 }
280
281 path.pop();
282 path.push("fceux.exe");
283 if path.is_file() {
284 return Some("fceux.exe".into())
285 }
286
287 path.pop();
288 path.push("fceux64.exe");
289 if path.is_file() {
290 return Some("fceux64.exe".into())
291 }
292
293 path.pop();
294 path.push("qfceux.exe");
295 if path.is_file() {
296 return Some("qfceux.exe".into())
297 }
298
299 None
300 }
301}