e1cfde849d
git-svn-id: svn://kolibrios.org@6577 a494cfbc-eb01-0410-851d-a64ba20cac60
528 lines
15 KiB
C
528 lines
15 KiB
C
/*
|
|
PokeMini - Pokémon-Mini Emulator
|
|
Copyright (C) 2009-2015 JustBurn
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "SDL.h"
|
|
|
|
#include "PokeMini.h"
|
|
#include "Hardware.h"
|
|
#include "ExportBMP.h"
|
|
#include "ExportWAV.h"
|
|
#include "Keyboard.h"
|
|
#include "KeybMapSDL.h"
|
|
|
|
#include "Video_x1.h"
|
|
#include "Video_x2.h"
|
|
#include "Video_x3.h"
|
|
#include "Video_x4.h"
|
|
#include "Video_x5.h"
|
|
#include "Video_x6.h"
|
|
#include "PokeMini_BG2.h"
|
|
#include "PokeMini_BG3.h"
|
|
#include "PokeMini_BG4.h"
|
|
#include "PokeMini_BG5.h"
|
|
#include "PokeMini_BG6.h"
|
|
|
|
const char *AppName = "PokeMini " PokeMini_Version " SDL";
|
|
|
|
int emurunning = 1, emulimiter = 1;
|
|
SDL_Surface *screen;
|
|
int PMWidth, PMHeight;
|
|
int PixPitch, PMOff, UIOff;
|
|
|
|
FILE *sdump;
|
|
void setup_screen();
|
|
|
|
// Sound buffer size
|
|
#define SOUNDBUFFER 512
|
|
#define PMSNDBUFFER (SOUNDBUFFER*2)
|
|
|
|
const char *clc_zoom_txt[] = {
|
|
"0x (Illegal)",
|
|
"1x ( 96x 64)",
|
|
"2x (192x128)",
|
|
"3x (288x192)",
|
|
"4x (384x256)",
|
|
"5x (480x320)",
|
|
"6x (576x384)",
|
|
};
|
|
|
|
// Custom command line (NEW IN 0.5.0)
|
|
int clc_zoom = 6, clc_bpp = 16, clc_fullscreen = 0;
|
|
char clc_dump_sound[PMTMPV] = {0};
|
|
int clc_displayfps = 0;
|
|
const TCommandLineCustom CustomArgs[] = {
|
|
{ "-dumpsound", (int *)&clc_dump_sound, COMMANDLINE_STR, PMTMPV-1 },
|
|
{ "-zoom", &clc_zoom, COMMANDLINE_INT, 1, 6 },
|
|
{ "-bpp", &clc_bpp, COMMANDLINE_INT, 16, 32 },
|
|
{ "-windowed", &clc_fullscreen, COMMANDLINE_INTSET, 0 },
|
|
{ "-fullscreen", &clc_fullscreen, COMMANDLINE_INTSET, 1 },
|
|
{ "-displayfps", &clc_displayfps, COMMANDLINE_INTSET, 1 },
|
|
{ "", NULL, COMMANDLINE_EOL }
|
|
};
|
|
const TCommandLineCustom CustomConf[] = {
|
|
{ "zoom", &clc_zoom, COMMANDLINE_INT, 1, 6 },
|
|
{ "bpp", &clc_bpp, COMMANDLINE_INT, 16, 32 },
|
|
{ "fullscreen", &clc_fullscreen, COMMANDLINE_BOOL },
|
|
{ "displayfps", &clc_displayfps, COMMANDLINE_BOOL },
|
|
{ "", NULL, COMMANDLINE_EOL }
|
|
};
|
|
|
|
// Platform menu (REQUIRED >= 0.4.4)
|
|
int UIItems_PlatformC(int index, int reason);
|
|
TUIMenu_Item UIItems_Platform[] = {
|
|
PLATFORMDEF_GOBACK,
|
|
{ 0, 1, "Zoom: %s", UIItems_PlatformC },
|
|
{ 0, 2, "Depth: %dbpp", UIItems_PlatformC },
|
|
/*{ 0, 3, "Fullscreen: %s", UIItems_PlatformC },*/
|
|
{ 0, 4, "Display FPS: %s", UIItems_PlatformC },
|
|
{ 0, 9, "Define Keyboard...", UIItems_PlatformC },
|
|
PLATFORMDEF_SAVEOPTIONS,
|
|
PLATFORMDEF_END(UIItems_PlatformC)
|
|
};
|
|
int UIItems_PlatformC(int index, int reason)
|
|
{
|
|
int zoomchanged = 0;
|
|
if (reason == UIMENU_OK) {
|
|
reason = UIMENU_RIGHT;
|
|
}
|
|
if (reason == UIMENU_CANCEL) {
|
|
UIMenu_PrevMenu();
|
|
}
|
|
if (reason == UIMENU_LEFT) {
|
|
switch (index) {
|
|
case 1: // Zoom
|
|
clc_zoom--;
|
|
if (clc_zoom < 1) clc_zoom = 6;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 2: // Bits-Per-Pixel
|
|
if (clc_bpp == 32)
|
|
clc_bpp = 16;
|
|
else
|
|
clc_bpp = 32;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 3: // Fullscreen
|
|
clc_fullscreen = !clc_fullscreen;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 4: // Display FPS
|
|
clc_displayfps = !clc_displayfps;
|
|
break;
|
|
}
|
|
}
|
|
if (reason == UIMENU_RIGHT) {
|
|
switch (index) {
|
|
case 1: // Zoom
|
|
clc_zoom++;
|
|
if (clc_zoom > 6) clc_zoom = 1;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 2: // Bits-Per-Pixel
|
|
if (clc_bpp == 16)
|
|
clc_bpp = 32;
|
|
else
|
|
clc_bpp = 16;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 3: // Fullscreen
|
|
clc_fullscreen = !clc_fullscreen;
|
|
zoomchanged = 1;
|
|
break;
|
|
case 4: // Display FPS
|
|
clc_displayfps = !clc_displayfps;
|
|
break;
|
|
break;
|
|
case 9: // Define Keyboard...
|
|
KeyboardEnterMenu();
|
|
break;
|
|
}
|
|
}
|
|
UIMenu_ChangeItem(UIItems_Platform, 1, "Zoom: %s", clc_zoom_txt[clc_zoom]);
|
|
UIMenu_ChangeItem(UIItems_Platform, 2, "Depth: %dbpp", clc_bpp);
|
|
UIMenu_ChangeItem(UIItems_Platform, 3, "Fullscreen: %s", clc_fullscreen ? "Yes" : "No");
|
|
UIMenu_ChangeItem(UIItems_Platform, 4, "Display FPS: %s", clc_displayfps ? "Yes" : "No");
|
|
if (zoomchanged) {
|
|
SDL_UnlockSurface(screen);
|
|
setup_screen();
|
|
SDL_LockSurface(screen);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Setup screen
|
|
void setup_screen()
|
|
{
|
|
TPokeMini_VideoSpec *videospec;
|
|
int depth, PMOffX, PMOffY, UIOffX, UIOffY;
|
|
|
|
// Calculate size based of zoom
|
|
if (clc_zoom == 1) {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video1x1;
|
|
PMWidth = 192; PMHeight = 128; PMOffX = 48; PMOffY = 32; UIOffX = 0; UIOffY = 0;
|
|
UIMenu_SetDisplay(192, 128, PokeMini_BGR16, (uint8_t *)PokeMini_BG2, (uint16_t *)PokeMini_BG2_PalBGR16, (uint32_t *)PokeMini_BG2_PalBGR32);
|
|
} else if (clc_zoom == 2) {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video2x2;
|
|
PMWidth = 208; PMHeight = 144; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
|
|
UIMenu_SetDisplay(192, 128, PokeMini_BGR16, (uint8_t *)PokeMini_BG2, (uint16_t *)PokeMini_BG2_PalBGR16, (uint32_t *)PokeMini_BG2_PalBGR32);
|
|
} else if (clc_zoom == 3) {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video3x3;
|
|
PMWidth = 304; PMHeight = 208; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
|
|
UIMenu_SetDisplay(288, 192, PokeMini_BGR16, (uint8_t *)PokeMini_BG3, (uint16_t *)PokeMini_BG3_PalBGR16, (uint32_t *)PokeMini_BG3_PalBGR32);
|
|
} else if (clc_zoom == 4) {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video4x4;
|
|
PMWidth = 400; PMHeight = 272; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
|
|
UIMenu_SetDisplay(384, 256, PokeMini_BGR16, (uint8_t *)PokeMini_BG4, (uint16_t *)PokeMini_BG4_PalBGR16, (uint32_t *)PokeMini_BG4_PalBGR32);
|
|
} else if (clc_zoom == 5) {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video5x5;
|
|
PMWidth = 496; PMHeight = 336; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
|
|
UIMenu_SetDisplay(480, 320, PokeMini_BGR16, (uint8_t *)PokeMini_BG5, (uint16_t *)PokeMini_BG5_PalBGR16, (uint32_t *)PokeMini_BG5_PalBGR32);
|
|
} else {
|
|
videospec = (TPokeMini_VideoSpec *)&PokeMini_Video6x6;
|
|
PMWidth = 592; PMHeight = 400; PMOffX = 8; PMOffY = 8; UIOffX = 8; UIOffY = 8;
|
|
UIMenu_SetDisplay(576, 384, PokeMini_BGR16, (uint8_t *)PokeMini_BG6, (uint16_t *)PokeMini_BG6_PalBGR16, (uint32_t *)PokeMini_BG6_PalBGR32);
|
|
}
|
|
|
|
// Set video spec and check if is supported
|
|
depth = PokeMini_SetVideo(videospec, clc_bpp, CommandLine.lcdfilter, CommandLine.lcdmode);
|
|
if (!depth) {
|
|
fprintf(stderr, "Couldn't set video spec from %i bpp\n", clc_bpp);
|
|
exit(1);
|
|
}
|
|
|
|
// Set video mode
|
|
screen = SDL_SetVideoMode(PMWidth, PMHeight, depth, SDL_HWSURFACE | SDL_DOUBLEBUF | (clc_fullscreen ? SDL_FULLSCREEN : 0));
|
|
if (screen == NULL) {
|
|
fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
|
|
// Calculate pitch and offset
|
|
if (depth == 32) {
|
|
PixPitch = screen->pitch / 4;
|
|
PMOff = (PMOffY * screen->pitch) + (PMOffX * 4);
|
|
UIOff = (UIOffY * screen->pitch) + (UIOffX * 4);
|
|
} else {
|
|
PixPitch = screen->pitch / 2;
|
|
PMOff = (PMOffY * screen->pitch) + (PMOffX * 2);
|
|
UIOff = (UIOffY * screen->pitch) + (UIOffX * 2);
|
|
}
|
|
clc_bpp = depth;
|
|
}
|
|
|
|
// Capture screen
|
|
void capture_screen()
|
|
{
|
|
FILE *capf;
|
|
int y, capnum;
|
|
unsigned long Video[96*64];
|
|
PokeMini_VideoPreview_32((uint32_t *)Video, 96, PokeMini_LCDMode);
|
|
capf = OpenUnique_ExportBMP(&capnum, 96, 64);
|
|
if (!capf) {
|
|
fprintf(stderr, "Error while saving capture\n");
|
|
return;
|
|
}
|
|
for (y=0; y<64; y++) {
|
|
WriteArray_ExportBMP(capf, (uint32_t *)&Video[(63-y) * 96], 96);
|
|
}
|
|
printf("Capture saved at 'snap_%03d.bmp'\n", capnum);
|
|
Close_ExportBMP(capf);
|
|
}
|
|
|
|
// Handle keyboard and quit events
|
|
void handleevents(SDL_Event *event)
|
|
{
|
|
switch (event->type) {
|
|
case SDL_KEYDOWN:
|
|
if (event->key.keysym.sym == SDLK_F9) { // Capture screen
|
|
capture_screen();
|
|
} else if (event->key.keysym.sym == SDLK_F4) { // Emulator Exit
|
|
if (event->key.keysym.mod & KMOD_ALT) {
|
|
emurunning = 0;
|
|
}
|
|
} else if (event->key.keysym.sym == SDLK_F10) { // Fullscreen/Window
|
|
clc_fullscreen = !clc_fullscreen;
|
|
setup_screen();
|
|
UIItems_PlatformC(0, UIMENU_LOAD);
|
|
} else if (event->key.keysym.sym == SDLK_F11) { // Disable speed throttling
|
|
emulimiter = !emulimiter;
|
|
} else if (event->key.keysym.sym == SDLK_TAB) { // Temp disable speed throttling
|
|
emulimiter = 0;
|
|
} else {
|
|
KeyboardPressEvent(event->key.keysym.sym);
|
|
}
|
|
break;
|
|
case SDL_KEYUP:
|
|
if (event->key.keysym.sym == SDLK_TAB) { // Speed threhold
|
|
emulimiter = 1;
|
|
} else {
|
|
KeyboardReleaseEvent(event->key.keysym.sym);
|
|
}
|
|
break;
|
|
case SDL_QUIT:
|
|
emurunning = 0;
|
|
break;
|
|
};
|
|
}
|
|
|
|
// Used to fill the sound buffer
|
|
void emulatorsound(void *unused, Uint8 *stream, int len)
|
|
{
|
|
MinxAudio_GetSamplesU8(stream, len);
|
|
if (clc_dump_sound[0]) WriteU8A_ExportWAV(sdump, stream, len>>1);
|
|
}
|
|
|
|
// Enable / Disable sound
|
|
void enablesound(int sound)
|
|
{
|
|
MinxAudio_ChangeEngine(sound);
|
|
if (AudioEnabled) SDL_PauseAudio(!sound);
|
|
}
|
|
|
|
// Menu loop
|
|
void menuloop()
|
|
{
|
|
SDL_Event event;
|
|
|
|
// Update window's title and stop sound
|
|
SDL_WM_SetCaption(AppName, "PMEWindow");
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
|
enablesound(0);
|
|
|
|
// Update EEPROM
|
|
PokeMini_SaveFromCommandLines(0);
|
|
|
|
// Menu's loop
|
|
while (emurunning && (UI_Status == UI_STATUS_MENU)) {
|
|
// Slowdown to approx. 60fps
|
|
SDL_Delay(1);
|
|
|
|
// Process UI
|
|
UIMenu_Process();
|
|
|
|
// Screen rendering
|
|
SDL_FillRect(screen, NULL, 0);
|
|
if (SDL_LockSurface(screen) == 0) {
|
|
// Render the menu or the game screen
|
|
if (PokeMini_VideoDepth == 32)
|
|
UIMenu_Display_32((uint32_t *)((uint8_t *)screen->pixels + UIOff), PixPitch);
|
|
else
|
|
UIMenu_Display_16((uint16_t *)((uint8_t *)screen->pixels + UIOff), PixPitch);
|
|
|
|
// Unlock surface
|
|
SDL_UnlockSurface(screen);
|
|
SDL_Flip(screen);
|
|
}
|
|
|
|
// Handle events
|
|
while (SDL_PollEvent(&event)) handleevents(&event);
|
|
}
|
|
|
|
// Apply configs
|
|
PokeMini_ApplyChanges();
|
|
if (UI_Status == UI_STATUS_EXIT) emurunning = 0;
|
|
else
|
|
{
|
|
if (CommandLine.sound == MINX_AUDIO_GENERATED)
|
|
enablesound(MINX_AUDIO_GENERATED);
|
|
else
|
|
enablesound(0);
|
|
}
|
|
SDL_EnableKeyRepeat(0, 0);
|
|
}
|
|
|
|
// Main function
|
|
int main(int argc, char **argv)
|
|
{
|
|
SDL_Event event;
|
|
char title[256];
|
|
char fpstxt[16];
|
|
|
|
// Process arguments
|
|
printf("%s\n\n", AppName);
|
|
PokeMini_InitDirs(argv[0], NULL);
|
|
CommandLineInit();
|
|
CommandLineConfFile("/tmp0/1/pokemini.cfg", "/tmp0/1/pokemini_sdl.cfg", CustomConf);
|
|
if (!CommandLineArgs(argc, argv, CustomArgs)) {
|
|
PrintHelpUsage(stdout);
|
|
printf(" -dumpsound sound.wav Dump sound into a WAV file\n");
|
|
printf(" -windowed Display in window (default)\n");
|
|
printf(" -fullscreen Display in fullscreen\n");
|
|
printf(" -displayfps Display FPS counter on screen\n");
|
|
printf(" -zoom n Zoom display: 1 to 6 (def 4)\n");
|
|
printf(" -bpp n Bits-Per-Pixel: 16 or 32 (def 16)\n");
|
|
return 1;
|
|
}
|
|
|
|
// Initialize SDL
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
|
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
atexit(SDL_Quit); // Clean up on exit
|
|
|
|
// Initialize the display
|
|
setup_screen();
|
|
|
|
// Initialize the sound
|
|
SDL_AudioSpec audfmt;
|
|
SDL_AudioSpec obtained;
|
|
audfmt.freq = 44100;
|
|
audfmt.format = AUDIO_U8;
|
|
audfmt.channels = 1;
|
|
audfmt.samples = SOUNDBUFFER;
|
|
audfmt.callback = emulatorsound;
|
|
audfmt.userdata = NULL;
|
|
|
|
// Open the audio device
|
|
// Second setting can't be NULL on KolibriOS or else it wont play sound
|
|
if (SDL_OpenAudio(&audfmt, &obtained) < 0) {
|
|
fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
|
|
fprintf(stderr, "Audio will be disabled\n");
|
|
AudioEnabled = 0;
|
|
} else {
|
|
AudioEnabled = 1;
|
|
}
|
|
|
|
// Set the window manager title bar
|
|
SDL_WM_SetCaption(AppName, "PMEWindow");
|
|
SDL_EnableKeyRepeat(0, 0);
|
|
|
|
// Open WAV capture if was requested
|
|
if (clc_dump_sound[0]) {
|
|
sdump = Open_ExportWAV(clc_dump_sound, EXPORTWAV_44KHZ | EXPORTWAV_MONO | EXPORTWAV_16BITS);
|
|
if (!sdump) {
|
|
fprintf(stderr, "Error opening sound export file.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Initialize the emulator
|
|
printf("Starting emulator...\n");
|
|
if (!PokeMini_Create(0, PMSNDBUFFER)) {
|
|
fprintf(stderr, "Error while initializing emulator\n");
|
|
return 1;
|
|
}
|
|
|
|
// Setup palette and LCD mode
|
|
PokeMini_VideoPalette_Init(PokeMini_BGR16, 1);
|
|
PokeMini_VideoPalette_Index(CommandLine.palette, CommandLine.custompal, CommandLine.lcdcontrast, CommandLine.lcdbright);
|
|
PokeMini_ApplyChanges();
|
|
|
|
// Load stuff
|
|
PokeMini_UseDefaultCallbacks();
|
|
if (!PokeMini_LoadFromCommandLines("Using FreeBIOS", "EEPROM data will be discarded!")) {
|
|
UI_Status = UI_STATUS_MENU;
|
|
}
|
|
|
|
// Enable sound & init UI
|
|
printf("Running emulator...\n");
|
|
UIMenu_Init();
|
|
KeyboardRemap(&KeybMapSDL);
|
|
|
|
if (CommandLine.sound == MINX_AUDIO_GENERATED)
|
|
enablesound(MINX_AUDIO_GENERATED);
|
|
else
|
|
enablesound(0);
|
|
|
|
// Emulator's loop
|
|
unsigned long time, NewTickFPS = 0, NewTickSync = 0;
|
|
int fps = 72, fpscnt = 0;
|
|
while (emurunning) {
|
|
// Emulate and syncronize
|
|
time = SDL_GetTicks();
|
|
if (RequireSoundSync) {
|
|
PokeMini_EmulateFrame();
|
|
// Sleep a little in the hope to free a few samples
|
|
if (emulimiter) while (MinxAudio_SyncWithAudio()) SDL_Delay(1);
|
|
} else {
|
|
PokeMini_EmulateFrame();
|
|
if (emulimiter) {
|
|
do {
|
|
SDL_Delay(1); // This lower CPU usage
|
|
time = SDL_GetTicks();
|
|
} while (time < NewTickSync);
|
|
NewTickSync = time + 13; // Aprox 72 times per sec
|
|
}
|
|
}
|
|
|
|
// Screen rendering
|
|
SDL_FillRect(screen, NULL, 0);
|
|
if (SDL_LockSurface(screen) == 0) {
|
|
// Render the menu or the game screen
|
|
if (PokeMini_Rumbling) {
|
|
PokeMini_VideoBlit((void *)((uint8_t *)screen->pixels + PMOff + PokeMini_GenRumbleOffset(screen->pitch)), PixPitch);
|
|
} else {
|
|
PokeMini_VideoBlit((void *)((uint8_t *)screen->pixels + PMOff), PixPitch);
|
|
}
|
|
LCDDirty = 0;
|
|
|
|
// Display FPS counter
|
|
if (clc_displayfps) {
|
|
if (PokeMini_VideoDepth == 32)
|
|
UIDraw_String_32((uint32_t *)screen->pixels, PixPitch, 4, 4, 10, fpstxt, UI_Font1_Pal32);
|
|
else
|
|
UIDraw_String_16((uint16_t *)screen->pixels, PixPitch, 4, 4, 10, fpstxt, UI_Font1_Pal16);
|
|
}
|
|
|
|
// Unlock surface
|
|
SDL_UnlockSurface(screen);
|
|
SDL_Flip(screen);
|
|
}
|
|
|
|
// Handle events
|
|
while (SDL_PollEvent(&event)) handleevents(&event);
|
|
|
|
// Menu
|
|
if (UI_Status == UI_STATUS_MENU) menuloop();
|
|
|
|
// calculate FPS
|
|
fpscnt++;
|
|
if (time >= NewTickFPS) {
|
|
fps = fpscnt;
|
|
sprintf(title, "%s - %d%%", AppName, fps * 100 / 72);
|
|
sprintf(fpstxt, "%i FPS", fps);
|
|
SDL_WM_SetCaption(title, "PMEWindow");
|
|
NewTickFPS = time + 1000;
|
|
fpscnt = 0;
|
|
}
|
|
}
|
|
|
|
// Disable sound & free UI
|
|
enablesound(0);
|
|
UIMenu_Destroy();
|
|
|
|
// Close WAV capture if there's one
|
|
if (clc_dump_sound[0]) Close_ExportWAV(sdump);
|
|
|
|
// Save Stuff
|
|
PokeMini_SaveFromCommandLines(1);
|
|
|
|
// Terminate...
|
|
printf("Shutdown emulator...\n");
|
|
PokeMini_VideoPalette_Free();
|
|
PokeMini_Destroy();
|
|
|
|
return 0;
|
|
}
|