c15f9ccd61654fd8002cca2f91647f04ecd3b4d4
[drnoksnes] / platform / sdl.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/time.h>
4 #include <time.h>
5 #include <SDL.h>
6
7 #include "platform.h"
8 #include "snes9x.h"
9 #include "cpuexec.h"
10 #include "gfx.h"
11 #include "ppu.h"
12 #include "memmap.h"
13 #include "soundux.h"
14 #include "hacks.h"
15 #include "snapshot.h"
16
17 #define kPollEveryNFrames               2               //Poll input only every this many frames
18
19 #if CONF_GUI
20 #include "osso.h"
21 #define kPollOssoEveryNFrames   10              //Poll dbus only every this many frames
22 #endif
23
24 #define TRACE printf("trace: %s:%s\n", __FILE__, __func__);
25 #define DIE(format, ...) do { \
26                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
27                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
28                 abort(); \
29         } while (0);
30
31 void S9xMessage(int type, int number, const char * message)
32 {
33         printf("%s\n", message);
34 }
35
36 void S9xAutoSaveSRAM()
37 {
38         Memory.SaveSRAM(S9xGetFilename(FILE_SRAM));
39 }
40
41 static void S9xInit() 
42 {
43         if (!Memory.Init () || !S9xInitAPU())
44          DIE("Memory or APU failed");
45
46         if (!S9xInitSound ())
47                 DIE("Sound failed");
48         S9xSetSoundMute (TRUE);
49         
50         // TODO: PAL/NTSC something better than this
51         Settings.PAL = Settings.ForcePAL;
52         
53         Settings.FrameTime = Settings.PAL?Settings.FrameTimePAL:Settings.FrameTimeNTSC;
54         Memory.ROMFramesPerSecond = Settings.PAL?50:60;
55         
56         IPPU.RenderThisFrame = TRUE;
57 }
58
59 static void loadRom()
60 {
61         const char * file = S9xGetFilename(FILE_ROM);
62
63         printf("ROM: %s\n", file);
64
65         if (!Memory.LoadROM(file))
66                 DIE("Loading ROM failed");
67         
68         file = S9xGetFilename(FILE_SRAM);
69         printf("SRAM: %s\n", file);
70         Memory.LoadSRAM(file); 
71 }
72
73 static void resumeGame()
74 {
75         if (!Config.snapshotLoad) return;
76
77         const char * file = S9xGetFilename(FILE_FREEZE);
78         int result = S9xUnfreezeGame(file);
79
80         printf("Unfreeze: %s", file);
81
82         if (!result) {
83                 printf(" failed");
84                 FILE* fp = fopen(file, "rb");
85                 if (fp) {
86                         if (Config.snapshotSave) {
87                                 puts(", but the file exists, so I'm not going to overwrite it");
88                                 Config.snapshotSave = false;
89                         } else {
90                                 puts(" (bad file?)");
91                         }
92                         fclose(fp);
93                 } else {
94                         puts(" (file does not exist)");
95                 }
96         } else {
97                 puts(" ok");
98         }
99 }
100
101 static void pauseGame()
102 {
103         if (!Config.snapshotSave) return;
104
105         const char * file = S9xGetFilename(FILE_FREEZE);
106         int result = S9xFreezeGame(file);
107
108         printf("Freeze: %s", file);
109
110         if (!result) {
111                 Config.snapshotSave = false; // Serves as a flag to Hgw
112                 puts(" failed");
113         } else {
114                 puts(" ok");
115         }
116 }
117
118 /* This comes nearly straight from snes9x */
119 /** Calculates framerate, enables frame skip if to low, sleeps if too high, etc. */
120 static void frameSync() {
121         Uint32 now = SDL_GetTicks();
122
123         if (Settings.TurboMode)
124         {
125                 // In Turbo mode, just skip as many frames as desired, but don't sleep.
126                 if(Settings.SkipFrames == AUTO_FRAMERATE || 
127                         ++IPPU.FrameSkip >= Settings.SkipFrames)
128                 {
129                         IPPU.FrameSkip = 0;
130                         IPPU.SkippedFrames = 0;
131                         IPPU.RenderThisFrame = TRUE;
132                 }
133                 else
134                 {
135                         ++IPPU.SkippedFrames;
136                         IPPU.RenderThisFrame = FALSE;
137                 }
138
139                 // Take care of framerate display
140                 if (Settings.DisplayFrameRate) {
141                         static Uint32 last = 0;
142                         // Update framecounter every second
143                         if (now > last && (now - last > 1000)) {
144                                 IPPU.DisplayedRenderedFrameCount =
145                                         IPPU.RenderedFramesCount;
146                                 IPPU.RenderedFramesCount = 0;
147                                 last = now;
148                         }
149                 }
150         } else {
151                 static Uint32 next1 = 0;
152
153                 // If there is no known "next" frame, initialize it now
154                 if (next1 == 0) {
155                         next1 = now + 1;
156                 }
157
158                 // If we're on AUTO_FRAMERATE, we'll display frames always
159                 // only if there's excess time.
160                 // Otherwise we'll display around 1 frame every 10.
161                 unsigned limit = Settings.SkipFrames == AUTO_FRAMERATE
162                                         ? (next1 < now ? 10 : 1)
163                                         : Settings.SkipFrames;
164
165                 IPPU.RenderThisFrame = ++IPPU.SkippedFrames >= limit;
166                 if (IPPU.RenderThisFrame) {
167                         IPPU.SkippedFrames = 0;
168                 } else {
169                         // If we were behind the schedule, check how much it is
170                         if (next1 < now)
171                         {
172                         unsigned long lag = now - next1;
173                         if (lag >= 500)
174                                 {
175                                         // More than a half-second behind means probably
176                                         // pause. The next line prevents the magic
177                                         // fast-forward effect.
178                                         next1 = now;
179                                 }
180                         }
181                 }
182
183                 // If we're now ahead of time, sleep a while
184                 if (next1 > now)
185                 {
186                         SDL_Delay(next1 - now);
187                         // SDL will take care if a signal arrives, restarting sleep.
188                 }
189
190                 // Calculate the timestamp of the next frame.
191                 next1 += Settings.FrameTime;
192
193                 // Take care of framerate display
194                 if (Settings.DisplayFrameRate) {
195                         // Update every theoretical 60 frames
196                         if (IPPU.FrameCount % Memory.ROMFramesPerSecond == 0) {
197                                 IPPU.DisplayedRenderedFrameCount =
198                                         IPPU.RenderedFramesCount;
199                                 IPPU.RenderedFramesCount = 0;
200                         }
201                 }
202         }
203 }
204
205 /** Wraps s9xProcessEvents, taking care of kPollEveryNFrames */
206 static inline void pollEvents() {
207         static int frames = 0;
208         
209         if (++frames > kPollEveryNFrames) {
210                 S9xProcessEvents(false);
211                 frames = 0;
212         }
213 }
214
215 #if CONF_GUI
216 /** Wraps OssoPollEvents, taking care of kPollOssoEveryNFrames */
217 static inline void pollOssoEvents() {
218         static int frames = 0;
219         
220         if (!OssoOk()) return;
221         
222         if (++frames > kPollOssoEveryNFrames) {
223                 OssoPollEvents();
224                 frames = 0;
225         }
226 }
227 #endif
228
229 int main(int argc, char ** argv) {
230         // Initialise SDL
231         if (SDL_Init(0) < 0) 
232                 DIE("SDL_Init: %s", SDL_GetError());
233
234         // Configure snes9x
235 #if CONF_GUI
236         OssoInit();                                             // Hildon-games-wrapper initialization.
237 #endif
238         S9xLoadConfig(argc, argv);              // Load config files and parse cmd line.
239 #if CONF_GUI
240         OssoConfig();                                   // Apply specific hildon-games config.
241 #endif
242
243         // S9x initialization
244         S9xInitDisplay(argc, argv);
245         S9xInitAudioOutput();
246         S9xInitInputDevices();
247         S9xInit();
248         S9xReset();
249
250         // Load rom and related files: state, unfreeze if needed
251         loadRom();
252         resumeGame();
253
254         // Late initialization
255         sprintf(String, "DrNokSnes - %s", Memory.ROMName);
256         S9xSetTitle(String);
257         S9xHacksLoadFile(Config.hacksFile);
258         if (!S9xGraphicsInit())
259          DIE("S9xGraphicsInit failed");
260         S9xAudioOutputEnable(true);
261
262         do {
263                 frameSync();                    // May block, or set frameskip to true.
264                 S9xMainLoop();                  // Does CPU things, renders if needed.
265                 pollEvents();
266 #if CONF_GUI
267                 pollOssoEvents();
268 #endif
269         } while (!Config.quitting);
270         
271         // Deinitialization
272         S9xAudioOutputEnable(false);
273         S9xDeinitInputDevices();
274         S9xDeinitAudioOutput();
275         S9xDeinitDisplay();
276
277         // Save state
278         Memory.SaveSRAM(S9xGetFilename(FILE_SRAM));
279         pauseGame();
280
281         // Late deinitialization
282         S9xGraphicsDeinit();
283         Memory.Deinit();
284         S9xUnloadConfig();
285 #if CONF_GUI
286         OssoDeinit();
287 #endif
288
289         SDL_Quit();
290
291         return 0;
292 }
293
294 void S9xDoAction(unsigned char action)
295 {
296         if (action & kActionQuit) 
297                 Config.quitting = true;
298
299         if (action & kActionToggleFullscreen) {
300                 S9xVideoToggleFullscreen();
301         }
302
303         if (action & kActionQuickLoad1) {
304                 const char * file = S9xGetQuickSaveFilename(1);
305                 int result = S9xUnfreezeGame(file);
306                 S9xSetInfoString("Load slot %u: %s", 1,
307                         (result ? "done" : "failed"));
308         }
309
310         if (action & kActionQuickSave1) {
311                 const char * file = S9xGetQuickSaveFilename(1);
312                 int result = S9xFreezeGame(file);
313                 S9xSetInfoString("Save slot %u: %s", 1,
314                         (result ? "done" : "failed"));
315         }
316
317         if (action & kActionQuickLoad2) {
318                 const char * file = S9xGetQuickSaveFilename(2);
319                 int result = S9xUnfreezeGame(file);
320                 S9xSetInfoString("Load slot %u: %s", 2,
321                         (result ? "done" : "failed"));
322         }
323
324         if (action & kActionQuickSave2) {
325                 const char * file = S9xGetQuickSaveFilename(2);
326                 int result = S9xFreezeGame(file);
327                 S9xSetInfoString("Save slot %u: %s", 2,
328                         (result ? "done" : "failed"));
329         }
330 }
331