reducing size of gfx state, cleanup
[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 /** Calculates framerate, enables frame skip if to low, sleeps if too high, etc. */
118 static void frameSync() {
119         Uint32 now = SDL_GetTicks();
120
121         if (Settings.TurboMode)
122         {
123                 // In Turbo mode, just skip as many frames as desired, but don't sleep.
124                 if(Settings.SkipFrames == AUTO_FRAMERATE || 
125                         ++IPPU.FrameSkip >= Settings.SkipFrames)
126                 {
127                         IPPU.FrameSkip = 0;
128                         IPPU.SkippedFrames = 0;
129                         IPPU.RenderThisFrame = TRUE;
130                 }
131                 else
132                 {
133                         ++IPPU.SkippedFrames;
134                         IPPU.RenderThisFrame = FALSE;
135                 }
136
137                 // Take care of framerate display
138                 if (Settings.DisplayFrameRate) {
139                         static Uint32 last = 0;
140                         // Update framecounter every second
141                         if (now > last && (now - last > 1000)) {
142                                 IPPU.DisplayedRenderedFrameCount =
143                                         IPPU.RenderedFramesCount;
144                                 IPPU.RenderedFramesCount = 0;
145                                 last = now;
146                         }
147                 }
148         } else {
149                 static Uint32 next1 = 0;
150
151                 // If there is no known "next" frame, initialize it now
152                 if (next1 == 0) {
153                         next1 = now + 1;
154                 }
155
156                 // If we're on AUTO_FRAMERATE, we'll display frames always
157                 // only if there's excess time.
158                 // Otherwise we'll display around 1 frame every 10.
159                 unsigned limit = Settings.SkipFrames == AUTO_FRAMERATE
160                                         ? (next1 < now ? 10 : 1)
161                                         : Settings.SkipFrames;
162
163                 IPPU.RenderThisFrame = ++IPPU.SkippedFrames >= limit;
164                 if (IPPU.RenderThisFrame) {
165                         IPPU.SkippedFrames = 0;
166                 } else {
167                         // If we were behind the schedule, check how much it is
168                         if (next1 < now)
169                         {
170                         unsigned long lag = now - next1;
171                         if (lag >= 500)
172                                 {
173                                         // More than a half-second behind means probably
174                                         // pause. The next line prevents the magic
175                                         // fast-forward effect.
176                                         next1 = now;
177                                 }
178                         }
179                 }
180
181                 // If we're now ahead of time, sleep a while
182                 if (next1 > now)
183                 {
184                         SDL_Delay(next1 - now);
185                         // SDL will take care if a signal arrives, restarting sleep.
186                 }
187
188                 // Calculate the timestamp of the next frame.
189                 next1 += Settings.FrameTime;
190
191                 // Take care of framerate display
192                 if (Settings.DisplayFrameRate) {
193                         // Update every theoretical 60 frames
194                         if (IPPU.FrameCount % Memory.ROMFramesPerSecond == 0) {
195                                 IPPU.DisplayedRenderedFrameCount =
196                                         IPPU.RenderedFramesCount;
197                                 IPPU.RenderedFramesCount = 0;
198                         }
199                 }
200         }
201 }
202
203 /** Wraps s9xProcessEvents, taking care of kPollEveryNFrames */
204 static inline void pollEvents() {
205         static int frames = 0;
206         
207         if (++frames > kPollEveryNFrames) {
208                 S9xProcessEvents(false);
209                 frames = 0;
210         }
211 }
212
213 #if CONF_GUI
214 /** Wraps OssoPollEvents, taking care of kPollOssoEveryNFrames */
215 static inline void pollOssoEvents() {
216         static int frames = 0;
217         
218         if (!OssoOk()) return;
219         
220         if (++frames > kPollOssoEveryNFrames) {
221                 OssoPollEvents();
222                 frames = 0;
223         }
224 }
225 #endif
226
227 int main(int argc, char ** argv) {
228         // Initialise SDL
229         if (SDL_Init(0) < 0) 
230                 DIE("SDL_Init: %s", SDL_GetError());
231
232         // Configure snes9x
233 #if CONF_GUI
234         OssoInit();                                             // Hildon-games-wrapper initialization.
235 #endif
236         S9xLoadConfig(argc, argv);              // Load config files and parse cmd line.
237 #if CONF_GUI
238         OssoConfig();                                   // Apply specific hildon-games config.
239 #endif
240
241         // S9x initialization
242         S9xInitDisplay(argc, argv);
243         S9xInitAudioOutput();
244         S9xInitInputDevices();
245         S9xInit();
246         S9xReset();
247
248         // Load rom and related files: state, unfreeze if needed
249         loadRom();
250         resumeGame();
251
252         // Late initialization
253         sprintf(String, "DrNokSnes - %s", Memory.ROMName);
254         S9xSetTitle(String);
255         S9xHacksLoadFile(Config.hacksFile);
256         if (!S9xGraphicsInit())
257          DIE("S9xGraphicsInit failed");
258         S9xAudioOutputEnable(true);
259
260         do {
261                 frameSync();                    // May block, or set frameskip to true.
262                 S9xMainLoop();                  // Does CPU things, renders if needed.
263                 pollEvents();
264 #if CONF_GUI
265                 pollOssoEvents();
266 #endif
267         } while (!Config.quitting);
268         
269         // Deinitialization
270         S9xAudioOutputEnable(false);
271         S9xDeinitInputDevices();
272         S9xDeinitAudioOutput();
273         S9xDeinitDisplay();
274
275         // Save state
276         Memory.SaveSRAM(S9xGetFilename(FILE_SRAM));
277         pauseGame();
278
279         // Late deinitialization
280         S9xGraphicsDeinit();
281         Memory.Deinit();
282         S9xUnloadConfig();
283 #if CONF_GUI
284         OssoDeinit();
285 #endif
286
287         SDL_Quit();
288
289         return 0;
290 }
291
292 void S9xDoAction(unsigned char action)
293 {
294         if (action & kActionQuit) 
295                 Config.quitting = true;
296
297         if (action & kActionToggleFullscreen) {
298                 S9xVideoToggleFullscreen();
299         }
300
301         if (action & kActionQuickLoad1) {
302                 const char * file = S9xGetQuickSaveFilename(1);
303                 int result = S9xUnfreezeGame(file);
304                 S9xSetInfoString("Load slot %u: %s", 1,
305                         (result ? "done" : "failed"));
306         }
307
308         if (action & kActionQuickSave1) {
309                 const char * file = S9xGetQuickSaveFilename(1);
310                 int result = S9xFreezeGame(file);
311                 S9xSetInfoString("Save slot %u: %s", 1,
312                         (result ? "done" : "failed"));
313         }
314
315         if (action & kActionQuickLoad2) {
316                 const char * file = S9xGetQuickSaveFilename(2);
317                 int result = S9xUnfreezeGame(file);
318                 S9xSetInfoString("Load slot %u: %s", 2,
319                         (result ? "done" : "failed"));
320         }
321
322         if (action & kActionQuickSave2) {
323                 const char * file = S9xGetQuickSaveFilename(2);
324                 int result = S9xFreezeGame(file);
325                 S9xSetInfoString("Save slot %u: %s", 2,
326                         (result ? "done" : "failed"));
327         }
328 }
329