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