Merge remote-tracking branch 'remotes/origin/master' into feature/dbg-save-as-dialog

This commit is contained in:
thrust26 2020-12-23 17:12:07 +01:00
commit 1a165ca81b
285 changed files with 11355 additions and 5471 deletions

10
.gitignore vendored
View File

@ -11,11 +11,12 @@ out
out.pgo out.pgo
out.pgen out.pgen
stella stella
stella-pgo stella-pgo
stella-pgo-generate stella-pgo-generate
*.diff *.diff
project.xcworkspace/ project.xcworkspace/
xcuserdata/ xcuserdata/
.DS_Store
build/ build/
src/macosx/M6502.ins src/macosx/M6502.ins
*.dSYM *.dSYM
@ -33,4 +34,7 @@ src/**/*.vspx
src/**/**.pdb src/**/**.pdb
Stella.xcscheme Stella.xcscheme
src/tools/fonts/* src/tools/fonts/*
*.sym
a.out
*.json
*.sqlite3

View File

@ -1,6 +1,7 @@
.core-defs: .core-defs:
variables: variables:
JNI_PATH: src/libretro JNI_PATH: src/libretro
MAKEFILE_PATH: src/libretro
CORENAME: stella CORENAME: stella
include: include:
@ -23,46 +24,37 @@ stages:
#Desktop #Desktop
libretro-build-linux-x64: libretro-build-linux-x64:
extends: extends:
- .core-defs
- .libretro-linux-x64-make-default - .libretro-linux-x64-make-default
variables: - .core-defs
MAKEFILE_PATH: src/libretro
MAKEFILE: Makefile
libretro-build-windows-x64: libretro-build-windows-x64:
extends: extends:
- .core-defs
- .libretro-windows-x64-mingw-make-default - .libretro-windows-x64-mingw-make-default
variables: - .core-defs
MAKEFILE_PATH: src/libretro
MAKEFILE: Makefile
# Android # Android
android-armeabi-v7a: android-armeabi-v7a:
extends: extends:
- .core-defs
- .libretro-android-jni-armeabi-v7a - .libretro-android-jni-armeabi-v7a
- .core-defs
android-arm64-v8a: android-arm64-v8a:
extends: extends:
- .core-defs
- .libretro-android-jni-arm64-v8a - .libretro-android-jni-arm64-v8a
- .core-defs
android-x86_64: android-x86_64:
extends: extends:
- .core-defs
- .libretro-android-jni-x86_64 - .libretro-android-jni-x86_64
- .core-defs
android-x86: android-x86:
extends: extends:
- .core-defs
- .libretro-android-jni-x86 - .libretro-android-jni-x86
- .core-defs
# Static # Static
libretro-build-libnx-aarch64: libretro-build-libnx-aarch64:
extends: extends:
- .core-defs
- .libretro-libnx-static-retroarch-master - .libretro-libnx-static-retroarch-master
variables: - .core-defs
MAKEFILE_PATH: src/libretro
MAKEFILE: Makefile

View File

@ -14,23 +14,35 @@
6.4 to 6.5 (December XX, 2020) 6.4 to 6.5 (December XX, 2020)
* Enhanced cut/copy/paste for text editing. (TODO: PromptWidget) * Added high scores saving.
* Added undo and redo to text editing. (TODO: PromptWidget) * Improved text editing functionality (except PromptWidget):
- Enhanced selection and cut/copy/paste from keyboard.
- Added undo and redo support.
- Added mouse support (selection, cut/copy/paste).
- All actions have keyboard shortcuts.
* Added wildcard support to launcher dialog filter. * Various improvements to the ROM launcher:
- Added wildcard support to the dialog filter
* Added option to search subdirectories in launcher. - Added option to search subdirectories
* Added static tooltips to some UI items. * Added static tooltips to some UI items.
* Added dynamic tooltips to most debugger items. * Added dynamic tooltips to most debugger items.
* Added sound to Time Machine playback.
* Extended global hotkeys for input devices & ports settings.
* Increased sample size for CDFJ+. * Increased sample size for CDFJ+.
* Fixed autofire bug for trackball controllers. * Fixed autofire bug for trackball controllers.
* Codebase now uses C++17 features. * Fixed Stelladaptor/2600'daptor devices sometimes not being assigned
correct default mappings.
* Codebase now uses C++17 features, which means a minimum of gcc7
or clang5 for Linux/Mac, and Visual Studio 2019 for Windows.
-Have fun! -Have fun!

4
configure vendored
View File

@ -580,10 +580,6 @@ else
darwin*) darwin*)
DEFINES="$DEFINES -DUNIX -DDARWIN" DEFINES="$DEFINES -DUNIX -DDARWIN"
_host_os=darwin _host_os=darwin
if test "$have_clang" = yes; then
CXXFLAGS="$CXXFLAGS -Wno-documentation-unknown-command -Wno-documentation-pedantic -Wno-poison-system-directories"
CXXFLAGS="$CXXFLAGS -Wno-unknown-warning-option"
fi
;; ;;
irix*) irix*)
DEFINES="$DEFINES -DUNIX" DEFINES="$DEFINES -DUNIX"

View File

@ -223,7 +223,7 @@ present in the debugger):</p>
<p>For space reasons, the Prompt, TIA, I/O and Audio displays are split into <p>For space reasons, the Prompt, TIA, I/O and Audio displays are split into
4 tabs, only one of which is visible at a time. You can use the mouse or 4 tabs, only one of which is visible at a time. You can use the mouse or
keyboard to select which tab you want to view. Control/Cmd + Tab cycles between keyboard to select which tab you want to view. Control/Cmd + Tab cycles between
tabs from left-to-right, Shift + Control/Cmd + Tab cycles right-to-left. tabs from left-to-right, Shift-Control/Cmd + Tab cycles right-to-left.
Pressing Tab (or Shift + Tab) cycles between widgets in the current tab (except Pressing Tab (or Shift + Tab) cycles between widgets in the current tab (except
for in the Prompt Tab, where 'tab' is used for something else).</p> for in the Prompt Tab, where 'tab' is used for something else).</p>
@ -339,7 +339,7 @@ previous rewind operation. The rewind buffer is 100 levels deep by default, the
size can be configured e.g. in the size can be configured e.g. in the
<b><a href="index.html#Debugger">Developer Settings</a> - Time Machine</b> dialog.<p> <b><a href="index.html#Debugger">Developer Settings</a> - Time Machine</b> dialog.<p>
<p>The other operations are Step, Trace, Scan+1, Frame+1 and Run.</p> <p>The other operations are Step, Trace, Scan +1, Frame +1 and Run.</p>
<p>You can also use the buttons from anywhere in the GUI via hotkeys.</p> <p>You can also use the buttons from anywhere in the GUI via hotkeys.</p>
<p> <p>
@ -349,48 +349,48 @@ size can be configured e.g. in the
<th>Function</th> <th>Function</th>
</tr> </tr>
<tr> <tr>
<td>Control-s</td> <td>Control + S</td>
<td>Step</td> <td>Step</td>
</tr> </tr>
<tr> <tr>
<td>Control-t</td> <td>Control + T</td>
<td>Trace</td> <td>Trace</td>
</tr> </tr>
<tr> <tr>
<td>Control-L</td> <td>Control + L</td>
<td>Scan+1</td> <td>Scan +1</td>
</tr> </tr>
<tr> <tr>
<td>Control-f</td> <td>Control + F</td>
<td>Frame+1</td> <td>Frame +1</td>
</tr> </tr>
<tr> <tr>
<td>Alt-Left arrow</td> <td>Alt + Left arrow</td>
<td>Rewind 1</td> <td>Rewind 1</td>
</tr> </tr>
<tr> <tr>
<td>Shift-Alt-Left arrow</td> <td>Shift-Alt + Left arrow</td>
<td>Rewind 10</td> <td>Rewind 10</td>
</tr> </tr>
<tr> <tr>
<td>Alt-Down arrow</td> <td>Alt + Down arrow</td>
<td>Rewind all</td> <td>Rewind all</td>
</tr> </tr>
<tr> <tr>
<td>Alt-Right arrow</td> <td>Alt + Right arrow</td>
<td>Unwind 1</td> <td>Unwind 1</td>
</tr> </tr>
<tr> <tr>
<td>Shift-Alt-Right arrow</td> <td>Shift-Alt + Right arrow</td>
<td>Unwind 10</td> <td>Unwind 10</td>
</tr> </tr>
<tr> <tr>
<td>Alt-Up arrow</td> <td>Alt + Up arrow</td>
<td>Unwind all</td> <td>Unwind all</td>
</tr> </tr>
<tr> <tr>
<td>Backquote (`)</td> <td>Backquote (`)</td>
<td>Run</td> <td>Run, exits debugger</td>
</tr> </tr>
</table> </table>
</p> </p>
@ -433,18 +433,18 @@ Bash-style commands are also supported:</p>
<tr><td>End</td><td>Move cursor to end of line</td></tr> <tr><td>End</td><td>Move cursor to end of line</td></tr>
<tr><td>Delete</td><td>Remove character to right of cursor</td></tr> <tr><td>Delete</td><td>Remove character to right of cursor</td></tr>
<tr><td>Backspace</td><td>Remove character to left of cursor</td></tr> <tr><td>Backspace</td><td>Remove character to left of cursor</td></tr>
<tr><td>Control-a</td><td>Same function as 'Home'</td></tr> <tr><td>Control + A</td><td>Same function as 'Home'</td></tr>
<tr><td>Control-e</td><td>Same function as 'End'</td></tr> <tr><td>Control + E</td><td>Same function as 'End'</td></tr>
<tr><td>Control-d</td><td>Same function as 'Delete'</td></tr> <tr><td>Control + D</td><td>Same function as 'Delete'</td></tr>
<tr><td>Control-k</td><td>Remove all characters from cursor to end of line</td></tr> <tr><td>Control + K</td><td>Remove all characters from cursor to end of line</td></tr>
<tr><td>Control-u</td><td>Remove all characters from cursor to beginning of line</td></tr> <tr><td>Control + U</td><td>Remove all characters from cursor to beginning of line</td></tr>
<tr><td>Control-w</td><td>Remove entire word to left of cursor</td></tr> <tr><td>Control + W</td><td>Remove entire word to left of cursor</td></tr>
<tr><td>Shift-PgUp</td><td>Scroll up through previous commands one screen/page</td></tr> <tr><td>Shift + PgUp</td><td>Scroll up through previous commands one screen/page</td></tr>
<tr><td>Shift-PgDown</td><td>Scroll down through previous commands one screen/page</td></tr> <tr><td>Shift + PgDown</td><td>Scroll down through previous commands one screen/page</td></tr>
<tr><td>Shift-Up</td><td>Scroll up through previous commands one line</td></tr> <tr><td>Shift + Up</td><td>Scroll up through previous commands one line</td></tr>
<tr><td>Shift-Down</td><td>Scroll down through previous commands one line</td></tr> <tr><td>Shift + Down</td><td>Scroll down through previous commands one line</td></tr>
<tr><td>Shift-Home</td><td>Scroll to beginning of commands</td></tr> <tr><td>Shift + Home</td><td>Scroll to beginning of commands</td></tr>
<tr><td>Shift-End</td><td>Scroll to end of commands</td></tr> <tr><td>Shift + End</td><td>Scroll to end of commands</td></tr>
</table> </table>
<p>You can also scroll with the mouse. Copy and paste is not yet supported.</p> <p>You can also scroll with the mouse. Copy and paste is not yet supported.</p>
@ -989,12 +989,12 @@ clearsavestateifs - Clear all savestate points
runto - Run until string xx in disassembly runto - Run until string xx in disassembly
runtopc - Run until PC is set to value xx runtopc - Run until PC is set to value xx
s - Set Stack Pointer to value xx s - Set Stack Pointer to value xx
save - Save breaks, watches, traps and functions to file xx save - Save breaks, watches, traps and functions to file xx (use ? for file dialog)
saveaccess - Save access counters to CSV file saveaccess - Save access counters to CSV file (use ? for file dialog)
saveconfig - Save DiStella config file (with default name) saveconfig - Save DiStella config file (with default name)
savedis - Save DiStella disassembly (with default name) savedis - Save DiStella disassembly (use ? for file dialog)
saverom - Save (possibly patched) ROM (with default name) saverom - Save (possibly patched) ROM (use ? for file dialog)
saveses - Save console session (with default name) saveses - Save console session (use ? for file dialog)
savesnap - Save current TIA image to PNG file savesnap - Save current TIA image to PNG file
saveallstates - Save all emulator states saveallstates - Save all emulator states
savestate - Save emulator state xx (valid args 0-9) savestate - Save emulator state xx (valid args 0-9)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatManager::CheatManager(OSystem& osystem) CheatManager::CheatManager(OSystem& osystem)
: myOSystem(osystem) : myOSystem{osystem}
{ {
} }
@ -253,8 +253,8 @@ void CheatManager::saveCheatDatabase()
return; return;
stringstream out; stringstream out;
for(const auto& iter: myCheatMap) for(const auto& [md5, cheat]: myCheatMap)
out << "\"" << iter.first << "\" " << "\"" << iter.second << "\"" << endl; out << "\"" << md5 << "\" " << "\"" << cheat << "\"" << endl;
try { myOSystem.cheatFile().write(out); } try { myOSystem.cheatFile().write(out); }
catch(...) { return; } catch(...) { return; }

View File

@ -23,9 +23,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheetahCheat::CheetahCheat(OSystem& os, const string& name, const string& code) CheetahCheat::CheetahCheat(OSystem& os, const string& name, const string& code)
: Cheat(os, name, code), : Cheat(os, name, code),
address(0xf000 + unhex(code.substr(0, 3))), address{uInt16(0xf000 + unhex(code.substr(0, 3)))},
value(uInt8(unhex(code.substr(3, 2)))), value{uInt8(unhex(code.substr(3, 2)))},
count(uInt8(unhex(code.substr(5, 1)) + 1)) count{uInt8(unhex(code.substr(5, 1)) + 1)}
{ {
// Back up original data; we need this if the cheat is ever disabled // Back up original data; we need this if the cheat is ever disabled
for(int i = 0; i < count; ++i) for(int i = 0; i < count; ++i)

View File

@ -25,8 +25,8 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamCheat::RamCheat(OSystem& os, const string& name, const string& code) RamCheat::RamCheat(OSystem& os, const string& name, const string& code)
: Cheat(os, name, code), : Cheat(os, name, code),
address(uInt16(unhex(myCode.substr(0, 2)))), address{uInt16(unhex(myCode.substr(0, 2)))},
value(uInt8(unhex(myCode.substr(2, 2)))) value{uInt8(unhex(myCode.substr(2, 2)))}
{ {
} }

View File

@ -22,10 +22,10 @@ using std::lock_guard;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AudioQueue::AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo) AudioQueue::AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo)
: myFragmentSize(fragmentSize), : myFragmentSize{fragmentSize},
myIsStereo(isStereo), myIsStereo{isStereo},
myFragmentQueue(capacity), myFragmentQueue{capacity},
myAllFragments(capacity + 2) myAllFragments{capacity + 2}
{ {
const uInt8 sampleSize = myIsStereo ? 2 : 1; const uInt8 sampleSize = myIsStereo ? 2 : 1;
@ -48,7 +48,7 @@ uInt32 AudioQueue::capacity() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 AudioQueue::size() uInt32 AudioQueue::size() const
{ {
lock_guard<mutex> guard(myMutex); lock_guard<mutex> guard(myMutex);

View File

@ -55,7 +55,7 @@ class AudioQueue
/** /**
Size getter. Size getter.
*/ */
uInt32 size(); uInt32 size() const;
/** /**
Stereo / mono getter. Stereo / mono getter.
@ -120,7 +120,7 @@ class AudioQueue
uInt32 myNextFragment{0}; uInt32 myNextFragment{0};
// We need a mutex for thread safety. // We need a mutex for thread safety.
std::mutex myMutex; mutable std::mutex myMutex;
// The first (empty) enqueue call returns this fragment. // The first (empty) enqueue call returns this fragment.
Int16* myFirstFragmentForEnqueue{nullptr}; Int16* myFirstFragmentForEnqueue{nullptr};

View File

@ -43,8 +43,7 @@ namespace {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AudioSettings::AudioSettings(Settings& settings) AudioSettings::AudioSettings(Settings& settings)
: mySettings(settings), : mySettings{settings}
myIsPersistent(true)
{ {
setPreset(normalizedPreset(mySettings.getInt(SETTING_PRESET))); setPreset(normalizedPreset(mySettings.getInt(SETTING_PRESET)));
} }

View File

@ -129,15 +129,15 @@ class AudioSettings
Settings& mySettings; Settings& mySettings;
Preset myPreset; Preset myPreset{Preset::custom};
uInt32 myPresetSampleRate; uInt32 myPresetSampleRate{0};
uInt32 myPresetFragmentSize; uInt32 myPresetFragmentSize{0};
uInt32 myPresetBufferSize; uInt32 myPresetBufferSize{0};
uInt32 myPresetHeadroom; uInt32 myPresetHeadroom{0};
ResamplingQuality myPresetResamplingQuality; ResamplingQuality myPresetResamplingQuality{ResamplingQuality::nearestNeightbour};
bool myIsPersistent; bool myIsPersistent{true};
}; };
#endif // AUDIO_PARAMTERS_HXX #endif // AUDIO_PARAMTERS_HXX

View File

@ -23,7 +23,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem) EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
: EventHandler(osystem) : EventHandler{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;

View File

@ -31,7 +31,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBBackendSDL2::FBBackendSDL2(OSystem& osystem) FBBackendSDL2::FBBackendSDL2(OSystem& osystem)
: myOSystem(osystem) : myOSystem{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -39,9 +39,8 @@ FBBackendSDL2::FBBackendSDL2(OSystem& osystem)
if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
{ {
ostringstream buf; ostringstream buf;
buf << "ERROR: Couldn't initialize SDL: " << SDL_GetError() << endl; buf << "ERROR: Couldn't initialize SDL: " << SDL_GetError();
Logger::error(buf.str()); throw runtime_error(buf.str());
throw runtime_error("FATAL ERROR");
} }
Logger::debug("FBBackendSDL2::FBBackendSDL2 SDL_Init()"); Logger::debug("FBBackendSDL2::FBBackendSDL2 SDL_Init()");

View File

@ -45,8 +45,8 @@ FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
uInt32 width, uInt32 height, uInt32 width, uInt32 height,
ScalingInterpolation inter, ScalingInterpolation inter,
const uInt32* staticData) const uInt32* staticData)
: myBackend(backend), : myBackend{backend},
myInterpolationMode(inter) myInterpolationMode{inter}
{ {
createSurface(width, height, staticData); createSurface(width, height, staticData);
} }

View File

@ -94,8 +94,8 @@ FilesystemNodeZIP::FilesystemNodeZIP(const string& p)
FilesystemNodeZIP::FilesystemNodeZIP( FilesystemNodeZIP::FilesystemNodeZIP(
const string& zipfile, const string& virtualpath, const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode, bool isdir) const AbstractFSNodePtr& realnode, bool isdir)
: _isDirectory(isdir), : _isDirectory{isdir},
_isFile(!isdir) _isFile{!isdir}
{ {
setFlags(zipfile, virtualpath, realnode); setFlags(zipfile, virtualpath, realnode);
} }

View File

@ -116,7 +116,7 @@ class FilesystemNodeZIP : public AbstractFSNode
const char* start = str.c_str(); const char* start = str.c_str();
const char* cur = start + str.size() - 2; const char* cur = start + str.size() - 2;
while (cur >= start && !(*cur == '/' || *cur == '\\')) while (cur >= start && *cur != FilesystemNode::PATH_SEPARATOR)
--cur; --cur;
return cur + 1; return cur + 1;

View File

@ -19,12 +19,14 @@
using namespace std::chrono; using namespace std::chrono;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FpsMeter::FpsMeter(uInt32 queueSize) FpsMeter::FpsMeter(uInt32 queueSize)
: myQueue(queueSize) : myQueue{queueSize}
{ {
reset(); reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FpsMeter::reset(uInt32 garbageFrameLimit) void FpsMeter::reset(uInt32 garbageFrameLimit)
{ {
myQueue.clear(); myQueue.clear();
@ -35,6 +37,7 @@ void FpsMeter::reset(uInt32 garbageFrameLimit)
myGarbageFrameLimit = garbageFrameLimit; myGarbageFrameLimit = garbageFrameLimit;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FpsMeter::render(uInt32 frameCount) void FpsMeter::render(uInt32 frameCount)
{ {
if (myGarbageFrameCounter < myGarbageFrameLimit) { if (myGarbageFrameCounter < myGarbageFrameLimit) {
@ -67,6 +70,7 @@ void FpsMeter::render(uInt32 frameCount)
if (myTimeInterval > 0) myFps = (myFrameCount - first.frames) / myTimeInterval; if (myTimeInterval > 0) myFps = (myFrameCount - first.frames) / myTimeInterval;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FpsMeter::fps() const float FpsMeter::fps() const
{ {
return myFps; return myFps;

View File

@ -0,0 +1,752 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
/*
Formats (all optional):
4, ; score digits
0, ; trailing zeroes
B, ; score format (BCD, HEX)
0, ; invert score order
B, ; variation format (BCD, HEX)
0, ; zero-based variation
"", ; special label (5 chars)
B, ; special format (BCD, HEX)
0, ; zero-based special
Addresses (in hex):
n-times xx, ; score info, high to low
xx, ; variation address (if more than 1 variation)
xx ; special address (if defined)
TODO:
- variation bits (Centipede)
- score swaps (Asteroids)
- special: one optional and named value extra per game (round, level...)
*/
#include <cmath>
#include "OSystem.hxx"
#include "PropsSet.hxx"
#include "System.hxx"
#include "Cart.hxx"
#include "Console.hxx"
#include "Launcher.hxx"
#include "Base.hxx"
#include "MD5.hxx"
#include "HighScoresManager.hxx"
using namespace BSPF;
using namespace std;
using namespace HSM;
using Common::Base;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HighScoresManager::HighScoresManager(OSystem& osystem)
: myOSystem{osystem}
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int16 HighScoresManager::peek(uInt16 addr) const
{
if (myOSystem.hasConsole())
{
if(addr < 0x100u || myOSystem.console().cartridge().internalRamSize() == 0)
return myOSystem.console().system().peek(addr);
else
return myOSystem.console().cartridge().internalRamGetValue(addr);
}
return NO_VALUE;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const json HighScoresManager::properties(const Properties& props) const
{
const string& property = props.get(PropType::Cart_Highscore);
if(property.empty())
return json::array();
return json::parse(property);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json HighScoresManager::properties(json& jprops) const
{
Properties props;
if(myOSystem.hasConsole())
{
props = myOSystem.console().properties();
}
else
{
const string& md5 = myOSystem.launcher().selectedRomMD5();
myOSystem.propSet().getMD5(md5, props);
}
return jprops = properties(props);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::enabled() const
{
json hsProp;
return properties(hsProp).contains(SCORE_ADDRESSES);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numVariations(const json& jprops) const
{
return min(getPropInt(jprops, VARIATIONS_COUNT, DEFAULT_VARIATION), MAX_VARIATIONS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::get(const Properties& props, uInt32& numVariationsR,
ScoresProps& info) const
{
json jprops = properties(props);
numVariationsR = numVariations(jprops);
info.numDigits = numDigits(jprops);
info.trailingZeroes = trailingZeroes(jprops);
info.scoreBCD = scoreBCD(jprops);
info.scoreInvert = scoreInvert(jprops);
info.varsBCD = varBCD(jprops);
info.varsZeroBased = varZeroBased(jprops);
info.special = specialLabel(jprops);
info.specialBCD = specialBCD(jprops);
info.specialZeroBased = specialZeroBased(jprops);
info.notes = notes(jprops);
info.varsAddr = varAddress(jprops);
info.specialAddr = specialAddress(jprops);
info.scoreAddr = getPropScoreAddr(jprops);
return enabled();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::set(Properties& props, uInt32 numVariations,
const ScoresProps& info) const
{
json jprops = json::object();
// handle variations
jprops[VARIATIONS_COUNT] = min(numVariations, MAX_VARIATIONS);
if(numVariations != DEFAULT_VARIATION)
jprops[VARIATIONS_ADDRESS] = "0x" + Base::toString(info.varsAddr, Base::Fmt::_16);
if(info.varsBCD != DEFAULT_VARS_BCD)
jprops[VARIATIONS_BCD] = info.varsBCD;
if(info.varsZeroBased != DEFAULT_VARS_ZERO_BASED)
jprops[VARIATIONS_ZERO_BASED] = info.varsZeroBased;
// handle score
if(info.numDigits != DEFAULT_DIGITS)
jprops[SCORE_DIGITS] = info.numDigits;
if(info.trailingZeroes != DEFAULT_TRAILING)
jprops[SCORE_TRAILING_ZEROES] = info.trailingZeroes;
if(info.scoreBCD != DEFAULT_SCORE_BCD)
jprops[SCORE_BCD] = info.scoreBCD;
if(info.scoreInvert != DEFAULT_SCORE_REVERSED)
jprops[SCORE_INVERTED] = info.scoreInvert;
uInt32 addrBytes = numAddrBytes(info.numDigits, info.trailingZeroes);
json addresses = json::array();
for(uInt32 a = 0; a < addrBytes; ++a)
addresses.push_back("0x" + Base::toString(info.scoreAddr[a], Base::Fmt::_16));
jprops[SCORE_ADDRESSES] = addresses;
// handle special
if(!info.special.empty())
jprops[SPECIAL_LABEL] = info.special;
if(!info.special.empty())
jprops[SPECIAL_ADDRESS] = "0x" + Base::toString(info.specialAddr, Base::Fmt::_16);
if(info.specialBCD != DEFAULT_SPECIAL_BCD)
jprops[SPECIAL_BCD] = info.specialBCD;
if(info.specialZeroBased != DEFAULT_SPECIAL_ZERO_BASED)
jprops[SPECIAL_ZERO_BASED] = info.specialZeroBased;
// handle notes
if(!info.notes.empty())
jprops[NOTES] = info.notes;
props.set(PropType::Cart_Highscore, jprops.dump());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numDigits(const json& jprops) const
{
return min(getPropInt(jprops, SCORE_DIGITS, DEFAULT_DIGITS), MAX_SCORE_DIGITS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::trailingZeroes(const json& jprops) const
{
return min(getPropInt(jprops, SCORE_TRAILING_ZEROES, DEFAULT_TRAILING), MAX_TRAILING);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::scoreBCD(const json& jprops) const
{
return getPropBool(jprops, SCORE_BCD, DEFAULT_SCORE_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::scoreInvert(const json& jprops) const
{
return getPropBool(jprops, SCORE_INVERTED, DEFAULT_SCORE_REVERSED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::varBCD(const json& jprops) const
{
return getPropBool(jprops, VARIATIONS_BCD, DEFAULT_VARS_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::varZeroBased(const json& jprops) const
{
return getPropBool(jprops, VARIATIONS_ZERO_BASED, DEFAULT_VARS_ZERO_BASED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::specialLabel(const json& jprops) const
{
return getPropStr(jprops, SPECIAL_LABEL);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::specialBCD(const json& jprops) const
{
return getPropBool(jprops, SPECIAL_BCD, DEFAULT_SPECIAL_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::specialZeroBased(const json& jprops) const
{
return getPropBool(jprops, SPECIAL_ZERO_BASED, DEFAULT_SPECIAL_ZERO_BASED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::notes(const json& jprops) const
{
return getPropStr(jprops, NOTES);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::varAddress(const json& jprops) const
{
return getPropAddr(jprops, VARIATIONS_ADDRESS, DEFAULT_ADDRESS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::specialAddress(const json& jprops) const
{
return getPropAddr(jprops, SPECIAL_ADDRESS, DEFAULT_ADDRESS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numAddrBytes(Int32 digits, Int32 trailing) const
{
return (digits - trailing + 1) / 2;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numAddrBytes(const json& jprops) const
{
return numAddrBytes(numDigits(jprops), trailingZeroes(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::numVariations() const
{
json jprops;
return numVariations(properties(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::specialLabel() const
{
json jprops;
return specialLabel(properties(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::variation(uInt16 addr, bool varBCD, bool zeroBased,
uInt32 numVariations) const
{
if (!myOSystem.hasConsole())
return DEFAULT_VARIATION;
Int32 var = peek(addr);
return convert(var, numVariations, varBCD, zeroBased);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::variation() const
{
json jprops;
uInt16 addr = varAddress(properties(jprops));
if(addr == DEFAULT_ADDRESS) {
if(numVariations() == 1)
return DEFAULT_VARIATION;
else
return NO_VALUE;
}
return variation(addr, varBCD(jprops), varZeroBased(jprops), numVariations(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::score(uInt32 numAddrBytes, uInt32 trailingZeroes,
bool isBCD, const ScoreAddresses& scoreAddr) const
{
if (!myOSystem.hasConsole())
return NO_VALUE;
Int32 totalScore = 0;
for (uInt32 b = 0; b < numAddrBytes; ++b)
{
uInt16 addr = scoreAddr[b];
Int32 score;
totalScore *= isBCD ? 100 : 256;
score = peek(addr);
if (isBCD)
{
score = fromBCD(score);
// verify if score is legit
if (score == NO_VALUE)
return NO_VALUE;
}
totalScore += score;
}
if (totalScore != NO_VALUE)
for (uInt32 i = 0; i < trailingZeroes; ++i)
totalScore *= 10;
return totalScore;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::score() const
{
json jprops;
uInt32 numBytes = numAddrBytes(properties(jprops));
const ScoreAddresses scoreAddr = getPropScoreAddr(jprops);
if(uInt32(scoreAddr.size()) < numBytes)
return NO_VALUE;
return score(numBytes, trailingZeroes(jprops), scoreBCD(jprops), scoreAddr);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::formattedScore(Int32 score, Int32 width) const
{
if(score <= 0)
return "";
ostringstream buf;
json jprops;
Int32 digits = numDigits(properties(jprops));
if(scoreBCD(jprops))
{
if(width > digits)
digits = width;
buf << std::setw(digits) << std::setfill(' ') << score;
}
else {
if(width > digits)
buf << std::setw(width - digits) << std::setfill(' ') << "";
buf << std::hex << std::setw(digits) << std::setfill('0') << score;
}
return buf.str();
}
string HighScoresManager::md5Props() const
{
json jprops;
properties(jprops);
ostringstream buf;
buf << varAddress(jprops) << numVariations() << varBCD(jprops)
<< varZeroBased(jprops);
uInt32 addrBytes = numAddrBytes(jprops);
HSM::ScoreAddresses addr = getPropScoreAddr(jprops);
for(uInt32 a = 0; a < addrBytes; ++a)
buf << addr[a];
buf << numDigits(jprops) << trailingZeroes(jprops) << scoreBCD(jprops)
<< scoreInvert(jprops) << specialAddress(jprops) << specialBCD(jprops)
<< specialZeroBased(jprops);
buf << specialAddress(jprops) << specialBCD(jprops) << specialZeroBased(jprops);
return MD5::hash(buf.str());
}
bool HighScoresManager::scoreInvert() const
{
json jprops;
return scoreInvert(properties(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::special() const
{
if(!myOSystem.hasConsole())
return NO_VALUE;
json jprops;
uInt16 addr = specialAddress(properties(jprops));
if (addr == DEFAULT_ADDRESS)
return NO_VALUE;
Int32 var = peek(addr);
if(specialBCD(jprops))
var = fromBCD(var);
var += specialZeroBased(jprops) ? 1 : 0;
return var;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::notes() const
{
json jprops;
return notes(properties(jprops));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::convert(Int32 val, uInt32 maxVal, bool isBCD, bool zeroBased) const
{
//maxVal += zeroBased ? 0 : 1;
maxVal -= zeroBased ? 1 : 0;
Int32 bits = isBCD
? ceil(log(maxVal) / log(10) * 4)
: ceil(log(maxVal) / log(2));
// limit to maxVal's bits
val %= 1 << bits;
if (isBCD)
val = fromBCD(val);
if(val == NO_VALUE)
return 0;
val += zeroBased ? 1 : 0;
return val;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::getPropBool(const json& jprops, const string& key,
bool defVal) const
{
return jprops.contains(key) ? jprops.at(key).get<bool>() : defVal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::getPropInt(const json& jprops, const string& key,
uInt32 defVal) const
{
return jprops.contains(key) ? jprops.at(key).get<uInt32>() : defVal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::getPropStr(const json& jprops, const string& key,
const string& defVal) const
{
return jprops.contains(key) ? jprops.at(key).get<string>() : defVal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::getPropAddr(const json& jprops, const string& key,
uInt16 defVal) const
{
const string str = getPropStr(jprops, key);
return str.empty() ? defVal : fromHexStr(str);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const HSM::ScoreAddresses HighScoresManager::getPropScoreAddr(const json& jprops) const
{
ScoreAddresses scoreAddr{};
if(jprops.contains(SCORE_ADDRESSES))
{
const json addrProps = jprops.at(SCORE_ADDRESSES);
if(!addrProps.empty() && addrProps.is_array())
{
int a = 0;
for(const json& addresses : addrProps)
{
const string address = addresses.get<string>();
if(address.empty())
scoreAddr[a++] = DEFAULT_ADDRESS;
else
scoreAddr[a++] = fromHexStr(address);
}
}
}
return scoreAddr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::fromHexStr(const string& addr) const
{
string naked = addr;
if(int pos = naked.find("0x") != std::string::npos)
naked = naked.substr(pos + 1);
return stringToIntBase16(naked);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::fromBCD(uInt8 bcd) const
{
// verify if score is legit
if ((bcd & 0xF0) >= 0xA0 || (bcd & 0xF) >= 0xA)
return NO_VALUE;
return (bcd >> 4) * 10 + bcd % 16;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::saveHighScores(const string& cartName, ScoresData& data) const
{
ostringstream buf;
buf << myOSystem.stateDir() << cartName << ".hs" << data.variation;
// Make sure the file can be opened for writing
FilesystemNode node(buf.str());
if(!node.isWritable())
{
buf.str("");
buf << "Can't open/save to high scores file for variation " << data.variation;
myOSystem.frameBuffer().showTextMessage(buf.str());
}
// Do a complete high data save
if(!save(node, data))
{
buf.str("");
buf << "Error saving high scores for variation" << data.variation;
myOSystem.frameBuffer().showTextMessage(buf.str());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::loadHighScores(const string& cartName, ScoresData& data)
{
for(uInt32 r = 0; r < NUM_RANKS; ++r)
{
data.scores[r].score = 0;
data.scores[r].special = 0;
data.scores[r].name = "";
data.scores[r].date = "";
}
ostringstream buf;
buf << myOSystem.stateDir() << cartName << ".hs" << data.variation;
FilesystemNode node(buf.str());
stringstream in;
// Make sure the file can be opened
try {
node.read(in);
}
catch(...) { return; }
bool invalid = false;
try {
string highscores;
buf.str("");
if(getline(in, highscores) && highscores.length() != 0)
{
const json hsObject = json::parse(highscores);
if(hsObject.contains(DATA))
{
const json hsData = hsObject.at(DATA);
// First test if we have a valid header
// If so, do a complete high data load
if(!hsData.contains(VERSION) || hsData.at(VERSION) != HIGHSCORE_HEADER)
buf << "Error: Incompatible high scores file for variation "
<< data.variation << ".";
else
{
if(!load(hsData, data)
|| !hsData.contains(PROPCHECK) || hsData.at(PROPCHECK) != md5Props()
|| !hsObject.contains(CHECKSUM) || hsObject.at(CHECKSUM) != MD5::hash(hsData.dump()))
invalid = true;
else
return;
}
}
else
invalid = true;
}
}
catch(...) { invalid = true; }
if(invalid)
buf << "Error: Invalid data in high scores file for variation " << data.variation << ".";
myOSystem.frameBuffer().showTextMessage(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::save(FilesystemNode& node, const ScoresData& data) const
{
try
{
json hsObject = json::object();
json hsData = json::object();
// Add header so that if the high score format changes in the future,
// we'll know right away, without having to parse the rest of the file
hsData[VERSION] = HIGHSCORE_HEADER;
hsData[MD5] = data.md5;
hsData[VARIATION] = data.variation;
json jScores = json::array();
for(uInt32 r = 0; r < NUM_RANKS && data.scores[r].score; ++r)
{
json jScore = json::object();
jScore[SCORE] = data.scores[r].score;
jScore[SPECIAL] = data.scores[r].special;
jScore[NAME] = data.scores[r].name;
jScore[DATE] = data.scores[r].date;
jScores.push_back(jScore);
}
hsData[SCORES] = jScores;
hsData[PROPCHECK] = md5Props();
hsObject[DATA] = hsData;
hsObject[CHECKSUM] = MD5::hash(hsData.dump());
stringstream ss(hsObject.dump());
node.write(ss);
}
catch(...)
{
cerr << "ERROR: HighScoresManager::save() exception\n";
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::load(const json& hsData, ScoresData& data)
{
if(!hsData.contains(MD5) || hsData.at(MD5) != data.md5
|| !hsData.contains(VARIATION) || hsData.at(VARIATION) != data.variation
|| !hsData.contains(SCORES))
return false;
const json& jScores = hsData.at(SCORES);
if(!jScores.empty() && jScores.is_array())
{
uInt32 r = 0;
for(const json& jScore : jScores)
{
if(jScore.contains(SCORE))
data.scores[r].score = jScore.at(SCORE).get<Int32>();
if(jScore.contains(SPECIAL))
data.scores[r].special = jScore.at(SPECIAL).get<Int32>();
if(jScore.contains(NAME))
data.scores[r].name = jScore.at(NAME).get<string>();
if(jScore.contains(DATE))
data.scores[r].date = jScore.at(DATE).get<string>();
if(++r == NUM_RANKS)
break;
}
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string HighScoresManager::VARIATIONS_COUNT = "variations_count";
const string HighScoresManager::VARIATIONS_ADDRESS = "variations_address";
const string HighScoresManager::VARIATIONS_BCD = "variations_bcd";
const string HighScoresManager::VARIATIONS_ZERO_BASED = "variations_zero_based";
const string HighScoresManager::SCORE_DIGITS = "score_digits";
const string HighScoresManager::SCORE_TRAILING_ZEROES = "score_trailing_zeroes";
const string HighScoresManager::SCORE_BCD = "score_bcd";
const string HighScoresManager::SCORE_INVERTED = "score_inverted";
const string HighScoresManager::SCORE_ADDRESSES = "score_addresses";
const string HighScoresManager::SPECIAL_LABEL = "special_label";
const string HighScoresManager::SPECIAL_ADDRESS = "special_address";
const string HighScoresManager::SPECIAL_BCD = "special_bcd";
const string HighScoresManager::SPECIAL_ZERO_BASED = "special_zero_based";
const string HighScoresManager::NOTES = "notes";
const string HighScoresManager::DATA = "data";
const string HighScoresManager::VERSION = "version";
const string HighScoresManager::MD5 = "md5";
const string HighScoresManager::VARIATION = "variation";
const string HighScoresManager::SCORES = "scores";
const string HighScoresManager::SCORE = "score";
const string HighScoresManager::SPECIAL = "special";
const string HighScoresManager::NAME = "name";
const string HighScoresManager::DATE = "date";
const string HighScoresManager::PROPCHECK = "propcheck";
const string HighScoresManager::CHECKSUM = "checksum";

View File

@ -0,0 +1,265 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef HIGHSCORES_MANAGER_HXX
#define HIGHSCORES_MANAGER_HXX
#define HIGHSCORE_HEADER "06010000highscores"
class OSystem;
#include "Props.hxx"
#include "json_lib.hxx"
#include "FSNode.hxx"
using json = nlohmann::json;
/**
This class provides an interface to all things related to high scores.
@author Thomas Jentzsch
*/
namespace HSM {
static constexpr uInt32 MAX_SCORE_DIGITS = 8;
static constexpr uInt32 MAX_ADDR_CHARS = MAX_SCORE_DIGITS / 2;
static constexpr uInt32 MAX_SCORE_ADDR = 4;
static constexpr uInt32 MAX_SPECIAL_NAME = 5;
static constexpr uInt32 MAX_SPECIAL_DIGITS = 3;
static constexpr uInt32 DEFAULT_VARIATION = 1;
static constexpr uInt32 DEFAULT_ADDRESS = 0;
static constexpr Int32 NO_VALUE = -1;
using ScoreAddresses = array<Int16, MAX_SCORE_ADDR>;
static const uInt32 NUM_RANKS = 10;
struct ScoresProps {
// Formats
uInt32 numDigits;
uInt32 trailingZeroes;
bool scoreBCD;
bool scoreInvert;
bool varsBCD;
bool varsZeroBased;
string special;
bool specialBCD;
bool specialZeroBased;
string notes;
// Addresses
ScoreAddresses scoreAddr;
uInt16 varsAddr;
uInt16 specialAddr;
};
struct ScoreEntry {
Int32 score;
Int32 special;
string name;
string date;
};
struct ScoresData {
Int32 variation;
string md5;
ScoreEntry scores[NUM_RANKS];
};
} // namespace HSM
/**
This class provides an interface to define, load and save scores. It is meant
for games which do not support saving highscores.
@author Thomas Jentzsch
*/
class HighScoresManager
{
public:
explicit HighScoresManager(OSystem& osystem);
virtual ~HighScoresManager() = default;
// check if high score data has been defined
bool enabled() const;
/**
Get the highscore data of game's properties
@return True if highscore data exists, else false
*/
bool get(const Properties& props, uInt32& numVariations,
HSM::ScoresProps& info) const;
/**
Set the highscore data of game's properties
*/
void set(Properties& props, uInt32 numVariations,
const HSM::ScoresProps& info) const;
/**
Calculate the score from given parameters
@return The current score or -1 if no valid data exists
*/
Int32 score(uInt32 numAddrBytes, uInt32 trailingZeroes, bool isBCD,
const HSM::ScoreAddresses& scoreAddr) const;
// Convert the given value, using only the maximum bits required by maxVal
// and adjusted for BCD and zero based data
Int32 convert(Int32 val, uInt32 maxVal, bool isBCD, bool zeroBased) const;
/**
Calculate the number of bytes for one player's score from given parameters
@return The number of score address bytes
*/
uInt32 numAddrBytes(Int32 digits, Int32 trailing) const;
// Retrieve current values (using game's properties)
Int32 numVariations() const;
const string specialLabel() const;
Int32 variation() const;
Int32 score() const;
const string formattedScore(Int32 score, Int32 width = -1) const;
bool scoreInvert() const;
Int32 special() const;
const string notes() const;
// Get md5 property definition checksum
string md5Props() const;
// Peek into memory
Int16 peek(uInt16 addr) const;
void saveHighScores(const string& cartName, HSM::ScoresData& scores) const;
void loadHighScores(const string& cartName, HSM::ScoresData& scores);
private:
static const string VARIATIONS_COUNT;
static const string VARIATIONS_ADDRESS;
static const string VARIATIONS_BCD;
static const string VARIATIONS_ZERO_BASED;
static const string SCORE_DIGITS;
static const string SCORE_TRAILING_ZEROES;
static const string SCORE_BCD;
static const string SCORE_INVERTED;
static const string SCORE_ADDRESSES;
static const string SPECIAL_LABEL;
static const string SPECIAL_ADDRESS;
static const string SPECIAL_BCD;
static const string SPECIAL_ZERO_BASED;
static const string NOTES;
static constexpr uInt32 MAX_VARIATIONS = 256;
static constexpr uInt32 MAX_TRAILING = 3;
static constexpr uInt32 DEFAULT_DIGITS = 4;
static constexpr uInt32 DEFAULT_TRAILING = 0;
static constexpr bool DEFAULT_SCORE_BCD = true;
static constexpr bool DEFAULT_SCORE_REVERSED = false;
static constexpr bool DEFAULT_VARS_BCD = true;
static constexpr bool DEFAULT_VARS_ZERO_BASED = false;
static constexpr bool DEFAULT_SPECIAL_BCD = true;
static constexpr bool DEFAULT_SPECIAL_ZERO_BASED = false;
static const string DATA;
static const string VERSION;
static const string MD5;
static const string VARIATION;
static const string SCORES;
static const string SCORE;
static const string SPECIAL;
static const string NAME;
static const string DATE;
static const string PROPCHECK;
static const string CHECKSUM;
private:
// Retrieve current values from (using given parameters)
Int32 variation(uInt16 addr, bool varBCD, bool zeroBased, uInt32 numVariations) const;
// Get individual highscore info from properties
uInt32 numVariations(const json& jprops) const;
uInt16 varAddress(const json& jprops) const;
uInt16 specialAddress(const json& jprops) const;
uInt32 numDigits(const json& jprops) const;
uInt32 trailingZeroes(const json& jprops) const;
bool scoreBCD(const json& jprops) const;
bool scoreInvert(const json& jprops) const;
bool varBCD(const json& jprops) const;
bool varZeroBased(const json& jprops) const;
const string specialLabel(const json& jprops) const;
bool specialBCD(const json& jprops) const;
bool specialZeroBased(const json& jprops) const;
const string notes(const json& jprops) const;
// Calculate the number of bytes for one player's score from property parameters
uInt32 numAddrBytes(const json& jprops) const;
// Get properties
const json properties(const Properties& props) const;
json properties(json& jprops) const;
// Get value from highscore properties for given key
bool getPropBool(const json& jprops, const string& key,
bool defVal = false) const;
uInt32 getPropInt(const json& jprops, const string& key,
uInt32 defVal = 0) const;
const string getPropStr(const json& jprops, const string& key,
const string& defVal = "") const;
uInt16 getPropAddr(const json& jprops, const string& key,
uInt16 defVal = 0) const;
const HSM::ScoreAddresses getPropScoreAddr(const json& jprops) const;
uInt16 fromHexStr(const string& addr) const;
Int32 fromBCD(uInt8 bcd) const;
/**
Saves the current high scores for this game and variation to the given file system node.
@param node The file system node to save to.
@param scores The saved high score data
@return The result of the save. True on success, false on failure.
*/
bool save(FilesystemNode& node, const HSM::ScoresData& scores) const;
/**
Loads the current high scores for this game and variation from the given JSON object.
@param hsData The JSON to parse
@param scores The loaded high score data
@return The result of the load. True on success, false on failure.
*/
bool load(const json& hsData, HSM::ScoresData& scores);
private:
// Reference to the osystem object
OSystem& myOSystem;
private:
// Following constructors and assignment operators not supported
HighScoresManager() = delete;
HighScoresManager(const HighScoresManager&) = delete;
HighScoresManager(HighScoresManager&&) = delete;
HighScoresManager& operator=(const HighScoresManager&) = delete;
HighScoresManager& operator=(HighScoresManager&&) = delete;
};
#endif

View File

@ -66,7 +66,7 @@ void JoyMap::erase(const EventMode mode, const int button,
Event::Type JoyMap::get(const JoyMapping& mapping) const Event::Type JoyMap::get(const JoyMapping& mapping) const
{ {
auto find = myMap.find(mapping); auto find = myMap.find(mapping);
if (find != myMap.end()) if(find != myMap.end())
return find->second; return find->second;
// try without button as modifier // try without button as modifier
@ -75,7 +75,7 @@ Event::Type JoyMap::get(const JoyMapping& mapping) const
m.button = JOY_CTRL_NONE; m.button = JOY_CTRL_NONE;
find = myMap.find(m); find = myMap.find(m);
if (find != myMap.end()) if(find != myMap.end())
return find->second; return find->second;
return Event::Type::NoType; return Event::Type::NoType;
@ -96,7 +96,7 @@ Event::Type JoyMap::get(const EventMode mode, const int button,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JoyMap::check(const JoyMapping & mapping) const bool JoyMap::check(const JoyMapping& mapping) const
{ {
auto find = myMap.find(mapping); auto find = myMap.find(mapping);
@ -117,14 +117,14 @@ string JoyMap::getDesc(const Event::Type event, const JoyMapping& mapping) const
ostringstream buf; ostringstream buf;
// button description // button description
if (mapping.button != JOY_CTRL_NONE) if(mapping.button != JOY_CTRL_NONE)
buf << "/B" << mapping.button; buf << "/B" << mapping.button;
// axis description // axis description
if (mapping.axis != JoyAxis::NONE) if(mapping.axis != JoyAxis::NONE)
{ {
buf << "/A"; buf << "/A";
switch (mapping.axis) switch(mapping.axis)
{ {
case JoyAxis::X: buf << "X"; break; case JoyAxis::X: buf << "X"; break;
case JoyAxis::Y: buf << "Y"; break; case JoyAxis::Y: buf << "Y"; break;
@ -132,19 +132,19 @@ string JoyMap::getDesc(const Event::Type event, const JoyMapping& mapping) const
default: buf << int(mapping.axis); break; default: buf << int(mapping.axis); break;
} }
if (Event::isAnalog(event)) if(Event::isAnalog(event))
buf << "+|-"; buf << "+|-";
else if (mapping.adir == JoyDir::NEG) else if(mapping.adir == JoyDir::NEG)
buf << "-"; buf << "-";
else else
buf << "+"; buf << "+";
} }
// hat description // hat description
if (mapping.hat != JOY_CTRL_NONE) if(mapping.hat != JOY_CTRL_NONE)
{ {
buf << "/H" << mapping.hat; buf << "/H" << mapping.hat;
switch (mapping.hdir) switch(mapping.hdir)
{ {
case JoyHatDir::UP: buf << "Y+"; break; case JoyHatDir::UP: buf << "Y+"; break;
case JoyHatDir::DOWN: buf << "Y-"; break; case JoyHatDir::DOWN: buf << "Y-"; break;
@ -162,13 +162,13 @@ string JoyMap::getEventMappingDesc(int stick, const Event::Type event, const Eve
{ {
ostringstream buf; ostringstream buf;
for (auto item : myMap) for (const auto& [_mapping, _event]: myMap)
{ {
if (item.second == event && item.first.mode == mode) if (_event == event && _mapping.mode == mode)
{ {
if (buf.str() != "") if(buf.str() != "")
buf << ", "; buf << ", ";
buf << "J" << stick << getDesc(event, item.first); buf << "J" << stick << getDesc(event, _mapping);
} }
} }
return buf.str(); return buf.str();
@ -179,9 +179,9 @@ JoyMap::JoyMappingArray JoyMap::getEventMapping(const Event::Type event, const E
{ {
JoyMappingArray map; JoyMappingArray map;
for (auto item : myMap) for (const auto& [_mapping, _event]: myMap)
if (item.second == event && item.first.mode == mode) if (_event == event && _mapping.mode == mode)
map.push_back(item.first); map.push_back(_mapping);
return map; return map;
} }
@ -189,25 +189,51 @@ JoyMap::JoyMappingArray JoyMap::getEventMapping(const Event::Type event, const E
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json JoyMap::saveMapping(const EventMode mode) const json JoyMap::saveMapping(const EventMode mode) const
{ {
using MapType = std::pair<JoyMapping, Event::Type>;
std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
std::sort(sortedMap.begin(), sortedMap.end(),
[](const MapType& a, const MapType& b)
{
// Event::Type first
if(a.first.button != b.first.button)
return a.first.button < b.first.button;
if(a.first.axis != b.first.axis)
return a.first.axis < b.first.axis;
if(a.first.adir != b.first.adir)
return a.first.adir < b.first.adir;
if(a.first.hat != b.first.hat)
return a.first.hat < b.first.hat;
if(a.first.hdir != b.first.hdir)
return a.first.hdir < b.first.hdir;
return a.second < b.second;
}
);
json eventMappings = json::array(); json eventMappings = json::array();
for (auto& item: myMap) { for (const auto& [_mapping, _event]: sortedMap) {
if (item.first.mode != mode) continue; if (_mapping.mode != mode) continue;
json eventMapping = json::object(); json eventMapping = json::object();
eventMapping["event"] = item.second; eventMapping["event"] = _event;
if (item.first.button != JOY_CTRL_NONE) eventMapping["button"] = item.first.button; if (_mapping.button != JOY_CTRL_NONE) eventMapping["button"] = _mapping.button;
if (item.first.axis != JoyAxis::NONE) { if (_mapping.axis != JoyAxis::NONE) {
eventMapping["axis"] = item.first.axis; eventMapping["axis"] = _mapping.axis;
eventMapping["axisDirection"] = item.first.adir; eventMapping["axisDirection"] = _mapping.adir;
} }
if (item.first.hat != -1) { if (_mapping.hat != -1) {
eventMapping["hat"] = item.first.hat; eventMapping["hat"] = _mapping.hat;
eventMapping["hatDirection"] = item.first.hdir; eventMapping["hatDirection"] = _mapping.hdir;
} }
eventMappings.push_back(eventMapping); eventMappings.push_back(eventMapping);
@ -221,7 +247,7 @@ int JoyMap::loadMapping(const json& eventMappings, const EventMode mode)
{ {
int i = 0; int i = 0;
for (const json& eventMapping: eventMappings) { for(const json& eventMapping : eventMappings) {
int button = eventMapping.contains("button") ? eventMapping.at("button").get<int>() : JOY_CTRL_NONE; int button = eventMapping.contains("button") ? eventMapping.at("button").get<int>() : JOY_CTRL_NONE;
JoyAxis axis = eventMapping.contains("axis") ? eventMapping.at("axis").get<JoyAxis>() : JoyAxis::NONE; JoyAxis axis = eventMapping.contains("axis") ? eventMapping.at("axis").get<JoyAxis>() : JoyAxis::NONE;
JoyDir axisDirection = eventMapping.contains("axis") ? eventMapping.at("axisDirection").get<JoyDir>() : JoyDir::NONE; JoyDir axisDirection = eventMapping.contains("axis") ? eventMapping.at("axisDirection").get<JoyDir>() : JoyDir::NONE;
@ -240,7 +266,7 @@ int JoyMap::loadMapping(const json& eventMappings, const EventMode mode)
); );
i++; i++;
} catch (json::exception) { } catch (const json::exception&) {
Logger::error("ignoring invalid joystick event"); Logger::error("ignoring invalid joystick event");
} }
} }
@ -262,22 +288,22 @@ json JoyMap::convertLegacyMapping(string list)
istringstream buf(list); istringstream buf(list);
int event, button, axis, adir, hat, hdir; int event, button, axis, adir, hat, hdir;
while (buf >> event && buf >> button while(buf >> event && buf >> button
&& buf >> axis && buf >> adir && buf >> axis && buf >> adir
&& buf >> hat && buf >> hdir) && buf >> hat && buf >> hdir)
{ {
json eventMapping = json::object(); json eventMapping = json::object();
eventMapping["event"] = Event::Type(event); eventMapping["event"] = Event::Type(event);
if (button != JOY_CTRL_NONE) eventMapping["button"] = button; if(button != JOY_CTRL_NONE) eventMapping["button"] = button;
if (JoyAxis(axis) != JoyAxis::NONE) { if(JoyAxis(axis) != JoyAxis::NONE) {
eventMapping["axis"] = JoyAxis(axis); eventMapping["axis"] = JoyAxis(axis);
eventMapping["axisDirection"] = JoyDir(adir); eventMapping["axisDirection"] = JoyDir(adir);
} }
if (hat != -1) { if(hat != -1) {
eventMapping["hat"] = hat; eventMapping["hat"] = hat;
eventMapping["hatDirection"] = JoyHatDir(hdir); eventMapping["hatDirection"] = JoyHatDir(hdir);
} }
@ -291,8 +317,8 @@ json JoyMap::convertLegacyMapping(string list)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::eraseMode(const EventMode mode) void JoyMap::eraseMode(const EventMode mode)
{ {
for (auto item = myMap.begin(); item != myMap.end();) for(auto item = myMap.begin(); item != myMap.end();)
if (item->first.mode == mode) { if(item->first.mode == mode) {
auto _item = item++; auto _item = item++;
erase(_item->first); erase(_item->first);
} }
@ -302,8 +328,8 @@ void JoyMap::eraseMode(const EventMode mode)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::eraseEvent(const Event::Type event, const EventMode mode) void JoyMap::eraseEvent(const Event::Type event, const EventMode mode)
{ {
for (auto item = myMap.begin(); item != myMap.end();) for(auto item = myMap.begin(); item != myMap.end();)
if (item->second == event && item->first.mode == mode) { if(item->second == event && item->first.mode == mode) {
auto _item = item++; auto _item = item++;
erase(_item->first); erase(_item->first);
} }

View File

@ -22,7 +22,7 @@
#include "Event.hxx" #include "Event.hxx"
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "json.hxx" #include "json_lib.hxx"
/** /**
This class handles controller mappings in Stella. This class handles controller mappings in Stella.
@ -44,19 +44,19 @@ class JoyMap
explicit JoyMapping(EventMode c_mode, int c_button, explicit JoyMapping(EventMode c_mode, int c_button,
JoyAxis c_axis, JoyDir c_adir, JoyAxis c_axis, JoyDir c_adir,
int c_hat, JoyHatDir c_hdir) int c_hat, JoyHatDir c_hdir)
: mode(c_mode), button(c_button), : mode{c_mode}, button{c_button},
axis(c_axis), adir(c_adir), axis{c_axis}, adir{c_adir},
hat(c_hat), hdir(c_hdir) { } hat{c_hat}, hdir{c_hdir} { }
explicit JoyMapping(EventMode c_mode, int c_button, explicit JoyMapping(EventMode c_mode, int c_button,
JoyAxis c_axis, JoyDir c_adir) JoyAxis c_axis, JoyDir c_adir)
: mode(c_mode), button(c_button), : mode{c_mode}, button{c_button},
axis(c_axis), adir(c_adir), axis{c_axis}, adir{c_adir},
hat(JOY_CTRL_NONE), hdir(JoyHatDir::CENTER) { } hat{JOY_CTRL_NONE}, hdir{JoyHatDir::CENTER} { }
explicit JoyMapping(EventMode c_mode, int c_button, explicit JoyMapping(EventMode c_mode, int c_button,
int c_hat, JoyHatDir c_hdir) int c_hat, JoyHatDir c_hdir)
: mode(c_mode), button(c_button), : mode{c_mode}, button{c_button},
axis(JoyAxis::NONE), adir(JoyDir::NONE), axis{JoyAxis::NONE}, adir{JoyDir::NONE},
hat(c_hat), hdir(c_hdir) { } hat{c_hat}, hdir{c_hdir} { }
JoyMapping(const JoyMapping&) = default; JoyMapping(const JoyMapping&) = default;
JoyMapping& operator=(const JoyMapping&) = default; JoyMapping& operator=(const JoyMapping&) = default;

View File

@ -22,6 +22,52 @@
using json = nlohmann::json; using json = nlohmann::json;
namespace {
json serializeModkeyMask(int mask)
{
if(mask == StellaMod::KBDM_NONE) return json(nullptr);
json serializedMask = json::array();
for(StellaMod mod: {
StellaMod::KBDM_CTRL,
StellaMod::KBDM_SHIFT,
StellaMod::KBDM_ALT,
StellaMod::KBDM_GUI,
StellaMod::KBDM_LSHIFT,
StellaMod::KBDM_RSHIFT,
StellaMod::KBDM_LCTRL,
StellaMod::KBDM_RCTRL,
StellaMod::KBDM_LALT,
StellaMod::KBDM_RALT,
StellaMod::KBDM_LGUI,
StellaMod::KBDM_RGUI,
StellaMod::KBDM_NUM,
StellaMod::KBDM_CAPS,
StellaMod::KBDM_MODE,
StellaMod::KBDM_RESERVED
}) {
if((mask & mod) != mod) continue;
serializedMask.push_back(json(mod));
mask &= ~mod;
}
return serializedMask.size() == 1 ? serializedMask.at(0) : serializedMask;
}
int deserializeModkeyMask(json serializedMask)
{
if(serializedMask.is_null()) return StellaMod::KBDM_NONE;
if(!serializedMask.is_array()) return serializedMask.get<StellaMod>();
int mask = 0;
for(const json& mod: serializedMask) mask |= mod.get<StellaMod>();
return mask;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::add(const Event::Type event, const Mapping& mapping) void KeyMap::add(const Event::Type event, const Mapping& mapping)
{ {
@ -51,10 +97,10 @@ Event::Type KeyMap::get(const Mapping& mapping) const
{ {
Mapping m = convertMod(mapping); Mapping m = convertMod(mapping);
if (myModEnabled) if(myModEnabled)
{ {
auto find = myMap.find(m); auto find = myMap.find(m);
if (find != myMap.end()) if(find != myMap.end())
return find->second; return find->second;
} }
@ -62,7 +108,7 @@ Event::Type KeyMap::get(const Mapping& mapping) const
m.mod = StellaMod(0); m.mod = StellaMod(0);
auto find = myMap.find(m); auto find = myMap.find(m);
if (find != myMap.end()) if(find != myMap.end())
return find->second; return find->second;
return Event::Type::NoType; return Event::Type::NoType;
@ -112,26 +158,26 @@ string KeyMap::getDesc(const Mapping& mapping) const
int RMOD3 = KBDM_RALT; int RMOD3 = KBDM_RALT;
#endif #endif
if ((mapping.mod & KBDM_CTRL) == KBDM_CTRL) buf << "Ctrl"; if((mapping.mod & KBDM_CTRL) == KBDM_CTRL) buf << "Ctrl";
else if (mapping.mod & KBDM_LCTRL) buf << "Left Ctrl"; else if(mapping.mod & KBDM_LCTRL) buf << "Left Ctrl";
else if (mapping.mod & KBDM_RCTRL) buf << "Right Ctrl"; else if(mapping.mod & KBDM_RCTRL) buf << "Right Ctrl";
if ((mapping.mod & (MOD2)) && buf.tellp()) buf << "+"; if((mapping.mod & (MOD2)) && buf.tellp()) buf << "+";
if ((mapping.mod & MOD2) == MOD2) buf << mod2; if((mapping.mod & MOD2) == MOD2) buf << mod2;
else if (mapping.mod & LMOD2) buf << "Left " << mod2; else if(mapping.mod & LMOD2) buf << "Left " << mod2;
else if (mapping.mod & RMOD2) buf << "Right " << mod2; else if(mapping.mod & RMOD2) buf << "Right " << mod2;
if ((mapping.mod & (MOD3)) && buf.tellp()) buf << "+"; if((mapping.mod & (MOD3)) && buf.tellp()) buf << "+";
if ((mapping.mod & MOD3) == MOD3) buf << mod3; if((mapping.mod & MOD3) == MOD3) buf << mod3;
else if (mapping.mod & LMOD3) buf << "Left " << mod3; else if(mapping.mod & LMOD3) buf << "Left " << mod3;
else if (mapping.mod & RMOD3) buf << "Right " << mod3; else if(mapping.mod & RMOD3) buf << "Right " << mod3;
if ((mapping.mod & (KBDM_SHIFT)) && buf.tellp()) buf << "+"; if((mapping.mod & (KBDM_SHIFT)) && buf.tellp()) buf << "+";
if ((mapping.mod & KBDM_SHIFT) == KBDM_SHIFT) buf << "Shift"; if((mapping.mod & KBDM_SHIFT) == KBDM_SHIFT) buf << "Shift";
else if (mapping.mod & KBDM_LSHIFT) buf << "Left Shift"; else if(mapping.mod & KBDM_LSHIFT) buf << "Left Shift";
else if (mapping.mod & KBDM_RSHIFT) buf << "Right Shift"; else if(mapping.mod & KBDM_RSHIFT) buf << "Right Shift";
if (buf.tellp()) buf << "+"; if(buf.tellp()) buf << "+";
buf << StellaKeyName::forKey(mapping.key); buf << StellaKeyName::forKey(mapping.key);
return buf.str(); return buf.str();
@ -148,13 +194,13 @@ string KeyMap::getEventMappingDesc(const Event::Type event, const EventMode mode
{ {
ostringstream buf; ostringstream buf;
for (auto item : myMap) for (const auto& [_mapping, _event]: myMap)
{ {
if (item.second == event && item.first.mode == mode) if (_event == event && _mapping.mode == mode)
{ {
if (buf.str() != "") if(buf.str() != "")
buf << ", "; buf << ", ";
buf << getDesc(item.first); buf << getDesc(_mapping);
} }
} }
return buf.str(); return buf.str();
@ -165,9 +211,9 @@ KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const Even
{ {
MappingArray map; MappingArray map;
for (auto item : myMap) for (const auto& [_mapping, _event]: myMap)
if (item.second == event && item.first.mode == mode) if (_event == event && _mapping.mode == mode)
map.push_back(item.first); map.push_back(_mapping);
return map; return map;
} }
@ -175,18 +221,35 @@ KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const Even
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json KeyMap::saveMapping(const EventMode mode) const json KeyMap::saveMapping(const EventMode mode) const
{ {
using MapType = std::pair<Mapping, Event::Type>;
std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
std::sort(sortedMap.begin(), sortedMap.end(),
[](const MapType& a, const MapType& b)
{
// Event::Type first
if(a.first.key != b.first.key)
return a.first.key < b.first.key;
if(a.first.mod != b.first.mod)
return a.first.mod < b.first.mod;
return a.second < b.second;
}
);
json mappings = json::array(); json mappings = json::array();
for (auto item : myMap) { for (const auto& [_mapping, _event]: sortedMap) {
if (item.first.mode != mode) continue; if (_mapping.mode != mode) continue;
json mapping = json::object(); json mapping = json::object();
mapping["event"] = item.second; mapping["event"] = _event;
mapping["key"] = item.first.key; mapping["key"] = _mapping.key;
if (item.first.mod != StellaMod::KBDM_NONE) if (_mapping.mod != StellaMod::KBDM_NONE)
mapping["mod"] = item.first.mod; mapping["mod"] = serializeModkeyMask(_mapping.mod);
mappings.push_back(mapping); mappings.push_back(mapping);
} }
@ -198,17 +261,18 @@ json KeyMap::saveMapping(const EventMode mode) const
int KeyMap::loadMapping(const json& mappings, const EventMode mode) { int KeyMap::loadMapping(const json& mappings, const EventMode mode) {
int i = 0; int i = 0;
for (const json& mapping: mappings) { for(const json& mapping : mappings)
{
try { try {
add( add(
mapping.at("event").get<Event::Type>(), mapping.at("event").get<Event::Type>(),
mode, mode,
mapping.at("key").get<StellaKey>(), mapping.at("key").get<StellaKey>(),
mapping.contains("mod") ? mapping.at("mod").get<StellaMod>() : StellaMod::KBDM_NONE mapping.contains("mod") ? deserializeModkeyMask(mapping.at("mod")) : StellaMod::KBDM_NONE
); );
i++; i++;
} catch (json::exception) { } catch (const json::exception&) {
Logger::error("ignoring bad keyboard mapping"); Logger::error("ignoring bad keyboard mapping");
} }
} }
@ -229,13 +293,15 @@ json KeyMap::convertLegacyMapping(string list)
istringstream buf(list); istringstream buf(list);
int event, key, mod; int event, key, mod;
while (buf >> event && buf >> key && buf >> mod) { while(buf >> event && buf >> key && buf >> mod)
{
json mapping = json::object(); json mapping = json::object();
mapping["event"] = Event::Type(event); mapping["event"] = Event::Type(event);
mapping["key"] = StellaKey(key); mapping["key"] = StellaKey(key);
if (StellaMod(mod) != StellaMod::KBDM_NONE) mapping["mod"] = StellaMod(mod); if(StellaMod(mod) != StellaMod::KBDM_NONE)
mapping["mod"] = serializeModkeyMask(StellaMod(mod));
convertedMapping.push_back(mapping); convertedMapping.push_back(mapping);
} }
@ -246,8 +312,8 @@ json KeyMap::convertLegacyMapping(string list)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::eraseMode(const EventMode mode) void KeyMap::eraseMode(const EventMode mode)
{ {
for (auto item = myMap.begin(); item != myMap.end();) for(auto item = myMap.begin(); item != myMap.end();)
if (item->first.mode == mode) { if(item->first.mode == mode) {
auto _item = item++; auto _item = item++;
erase(_item->first); erase(_item->first);
} }
@ -257,8 +323,8 @@ void KeyMap::eraseMode(const EventMode mode)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::eraseEvent(const Event::Type event, const EventMode mode) void KeyMap::eraseEvent(const Event::Type event, const EventMode mode)
{ {
for (auto item = myMap.begin(); item != myMap.end();) for(auto item = myMap.begin(); item != myMap.end();)
if (item->second == event && item->first.mode == mode) { if(item->second == event && item->first.mode == mode) {
auto _item = item++; auto _item = item++;
erase(_item->first); erase(_item->first);
} }
@ -270,7 +336,7 @@ KeyMap::Mapping KeyMap::convertMod(const Mapping& mapping) const
{ {
Mapping m = mapping; Mapping m = mapping;
if (m.key >= KBDK_LCTRL && m.key <= KBDK_RGUI) if(m.key >= KBDK_LCTRL && m.key <= KBDK_RGUI)
// handle solo modifier keys differently // handle solo modifier keys differently
m.mod = KBDM_NONE; m.mod = KBDM_NONE;
else else

View File

@ -22,7 +22,7 @@
#include "Event.hxx" #include "Event.hxx"
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "StellaKeys.hxx" #include "StellaKeys.hxx"
#include "json.hxx" #include "json_lib.hxx"
/** /**
This class handles keyboard mappings in Stella. This class handles keyboard mappings in Stella.
@ -39,9 +39,9 @@ class KeyMap
StellaMod mod{StellaMod(0)}; StellaMod mod{StellaMod(0)};
explicit Mapping(EventMode c_mode, StellaKey c_key, StellaMod c_mod) explicit Mapping(EventMode c_mode, StellaKey c_key, StellaMod c_mod)
: mode(c_mode), key(c_key), mod(c_mod) { } : mode{c_mode}, key{c_key}, mod{c_mod} { }
explicit Mapping(EventMode c_mode, int c_key, int c_mod) explicit Mapping(EventMode c_mode, int c_key, int c_mod)
: mode(c_mode), key(StellaKey(c_key)), mod(StellaMod(c_mod)) { } : mode{c_mode}, key{StellaKey(c_key)}, mod{StellaMod(c_mod)} { }
Mapping(const Mapping&) = default; Mapping(const Mapping&) = default;
Mapping& operator=(const Mapping&) = default; Mapping& operator=(const Mapping&) = default;
Mapping(Mapping&&) = default; Mapping(Mapping&&) = default;

View File

@ -24,9 +24,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MouseControl::MouseControl(Console& console, const string& mode) MouseControl::MouseControl(Console& console, const string& mode)
: myProps(console.properties()), : myProps{console.properties()},
myLeftController(console.leftController()), myLeftController{console.leftController()},
myRightController(console.rightController()) myRightController{console.rightController()}
{ {
istringstream m_axis(mode); istringstream m_axis(mode);
string m_mode; string m_mode;
@ -137,13 +137,17 @@ MouseControl::MouseControl(Console& console, const string& mode)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& MouseControl::next() const string& MouseControl::change(int direction)
{ {
myCurrentModeNum = BSPF::clampw(myCurrentModeNum + direction, 0, int(myModeList.size() - 1));
const MouseMode& mode = myModeList[myCurrentModeNum]; const MouseMode& mode = myModeList[myCurrentModeNum];
myCurrentModeNum = (myCurrentModeNum + 1) % myModeList.size();
myLeftController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid); bool leftControl =
myRightController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid); myLeftController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid);
bool rightControl =
myRightController.setMouseControl(mode.xtype, mode.xid, mode.ytype, mode.yid);
myHasMouseControl = leftControl || rightControl;
return mode.message; return mode.message;
} }

View File

@ -62,7 +62,12 @@ class MouseControl
@return A message explaining the current mouse mode @return A message explaining the current mouse mode
*/ */
const string& next(); const string& change(int direction = +1);
/**
Get whether any current controller supports mouse control
*/
bool hasMouseControl() const { return myHasMouseControl; }
private: private:
void addLeftControllerModes(bool noswap); void addLeftControllerModes(bool noswap);
@ -101,6 +106,7 @@ class MouseControl
int myCurrentModeNum{0}; int myCurrentModeNum{0};
vector<MouseMode> myModeList; vector<MouseMode> myModeList;
bool myHasMouseControl{false};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -19,6 +19,9 @@
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Console.hxx" #include "Console.hxx"
#include "Joystick.hxx" #include "Joystick.hxx"
#include "Paddles.hxx"
#include "PointingDevice.hxx"
#include "Driving.hxx"
#include "Settings.hxx" #include "Settings.hxx"
#include "EventHandler.hxx" #include "EventHandler.hxx"
#include "PJoystickHandler.hxx" #include "PJoystickHandler.hxx"
@ -33,8 +36,8 @@ using json = nlohmann::json;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::PhysicalJoystickHandler( PhysicalJoystickHandler::PhysicalJoystickHandler(
OSystem& system, EventHandler& handler) OSystem& system, EventHandler& handler)
: myOSystem(system), : myOSystem{system},
myHandler(handler) myHandler{handler}
{ {
if(myOSystem.settings().getInt("event_ver") != Event::VERSION) { if(myOSystem.settings().getInt("event_ver") != Event::VERSION) {
Logger::info("event version mismatch; dropping previous joystick mappings"); Logger::info("event version mismatch; dropping previous joystick mappings");
@ -47,7 +50,7 @@ PhysicalJoystickHandler::PhysicalJoystickHandler(
try { try {
mappings = json::parse(serializedMapping); mappings = json::parse(serializedMapping);
} catch (json::exception) { } catch (const json::exception&) {
Logger::info("converting legacy joystick mappings"); Logger::info("converting legacy joystick mappings");
mappings = convertLegacyMapping(serializedMapping); mappings = convertLegacyMapping(serializedMapping);
@ -122,8 +125,8 @@ int PhysicalJoystickHandler::add(const PhysicalJoystickPtr& stick)
// For non-unique names that already have a database entry, // For non-unique names that already have a database entry,
// we append ' #x', where 'x' increases consecutively // we append ' #x', where 'x' increases consecutively
int count = 0; int count = 0;
for(const auto& i: myDatabase) for(const auto& [_name, _info]: myDatabase)
if(BSPF::startsWithIgnoreCase(i.first, stick->name) && i.second.joy) if(BSPF::startsWithIgnoreCase(_name, stick->name) && _info.joy)
++count; ++count;
if(count > 0) if(count > 0)
@ -132,12 +135,13 @@ int PhysicalJoystickHandler::add(const PhysicalJoystickPtr& stick)
name << stick->name << " #" << count+1; name << stick->name << " #" << count+1;
stick->name = name.str(); stick->name = name.str();
} }
stick->type = PhysicalJoystick::JT_REGULAR; stick->type = PhysicalJoystick::Type::REGULAR;
} }
// The stick *must* be inserted here, since it may be used below // The stick *must* be inserted here, since it may be used below
mySticks[stick->ID] = stick; mySticks[stick->ID] = stick;
// Map the stelladaptors we've found according to the specified ports // Map the stelladaptors we've found according to the specified ports
// The 'type' is also set there
if(specialAdaptor) if(specialAdaptor)
mapStelladaptors(myOSystem.settings().getString("saport")); mapStelladaptors(myOSystem.settings().getString("saport"));
@ -217,45 +221,45 @@ void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
// We know there will be only two such devices (at most), since the logic // We know there will be only two such devices (at most), since the logic
// in setupJoysticks take care of that // in setupJoysticks take care of that
int saCount = 0; int saCount = 0;
int saOrder[NUM_PORTS] = { 1, 2 }; int saOrder[] = { 1, 2 };
if(BSPF::equalsIgnoreCase(saport, "rl")) if(BSPF::equalsIgnoreCase(saport, "rl"))
{ {
saOrder[0] = 2; saOrder[1] = 1; saOrder[0] = 2; saOrder[1] = 1;
} }
for(auto& stick: mySticks) for(auto& [_id, _joyptr]: mySticks)
{ {
// remove previously added emulated ports // remove previously added emulated ports
size_t pos = stick.second->name.find(" (emulates "); size_t pos = _joyptr->name.find(" (emulates ");
if(pos != std::string::npos) if(pos != std::string::npos)
stick.second->name.erase(pos); _joyptr->name.erase(pos);
if(BSPF::startsWithIgnoreCase(stick.second->name, "Stelladaptor")) if(BSPF::startsWithIgnoreCase(_joyptr->name, "Stelladaptor"))
{ {
if(saOrder[saCount] == 1) if(saOrder[saCount] == 1)
{ {
stick.second->name += " (emulates left joystick port)"; _joyptr->name += " (emulates left joystick port)";
stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_LEFT; _joyptr->type = PhysicalJoystick::Type::LEFT_STELLADAPTOR;
} }
else if(saOrder[saCount] == 2) else if(saOrder[saCount] == 2)
{ {
stick.second->name += " (emulates right joystick port)"; _joyptr->name += " (emulates right joystick port)";
stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_RIGHT; _joyptr->type = PhysicalJoystick::Type::RIGHT_STELLADAPTOR;
} }
saCount++; saCount++;
} }
else if(BSPF::startsWithIgnoreCase(stick.second->name, "2600-daptor")) else if(BSPF::startsWithIgnoreCase(_joyptr->name, "2600-daptor"))
{ {
if(saOrder[saCount] == 1) if(saOrder[saCount] == 1)
{ {
stick.second->name += " (emulates left joystick port)"; _joyptr->name += " (emulates left joystick port)";
stick.second->type = PhysicalJoystick::JT_2600DAPTOR_LEFT; _joyptr->type = PhysicalJoystick::Type::LEFT_2600DAPTOR;
} }
else if(saOrder[saCount] == 2) else if(saOrder[saCount] == 2)
{ {
stick.second->name += " (emulates right joystick port)"; _joyptr->name += " (emulates right joystick port)";
stick.second->type = PhysicalJoystick::JT_2600DAPTOR_RIGHT; _joyptr->type = PhysicalJoystick::Type::RIGHT_2600DAPTOR;
} }
saCount++; saCount++;
} }
@ -263,6 +267,24 @@ void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
myOSystem.settings().setValue("saport", saport); myOSystem.settings().setValue("saport", saport);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::hasStelladaptors() const
{
for(auto& [_id, _joyptr] : mySticks)
{
// remove previously added emulated ports
size_t pos = _joyptr->name.find(" (emulates ");
if(pos != std::string::npos)
_joyptr->name.erase(pos);
if(BSPF::startsWithIgnoreCase(_joyptr->name, "Stelladaptor")
|| BSPF::startsWithIgnoreCase(_joyptr->name, "2600-daptor"))
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Depending on parameters, this method does the following: // Depending on parameters, this method does the following:
// 1. update all events with default (event == Event::NoType, updateDefault == true) // 1. update all events with default (event == Event::NoType, updateDefault == true)
@ -313,7 +335,15 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
switch (mode) switch (mode)
{ {
case EventMode::kEmulationMode: case EventMode::kEmulationMode:
if((stick % 2) == 0) // even sticks {
// A regular joystick defaults to left or right based on the
// stick number being even or odd; 'daptor joysticks request a
// specific port
const bool useLeftMappings =
j->type == PhysicalJoystick::Type::REGULAR ? ((stick % 2) == 0) :
(j->type == PhysicalJoystick::Type::LEFT_STELLADAPTOR ||
j->type == PhysicalJoystick::Type::LEFT_2600DAPTOR);
if(useLeftMappings)
{ {
// put all controller events into their own mode's mappings // put all controller events into their own mode's mappings
for (const auto& item : DefaultLeftJoystickMapping) for (const auto& item : DefaultLeftJoystickMapping)
@ -323,7 +353,7 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
for (const auto& item : DefaultLeftKeypadMapping) for (const auto& item : DefaultLeftKeypadMapping)
setDefaultAction(stick, item, event, EventMode::kKeypadMode, updateDefaults); setDefaultAction(stick, item, event, EventMode::kKeypadMode, updateDefaults);
} }
else // odd sticks else
{ {
// put all controller events into their own mode's mappings // put all controller events into their own mode's mappings
for (const auto& item : DefaultRightJoystickMapping) for (const auto& item : DefaultRightJoystickMapping)
@ -338,6 +368,7 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
// update running emulation mapping too // update running emulation mapping too
enableEmulationMappings(); enableEmulationMappings();
break; break;
}
case EventMode::kMenuMode: case EventMode::kMenuMode:
for (const auto& item : DefaultMenuMapping) for (const auto& item : DefaultMenuMapping)
@ -354,8 +385,8 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mode) void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mode)
{ {
eraseMapping(event, mode); eraseMapping(event, mode);
for (auto& i : mySticks) for (const auto& [_id, _joyptr]: mySticks)
setStickDefaultMapping(i.first, event, mode); setStickDefaultMapping(_id, event, mode);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -529,24 +560,24 @@ void PhysicalJoystickHandler::eraseMapping(Event::Type event, EventMode mode)
// Otherwise, only reset the given event // Otherwise, only reset the given event
if(event == Event::NoType) if(event == Event::NoType)
{ {
for (auto& stick : mySticks) for (auto& [_id, _joyptr]: mySticks)
{ {
stick.second->eraseMap(mode); // erase all events _joyptr->eraseMap(mode); // erase all events
if(mode == EventMode::kEmulationMode) if(mode == EventMode::kEmulationMode)
{ {
stick.second->eraseMap(EventMode::kCommonMode); _joyptr->eraseMap(EventMode::kCommonMode);
stick.second->eraseMap(EventMode::kJoystickMode); _joyptr->eraseMap(EventMode::kJoystickMode);
stick.second->eraseMap(EventMode::kPaddlesMode); _joyptr->eraseMap(EventMode::kPaddlesMode);
stick.second->eraseMap(EventMode::kKeypadMode); _joyptr->eraseMap(EventMode::kKeypadMode);
} }
} }
} }
else else
{ {
for (auto& stick : mySticks) for (auto& [_id, _joyptr]: mySticks)
{ {
stick.second->eraseEvent(event, mode); // only reset the specific event _joyptr->eraseEvent(event, mode); // only reset the specific event
stick.second->eraseEvent(event, getEventMode(event, mode)); _joyptr->eraseEvent(event, getEventMode(event, mode));
} }
} }
} }
@ -558,9 +589,9 @@ void PhysicalJoystickHandler::saveMapping()
// any changes that have been made during the program run // any changes that have been made during the program run
json mapping = json::array(); json mapping = json::array();
for(const auto& i: myDatabase) for(const auto& [_name, _info]: myDatabase)
{ {
json map = i.second.joy ? i.second.joy->getMap() : i.second.mapping; json map = _info.joy ? _info.joy->getMap() : _info.mapping;
if (!map.is_null()) mapping.emplace_back(map); if (!map.is_null()) mapping.emplace_back(map);
} }
@ -574,19 +605,16 @@ string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode
ostringstream buf; ostringstream buf;
EventMode evMode = getEventMode(event, mode); EventMode evMode = getEventMode(event, mode);
for(const auto& s: mySticks) for(const auto& [_id, _joyptr]: mySticks)
{ {
uInt32 stick = s.first; if(_joyptr)
const PhysicalJoystickPtr j = s.second;
if(j)
{ {
//Joystick mapping / labeling //Joystick mapping / labeling
if(j->joyMap.getEventMapping(event, evMode).size()) if(_joyptr->joyMap.getEventMapping(event, evMode).size())
{ {
if(buf.str() != "") if(buf.str() != "")
buf << ", "; buf << ", ";
buf << j->joyMap.getEventMappingDesc(stick, event, evMode); buf << _joyptr->joyMap.getEventMappingDesc(_id, event, evMode);
} }
} }
} }
@ -819,8 +847,8 @@ void PhysicalJoystickHandler::handleHatEvent(int stick, int hat, int value)
VariantList PhysicalJoystickHandler::database() const VariantList PhysicalJoystickHandler::database() const
{ {
VariantList db; VariantList db;
for(const auto& i: myDatabase) for(const auto& [_name, _info]: myDatabase)
VarList::push_back(db, i.first, i.second.joy ? i.second.joy->ID : -1); VarList::push_back(db, _name, _info.joy ? _info.joy->ID : -1);
return db; return db;
} }
@ -830,19 +858,160 @@ ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
{ {
os << "---------------------------------------------------------" << endl os << "---------------------------------------------------------" << endl
<< "joy database:" << endl; << "joy database:" << endl;
for(const auto& i: jh.myDatabase) for(const auto& [_name, _info]: jh.myDatabase)
os << i.first << endl << i.second << endl << endl; os << _name << endl << _info << endl << endl;
os << "---------------------" << endl os << "---------------------" << endl
<< "joy active:" << endl; << "joy active:" << endl;
for(const auto& i: jh.mySticks) for(const auto& [_id, _joyptr]: jh.mySticks)
os << i.first << ": " << *i.second << endl; os << _id << ": " << *_joyptr << endl;
os << "---------------------------------------------------------" os << "---------------------------------------------------------"
<< endl << endl << endl; << endl << endl << endl;
return os; return os;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDeadzone(int direction)
{
int deadzone = BSPF::clamp(myOSystem.settings().getInt("joydeadzone") + direction,
Joystick::DEAD_ZONE_MIN, Joystick::DEAD_ZONE_MAX);
myOSystem.settings().setValue("joydeadzone", deadzone);
Joystick::setDeadZone(deadzone);
int value = Joystick::deadZoneValue(deadzone);
myOSystem.frameBuffer().showGaugeMessage("Joystick deadzone", std::to_string(value),
value, 3200, 32200);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeAnalogPaddleSensitivity(int direction)
{
int sense = BSPF::clamp(myOSystem.settings().getInt("psense") + direction,
Paddles::MIN_ANALOG_SENSE, Paddles::MAX_ANALOG_SENSE);
myOSystem.settings().setValue("psense", sense);
Paddles::setAnalogSensitivity(sense);
ostringstream ss;
ss << std::round(Paddles::analogSensitivityValue(sense) * 100.F) << "%";
myOSystem.frameBuffer().showGaugeMessage("Analog paddle sensitivity", ss.str(), sense,
Paddles::MIN_ANALOG_SENSE, Paddles::MAX_ANALOG_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changePaddleDejitterAveraging(int direction)
{
int dejitter = BSPF::clamp(myOSystem.settings().getInt("dejitter.base") + direction,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
myOSystem.settings().setValue("dejitter.base", dejitter);
Paddles::setDejitterBase(dejitter);
ostringstream ss;
if(dejitter)
ss << dejitter;
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage("Analog paddle dejitter averaging",
ss.str(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changePaddleDejitterReaction(int direction)
{
int dejitter = BSPF::clamp(myOSystem.settings().getInt("dejitter.diff") + direction,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
myOSystem.settings().setValue("dejitter.diff", dejitter);
Paddles::setDejitterDiff(dejitter);
ostringstream ss;
if(dejitter)
ss << dejitter;
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage("Analog paddle dejitter reaction",
ss.str(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDigitalPaddleSensitivity(int direction)
{
int sense = BSPF::clamp(myOSystem.settings().getInt("dsense") + direction,
Paddles::MIN_DIGITAL_SENSE, Paddles::MAX_DIGITAL_SENSE);
myOSystem.settings().setValue("dsense", sense);
Paddles::setDigitalSensitivity(sense);
ostringstream ss;
if(sense)
ss << sense * 10 << "%";
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage("Digital sensitivity",
ss.str(), sense,
Paddles::MIN_DIGITAL_SENSE, Paddles::MAX_DIGITAL_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeMousePaddleSensitivity(int direction)
{
int sense = BSPF::clamp(myOSystem.settings().getInt("msense") + direction,
Paddles::MIN_MOUSE_SENSE, Paddles::MAX_MOUSE_SENSE);
myOSystem.settings().setValue("msense", sense);
Paddles::setMouseSensitivity(sense);
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage("Mouse paddle sensitivity",
ss.str(), sense,
Paddles::MIN_MOUSE_SENSE, Paddles::MAX_MOUSE_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeMouseTrackballSensitivity(int direction)
{
int sense = BSPF::clamp(myOSystem.settings().getInt("tsense") + direction,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
myOSystem.settings().setValue("tsense", sense);
PointingDevice::setSensitivity(sense);
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage("Mouse trackball sensitivity",
ss.str(), sense,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDrivingSensitivity(int direction)
{
int sense = BSPF::clamp(myOSystem.settings().getInt("dcsense") + direction,
Driving::MIN_SENSE, Driving::MAX_SENSE);
myOSystem.settings().setValue("dcsense", sense);
Driving::setSensitivity(sense);
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage("Driving controller sensitivity",
ss.str(), sense,
Driving::MIN_SENSE, Driving::MAX_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftJoystickMapping = { PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftJoystickMapping = {
// Left joystick (assume buttons zero..two) // Left joystick (assume buttons zero..two)
@ -956,7 +1125,8 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRight
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultCommonMapping = { PhysicalJoystickHandler::EventMappingArray
PhysicalJoystickHandler::DefaultCommonMapping = {
// valid for all joysticks // valid for all joysticks
//#if defined(RETRON77) //#if defined(RETRON77)
{Event::CmdMenuMode, 3}, // Note: buttons 0..2 are used by controllers! {Event::CmdMenuMode, 3}, // Note: buttons 0..2 are used by controllers!
@ -969,7 +1139,8 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultCommo
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultMenuMapping = { PhysicalJoystickHandler::EventMappingArray
PhysicalJoystickHandler::DefaultMenuMapping = {
// valid for all joysticks // valid for all joysticks
{Event::UISelect, 0}, {Event::UISelect, 0},
{Event::UIOK, 1}, {Event::UIOK, 1},

View File

@ -28,7 +28,7 @@ class Event;
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "PhysicalJoystick.hxx" #include "PhysicalJoystick.hxx"
#include "Variant.hxx" #include "Variant.hxx"
#include "json.hxx" #include "json_lib.hxx"
using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>; using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>;
@ -49,8 +49,8 @@ class PhysicalJoystickHandler
private: private:
struct StickInfo struct StickInfo
{ {
StickInfo(const nlohmann::json& map = nullptr, PhysicalJoystickPtr stick = nullptr) explicit StickInfo(nlohmann::json map, PhysicalJoystickPtr stick = nullptr)
: mapping(map), joy(std::move(stick)) {} : mapping(map), joy{std::move(stick)} {}
nlohmann::json mapping; nlohmann::json mapping;
PhysicalJoystickPtr joy; PhysicalJoystickPtr joy;
@ -71,6 +71,7 @@ class PhysicalJoystickHandler
bool remove(int id); bool remove(int id);
bool remove(const string& name); bool remove(const string& name);
void mapStelladaptors(const string& saport); void mapStelladaptors(const string& saport);
bool hasStelladaptors() const;
void setDefaultMapping(Event::Type type, EventMode mode); void setDefaultMapping(Event::Type type, EventMode mode);
/** define mappings for current controllers */ /** define mappings for current controllers */
@ -109,6 +110,15 @@ class PhysicalJoystickHandler
/** Returns a list of pairs consisting of joystick name and associated ID. */ /** Returns a list of pairs consisting of joystick name and associated ID. */
VariantList database() const; VariantList database() const;
void changeDeadzone(int direction = +1);
void changeAnalogPaddleSensitivity(int direction = +1);
void changePaddleDejitterAveraging(int direction = +1);
void changePaddleDejitterReaction(int direction = +1);
void changeDigitalPaddleSensitivity(int direction = +1);
void changeMousePaddleSensitivity(int direction = +1);
void changeMouseTrackballSensitivity(int direction = +1);
void changeDrivingSensitivity(int direction = +1);
private: private:
using StickDatabase = std::map<string,StickInfo>; using StickDatabase = std::map<string,StickInfo>;
using StickList = std::map<int, PhysicalJoystickPtr>; using StickList = std::map<int, PhysicalJoystickPtr>;

View File

@ -19,7 +19,7 @@
#include "Console.hxx" #include "Console.hxx"
#include "EventHandler.hxx" #include "EventHandler.hxx"
#include "PKeyboardHandler.hxx" #include "PKeyboardHandler.hxx"
#include "json.hxx" #include "json_lib.hxx"
using json = nlohmann::json; using json = nlohmann::json;
@ -40,8 +40,8 @@ static constexpr int MOD3 = KBDM_ALT;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler& handler) PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler& handler)
: myOSystem(system), : myOSystem{system},
myHandler(handler) myHandler{handler}
{ {
Int32 version = myOSystem.settings().getInt("event_ver"); Int32 version = myOSystem.settings().getInt("event_ver");
bool updateDefaults = false; bool updateDefaults = false;
@ -74,7 +74,7 @@ void PhysicalKeyboardHandler::loadSerializedMappings(const string& serializedMap
try { try {
mapping = json::parse(serializedMapping); mapping = json::parse(serializedMapping);
} catch (json::exception) { } catch (const json::exception&) {
Logger::info("converting legacy keyboard mappings"); Logger::info("converting legacy keyboard mappings");
mapping = KeyMap::convertLegacyMapping(serializedMapping); mapping = KeyMap::convertLegacyMapping(serializedMapping);
@ -82,7 +82,7 @@ void PhysicalKeyboardHandler::loadSerializedMappings(const string& serializedMap
try { try {
myKeyMap.loadMapping(mapping, mode); myKeyMap.loadMapping(mapping, mode);
} catch (json::exception) { } catch (const json::exception&) {
Logger::error("ignoring bad keyboard mappings"); Logger::error("ignoring bad keyboard mappings");
} }
} }
@ -480,160 +480,215 @@ void PhysicalKeyboardHandler::handleEvent(StellaKey key, StellaMod mod,
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::toggleModKeys(bool toggle)
{
bool modCombo = myOSystem.settings().getBool("modcombo");
if(toggle)
{
modCombo = !modCombo;
myKeyMap.enableMod() = modCombo;
myOSystem.settings().setValue("modcombo", modCombo);
}
ostringstream ss;
ss << "Modifier key combos ";
ss << (modCombo ? "enabled" : "disabled");
myOSystem.frameBuffer().showTextMessage(ss.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::EventMappingArray
PhysicalKeyboardHandler::DefaultCommonMapping = { PhysicalKeyboardHandler::DefaultCommonMapping = {
{Event::ConsoleSelect, KBDK_F1}, { Event::ConsoleSelect, KBDK_F1 },
{Event::ConsoleReset, KBDK_F2}, { Event::ConsoleReset, KBDK_F2 },
{Event::ConsoleColor, KBDK_F3}, { Event::ConsoleColor, KBDK_F3 },
{Event::Console7800Pause, KBDK_F3, MOD3}, { Event::Console7800Pause, KBDK_F3, MOD3 },
{Event::ConsoleLeftDiffA, KBDK_F5}, { Event::ConsoleLeftDiffA, KBDK_F5 },
{Event::ConsoleRightDiffA, KBDK_F7}, { Event::ConsoleRightDiffA, KBDK_F7 },
{Event::SaveState, KBDK_F9}, { Event::SaveState, KBDK_F9 },
{Event::SaveAllStates, KBDK_F9, MOD3}, { Event::SaveAllStates, KBDK_F9, MOD3 },
{Event::PreviousState, KBDK_F10, KBDM_SHIFT}, { Event::PreviousState, KBDK_F10, KBDM_SHIFT },
{Event::NextState, KBDK_F10}, { Event::NextState, KBDK_F10 },
{Event::ToggleAutoSlot, KBDK_F10, MOD3}, { Event::ToggleAutoSlot, KBDK_F10, MOD3 },
{Event::LoadState, KBDK_F11}, { Event::LoadState, KBDK_F11 },
{Event::LoadAllStates, KBDK_F11, MOD3}, { Event::LoadAllStates, KBDK_F11, MOD3 },
{Event::TakeSnapshot, KBDK_F12}, { Event::TakeSnapshot, KBDK_F12 },
#ifdef BSPF_MACOS #ifdef BSPF_MACOS
{Event::TogglePauseMode, KBDK_P, KBDM_SHIFT | MOD3}, { Event::TogglePauseMode, KBDK_P, KBDM_SHIFT | MOD3 },
#else #else
{Event::TogglePauseMode, KBDK_PAUSE}, { Event::TogglePauseMode, KBDK_PAUSE },
#endif #endif
{Event::OptionsMenuMode, KBDK_TAB}, { Event::OptionsMenuMode, KBDK_TAB },
{Event::CmdMenuMode, KBDK_BACKSLASH}, { Event::CmdMenuMode, KBDK_BACKSLASH },
{Event::TimeMachineMode, KBDK_T, KBDM_SHIFT}, { Event::TimeMachineMode, KBDK_T, KBDM_SHIFT },
{Event::DebuggerMode, KBDK_GRAVE}, { Event::DebuggerMode, KBDK_GRAVE },
{Event::ExitMode, KBDK_ESCAPE}, { Event::ExitMode, KBDK_ESCAPE },
#ifdef BSPF_MACOS #ifdef BSPF_MACOS
{Event::Quit, KBDK_Q, MOD3}, { Event::Quit, KBDK_Q, MOD3 },
#else #else
{Event::Quit, KBDK_Q, KBDM_CTRL}, { Event::Quit, KBDK_Q, KBDM_CTRL },
#endif #endif
{Event::ReloadConsole, KBDK_R, KBDM_CTRL}, { Event::ReloadConsole, KBDK_R, KBDM_CTRL },
{Event::PreviousMultiCartRom, KBDK_R, KBDM_SHIFT | KBDM_CTRL}, { Event::PreviousMultiCartRom, KBDK_R, KBDM_SHIFT | KBDM_CTRL },
{Event::VidmodeDecrease, KBDK_MINUS, MOD3}, { Event::VidmodeDecrease, KBDK_MINUS, MOD3 },
{Event::VidmodeIncrease, KBDK_EQUALS, MOD3}, { Event::VidmodeIncrease, KBDK_EQUALS, MOD3 },
{Event::VCenterDecrease, KBDK_PAGEUP, MOD3}, { Event::VCenterDecrease, KBDK_PAGEUP, MOD3 },
{Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3}, { Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3 },
{Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3}, { Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3 },
{Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3}, { Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3 },
{Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_CTRL}, { Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_CTRL },
{Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3}, { Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3 },
{Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3}, { Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3 },
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL}, { Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL },
{Event::ToggleFullScreen, KBDK_RETURN, MOD3}, { Event::ToggleFullScreen, KBDK_RETURN, MOD3 },
{Event::ToggleAdaptRefresh, KBDK_R, MOD3}, { Event::ToggleAdaptRefresh, KBDK_R, MOD3 },
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT}, { Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT },
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT}, { Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT },
//{Event::VidmodeStd, KBDK_1, MOD3}, //{Event::VidmodeStd, KBDK_1, MOD3},
//{Event::VidmodeRGB, KBDK_2, MOD3}, //{Event::VidmodeRGB, KBDK_2, MOD3},
//{Event::VidmodeSVideo, KBDK_3, MOD3}, //{Event::VidmodeSVideo, KBDK_3, MOD3},
//{Event::VidModeComposite, KBDK_4, MOD3}, //{Event::VidModeComposite, KBDK_4, MOD3},
//{Event::VidModeBad, KBDK_5, MOD3}, //{Event::VidModeBad, KBDK_5, MOD3},
//{Event::VidModeCustom, KBDK_6, MOD3}, //{Event::VidModeCustom, KBDK_6, MOD3},
{Event::PreviousVideoMode, KBDK_1, KBDM_SHIFT | MOD3}, { Event::PreviousVideoMode, KBDK_1, KBDM_SHIFT | MOD3 },
{Event::NextVideoMode, KBDK_1, MOD3}, { Event::NextVideoMode, KBDK_1, MOD3 },
{Event::PreviousAttribute, KBDK_2, KBDM_SHIFT | MOD3}, { Event::PreviousAttribute, KBDK_2, KBDM_SHIFT | MOD3 },
{Event::NextAttribute, KBDK_2, MOD3}, { Event::NextAttribute, KBDK_2, MOD3 },
{Event::DecreaseAttribute, KBDK_3, KBDM_SHIFT | MOD3}, { Event::DecreaseAttribute, KBDK_3, KBDM_SHIFT | MOD3 },
{Event::IncreaseAttribute, KBDK_3, MOD3}, { Event::IncreaseAttribute, KBDK_3, MOD3 },
{Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3}, { Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3 },
{Event::PhosphorIncrease, KBDK_4, MOD3}, { Event::PhosphorIncrease, KBDK_4, MOD3 },
{Event::TogglePhosphor, KBDK_P, MOD3}, { Event::TogglePhosphor, KBDK_P, MOD3 },
{Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3}, { Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 },
{Event::ScanlinesIncrease, KBDK_5, MOD3}, { Event::ScanlinesIncrease, KBDK_5, MOD3 },
{Event::PreviousPaletteAttribute, KBDK_9, KBDM_SHIFT | MOD3}, { Event::PreviousPaletteAttribute, KBDK_9, KBDM_SHIFT | MOD3 },
{Event::NextPaletteAttribute, KBDK_9, MOD3}, { Event::NextPaletteAttribute, KBDK_9, MOD3 },
{Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3}, { Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3 },
{Event::PaletteAttributeIncrease, KBDK_0, MOD3}, { Event::PaletteAttributeIncrease, KBDK_0, MOD3 },
{Event::ToggleColorLoss, KBDK_L, KBDM_CTRL}, { Event::ToggleColorLoss, KBDK_L, KBDM_CTRL },
{Event::PaletteDecrease, KBDK_P, KBDM_SHIFT | KBDM_CTRL}, { Event::PaletteDecrease, KBDK_P, KBDM_SHIFT | KBDM_CTRL },
{Event::PaletteIncrease, KBDK_P, KBDM_CTRL}, { Event::PaletteIncrease, KBDK_P, KBDM_CTRL },
{ Event::FormatDecrease, KBDK_F, KBDM_SHIFT | KBDM_CTRL },
{ Event::FormatIncrease, KBDK_F, KBDM_CTRL },
#ifndef BSPF_MACOS
{ Event::PreviousSetting, KBDK_END },
{ Event::NextSetting, KBDK_HOME },
{ Event::PreviousSettingGroup, KBDK_END, KBDM_CTRL },
{ Event::NextSettingGroup, KBDK_HOME, KBDM_CTRL },
#else
// HOME & END keys are swapped on Mac keyboards
{ Event::PreviousSetting, KBDK_HOME },
{ Event::NextSetting, KBDK_END },
{ Event::PreviousSettingGroup, KBDK_HOME, KBDM_CTRL },
{ Event::NextSettingGroup, KBDK_END, KBDM_CTRL },
#endif
{ Event::PreviousSetting, KBDK_KP_1 },
{ Event::NextSetting, KBDK_KP_7 },
{ Event::PreviousSettingGroup, KBDK_KP_1, KBDM_CTRL },
{ Event::NextSettingGroup, KBDK_KP_7, KBDM_CTRL },
{ Event::SettingDecrease, KBDK_PAGEDOWN },
{ Event::SettingDecrease, KBDK_KP_3, KBDM_CTRL },
{ Event::SettingIncrease, KBDK_PAGEUP },
{ Event::SettingIncrease, KBDK_KP_9, KBDM_CTRL },
#ifndef BSPF_MACOS { Event::ToggleInter, KBDK_I, KBDM_CTRL },
{Event::PreviousSetting, KBDK_END}, { Event::DecreaseSpeed, KBDK_S, KBDM_SHIFT | KBDM_CTRL },
{Event::NextSetting, KBDK_HOME}, { Event::IncreaseSpeed, KBDK_S, KBDM_CTRL },
{Event::PreviousSettingGroup, KBDK_END, KBDM_CTRL}, { Event::ToggleTurbo, KBDK_T, KBDM_CTRL },
{Event::NextSettingGroup, KBDK_HOME, KBDM_CTRL}, { Event::ToggleJitter, KBDK_J, MOD3 },
#else { Event::ToggleFrameStats, KBDK_L, MOD3 },
// HOME & END keys are swapped on Mac keyboards { Event::ToggleTimeMachine, KBDK_T, MOD3 },
{Event::PreviousSetting, KBDK_HOME},
{Event::NextSetting, KBDK_END},
{Event::PreviousSettingGroup, KBDK_HOME, KBDM_CTRL},
{Event::NextSettingGroup, KBDK_END, KBDM_CTRL},
#endif
{Event::PreviousSetting, KBDK_KP_1},
{Event::NextSetting, KBDK_KP_7},
{Event::PreviousSettingGroup, KBDK_KP_1, KBDM_CTRL},
{Event::NextSettingGroup, KBDK_KP_7, KBDM_CTRL},
{Event::SettingDecrease, KBDK_PAGEDOWN},
{Event::SettingDecrease, KBDK_KP_3, KBDM_CTRL},
{Event::SettingIncrease, KBDK_PAGEUP},
{Event::SettingIncrease, KBDK_KP_9, KBDM_CTRL},
{Event::ToggleInter, KBDK_I, KBDM_CTRL}, #ifdef PNG_SUPPORT
{Event::DecreaseSpeed, KBDK_S, KBDM_SHIFT | KBDM_CTRL}, { Event::ToggleContSnapshots, KBDK_S, MOD3 | KBDM_CTRL },
{Event::IncreaseSpeed, KBDK_S, KBDM_CTRL }, { Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3 | KBDM_CTRL },
{Event::ToggleTurbo, KBDK_T, KBDM_CTRL}, #endif
{Event::ToggleJitter, KBDK_J, MOD3},
{Event::ToggleFrameStats, KBDK_L, MOD3},
{Event::ToggleTimeMachine, KBDK_T, MOD3},
#ifdef PNG_SUPPORT { Event::DecreaseDeadzone, KBDK_F1, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleContSnapshots, KBDK_S, MOD3 | KBDM_CTRL}, { Event::IncreaseDeadzone, KBDK_F1, KBDM_CTRL },
{Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3 | KBDM_CTRL}, { Event::DecAnalogSense, KBDK_F2, KBDM_CTRL | KBDM_SHIFT },
#endif { Event::IncAnalogSense, KBDK_F2, KBDM_CTRL },
{ Event::DecDejtterAveraging, KBDK_F3, KBDM_CTRL | KBDM_SHIFT },
{ Event::IncDejtterAveraging, KBDK_F3, KBDM_CTRL },
{ Event::DecDejtterReaction, KBDK_F4, KBDM_CTRL | KBDM_SHIFT },
{ Event::IncDejtterReaction, KBDK_F4, KBDM_CTRL },
{ Event::DecDigitalSense, KBDK_F5, KBDM_CTRL | KBDM_SHIFT },
{ Event::IncDigitalSense, KBDK_F5, KBDM_CTRL },
{ Event::DecreaseAutoFire, KBDK_A, KBDM_CTRL | KBDM_SHIFT },
{ Event::IncreaseAutoFire, KBDK_A, KBDM_CTRL },
{ Event::ToggleFourDirections, KBDK_F6, KBDM_CTRL },
{ Event::ToggleKeyCombos, KBDK_F7, KBDM_CTRL },
{ Event::ToggleSAPortOrder, KBDK_1, KBDM_CTRL },
{Event::DecreaseAutoFire, KBDK_A, KBDM_SHIFT | KBDM_CTRL}, { Event::PrevMouseAsController, KBDK_F8, KBDM_CTRL | KBDM_SHIFT },
{Event::IncreaseAutoFire, KBDK_A, KBDM_CTRL }, { Event::NextMouseAsController, KBDK_F8, KBDM_CTRL },
{Event::HandleMouseControl, KBDK_0, KBDM_CTRL}, { Event::DecMousePaddleSense, KBDK_F9, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleGrabMouse, KBDK_G, KBDM_CTRL}, { Event::IncMousePaddleSense, KBDK_F9, KBDM_CTRL },
{Event::ToggleSAPortOrder, KBDK_1, KBDM_CTRL}, { Event::DecMouseTrackballSense, KBDK_F10, KBDM_CTRL | KBDM_SHIFT },
{Event::FormatDecrease, KBDK_F, KBDM_SHIFT | KBDM_CTRL}, { Event::IncMouseTrackballSense, KBDK_F10, KBDM_CTRL },
{Event::FormatIncrease, KBDK_F, KBDM_CTRL}, { Event::DecreaseDrivingSense, KBDK_F11, KBDM_CTRL | KBDM_SHIFT },
{ Event::IncreaseDrivingSense, KBDK_F11, KBDM_CTRL },
{ Event::PreviousCursorVisbility, KBDK_F12, KBDM_CTRL | KBDM_SHIFT },
{ Event::NextCursorVisbility, KBDK_F12, KBDM_CTRL },
{ Event::ToggleGrabMouse, KBDK_G, KBDM_CTRL },
{Event::ToggleP0Collision, KBDK_Z, KBDM_SHIFT | MOD3}, { Event::PreviousLeftPort, KBDK_2, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleP0Bit, KBDK_Z, MOD3}, { Event::NextLeftPort, KBDK_2, KBDM_CTRL },
{Event::ToggleP1Collision, KBDK_X, KBDM_SHIFT | MOD3}, { Event::PreviousRightPort, KBDK_3, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleP1Bit, KBDK_X, MOD3}, { Event::NextRightPort, KBDK_3, KBDM_CTRL },
{Event::ToggleM0Collision, KBDK_C, KBDM_SHIFT | MOD3}, { Event::ToggleSwapPorts, KBDK_4, KBDM_CTRL },
{Event::ToggleM0Bit, KBDK_C, MOD3}, { Event::ToggleSwapPaddles, KBDK_5, KBDM_CTRL },
{Event::ToggleM1Collision, KBDK_V, KBDM_SHIFT | MOD3}, { Event::DecreasePaddleCenterX, KBDK_6, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleM1Bit, KBDK_V, MOD3}, { Event::IncreasePaddleCenterX, KBDK_6, KBDM_CTRL },
{Event::ToggleBLCollision, KBDK_B, KBDM_SHIFT | MOD3}, { Event::DecreasePaddleCenterY, KBDK_7, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleBLBit, KBDK_B, MOD3}, { Event::IncreasePaddleCenterY, KBDK_7, KBDM_CTRL },
{Event::TogglePFCollision, KBDK_N, KBDM_SHIFT | MOD3}, { Event::PreviousMouseControl, KBDK_0, KBDM_CTRL | KBDM_SHIFT },
{Event::TogglePFBit, KBDK_N, MOD3}, { Event::NextMouseControl, KBDK_0, KBDM_CTRL },
{Event::ToggleCollisions, KBDK_COMMA, KBDM_SHIFT | MOD3}, { Event::DecreaseMouseAxesRange, KBDK_8, KBDM_CTRL | KBDM_SHIFT },
{Event::ToggleBits, KBDK_COMMA, MOD3}, { Event::IncreaseMouseAxesRange, KBDK_8, KBDM_CTRL },
{Event::ToggleFixedColors, KBDK_PERIOD, MOD3},
{Event::RewindPause, KBDK_LEFT, KBDM_SHIFT}, { Event::ToggleP0Collision, KBDK_Z, KBDM_SHIFT | MOD3 },
{Event::Rewind1Menu, KBDK_LEFT, MOD3}, { Event::ToggleP0Bit, KBDK_Z, MOD3 },
{Event::Rewind10Menu, KBDK_LEFT, KBDM_SHIFT | MOD3}, { Event::ToggleP1Collision, KBDK_X, KBDM_SHIFT | MOD3 },
{Event::RewindAllMenu, KBDK_DOWN, MOD3}, { Event::ToggleP1Bit, KBDK_X, MOD3 },
{Event::UnwindPause, KBDK_LEFT, KBDM_SHIFT}, { Event::ToggleM0Collision, KBDK_C, KBDM_SHIFT | MOD3 },
{Event::Unwind1Menu, KBDK_RIGHT, MOD3}, { Event::ToggleM0Bit, KBDK_C, MOD3 },
{Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3}, { Event::ToggleM1Collision, KBDK_V, KBDM_SHIFT | MOD3 },
{Event::UnwindAllMenu, KBDK_UP, MOD3}, { Event::ToggleM1Bit, KBDK_V, MOD3 },
{Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT}, { Event::ToggleBLCollision, KBDK_B, KBDM_SHIFT | MOD3 },
{ Event::ToggleBLBit, KBDK_B, MOD3 },
{ Event::TogglePFCollision, KBDK_N, KBDM_SHIFT | MOD3 },
{ Event::TogglePFBit, KBDK_N, MOD3 },
{ Event::ToggleCollisions, KBDK_COMMA, KBDM_SHIFT | MOD3 },
{ Event::ToggleBits, KBDK_COMMA, MOD3 },
{ Event::ToggleFixedColors, KBDK_PERIOD, MOD3 },
#if defined(RETRON77) { Event::RewindPause, KBDK_LEFT, KBDM_SHIFT },
{Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W") { Event::Rewind1Menu, KBDK_LEFT, MOD3 },
{Event::ConsoleLeftDiffToggle, KBDK_F6}, // front ("SKILL P1") { Event::Rewind10Menu, KBDK_LEFT, KBDM_SHIFT | MOD3 },
{Event::ConsoleRightDiffToggle, KBDK_F8}, // front ("SKILL P2") { Event::RewindAllMenu, KBDK_DOWN, MOD3 },
{Event::CmdMenuMode, KBDK_F13}, // back ("4:3","16:9") { Event::UnwindPause, KBDK_LEFT, KBDM_SHIFT },
{Event::ExitMode, KBDK_BACKSPACE}, // back ("FRY") { Event::Unwind1Menu, KBDK_RIGHT, MOD3 },
#else // defining duplicate keys must be avoided! { Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3 },
{Event::ConsoleBlackWhite, KBDK_F4}, { Event::UnwindAllMenu, KBDK_UP, MOD3 },
{Event::ConsoleLeftDiffB, KBDK_F6}, { Event::HighScoresMenuMode, KBDK_INSERT },
{Event::ConsoleRightDiffB, KBDK_F8}, { Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT },
{Event::Fry, KBDK_BACKSPACE},
#if defined(RETRON77)
{ Event::ConsoleColorToggle, KBDK_F4 }, // back ("COLOR","B/W")
{ Event::ConsoleLeftDiffToggle, KBDK_F6 }, // front ("SKILL P1")
{ Event::ConsoleRightDiffToggle, KBDK_F8 }, // front ("SKILL P2")
{ Event::CmdMenuMode, KBDK_F13 }, // back ("4:3","16:9")
{ Event::ExitMode, KBDK_BACKSPACE }, // back ("FRY")
#else // defining duplicate keys must be avoided!
{ Event::ConsoleBlackWhite, KBDK_F4 },
{ Event::ConsoleLeftDiffB, KBDK_F6 },
{ Event::ConsoleRightDiffB, KBDK_F8 },
{ Event::Fry, KBDK_BACKSPACE },
#endif #endif
}; };

View File

@ -79,6 +79,8 @@ class PhysicalKeyboardHandler
/** See comments on KeyMap.myModEnabled for more information. */ /** See comments on KeyMap.myModEnabled for more information. */
bool& useModKeys() { return myKeyMap.enableMod(); } bool& useModKeys() { return myKeyMap.enableMod(); }
void toggleModKeys(bool toggle = true);
private: private:
// Structure used for action menu items // Structure used for action menu items

View File

@ -33,7 +33,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::PNGLibrary(OSystem& osystem) PNGLibrary::PNGLibrary(OSystem& osystem)
: myOSystem(osystem) : myOSystem{osystem}
{ {
} }

View File

@ -22,8 +22,9 @@
#include "PaletteHandler.hxx" #include "PaletteHandler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PaletteHandler::PaletteHandler(OSystem& system) PaletteHandler::PaletteHandler(OSystem& system)
: myOSystem(system) : myOSystem{system}
{ {
// Load user-defined palette for this ROM // Load user-defined palette for this ROM
loadUserPalette(); loadUserPalette();
@ -441,7 +442,7 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing)
if(timing == ConsoleTiming::ntsc) if(timing == ConsoleTiming::ntsc)
{ {
vector2d IQ[NUM_CHROMA]; vector2d IQ[NUM_CHROMA];
// YIQ is YUV shifted by 33° // YIQ is YUV shifted by 33 degrees
constexpr float offset = 33 * BSPF::PI_f / 180; constexpr float offset = 33 * BSPF::PI_f / 180;
const float shift = myPhaseNTSC * BSPF::PI_f / 180; const float shift = myPhaseNTSC * BSPF::PI_f / 180;
@ -541,7 +542,7 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing)
void PaletteHandler::adjustHueSaturation(int& R, int& G, int& B, float H, float S) void PaletteHandler::adjustHueSaturation(int& R, int& G, int& B, float H, float S)
{ {
// Adapted from http://beesbuzz.biz/code/16-hsv-color-transforms // Adapted from http://beesbuzz.biz/code/16-hsv-color-transforms
// (C) J. Fluffy Shagam // (C) J. Fluffy Shagam
// License: CC BY-SA 4.0 // License: CC BY-SA 4.0
const float su = S * cosf(-H * BSPF::PI_f); const float su = S * cosf(-H * BSPF::PI_f);
const float sw = S * sinf(-H * BSPF::PI_f); const float sw = S * sinf(-H * BSPF::PI_f);

View File

@ -65,7 +65,7 @@ class PaletteHandler
}; };
public: public:
PaletteHandler(OSystem& system); explicit PaletteHandler(OSystem& system);
/** /**
Cycle through available palettes. Cycle through available palettes.
@ -139,7 +139,7 @@ class PaletteHandler
float y{0.F}; float y{0.F};
explicit vector2d(float _x = 0.F, float _y = 0.F) explicit vector2d(float _x = 0.F, float _y = 0.F)
: x(_x), y(_y) { } : x{_x}, y{_y} { }
}; };
/** /**

View File

@ -38,7 +38,7 @@ class PhosphorHandler
@return Averaged value of the two RGB colors @return Averaged value of the two RGB colors
*/ */
static inline uInt32 getPixel(const uInt32 c, const uInt32 p) static constexpr uInt32 getPixel(const uInt32 c, const uInt32 p)
{ {
// Mix current calculated frame with previous displayed frame // Mix current calculated frame with previous displayed frame
const uInt8 rc = static_cast<uInt8>(c >> 16), const uInt8 rc = static_cast<uInt8>(c >> 16),

View File

@ -87,7 +87,7 @@ bool PhysicalJoystick::setMap(const json& map)
try { try {
joyMap.loadMapping(entry.value(), eventModeFromJsonName(entry.key())); joyMap.loadMapping(entry.value(), eventModeFromJsonName(entry.key()));
} catch (json::exception) { } catch (const json::exception&) {
Logger::error("ignoring invalid json mapping for " + entry.key()); Logger::error("ignoring invalid json mapping for " + entry.key());
} }

View File

@ -21,7 +21,7 @@
#include "Event.hxx" #include "Event.hxx"
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "JoyMap.hxx" #include "JoyMap.hxx"
#include "json.hxx" #include "json_lib.hxx"
/** /**
An abstraction of a physical (real) joystick in Stella. An abstraction of a physical (real) joystick in Stella.
@ -59,17 +59,13 @@ class PhysicalJoystick
int axes, int buttons, int hats, int balls); int axes, int buttons, int hats, int balls);
private: private:
// TODO: these are not required anymore, delete or keep for future usage? enum class Type {
enum JoyType { REGULAR,
JT_NONE = 0, LEFT_STELLADAPTOR, RIGHT_STELLADAPTOR,
JT_REGULAR = 1, LEFT_2600DAPTOR, RIGHT_2600DAPTOR
JT_STELLADAPTOR_LEFT = 2,
JT_STELLADAPTOR_RIGHT = 3,
JT_2600DAPTOR_LEFT = 4,
JT_2600DAPTOR_RIGHT = 5
}; };
JoyType type{JT_NONE}; Type type{Type::REGULAR};
int ID{-1}; int ID{-1};
string name{"None"}; string name{"None"};
int numAxes{0}, numButtons{0}, numHats{0}; int numAxes{0}, numButtons{0}, numHats{0};

View File

@ -32,6 +32,7 @@ namespace Common {
*/ */
struct Point struct Point
{ {
// FIXME : make this uInt32
Int32 x{0}; //!< The horizontal part of the point Int32 x{0}; //!< The horizontal part of the point
Int32 y{0}; //!< The vertical part of the point Int32 y{0}; //!< The vertical part of the point

View File

@ -28,8 +28,8 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RewindManager::RewindManager(OSystem& system, StateManager& statemgr) RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
: myOSystem(system), : myOSystem{system},
myStateManager(statemgr) myStateManager{statemgr}
{ {
setup(); setup();
} }
@ -41,18 +41,15 @@ void RewindManager::setup()
const string& prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr."; const string& prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr.";
// Work around a bug in XCode 11.2 with -O0 and -O1
const uInt32 maxBufSize = MAX_BUF_SIZE;
// TODO - Add proper bounds checking (define constexpr variables for this) // TODO - Add proper bounds checking (define constexpr variables for this)
// Use those bounds in DeveloperDialog too // Use those bounds in DeveloperDialog too
mySize = std::min<uInt32>( mySize = std::min<uInt32>(
myOSystem.settings().getInt(prefix + "tm.size"), maxBufSize); myOSystem.settings().getInt(prefix + "tm.size"), MAX_BUF_SIZE);
if(mySize != myStateList.capacity()) if(mySize != myStateList.capacity())
resize(mySize); resize(mySize);
myUncompressed = std::min<uInt32>( myUncompressed = std::min<uInt32>(
myOSystem.settings().getInt(prefix + "tm.uncompressed"), maxBufSize); myOSystem.settings().getInt(prefix + "tm.uncompressed"), MAX_BUF_SIZE);
myInterval = INTERVAL_CYCLES[0]; myInterval = INTERVAL_CYCLES[0];
for(int i = 0; i < NUM_INTERVALS; ++i) for(int i = 0; i < NUM_INTERVALS; ++i)

View File

@ -158,6 +158,7 @@ class RewindManager
uInt64 getFirstCycles() const; uInt64 getFirstCycles() const;
uInt64 getCurrentCycles() const; uInt64 getCurrentCycles() const;
uInt64 getLastCycles() const; uInt64 getLastCycles() const;
uInt64 getInterval() const { return myInterval; }
/** /**
Get a collection of cycle timestamps, offset from the first one in Get a collection of cycle timestamps, offset from the first one in

View File

@ -40,8 +40,8 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings) SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
: Sound(osystem), : Sound{osystem},
myAudioSettings(audioSettings) myAudioSettings{audioSettings}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -273,7 +273,7 @@ void SoundSDL2::adjustVolume(int direction)
if(percent > 0 && !enabled) if(percent > 0 && !enabled)
{ {
setEnabled(!enabled); setEnabled(true);
myOSystem.console().initializeAudio(); myOSystem.console().initializeAudio();
} }

View File

@ -37,8 +37,8 @@ namespace {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StaggeredLogger::StaggeredLogger(const string& message, Logger::Level level) StaggeredLogger::StaggeredLogger(const string& message, Logger::Level level)
: myMessage(message), : myMessage{message},
myLevel(level) myLevel{level}
{ {
} }

View File

@ -31,7 +31,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StateManager::StateManager(OSystem& osystem) StateManager::StateManager(OSystem& osystem)
: myOSystem(osystem) : myOSystem{osystem}
{ {
myRewindManager = make_unique<RewindManager>(myOSystem, *this); myRewindManager = make_unique<RewindManager>(myOSystem, *this);
reset(); reset();

View File

@ -20,7 +20,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TimerManager::TimerManager() TimerManager::TimerManager()
: nextId(no_timer + 1), : nextId{no_timer + 1},
queue() queue()
{ {
} }

View File

@ -44,8 +44,8 @@ class Variant
public: public:
Variant() { } // NOLINT Variant() { } // NOLINT
Variant(const string& s) : data(s) { } Variant(const string& s) : data{s} { }
Variant(const char* s) : data(s) { } Variant(const char* s) : data{s} { }
Variant(Int32 i) { buf().str(""); buf() << i; data = buf().str(); } Variant(Int32 i) { buf().str(""); buf() << i; data = buf().str(); }
Variant(uInt32 i) { buf().str(""); buf() << i; data = buf().str(); } Variant(uInt32 i) { buf().str(""); buf() << i; data = buf().str(); }

View File

@ -105,14 +105,12 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode,
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, Int32 fsindex, const string& desc, Stretch smode, Int32 fsindex, const string& desc,
float zoomLevel, float overscan) float zoomLevel, float overscan)
: stretch(smode), : screenS(sw, sh),
stretch(smode),
description(desc), description(desc),
zoom(zoomLevel), zoom(zoomLevel),
fsIndex(fsindex) fsIndex(fsindex)
{ {
// First set default size and positioning
screenS = Common::Size(sw, sh);
// Now resize based on windowed/fullscreen mode and stretch factor // Now resize based on windowed/fullscreen mode and stretch factor
if(fsIndex != -1) // fullscreen mode if(fsIndex != -1) // fullscreen mode
{ {

View File

@ -19,9 +19,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConvolutionBuffer::ConvolutionBuffer(uInt32 size) ConvolutionBuffer::ConvolutionBuffer(uInt32 size)
: mySize(size) : myData{make_unique<float[]>(size)},
mySize{size}
{ {
myData = make_unique<float[]>(mySize);
std::fill_n(myData.get(), mySize, 0.F); std::fill_n(myData.get(), mySize, 0.F);
} }

View File

@ -20,7 +20,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HighPass::HighPass(float cutOffFrequency, float frequency) HighPass::HighPass(float cutOffFrequency, float frequency)
: myAlpha(1.F / (1.F + 2.F*BSPF::PI_f*cutOffFrequency/frequency)) : myAlpha{1.F / (1.F + 2.F*BSPF::PI_f*cutOffFrequency/frequency)}
{ {
} }

View File

@ -69,12 +69,12 @@ LanczosResampler::LanczosResampler(
// formatFrom.sampleRate / formatTo.sampleRate = M / N // formatFrom.sampleRate / formatTo.sampleRate = M / N
// //
// -> we find N from fully reducing the fraction. // -> we find N from fully reducing the fraction.
myPrecomputedKernelCount(reducedDenominator(formatFrom.sampleRate, formatTo.sampleRate)), myPrecomputedKernelCount{reducedDenominator(formatFrom.sampleRate, formatTo.sampleRate)},
myKernelSize(2 * kernelParameter), myKernelSize{2 * kernelParameter},
myKernelParameter(kernelParameter), myKernelParameter{kernelParameter},
myHighPassL(HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)), myHighPassL{HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)},
myHighPassR(HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)), myHighPassR{HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)},
myHighPass(HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)) myHighPass{HIGH_PASS_CUT_OFF, float(formatFrom.sampleRate)}
{ {
myPrecomputedKernels = make_unique<float[]>(myPrecomputedKernelCount * myKernelSize); myPrecomputedKernels = make_unique<float[]>(myPrecomputedKernelCount * myKernelSize);

View File

@ -51,12 +51,11 @@ class Resampler {
public: public:
Resampler(Format formatFrom, Format formatTo, Resampler(Format formatFrom, Format formatTo,
const NextFragmentCallback& nextFragmentCallback) : const NextFragmentCallback& nextFragmentCallback)
myFormatFrom(formatFrom), : myFormatFrom{formatFrom},
myFormatTo(formatTo), myFormatTo{formatTo},
myNextFragmentCallback(nextFragmentCallback), myNextFragmentCallback{nextFragmentCallback},
myUnderrunLogger("audio buffer underrun", Logger::Level::INFO) myUnderrunLogger{"audio buffer underrun", Logger::Level::INFO} { }
{}
virtual void fillFragment(float* fragment, uInt32 length) = 0; virtual void fillFragment(float* fragment, uInt32 length) = 0;

View File

@ -39,13 +39,11 @@ class SimpleResampler : public Resampler
bool myIsUnderrun{true}; bool myIsUnderrun{true};
private: private:
SimpleResampler() = delete; SimpleResampler() = delete;
SimpleResampler(const SimpleResampler&) = delete; SimpleResampler(const SimpleResampler&) = delete;
SimpleResampler(SimpleResampler&&) = delete; SimpleResampler(SimpleResampler&&) = delete;
SimpleResampler& operator=(const SimpleResampler&) = delete; SimpleResampler& operator=(const SimpleResampler&) = delete;
SimpleResampler& operator=(const SimpleResampler&&) = delete; SimpleResampler& operator=(const SimpleResampler&&) = delete;
}; };
#endif // SIMPLE_RESAMPLER_HXX #endif // SIMPLE_RESAMPLER_HXX

View File

@ -47,6 +47,7 @@ using uInt64 = uint64_t;
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view>
#include <sstream> #include <sstream>
#include <cstring> #include <cstring>
#include <cctype> #include <cctype>
@ -60,6 +61,7 @@ using std::cout;
using std::cerr; using std::cerr;
using std::endl; using std::endl;
using std::string; using std::string;
using std::string_view;
using std::istream; using std::istream;
using std::ostream; using std::ostream;
using std::fstream; using std::fstream;
@ -193,51 +195,54 @@ namespace BSPF
catch(...) { return defaultValue; } catch(...) { return defaultValue; }
} }
// Compare two strings, ignoring case // Convert string with base 16 to integer, using default value on any error
inline int compareIgnoreCase(const string& s1, const string& s2) inline int stringToIntBase16(const string& s, const int defaultValue = 0)
{ {
#if (defined BSPF_WINDOWS || defined __WIN32__) && !defined __GNUG__ try { return std::stoi(s, nullptr, 16); }
return _stricmp(s1.c_str(), s2.c_str()); catch(...) { return defaultValue; }
#else
return strcasecmp(s1.c_str(), s2.c_str());
#endif
}
inline int compareIgnoreCase(const char* s1, const char* s2)
{
#if (defined BSPF_WINDOWS || defined __WIN32__) && !defined __GNUG__
return _stricmp(s1, s2);
#else
return strcasecmp(s1, s2);
#endif
} }
// Test whether the first string starts with the second one (case insensitive) // Compare two strings (case insensitive)
inline bool startsWithIgnoreCase(const string& s1, const string& s2) // Return negative, zero, positive result for <,==,> respectively
static constexpr int compareIgnoreCase(string_view s1, string_view s2)
{ {
#if (defined BSPF_WINDOWS || defined __WIN32__) && !defined __GNUG__ // Only compare up to the length of the shorter string
return _strnicmp(s1.c_str(), s2.c_str(), s2.length()) == 0; const auto maxsize = std::min(s1.size(), s2.size());
#else for(size_t i = 0; i < maxsize; ++i)
return strncasecmp(s1.c_str(), s2.c_str(), s2.length()) == 0; if(toupper(s1[i]) != toupper(s2[i]))
#endif return toupper(s1[i]) - toupper(s2[i]);
}
inline bool startsWithIgnoreCase(const char* s1, const char* s2) // Otherwise the length of the string takes priority
{ return static_cast<int>(s1.size() - s2.size());
#if (defined BSPF_WINDOWS || defined __WIN32__) && !defined __GNUG__
return _strnicmp(s1, s2, strlen(s2)) == 0;
#else
return strncasecmp(s1, s2, strlen(s2)) == 0;
#endif
} }
// Test whether two strings are equal (case insensitive) // Test whether two strings are equal (case insensitive)
inline bool equalsIgnoreCase(const string& s1, const string& s2) inline constexpr bool equalsIgnoreCase(string_view s1, string_view s2)
{ {
return compareIgnoreCase(s1, s2) == 0; return s1.size() == s2.size() ? (compareIgnoreCase(s1, s2) == 0) : false;
}
// Test whether the first string starts with the second one (case insensitive)
inline constexpr bool startsWithIgnoreCase(string_view s1, string_view s2)
{
if(s1.size() >= s2.size())
return compareIgnoreCase(s1.substr(0, s2.size()), s2) == 0;
return false;
}
// Test whether the first string ends with the second one (case insensitive)
inline constexpr bool endsWithIgnoreCase(string_view s1, string_view s2)
{
if(s1.size() >= s2.size())
return compareIgnoreCase(s1.substr(s1.size() - s2.size()), s2) == 0;
return false;
} }
// Find location (if any) of the second string within the first, // Find location (if any) of the second string within the first,
// starting from 'startpos' in the first string // starting from 'startpos' in the first string
inline size_t findIgnoreCase(const string& s1, const string& s2, size_t startpos = 0) static size_t findIgnoreCase(string_view s1, string_view s2, size_t startpos = 0)
{ {
auto pos = std::search(s1.cbegin()+startpos, s1.cend(), auto pos = std::search(s1.cbegin()+startpos, s1.cend(),
s2.cbegin(), s2.cend(), [](char ch1, char ch2) { s2.cbegin(), s2.cend(), [](char ch1, char ch2) {
@ -246,19 +251,8 @@ namespace BSPF
return pos == s1.cend() ? string::npos : pos - (s1.cbegin()+startpos); return pos == s1.cend() ? string::npos : pos - (s1.cbegin()+startpos);
} }
// Test whether the first string ends with the second one (case insensitive)
inline bool endsWithIgnoreCase(const string& s1, const string& s2)
{
if(s1.length() >= s2.length())
{
const char* end = s1.c_str() + s1.length() - s2.length();
return compareIgnoreCase(end, s2.c_str()) == 0;
}
return false;
}
// Test whether the first string contains the second one (case insensitive) // Test whether the first string contains the second one (case insensitive)
inline bool containsIgnoreCase(const string& s1, const string& s2) inline bool containsIgnoreCase(string_view s1, string_view s2)
{ {
return findIgnoreCase(s1, s2) != string::npos; return findIgnoreCase(s1, s2) != string::npos;
} }
@ -266,14 +260,14 @@ namespace BSPF
// Test whether the first string matches the second one (case insensitive) // Test whether the first string matches the second one (case insensitive)
// - the first character must match // - the first character must match
// - the following characters must appear in the order of the first string // - the following characters must appear in the order of the first string
inline bool matches(const string& s1, const string& s2) inline bool matches(string_view s1, string_view s2)
{ {
if(BSPF::startsWithIgnoreCase(s1, s2.substr(0, 1))) if(startsWithIgnoreCase(s1, s2.substr(0, 1)))
{ {
size_t pos = 1; size_t pos = 1;
for(uInt32 j = 1; j < s2.size(); ++j) for(uInt32 j = 1; j < s2.size(); ++j)
{ {
size_t found = BSPF::findIgnoreCase(s1, s2.substr(j, 1), pos); size_t found = findIgnoreCase(s1, s2.substr(j, 1), pos);
if(found == string::npos) if(found == string::npos)
return false; return false;
pos += found + 1; pos += found + 1;
@ -283,6 +277,27 @@ namespace BSPF
return false; return false;
} }
// Modify 'str', replacing all occurrences of 'from' with 'to'
inline void replaceAll(string& str, const string& from, const string& to)
{
if(from.empty()) return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from',
// like replacing 'x' with 'yx'
}
}
// Trim leading and trailing whitespace from a string
inline string trim(const string& str)
{
string::size_type first = str.find_first_not_of(' ');
return (first == string::npos) ? EmptyString :
str.substr(first, str.find_last_not_of(' ')-first+1);
}
// C++11 way to get local time // C++11 way to get local time
// Equivalent to the C-style localtime() function, but is thread-safe // Equivalent to the C-style localtime() function, but is thread-safe
inline std::tm localTime() inline std::tm localTime()

View File

@ -1,9 +1,26 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef JSON_DEFINITIONS_HXX #ifndef JSON_DEFINITIONS_HXX
#define JSON_DEFINITIONS_HXX #define JSON_DEFINITIONS_HXX
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "Event.hxx" #include "Event.hxx"
#include "json.hxx" #include "json_lib.hxx"
#include "StellaKeys.hxx" #include "StellaKeys.hxx"
NLOHMANN_JSON_SERIALIZE_ENUM(JoyAxis, { NLOHMANN_JSON_SERIALIZE_ENUM(JoyAxis, {
@ -27,7 +44,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(JoyHatDir, {
{JoyHatDir::LEFT, "left"}, {JoyHatDir::LEFT, "left"},
{JoyHatDir::RIGHT, "right"}, {JoyHatDir::RIGHT, "right"},
{JoyHatDir::UP, "up"} {JoyHatDir::UP, "up"}
}); })
NLOHMANN_JSON_SERIALIZE_ENUM(EventMode, { NLOHMANN_JSON_SERIALIZE_ENUM(EventMode, {
{EventMode::kEditMode, "kEditMode"}, {EventMode::kEditMode, "kEditMode"},
@ -39,7 +56,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(EventMode, {
{EventMode::kCompuMateMode, "kCompuMateMode"}, {EventMode::kCompuMateMode, "kCompuMateMode"},
{EventMode::kCommonMode, "kCommonMode"}, {EventMode::kCommonMode, "kCommonMode"},
{EventMode::kNumModes, "kNumModes"}, {EventMode::kNumModes, "kNumModes"},
}); })
NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, { NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::NoType, "NoType"}, {Event::NoType, "NoType"},
@ -69,6 +86,16 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::JoystickOneFire, "JoystickOneFire"}, {Event::JoystickOneFire, "JoystickOneFire"},
{Event::JoystickOneFire5, "JoystickOneFire5"}, {Event::JoystickOneFire5, "JoystickOneFire5"},
{Event::JoystickOneFire9, "JoystickOneFire9"}, {Event::JoystickOneFire9, "JoystickOneFire9"},
{Event::JoystickTwoUp, "JoystickTwoUp"},
{Event::JoystickTwoDown, "JoystickTwoDown"},
{Event::JoystickTwoLeft, "JoystickTwoLeft"},
{Event::JoystickTwoRight, "JoystickTwoRight"},
{Event::JoystickTwoFire, "JoystickTwoFire"},
{Event::JoystickThreeUp, "JoystickThreeUp"},
{Event::JoystickThreeDown, "JoystickThreeDown"},
{Event::JoystickThreeLeft, "JoystickThreeLeft"},
{Event::JoystickThreeRight, "JoystickThreeRight"},
{Event::JoystickThreeFire, "JoystickThreeFire"},
{Event::PaddleZeroDecrease, "PaddleZeroDecrease"}, {Event::PaddleZeroDecrease, "PaddleZeroDecrease"},
{Event::PaddleZeroIncrease, "PaddleZeroIncrease"}, {Event::PaddleZeroIncrease, "PaddleZeroIncrease"},
{Event::PaddleZeroAnalog, "PaddleZeroAnalog"}, {Event::PaddleZeroAnalog, "PaddleZeroAnalog"},
@ -160,6 +187,50 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::CompuMateEquals, "CompuMateEquals"}, {Event::CompuMateEquals, "CompuMateEquals"},
{Event::CompuMatePlus, "CompuMatePlus"}, {Event::CompuMatePlus, "CompuMatePlus"},
{Event::CompuMateSlash, "CompuMateSlash"}, {Event::CompuMateSlash, "CompuMateSlash"},
{Event::DecreaseDeadzone, "DecreaseDeadzone"},
{Event::IncreaseDeadzone, "IncreaseDeadzone"},
{Event::DecAnalogSense, "DecAnalogSense"},
{Event::IncAnalogSense, "IncAnalogSense"},
{Event::DecDejtterAveraging, "DecDejtterAveraging"},
{Event::IncDejtterAveraging, "IncDejtterAveraging"},
{Event::DecDejtterReaction, "DecDejtterReaction"},
{Event::IncDejtterReaction, "IncDejtterReaction"},
{Event::DecDigitalSense, "DecDigitalSense"},
{Event::IncDigitalSense, "IncDigitalSense"},
{Event::DecreaseAutoFire, "DecreaseAutoFire"},
{Event::IncreaseAutoFire, "IncreaseAutoFire"},
{Event::ToggleFourDirections, "ToggleFourDirections"},
{Event::ToggleKeyCombos, "ToggleKeyCombos"},
{Event::ToggleSAPortOrder, "ToggleSAPortOrder"},
{Event::PrevMouseAsController, "PrevMouseAsController"},
{Event::NextMouseAsController, "NextMouseAsController"},
{Event::DecMousePaddleSense, "DecMousePaddleSense"},
{Event::IncMousePaddleSense, "IncMousePaddleSense"},
{Event::DecMouseTrackballSense, "DecMouseTrackballSense"},
{Event::IncMouseTrackballSense, "IncMouseTrackballSense"},
{Event::DecreaseDrivingSense, "DecreaseDrivingSense"},
{Event::IncreaseDrivingSense, "IncreaseDrivingSense"},
{Event::PreviousCursorVisbility, "PreviousCursorVisbility"},
{Event::NextCursorVisbility, "NextCursorVisbility"},
{Event::ToggleGrabMouse, "ToggleGrabMouse"},
{Event::PreviousLeftPort, "PreviousLeftPort"},
{Event::NextLeftPort, "NextLeftPort"},
{Event::PreviousRightPort, "PreviousRightPort"},
{Event::NextRightPort, "NextRightPort"},
{Event::ToggleSwapPorts, "ToggleSwapPorts"},
{Event::ToggleSwapPaddles,"ToggleSwapPaddles"},
{Event::DecreasePaddleCenterX, "DecreasePaddleCenterX"},
{Event::IncreasePaddleCenterX, "IncreasePaddleCenterX"},
{Event::DecreasePaddleCenterY, "DecreasePaddleCenterY"},
{Event::IncreasePaddleCenterY, "IncreasePaddleCenterY"},
{Event::PreviousMouseControl, "PreviousMouseControl"},
{Event::NextMouseControl, "NextMouseControl"},
{Event::DecreaseMouseAxesRange, "DecreaseMouseAxesRange"},
{Event::IncreaseMouseAxesRange, "IncreaseMouseAxesRange"},
{Event::Combo1, "Combo1"}, {Event::Combo1, "Combo1"},
{Event::Combo2, "Combo2"}, {Event::Combo2, "Combo2"},
{Event::Combo3, "Combo3"}, {Event::Combo3, "Combo3"},
@ -192,8 +263,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::UIPrevDir, "UIPrevDir"}, {Event::UIPrevDir, "UIPrevDir"},
{Event::UITabPrev, "UITabPrev"}, {Event::UITabPrev, "UITabPrev"},
{Event::UITabNext, "UITabNext"}, {Event::UITabNext, "UITabNext"},
{Event::HandleMouseControl, "HandleMouseControl"},
{Event::ToggleGrabMouse, "ToggleGrabMouse"},
{Event::MouseAxisXMove, "MouseAxisXMove"}, {Event::MouseAxisXMove, "MouseAxisXMove"},
{Event::MouseAxisYMove, "MouseAxisYMove"}, {Event::MouseAxisYMove, "MouseAxisYMove"},
{Event::MouseAxisXValue, "MouseAxisXValue"}, {Event::MouseAxisXValue, "MouseAxisXValue"},
@ -207,6 +276,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::StartPauseMode, "StartPauseMode"}, {Event::StartPauseMode, "StartPauseMode"},
{Event::OptionsMenuMode, "OptionsMenuMode"}, {Event::OptionsMenuMode, "OptionsMenuMode"},
{Event::CmdMenuMode, "CmdMenuMode"}, {Event::CmdMenuMode, "CmdMenuMode"},
{Event::HighScoresMenuMode, "HighScoresMenuMode"},
{Event::DebuggerMode, "DebuggerMode"}, {Event::DebuggerMode, "DebuggerMode"},
{Event::ExitMode, "ExitMode"}, {Event::ExitMode, "ExitMode"},
{Event::TakeSnapshot, "TakeSnapshot"}, {Event::TakeSnapshot, "TakeSnapshot"},
@ -286,7 +356,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::ToggleBits, "ToggleBits"}, {Event::ToggleBits, "ToggleBits"},
{Event::ToggleFixedColors, "ToggleFixedColors"}, {Event::ToggleFixedColors, "ToggleFixedColors"},
{Event::ToggleFrameStats, "ToggleFrameStats"}, {Event::ToggleFrameStats, "ToggleFrameStats"},
{Event::ToggleSAPortOrder, "ToggleSAPortOrder"},
{Event::ExitGame, "ExitGame"}, {Event::ExitGame, "ExitGame"},
{Event::SettingDecrease, "SettingDecrease"}, {Event::SettingDecrease, "SettingDecrease"},
{Event::SettingIncrease, "SettingIncrease"}, {Event::SettingIncrease, "SettingIncrease"},
@ -301,16 +370,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::IncreaseAutoFire, "IncreaseAutoFire"}, {Event::IncreaseAutoFire, "IncreaseAutoFire"},
{Event::DecreaseSpeed, "DecreaseSpeed"}, {Event::DecreaseSpeed, "DecreaseSpeed"},
{Event::IncreaseSpeed, "IncreaseSpeed"}, {Event::IncreaseSpeed, "IncreaseSpeed"},
{Event::JoystickTwoUp, "JoystickTwoUp"},
{Event::JoystickTwoDown, "JoystickTwoDown"},
{Event::JoystickTwoLeft, "JoystickTwoLeft"},
{Event::JoystickTwoRight, "JoystickTwoRight"},
{Event::JoystickTwoFire, "JoystickTwoFire"},
{Event::JoystickThreeUp, "JoystickThreeUp"},
{Event::JoystickThreeDown, "JoystickThreeDown"},
{Event::JoystickThreeLeft, "JoystickThreeLeft"},
{Event::JoystickThreeRight, "JoystickThreeRight"},
{Event::JoystickThreeFire, "JoystickThreeFire"},
{Event::ToggleCorrectAspectRatio, "ToggleCorrectAspectRatio"}, {Event::ToggleCorrectAspectRatio, "ToggleCorrectAspectRatio"},
{Event::MoveLeftChar, "MoveLeftChar"}, {Event::MoveLeftChar, "MoveLeftChar"},
{Event::MoveRightChar, "MoveRightChar"}, {Event::MoveRightChar, "MoveRightChar"},

View File

@ -123,11 +123,11 @@ void parseCommandLine(int ac, char* av[],
#if 0 #if 0
cout << "Global opts:" << endl; cout << "Global opts:" << endl;
for(const auto& x: globalOpts) for(const auto& [key, value]: globalOpts)
cout << " -> " << x.first << ": " << x.second << endl; cout << " -> " << key << ": " << value << endl;
cout << "Local opts:" << endl; cout << "Local opts:" << endl;
for(const auto& x: localOpts) for(const auto& [key, value]: localOpts)
cout << " -> " << x.first << ": " << x.second << endl; cout << " -> " << key << ": " << value << endl;
#endif #endif
} }

View File

@ -9,6 +9,7 @@ MODULE_OBJS := \
src/common/FBSurfaceSDL2.o \ src/common/FBSurfaceSDL2.o \
src/common/FpsMeter.o \ src/common/FpsMeter.o \
src/common/FSNodeZIP.o \ src/common/FSNodeZIP.o \
src/common/HighScoresManager.o \
src/common/JoyMap.o \ src/common/JoyMap.o \
src/common/KeyMap.o \ src/common/KeyMap.o \
src/common/Logger.o \ src/common/Logger.o \

View File

@ -18,18 +18,9 @@
#include "KeyValueRepositoryConfigfile.hxx" #include "KeyValueRepositoryConfigfile.hxx"
#include "Logger.hxx" #include "Logger.hxx"
namespace {
string trim(const string& str)
{
string::size_type first = str.find_first_not_of(' ');
return (first == string::npos) ? EmptyString :
str.substr(first, str.find_last_not_of(' ')-first+1);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const FilesystemNode& file) KeyValueRepositoryConfigfile::KeyValueRepositoryConfigfile(const FilesystemNode& file)
: myFile(file) : myFile{file}
{ {
} }
@ -67,8 +58,8 @@ std::map<string, Variant> KeyValueRepositoryConfigfile::load()
continue; continue;
// Split the line into key/value pairs and trim any whitespace // Split the line into key/value pairs and trim any whitespace
key = trim(line.substr(0, equalPos)); key = BSPF::trim(line.substr(0, equalPos));
value = trim(line.substr(equalPos + 1, line.length() - key.length() - 1)); value = BSPF::trim(line.substr(equalPos + 1, line.length() - key.length() - 1));
// Skip absent key // Skip absent key
if(key.length() == 0) if(key.length() == 0)
@ -100,8 +91,8 @@ void KeyValueRepositoryConfigfile::save(const std::map<string, Variant>& values)
<< ";" << endl; << ";" << endl;
// Write out each of the key and value pairs // Write out each of the key and value pairs
for(const auto& pair: values) for(const auto& [key, value]: values)
out << pair.first << " = " << pair.second << endl; out << key << " = " << value << endl;
try try
{ {

View File

@ -22,10 +22,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValueRepositorySqlite::KeyValueRepositorySqlite( KeyValueRepositorySqlite::KeyValueRepositorySqlite(
SqliteDatabase& db, SqliteDatabase& db, const string& tableName)
const string& tableName : myTableName{tableName},
) : myTableName(tableName), myDb{db}
myDb(db)
{ {
} }

View File

@ -20,12 +20,11 @@
#include "SqliteError.hxx" #include "SqliteError.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsDb::SettingsDb( SettingsDb::SettingsDb(const string& databaseDirectory, const string& databaseName)
const string& databaseDirectory, : myDatabaseDirectory{databaseDirectory},
const string& databaseName myDatabaseName{databaseName}
) : myDatabaseDirectory(databaseDirectory), {
myDatabaseName(databaseName) }
{}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SettingsDb::initialize() bool SettingsDb::initialize()

View File

@ -30,7 +30,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteDatabase::SqliteDatabase(const string& databaseDirectory, SqliteDatabase::SqliteDatabase(const string& databaseDirectory,
const string& databaseName) const string& databaseName)
: myDatabaseFile(databaseDirectory + SEPARATOR + databaseName + ".sqlite3") : myDatabaseFile{databaseDirectory + SEPARATOR + databaseName + ".sqlite3"}
{ {
} }

View File

@ -19,7 +19,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteStatement::SqliteStatement(sqlite3* handle, string sql) SqliteStatement::SqliteStatement(sqlite3* handle, string sql)
: myHandle(handle) : myHandle{handle}
{ {
if (sqlite3_prepare_v2(handle, sql.c_str(), -1, &myStmt, nullptr) != SQLITE_OK) if (sqlite3_prepare_v2(handle, sql.c_str(), -1, &myStmt, nullptr) != SQLITE_OK)
throw SqliteError(handle); throw SqliteError(handle);

View File

@ -20,7 +20,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SqliteTransaction::SqliteTransaction(SqliteDatabase& db) SqliteTransaction::SqliteTransaction(SqliteDatabase& db)
: myDb(db) : myDb{db}
{ {
myDb.exec("BEGIN TRANSACTION"); myDb.exec("BEGIN TRANSACTION");
} }

View File

@ -21,8 +21,8 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BilinearBlitter::BilinearBlitter(FBBackendSDL2& fb, bool interpolate) BilinearBlitter::BilinearBlitter(FBBackendSDL2& fb, bool interpolate)
: myFB(fb), : myFB{fb},
myInterpolate(interpolate) myInterpolate{interpolate}
{ {
} }

View File

@ -21,7 +21,8 @@
#include "BilinearBlitter.hxx" #include "BilinearBlitter.hxx"
#include "QisBlitter.hxx" #include "QisBlitter.hxx"
unique_ptr<Blitter> BlitterFactory::createBlitter(FBBackendSDL2& fb, ScalingAlgorithm scaling) unique_ptr<Blitter>
BlitterFactory::createBlitter(FBBackendSDL2& fb, ScalingAlgorithm scaling)
{ {
if (!fb.isInitialized()) { if (!fb.isInitialized()) {
throw runtime_error("BlitterFactory requires an initialized framebuffer!"); throw runtime_error("BlitterFactory requires an initialized framebuffer!");

View File

@ -21,7 +21,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QisBlitter::QisBlitter(FBBackendSDL2& fb) QisBlitter::QisBlitter(FBBackendSDL2& fb)
: myFB(fb) : myFB{fb}
{ {
} }

374
src/common/stella.pro Normal file
View File

@ -0,0 +1,374 @@
"Cart.MD5" "05215b73ec33b502449ee726ac6b201f"
"Cart.Name" "draconian_20171013_RC6"
"Display.Phosphor" "YES"
"Cart.Variations" "4"
"Cart.Formats" "8,0,B,0,B,1,SECT.,D,1,VARIATIONS_ARE_DIFFICULTY_LEVEL"
"Cart.Addresses" "177B,177A,1779,1778,811,1780"
""
"Cart.MD5" "081e2c114c9c20b61acf25fc95c71bf4"
"Cart.Manufacturer" "Parker Brothers, Ed English, David Lamkins"
"Cart.ModelNo" "PB5300"
"Cart.Name" "Frogger (1982) (Parker Bros)"
"Display.Phosphor" "YES"
"Cart.Variations" "6"
"Cart.Addresses" "CC,CE,DD,E6"
""
"Cart.MD5" "136f75c4dd02c29283752b7e5799f978"
"Cart.Manufacturer" "Atari, Dan Hitchens - Sears"
"Cart.ModelNo" "CX2650 - 49-75168"
"Cart.Name" "Berzerk (1982) (Atari)"
"Cart.Rarity" "Common"
"Cart.Variations" "12"
"Cart.Formats" "6"
"Cart.Addresses" "DD,DE,DF,80"
""
"Cart.MD5" "137373599e9b7bf2cf162a102eb5927f"
"Cart.Manufacturer" "AtariAge, Joe Grand"
"Cart.Name" "Ultra SCSIcide (SCSIcide 2.0)"
"Controller.Left" "PADDLES"
"Cart.Formats" "6,0,H"
"Cart.Addresses" "DC,DD,DE"
""
"Cart.MD5" "211774f4c5739042618be8ff67351177"
"Cart.Manufacturer" "Atari - GCC, Mark Ackerman, Tom Calderwood, Glenn Parker"
"Cart.ModelNo" "CX2684"
"Cart.Name" "Galaxian (1983) (Atari)"
"Display.Phosphor" "YES"
"Cart.Variations" "9"
"Cart.Formats" "6,0,B,0,B,0,WAVE"
"Cart.Addresses" "AC,AD,AE,B3,AF"
""
"Cart.MD5" "240bfbac5163af4df5ae713985386f92"
"Cart.Manufacturer" "Activision, Steve Cartwright"
"Cart.ModelNo" "AX-022"
"Cart.Name" "Seaquest (1983) (Activision)"
"Cart.Formats" "6,0,B,0,B,0,_,B,0,HIGH_SCORE_IS_FROM_CURRENT_PLAYER"
"Cart.Addresses" "B8,B9,BA"
""
"Cart.MD5" "278f14887d601b5e5b620f1870bc09f6"
"Cart.Manufacturer" "Thomas Jentzsch"
"Cart.Name" "SWOOPS! (v0.96) (TJ)"
"Cart.Note" "Uses the Joystick (L) and Paddle (R) Controllers"
"Cart.Rarity" "Homebrew"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,1"
"Cart.Addresses" "FD,FE,FF,FC"
""
"Cart.MD5" "2903896d88a341511586d69fcfc20f7d"
"Cart.Manufacturer" "Activision, David Crane"
"Cart.ModelNo" "AX-014, AX-014-04"
"Cart.Name" "Grand Prix (1982) (Activision)"
"Cart.Variations" "4"
"Cart.Formats" "5,0,B,1,B,1,_,B,0,TIME/SCORE_SHOWS_EXTRA_DIGIT"
"Cart.Addresses" "EB,EC,ED,80"
""
"Cart.MD5" "2a0ba55e56e7a596146fa729acf0e109"
"Cart.Manufacturer" "Activision, Bob Whitehead"
"Cart.ModelNo" "AG-019"
"Cart.Name" "Sky Jinks (1982) (Activision)"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,1,B,1"
"Cart.Addresses" "9E,A6,A2,99"
""
"Cart.MD5" "2bb9f4686f7e08c5fcc69ec1a1c66fe7"
"Cart.Manufacturer" "Atari - GCC, John Allred, Mike Feinstein"
"Cart.ModelNo" "CX2688"
"Cart.Name" "Jungle Hunt (1983) (Atari)"
"Cart.Variations" "2"
"Cart.Formats" "6,0,B,0,B,1"
"Cart.Addresses" "85,84,83,8B"
""
"Cart.MD5" "318a9d6dda791268df92d72679914ac3"
"Cart.Manufacturer" "Activision, Steve Cartwright"
"Cart.ModelNo" "AX-017, AX-017-04"
"Cart.Name" "MegaMania (1982) (Activision)"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,1"
"Cart.Addresses" "DB,DC,DD,80"
""
"Cart.MD5" "36b20c427975760cb9cf4a47e41369e4"
"Cart.Manufacturer" "Coleco - Woodside Design Associates - Imaginative Systems Software, Garry Kitchen"
"Cart.ModelNo" "2451"
"Cart.Name" "Donkey Kong (1982) (Coleco)"
"Cart.Formats" "6,2"
"Cart.Addresses" "87,88"
""
"Cart.MD5" "3a2e2d0c6892aa14544083dfb7762782"
"Cart.Manufacturer" "Atari, Rob Fulop - Sears"
"Cart.ModelNo" "CX2638 - 49-75166"
"Cart.Name" "Missile Command (1981) (Atari)"
"Display.Phosphor" "YES"
"Cart.Variations" "34"
"Cart.Formats" "6"
"Cart.Addresses" "F3,F1,EF,E9"
""
"Cart.MD5" "3e90cf23106f2e08b2781e41299de556"
"Cart.Manufacturer" "Activision, David Crane"
"Cart.ModelNo" "AX-018, AX-018-04"
"Cart.Name" "Pitfall! (1982) (Activision)"
"Cart.Note" "Pitfall Harry's Jungle Adventure (Jungle Runner)"
"Cart.Formats" "6"
"Cart.Addresses" "D5,D6,D7"
""
"Cart.MD5" "4ca73eb959299471788f0b685c3ba0b5"
"Cart.Manufacturer" "Activision, Steve Cartwright"
"Cart.ModelNo" "AX-031"
"Cart.Name" "Frostbite (1983) (Activision)"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,1,LEVEL,B,1"
"Cart.Addresses" "C8,C9,CA,80,CB"
""
"Cart.MD5" "515046e3061b7b18aa3a551c3ae12673"
"Cart.Manufacturer" "Atari - GCC, Mark Ackerman, Noellie Alito"
"Cart.ModelNo" "CX2692"
"Cart.Name" "Moon Patrol (1983) (Atari)"
"Cart.Variations" "6"
"Cart.Formats" "6,0,B,0,B,1"
"Cart.Addresses" "EA,EB,EC,F9"
""
"Cart.MD5" "541cac55ebcf7891d9d51c415922303f"
"Cart.Name" "SF2_20131217_RC8_NTSC"
"Display.Phosphor" "YES"
"Cart.Formats" "8,0,B,0,B,0,LEVEL,D,1"
"Cart.Addresses" "1CF7,1CF6,1CF5,1CF4,0,18AC"
""
"Cart.MD5" "6dda84fb8e442ecf34241ac0d1d91d69"
"Cart.Manufacturer" "Atari - GCC, Douglas B. Macrae"
"Cart.ModelNo" "CX2677"
"Cart.Name" "Dig Dug (1983) (Atari)"
"Cart.Variations" "2"
"Cart.Formats" "4,0,B,0,B,1,_,B,0,VARIATION_1_IS_EASY;_2_IS_NORMAL"
"Cart.Addresses" "F0FE,F0FD,80"
""
"Cart.MD5" "72ffbef6504b75e69ee1045af9075f66"
"Cart.Manufacturer" "Atari, Richard Maurer - Sears"
"Cart.ModelNo" "CX2632 - 49-75153"
"Cart.Name" "Space Invaders (1980) (Atari)"
"Cart.Variations" "112"
"Cart.Formats" "4,0,B,0,D,1,_,B,0,ONLY_PLAYER_1_SUPPORTED"
"Cart.Addresses" "E6,E8,DC"
""
"Cart.MD5" "77057d9d14b99e465ea9e29783af0ae3"
"Cart.Manufacturer" "Activision, David Crane"
"Cart.ModelNo" "AG-001"
"Cart.Name" "Dragster (1980) (Activision)"
"Cart.Note" "AKA Drag Strip"
"Cart.Formats" "4,0,B,1,B,1"
"Cart.Addresses" "B3,B5,80,0"
""
"Cart.MD5" "7e52a95074a66640fcfde124fffd491a"
"Cart.Manufacturer" "Atari - GCC, Mike Feinstein, John Mracek"
"Cart.ModelNo" "CX2673"
"Cart.Name" "Phoenix (1983) (Atari)"
"Cart.Formats" "6"
"Cart.Addresses" "C9,C8,C7"
""
"Cart.MD5" "87e79cd41ce136fd4f72cc6e2c161bee"
"Cart.Manufacturer" "Atari - GCC, Mark Ackerman, Glenn Parker"
"Cart.ModelNo" "CX2675"
"Cart.Name" "Ms. Pac-Man (1983) (Atari)"
"Display.Phosphor" "YES"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,1,_,D,1,VARS_VALID_AT_GAME_OVER;_CHERRIES_=_VAR_#4"
"Cart.Addresses" "FA,F9,F8,F7,0"
""
"Cart.MD5" "91c2098e88a6b13f977af8c003e0bca5"
"Cart.Manufacturer" "Atari - GCC"
"Cart.ModelNo" "CX2676"
"Cart.Name" "Centipede (1983) (Atari)"
"Cart.Formats" "6,0,B,0,B,0,_,B,0,VARIATIONS_CANNOT_BE_DEFINED"
"Cart.Addresses" "F4,F5,F6"
""
"Cart.MD5" "91f0a708eeb93c133e9672ad2c8e0429"
"Cart.Name" "Oystron (V2.9) (Piero Cavina) (PD)"
"Cart.Rarity" "New Release"
"Cart.Variations" "3"
"Cart.Formats" "5,1,B,0,B,1"
"Cart.Addresses" "D4,D3,E5"
""
"Cart.MD5" "94b92a882f6dbaa6993a46e2dcc58402"
"Cart.Manufacturer" "Activision, Larry Miller"
"Cart.ModelNo" "AX-026, AX-026-04"
"Cart.Name" "Enduro (1983) (Activision)"
"Cart.Formats" "6,0,B,0,B,0,DAY"
"Cart.Addresses" "AA,A9,A8,0,AD"
""
"Cart.MD5" "9ad36e699ef6f45d9eb6c4cf90475c9f"
"Cart.Manufacturer" "Imagic, Dennis Koble"
"Cart.ModelNo" "720103-1A, 720103-1B, IA3203, IX-010-04"
"Cart.Name" "Atlantis (1982) (Imagic)"
"Cart.Note" "AKA Lost City of Atlantis"
"Cart.Rarity" "Uncommon"
"Cart.Variations" "4"
"Cart.Formats" "6,2,B,0,B,1"
"Cart.Addresses" "A3,A2,8D"
""
"Cart.MD5" "ab5bf1ef5e463ad1cbb11b6a33797228"
"Cart.Manufacturer" "Imagic, Rob Fulop"
"Cart.ModelNo" "720104-1A, 720104-1B, IA3204"
"Cart.Name" "Cosmic Ark (1982) (Imagic)"
"Cart.Variations" "6"
"Cart.Formats" "6"
"Cart.Addresses" "AE,B0,B2,BC"
""
"Cart.MD5" "ac7c2260378975614192ca2bc3d20e0b"
"Cart.Manufacturer" "Activision, David Crane"
"Cart.ModelNo" "AG-930-04, AZ-030"
"Cart.Name" "Decathlon (1983) (Activision)"
"Cart.Rarity" "Rare"
"Cart.Variations" "10"
"Cart.Formats" "4,0,B,0,D,1,_,B,0,DECATHLON_&_100M_DASH_SHARE_VARIATION_1"
"Cart.Addresses" "95,96,80,0"
""
"Cart.MD5" "be929419902e21bd7830a7a7d746195d"
"Cart.Manufacturer" "Activision, Garry Kitchen"
"Cart.ModelNo" "AX-025, AX-025-04"
"Cart.Name" "Keystone Kapers (1983) (Activision)"
"Cart.Formats" "6"
"Cart.Addresses" "9A,9B,9C"
""
"Cart.MD5" "c1cb228470a87beb5f36e90ac745da26"
"Cart.Manufacturer" "Activision, Bob Whitehead"
"Cart.ModelNo" "AX-015, AX-015-04"
"Cart.Name" "Chopper Command (1982) (Activision)"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,1"
"Cart.Addresses" "EC,EE,F0,E0"
""
"Cart.MD5" "c5930d0e8cdae3e037349bfa08e871be"
"Cart.Manufacturer" "Atari, Howard Scott Warshaw - Sears"
"Cart.ModelNo" "CX2655 - 49-75167"
"Cart.Name" "Yars' Revenge (1982) (Atari)"
"Display.Phosphor" "YES"
"Cart.Variations" "8"
"Cart.Formats" "6,0,B,0,B,1,_,B,0,VARIATIONS_ARE_LARGER_BY_1"
"Cart.Addresses" "E0,E1,E2,80"
""
"Cart.MD5" "c6556e082aac04260596b4045bc122de"
"Cart.Manufacturer" "Atari - GCC, Dave Payne"
"Cart.ModelNo" "CX2669"
"Cart.Name" "Vanguard (1983) (Atari)"
"Cart.Formats" "6,0,B,0,B,0,_,B,0,SCORE_IS_FOR_CURRENT_PLAYER"
"Cart.Addresses" "99,98,97"
""
"Cart.MD5" "ccbd36746ed4525821a8083b0d6d2c2c"
"Cart.Manufacturer" "Atari, Brad Stewart - Sears"
"Cart.ModelNo" "CX2649, 49-75163"
"Cart.Name" "Asteroids (1981) (Atari) [no copyright]"
"Cart.Rarity" "Common"
"Display.Phosphor" "YES"
"Cart.Variations" "66"
"Cart.Formats" "5,1,H,1,D,0,_,B,0,VARIATIONS_>_32_DIFFER_BY_1"
"Cart.Addresses" "BD,BE,80"
""
"Cart.MD5" "d69559f9c9dc6ef528d841bf9d91b275"
"Cart.Manufacturer" "Activision, Alan Miller"
"Cart.ModelNo" "AX-016"
"Cart.Name" "StarMaster (1982) (Activision)"
"Cart.Note" "Use Color/BW switch to change between galactic chart and front views"
"Cart.Variations" "4"
"Cart.Formats" "4,0,B,0,B,1,_,B,0,SCORE_ONLY_CALCULATED_WHEN_GAME_IS_OVER"
"Cart.Addresses" "C1,C2,80"
""
"Cart.MD5" "dd7884b4f93cab423ac471aa1935e3df"
"Cart.Manufacturer" "Atari, Brad Stewart - Sears"
"Cart.ModelNo" "CX2649, 49-75163"
"Cart.Name" "Asteroids (1981) (Atari)"
"Cart.Variations" "66"
"Cart.Formats" "5,1,H,1,D,0,_,B,0,VARIATIONS_>_32_DIFFER_BY_1"
"Cart.Addresses" "BD,BE,80"
""
"Cart.MD5" "dde55d9868911407fe8b3fefef396f00"
"Cart.Name" "Seawolf (2004) (Xype, Manuel Rotschkar)"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,0,B,0,_,B,0,USER_DEFINED_VARIATION_BASED_ON_SWITCHES"
"Cart.Addresses" "90,91,92,0,0"
""
"Cart.MD5" "f0e0addc07971561ab80d9abe1b8d333"
"Cart.Manufacturer" "Imagic, Rob Fulop"
"Cart.ModelNo" "720000-200, 720101-1B, 720101-1C, IA3200, IA3200C, IX-006-04"
"Cart.Name" "Demon Attack (1982) (Imagic)"
"Cart.Note" "AKA Death from Above"
"Cart.Variations" "10"
"Cart.Formats" "6,0,B,0,B,0,WAVE,B,1"
"Cart.Addresses" "81,83,85,EA,80"
""
"Cart.MD5" "f1489e27a4539a0c6c8529262f9f7e18"
"Cart.Manufacturer" "Champ Games"
"Cart.ModelNo" "CG-01-P"
"Cart.Name" "Lady Bug (PAL60)"
"Cart.Rarity" "Homebrew"
"Console.RightDiff" "A"
"Display.Format" "PAL60"
"Display.Phosphor" "YES"
"Cart.Variations" "3"
"Cart.Formats" "6,0,B,0,B,1,PART,D,1"
"Cart.Addresses" "8E,8D,8C,8A,89"
""
"Cart.MD5" "f34f08e5eb96e500e851a80be3277a56"
"Cart.Manufacturer" "Atari, Brad Stewart - Sears"
"Cart.ModelNo" "CX2622 - 6-99813, 49-75107"
"Cart.Name" "Breakout (1978) (Atari)"
"Cart.Note" "Uses the Paddle Controllers"
"Controller.MouseAxis" "01 60"
"Cart.Formats" "3,0,B,0,B,0,_,B,0,VARIATIONS_CANNOT_BE_DEFINED"
"Cart.Addresses" "CE,CD"
""
"Cart.MD5" "f8240e62d8c0a64a61e19388414e3104"
"Cart.Manufacturer" "Activision, Steve Cartwright"
"Cart.ModelNo" "AX-013"
"Cart.Name" "Barnstorming (1982) (Activision)"
"Cart.Rarity" "Uncommon"
"Cart.Variations" "4"
"Cart.Formats" "6,0,B,1,B,1"
"Cart.Addresses" "B5,B6,B7,80"
""
"Cart.MD5" "fca4a5be1251927027f2c24774a02160"
"Cart.Manufacturer" "Activision, John Van Ryzin"
"Cart.ModelNo" "AZ-036-04"
"Cart.Name" "H.E.R.O. (1984) (Activision)"
"Cart.Variations" "5"
"Cart.Formats" "6,0,B,0,B,1,LEVEL,D,1"
"Cart.Addresses" "B7,B8,B9,80,F5"
""

View File

@ -232,7 +232,7 @@ class AtariNTSC
} }
// Common ntsc macros // Common ntsc macros
static inline constexpr void ATARI_NTSC_CLAMP( uInt32& io, uInt32 shift ) { static constexpr void ATARI_NTSC_CLAMP( uInt32& io, uInt32 shift ) {
uInt32 sub = io >> (9-(shift)) & atari_ntsc_clamp_mask; uInt32 sub = io >> (9-(shift)) & atari_ntsc_clamp_mask;
uInt32 clamp = atari_ntsc_clamp_add - sub; uInt32 clamp = atari_ntsc_clamp_add - sub;
io |= clamp; io |= clamp;
@ -240,31 +240,31 @@ class AtariNTSC
io &= clamp; io &= clamp;
} }
static inline constexpr void RGB_TO_YIQ(float r, float g, float b, static constexpr void RGB_TO_YIQ(float r, float g, float b,
float& y, float& i, float& q) { float& y, float& i, float& q) {
y = r * 0.299F + g * 0.587F + b * 0.114F; y = r * 0.299F + g * 0.587F + b * 0.114F;
i = r * 0.595716F - g * 0.274453F - b * 0.321263F; i = r * 0.595716F - g * 0.274453F - b * 0.321263F;
q = r * 0.211456F - g * 0.522591F + b * 0.311135F; q = r * 0.211456F - g * 0.522591F + b * 0.311135F;
} }
static inline constexpr void YIQ_TO_RGB(float y, float i, float q, static constexpr void YIQ_TO_RGB(float y, float i, float q,
const float* to_rgb, int& ir, int& ig, int& ib) { const float* to_rgb, int& ir, int& ig, int& ib) {
ir = static_cast<int>(y + to_rgb[0] * i + to_rgb[1] * q); ir = static_cast<int>(y + to_rgb[0] * i + to_rgb[1] * q);
ig = static_cast<int>(y + to_rgb[2] * i + to_rgb[3] * q); ig = static_cast<int>(y + to_rgb[2] * i + to_rgb[3] * q);
ib = static_cast<int>(y + to_rgb[4] * i + to_rgb[5] * q); ib = static_cast<int>(y + to_rgb[4] * i + to_rgb[5] * q);
} }
static inline constexpr uInt32 PACK_RGB( int r, int g, int b ) { static constexpr uInt32 PACK_RGB( int r, int g, int b ) {
return r << 21 | g << 11 | b << 1; return r << 21 | g << 11 | b << 1;
} }
// Converted from C-style macros; I don't even pretend to understand the logic here :) // Converted from C-style macros; I don't even pretend to understand the logic here :)
static inline constexpr int PIXEL_OFFSET1( int ntsc, int scaled ) { static constexpr int PIXEL_OFFSET1( int ntsc, int scaled ) {
return (kernel_size / 2 + ((ntsc) - (scaled) / rescale_out * rescale_in) + return (kernel_size / 2 + ((ntsc) - (scaled) / rescale_out * rescale_in) +
((((scaled) + rescale_out * 10) % rescale_out) != 0) + ((((scaled) + rescale_out * 10) % rescale_out) != 0) +
(rescale_out - (((scaled) + rescale_out * 10) % rescale_out)) % rescale_out + (rescale_out - (((scaled) + rescale_out * 10) % rescale_out)) % rescale_out +
(kernel_size * 2 * (((scaled) + rescale_out * 10) % rescale_out))); (kernel_size * 2 * (((scaled) + rescale_out * 10) % rescale_out)));
} }
static inline constexpr int PIXEL_OFFSET2( int ntsc ) { static constexpr int PIXEL_OFFSET2( int ntsc ) {
return 1.0F - (((ntsc) + 100) & 2); return 1.0F - (((ntsc) + 100) & 2);
} }

View File

@ -192,7 +192,7 @@ void NTSCFilter::getAdjustables(Adjustable& adjustable, Preset preset) const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void NTSCFilter::setCustomAdjustables(Adjustable& adjustable) void NTSCFilter::setCustomAdjustables(const Adjustable& adjustable)
{ {
#ifdef BLARGG_PALETTE #ifdef BLARGG_PALETTE
//myCustomSetup.hue = scaleFrom100(adjustable.hue); //myCustomSetup.hue = scaleFrom100(adjustable.hue);

View File

@ -90,7 +90,7 @@ class NTSCFilter
// Set custom adjustables to given values // Set custom adjustables to given values
// Values will be scaled to 0 - 100 range, independent of how // Values will be scaled to 0 - 100 range, independent of how
// they're actually stored internally // they're actually stored internally
void setCustomAdjustables(Adjustable& adjustable); void setCustomAdjustables(const Adjustable& adjustable);
// The following methods cycle through each custom adjustable // The following methods cycle through each custom adjustable
// They are used in conjunction with the increase/decrease // They are used in conjunction with the increase/decrease

View File

@ -30,12 +30,12 @@
class BreakpointMap class BreakpointMap
{ {
private: private:
static const uInt16 ADDRESS_MASK = 0x1fff; // either 0x1fff or 0xffff (not needed then) static constexpr uInt16 ADDRESS_MASK = 0x1fff; // either 0x1fff or 0xffff (not needed then)
public: public:
// breakpoint flags // breakpoint flags
static const uInt32 ONE_SHOT = 1 << 0; // used for 'trace' command static constexpr uInt32 ONE_SHOT = 1 << 0; // used for 'trace' command
static const uInt8 ANY_BANK = 255; // breakpoint valid in any bank static constexpr uInt8 ANY_BANK = 255; // breakpoint valid in any bank
struct Breakpoint struct Breakpoint
{ {

View File

@ -48,7 +48,7 @@ using std::right;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem) CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
: DebuggerSystem(dbg, console), : DebuggerSystem(dbg, console),
myOSystem(osystem) myOSystem{osystem}
{ {
// Add case sensitive compare for user labels // Add case sensitive compare for user labels
// TODO - should user labels be case insensitive too? // TODO - should user labels be case insensitive too?
@ -1041,7 +1041,7 @@ string CartDebug::saveConfigFile()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartDebug::saveDisassembly() string CartDebug::saveDisassembly(string path)
{ {
string NTSC_COLOR[16] = { string NTSC_COLOR[16] = {
"BLACK", "YELLOW", "BROWN", "ORANGE", "BLACK", "YELLOW", "BROWN", "ORANGE",
@ -1350,9 +1350,16 @@ string CartDebug::saveDisassembly()
// And finally, output the disassembly // And finally, output the disassembly
out << buf.str(); out << buf.str();
const string& propsname =
myConsole.properties().get(PropType::Cart_Name) + ".asm"; if(path.empty())
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + propsname); path = myOSystem.defaultSaveDir().getPath()
+ myConsole.properties().get(PropType::Cart_Name) + ".asm";
else
// Append default extension when missing
if(path.find_last_of('.') == string::npos)
path += ".asm";
FilesystemNode node(path);
stringstream retVal; stringstream retVal;
try try
{ {
@ -1370,11 +1377,18 @@ string CartDebug::saveDisassembly()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartDebug::saveRom() string CartDebug::saveRom(string path)
{ {
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26"; if(path.empty())
path = myOSystem.defaultSaveDir().getPath()
+ myConsole.properties().get(PropType::Cart_Name) + ".a26";
else
// Append default extension when missing
if(path.find_last_of('.') == string::npos)
path += ".a26";
FilesystemNode node(path);
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
if(myConsole.cartridge().saveROM(node)) if(myConsole.cartridge().saveROM(node))
return "saved ROM as " + node.getShortPath(); return "saved ROM as " + node.getShortPath();
else else
@ -1382,7 +1396,7 @@ string CartDebug::saveRom()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartDebug::saveAccessFile() string CartDebug::saveAccessFile(string path)
{ {
stringstream out; stringstream out;
out << myConsole.tia().getAccessCounters(); out << myConsole.tia().getAccessCounters();
@ -1391,8 +1405,15 @@ string CartDebug::saveAccessFile()
try try
{ {
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".csv"; if(path.empty())
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom); path = myOSystem.defaultSaveDir().getPath()
+ myConsole.properties().get(PropType::Cart_Name) + ".csv";
else
// Append default extension when missing
if(path.find_last_of('.') == string::npos)
path += ".csv";
FilesystemNode node(path);
node.write(out); node.write(out);
return "saved access counters as " + node.getShortPath(); return "saved access counters as " + node.getShortPath();
@ -1516,7 +1537,7 @@ CartDebug::AddrType CartDebug::addressType(uInt16 addr) const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const void CartDebug::getBankDirectives(ostream& buf, const BankInfo& info) const
{ {
// Start with the offset for this bank // Start with the offset for this bank
buf << "ORG " << Base::HEX4 << info.offset << endl; buf << "ORG " << Base::HEX4 << info.offset << endl;

View File

@ -236,13 +236,13 @@ class CartDebug : public DebuggerSystem
/** /**
Save disassembly and ROM file Save disassembly and ROM file
*/ */
string saveDisassembly(); string saveDisassembly(string path);
string saveRom(); string saveRom(string path);
/** /**
Save access counters file Save access counters file
*/ */
string saveAccessFile(); string saveAccessFile(string path);
/** /**
Show Distella directives (both set by the user and determined by Distella) Show Distella directives (both set by the user and determined by Distella)
@ -324,7 +324,7 @@ class CartDebug : public DebuggerSystem
// Analyze of bank of ROM, generating a list of Distella directives // Analyze of bank of ROM, generating a list of Distella directives
// based on its disassembly // based on its disassembly
void getBankDirectives(ostream& buf, BankInfo& info) const; void getBankDirectives(ostream& buf, const BankInfo& info) const;
// Get access enum type from 'flags', taking precendence into account // Get access enum type from 'flags', taking precendence into account
Device::AccessType accessTypeAbsolute(Device::AccessFlags flags) const; Device::AccessType accessTypeAbsolute(Device::AccessFlags flags) const;

View File

@ -27,7 +27,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CpuDebug::CpuDebug(Debugger& dbg, Console& console) CpuDebug::CpuDebug(Debugger& dbg, Console& console)
: DebuggerSystem(dbg, console), : DebuggerSystem(dbg, console),
my6502(mySystem.m6502()) my6502{mySystem.m6502()}
{ {
} }

View File

@ -62,8 +62,8 @@ Debugger* Debugger::myStaticDebugger = nullptr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::Debugger(OSystem& osystem, Console& console) Debugger::Debugger(OSystem& osystem, Console& console)
: DialogContainer(osystem), : DialogContainer(osystem),
myConsole(console), myConsole{console},
mySystem(console.system()) mySystem{console.system()}
{ {
// Init parser // Init parser
myParser = make_unique<DebuggerParser>(*this, osystem.settings()); myParser = make_unique<DebuggerParser>(*this, osystem.settings());
@ -716,10 +716,8 @@ bool Debugger::addFunction(const string& name, const string& definition,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::isBuiltinFunction(const string& name) bool Debugger::isBuiltinFunction(const string& name)
{ {
for(const auto& func: ourBuiltinFunctions) return std::any_of(ourBuiltinFunctions.cbegin(), ourBuiltinFunctions.cend(),
if(name == func.name) [&](const auto& func) { return name == func.name; });
return true;
return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -76,9 +76,6 @@ class Debugger : public DialogContainer
Debugger(OSystem& osystem, Console& console); Debugger(OSystem& osystem, Console& console);
~Debugger() override; ~Debugger() override;
private:
static const Int8 ANY_BANK = -1;
public: public:
/** /**
Initialize the debugger dialog container. Initialize the debugger dialog container.
@ -284,8 +281,6 @@ class Debugger : public DialogContainer
*/ */
Dialog* baseDialog() override { return myDialog; } Dialog* baseDialog() override { return myDialog; }
static const Int32 NOT_FOUND = -1;
private: private:
/** /**
Save state of each debugger subsystem and, by default, mark all Save state of each debugger subsystem and, by default, mark all
@ -367,6 +362,8 @@ class Debugger : public DialogContainer
static std::array<BuiltinFunction, 18> ourBuiltinFunctions; static std::array<BuiltinFunction, 18> ourBuiltinFunctions;
static std::array<PseudoRegister, 16> ourPseudoRegisters; static std::array<PseudoRegister, 16> ourPseudoRegisters;
static constexpr Int8 ANY_BANK = -1;
private: private:
// rewind/unwind n states // rewind/unwind n states
uInt16 windStates(uInt16 numStates, bool unwind, string& message); uInt16 windStates(uInt16 numStates, bool unwind, string& message);

View File

@ -91,7 +91,7 @@ class ByteDerefOffsetExpression : public Expression
class ConstExpression : public Expression class ConstExpression : public Expression
{ {
public: public:
ConstExpression(const int value) : Expression(), myValue(value) { } ConstExpression(const int value) : Expression(), myValue{value} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return myValue; } { return myValue; }
@ -103,7 +103,7 @@ class ConstExpression : public Expression
class CpuMethodExpression : public Expression class CpuMethodExpression : public Expression
{ {
public: public:
CpuMethodExpression(CpuMethod method) : Expression(), myMethod(std::mem_fn(method)) { } CpuMethodExpression(CpuMethod method) : Expression(), myMethod{std::mem_fn(method)} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return myMethod(Debugger::debugger().cpuDebug()); } { return myMethod(Debugger::debugger().cpuDebug()); }
@ -134,7 +134,7 @@ class EqualsExpression : public Expression
class EquateExpression : public Expression class EquateExpression : public Expression
{ {
public: public:
EquateExpression(const string& label) : Expression(), myLabel(label) { } EquateExpression(const string& label) : Expression(), myLabel{label} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return Debugger::debugger().cartDebug().getAddress(myLabel); } { return Debugger::debugger().cartDebug().getAddress(myLabel); }
@ -146,7 +146,7 @@ class EquateExpression : public Expression
class FunctionExpression : public Expression class FunctionExpression : public Expression
{ {
public: public:
FunctionExpression(const string& label) : Expression(), myLabel(label) { } FunctionExpression(const string& label) : Expression(), myLabel{label} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return Debugger::debugger().getFunction(myLabel).evaluate(); } { return Debugger::debugger().getFunction(myLabel).evaluate(); }
@ -285,7 +285,7 @@ class PlusExpression : public Expression
class CartMethodExpression : public Expression class CartMethodExpression : public Expression
{ {
public: public:
CartMethodExpression(CartMethod method) : Expression(), myMethod(std::mem_fn(method)) { } CartMethodExpression(CartMethod method) : Expression(), myMethod{std::mem_fn(method)} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return myMethod(Debugger::debugger().cartDebug()); } { return myMethod(Debugger::debugger().cartDebug()); }
@ -315,7 +315,7 @@ class ShiftRightExpression : public Expression
class RiotMethodExpression : public Expression class RiotMethodExpression : public Expression
{ {
public: public:
RiotMethodExpression(RiotMethod method) : Expression(), myMethod(std::mem_fn(method)) { } RiotMethodExpression(RiotMethod method) : Expression(), myMethod{std::mem_fn(method)} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return myMethod(Debugger::debugger().riotDebug()); } { return myMethod(Debugger::debugger().riotDebug()); }
@ -327,7 +327,7 @@ class RiotMethodExpression : public Expression
class TiaMethodExpression : public Expression class TiaMethodExpression : public Expression
{ {
public: public:
TiaMethodExpression(TiaMethod method) : Expression(), myMethod(std::mem_fn(method)) { } TiaMethodExpression(TiaMethod method) : Expression(), myMethod{std::mem_fn(method)} { }
Int32 evaluate() const override Int32 evaluate() const override
{ return myMethod(Debugger::debugger().tiaDebug()); } { return myMethod(Debugger::debugger().tiaDebug()); }

View File

@ -58,8 +58,8 @@ using std::right;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerParser::DebuggerParser(Debugger& d, Settings& s) DebuggerParser::DebuggerParser(Debugger& d, Settings& s)
: debugger(d), : debugger{d},
settings(s) settings{s}
{ {
} }
@ -565,6 +565,12 @@ string DebuggerParser::eval()
return buf.str(); return buf.str();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& DebuggerParser::cartName() const
{
return debugger.myOSystem.console().properties().get(PropType::Cart_Name);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerParser::listTraps(bool listCond) void DebuggerParser::listTraps(bool listCond)
{ {
@ -630,15 +636,11 @@ string DebuggerParser::trapStatus(const Trap& trap)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DebuggerParser::saveScriptFile(string file) string DebuggerParser::saveScriptFile(string file)
{ {
// Append 'script' extension when necessary
if(file.find_last_of('.') == string::npos)
file += ".script";
stringstream out; stringstream out;
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap(); Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
for(const auto& f: funcs) for(const auto& [name, cmd]: funcs)
if (!debugger.isBuiltinFunction(f.first)) if (!debugger.isBuiltinFunction(name))
out << "function " << f.first << " {" << f.second << "}" << endl; out << "function " << name << " {" << cmd << "}" << endl;
for(const auto& w: myWatches) for(const auto& w: myWatches)
out << "watch " << w << endl; out << "watch " << w << endl;
@ -675,7 +677,16 @@ string DebuggerParser::saveScriptFile(string file)
out << endl; out << endl;
} }
FilesystemNode node(debugger.myOSystem.defaultSaveDir().getPath() + file); // Append 'script' extension when necessary
if(file.find_last_of('.') == string::npos)
file += ".script";
// Use default path if no path is provided
if(file.find_first_of(FilesystemNode::PATH_SEPARATOR) == string::npos)
file = debugger.myOSystem.defaultSaveDir().getPath() + file;
FilesystemNode node(file);
try try
{ {
node.write(out); node.write(out);
@ -1527,10 +1538,8 @@ void DebuggerParser::executeListfunctions()
const Debugger::FunctionDefMap& functions = debugger.getFunctionDefMap(); const Debugger::FunctionDefMap& functions = debugger.getFunctionDefMap();
if(functions.size() > 0) if(functions.size() > 0)
{ for(const auto& [name, cmd]: functions)
for(const auto& iter: functions) commandResult << name << " -> " << cmd << endl;
commandResult << iter.first << " -> " << iter.second << endl;
}
else else
commandResult << "no user-defined functions"; commandResult << "no user-defined functions";
} }
@ -1837,7 +1846,11 @@ void DebuggerParser::executeS()
void DebuggerParser::executeSave() void DebuggerParser::executeSave()
{ {
if(argCount && argStrings[0] == "?") if(argCount && argStrings[0] == "?")
debugger.myDialog->showBrowser(DebuggerDialog::svScript); {
debugger.myDialog->showBrowser(DebuggerDialog::svScript, cartName() + ".script");
// avoid printing a new prompt
commandResult << "_EXIT_DEBUGGER";
}
else else
commandResult << saveScriptFile(argStrings[0]); commandResult << saveScriptFile(argStrings[0]);
} }
@ -1847,9 +1860,13 @@ void DebuggerParser::executeSave()
void DebuggerParser::executeSaveAccess() void DebuggerParser::executeSaveAccess()
{ {
if(argCount && argStrings[0] == "?") if(argCount && argStrings[0] == "?")
debugger.myDialog->showBrowser(DebuggerDialog::svAccess); {
debugger.myDialog->showBrowser(DebuggerDialog::svAccess, cartName() + ".csv");
// avoid printing a new prompt
commandResult << "_EXIT_DEBUGGER";
}
else else
commandResult << debugger.cartDebug().saveAccessFile(); commandResult << debugger.cartDebug().saveAccessFile(argCount ? argStrings[0] : EmptyString);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1863,14 +1880,28 @@ void DebuggerParser::executeSaveconfig()
// "savedis" // "savedis"
void DebuggerParser::executeSavedisassembly() void DebuggerParser::executeSavedisassembly()
{ {
commandResult << debugger.cartDebug().saveDisassembly(); if(argCount && argStrings[0] == "?")
{
debugger.myDialog->showBrowser(DebuggerDialog::svDis, cartName() + ".asm");
// avoid printing a new prompt
commandResult << "_EXIT_DEBUGGER";
}
else
commandResult << debugger.cartDebug().saveDisassembly(argCount ? argStrings[0] : EmptyString);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "saverom" // "saverom"
void DebuggerParser::executeSaverom() void DebuggerParser::executeSaverom()
{ {
commandResult << debugger.cartDebug().saveRom(); if(argCount && argStrings[0] == "?")
{
debugger.myDialog->showBrowser(DebuggerDialog::svRom, cartName() + ".a26");
// avoid printing a new prompt
commandResult << "_EXIT_DEBUGGER";
}
else
commandResult << debugger.cartDebug().saveRom(argCount ? argStrings[0] : EmptyString);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1880,12 +1911,22 @@ void DebuggerParser::executeSaveses()
ostringstream filename; ostringstream filename;
auto timeinfo = BSPF::localTime(); auto timeinfo = BSPF::localTime();
filename << debugger.myOSystem.defaultSaveDir() filename << debugger.myOSystem.defaultSaveDir()
<< std::put_time(&timeinfo, "session_%F_%H-%M-%S.txt"); << std::put_time(&timeinfo, "session_%F_%H-%M-%S.txt");
FilesystemNode file(filename.str());
if(debugger.prompt().saveBuffer(file)) if(argCount && argStrings[0] == "?")
commandResult << "saved " + file.getShortPath() + " OK"; {
debugger.myDialog->showBrowser(DebuggerDialog::svSession, filename.str());
commandResult << "_EXIT_DEBUGGER";
}
else else
commandResult << "unable to save session"; {
FilesystemNode file(filename.str());
if(debugger.prompt().saveBuffer(file))
commandResult << "saved " + file.getShortPath() + " OK";
else
commandResult << "unable to save session";
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -3051,7 +3092,7 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
{ {
"save", "save",
"Save breaks, watches, traps and functions to file xx", "Save breaks, watches, traps and functions to file xx",
"Example: save commands.script", "Example: save commands.script, save ?",
true, true,
false, false,
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS }, { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
@ -3061,10 +3102,11 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
{ {
"saveaccess", "saveaccess",
"Save the access counters to CSV file", "Save the access counters to CSV file",
"Example: saveaccess (no parameters)", "Example: saveaccess, saveaccess ?\n"
"NOTE: saves to default save location without ? parameter",
false, false,
false, false,
{ Parameters::ARG_END_ARGS }, { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeSaveAccess) std::mem_fn(&DebuggerParser::executeSaveAccess)
}, },
@ -3080,34 +3122,34 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
{ {
"savedis", "savedis",
"Save Distella disassembly (with default name)", "Save Distella disassembly",
"Example: savedis\n" "Example: savedis, savedis ?\n"
"NOTE: saves to default save location", "NOTE: saves to default save location without ? parameter",
false, false,
false, false,
{ Parameters::ARG_END_ARGS }, { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeSavedisassembly) std::mem_fn(&DebuggerParser::executeSavedisassembly)
}, },
{ {
"saverom", "saverom",
"Save (possibly patched) ROM (with default name)", "Save (possibly patched) ROM",
"Example: saverom\n" "Example: saverom, saverom ?\n"
"NOTE: saves to default save location", "NOTE: saves to default save location without ? parameter",
false, false,
false, false,
{ Parameters::ARG_END_ARGS }, { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeSaverom) std::mem_fn(&DebuggerParser::executeSaverom)
}, },
{ {
"saveses", "saveses",
"Save console session (with default name)", "Save console session",
"Example: saveses\n" "Example: saveses, saveses ?\n"
"NOTE: saves to default save location", "NOTE: saves to default save location without ? parameter",
false, false,
false, false,
{ Parameters::ARG_END_ARGS }, { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeSaveses) std::mem_fn(&DebuggerParser::executeSaveses)
}, },

View File

@ -65,6 +65,7 @@ class DebuggerParser
bool validateArgs(int cmd); bool validateArgs(int cmd);
string eval(); string eval();
string saveScriptFile(string file); string saveScriptFile(string file);
const string& cartName() const;
private: private:
// Constants for argument processing // Constants for argument processing

View File

@ -27,12 +27,12 @@ DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
CartDebug::AddrTypeArray& labels, CartDebug::AddrTypeArray& labels,
CartDebug::AddrTypeArray& directives, CartDebug::AddrTypeArray& directives,
CartDebug::ReservedEquates& reserved) CartDebug::ReservedEquates& reserved)
: myDbg(dbg), : myDbg{dbg},
myList(list), myList{list},
mySettings(s), mySettings{s},
myReserved(reserved), myReserved{reserved},
myLabels(labels), myLabels{labels},
myDirectives(directives) myDirectives{directives}
{ {
bool resolve_code = mySettings.resolveCode; bool resolve_code = mySettings.resolveCode;
CartDebug::AddressList& debuggerAddresses = info.addressList; CartDebug::AddressList& debuggerAddresses = info.addressList;

View File

@ -32,7 +32,7 @@ class Expression
{ {
public: public:
Expression(Expression* lhs = nullptr, Expression* rhs = nullptr) Expression(Expression* lhs = nullptr, Expression* rhs = nullptr)
: myLHS(lhs), myRHS(rhs) { } : myLHS{lhs}, myRHS{rhs} { }
virtual ~Expression() = default; virtual ~Expression() = default;
virtual Int32 evaluate() const { return 0; } virtual Int32 evaluate() const { return 0; }

View File

@ -26,7 +26,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIADebug::TIADebug(Debugger& dbg, Console& console) TIADebug::TIADebug(Debugger& dbg, Console& console)
: DebuggerSystem(dbg, console), : DebuggerSystem(dbg, console),
myTIA(console.tia()) myTIA{console.tia()}
{ {
} }

View File

@ -25,7 +25,7 @@ Cartridge3EPlusWidget::Cartridge3EPlusWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge3EPlus& cart) int x, int y, int w, int h, Cartridge3EPlus& cart)
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart),
myCart3EP(cart) myCart3EP{cart}
{ {
initialize(); initialize();
} }

View File

@ -24,7 +24,7 @@ Cartridge4A50Widget::Cartridge4A50Widget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge4A50& cart) int x, int y, int w, int h, Cartridge4A50& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
string info = string info =
"4A50 cartridge - 128K ROM and 32K RAM, split in various bank configurations\n" "4A50 cartridge - 128K ROM and 32K RAM, split in various bank configurations\n"

View File

@ -27,7 +27,7 @@ CartridgeARWidget::CartridgeARWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeAR& cart) int x, int y, int w, int h, CartridgeAR& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
size_t size = myCart.mySize; size_t size = myCart.mySize;

View File

@ -25,7 +25,7 @@ CartridgeBUSWidget::CartridgeBUSWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeBUS& cart) int x, int y, int w, int h, CartridgeBUS& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
uInt16 size = 8 * 4096; uInt16 size = 8 * 4096;
@ -75,7 +75,6 @@ CartridgeBUSWidget::CartridgeBUSWidget(
ypos += myLineHeight + 4; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth, new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Datastream Pointers", TextAlign::Left); myFontHeight, "Datastream Pointers", TextAlign::Left);
xpos += lwidth;
myDatastreamPointers = new DataGridWidget(boss, _nfont, DS_X, ypos+myLineHeight-2, 4, 4, 6, 32, Common::Base::Fmt::_16_3_2); myDatastreamPointers = new DataGridWidget(boss, _nfont, DS_X, ypos+myLineHeight-2, 4, 4, 6, 32, Common::Base::Fmt::_16_3_2);
myDatastreamPointers->setTarget(this); myDatastreamPointers->setTarget(this);

View File

@ -24,7 +24,7 @@ CartridgeCDFWidget::CartridgeCDFWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeCDF& cart) int x, int y, int w, int h, CartridgeCDF& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
const int VBORDER = 8; const int VBORDER = 8;
const int HBORDER = 2; const int HBORDER = 2;

View File

@ -31,7 +31,7 @@ CartridgeCMWidget::CartridgeCMWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeCM& cart) int x, int y, int w, int h, CartridgeCM& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
uInt16 size = 4 * 4096; uInt16 size = 4 * 4096;

View File

@ -27,7 +27,7 @@ CartridgeCTYWidget::CartridgeCTYWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeCTY& cart) int x, int y, int w, int h, CartridgeCTY& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
uInt16 size = 8 * 4096; uInt16 size = 8 * 4096;

View File

@ -25,7 +25,7 @@ CartridgeDPCPlusWidget::CartridgeDPCPlusWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeDPCPlus& cart) int x, int y, int w, int h, CartridgeDPCPlus& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
size_t size = cart.mySize; size_t size = cart.mySize;

View File

@ -25,7 +25,7 @@ CartridgeDPCWidget::CartridgeDPCWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeDPC& cart) int x, int y, int w, int h, CartridgeDPC& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
const int V_GAP = 4; const int V_GAP = 4;
size_t size = cart.mySize; size_t size = cart.mySize;
@ -68,11 +68,10 @@ CartridgeDPCWidget::CartridgeDPCWidget(
ypos += myLineHeight + V_GAP * 3; ypos += myLineHeight + V_GAP * 3;
// Data fetchers // Data fetchers
int lwidth = _font.getStringWidth("Data fetchers ");
new StaticTextWidget(boss, _font, xpos, ypos, "Data fetchers "); new StaticTextWidget(boss, _font, xpos, ypos, "Data fetchers ");
// Top registers // Top registers
lwidth = _font.getStringWidth("Counter registers "); int lwidth = _font.getStringWidth("Counter registers ");
xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4; xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, "Top registers "); new StaticTextWidget(boss, _font, xpos, ypos, "Top registers ");
xpos += lwidth; xpos += lwidth;

View File

@ -29,11 +29,11 @@ CartDebugWidget::CartDebugWidget(GuiObject* boss, const GUI::Font& lfont,
int x, int y, int w, int h) int x, int y, int w, int h)
: Widget(boss, lfont, x, y, w, h), : Widget(boss, lfont, x, y, w, h),
CommandSender(boss), CommandSender(boss),
_nfont(nfont), _nfont{nfont},
myFontWidth(lfont.getMaxCharWidth()), myFontWidth{lfont.getMaxCharWidth()},
myFontHeight(lfont.getFontHeight()), myFontHeight{lfont.getFontHeight()},
myLineHeight(lfont.getLineHeight()), myLineHeight{lfont.getLineHeight()},
myButtonHeight(myLineHeight + 4) myButtonHeight{myLineHeight + 4}
{ {
} }

View File

@ -21,9 +21,9 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeE78KWidget::CartridgeE78KWidget( CartridgeE78KWidget::CartridgeE78KWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, int x, int y, int w, int h,
CartridgeMNetwork& cart) CartridgeMNetwork& cart)
: CartridgeMNetworkWidget(boss, lfont, nfont, x, y, w, h, cart) : CartridgeMNetworkWidget(boss, lfont, nfont, x, y, w, h, cart)
{ {
ostringstream info; ostringstream info;

View File

@ -28,7 +28,7 @@ CartridgeEnhancedWidget::CartridgeEnhancedWidget(GuiObject* boss, const GUI::Fon
int x, int y, int w, int h, int x, int y, int w, int h,
CartridgeEnhanced& cart) CartridgeEnhanced& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h), : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart) myCart{cart}
{ {
} }

Some files were not shown because too many files have changed in this diff Show More