81
89
myStateManager(NULL),
86
96
myConsoleFont(NULL)
89
myFeatures += "OpenGL ";
92
myFeatures += "Sound ";
94
#ifdef JOYSTICK_SUPPORT
95
myFeatures += "Joystick ";
97
#ifdef DEBUGGER_SUPPORT
98
myFeatures += "Debugger ";
100
#ifdef CHEATCODE_SUPPORT
101
myFeatures += "Cheats";
98
// Get built-in features
100
myFeatures += "OpenGL ";
103
myFeatures += "Sound ";
105
#ifdef JOYSTICK_SUPPORT
106
myFeatures += "Joystick ";
108
#ifdef DEBUGGER_SUPPORT
109
myFeatures += "Debugger ";
111
#ifdef CHEATCODE_SUPPORT
112
myFeatures += "Cheats";
117
const SDL_version* ver = SDL_Linked_Version();
119
info << "Build " << STELLA_BUILD << ", using ";
120
info << "SDL " << (int)ver->major << "." << (int)ver->minor << "." << (int)ver->patch << " ";
121
info << "[" << BSPF_ARCH << "]";
122
myBuildInfo = info.str();
105
125
// Debugging info for the GUI widgets
106
cerr << " kStaticTextWidget = " << kStaticTextWidget << endl;
107
cerr << " kEditTextWidget = " << kEditTextWidget << endl;
108
cerr << " kButtonWidget = " << kButtonWidget << endl;
109
cerr << " kCheckboxWidget = " << kCheckboxWidget << endl;
110
cerr << " kSliderWidget = " << kSliderWidget << endl;
111
cerr << " kListWidget = " << kListWidget << endl;
112
cerr << " kScrollBarWidget = " << kScrollBarWidget << endl;
113
cerr << " kPopUpWidget = " << kPopUpWidget << endl;
114
cerr << " kTabWidget = " << kTabWidget << endl;
115
cerr << " kEventMappingWidget = " << kEventMappingWidget << endl;
116
cerr << " kEditableWidget = " << kEditableWidget << endl;
117
cerr << " kAudioWidget = " << kAudioWidget << endl;
118
cerr << " kColorWidget = " << kColorWidget << endl;
119
cerr << " kCpuWidget = " << kCpuWidget << endl;
120
cerr << " kDataGridOpsWidget = " << kDataGridOpsWidget << endl;
121
cerr << " kDataGridWidget = " << kDataGridWidget << endl;
122
cerr << " kPromptWidget = " << kPromptWidget << endl;
123
cerr << " kRamWidget = " << kRamWidget << endl;
124
cerr << " kRomListWidget = " << kRomListWidget << endl;
125
cerr << " kRomWidget = " << kRomWidget << endl;
126
cerr << " kTiaInfoWidget = " << kTiaInfoWidget << endl;
127
cerr << " kTiaOutputWidget = " << kTiaOutputWidget << endl;
128
cerr << " kTiaWidget = " << kTiaWidget << endl;
129
cerr << " kTiaZoomWidget = " << kTiaZoomWidget << endl;
130
cerr << " kToggleBitWidget = " << kToggleBitWidget << endl;
131
cerr << " kTogglePixelWidget = " << kTogglePixelWidget << endl;
132
cerr << " kToggleWidget = " << kToggleWidget << endl;
127
buf << " kStaticTextWidget = " << kStaticTextWidget << endl
128
<< " kEditTextWidget = " << kEditTextWidget << endl
129
<< " kButtonWidget = " << kButtonWidget << endl
130
<< " kCheckboxWidget = " << kCheckboxWidget << endl
131
<< " kSliderWidget = " << kSliderWidget << endl
132
<< " kListWidget = " << kListWidget << endl
133
<< " kScrollBarWidget = " << kScrollBarWidget << endl
134
<< " kPopUpWidget = " << kPopUpWidget << endl
135
<< " kTabWidget = " << kTabWidget << endl
136
<< " kEventMappingWidget = " << kEventMappingWidget << endl
137
<< " kEditableWidget = " << kEditableWidget << endl
138
<< " kAudioWidget = " << kAudioWidget << endl
139
<< " kColorWidget = " << kColorWidget << endl
140
<< " kCpuWidget = " << kCpuWidget << endl
141
<< " kDataGridOpsWidget = " << kDataGridOpsWidget << endl
142
<< " kDataGridWidget = " << kDataGridWidget << endl
143
<< " kPromptWidget = " << kPromptWidget << endl
144
<< " kRamWidget = " << kRamWidget << endl
145
<< " kRomListWidget = " << kRomListWidget << endl
146
<< " kRomWidget = " << kRomWidget << endl
147
<< " kTiaInfoWidget = " << kTiaInfoWidget << endl
148
<< " kTiaOutputWidget = " << kTiaOutputWidget << endl
149
<< " kTiaWidget = " << kTiaWidget << endl
150
<< " kTiaZoomWidget = " << kTiaZoomWidget << endl
151
<< " kToggleBitWidget = " << kToggleBitWidget << endl
152
<< " kTogglePixelWidget = " << kTogglePixelWidget << endl
153
<< " kToggleWidget = " << kToggleWidget << endl;
154
logMessage(buf.str(), 0);
173
197
// Get updated paths for all configuration files
174
198
setConfigPaths();
200
buf << "Base directory: '" << myBaseDir << "'" << endl
201
<< "Configuration file: '" << myConfigFile << "'" << endl
202
<< "User game properties: '" << myPropertiesFile << "'" << endl
204
logMessage(buf.str(), 1);
176
206
// Get relevant information about the video hardware
177
207
// This must be done before any graphics context is created, since
178
208
// it may be needed to initialize the size of graphical objects
179
queryVideoHardware();
209
if(!queryVideoHardware())
212
////////////////////////////////////////////////////////////////////
181
213
// Create fonts to draw text
182
// TODO - this should be configurable, and also depend on the minimum
183
// size of the launcher and maximum size of the TIA window
184
// The logic must be taken care of here, so the GUI classes
185
// can just create the interface and not worry about checking
186
myFont = new GUI::Font(GUI::stellaDesc);
214
// NOTE: the logic determining appropriate font sizes is done here,
215
// so that the UI classes can just use the font they expect,
216
// and not worry about it
217
// This logic should also take into account the size of the
218
// framebuffer, and try to be intelligent about font sizes
219
// We can probably add ifdefs to take care of corner cases,
220
// but that means we've failed to abstract it enough ...
221
////////////////////////////////////////////////////////////////////
222
bool smallScreen = myDesktopWidth < 640 || myDesktopHeight < 480;
224
// This font is used in a variety of situations when a really small
225
// font is needed; we let the specific widget/dialog decide when to
227
mySmallFont = new GUI::Font(GUI::stellaDesc);
229
// The console font is always the same size (for now at least)
187
230
myConsoleFont = new GUI::Font(GUI::consoleDesc);
188
if(mySettings->getString("launcherfont") == "small")
232
// The general font used in all UI elements
233
// This is determined by the size of the framebuffer
234
myFont = new GUI::Font(smallScreen ? GUI::stellaDesc : GUI::stellaMediumDesc);
236
// The font used by the ROM launcher
237
// Normally, this is configurable by the user, except in the case of
238
// very small screens
241
if(mySettings->getString("launcherfont") == "small")
242
myLauncherFont = new GUI::Font(GUI::consoleDesc);
243
else if(mySettings->getString("launcherfont") == "medium")
244
myLauncherFont = new GUI::Font(GUI::stellaMediumDesc);
246
myLauncherFont = new GUI::Font(GUI::stellaLargeDesc);
189
249
myLauncherFont = new GUI::Font(GUI::stellaDesc);
191
myLauncherFont = new GUI::Font(GUI::stellaLargeDesc);
193
251
// Create the event handler for the system
194
252
myEventHandler = new EventHandler(this);
239
297
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
240
298
void OSystem::setConfigPaths()
242
myStateDir = mySettings->getString("statedir");
244
myStateDir = myBaseDir + BSPF_PATH_SEPARATOR + "state";
245
if(!FilesystemNode::dirExists(myStateDir))
246
FilesystemNode::makeDir(myStateDir);
247
mySettings->setString("statedir", myStateDir);
249
mySnapshotDir = mySettings->getString("ssdir");
250
if(mySnapshotDir == "")
251
mySnapshotDir = myBaseDir + BSPF_PATH_SEPARATOR + "snapshots";
252
if(!FilesystemNode::dirExists(mySnapshotDir))
253
FilesystemNode::makeDir(mySnapshotDir);
254
mySettings->setString("ssdir", mySnapshotDir);
256
myCheatFile = mySettings->getString("cheatfile");
257
if(myCheatFile == "")
258
myCheatFile = myBaseDir + BSPF_PATH_SEPARATOR + "stella.cht";
259
mySettings->setString("cheatfile", myCheatFile);
261
myPaletteFile = mySettings->getString("palettefile");
262
if(myPaletteFile == "")
263
myPaletteFile = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pal";
264
mySettings->setString("palettefile", myPaletteFile);
266
myPropertiesFile = mySettings->getString("propsfile");
267
if(myPropertiesFile == "")
268
myPropertiesFile = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pro";
269
mySettings->setString("propsfile", myPropertiesFile);
300
// Paths are saved with special characters preserved ('~' or '.')
301
// We do some error checking here, so the rest of the codebase doesn't
302
// have to worry about it
306
s = mySettings->getString("statedir");
307
if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "state";
308
node = FilesystemNode(s);
309
myStateDir = node.getPath();
310
mySettings->setString("statedir", node.getRelativePath());
311
if(!node.isDirectory())
312
AbstractFilesystemNode::makeDir(myStateDir);
314
s = mySettings->getString("ssdir");
315
if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "snapshots";
316
node = FilesystemNode(s);
317
mySnapshotDir = node.getPath();
318
mySettings->setString("ssdir", node.getRelativePath());
319
if(!node.isDirectory())
320
AbstractFilesystemNode::makeDir(mySnapshotDir);
322
s = mySettings->getString("eepromdir");
323
if(s == "") s = myBaseDir;
324
node = FilesystemNode(s);
325
myEEPROMDir = node.getPath();
326
mySettings->setString("eepromdir", node.getRelativePath());
327
if(!node.isDirectory())
328
AbstractFilesystemNode::makeDir(myEEPROMDir);
330
s = mySettings->getString("cheatfile");
331
if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.cht";
332
node = FilesystemNode(s);
333
myCheatFile = node.getPath();
334
mySettings->setString("cheatfile", node.getRelativePath());
336
s = mySettings->getString("palettefile");
337
if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pal";
338
node = FilesystemNode(s);
339
myPaletteFile = node.getPath();
340
mySettings->setString("palettefile", node.getRelativePath());
342
s = mySettings->getString("propsfile");
343
if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pro";
344
node = FilesystemNode(s);
345
myPropertiesFile = node.getPath();
346
mySettings->setString("propsfile", node.getRelativePath());
272
349
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275
352
int palette = mySettings->getInt("uipalette") - 1;
276
353
if(palette < 0 || palette >= kNumUIPalettes) palette = 0;
277
354
myFrameBuffer->setUIPalette(&ourGUIColors[palette][0]);
278
myEventHandler->refreshDisplay();
355
myFrameBuffer->refresh();
281
358
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
282
359
void OSystem::setBaseDir(const string& basedir)
285
if(!FilesystemNode::dirExists(myBaseDir))
286
FilesystemNode::makeDir(myBaseDir);
361
FilesystemNode node(basedir);
362
myBaseDir = node.getPath();
363
if(!node.isDirectory())
364
AbstractFilesystemNode::makeDir(myBaseDir);
367
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
368
void OSystem::setConfigFile(const string& file)
370
FilesystemNode node(file);
371
myConfigFile = node.getPath();
289
374
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
290
375
void OSystem::setFramerate(float framerate)
292
myDisplayFrameRate = framerate;
293
myTimePerFrame = (uInt32)(1000000.0 / myDisplayFrameRate);
379
myDisplayFrameRate = framerate;
380
myTimePerFrame = (uInt32)(1000000.0 / myDisplayFrameRate);
296
384
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
297
bool OSystem::createFrameBuffer(bool showmessage)
385
bool OSystem::createFrameBuffer()
299
// Check if we can re-use the current framebuffer
300
bool changeBuffer = (myFrameBuffer == NULL);
303
if((mySettings->getString("video") == "soft" &&
304
myFrameBuffer->type() != kSoftBuffer) ||
305
(mySettings->getString("video") == "gl" &&
306
myFrameBuffer->type() != kGLBuffer))
309
// Now we only create when absolutely necessary
312
delete myFrameBuffer;
387
// There is only ever one FrameBuffer created per run of Stella
388
// Due to the multi-surface nature of the FrameBuffer, repeatedly
389
// creating and destroying framebuffer objects causes crashes which
390
// are far too invasive to fix right now
391
// Besides, how often does one really switch between software and
392
// OpenGL rendering modes, and even when they do, does it really
393
// need to be dynamic?
395
bool firstTime = (myFrameBuffer == NULL);
313
397
myFrameBuffer = MediaFactory::createVideo(this);
316
399
// Re-initialize the framebuffer to current settings
317
400
switch(myEventHandler->state())
320
403
case EventHandler::S_PAUSE:
321
404
case EventHandler::S_MENU:
322
405
case EventHandler::S_CMDMENU:
323
myConsole->initializeVideo();
406
if(!myConsole->initializeVideo())
324
408
break; // S_EMULATE, S_PAUSE, S_MENU, S_CMDMENU
326
410
case EventHandler::S_LAUNCHER:
327
myLauncher->initializeVideo();
411
if(!myLauncher->initializeVideo())
328
413
break; // S_LAUNCHER
330
415
#ifdef DEBUGGER_SUPPORT
331
416
case EventHandler::S_DEBUGGER:
332
myDebugger->initializeVideo();
417
if(!myDebugger->initializeVideo())
333
419
break; // S_DEBUGGER
422
default: // Should never happen
423
logMessage("ERROR: Unknown emulation state in createFrameBuffer()\n", 0);
340
// Setup the SDL joysticks (must be done after FrameBuffer is created)
341
if(changeBuffer) myEventHandler->setupJoysticks();
343
// Let the system know that we've possibly resized the display
344
if(changeBuffer) myEventHandler->handleResizeEvent();
346
// Update the UI palette
351
switch(myFrameBuffer->type())
427
// The following only need to be done once
430
// Setup the SDL joysticks (must be done after FrameBuffer is created)
431
myEventHandler->setupJoysticks();
433
// Update the UI palette
439
// GOTO are normally considered evil, unless well documented :)
440
// If initialization of video system fails while in OpenGL mode,
441
// attempt to fallback to software mode
443
if(myFrameBuffer && myFrameBuffer->type() == kGLBuffer)
445
logMessage("ERROR: OpenGL mode failed, fallback to software\n", 0);
446
delete myFrameBuffer; myFrameBuffer = NULL;
447
mySettings->setString("video", "soft");
448
bool ret = createFrameBuffer();
354
myFrameBuffer->showMessage("Software mode");
357
myFrameBuffer->showMessage("OpenGL mode");
452
myFrameBuffer->showMessage("OpenGL mode failed, fallback to software",
453
kMiddleCenter, true);
365
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
366
void OSystem::toggleFrameBuffer()
368
#ifdef DISPLAY_OPENGL
369
// First figure out which mode to switch to
370
string video = mySettings->getString("video");
373
else if(video == "gl")
375
else // a driver that doesn't exist was requested, so use software mode
378
// Update the settings and create the framebuffer
379
mySettings->setString("video", video);
380
createFrameBuffer(true); // show onscreen message, re-initialize framebuffer
382
// The palette and phosphor info for the framebuffer will be lost
383
// when a new framebuffer is created; we must restore it
385
myConsole->initializeVideo(false);
389
461
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
410
484
showmessage = true; // we show a message if a ROM is being reloaded
411
485
if(myRomFile == "")
413
cerr << "ERROR: Rom file not specified ..." << endl;
487
logMessage("ERROR: Rom file not specified ...\n", 0);
418
493
myRomFile = romfile;
420
// Open the cartridge image and read it in
424
if(openROM(myRomFile, md5, &image, &size))
496
// Each time a new console is loaded, we simulate a cart removal
497
// Some carts need knowledge of this, as they behave differently
498
// based on how many power-cycles they've been through since plugged in
499
mySettings->setInt("romloadcount", 0);
502
// Create an instance of the 2600 game console
504
myConsole = openConsole(myRomFile, myRomMD5, type, id);
426
// Get all required info for creating a valid console
427
Cartridge* cart = (Cartridge*) NULL;
429
if(queryConsoleInfo(image, size, md5, &cart, props))
507
#ifdef CHEATCODE_SUPPORT
508
myCheatManager->loadCheats(myRomMD5);
510
bool audiofirst = mySettings->getBool("audiofirst");
511
//////////////////////////////////////////////////////////////////////////
512
// For some reason, ATI video drivers for OpenGL in Win32 cause problems
513
// if the sound isn't initialized before the video
514
// According to the SDL documentation, it shouldn't matter what order the
515
// systems are initialized, but apparently it *does* matter
516
// For now, I'll just reverse the ordering, as suggested by 'zagon' at
517
// http://www.atariage.com/forums/index.php?showtopic=126090&view=findpost&p=1648693
518
// Hopefully it won't break anything else
519
//////////////////////////////////////////////////////////////////////////
520
if(audiofirst) myConsole->initializeAudio();
521
myEventHandler->reset(EventHandler::S_EMULATE);
522
if(!createFrameBuffer()) // Takes care of initializeVideo()
431
// Create an instance of the 2600 game console
432
myConsole = new Console(this, cart, props);
433
#ifdef CHEATCODE_SUPPORT
434
myCheatManager->loadCheats(md5);
436
myEventHandler->reset(EventHandler::S_EMULATE);
437
createFrameBuffer(false); // Takes care of initializeVideo()
438
myConsole->initializeAudio();
439
#ifdef DEBUGGER_SUPPORT
440
myDebugger->setConsole(myConsole);
441
myDebugger->initialize();
524
logMessage("ERROR: Couldn't create framebuffer for console\n", 0);
525
myEventHandler->reset(EventHandler::S_LAUNCHER);
528
if(!audiofirst) myConsole->initializeAudio();
445
533
myFrameBuffer->showMessage("New console created");
446
if(mySettings->getBool("showinfo"))
447
cout << "Game console created:" << endl
448
<< " ROM file: " << myRomFile << endl << endl
449
<< myConsole->about() << endl;
451
// Update the timing info for a new console run
454
myFrameBuffer->setCursorState();
459
cerr << "ERROR: Couldn't create console for " << myRomFile << " ..." << endl;
535
myFrameBuffer->showMessage("Multicart " + type + ", loading ROM" + id);
537
buf << "Game console created:" << endl
538
<< " ROM file: " << myRomFile << endl << endl
539
<< getROMInfo(myConsole) << endl;
540
logMessage(buf.str(), 1);
542
// Update the timing info for a new console run
545
myFrameBuffer->setCursorState();
465
cerr << "ERROR: Couldn't open " << myRomFile << " ..." << endl;
550
buf << "ERROR: Couldn't create console for " << myRomFile << endl;
551
logMessage(buf.str(), 0);
469
// Free the image since we don't need it any longer
555
// Also check if certain virtual buttons should be held down
556
// These must be checked each time a new console is being created
557
if(mySettings->getBool("holdreset"))
558
myEventHandler->handleEvent(Event::ConsoleReset, 1);
559
if(mySettings->getBool("holdselect"))
560
myEventHandler->handleEvent(Event::ConsoleSelect, 1);
561
if(mySettings->getBool("holdbutton0"))
562
myEventHandler->handleEvent(Event::JoystickZeroFire1, 1);
482
573
#ifdef CHEATCODE_SUPPORT
483
574
myCheatManager->saveCheats(myConsole->properties().get(Cartridge_MD5));
485
if(mySettings->getBool("showinfo"))
487
double executionTime = (double) myTimingInfo.totalTime / 1000000.0;
488
double framesPerSecond = (double) myTimingInfo.totalFrames / executionTime;
489
cout << "Game console stats:" << endl
490
<< " Total frames drawn: " << myTimingInfo.totalFrames << endl
491
<< " Total time (sec): " << executionTime << endl
492
<< " Frames per second: " << framesPerSecond << endl
577
double executionTime = (double) myTimingInfo.totalTime / 1000000.0;
578
double framesPerSecond = (double) myTimingInfo.totalFrames / executionTime;
579
buf << "Game console stats:" << endl
580
<< " Total frames drawn: " << myTimingInfo.totalFrames << endl
581
<< " Total time (sec): " << executionTime << endl
582
<< " Frames per second: " << framesPerSecond << endl
584
logMessage(buf.str(), 1);
495
586
delete myConsole; myConsole = NULL;
499
590
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
500
void OSystem::createLauncher()
591
bool OSystem::createLauncher()
502
593
myEventHandler->reset(EventHandler::S_LAUNCHER);
503
createFrameBuffer(false);
594
if(!createFrameBuffer())
596
logMessage("ERROR: Couldn't create launcher\n", 0);
504
599
myLauncher->reStack();
505
600
myFrameBuffer->setCursorState();
506
myEventHandler->refreshDisplay();
601
myFrameBuffer->refresh();
508
603
setFramerate(60);
509
604
resetLoopTiming();
512
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
513
bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size)
609
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
610
string OSystem::getROMInfo(const string& romfile)
612
string md5, type, id, result = "";
613
Console* console = openConsole(romfile, md5, type, id);
616
result = getROMInfo(console);
620
result = "ERROR: Couldn't get ROM info for " + romfile + " ...";
625
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
626
string OSystem::MD5FromFile(const string& filename)
632
if((image = openROM(filename, md5, size)) != 0)
633
if(image != 0 && size > 0)
639
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
640
void OSystem::logMessage(const string& message, uInt8 level)
644
else if(level <= (uInt8)mySettings->getInt("showinfo"))
648
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
649
Console* OSystem::openConsole(const string& romfile, string& md5,
650
string& type, string& id)
652
#define CMDLINE_PROPS_UPDATE(cl_name, prop_name) \
653
s = mySettings->getString(cl_name); \
654
if(s != "") props.set(prop_name, s);
656
Console* console = (Console*) NULL;
658
// Open the cartridge image and read it in
661
if((image = openROM(romfile, md5, size)) != 0)
663
// Get a valid set of properties, including any entered on the commandline
664
// For initial creation of the Cart, we're only concerned with the BS type
666
myPropSet->getMD5(md5, props);
668
CMDLINE_PROPS_UPDATE("bs", Cartridge_Type);
669
CMDLINE_PROPS_UPDATE("type", Cartridge_Type);
671
// Now create the cartridge
672
string cartmd5 = md5;
673
type = props.get(Cartridge_Type);
675
Cartridge::create(image, size, cartmd5, type, id, *mySettings);
677
// It's possible that the cart created was from a piece of the image,
678
// and that the md5 (and hence the cart) has changed
679
if(props.get(Cartridge_MD5) != cartmd5)
681
string name = props.get(Cartridge_Name);
682
if(!myPropSet->getMD5(cartmd5, props))
684
// Cart md5 wasn't found, so we create a new props for it
685
props.set(Cartridge_MD5, cartmd5);
686
props.set(Cartridge_Name, name+id);
687
myPropSet->insert(props, false);
691
CMDLINE_PROPS_UPDATE("channels", Cartridge_Sound);
692
CMDLINE_PROPS_UPDATE("ld", Console_LeftDifficulty);
693
CMDLINE_PROPS_UPDATE("rd", Console_RightDifficulty);
694
CMDLINE_PROPS_UPDATE("tv", Console_TelevisionType);
695
CMDLINE_PROPS_UPDATE("sp", Console_SwapPorts);
696
CMDLINE_PROPS_UPDATE("lc", Controller_Left);
697
CMDLINE_PROPS_UPDATE("rc", Controller_Right);
698
s = mySettings->getString("bc");
699
if(s != "") { props.set(Controller_Left, s); props.set(Controller_Right, s); }
700
CMDLINE_PROPS_UPDATE("cp", Controller_SwapPaddles);
701
CMDLINE_PROPS_UPDATE("format", Display_Format);
702
CMDLINE_PROPS_UPDATE("ystart", Display_YStart);
703
CMDLINE_PROPS_UPDATE("height", Display_Height);
704
CMDLINE_PROPS_UPDATE("pp", Display_Phosphor);
705
CMDLINE_PROPS_UPDATE("ppblend", Display_PPBlend);
707
// Finally, create the cart with the correct properties
709
console = new Console(this, cart, props);
714
buf << "ERROR: Couldn't open \'" << romfile << "\'" << endl;
715
logMessage(buf.str(), 0);
718
// Free the image since we don't need it any longer
719
if(image != 0 && size > 0)
725
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
726
uInt8* OSystem::openROM(string file, string& md5, uInt32& size)
728
// This method has a documented side-effect:
729
// It not only loads a ROM and creates an array with its contents,
730
// but also adds a properties entry if the one for the ROM doesn't
731
// contain a valid name
515
735
// Try to open the file as a zipped archive
516
736
// If that fails, we assume it's just a gzipped or normal data file
518
if((tz = unzOpen(rom.c_str())) != NULL)
738
if((tz = unzOpen(file.c_str())) != NULL)
520
740
if(unzGoToFirstFile(tz) == UNZ_OK)
583
807
// Now we make sure that the file has a valid properties entry
584
808
// To save time, only generate an MD5 if we really need one
586
md5 = MD5(*image, *size);
810
md5 = MD5(image, size);
588
812
// Some games may not have a name, since there may not
589
813
// be an entry in stella.pro. In that case, we use the rom name
590
814
// and reinsert the properties object
591
815
Properties props;
592
myPropSet->getMD5(md5, props);
594
string name = props.get(Cartridge_Name);
595
if(name == "Untitled")
816
if(!myPropSet->getMD5(md5, props))
597
818
// Get the filename from the rom pathname
598
string::size_type pos = rom.find_last_of(BSPF_PATH_SEPARATOR);
599
if(pos+1 != string::npos)
601
name = rom.substr(pos+1);
602
props.set(Cartridge_MD5, md5);
603
props.set(Cartridge_Name, name);
604
myPropSet->insert(props, false);
611
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
612
bool OSystem::isValidRomName(const string& filename, string& extension)
614
string::size_type idx = filename.find_last_of('.');
615
if(idx != string::npos)
617
extension = filename.substr(idx+1);
618
return BSPF_strncasecmp(extension.c_str(), "bin", 3) == 0 ||
619
BSPF_strncasecmp(extension.c_str(), "a26", 3) == 0 ||
620
BSPF_strncasecmp(extension.c_str(), "zip", 3) == 0 ||
621
BSPF_strncasecmp(extension.c_str(), "rom", 3) == 0 ||
622
BSPF_strncasecmp(extension.c_str(), "gz", 2) == 0 ;
627
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
628
string OSystem::MD5FromFile(const string& filename)
634
if(openROM(filename, md5, &image, &size))
641
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
642
string OSystem::getROMInfo(const string& romfile)
819
string::size_type pos = file.find_last_of("/\\");
820
if(pos != string::npos) file = file.substr(pos+1);
822
props.set(Cartridge_MD5, md5);
823
props.set(Cartridge_Name, file);
824
myPropSet->insert(props, false);
830
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
831
string OSystem::getROMInfo(const Console* console)
833
const ConsoleInfo& info = console->about();
644
834
ostringstream buf;
646
// Open the cartridge image and read it in
650
if(openROM(romfile, md5, &image, &size))
652
// Get all required info for creating a temporary console
653
Cartridge* cart = (Cartridge*) NULL;
655
if(queryConsoleInfo(image, size, md5, &cart, props))
657
Console* console = new Console(this, cart, props);
659
buf << console->about();
661
buf << "ERROR: Couldn't get ROM info for " << romfile << " ..." << endl;
666
buf << "ERROR: Couldn't open " << romfile << " ..." << endl;
668
// Free the image and console since we don't need it any longer
836
buf << " Cart Name: " << info.CartName << endl
837
<< " Cart MD5: " << info.CartMD5 << endl
838
<< " Controller 0: " << info.Control0 << endl
839
<< " Controller 1: " << info.Control1 << endl
840
<< " Display Format: " << info.DisplayFormat << endl
841
<< " Bankswitch Type: " << info.BankSwitch << endl;
672
843
return buf.str();
675
846
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
676
bool OSystem::queryConsoleInfo(const uInt8* image, uInt32 size,
678
Cartridge** cart, Properties& props)
680
// Get a valid set of properties, including any entered on the commandline
682
myPropSet->getMD5(md5, props);
684
s = mySettings->getString("bs");
685
if(s != "") props.set(Cartridge_Type, s);
686
s = mySettings->getString("type");
687
if(s != "") props.set(Cartridge_Type, s);
688
s = mySettings->getString("channels");
689
if(s != "") props.set(Cartridge_Sound, s);
690
s = mySettings->getString("ld");
691
if(s != "") props.set(Console_LeftDifficulty, s);
692
s = mySettings->getString("rd");
693
if(s != "") props.set(Console_RightDifficulty, s);
694
s = mySettings->getString("tv");
695
if(s != "") props.set(Console_TelevisionType, s);
696
s = mySettings->getString("sp");
697
if(s != "") props.set(Console_SwapPorts, s);
698
s = mySettings->getString("lc");
699
if(s != "") props.set(Controller_Left, s);
700
s = mySettings->getString("rc");
701
if(s != "") props.set(Controller_Right, s);
702
s = mySettings->getString("bc");
703
if(s != "") { props.set(Controller_Left, s); props.set(Controller_Right, s); }
704
s = mySettings->getString("cp");
705
if(s != "") props.set(Controller_SwapPaddles, s);
706
s = mySettings->getString("format");
707
if(s != "") props.set(Display_Format, s);
708
s = mySettings->getString("ystart");
709
if(s != "") props.set(Display_YStart, s);
710
s = mySettings->getString("height");
711
if(s != "") props.set(Display_Height, s);
712
s = mySettings->getString("pp");
713
if(s != "") props.set(Display_Phosphor, s);
714
s = mySettings->getString("ppblend");
715
if(s != "") props.set(Display_PPBlend, s);
716
s = mySettings->getString("hmove");
717
if(s != "") props.set(Emulation_HmoveBlanks, s);
719
*cart = Cartridge::create(image, size, props, *mySettings);
726
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
727
847
void OSystem::resetLoopTiming()
729
memset(&myTimingInfo, 0, sizeof(TimingInfo));
730
myTimingInfo.start = getTicks();
731
myTimingInfo.virt = getTicks();
849
myTimingInfo.start = myTimingInfo.virt = getTicks();
850
myTimingInfo.current = 0;
851
myTimingInfo.totalTime = 0;
852
myTimingInfo.totalFrames = 0;
734
855
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
838
981
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
839
void OSystem::queryVideoHardware()
982
bool OSystem::queryVideoHardware()
841
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
984
// Go ahead and open the video hardware; we're going to need it eventually
985
if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
986
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
844
989
// First get the maximum windowed desktop resolution
845
const SDL_VideoInfo* info = SDL_GetVideoInfo();
846
myDesktopWidth = info->current_w;
847
myDesktopHeight = info->current_h;
990
// Check the 'maxres' setting, which is an undocumented developer feature
991
// that specifies the desktop size
992
// Normally, this wouldn't be set, and we ask SDL directly
994
mySettings->getSize("maxres", w, h);
997
const SDL_VideoInfo* info = SDL_GetVideoInfo();
998
myDesktopWidth = info->current_w;
999
myDesktopHeight = info->current_h;
1003
myDesktopWidth = BSPF_max(w, 320);
1004
myDesktopHeight = BSPF_max(h, 240);
1007
// Various parts of the codebase assume a minimum screen size of 320x240
1008
assert(myDesktopWidth >= 320 && myDesktopHeight >= 240);
849
1010
// Then get the valid fullscreen modes
850
1011
// If there are any errors, just use the desktop resolution