1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #ifndef OSYSTEM_HXX |
19 | #define OSYSTEM_HXX |
20 | |
21 | class Console; |
22 | class FrameBuffer; |
23 | class EventHandler; |
24 | class Properties; |
25 | class PropertiesSet; |
26 | class Random; |
27 | class Sound; |
28 | class StateManager; |
29 | class TimerManager; |
30 | class EmulationWorker; |
31 | class AudioSettings; |
32 | #ifdef CHEATCODE_SUPPORT |
33 | class CheatManager; |
34 | #endif |
35 | #ifdef DEBUGGER_SUPPORT |
36 | class Debugger; |
37 | #endif |
38 | #ifdef GUI_SUPPORT |
39 | class CommandMenu; |
40 | class Launcher; |
41 | class ; |
42 | class TimeMachine; |
43 | class VideoDialog; |
44 | #endif |
45 | #ifdef PNG_SUPPORT |
46 | class PNGLibrary; |
47 | #endif |
48 | #ifdef SQLITE_SUPPORT |
49 | class SettingsDb; |
50 | #endif |
51 | |
52 | #include <chrono> |
53 | |
54 | #include "FSNode.hxx" |
55 | #include "FrameBufferConstants.hxx" |
56 | #include "EventHandlerConstants.hxx" |
57 | #include "FpsMeter.hxx" |
58 | #include "Settings.hxx" |
59 | #include "Logger.hxx" |
60 | #include "bspf.hxx" |
61 | #include "repository/KeyValueRepository.hxx" |
62 | |
63 | /** |
64 | This class provides an interface for accessing operating system specific |
65 | functions. It also comprises an overall parent object, to which all the |
66 | other objects belong. |
67 | |
68 | @author Stephen Anthony |
69 | */ |
70 | class OSystem |
71 | { |
72 | friend class EventHandler; |
73 | |
74 | public: |
75 | OSystem(); |
76 | virtual ~OSystem(); |
77 | |
78 | /** |
79 | Create all child objects which belong to this OSystem |
80 | */ |
81 | virtual bool create(); |
82 | |
83 | /** |
84 | Creates the various framebuffers/renderers available in this system. |
85 | Note that it will only create one type per run of Stella. |
86 | |
87 | @return Success or failure of the framebuffer creation |
88 | */ |
89 | FBInitStatus createFrameBuffer(); |
90 | |
91 | public: |
92 | /** |
93 | Get the event handler of the system. |
94 | |
95 | @return The event handler |
96 | */ |
97 | EventHandler& eventHandler() const { return *myEventHandler; } |
98 | |
99 | /** |
100 | Get the frame buffer of the system. |
101 | |
102 | @return The frame buffer |
103 | */ |
104 | FrameBuffer& frameBuffer() const { return *myFrameBuffer; } |
105 | |
106 | /** |
107 | Get the sound object of the system. |
108 | |
109 | @return The sound object |
110 | */ |
111 | Sound& sound() const { return *mySound; } |
112 | |
113 | /** |
114 | Get the settings object of the system. |
115 | |
116 | @return The settings object |
117 | */ |
118 | Settings& settings() const { return *mySettings; } |
119 | |
120 | /** |
121 | Get the random object of the system. |
122 | |
123 | @return The random object |
124 | */ |
125 | Random& random() const { return *myRandom; } |
126 | |
127 | /** |
128 | Get the set of game properties for the system. |
129 | |
130 | @return The properties set object |
131 | */ |
132 | PropertiesSet& propSet() const { return *myPropSet; } |
133 | |
134 | /** |
135 | Get the console of the system. The console won't always exist, |
136 | so we should test if it's available. |
137 | |
138 | @return The console object |
139 | */ |
140 | Console& console() const { return *myConsole; } |
141 | bool hasConsole() const; |
142 | |
143 | /** |
144 | Get the audio settings object of the system. |
145 | |
146 | @return The audio settings object |
147 | */ |
148 | AudioSettings& audioSettings() { return *myAudioSettings; } |
149 | |
150 | /** |
151 | Get the state manager of the system. |
152 | |
153 | @return The statemanager object |
154 | */ |
155 | StateManager& state() const { return *myStateManager; } |
156 | |
157 | /** |
158 | Get the timer/callback manager of the system. |
159 | |
160 | @return The timermanager object |
161 | */ |
162 | TimerManager& timer() const { return *myTimerManager; } |
163 | |
164 | /** |
165 | This method should be called to initiate the process of loading settings |
166 | from the config file. It takes care of loading settings, applying |
167 | commandline overrides, and finally validating all settings. |
168 | */ |
169 | void loadConfig(const Settings::Options& options); |
170 | |
171 | /** |
172 | This method should be called to save the current settings. It first asks |
173 | each subsystem to update its settings, then it saves all settings to the |
174 | config file. |
175 | */ |
176 | void saveConfig(); |
177 | |
178 | #ifdef CHEATCODE_SUPPORT |
179 | /** |
180 | Get the cheat manager of the system. |
181 | |
182 | @return The cheatmanager object |
183 | */ |
184 | CheatManager& cheat() const { return *myCheatManager; } |
185 | #endif |
186 | |
187 | #ifdef DEBUGGER_SUPPORT |
188 | /** |
189 | Get the ROM debugger of the system. |
190 | |
191 | @return The debugger object |
192 | */ |
193 | Debugger& debugger() const { return *myDebugger; } |
194 | #endif |
195 | |
196 | #ifdef GUI_SUPPORT |
197 | /** |
198 | Get the settings menu of the system. |
199 | |
200 | @return The settings menu object |
201 | */ |
202 | Menu& () const { return *myMenu; } |
203 | |
204 | /** |
205 | Get the command menu of the system. |
206 | |
207 | @return The command menu object |
208 | */ |
209 | CommandMenu& commandMenu() const { return *myCommandMenu; } |
210 | |
211 | /** |
212 | Get the ROM launcher of the system. |
213 | |
214 | @return The launcher object |
215 | */ |
216 | Launcher& launcher() const { return *myLauncher; } |
217 | |
218 | /** |
219 | Get the time machine of the system (manages state files). |
220 | |
221 | @return The time machine object |
222 | */ |
223 | TimeMachine& timeMachine() const { return *myTimeMachine; } |
224 | #endif |
225 | |
226 | #ifdef PNG_SUPPORT |
227 | /** |
228 | Get the PNG handler of the system. |
229 | |
230 | @return The PNGlib object |
231 | */ |
232 | PNGLibrary& png() const { return *myPNGLib; } |
233 | #endif |
234 | |
235 | /** |
236 | Set all config file paths for the OSystem. |
237 | */ |
238 | void setConfigPaths(); |
239 | |
240 | /** |
241 | Return the default full/complete directory name for storing data. |
242 | */ |
243 | const string& baseDir() const { return myBaseDir; } |
244 | |
245 | /** |
246 | Return the full/complete directory name for storing state files. |
247 | */ |
248 | const string& stateDir() const { return myStateDir; } |
249 | |
250 | /** |
251 | Return the full/complete directory name for storing nvram |
252 | (flash/EEPROM) files. |
253 | */ |
254 | const string& nvramDir() const { return myNVRamDir; } |
255 | |
256 | #ifdef CHEATCODE_SUPPORT |
257 | /** |
258 | This method should be called to get the full path of the cheat file. |
259 | |
260 | @return String representing the full path of the cheat filename. |
261 | */ |
262 | const string& cheatFile() const { return myCheatFile; } |
263 | #endif |
264 | |
265 | #ifdef PNG_SUPPORT |
266 | /** |
267 | Return the full/complete directory name for saving and loading |
268 | PNG snapshots. |
269 | */ |
270 | const string& snapshotSaveDir() const { return mySnapshotSaveDir; } |
271 | const string& snapshotLoadDir() const { return mySnapshotLoadDir; } |
272 | #endif |
273 | |
274 | /** |
275 | This method should be called to get the full path of the |
276 | (optional) palette file. |
277 | |
278 | @return String representing the full path of the properties filename. |
279 | */ |
280 | const string& paletteFile() const { return myPaletteFile; } |
281 | |
282 | /** |
283 | This method should be called to get the full path of the currently |
284 | loaded ROM. |
285 | |
286 | @return FSNode object representing the ROM file. |
287 | */ |
288 | const FilesystemNode& romFile() const { return myRomFile; } |
289 | |
290 | /** |
291 | The default locations for saving and loading various files that |
292 | don't already have a specific location. |
293 | */ |
294 | const string& defaultSaveDir() const { return myDefaultSaveDir; } |
295 | const string& defaultLoadDir() const { return myDefaultLoadDir; } |
296 | |
297 | /** |
298 | Open the given ROM and return an array containing its contents. |
299 | Also, the properties database is updated with a valid ROM name |
300 | for this ROM (if necessary). |
301 | |
302 | @param rom The file node of the ROM to open (contains path) |
303 | @param md5 The md5 calculated from the ROM file |
304 | (will be recalculated if necessary) |
305 | @param size The amount of data read into the image array |
306 | |
307 | @return Unique pointer to the array |
308 | */ |
309 | ByteBuffer openROM(const FilesystemNode& rom, string& md5, uInt32& size); |
310 | |
311 | /** |
312 | Creates a new game console from the specified romfile, and correctly |
313 | initializes the system state to start emulation of the Console. |
314 | |
315 | @param rom The FSNode of the ROM to use (contains path, etc) |
316 | @param md5 The MD5sum of the ROM |
317 | @param newrom Whether this is a new ROM, or a reload of current one |
318 | |
319 | @return String indicating any error message (EmptyString for no errors) |
320 | */ |
321 | string createConsole(const FilesystemNode& rom, const string& md5 = "" , |
322 | bool newrom = true); |
323 | |
324 | /** |
325 | Reloads the current console (essentially deletes and re-creates it). |
326 | This can be thought of as a real console off/on toggle. |
327 | |
328 | @return True on successful creation, otherwise false |
329 | */ |
330 | bool reloadConsole(); |
331 | |
332 | /** |
333 | Creates a new ROM launcher, to select a new ROM to emulate. |
334 | |
335 | @param startdir The directory to use when opening the launcher; |
336 | if blank, use 'romdir' setting. |
337 | |
338 | @return True on successful creation, otherwise false |
339 | */ |
340 | bool createLauncher(const string& startdir = "" ); |
341 | |
342 | /** |
343 | Answers whether the ROM launcher was actually successfully used |
344 | at some point since the app started. |
345 | |
346 | @return True if launcher was ever used, otherwise false |
347 | */ |
348 | bool launcherUsed() const { return myLauncherUsed; } |
349 | |
350 | /** |
351 | Gets all possible info about the ROM by creating a temporary |
352 | Console object and querying it. |
353 | |
354 | @param romfile The file node of the ROM to use |
355 | @return Some information about this ROM |
356 | */ |
357 | string getROMInfo(const FilesystemNode& romfile); |
358 | |
359 | /** |
360 | The features which are conditionally compiled into Stella. |
361 | |
362 | @return The supported features |
363 | */ |
364 | const string& features() const { return myFeatures; } |
365 | |
366 | /** |
367 | The build information for Stella (toolkit version, architecture, etc). |
368 | |
369 | @return The build info |
370 | */ |
371 | const string& buildInfo() const { return myBuildInfo; } |
372 | |
373 | /** |
374 | Issue a quit event to the OSystem. |
375 | */ |
376 | void quit() { myQuitLoop = true; } |
377 | |
378 | /** |
379 | Get the system messages logged up to this point. |
380 | |
381 | @return The list of log messages |
382 | */ |
383 | const string& logMessages() const { return myLogMessages; } |
384 | |
385 | /** |
386 | Reset FPS measurement. |
387 | */ |
388 | void resetFps(); |
389 | |
390 | float frameRate() const; |
391 | |
392 | /** |
393 | Attempt to override the base directory that will be used by derived |
394 | classes, and use this one instead. Note that this is only a hint; |
395 | derived classes are free to ignore this, as some can't use an |
396 | alternate base directory. |
397 | |
398 | Alternatively, attempt to use the application directory directly. |
399 | Again, this is not supported on all systems, so it may be simply |
400 | ignored. |
401 | */ |
402 | static void overrideBaseDir(const string& path) { ourOverrideBaseDir = path; } |
403 | static void overrideBaseDirWithApp() { ourOverrideBaseDirWithApp = true; } |
404 | |
405 | public: |
406 | ////////////////////////////////////////////////////////////////////// |
407 | // The following methods are system-specific and can be overrided in |
408 | // derived classes. Otherwise, the base methods will be used. |
409 | ////////////////////////////////////////////////////////////////////// |
410 | /** |
411 | This method runs the main loop. Since different platforms |
412 | may use different timing methods and/or algorithms, this method can |
413 | be overrided. However, the port then takes all responsibility for |
414 | running the emulation and taking care of timing. |
415 | */ |
416 | virtual void mainLoop(); |
417 | |
418 | /** |
419 | Informs the OSystem of a change in EventHandler state. |
420 | */ |
421 | virtual void stateChanged(EventHandlerState state) { } |
422 | |
423 | protected: |
424 | |
425 | virtual shared_ptr<KeyValueRepository> createSettingsRepository(); |
426 | |
427 | /** |
428 | Append a message to the internal log |
429 | (a newline is automatically added). |
430 | |
431 | @param message The message to be appended |
432 | @param level If 0, always output the message, only append when |
433 | level is less than or equal to that in 'loglevel' |
434 | */ |
435 | void logMessage(const string& message, Logger::Level level); |
436 | |
437 | ////////////////////////////////////////////////////////////////////// |
438 | // The following methods are system-specific and *must* be |
439 | // implemented in derived classes. |
440 | ////////////////////////////////////////////////////////////////////// |
441 | /** |
442 | Determine the base directory and main configuration file from the |
443 | derived class. It can also use hints, as described below. |
444 | |
445 | @param basedir The base directory for all configuration files |
446 | @param cfgfile The fully qualified pathname of the config file |
447 | (including the base directory) |
448 | @param savedir The default directory to save various other files |
449 | @param loaddir The default directory to load various other files |
450 | @param useappdir A hint that the base dir should be set to the |
451 | app directory; not all ports can do this, so |
452 | they are free to ignore it |
453 | @param usedir A hint that the base dir should be set to this |
454 | parameter; not all ports can do this, so |
455 | they are free to ignore it |
456 | */ |
457 | virtual void getBaseDirAndConfig(string& basedir, string& cfgfile, |
458 | string& savedir, string& loaddir, |
459 | bool useappdir, const string& usedir) = 0; |
460 | |
461 | protected: |
462 | // Pointer to the EventHandler object |
463 | unique_ptr<EventHandler> myEventHandler; |
464 | |
465 | // Pointer to the FrameBuffer object |
466 | unique_ptr<FrameBuffer> myFrameBuffer; |
467 | |
468 | // Pointer to the Sound object |
469 | unique_ptr<Sound> mySound; |
470 | |
471 | // Pointer to the Settings object |
472 | unique_ptr<Settings> mySettings; |
473 | |
474 | // Pointer to the Random object |
475 | unique_ptr<Random> myRandom; |
476 | |
477 | // Pointer to the PropertiesSet object |
478 | unique_ptr<PropertiesSet> myPropSet; |
479 | |
480 | // Pointer to the (currently defined) Console object |
481 | unique_ptr<Console> myConsole; |
482 | |
483 | // Pointer to audio settings object |
484 | unique_ptr<AudioSettings> myAudioSettings; |
485 | |
486 | #ifdef CHEATCODE_SUPPORT |
487 | // Pointer to the CheatManager object |
488 | unique_ptr<CheatManager> myCheatManager; |
489 | #endif |
490 | |
491 | #ifdef DEBUGGER_SUPPORT |
492 | // Pointer to the Debugger object |
493 | unique_ptr<Debugger> myDebugger; |
494 | #endif |
495 | |
496 | #ifdef GUI_SUPPORT |
497 | // Pointer to the Menu object |
498 | unique_ptr<Menu> ; |
499 | |
500 | // Pointer to the CommandMenu object |
501 | unique_ptr<CommandMenu> myCommandMenu; |
502 | |
503 | // Pointer to the Launcher object |
504 | unique_ptr<Launcher> myLauncher; |
505 | |
506 | // Pointer to the TimeMachine object |
507 | unique_ptr<TimeMachine> myTimeMachine; |
508 | #endif |
509 | |
510 | #ifdef PNG_SUPPORT |
511 | // PNG object responsible for loading/saving PNG images |
512 | unique_ptr<PNGLibrary> myPNGLib; |
513 | #endif |
514 | |
515 | // Pointer to the StateManager object |
516 | unique_ptr<StateManager> myStateManager; |
517 | |
518 | // Pointer to the TimerManager object |
519 | unique_ptr<TimerManager> myTimerManager; |
520 | |
521 | // The list of log messages |
522 | string myLogMessages; |
523 | |
524 | // Indicates whether ROM launcher was ever opened during this run |
525 | bool myLauncherUsed; |
526 | |
527 | // Indicates whether to stop the main loop |
528 | bool myQuitLoop; |
529 | |
530 | private: |
531 | string myBaseDir; |
532 | string myStateDir; |
533 | string mySnapshotSaveDir; |
534 | string mySnapshotLoadDir; |
535 | string myNVRamDir; |
536 | string myDefaultSaveDir; |
537 | string myDefaultLoadDir; |
538 | |
539 | bool mySettingsLoaded; |
540 | |
541 | string myCheatFile; |
542 | string myConfigFile; |
543 | string myPaletteFile; |
544 | string myPropertiesFile; |
545 | |
546 | FilesystemNode myRomFile; |
547 | string myRomMD5; |
548 | |
549 | string myFeatures; |
550 | string myBuildInfo; |
551 | |
552 | FpsMeter myFpsMeter; |
553 | |
554 | // If not empty, a hint for derived classes to use this as the |
555 | // base directory (where all settings are stored) |
556 | // Derived classes are free to ignore it and use their own defaults |
557 | static string ourOverrideBaseDir; |
558 | static bool ourOverrideBaseDirWithApp; |
559 | |
560 | #ifdef SQLITE_SUPPORT |
561 | shared_ptr<SettingsDb> mySettingsDb; |
562 | #endif |
563 | |
564 | private: |
565 | /** |
566 | Creates the various sound devices available in this system |
567 | */ |
568 | void createSound(); |
569 | |
570 | /** |
571 | Creates an actual Console object based on the given info. |
572 | |
573 | @param romfile The file node of the ROM to use (contains path) |
574 | @param md5 The MD5sum of the ROM |
575 | |
576 | @return The actual Console object, otherwise nullptr. |
577 | */ |
578 | unique_ptr<Console> openConsole(const FilesystemNode& romfile, string& md5); |
579 | |
580 | /** |
581 | Close and finalize any currently open console. |
582 | */ |
583 | void closeConsole(); |
584 | |
585 | /** |
586 | Gets all possible info about the given console. |
587 | |
588 | @param console The console to use |
589 | @return Some information about this console |
590 | */ |
591 | string getROMInfo(const Console& console); |
592 | |
593 | double dispatchEmulation(EmulationWorker& emulationWorker); |
594 | |
595 | // Following constructors and assignment operators not supported |
596 | OSystem(const OSystem&) = delete; |
597 | OSystem(OSystem&&) = delete; |
598 | OSystem& operator=(const OSystem&) = delete; |
599 | OSystem& operator=(OSystem&&) = delete; |
600 | }; |
601 | |
602 | #endif |
603 | |