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