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