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:
Type | Description | File Extension (to force type) |
@@ -3790,8 +3791,18 @@ Ms Pac-Man (Stella extended codes):
Display.Format: |
Indicates the television format the game was designed for. The value
- must be Auto, NTSC, PAL, SECAM, NTSC50,
- PAL60 or SECAM60. |
+ 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.
+
+ Format | Filename Pattern (to force format) |
+ NTSC | NTSC, NTSC60, NTSC 60, NTSC-60 |
+ PAL | PAL, 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