Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.   PokeMini - Pokщmon-Mini Emulator
  3.   Copyright (C) 2009-2015  JustBurn
  4.  
  5.   This program is free software: you can redistribute it and/or modify
  6.   it under the terms of the GNU General Public License as published by
  7.   the Free Software Foundation, either version 3 of the License, or
  8.   (at your option) any later version.
  9.  
  10.   This program is distributed in the hope that it will be useful,
  11.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.   GNU General Public License for more details.
  14.  
  15.   You should have received a copy of the GNU General Public License
  16.   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <math.h>
  23. #include "SDL.h"
  24.  
  25. #include "PokeMini.h"
  26. #include "Hardware.h"
  27. #include "ExportBMP.h"
  28. #include "ExportWAV.h"
  29. #include "Keyboard.h"
  30. #include "KeybMapSDL.h"
  31.  
  32. #include "Video_x1.h"
  33. #include "Video_x2.h"
  34. #include "Video_x3.h"
  35. #include "Video_x4.h"
  36. #include "Video_x5.h"
  37. #include "Video_x6.h"
  38. #include "PokeMini_BG2.h"
  39. #include "PokeMini_BG3.h"
  40. #include "PokeMini_BG4.h"
  41. #include "PokeMini_BG5.h"
  42. #include "PokeMini_BG6.h"
  43.  
  44. const char *AppName = "PokeMini " PokeMini_Version " SDL";
  45.  
  46. int emurunning = 1, emulimiter = 1;
  47. SDL_Surface *screen;
  48. int PMWidth, PMHeight;
  49. int PixPitch, PMOff, UIOff;
  50.  
  51. FILE *sdump;
  52. void setup_screen();
  53.  
  54. // Sound buffer size
  55. #define SOUNDBUFFER     512
  56. #define PMSNDBUFFER     (SOUNDBUFFER*2)
  57.  
  58. const char *clc_zoom_txt[] = {
  59.         "0x (Illegal)",
  60.         "1x ( 96x 64)",
  61.         "2x (192x128)",
  62.         "3x (288x192)",
  63.         "4x (384x256)",
  64.         "5x (480x320)",
  65.         "6x (576x384)",
  66. };
  67.  
  68. // Custom command line (NEW IN 0.5.0)
  69. int clc_zoom = 6, clc_bpp = 16, clc_fullscreen = 0;
  70. char clc_dump_sound[PMTMPV] = {0};
  71. int clc_displayfps = 0;
  72. const TCommandLineCustom CustomArgs[] = {
  73.         { "-dumpsound", (int *)&clc_dump_sound, COMMANDLINE_STR, PMTMPV-1 },
  74.         { "-zoom", &clc_zoom, COMMANDLINE_INT, 1, 6 },
  75.         { "-bpp", &clc_bpp, COMMANDLINE_INT, 16, 32 },
  76.         { "-windowed", &clc_fullscreen, COMMANDLINE_INTSET, 0 },
  77.         { "-fullscreen", &clc_fullscreen, COMMANDLINE_INTSET, 1 },
  78.         { "-displayfps", &clc_displayfps, COMMANDLINE_INTSET, 1 },
  79.         { "", NULL, COMMANDLINE_EOL }
  80. };
  81. const TCommandLineCustom CustomConf[] = {
  82.         { "zoom", &clc_zoom, COMMANDLINE_INT, 1, 6 },
  83.         { "bpp", &clc_bpp, COMMANDLINE_INT, 16, 32 },
  84.         { "fullscreen", &clc_fullscreen, COMMANDLINE_BOOL },
  85.         { "displayfps", &clc_displayfps, COMMANDLINE_BOOL },
  86.         { "", NULL, COMMANDLINE_EOL }
  87. };
  88.  
  89. // Platform menu (REQUIRED >= 0.4.4)
  90. int UIItems_PlatformC(int index, int reason);
  91. TUIMenu_Item UIItems_Platform[] = {
  92.         PLATFORMDEF_GOBACK,
  93.         { 0,  1, "Zoom: %s", UIItems_PlatformC },
  94.         { 0,  2, "Depth: %dbpp", UIItems_PlatformC },
  95.         /*{ 0,  3, "Fullscreen: %s", UIItems_PlatformC },*/
  96.         { 0,  4, "Display FPS: %s", UIItems_PlatformC },
  97.         { 0,  9, "Define Keyboard...", UIItems_PlatformC },
  98.         PLATFORMDEF_SAVEOPTIONS,
  99.         PLATFORMDEF_END(UIItems_PlatformC)
  100. };
  101. int UIItems_PlatformC(int index, int reason)
  102. {
  103.         int zoomchanged = 0;
  104.         if (reason == UIMENU_OK) {
  105.                 reason = UIMENU_RIGHT;
  106.         }
  107.         if (reason == UIMENU_CANCEL) {
  108.                 UIMenu_PrevMenu();
  109.         }
  110.         if (reason == UIMENU_LEFT) {
  111.                 switch (index) {
  112.                         case 1: // Zoom
  113.                                 clc_zoom--;
  114.                                 if (clc_zoom < 1) clc_zoom = 6;
  115.                                 zoomchanged = 1;
  116.                                 break;
  117.                         case 2: // Bits-Per-Pixel
  118.                                 if (clc_bpp == 32)
  119.                                         clc_bpp = 16;
  120.                                 else
  121.                                         clc_bpp = 32;
  122.                                 zoomchanged = 1;
  123.                                 break;
  124.                         case 3: // Fullscreen
  125.                                 clc_fullscreen = !clc_fullscreen;
  126.                                 zoomchanged = 1;
  127.                                 break;
  128.                         case 4: // Display FPS
  129.                                 clc_displayfps = !clc_displayfps;
  130.                                 break;
  131.                 }
  132.         }
  133.         if (reason == UIMENU_RIGHT) {
  134.                 switch (index) {
  135.                         case 1: // Zoom
  136.                                 clc_zoom++;
  137.                                 if (clc_zoom > 6) clc_zoom = 1;
  138.                                 zoomchanged = 1;
  139.                                 break;
  140.                         case 2: // Bits-Per-Pixel
  141.                                 if (clc_bpp == 16)
  142.                                         clc_bpp = 32;
  143.                                 else
  144.                                         clc_bpp = 16;
  145.                                 zoomchanged = 1;
  146.                                 break;
  147.                         case 3: // Fullscreen
  148.                                 clc_fullscreen = !clc_fullscreen;
  149.                                 zoomchanged = 1;
  150.                                 break;
  151.                         case 4: // Display FPS
  152.                                 clc_displayfps = !clc_displayfps;
  153.                                 break;
  154.                                 break;
  155.                         case 9: // Define Keyboard...
  156.                                 KeyboardEnterMenu();
  157.                                 break;
  158.                 }
  159.         }
  160.         UIMenu_ChangeItem(UIItems_Platform, 1, "Zoom: %s", clc_zoom_txt[clc_zoom]);
  161.         UIMenu_ChangeItem(UIItems_Platform, 2, "Depth: %dbpp", clc_bpp);
  162.         UIMenu_ChangeItem(UIItems_Platform, 3, "Fullscreen: %s", clc_fullscreen ? "Yes" : "No");
  163.         UIMenu_ChangeItem(UIItems_Platform, 4, "Display FPS: %s", clc_displayfps ? "Yes" : "No");
  164.         if (zoomchanged) {
  165.                 SDL_UnlockSurface(screen);
  166.                 setup_screen();
  167.                 SDL_LockSurface(screen);
  168.                 return 0;
  169.         }
  170.         return 1;
  171. }
  172.  
  173. // Setup screen
  174. void setup_screen()
  175. {
  176.         TPokeMini_VideoSpec *videospec;
  177.         int depth, PMOffX, PMOffY, UIOffX, UIOffY;
  178.  
  179.         // Calculate size based of zoom
  180.         if (clc_zoom == 1) {
  181.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video1x1;
  182.                 PMWidth = 192; PMHeight = 128; PMOffX = 48; PMOffY = 32; UIOffX = 0; UIOffY = 0;
  183.                 UIMenu_SetDisplay(192, 128, PokeMini_BGR16, (uint8_t *)PokeMini_BG2, (uint16_t *)PokeMini_BG2_PalBGR16, (uint32_t *)PokeMini_BG2_PalBGR32);
  184.         } else if (clc_zoom == 2) {
  185.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video2x2;
  186.                 PMWidth = 208; PMHeight = 144; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
  187.                 UIMenu_SetDisplay(192, 128, PokeMini_BGR16, (uint8_t *)PokeMini_BG2, (uint16_t *)PokeMini_BG2_PalBGR16, (uint32_t *)PokeMini_BG2_PalBGR32);
  188.         } else if (clc_zoom == 3) {
  189.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video3x3;
  190.                 PMWidth = 304; PMHeight = 208; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
  191.                 UIMenu_SetDisplay(288, 192, PokeMini_BGR16, (uint8_t *)PokeMini_BG3, (uint16_t *)PokeMini_BG3_PalBGR16, (uint32_t *)PokeMini_BG3_PalBGR32);
  192.         } else if (clc_zoom == 4) {
  193.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video4x4;
  194.                 PMWidth = 400; PMHeight = 272; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
  195.                 UIMenu_SetDisplay(384, 256, PokeMini_BGR16, (uint8_t *)PokeMini_BG4, (uint16_t *)PokeMini_BG4_PalBGR16, (uint32_t *)PokeMini_BG4_PalBGR32);
  196.         } else if (clc_zoom == 5) {
  197.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video5x5;
  198.                 PMWidth = 496; PMHeight = 336; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
  199.                 UIMenu_SetDisplay(480, 320, PokeMini_BGR16, (uint8_t *)PokeMini_BG5, (uint16_t *)PokeMini_BG5_PalBGR16, (uint32_t *)PokeMini_BG5_PalBGR32);
  200.         } else {
  201.                 videospec = (TPokeMini_VideoSpec *)&PokeMini_Video6x6;
  202.                 PMWidth = 592; PMHeight = 400; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
  203.                 UIMenu_SetDisplay(576, 384, PokeMini_BGR16, (uint8_t *)PokeMini_BG6, (uint16_t *)PokeMini_BG6_PalBGR16, (uint32_t *)PokeMini_BG6_PalBGR32);
  204.         }
  205.  
  206.         // Set video spec and check if is supported
  207.         depth = PokeMini_SetVideo(videospec, clc_bpp, CommandLine.lcdfilter, CommandLine.lcdmode);
  208.         if (!depth) {
  209.                 fprintf(stderr, "Couldn't set video spec from %i bpp\n", clc_bpp);
  210.                 exit(1);
  211.         }
  212.  
  213.         // Set video mode
  214.         screen = SDL_SetVideoMode(PMWidth, PMHeight, depth, SDL_HWSURFACE | SDL_DOUBLEBUF | (clc_fullscreen ? SDL_FULLSCREEN : 0));
  215.         if (screen == NULL) {
  216.                 fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
  217.                 exit(1);
  218.         }
  219.  
  220.         // Calculate pitch and offset
  221.         if (depth == 32) {
  222.                 PixPitch = screen->pitch / 4;
  223.                 PMOff = (PMOffY * screen->pitch) + (PMOffX * 4);
  224.                 UIOff = (UIOffY * screen->pitch) + (UIOffX * 4);
  225.         } else {
  226.                 PixPitch = screen->pitch / 2;
  227.                 PMOff = (PMOffY * screen->pitch) + (PMOffX * 2);
  228.                 UIOff = (UIOffY * screen->pitch) + (UIOffX * 2);
  229.         }
  230.         clc_bpp = depth;
  231. }
  232.  
  233. // Capture screen
  234. void capture_screen()
  235. {
  236.         FILE *capf;
  237.         int y, capnum;
  238.         unsigned long Video[96*64];
  239.         PokeMini_VideoPreview_32((uint32_t *)Video, 96, PokeMini_LCDMode);
  240.         capf = OpenUnique_ExportBMP(&capnum, 96, 64);
  241.         if (!capf) {
  242.                 fprintf(stderr, "Error while saving capture\n");
  243.                 return;
  244.         }
  245.         for (y=0; y<64; y++) {
  246.                 WriteArray_ExportBMP(capf, (uint32_t *)&Video[(63-y) * 96], 96);
  247.         }
  248.         printf("Capture saved at 'snap_%03d.bmp'\n", capnum);
  249.         Close_ExportBMP(capf);
  250. }
  251.  
  252. // Handle keyboard and quit events
  253. void handleevents(SDL_Event *event)
  254. {
  255.         switch (event->type) {
  256.         case SDL_KEYDOWN:
  257.                 if (event->key.keysym.sym == SDLK_F9) {                 // Capture screen
  258.                         capture_screen();
  259.                 } else if (event->key.keysym.sym == SDLK_F4) {          // Emulator Exit
  260.                         if (event->key.keysym.mod & KMOD_ALT) {
  261.                                 emurunning = 0;
  262.                         }
  263.                 } else if (event->key.keysym.sym == SDLK_F10) {         // Fullscreen/Window
  264.                         clc_fullscreen = !clc_fullscreen;
  265.                         setup_screen();
  266.                         UIItems_PlatformC(0, UIMENU_LOAD);
  267.                 } else if (event->key.keysym.sym == SDLK_F11) {         // Disable speed throttling
  268.                         emulimiter = !emulimiter;
  269.                 } else if (event->key.keysym.sym == SDLK_TAB) {         // Temp disable speed throttling
  270.                         emulimiter = 0;
  271.                 } else {
  272.                         KeyboardPressEvent(event->key.keysym.sym);
  273.                 }
  274.                 break;
  275.         case SDL_KEYUP:
  276.                 if (event->key.keysym.sym == SDLK_TAB) {                // Speed threhold
  277.                         emulimiter = 1;
  278.                 } else {
  279.                         KeyboardReleaseEvent(event->key.keysym.sym);
  280.                 }
  281.                 break;
  282.         case SDL_QUIT:
  283.                 emurunning = 0;
  284.                 break;
  285.         };
  286. }
  287.  
  288. // Used to fill the sound buffer
  289. void emulatorsound(void *unused, Uint8 *stream, int len)
  290. {
  291.         MinxAudio_GetSamplesU8(stream, len);
  292.         if (clc_dump_sound[0]) WriteU8A_ExportWAV(sdump, stream, len>>1);
  293. }
  294.  
  295. // Enable / Disable sound
  296. void enablesound(int sound)
  297. {
  298.         MinxAudio_ChangeEngine(sound);
  299.         if (AudioEnabled) SDL_PauseAudio(!sound);
  300. }
  301.  
  302. // Menu loop
  303. void menuloop()
  304. {
  305.         SDL_Event event;
  306.  
  307.         // Update window's title and stop sound
  308.         SDL_WM_SetCaption(AppName, "PMEWindow");
  309.         SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
  310.         enablesound(0);
  311.  
  312.         // Update EEPROM
  313.         PokeMini_SaveFromCommandLines(0);
  314.  
  315.         // Menu's loop
  316.         while (emurunning && (UI_Status == UI_STATUS_MENU)) {
  317.                 // Slowdown to approx. 60fps
  318.                 SDL_Delay(1);
  319.  
  320.                 // Process UI
  321.                 UIMenu_Process();
  322.  
  323.                 // Screen rendering
  324.                 SDL_FillRect(screen, NULL, 0);
  325.                 if (SDL_LockSurface(screen) == 0) {
  326.                         // Render the menu or the game screen
  327.                         if (PokeMini_VideoDepth == 32)
  328.                                 UIMenu_Display_32((uint32_t *)((uint8_t *)screen->pixels + UIOff), PixPitch);
  329.                         else  
  330.                                 UIMenu_Display_16((uint16_t *)((uint8_t *)screen->pixels + UIOff), PixPitch);
  331.  
  332.                         // Unlock surface
  333.                         SDL_UnlockSurface(screen);
  334.                         SDL_Flip(screen);
  335.                 }
  336.  
  337.                 // Handle events
  338.                 while (SDL_PollEvent(&event)) handleevents(&event);
  339.         }
  340.  
  341.         // Apply configs
  342.         PokeMini_ApplyChanges();
  343.         if (UI_Status == UI_STATUS_EXIT) emurunning = 0;
  344.         else
  345.         {
  346.                 if (CommandLine.sound == MINX_AUDIO_GENERATED)
  347.                         enablesound(MINX_AUDIO_GENERATED);
  348.                 else
  349.                         enablesound(0);
  350.         }
  351.         SDL_EnableKeyRepeat(0, 0);
  352. }
  353.  
  354. // Main function
  355. int main(int argc, char **argv)
  356. {
  357.         SDL_Event event;
  358.         char title[256];
  359.         char fpstxt[16];
  360.  
  361.         // Process arguments
  362.         printf("%s\n\n", AppName);
  363.         PokeMini_InitDirs(argv[0], NULL);
  364.         CommandLineInit();
  365.         CommandLineConfFile("/tmp0/1/pokemini.cfg", "/tmp0/1/pokemini_sdl.cfg", CustomConf);
  366.         if (!CommandLineArgs(argc, argv, CustomArgs)) {
  367.                 PrintHelpUsage(stdout);
  368.                 printf("  -dumpsound sound.wav   Dump sound into a WAV file\n");
  369.                 printf("  -windowed              Display in window (default)\n");
  370.                 printf("  -fullscreen            Display in fullscreen\n");
  371.                 printf("  -displayfps            Display FPS counter on screen\n");
  372.                 printf("  -zoom n                Zoom display: 1 to 6 (def 4)\n");
  373.                 printf("  -bpp n                 Bits-Per-Pixel: 16 or 32 (def 16)\n");
  374.                 return 1;
  375.         }
  376.  
  377.         // Initialize SDL
  378.         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
  379.                 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
  380.                 return 1;
  381.         }
  382.         atexit(SDL_Quit); // Clean up on exit
  383.  
  384.         // Initialize the display
  385.         setup_screen();
  386.  
  387.         // Initialize the sound
  388.         SDL_AudioSpec audfmt;
  389.         SDL_AudioSpec obtained;
  390.         audfmt.freq = 44100;
  391.         audfmt.format = AUDIO_U8;
  392.         audfmt.channels = 1;
  393.         audfmt.samples = SOUNDBUFFER;
  394.         audfmt.callback = emulatorsound;
  395.         audfmt.userdata = NULL;
  396.  
  397.         // Open the audio device
  398.         // Second setting can't be NULL on KolibriOS or else it wont play sound
  399.         if (SDL_OpenAudio(&audfmt, &obtained) < 0) {
  400.                 fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
  401.                 fprintf(stderr, "Audio will be disabled\n");
  402.                 AudioEnabled = 0;
  403.         } else {
  404.                 AudioEnabled = 1;
  405.         }
  406.  
  407.         // Set the window manager title bar
  408.         SDL_WM_SetCaption(AppName, "PMEWindow");
  409.         SDL_EnableKeyRepeat(0, 0);
  410.  
  411.         // Open WAV capture if was requested
  412.         if (clc_dump_sound[0]) {
  413.                 sdump = Open_ExportWAV(clc_dump_sound, EXPORTWAV_44KHZ | EXPORTWAV_MONO | EXPORTWAV_16BITS);
  414.                 if (!sdump) {
  415.                         fprintf(stderr, "Error opening sound export file.\n");
  416.                         return 1;
  417.                 }
  418.         }
  419.  
  420.         // Initialize the emulator
  421.         printf("Starting emulator...\n");
  422.         if (!PokeMini_Create(0, PMSNDBUFFER)) {
  423.                 fprintf(stderr, "Error while initializing emulator\n");
  424.                 return 1;
  425.         }
  426.  
  427.         // Setup palette and LCD mode
  428.         PokeMini_VideoPalette_Init(PokeMini_BGR16, 1);
  429.         PokeMini_VideoPalette_Index(CommandLine.palette, CommandLine.custompal, CommandLine.lcdcontrast, CommandLine.lcdbright);
  430.         PokeMini_ApplyChanges();
  431.  
  432.         // Load stuff
  433.         PokeMini_UseDefaultCallbacks();
  434.         if (!PokeMini_LoadFromCommandLines("Using FreeBIOS", "EEPROM data will be discarded!")) {
  435.                 UI_Status = UI_STATUS_MENU;
  436.         }
  437.  
  438.         // Enable sound & init UI
  439.         printf("Running emulator...\n");
  440.         UIMenu_Init();
  441.         KeyboardRemap(&KeybMapSDL);
  442.        
  443.         if (CommandLine.sound == MINX_AUDIO_GENERATED)
  444.                 enablesound(MINX_AUDIO_GENERATED);
  445.         else
  446.                 enablesound(0);
  447.        
  448.         // Emulator's loop
  449.         unsigned long time, NewTickFPS = 0, NewTickSync = 0;
  450.         int fps = 72, fpscnt = 0;
  451.         while (emurunning) {
  452.                 // Emulate and syncronize
  453.                 time = SDL_GetTicks();
  454.                 if (RequireSoundSync) {
  455.                         PokeMini_EmulateFrame();
  456.                         // Sleep a little in the hope to free a few samples
  457.                         if (emulimiter) while (MinxAudio_SyncWithAudio()) SDL_Delay(1);
  458.                 } else {
  459.                         PokeMini_EmulateFrame();
  460.                         if (emulimiter) {
  461.                                 do {
  462.                                         SDL_Delay(1);           // This lower CPU usage
  463.                                         time = SDL_GetTicks();
  464.                                 } while (time < NewTickSync);
  465.                                 NewTickSync = time + 13;        // Aprox 72 times per sec
  466.                         }
  467.                 }
  468.  
  469.                 // Screen rendering
  470.                 SDL_FillRect(screen, NULL, 0);
  471.                 if (SDL_LockSurface(screen) == 0) {
  472.                         // Render the menu or the game screen
  473.                         if (PokeMini_Rumbling) {
  474.                                 PokeMini_VideoBlit((void *)((uint8_t *)screen->pixels + PMOff + PokeMini_GenRumbleOffset(screen->pitch)), PixPitch);
  475.                         } else {
  476.                                 PokeMini_VideoBlit((void *)((uint8_t *)screen->pixels + PMOff), PixPitch);
  477.                         }
  478.                         LCDDirty = 0;
  479.  
  480.                         // Display FPS counter
  481.                         if (clc_displayfps) {
  482.                                 if (PokeMini_VideoDepth == 32)
  483.                                         UIDraw_String_32((uint32_t *)screen->pixels, PixPitch, 4, 4, 10, fpstxt, UI_Font1_Pal32);
  484.                                 else
  485.                                         UIDraw_String_16((uint16_t *)screen->pixels, PixPitch, 4, 4, 10, fpstxt, UI_Font1_Pal16);
  486.                         }
  487.  
  488.                         // Unlock surface
  489.                         SDL_UnlockSurface(screen);
  490.                         SDL_Flip(screen);
  491.                 }
  492.  
  493.                 // Handle events
  494.                 while (SDL_PollEvent(&event)) handleevents(&event);
  495.  
  496.                 // Menu
  497.                 if (UI_Status == UI_STATUS_MENU) menuloop();
  498.  
  499.                 // calculate FPS
  500.                 fpscnt++;
  501.                 if (time >= NewTickFPS) {
  502.                         fps = fpscnt;
  503.                         sprintf(title, "%s - %d%%", AppName, fps * 100 / 72);
  504.                         sprintf(fpstxt, "%i FPS", fps);
  505.                         SDL_WM_SetCaption(title, "PMEWindow");
  506.                         NewTickFPS = time + 1000;
  507.                         fpscnt = 0;
  508.                 }
  509.         }
  510.  
  511.         // Disable sound & free UI
  512.         enablesound(0);
  513.         UIMenu_Destroy();
  514.  
  515.         // Close WAV capture if there's one
  516.         if (clc_dump_sound[0]) Close_ExportWAV(sdump);
  517.  
  518.         // Save Stuff
  519.         PokeMini_SaveFromCommandLines(1);
  520.  
  521.         // Terminate...
  522.         printf("Shutdown emulator...\n");
  523.         PokeMini_VideoPalette_Free();
  524.         PokeMini_Destroy();
  525.  
  526.         return 0;
  527. }
  528.