diff --git a/Changes.txt b/Changes.txt index d2d995e9e..9573b1389 100644 --- a/Changes.txt +++ b/Changes.txt @@ -152,6 +152,8 @@ * Added FC bankswitching for Amiga's Power Play Arcade Video Game Album + * Added auto-detection of display format based on filename. + * Auto-detection of bankswitch scheme by file extension now includes more human-readable formats (not restricted to DOS 3-char length). See the documentation for the new names. diff --git a/docs/index.html b/docs/index.html index fdac67681..b2ab9f1b5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -300,6 +300,7 @@ including partial emulation of the ARM processor
  • Supports cartridge autodetection for almost all bankswitching schemes
  • Supports using ROM filename extensions to force specific bankswitching schemes
  • +
  • Supports using ROM filename to force specific display formats
  • Supports Supercharger single-load and multi-load games
  • Supports ROMs stored in ZIP and GZIP format, as well as the usual raw A26/BIN/ROM formats
  • Supports property file for setting the properties associated with games
  • @@ -3723,7 +3724,7 @@ Ms Pac-Man (Stella extended codes): The value of this property must be either Auto or one of the following (for more information about bank-switching see Kevin Horton's 2600 bankswitching document or the documentation in each cartridge's source code file) types. Types marked - as (¹) do not currently have reliable auto-detection, those marked as (²) + as (¹) do currently have no reliable auto-detection, those marked as (²) are not fully supported in the debugger: @@ -3790,8 +3791,18 @@ Ms Pac-Man (Stella extended codes): + must be Auto or one of the following. Types marked as (¹) + do currently have no reliable auto-detection. A format can be enforced + by using one of the following pattern in the filename. +
     Type DescriptionFile Extension
    (to force type)
    Display.Format: Indicates the television format the game was designed for. The value - must be Auto, NTSC, PAL, SECAM, NTSC50, - PAL60 or SECAM60.
    + + + + + + + +
     Format Filename Pattern (to force format)
    NTSCNTSC, NTSC60, NTSC 60, NTSC-60
    PALPAL, PAL50, PAL 50, PAL-50
    SECAM ¹SECAM, SECAM50, SECAM 50, SECAM-50
    NTSC50 ¹NTSC50, NTSC 50, NTSC-50
    PAL60 ¹PAL60, PAL 60, PAL-60
    SECAM60 ¹SECAM60, SECAM 60, SECAM-60
    diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index e94288c49..6dc7158db 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -119,6 +119,9 @@ Console::Console(OSystem& osystem, unique_ptr& cart, string autodetected = ""; myDisplayFormat = myProperties.get(PropType::Display_Format); + if (myDisplayFormat == "AUTO") + myDisplayFormat = formatFromFilename(); + // Add the real controllers for this system // This must be done before the debugger is initialized const string& md5 = myProperties.get(PropType::Cart_MD5); @@ -149,33 +152,28 @@ Console::Console(OSystem& osystem, unique_ptr& cart, if(myDisplayFormat == "NTSC") { myCurrentFormat = 1; - myConsoleTiming = ConsoleTiming::ntsc; } else if(myDisplayFormat == "PAL") { myCurrentFormat = 2; - myConsoleTiming = ConsoleTiming::pal; } else if(myDisplayFormat == "SECAM") { myCurrentFormat = 3; - myConsoleTiming = ConsoleTiming::secam; } else if(myDisplayFormat == "NTSC50") { myCurrentFormat = 4; - myConsoleTiming = ConsoleTiming::ntsc; } else if(myDisplayFormat == "PAL60") { myCurrentFormat = 5; - myConsoleTiming = ConsoleTiming::pal; } else if(myDisplayFormat == "SECAM60") { myCurrentFormat = 6; - myConsoleTiming = ConsoleTiming::secam; } + setConsoleTiming(); setTIAProperties(); @@ -213,6 +211,24 @@ Console::~Console() myOSystem.sound().close(); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Console::setConsoleTiming() +{ + if (myDisplayFormat == "NTSC" || myDisplayFormat == "NTSC50") + { + myConsoleTiming = ConsoleTiming::ntsc; + } + else if (myDisplayFormat == "PAL" || myDisplayFormat == "PAL60") + { + myConsoleTiming = ConsoleTiming::pal; + } + else if (myDisplayFormat == "SECAM" || myDisplayFormat == "SECAM60") + { + myConsoleTiming = ConsoleTiming::secam; + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Console::autodetectFrameLayout(bool reset) { @@ -255,6 +271,48 @@ void Console::redetectFrameLayout() initializeAudio(); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Console::formatFromFilename() const +{ + std::map Pattern = { + {"NTSC50", "NTSC50"}, {"NTSC 50", "NTSC50"}, {"NTSC-50", "NTSC50"}, + {"PAL60", "PAL60"}, {"PAL 60", "PAL60"}, {"PAL-60", "PAL60"}, + {"SECAM60", "SECAM60"}, {"SECAM 60", "SECAM60"}, {"SECAM-60", "SECAM60"}, + {"NTSC60", "NTSC" }, {"NTSC", "NTSC"}, // also finds "NTSC 60" and "NTSC-60" + {"PAL50", "PAL" }, {"PAL", "PAL"}, // also finds "PAL 50" and "PAL-50" + {"SECAM50", "SECAM"}, {"SECAM", "SECAM"}, // also finds "SECAM 50" and "SECAM-50" + }; + string filename = myOSystem.romFile().getNameWithExt(""); // get filename *without* extension + + for (const auto& item : Pattern) + { + size_t pos = filename.find(item.first); + if (pos != string::npos) + { + // avoid false positives + if (pos == filename.length() - (item.first).length() || // pattern at the very end + ((pos == 0 || isWhiteSpace(filename.at(pos - 1))) && // pattern within withspaces + isWhiteSpace(filename.at(pos + (item.first).length())))) + return item.second; + } + } + // nothing found + return "AUTO"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Console::isWhiteSpace(const char s) const +{ + const string WHITESPACES = " _-()[]<>"; + + for (size_t i = 0; i < WHITESPACES.length(); ++i) + if (s == WHITESPACES[i]) + return true; + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Console::save(Serializer& out) const { @@ -331,13 +389,20 @@ void Console::setFormat(uInt32 format) { if (myFormatAutodetected) return; - string oldDisplayFormat = myDisplayFormat; - redetectFrameLayout(); - myFormatAutodetected = true; + myDisplayFormat = formatFromFilename(); + if (myDisplayFormat == "AUTO") + { + redetectFrameLayout(); + myFormatAutodetected = true; + autodetected = "*"; + message = "Auto-detect mode: " + myDisplayFormat; + } + else + { + message = myDisplayFormat + " mode"; + } saveformat = "AUTO"; - autodetected = "*"; - myConsoleTiming = myDisplayFormat == "PAL" ? ConsoleTiming::pal : ConsoleTiming::ntsc; - message = "Auto-detect mode: " + myDisplayFormat; + setConsoleTiming(); break; } case 1: diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index 5db42048b..05ea929aa 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -327,6 +327,11 @@ class Console : public Serializable, public ConsoleIO void setTIAProperties(); private: + /** + * Define console timing based on current display format + */ + void setConsoleTiming(); + /** * Dry-run the emulation and detect the frame layout (PAL / NTSC). */ @@ -337,6 +342,19 @@ class Console : public Serializable, public ConsoleIO */ void redetectFrameLayout(); + /** + * Determine display format by filename + * Returns "AUTO" if nothing is found + */ + string formatFromFilename() const; + + /** + Check if the given character is a whitespace. + @param s Character to check + @return True if whitespace character + */ + bool isWhiteSpace(const char s) const; + /** Create the audio queue */ diff --git a/src/emucore/KidVid.cxx b/src/emucore/KidVid.cxx index 348534f6e..f9ade13e1 100644 --- a/src/emucore/KidVid.cxx +++ b/src/emucore/KidVid.cxx @@ -59,7 +59,7 @@ void KidVid::update() myBlockIdx = KVBLOCKBITS; myBlock = 0; openSampleFile(); -cerr << "myTape = " << myTape << endl; +//cerr << "myTape = " << myTape << endl; } else if(myEvent.get(Event::KeyboardOne2)) { @@ -68,7 +68,7 @@ cerr << "myTape = " << myTape << endl; myBlockIdx = KVBLOCKBITS; myBlock = 0; openSampleFile(); -cerr << "myTape = " << myTape << endl; +//cerr << "myTape = " << myTape << endl; } else if(myEvent.get(Event::KeyboardOne3)) { @@ -76,13 +76,13 @@ cerr << "myTape = " << myTape << endl; { myTape = 4; myIdx = KVBLOCKBITS; -cerr << "myTape = " << myTape << endl; +//cerr << "myTape = " << myTape << endl; } else /* no, Smurf Save The Day */ { myTape = 1; myIdx = 0; -cerr << "myTape = " << myTape << endl; +//cerr << "myTape = " << myTape << endl; } myBlockIdx = KVBLOCKBITS; myBlock = 0; diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index d507174b6..3929268ad 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -438,7 +438,11 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) if(instance().hasConsole() && myFormat->getSelectedTag().toString() == "AUTO") { const string& format = instance().console().about().DisplayFormat; - string label = format.substr(0, format.length() - 1); + string label; + if (format.at(format.length() - 1) == '*') + label = format.substr(0, format.length() - 1); + else + label = format; myFormatDetected->setLabel(label + " detected"); } else