emu_runner/contexts/
gens.rs

1use camino::Utf8PathBuf;
2use crate::{EmulatorContext, Error};
3use crate::includes::copy_if_different;
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6pub enum GensVersion {
7    Ver11A,
8    Ver11B,
9    GitA2425B5,
10}
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct GensContext {
14    pub version: GensVersion,
15    pub start_paused: bool,
16    pub rom: Option<Utf8PathBuf>,
17    pub movie: Option<Utf8PathBuf>,
18    pub lua: Option<Utf8PathBuf>,
19    pub working_dir: Utf8PathBuf,
20}
21impl EmulatorContext for GensContext {
22    fn cmd_name(&self) -> String {
23        #[cfg(target_family = "unix")]
24        { "wine".into() }
25        
26        #[cfg(target_family = "windows")]
27        { "Gens.exe".into() }
28    }
29    
30    fn args(&self) -> Vec<String> {
31        let mut args = Vec::with_capacity(5);
32        
33        #[cfg(target_family = "unix")]
34        {
35            let mut executable = self.working_dir.clone();
36            executable.push("Gens.exe");
37            args.push(executable.to_string());
38        }
39        
40        use GensVersion::*;
41        match self.version {
42            Ver11A | Ver11B | GitA2425B5 => { // TODO: verify for correctness
43                if self.start_paused {
44                    args.push("-pause".into());
45                    args.push("0".into());
46                }
47                if let Some(rom) = self.rom.as_ref() {
48                    args.push("-rom".into());
49                    args.push(rom.to_string());
50                }
51                if let Some(movie) = self.movie.as_ref() {
52                    args.push("-play".into());
53                    args.push(movie.to_string());
54                }
55                if let Some(lua) = self.lua.as_ref() {
56                    args.push("-lua".into());
57                    args.push(lua.to_string());
58                }
59            },
60        }
61        
62        args
63    }
64    
65    fn env(&self) -> Vec<(String, String)> {
66        let mut vars = vec![];
67
68        #[cfg(target_family = "unix")]
69        {
70            let mut prefix = self.working_dir.clone();
71            prefix.push(".wine/");
72            
73            vars.push(("WINEPREFIX".into(), prefix.to_string()));
74        }
75        
76        vars
77    }
78    
79    fn prepare(&mut self) -> Result<(), Error> {
80        // Gens has inconsistent requirements for where files exist
81        
82        if let Some(rom) = self.rom.as_ref() {
83            if !rom.is_file() {
84                return Err(Error::MissingRom(rom.clone()));
85            }
86            let mut dest = self.working_dir.clone();
87            dest.push(rom.file_name().unwrap());
88            
89            copy_if_different(&std::fs::read(rom)?, dest)?;
90            
91            self.rom = Some(rom.file_name().unwrap().into());
92        }
93        if let Some(movie) = self.movie.as_ref() {
94            if !movie.is_file() {
95                return Err(Error::MissingMovie(movie.clone()));
96            }
97            
98            // movie path can be outside working dir, but must be absolute
99            if !movie.is_absolute() {
100                let mut dest = self.working_dir.clone();
101                dest.push(movie.file_name().unwrap());
102                
103                copy_if_different(&std::fs::read(movie)?, dest)?;
104                
105                self.movie = Some(movie.file_name().unwrap().into());
106            }
107        }
108        if let Some(lua) = self.lua.as_ref() {
109            if !lua.is_file() {
110                return Err(Error::MissingLua(lua.clone()));
111            }
112            
113            // lua path can be outside working dir, but must be absolute
114            if !lua.is_absolute() {
115                let mut dest = self.working_dir.clone();
116                dest.push(lua.file_name().unwrap());
117                
118                copy_if_different(&std::fs::read(lua)?, dest)?;
119                
120                self.lua = Some(lua.file_name().unwrap().into());
121            }
122        }
123        
124        Ok(())
125    }
126    
127    fn working_dir(&self) -> Utf8PathBuf {
128        self.working_dir.clone()
129    }
130}
131impl GensContext {
132    /// Creates a new Context with default options.
133    /// 
134    /// If the path does not point to a directory, or a file within a directory, which contains `Gens.exe`,
135    /// an error message will be returned.
136    pub fn new<P: Into<Utf8PathBuf>>(working_dir: P, version: GensVersion) -> Result<Self, Error> {
137        let mut working_dir = working_dir.into();
138        if working_dir.is_file() {
139            working_dir.pop();
140        }
141        
142        working_dir = working_dir.canonicalize_utf8().unwrap_or(working_dir);
143        
144        let mut detect_exe = working_dir.clone();
145        detect_exe.push("Gens.exe");
146        if working_dir.is_file() || !working_dir.exists() || !detect_exe.is_file() {
147            return Err(Error::MissingExecutable(detect_exe));
148        }
149        
150        Ok(Self {
151            version,
152            start_paused: false,
153            rom: None,
154            movie: None,
155            lua: None,
156            working_dir,
157        })
158    }
159    
160    pub fn with_pause(self, start_paused: bool) -> Self {
161        Self {
162            start_paused,
163            ..self
164        }
165    }
166    
167    pub fn with_rom<P: Into<Utf8PathBuf>>(self, rom: P) -> Self {
168        Self {
169            rom: Some(rom.into()),
170            ..self
171        }
172    }
173    
174    pub fn with_movie<P: Into<Utf8PathBuf>>(self, movie: P) -> Self {
175        Self {
176            movie: Some(movie.into()),
177            ..self
178        }
179    }
180    
181    pub fn with_lua<P: Into<Utf8PathBuf>>(self, lua: P) -> Self {
182        Self {
183            lua: Some(lua.into()),
184            ..self
185        }
186    }
187}