Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6577 leency 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 .
17
*/
18
 
19
#include 
20
#include 
21
#include 
22
#include 
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
}