window-size sdl surfaces instead of snes
[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 "gfx.h"
10 #include "display.h"
11 #include "memmap.h"
12 #include "soundux.h"
13 #include "hacks.h"
14 #include "snapshot.h"
15 #include "hgw.h"
16
17 #define kPollEveryNFrames               5               //Poll input only every this many frames
18 #define kPollHgwEveryNFrames    10              //Poll dbus only every this many frames
19
20 #define TRACE printf("trace: %s:%s\n", __FILE__, __func__);
21 #define DIE(format, ...) do { \
22                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
23                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
24                 abort(); \
25         } while (0);
26
27 void S9xMessage(int type, int number, const char * message)
28 {
29         printf("%s\n", message);
30 }
31
32 void S9xLoadSDD1Data()
33 {TRACE
34         Settings.SDD1Pack=FALSE;
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 static void frameSync() {
121         if (Settings.TurboMode)
122         {
123                 if(Settings.SkipFrames == AUTO_FRAMERATE || 
124                         ++IPPU.FrameSkip >= Settings.SkipFrames)
125                 {
126                         IPPU.FrameSkip = 0;
127                         IPPU.SkippedFrames = 0;
128                         IPPU.RenderThisFrame = TRUE;
129                 }
130                 else
131                 {
132                         ++IPPU.SkippedFrames;
133                         IPPU.RenderThisFrame = FALSE;
134                 }
135                 return;
136         } else {
137                 static Uint32 next1 = 0;
138                 Uint32 now = SDL_GetTicks();
139
140                 // If there is no known "next" frame, initialize it now
141                 if (next1 == 0) {
142                         next1 = now + 1;
143                 }
144
145                 // If we're on AUTO_FRAMERATE, we'll display frames always
146                 // only if there's excess time.
147                 // Otherwise we'll display around 1 frame every 10.
148                 unsigned limit = Settings.SkipFrames == AUTO_FRAMERATE
149                                         ? (next1 < now ? 10 : 1)
150                                         : Settings.SkipFrames;
151
152                 IPPU.RenderThisFrame = ++IPPU.SkippedFrames >= limit;
153                 if (IPPU.RenderThisFrame) {
154                         IPPU.SkippedFrames = 0;
155                 } else {
156                         // If we were behind the schedule, check how much it is
157                         if (next1 < now)
158                         {
159                         unsigned long lag = now - next1;
160                         if (lag >= 500)
161                                 {
162                                         // More than a half-second behind means probably
163                                         // pause. The next line prevents the magic
164                                         // fast-forward effect.
165                                         next1 = now;
166                                 }
167                         }
168                 }
169
170                 // If we're now ahead of time, sleep a while
171                 if (next1 > now)
172                 {
173                         SDL_Delay(next1 - now);
174                         // SDL will take care if a signal arrives, restarting sleep.
175                 }
176
177                 // Calculate the timestamp of the next frame.
178                 next1 += Settings.FrameTime;
179         }
180 }
181
182 /** Wraps s9xProcessEvents, taking care of kPollEveryNFrames */
183 static inline void pollEvents() {
184         static int frames = 0;
185         
186         if (++frames > kPollEveryNFrames) {
187                 S9xProcessEvents(FALSE);
188                 frames = 0;
189         }
190 }
191
192 /** Wraps HgwPollEvents, taking care of kPollHgwEveryNFrames */
193 static inline void pollHgwEvents() {
194         static int frames = 0;
195         
196         if (!hgwLaunched) return;
197         
198         if (++frames > kPollHgwEveryNFrames) {
199                 HgwPollEvents();
200                 frames = 0;
201         }
202 }
203
204 int main(int argc, const char ** argv) {        
205         // Initialise SDL
206         if (SDL_Init(0) < 0) 
207                 DIE("SDL_Init: %s", SDL_GetError());
208
209         // Configure snes9x
210         HgwInit();                                              // Hildon-games-wrapper initialization.
211         S9xLoadConfig(argc, argv);              // Load config files and parse cmd line.
212         HgwConfig();                                    // Apply specific hildon-games config.
213
214         // S9x initialization
215         S9xInitDisplay(argc, argv);
216         S9xInitAudioOutput();
217         S9xInitInputDevices();
218         S9xInit();
219         S9xReset();
220
221         // Load rom and related files: state, unfreeze if needed
222         loadRom();
223         resumeGame();
224
225         // Late initialization
226         sprintf(String, "DrNokSnes - %s", Memory.ROMName);
227         S9xSetTitle(String);
228         S9xHacksLoadFile(Config.hacksFile);
229         if (!S9xGraphicsInit())
230          DIE("S9xGraphicsInit failed");
231         S9xAudioOutputEnable(true);
232
233         do {
234                 frameSync();                    // May block, or set frameskip to true.
235                 S9xMainLoop();                  // Does CPU things, renders if needed.
236                 pollEvents();
237                 pollHgwEvents();
238         } while (!Config.quitting);
239         
240         // Deinitialization
241         S9xAudioOutputEnable(false);
242         S9xDeinitInputDevices();
243         S9xDeinitAudioOutput();
244         S9xDeinitDisplay();
245
246         // Save state
247         Memory.SaveSRAM(S9xGetFilename(FILE_SRAM));
248         pauseGame();
249
250         // Late deinitialization
251         S9xGraphicsDeinit();
252         Memory.Deinit();
253         S9xUnloadConfig();
254         HgwDeinit();
255
256         SDL_Quit();
257
258         return 0;
259 }
260
261 void S9xDoAction(unsigned char action)
262 {
263         if (action & kActionQuit) 
264                 Config.quitting = true;
265
266         if (action & kActionToggleFullscreen) {
267                 S9xVideoToggleFullscreen();
268                 S9xInputFullscreenChanged();
269         }
270 }
271