Compare commits

..

6 Commits

Author SHA1 Message Date
Stephen Anthony bbef85b3da Final commit before tagging 6.7.1 release. 2024-01-12 15:51:34 -03:30
Stephen Anthony 37a9b6ea41 Updated 6.7.1 build for recent Visual Studio changes. 2024-01-12 15:48:27 -03:30
Stephen Anthony af4196c049 More fixes to changelog. 2024-01-05 22:00:39 -03:30
Stephen Anthony 1909c1f20f Updates for upcoming 6.7.1 release. 2024-01-05 21:56:29 -03:30
Stephen Anthony c387e7c8c2 Add driving controller fixes for 6.7.1 release. 2024-01-05 21:47:27 -03:30
Stephen Anthony 853c0927c5 Default to using system-wide libsqlite3 on Linux/UNIX systems.
Fix for slow exit from app in this mode is now fixed.
2024-01-05 21:46:50 -03:30
997 changed files with 48694 additions and 89866 deletions

View File

@ -1,68 +0,0 @@
---
name: Build
on:
pull_request:
push:
permissions: {}
jobs:
linux:
name: Linux
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Install SDL2
run: |
sudo apt-get update
sudo apt-get install libsdl2-dev
- name: Build Stella
run: |
./configure && make -j2 all
macos:
name: macOS
runs-on: macos-latest
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.2'
- name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Install SDL2
run: |
curl https://www.libsdl.org/release/SDL2-2.26.0.tar.gz | tar xzf -
mkdir SDL2-2.26.0/build
cd SDL2-2.26.0/build
../configure && make -j3 && sudo make install
- name: Build Stella
run: |
./configure && make -j3 all
windows:
name: Windows
runs-on: windows-latest
strategy:
matrix:
platform: [x64]
env:
Platform: ${{ matrix.platform }}
SDL2_version: 2.26.0
steps:
- name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Set up MSBUILD
uses: microsoft/setup-msbuild@34cfbaee7f672c76950673338facd8a73f637506
- name: Install SDL2
shell: cmd
run: |
curl -o "C:\SDL2-devel.zip" "https://www.libsdl.org/release/SDL2-devel-%SDL2_version%-VC.zip"
7z x "C:\SDL2-devel.zip" -o"C:\"
xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
if %Platform%==x64 xcopy /S "C:\SDL2-%SDL2_version%\lib\x64" src\os\windows
- name: Build Stella
run: |
msbuild src\os\windows\Stella.sln

21
.gitignore vendored
View File

@ -10,9 +10,7 @@ src/**/*.tlog
out
out.pgo
out.pgen
out.test
stella
stella-test
stella-pgo
stella-pgo-generate
*.diff
@ -20,17 +18,17 @@ project.xcworkspace/
xcuserdata/
.DS_Store
build/
src/os/macos/M6502.ins
src/macosx/M6502.ins
*.dSYM
.vscode/c_cpp_properties.json
.vscode/settings.json
src/os/windows/sdl/*
src/os/windows/x64/*
src/os/windows/Win32/*
src/os/windows/Stella.vcxproj.user
src/os/windows/*.bak
src/os/libretro/x64/*
src/os/libretro/Stella.vcxproj.user
src/windows/sdl/*
src/windows/x64/*
src/windows/Win32/*
src/windows/Stella.vcxproj.user
src/windows/*.bak
src/libretro/x64/*
src/libretro/Stella.vcxproj.user
.vs/*
.tgitconfig
src/**/*.psess
@ -42,5 +40,4 @@ src/tools/fonts/*
a.out
*.json
*.sqlite3
*.bak
debian/files
*.bak

View File

@ -7,8 +7,8 @@
# Core definitions
.core-defs:
variables:
JNI_PATH: src/os/libretro
MAKEFILE_PATH: src/os/libretro
JNI_PATH: src/libretro
MAKEFILE_PATH: src/libretro
CORENAME: stella
# Inclusion templates, required for the build to work
@ -18,10 +18,18 @@ include:
- project: 'libretro-infrastructure/ci-templates'
file: '/windows-x64-mingw.yml'
# Windows 32-bit
- project: 'libretro-infrastructure/ci-templates'
file: '/windows-i686-mingw.yml'
# Linux 64-bit
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-x64.yml'
# Linux 32-bit
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-i686.yml'
# MacOS 64-bit
- project: 'libretro-infrastructure/ci-templates'
file: '/osx-x64.yml'
@ -51,7 +59,7 @@ include:
# tvOS (AppleTV)
- project: 'libretro-infrastructure/ci-templates'
file: '/tvos-arm64.yml'
#################################### MISC ##################################
# Stages for building
@ -71,69 +79,73 @@ libretro-build-windows-x64:
- .libretro-windows-x64-mingw-make-default
- .core-defs
# Windows 32-bit
libretro-build-windows-i686:
extends:
- .libretro-windows-i686-mingw-make-default
- .core-defs
# Linux 64-bit
libretro-build-linux-x64:
extends:
- .libretro-linux-x64-make-default
- .core-defs
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:latest
before_script:
- export NUMPROC=$(($(nproc)/5))
- sudo apt-get update -qy
- sudo apt-get install -qy software-properties-common
- sudo add-apt-repository -y ppa:savoury1/build-tools
- sudo add-apt-repository -y ppa:savoury1/gcc-defaults-12
- sudo apt-get update -qy
- sudo apt-get install -qy gcc-12 g++-12
# This container's existing installations of gcc and CMake are way too old
variables:
CC: /usr/bin/gcc-12
CXX: /usr/bin/g++-12
CXXFLAGS: -Wno-deprecated-declarations # Deprecation warnings aren't helpful on the libretro Gitlab
# Linux 32-bit
libretro-build-linux-i686:
extends:
- .libretro-linux-i686-make-default
- .core-defs
# MacOS 64-bit
libretro-build-osx-x64:
tags:
- mac-apple-silicon
extends:
- .libretro-osx-x64-make-default
- .core-defs
# MacOS ARM 64-bit
libretro-build-osx-arm64:
tags:
- mac-apple-silicon
extends:
- .libretro-osx-arm64-make-default
- .core-defs
################################### CELLULAR #################################
# Android ARMv7a
# android-armeabi-v7a:
# extends:
# - .libretro-android-jni-armeabi-v7a
# - .core-defs
android-armeabi-v7a:
extends:
- .libretro-android-jni-armeabi-v7a
- .core-defs
# Android ARMv8a
# android-arm64-v8a:
# extends:
# - .libretro-android-jni-arm64-v8a
# - .core-defs
android-arm64-v8a:
extends:
- .libretro-android-jni-arm64-v8a
- .core-defs
# Android 64-bit x86
# android-x86_64:
# extends:
# - .libretro-android-jni-x86_64
# - .core-defs
android-x86_64:
extends:
- .libretro-android-jni-x86_64
- .core-defs
# Android 32-bit x86
android-x86:
extends:
- .libretro-android-jni-x86
- .core-defs
# iOS
libretro-build-ios-arm64:
tags:
- mac-apple-silicon
extends:
- .libretro-ios-arm64-make-default
- .core-defs
# iOS (armv7) [iOS 9 and up]
libretro-build-ios9:
extends:
- .libretro-ios9-make-default
- .core-defs
# tvOS
libretro-build-tvos-arm64:
extends:
@ -142,7 +154,7 @@ libretro-build-tvos-arm64:
################################### CONSOLES #################################
# Nintendo Switch
# libretro-build-libnx-aarch64:
# extends:
# - .libretro-libnx-static-retroarch-master
# - .core-defs
libretro-build-libnx-aarch64:
extends:
- .libretro-libnx-static-retroarch-master
- .core-defs

63
.travis.yml Normal file
View File

@ -0,0 +1,63 @@
# Build matrix / environment variables are explained on:
# http://about.travis-ci.org/docs/user/build-configuration/
# This file can be validated on: http://lint.travis-ci.org/
language: cpp
matrix:
include:
- os: linux
dist: xenial
compiler: gcc
env: GCC=9 CC=gcc-9 CXX=g++-9
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-9
- g++-9
update: true
- os: osx
osx_image: xcode11.3
compiler: clang
addons:
homebrew:
update: true
install:
- |
old_cwd=$(pwd)
cd ~
if [[ "$HOST" == macosx-* ]]; then
curl -O https://www.libsdl.org/release/SDL2-2.0.10.tar.gz
tar xzf SDL2-2.0.10.tar.gz
cd SDL2-2.0.10/Xcode/SDL
sed -i -e 's/@rpath//g' SDL.xcodeproj/project.pbxproj
xcodebuild -configuration Release
mkdir -p ~/Library/Frameworks/
ln -s `pwd`/build/Release/SDL2.framework ~/Library/Frameworks/
else
curl -O https://www.libsdl.org/release/SDL2-2.0.10.tar.gz
tar xzf SDL2-2.0.10.tar.gz
cd SDL2-2.0.10
mkdir build
cd build
../configure
make
sudo make install
fi
cd $old_cwd
script:
- |
./configure
make all

15
.vscode/settings.json vendored
View File

@ -14,7 +14,6 @@
"C_Cpp.intelliSenseEngine": "Default",
"files.insertFinalNewline": true,
"files.associations": {
"*.h": "cpp",
"__functional_base": "cpp",
"array": "cpp",
"istream": "cpp",
@ -104,18 +103,6 @@
"version": "cpp",
"shared_mutex": "cpp",
"compare": "cpp",
"concepts": "cpp",
"__verbose_abort": "cpp",
"any": "cpp",
"charconv": "cpp",
"csignal": "cpp",
"execution": "cpp",
"numbers": "cpp",
"span": "cpp",
"unordered_set": "cpp",
"variant": "cpp",
"hash_map": "cpp",
"format": "cpp",
"*.inc": "cpp"
"concepts": "cpp"
}
}

View File

@ -9,7 +9,7 @@
SSSS ttt eeeee llll llll aaaaa
===========================================================================
Release 7.0 for Linux, macOS and Windows
Release 6.7.1 for Linux, macOS and Windows
===========================================================================
The Atari 2600 Video Computer System (VCS), introduced in 1977, was the
@ -21,21 +21,22 @@ all of your favourite Atari 2600 games again! Stella was originally
developed for Linux by Bradford W. Mott, however, it has been ported to a
number of other platforms and is currently maintained by Stephen Anthony.
This is the 7.0 release of Stella for Linux, macOS and Windows. The
This is the 6.7.1 release of Stella for Linux, macOS and Windows. The
distributions currently available are:
* Binary for Windows 7/8/10/11 :
Stella-7.0-x64.exe (64-bit EXE installer)
Stella-7.0-windows.zip (64 bit ZIP version)
* Binaries for Windows 7/8/10/11 :
Stella-6.7.1-win32.exe (32-bit EXE installer)
Stella-6.7.1-x64.exe (64-bit EXE installer)
Stella-6.7.1-windows.zip (32/64 bit versions)
* Binary distribution for macOS 10.13 and above :
Stella-7.0-macos.dmg (ARM M1 and 64-bit Intel)
* Binary distribution for macOS 10.7 and above :
Stella-6.7.1-macos.dmg (ARM M1 and 64-bit Intel)
* Binary distribution for 64-bit Ubuntu :
stella_7.0-1_amd64.deb
stella_6.7.1-1_amd64.deb
* Source code distribution for all platforms :
stella-7.0-src.tar.xz
stella-6.7.1-src.tar.xz
Distribution Site

View File

@ -12,74 +12,6 @@
Release History
===========================================================================
7.0 to 7.1 (xxx x, 202x)
* Added developer option for disabling PlusROM support (TODO: Doc)
* Enhanced PAL-60 detection, searches for signature (e.g. PAL60) in ROM
* Enhanced VSYNC emulation, considers VBLANK now too
6.7.1 to 7.0 (October 5, 2024)
* Enhanced ROM launcher to allow multiple images per ROM.
* Made heaps of additional images available for the ROM launcher.
* Added searching by filename for ROM launcher images.
* Added option to start random ROM.
* Added automatically enabled phosphor modes.
* Enhanced Game Properties dialog for multigame ROMs.
* Added 2nd UI theme and hotkey for toggling UI theme.
* Added bezel support (incl. Sinden Lightgun).
* Added optional type format detection based on colors used.
* Added Joy2B+ controller support.
* Added auto detection for QuadTari attached controllers.
* Enhanced Kid Vid support to play tape audio.
* Added port selection, used for controller default mapping.
* Added missing PlusROM support for E7 bankswitching.
* Enhanced movie cart (MVC) support.
* Accelerated emulation up to ~15% (ARM).
* Added limited GameLine Master Module bankswitching support.
* Added 03E0 bankswitching for Brazilian Parker Bros ROMs.
* Added WF8 bankswitching used by some certain Coleco white carts.
* Added JANE bankswitching used by Coleco's Tarzan prototype.
* Added ELF mapper for Mattress Monkeys.
* Added BUS bankswitching support for some older demos.
* Fixed broken 7800 pause key support.
* Added developer option for random hotspot peek values.
* Added user defined CPU cycle timers to debugger.
* Removed 'launcherroms' option, since it was causing some issues.
* Codebase now uses C++20 features, which means a minimum of gcc-11
or clang-10 for Linux/Mac, and Visual Studio 2022 for Windows.
-Have fun!
6.7 to 6.7.1 (January 15, 2024)
* Fixed broken mouse and Stelladaptor input for Driving Controller.
@ -88,6 +20,8 @@
when available, and fixes delay on exiting app experienced on some
systems.
-Have fun!
6.6 to 6.7 (June 13, 2022)

View File

@ -12,7 +12,7 @@
License Information and Copyright Notice
===========================================================================
Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony and the
Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony and the
Stella Team
This program is free software; you can redistribute it and/or modify it

View File

@ -8,7 +8,7 @@
## SS SS tt ee ll ll aa aa
## SSSS ttt eeeee llll llll aaaaa
##
## Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
## Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
## and the Stella Team
##
## See the file "License.txt" for information on usage and redistribution of
@ -26,7 +26,6 @@ LDFLAGS := -pthread
INCLUDES :=
LIBS :=
OBJS :=
OBJS_TEST :=
PROF :=
MODULES :=
@ -48,53 +47,29 @@ else
CFLAGS:= -O2
endif
ifndef CXXFLAGS_TEST
CXXFLAGS_TEST := $(CXXFLAGS)
endif
ifndef CFLAGS_TEST
CFLAGS_TEST := $(CFLAGS)
endif
ifndef LDFLAGS_TEST
LDFLAGS_TEST := $(LDFLAGS)
endif
CXXFLAGS+= -Wall -Wextra -Wno-unused-parameter
CFLAGS+= -Wall -Wextra -Wno-unused-parameter
CXXFLAGS_TEST+= -Wall -Wextra -Wno-unused-parameter
CFLAGS_TEST+= -Wall -Wextra -Wno-unused-parameter
ifdef HAVE_GCC
CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -std=c++20
CFLAGS+= -Wunused
CXXFLAGS_TEST+= -Wno-multichar -Wunused -Woverloaded-virtual -std=c++20
CFLAGS_TEST+= -Wno-multichar -Wunused
CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -std=c++17
CFLAGS+= -Wno-multichar -Wunused
endif
ifdef HAVE_CLANG
CXXFLAGS+= -Wunused -Woverloaded-virtual -std=c++20
CFLAGS+= -Wunused
CXXFLAGS_TEST+= -Wno-multichar -Wunused -Woverloaded-virtual -std=c++20
CFLAGS_TEST+= -Wno-multichar -Wunused
CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -std=c++17
CFLAGS+= -Wno-multichar -Wunused
endif
ifdef CLANG_WARNINGS
EXTRA_WARN=-Wno-c++98-compat-pedantic -Wno-undefined-func-template \
EXTRA_WARN=-Weverything -Wno-c++98-compat-pedantic -Wno-unknown-warning-option \
-Wno-switch-enum -Wno-conversion -Wno-covered-switch-default \
-Wno-documentation -Wno-float-equal \
-Wno-inconsistent-missing-destructor-override -Wno-float-equal \
-Wno-exit-time-destructors -Wno-global-constructors -Wno-weak-vtables \
-Wno-four-char-constants -Wno-padded -Wno-reserved-identifier \
-Wno-duplicate-enum -Wno-unsafe-buffer-usage
-Wno-duplicate-enum
CXXFLAGS+= $(EXTRA_WARN)
CFLAGS+= $(EXTRA_WARN)
CXXFLAGS_TEST+= $(EXTRA_WARN)
CFLAGS_TEST+= $(EXTRA_WARN)
endif
ifdef PROFILE
@ -133,14 +108,12 @@ ifdef STELLA_BUILD_ROOT
else
OBJECT_ROOT := out
endif
OBJECT_ROOT_TEST := out.test
OBJECT_ROOT_PROFILE_GENERERATE := out.pgen
OBJECT_ROOT_PROFILE_USE := out.pgo
EXECUTABLE := stella$(EXEEXT)
EXECUTABLE_PROFILE_GENERATE := stella-pgo-generate$(EXEEXT)
EXECUTABLE_PROFILE_USE := stella-pgo$(EXEEXT)
EXECUTABLE_TEST := stella-test$(EXEEXT)
PROFILE_DIR = $(CURDIR)/test/roms/profile
PROFILE_OUT = $(PROFILE_DIR)/out
@ -181,9 +154,6 @@ all: $(EXECUTABLE)
pgo: $(EXECUTABLE_PROFILE_USE)
test: $(EXECUTABLE_TEST)
./$(EXECUTABLE_TEST)
######################################################################
# Various minor settings
######################################################################
@ -206,7 +176,6 @@ MODULES += \
src/emucore \
src/emucore/tia \
src/emucore/tia/frame-manager \
src/emucore/elf \
src/common/repository/sqlite
######################################################################
@ -225,7 +194,6 @@ DEPDIRS = $(addsuffix /$(DEPDIR),$(MODULE_DIRS))
DEPFILES =
OBJ=$(addprefix $(OBJECT_ROOT)/,$(OBJS))
OBJ_TEST=$(addprefix $(OBJECT_ROOT_TEST)/,$(OBJS_TEST))
OBJ_PROFILE_GENERATE=$(addprefix $(OBJECT_ROOT_PROFILE_GENERERATE)/,$(OBJS))
OBJ_PROFILE_USE=$(addprefix $(OBJECT_ROOT_PROFILE_USE)/,$(OBJS))
@ -233,9 +201,6 @@ OBJ_PROFILE_USE=$(addprefix $(OBJECT_ROOT_PROFILE_USE)/,$(OBJS))
$(EXECUTABLE): $(OBJ)
$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) $(PROF) -o $@
$(EXECUTABLE_TEST): $(OBJ_TEST)
$(LD) $(LDFLAGS_TEST) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -lgtest -lgtest_main -o $@
$(EXECUTABLE_PROFILE_GENERATE): $(OBJ_PROFILE_GENERATE)
$(LD) $(LDFLAGS_PROFILE_GENERATE) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) $(PROF) -o $@
@ -250,7 +215,7 @@ clean:
-$(RM) -fr \
$(OBJECT_ROOT) $(OBJECT_ROOT_PROFILE_GENERERATE) $(OBJECT_ROOT_PROFILE_USE) \
$(EXECUTABLE) $(EXECUTABLE_PROFILE_GENERATE) $(EXECUTABLE_PROFILE_USE) \
$(OBJECT_ROOT_TEST) $(PROFILE_OUT) $(PROFILE_STAMP)
$(PROFILE_OUT) $(PROFILE_STAMP)
.PHONY: all clean dist distclean
@ -278,16 +243,6 @@ $(OBJECT_ROOT)/%.o: %.cxx
$(merge_dep)
$(OBJECT_ROOT)/%.o: %.c
$(create_dir)
$(CC) $(CXX_UPDATE_DEP_FLAG) $(CFLAGS_TEST) $(CPPFLAGS) -c $(<) -o $@
$(merge_dep)
$(OBJECT_ROOT_TEST)/%.o: %.cxx
$(create_dir)
$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS_TEST) $(CPPFLAGS) -c $(<) -o $@
$(merge_dep)
$(OBJECT_ROOT_TEST)/%.o: %.c
$(create_dir)
$(CC) $(CXX_UPDATE_DEP_FLAG) $(CFLAGS) $(CPPFLAGS) -c $(<) -o $@
$(merge_dep)
@ -325,14 +280,6 @@ $(OBJECT_ROOT)/%.o: %.c
$(create_dir)
$(CC) $(CXX_UPDATE_DEP_FLAG) $(CFLAGS) $(CPPFLAGS) -c $(<) -o $@
$(OBJECT_ROOT_TEST)/%.o: %.cxx
$(create_dir)
$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS_TEST) $(CPPFLAGS) -c $(<) -o $@
$(OBJECT_ROOT_TEST)/%.o: %.c
$(create_dir)
$(CC) $(CXX_UPDATE_DEP_FLAG) $(CFLAGS_TEST) $(CPPFLAGS) -c $(<) -o $@
$(OBJECT_ROOT_PROFILE_GENERERATE)/%.o: %.cxx
$(create_dir)
$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS_PROFILE_GENERATE) $(CPPFLAGS) -c $(<) -o $@
@ -370,11 +317,11 @@ install: all
$(INSTALL) -d "$(DESTDIR)$(BINDIR)"
$(INSTALL) -c -m 755 "$(srcdir)/stella$(EXEEXT)" "$(DESTDIR)$(BINDIR)/stella$(EXEEXT)"
$(INSTALL) -d "$(DESTDIR)$(DOCDIR)"
$(INSTALL) -c -m 644 "$(srcdir)/Announce.txt" "$(srcdir)/Changes.txt" "$(srcdir)/Copyright.txt" "$(srcdir)/License.txt" "$(srcdir)/README-SDL.txt" "$(srcdir)/README.md" "$(srcdir)/Todo.txt" "$(srcdir)/docs/index.html" "$(srcdir)/docs/debugger.html" "$(DESTDIR)$(DOCDIR)/"
$(INSTALL) -c -m 644 "$(srcdir)/Announce.txt" "$(srcdir)/Changes.txt" "$(srcdir)/Copyright.txt" "$(srcdir)/License.txt" "$(srcdir)/README-SDL.txt" "$(srcdir)/Readme.txt" "$(srcdir)/Todo.txt" "$(srcdir)/docs/index.html" "$(srcdir)/docs/debugger.html" "$(DESTDIR)$(DOCDIR)/"
$(INSTALL) -d "$(DESTDIR)$(DOCDIR)/graphics"
$(INSTALL) -c -m 644 $(wildcard $(srcdir)/docs/graphics/*.png) "$(DESTDIR)$(DOCDIR)/graphics"
$(INSTALL) -d "$(DESTDIR)$(DATADIR)/applications"
$(INSTALL) -c -m 644 "$(srcdir)/src/os/unix/stella.desktop" "$(DESTDIR)$(DATADIR)/applications"
$(INSTALL) -c -m 644 "$(srcdir)/src/unix/stella.desktop" "$(DESTDIR)$(DATADIR)/applications"
$(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/hicolor/16x16/apps"
$(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/hicolor/22x22/apps"
$(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/hicolor/24x24/apps"
@ -410,7 +357,7 @@ src/emucore/M6502.ins: src/emucore/M6502.m4
m4 src/emucore/M6502.m4 > src/emucore/M6502.ins
# Special rule for windows icon stuff (there's probably a better way to do this ...)
src/os/windows/stella_icon.o: src/os/windows/stella.ico src/os/windows/stella.rc
windres --include-dir src/os/windows src/os/windows/stella.rc src/os/windows/stella_icon.o
src/windows/stella_icon.o: src/windows/stella.ico src/windows/stella.rc
windres --include-dir src/windows src/windows/stella.rc src/windows/stella_icon.o
.PHONY: deb bundle test install uninstall

View File

@ -15,10 +15,6 @@ Please check the list of known issues first (see below) before reporting new one
If you encounter any issues, please open a new issue on the project
[issue tracker](https://github.com/stella-emu/stella/issues).
Note: Please do **not** report issues regarding platforms other than PC and RetroN 77
(e.g. Libretro) here. The Stella Team is not responsible for these forks and cannot
help with them.
# Known issues
Please check out the [issue tracker](https://github.com/stella-emu/stella/issues) for

10
Readme.txt Normal file
View File

@ -0,0 +1,10 @@
Stella is a multi-platform Atari 2600 VCS emulator which allows you to
play all of your favourite Atari 2600 games on your PC. You'll find the
Stella Users Manual in the docs subdirectory. If you'd like to verify
that you have the latest release of Stella, visit the Stella Website at:
https://stella-emu.github.io
Enjoy,
The Stella Team

32
appveyor.yml Normal file
View File

@ -0,0 +1,32 @@
image: Visual Studio 2019
shallow_clone: true
environment:
matrix:
- Platform: x64
- Platform: Win32
Configuration: Release
SDL2_version: 2.0.12
install:
- cmd: |
curl -o "C:\SDL2-devel.zip" "https://www.libsdl.org/release/SDL2-devel-%SDL2_version%-VC.zip"
7z x "C:\SDL2-devel.zip" -o"C:\"
xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
if %Platform%==x64 xcopy /S "C:\SDL2-%SDL2_version%\lib\x64" src\windows
if %Platform%==Win32 xcopy /S "C:\SDL2-%SDL2_version%\lib\x86" src\windows
build_script:
- cmd: |
msbuild src\windows\Stella.sln
artifacts:
- path: '**\stella.exe'

View File

@ -4,9 +4,7 @@
# contains the module name, a trick we use so we can keep multiple different
# module object lists, one for each module.
MODULE_OBJS-$(MODULE) := $(MODULE_OBJS)
MODULE_TEST_OBJS-$(MODULE) := $(MODULE_TEST_OBJS)
# If not building as a plugin, add the object files to the main OBJS list
#OBJS += $(MODULE_LIB-$(MODULE))
OBJS += $(MODULE_OBJS)
OBJS_TEST += $(MODULE_TEST_OBJS)

121
config.guess vendored
View File

@ -1,14 +1,14 @@
#! /bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2024 Free Software Foundation, Inc.
# Copyright 1992-2021 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
timestamp='2024-07-27'
timestamp='2021-06-03'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@ -47,7 +47,7 @@ me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system '$me' is run on.
Output the configuration name of the system \`$me' is run on.
Options:
-h, --help print this help, then exit
@ -60,13 +60,13 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright 1992-2024 Free Software Foundation, Inc.
Copyright 1992-2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try '$me --help' for more information."
Try \`$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
@ -102,8 +102,8 @@ GUESS=
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
# use 'HOST_CC' if defined, but it is deprecated.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
@ -123,7 +123,7 @@ set_cc_for_build() {
dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c17 c99 c89 ; do
for driver in cc gcc c89 c99 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver
break
@ -155,9 +155,6 @@ Linux|GNU|GNU/*)
set_cc_for_build
cat <<-EOF > "$dummy.c"
#if defined(__ANDROID__)
LIBC=android
#else
#include <features.h>
#if defined(__UCLIBC__)
LIBC=uclibc
@ -165,8 +162,6 @@ Linux|GNU|GNU/*)
LIBC=dietlibc
#elif defined(__GLIBC__)
LIBC=gnu
#elif defined(__LLVM_LIBC__)
LIBC=llvm
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
@ -174,7 +169,6 @@ Linux|GNU|GNU/*)
LIBC=musl
#endif
#endif
#endif
EOF
cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
eval "$cc_set_libc"
@ -443,7 +437,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
# This test works for both compilers.
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH=x86_64
@ -465,7 +459,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
UNAME_RELEASE=`uname -v`
;;
esac
# Japanese Language versions have a version number like '4.1.3-JL'.
# Japanese Language versions have a version number like `4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL
;;
@ -634,8 +628,7 @@ EOF
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
int
main ()
main()
{
if (!__power_pc())
exit(1);
@ -719,8 +712,7 @@ EOF
#include <stdlib.h>
#include <unistd.h>
int
main ()
int main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
@ -912,7 +904,7 @@ EOF
fi
;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`uname -p`
UNAME_PROCESSOR=`/usr/bin/uname -p`
case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
@ -937,9 +929,6 @@ EOF
i*:PW*:*)
GUESS=$UNAME_MACHINE-pc-pw32
;;
*:SerenityOS:*:*)
GUESS=$UNAME_MACHINE-pc-serenity
;;
*:Interix*:*)
case $UNAME_MACHINE in
x86)
@ -974,37 +963,11 @@ EOF
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
;;
x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
;;
*:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
;;
*:Minix:*:*)
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __ARM_EABI__
#ifdef __ARM_PCS_VFP
ABI=eabihf
#else
ABI=eabi
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
esac
fi
GUESS=$CPU-unknown-linux-$LIBCABI
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
@ -1070,16 +1033,7 @@ EOF
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:cos:*:*)
GUESS=$UNAME_MACHINE-unknown-cos
;;
kvx:mbr:*:*)
GUESS=$UNAME_MACHINE-unknown-mbr
;;
loongarch32:Linux:*:* | loongarch64:Linux:*:*)
loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m32r*:Linux:*:*)
@ -1194,27 +1148,16 @@ EOF
;;
x86_64:Linux:*:*)
set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __i386__
ABI=x86
#else
#ifdef __ILP32__
ABI=x32
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
x86) CPU=i686 ;;
x32) LIBCABI=${LIBC}x32 ;;
esac
if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_X32 >/dev/null
then
LIBCABI=${LIBC}x32
fi
fi
GUESS=$CPU-pc-linux-$LIBCABI
GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
;;
xtensa*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
@ -1234,7 +1177,7 @@ EOF
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
# If we were able to find 'uname', then EMX Unix compatibility
# If we were able to find `uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
@ -1375,7 +1318,7 @@ EOF
GUESS=ns32k-sni-sysv
fi
;;
PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
@ -1421,11 +1364,8 @@ EOF
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
ppc:Haiku:*:*) # Haiku running on Apple PowerPC
GUESS=powerpc-apple-haiku
;;
*:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat)
GUESS=$UNAME_MACHINE-unknown-haiku
x86_64:Haiku:*:*)
GUESS=x86_64-unknown-haiku
;;
SX-4:SUPER-UX:*:*)
GUESS=sx4-nec-superux$UNAME_RELEASE
@ -1582,9 +1522,6 @@ EOF
i*86:rdos:*:*)
GUESS=$UNAME_MACHINE-pc-rdos
;;
i*86:Fiwix:*:*)
GUESS=$UNAME_MACHINE-pc-fiwix
;;
*:AROS:*:*)
GUESS=$UNAME_MACHINE-unknown-aros
;;
@ -1597,9 +1534,6 @@ EOF
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
*:Ironclad:*:*)
GUESS=$UNAME_MACHINE-unknown-ironclad
;;
esac
# Do we have a guess based on uname results?
@ -1623,7 +1557,6 @@ cat > "$dummy.c" <<EOF
#endif
#endif
#endif
int
main ()
{
#if defined (sony)

952
config.sub vendored

File diff suppressed because it is too large Load Diff

98
configure vendored
View File

@ -13,16 +13,6 @@
# use environment vars if set
CXXFLAGS="$CXXFLAGS $CPPFLAGS"
if test -n "$CXXFLAGS_TEST"; then
CXXFLAGS_TEST="$CXXFLAGS_TEST $CPPFLAGS"
else
CXXFLAGS_TEST="$CXXFLAGS"
fi
if test -z "$LDFLAGS_TEST"; then
LDFLAGS_TEST="$LDFLAGS"
fi
# default option behaviour yes/no
_build_gui=yes
_build_windowed=yes
@ -38,14 +28,12 @@ _build_static=no
_build_profile=no
_build_debug=no
_build_release=no
_build_mold=no
# more defaults
_ranlib=ranlib
_install=install
_ar="ar cru"
_strip=strip
_pkg_config=pkg-config
_mkdir="mkdir -p"
_echo=printf
_cat=cat
@ -230,13 +218,11 @@ Optional Features:
--disable-debug
--enable-release build with all optimizations, for final release [disabled]
--disable-release
--use-mold-linker use mold linker (experimental) [disabled]
Optional Libraries:
--with-sdl-prefix=DIR Prefix where the sdl2-config script is installed (optional)
--with-libpng-prefix=DIR Prefix where libpng is installed (optional)
--with-zlib-prefix=DIR Prefix where zlib is installed (optional)
--with-gtest-prefix=DIR Prefix where googletest is installed (optional)
Some influential environment variables:
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
@ -245,8 +231,6 @@ Some influential environment variables:
CXXFLAGS C++ compiler flags
CPPFLAGS C++ preprocessor flags, e.g. -I<include dir> if you have
headers in a nonstandard directory <include dir>
LDFLAGS_TEST linker flags for unit tests
CXXFLAGS_TEST C++ compiler flags for unit tests
EOF
exit 0
@ -280,7 +264,6 @@ for ac_option in $@; do
--disable-debug) _build_debug=no ;;
--enable-release) _build_release=yes ;;
--disable-release) _build_release=no ;;
--use-mold-linker) _build_mold=yes ;;
--with-sdl-prefix=*)
arg=`echo $ac_option | cut -d '=' -f 2`
_sdlpath="$arg:$arg/bin"
@ -294,11 +277,6 @@ for ac_option in $@; do
_prefix=`echo $ac_option | cut -d '=' -f 2`
ZLIB_CFLAGS="-I$_prefix/include"
ZLIB_LIBS="-L$_prefix/lib"
;;
--with-gtest-prefix=*)
_prefix=`echo $ac_option | cut -d '=' -f 2`
CXXFLAGS_TEST="$CXXFLAGS_TEST -I$_prefix/include"
LDFLAGS_TEST="$LDFLAGS_TEST -L$_prefix/lib"
;;
--host=*)
_host=`echo $ac_option | cut -d '=' -f 2`
@ -344,17 +322,12 @@ mingw32-cross)
_host_cpu=i386
_host_prefix=i386-mingw32msvc
;;
"")
*)
guessed_host=`$_srcdir/config.guess`
_host_cpu=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
_host_os=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
_host_vendor=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
;;
*)
_host_cpu=`echo "$_host" | sed 's/^\([^-]*\)-.*/\1/'`
_host_os=`echo "$_host" | sed 's/-\([^-]*\)-[^-]*$/\1/'`
_host_prefix="$_host"
;;
esac
#
@ -370,6 +343,9 @@ arm-riscos-aof)
psp)
EXEEXT=".elf"
;;
gp2x)
EXEEXT=""
;;
*)
EXEEXT=""
;;
@ -402,7 +378,7 @@ else
fi
for compiler in $compilers; do
if test_compiler "$compiler -std=c++20"; then
if test_compiler "$compiler -std=c++17"; then
CXX=$compiler
echo $CXX
break
@ -632,9 +608,6 @@ fi
# Cross-compilers use their own commands for the following functions
if test -n "$_host_prefix"; then
_strip="$_host_prefix-$_strip"
if command -v "$_host_prefix-$_pkg_config" >/dev/null 2>&1; then
_pkg_config="$_host_prefix-$_pkg_config"
fi
fi
#
@ -648,7 +621,7 @@ if test "$_build_zip" = yes ; then
#include <zlib.h>
int main(void) { return strcmp(ZLIB_VERSION, zlibVersion()); }
EOF
cc_check $LDFLAGS $CXXFLAGS $ZLIB_CFLAGS $ZLIB_LIBS `$_pkg_config --libs zlib` && _zlib=yes
cc_check $LDFLAGS $CXXFLAGS $ZLIB_CFLAGS $ZLIB_LIBS `pkg-config --libs zlib` && _zlib=yes
if test "$_zlib" = yes ; then
echo "$_zlib"
@ -674,7 +647,7 @@ if test "$_build_png" = yes ; then
#include <png.h>
int main(void) { return printf("%s\n", PNG_HEADER_VERSION_STRING); }
EOF
cc_check $LDFLAGS $CXXFLAGS $LIBPNG_CFLAGS $LIBPNG_LIBS `$_pkg_config --libs libpng` && _libpng=yes
cc_check $LDFLAGS $CXXFLAGS $LIBPNG_CFLAGS $LIBPNG_LIBS `pkg-config --libs libpng` && _libpng=yes
if test "$_libpng" = yes ; then
echo "$_libpng"
@ -698,7 +671,7 @@ if test "$_build_sqlite3" = yes ; then
#include <sqlite3.h>
int main(void) { return printf("%s\n", SQLITE_VERSION); }
EOF
cc_check $LDFLAGS $CXXFLAGS `$_pkg_config --libs sqlite3` && _libsqlite3=yes
cc_check $LDFLAGS $CXXFLAGS `pkg-config --libs sqlite3` && _libsqlite3=yes
if test "$_libsqlite3" = yes ; then
echo "$_libsqlite3"
@ -821,29 +794,24 @@ echo
find_sdlconfig
SRC="src"
SRC_OS="$SRC/os"
SRC_LIB="$SRC/lib"
CORE="$SRC/emucore"
COMMON="$SRC/common"
TIA="$SRC/emucore/tia"
ELF="$SRC/emucore/elf"
TIA_FRAME_MANAGER="$SRC/emucore/tia/frame-manager"
TV="$SRC/common/tv_filters"
GUI="$SRC/gui"
DBG="$SRC/debugger"
DBGGUI="$SRC/debugger/gui"
YACC="$SRC/debugger/yacc"
YACC="$SRC/yacc"
CHEAT="$SRC/cheat"
LIBPNG="$SRC_LIB/libpng"
LIBJPG="$SRC_LIB/nanojpeg"
LIBJPGEXIF="$SRC_LIB/tinyexif"
ZLIB="$SRC_LIB/zlib"
LIBPNG="$SRC/libpng"
ZLIB="$SRC/zlib"
SQLITE_REPO="$SRC/common/repository/sqlite"
SQLITE_LIB="$SRC_LIB/sqlite"
JSON="$SRC_LIB/json"
HTTP_LIB="$SRC_LIB/httplib"
SQLITE_LIB="$SRC/sqlite"
JSON="$SRC/json"
HTTP_LIB="$SRC/httplib"
INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$ELF -I$JSON -I$SQLITE_REPO"
INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$JSON -I$SQLITE_REPO"
INCLUDES="$INCLUDES `$_sdlconfig --cflags`"
if test "$_build_static" = yes ; then
@ -859,30 +827,26 @@ LD=$CXX
case $_host_os in
unix)
DEFINES="$DEFINES -DBSPF_UNIX"
MODULES="$MODULES $SRC_OS/unix"
INCLUDES="$INCLUDES -I$SRC_OS/unix"
MODULES="$MODULES $SRC/unix"
INCLUDES="$INCLUDES -I$SRC/unix"
;;
darwin)
DEFINES="$DEFINES -DBSPF_UNIX -DMACOS_KEYS"
MODULES="$MODULES $SRC_OS/unix"
INCLUDES="$INCLUDES -I$SRC_OS/unix"
MODULES="$MODULES $SRC/unix"
INCLUDES="$INCLUDES -I$SRC/unix"
if test "$have_clang" == yes; then
CXXFLAGS="$CXXFLAGS -Wno-poison-system-directories"
if test -n "$CXXFLAGS_TEST"; then
CXXFLAGS_TEST="$CXXFLAGS_TEST -Wno-poison-system-directories"
fi
fi
_libsqlite3=no
;;
retron77)
DEFINES="$DEFINES -DBSPF_UNIX -DRETRON77"
MODULES="$MODULES $SRC_OS/unix $SRC_OS/unix/r77"
INCLUDES="$INCLUDES -I$SRC_OS/unix -I$SRC_OS/unix/r77"
MODULES="$MODULES $SRC/unix $SRC/unix/r77"
INCLUDES="$INCLUDES -I$SRC/unix -I$SRC/unix/r77"
;;
win32)
DEFINES="$DEFINES -DBSPF_WINDOWS"
MODULES="$MODULES $SRC_OS/windows"
INCLUDES="$INCLUDES -I$SRC_OS/windows"
MODULES="$MODULES $SRC/windows"
INCLUDES="$INCLUDES -I$SRC/windows"
LIBS="$LIBS -lmingw32 -lwinmm"
;;
*)
@ -927,11 +891,9 @@ if test "$_build_httplib" = yes ; then
fi
if test "$_build_png" = yes ; then
DEFINES="$DEFINES -DIMAGE_SUPPORT"
INCLUDES="$INCLUDES -I$LIBJPG -I$LIBJPGEXIF"
MODULES="$MODULES $LIBJPGEXIF"
DEFINES="$DEFINES -DPNG_SUPPORT"
if test "$_libpng" = yes ; then
LIBS="$LIBS `$_pkg_config --libs libpng`"
LIBS="$LIBS `pkg-config --libs libpng`"
else
MODULES="$MODULES $LIBPNG"
INCLUDES="$INCLUDES -I$LIBPNG"
@ -939,7 +901,7 @@ if test "$_build_png" = yes ; then
fi
if test "$_libsqlite3" = yes ; then
LIBS="$LIBS `$_pkg_config --libs sqlite3`"
LIBS="$LIBS `pkg-config --libs sqlite3`"
else
MODULES="$MODULES $SQLITE_LIB"
INCLUDES="$INCLUDES -I$SQLITE_LIB"
@ -948,7 +910,7 @@ fi
if test "$_build_zip" = yes ; then
DEFINES="$DEFINES -DZIP_SUPPORT"
if test "$_zlib" = yes ; then
LIBS="$LIBS `$_pkg_config --libs zlib`"
LIBS="$LIBS `pkg-config --libs zlib`"
else
MODULES="$MODULES $ZLIB"
INCLUDES="$INCLUDES -I$ZLIB"
@ -967,10 +929,6 @@ if test "$_build_release" = no ; then
_build_release=
fi
if test "$_build_mold" = yes ; then
LDFLAGS="-fuse-ld=mold"
fi
# Workaround until we deal with autodetection of C compiler properly
# Or we remove C files from Stella entirely, by making them C++
if test -z "$CC"; then
@ -984,7 +942,6 @@ cat > config.mak << EOF
CC := $CC
CXX := $CXX
CXXFLAGS := $CXXFLAGS
CXXFLAGS_TEST := $CXXFLAGS_TEST
LD := $LD
LIBS += $LIBS
RANLIB := $_ranlib
@ -1022,7 +979,6 @@ INCLUDES += $INCLUDES
OBJS += $OBJS
DEFINES += $DEFINES
LDFLAGS += $LDFLAGS
LDFLAGS_TEST += $LDFLAGS_TEST
$_config_mk_data
EOF

6
debian/changelog vendored
View File

@ -1,8 +1,8 @@
stella (7.0) stable; urgency=high
stella (6.7.1) stable; urgency=high
* Version 7.0 release
* Version 6.7.1 release
-- Stephen Anthony <sa666666@gmail.com> Sat, 5 Oct 2024 17:09:59 -0230
-- Stephen Anthony <sa666666@gmail.com> Mon, 15 Jan 2024 17:09:59 -0230
stella (6.7) stable; urgency=high

1
debian/control vendored
View File

@ -3,7 +3,6 @@ Maintainer: Stephen Anthony <sa666666@gmail.com>
Section: otherosfs
Priority: optional
Build-Depends: debhelper (>= 10~),
libgtest-dev,
libpng-dev,
libsdl2-dev,
zlib1g-dev

14
debian/copyright vendored
View File

@ -1,18 +1,18 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0
Upstream-Name: stella
Source: https://stella-emu.github.io
Copyright: 1995-2025 Bradford W. Mott, Stephen Anthony and the Stella Team
Copyright: 1995-2022 Bradford W. Mott, Stephen Anthony and the Stella Team
License: GPL-2+
Files: *
Copyright: 1995-2025 Bradford W. Mott, Stephen Anthony and the Stella
Copyright: 1995-2022 Bradford W. Mott, Stephen Anthony and the Stella
Team
License: GPL-2+
Files: debian/*
Copyright: 1998-2004 Tom Lear <tom@trap.mtview.ca.us>
2006 Mario Iseli <admin@marioiseli.com>
2010-2025 Stephen Kitt <skitt@debian.org>
2010-2022 Stephen Kitt <skitt@debian.org>
License: GPL-2+
Files:
@ -73,7 +73,7 @@ License: RSA
These notices must be retained in any copies of any part of this
documentation and/or software.
Files: src/lib/libpng/*
Files: src/libpng/*
Copyright: 1995-1996 Guy Eric Schalnat, Group 42, Inc.
1996-1997 Andreas Dilger
1998-2013 Glenn Randers-Pehrson
@ -112,7 +112,7 @@ License: libpng
risk of satisfactory quality, performance, accuracy, and effort is
with the user.
Files: src/os/libretro/libretro.h
Files: src/libretro/libretro.h
Copyright: 2010-2017 The RetroArch team
License: MIT
Permission is hereby granted, free of charge, to any person obtaining
@ -135,7 +135,7 @@ License: MIT
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Files: src/os/macos/*
Files: src/macos/*
Copyright: 2005-2006 Mark Grebe
License: GPL-2+
@ -164,7 +164,7 @@ License: GPL-2
License version 2 can be found in
`/usr/share/common-licenses/GPL-2'.
Files: src/lib/zlib/*
Files: src/zlib/*
Copyright: 1995-2012, 2016 Jean-loup Gailly and Mark Adler
License: zlib
This software is provided 'as-is', without any express or implied

2
debian/docs vendored
View File

@ -2,7 +2,7 @@ Announce.txt
Changes.txt
Copyright.txt
README-SDL.txt
README.md
Readme.txt
docs/*.html
docs/graphics/*.png
Todo.txt

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -15,7 +15,7 @@
<body>
<center><b><font size="7">Stella</font></b></center>
<center><h4><b>Release 7.0</b></h4></center>
<center><h4><b>Release 6.7.1</b></h4></center>
<center><h1><b>Integrated Debugger</b></h1></center>
<center><h4><b>(a work in progress)</b></h4></center>
<br>
@ -45,7 +45,6 @@
<li><a href="#PseudoRegisters">Pseudo-Registers</a></li>
<li><a href="#Watches">Watches</a></li>
<li><a href="#Traps">Traps</a></li>
<li><a href="#Timers">Timers</a></li>
</ul>
</li>
<li><a href="#SaveWork">Save your work!</a></li>
@ -311,7 +310,7 @@ more convenient.
</li>
<li>
"&lt;rom_filename&gt;.sym" (located in the same directory as the ROM)</br>
If you provide the -l and -s parameters DASM will create these two files during
If you provied the -l and -s parameters DASM will create these two files during
assembly. Stella uses the file content to display the correct labels.
</li>
</ul>
@ -859,22 +858,6 @@ can remove a trap with "delTrap number", where the number comes from
"listTraps" or by entering the identical trap again. You can get rid of
all traps at once with the "clearTraps" command.</p></p>
<h4><a name="Timers">Timers</a></h4>
<p>Timers are used to measure cycles used between two timer points. They
measure the minimum, maximum and average cycles executed between their two
timer points. Start and end points can be set via prompt command or ROM
Disassembly settings dialog.</p>
<p>If the timer point is defined without an address or bank, the current PC
address or bank are used. If a '+' is added, both timer points use address
mirrors too, a '*' triggers both timer points in any bank.</p>
<p>All timers can be shown in detail with "listTimers", a single timer
with "printTimer number". "resetTimers" allows resetting all timer statistics,
"delTimer number" removes a single timer. All timers can be deleted with the
"clearTimers" command.</p>
</br>
<h3><a name="SaveWork">Save your work!</a></h3>
<p>Stella offers several commands to save your work inside the debugger for
@ -962,7 +945,6 @@ Type "help 'cmd'" to see extended information about the given command.</p>
clearConfig - Clear DiStella config directives [bank xx]
clearHistory - Clear the prompt history
clearSaveStateIfs - Clear all saveState points
clearTimers - Clear all timers
clearTraps - Clear all traps
clearWatches - Clear all watches
cls - Clear prompt area of text
@ -977,7 +959,6 @@ clearSaveStateIfs - Clear all saveState points
delFunction - Delete function with label xx
delSaveStateIf - Delete conditional saveState point &lt;xx&gt;
delTrap - Delete trap &lt;xx&gt;
delTimer - Delete timer &lt;xx&gt;
delWatch - Delete watch &lt;xx&gt;
disAsm - Disassemble address xx [yy lines] (default=PC)
dump - Dump data at address &lt;xx&gt; [to yy] [1: memory; 2: CPU state; 4: input regs] [?]
@ -1007,17 +988,14 @@ clearSaveStateIfs - Clear all saveState points
loadAllStates - Load all emulator states
loadState - Load emulator state xx (0-9)
logBreaks - Logs breaks and traps and continues emulation
logTrace - Logs emulation (note: emulation may slow down and the log becomes huge soon)
n - Negative Flag: set (0 or 1), or toggle (no arg)
palette - Show current TIA palette
pc - Set Program Counter to address xx
pCol - Mark 'PCOL' range in disassembly
pGfx - Mark 'PGFX' range in disassembly
print - Evaluate/print expression xx in hex/dec/binary
printTimer - Print details of timer xx
ram - Show ZP RAM, or set address xx to yy1 [yy2 ...]
reset - Reset system to power-on state
resetTimers - Reset all timers' statistics
rewind - Rewind state by one or [xx] steps/traces/scanlines/frames...
riot - Show RIOT timer/input status
rom - Set ROM address xx to yy1 [yy2 ...]
@ -1039,9 +1017,7 @@ clearSaveStateIfs - Clear all saveState points
scanLine - Advance emulation by &lt;xx&gt; scanlines (default=1)
step - Single step CPU [with count xx]
stepWhile - Single step CPU while &lt;condition&gt; is true
swchb - Set SWCHB to value xx
tia - Show TIA state
timer - Set a timer point
trace - Single step CPU over subroutines [with count xx]
trap - Trap read/write access to address(es) xx [yy]
trapIf - On &lt;condition&gt; trap R/W access to address(es) xx [yy]
@ -1108,7 +1084,7 @@ VDELBL selects the register that is used to control the ball. This is
visualized in the debugger in the same way as the two copies of GRP0 and
GRP1</p>
<p>For many registers, writes don't take effect immediately as the
<p>For many registers, writes don't take effect immediatelly as the
TIA takes some color clocks to change state. In Stella's TIA core, this
is implemented by queueing the writes, and the contents of this queue
are visualized in the debugger in the "Queued Writes" area of the TIA tab.</p>
@ -1586,16 +1562,13 @@ matches the address of the disassembly line where the mouse was clicked.</li>
<li><b>Disassemble @ current line</b>: Disassemble from the disassembly line where the mouse was clicked.
This allows to fill gaps in the disassembly and is most useful for bankswitched ROMs.</li>
<li><b>Set timer @ current line</b>: Set a timer point using the current
disassembly line's address and bank</li>
<li><b>Show tentative code</b>: Allow DiStella to do a static analysis/disassembly.</li>
<li><b>Show PC addresses</b>: Show Program Counter addresses as labels (where there
isn't already a defined label).</li>
<li><b>Show GFX as binary</b>: Switch between displaying/editing GFX and PGFX sections
in either binary or hexadecimal.</li>
in either binary or hexidecimal.</li>
<li><b>Use address relocation</b>: Corresponds to the DiStella '-r' option
(Relocate calls out of address range).</br>

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

BIN
docs/graphics/show_all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

BIN
docs/graphics/show_roms.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@
<center><h1>Stella for RetroN 77</h1></center>
<center><h2>Atari 2600 VCS emulator</h2></center>
<center>Release 7.0</center>
<center>Release 6.7.1</center>
<center><h2>Quick Navigation Guide</h2></center>
<br/>
@ -95,17 +95,17 @@
<td>Open settings</td>
</tr>
<tr>
<td>Button 8</td>
<td>Button 7</td>
<td>-</td>
<td>Rewind game</td>
</tr>
<tr>
<td>Button 9</td>
<td>Button 8</td>
<td>MODE</td>
<td>Select</td>
</tr>
<tr>
<td>Button 10</td>
<td>Button 9</td>
<td>RESET</td>
<td>Reset</td>
</tr>
@ -160,21 +160,6 @@
Button 2 or 6 close the menu without any action.
</td>
</tr>
<tr>
<td>Button 1 + Left</td>
<td>-</td>
<td>Display previous image</td>
</tr>
<tr>
<td>Button 1 + Right</td>
<td>-</td>
<td>Display next image</td>
</tr>
<tr>
<td>Button 1 + Up</td>
<td>-</td>
<td>Toggle image zoom</td>
</tr>
<tr>
<td>Button 2</td>
<td>SKILL P2</td>
@ -264,7 +249,7 @@
<td width="41%">This dialog is similar to the PC version's
<a href="index.html#CommandMenu"><b>Command Menu</b></a>, but with some
commands especially selected for the RetroN&nbsp;77.</td>
<td><p><img src="graphics/commandsmenu_r77.png"></p></td>
<td><p><img src="commandsmenu_r77.png"></p></td>
</tr>
</table>
</p>
@ -309,7 +294,7 @@
</tr>
</table>
</td>
<td><p><img src="graphics/basic_settings.png"></p></td>
<td><p><img src="basic_settings.png"></p></td>
</tr>
</table>
</p>

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -21,19 +21,19 @@
#include "BankRomCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BankRomCheat::BankRomCheat(OSystem& os, string_view name, string_view code)
BankRomCheat::BankRomCheat(OSystem& os, const string& name, const string& code)
: Cheat(os, name, code)
{
if(myCode.length() == 7)
myCode = "0" + string{code};
myCode = "0" + code;
bank = BSPF::stoi<16>(myCode.substr(0, 2));
address = 0xf000 + BSPF::stoi<16>(myCode.substr(2, 3));
value = static_cast<uInt8>(BSPF::stoi<16>(myCode.substr(5, 2)));
count = static_cast<uInt8>(BSPF::stoi<16>(myCode.substr(7, 1)) + 1);
bank = unhex(myCode.substr(0, 2));
address = 0xf000 + unhex(myCode.substr(2, 3));
value = static_cast<uInt8>(unhex(myCode.substr(5, 2)));
count = static_cast<uInt8>(unhex(myCode.substr(7, 1)) + 1);
// Back up original data; we need this if the cheat is ever disabled
for(int i = 0; std::cmp_less(i, count); ++i)
for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem.console().cartridge().peek(address + i);
}
@ -50,8 +50,8 @@ bool BankRomCheat::disable()
const int oldBank = myOSystem.console().cartridge().getBank(address);
myOSystem.console().cartridge().bank(bank);
for(int i = 0; std::cmp_less(i, count); ++i)
myOSystem.console().cartridge().patch(address + i, savedRom[i]);
for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, savedRom[i]);
myOSystem.console().cartridge().bank(oldBank);
@ -66,7 +66,7 @@ void BankRomCheat::evaluate()
const int oldBank = myOSystem.console().cartridge().getBank(address);
myOSystem.console().cartridge().bank(bank);
for(int i = 0; std::cmp_less(i, count); ++i)
for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, value);
myOSystem.console().cartridge().bank(oldBank);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,7 +23,7 @@
class BankRomCheat : public Cheat
{
public:
BankRomCheat(OSystem& os, string_view name, string_view code);
BankRomCheat(OSystem& os, const string& name, const string& code);
~BankRomCheat() override = default;
bool enable() override;
@ -31,7 +31,7 @@ class BankRomCheat : public Cheat
void evaluate() override;
private:
std::array<uInt8, 16> savedRom{};
std::array<uInt8, 16> savedRom;
uInt16 address{0};
uInt8 value{0};
uInt8 count{0};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -25,9 +25,9 @@ class OSystem;
class Cheat
{
public:
Cheat(OSystem& osystem, string_view name, string_view code)
Cheat(OSystem& osystem, const string& name, const string& code)
: myOSystem{osystem},
myName{name.empty() ? code : name},
myName{name == "" ? code : name},
myCode{code} { }
virtual ~Cheat() = default;
@ -40,6 +40,23 @@ class Cheat
virtual void evaluate() = 0;
protected:
static uInt16 unhex(const string& hex)
{
int ret = 0;
for(const auto c: hex)
{
ret *= 16;
if(c >= '0' && c <= '9')
ret += c - '0';
else if(c >= 'A' && c <= 'F')
ret += c - 'A' + 10;
else
ret += c - 'a' + 10;
}
return ret;
}
protected:
OSystem& myOSystem;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -25,6 +25,7 @@
#include "Font.hxx"
#include "InputTextDialog.hxx"
#include "OSystem.hxx"
#include "Props.hxx"
#include "Widget.hxx"
#include "CheatCodeDialog.hxx"
@ -82,18 +83,18 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
// Inputbox which will pop up when adding/editing a cheat
StringList labels;
labels.emplace_back("Name ");
labels.emplace_back("Code (hex) ");
labels.push_back("Name ");
labels.push_back("Code (hex) ");
myCheatInput = make_unique<InputTextDialog>(this, font, labels, "Cheat code");
myCheatInput->setTarget(this);
// Add filtering for each textfield
const EditableWidget::TextFilter f0 = [](char c) {
EditableWidget::TextFilter f0 = [](char c) {
return isprint(c) && c != '\"' && c != ':';
};
myCheatInput->setTextFilter(f0, 0);
const EditableWidget::TextFilter f1 = [](char c) {
EditableWidget::TextFilter f1 = [](char c) {
return (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
};
myCheatInput->setTextFilter(f1, 1);
@ -110,7 +111,7 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheatCodeDialog::~CheatCodeDialog() // NOLINT (we need an empty d'tor)
CheatCodeDialog::~CheatCodeDialog()
{
}
@ -127,14 +128,14 @@ void CheatCodeDialog::loadConfig()
for(const auto& c: list)
{
l.push_back(c->name());
b.push_back(c->enabled());
b.push_back(bool(c->enabled()));
}
myCheatList->setList(l, b);
// Redraw the list, auto-selecting the first item if possible
myCheatList->setSelected(!l.empty() ? 0 : -1);
myCheatList->setSelected(l.size() > 0 ? 0 : -1);
const bool enabled = !list.empty();
const bool enabled = (list.size() > 0);
myEditButton->setEnabled(enabled);
myRemoveButton->setEnabled(enabled);
}
@ -232,7 +233,7 @@ void CheatCodeDialog::handleCommand(CommandSender* sender, int cmd,
{
const string& name = myCheatInput->getResult(0);
const string& code = myCheatInput->getResult(1);
if(CheatManager::isValidCode(code))
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().add(name, code);
@ -249,7 +250,7 @@ void CheatCodeDialog::handleCommand(CommandSender* sender, int cmd,
const string& code = myCheatInput->getResult(1);
const bool enable = myCheatList->getSelectedState();
const int idx = myCheatList->getSelected();
if(CheatManager::isValidCode(code))
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().add(name, code, enable, idx);
@ -272,7 +273,7 @@ void CheatCodeDialog::handleCommand(CommandSender* sender, int cmd,
{
const string& name = myCheatInput->getResult(0);
const string& code = myCheatInput->getResult(1);
if(CheatManager::isValidCode(code))
if(instance().cheat().isValidCode(code))
{
myCheatInput->close();
instance().cheat().addOneShot(name, code);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -16,6 +16,7 @@
//============================================================================
#include "OSystem.hxx"
#include "Console.hxx"
#include "Cheat.hxx"
#include "Settings.hxx"
#include "CheetahCheat.hxx"
@ -32,10 +33,10 @@ CheatManager::CheatManager(OSystem& osystem)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheatManager::add(string_view name, string_view code,
bool CheatManager::add(const string& name, const string& code,
bool enable, int idx)
{
const shared_ptr<Cheat> cheat = createCheat(name, code);
shared_ptr<Cheat> cheat = createCheat(name, code);
if(!cheat)
return false;
@ -78,7 +79,7 @@ void CheatManager::remove(int idx)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::addPerFrame(string_view name, string_view code, bool enable)
void CheatManager::addPerFrame(const string& name, const string& code, bool enable)
{
// The actual cheat will always be in the main list; we look there first
shared_ptr<Cheat> cheat;
@ -92,8 +93,8 @@ void CheatManager::addPerFrame(string_view name, string_view code, bool enable)
}
// Make sure there are no duplicates
bool found{false};
uInt32 i{0};
bool found = false;
uInt32 i;
for(i = 0; i < myPerFrameList.size(); ++i)
{
if(myPerFrameList[i]->code() == cheat->code())
@ -116,16 +117,16 @@ void CheatManager::addPerFrame(string_view name, string_view code, bool enable)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::addOneShot(string_view name, string_view code)
void CheatManager::addOneShot(const string& name, const string& code)
{
// Evaluate this cheat once, and then immediately discard it
const shared_ptr<Cheat> cheat = createCheat(name, code);
shared_ptr<Cheat> cheat = createCheat(name, code);
if(cheat)
cheat->evaluate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<Cheat> CheatManager::createCheat(string_view name, string_view code) const
shared_ptr<Cheat> CheatManager::createCheat(const string& name, const string& code) const
{
if(!isValidCode(code))
return nullptr;
@ -135,14 +136,14 @@ shared_ptr<Cheat> CheatManager::createCheat(string_view name, string_view code)
{
case 4: return make_shared<RamCheat>(myOSystem, name, code);
case 6: return make_shared<CheetahCheat>(myOSystem, name, code);
case 7: [[fallthrough]];
case 7: return make_shared<BankRomCheat>(myOSystem, name, code);
case 8: return make_shared<BankRomCheat>(myOSystem, name, code);
default: return nullptr;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::parse(string_view cheats)
void CheatManager::parse(const string& cheats)
{
StringList s;
string::size_type lastPos = cheats.find_first_not_of(',', 0);
@ -197,7 +198,7 @@ void CheatManager::parse(string_view cheats)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::enable(string_view code, bool enable)
void CheatManager::enable(const string& code, bool enable)
{
for(const auto& cheat: myCheatList)
{
@ -224,7 +225,7 @@ void CheatManager::loadCheatDatabase()
// Loop reading cheats
while(getline(in, line))
{
if(line.empty())
if(line.length() == 0)
continue;
const string::size_type one = line.find('\"', 0);
@ -255,14 +256,14 @@ void CheatManager::saveCheatDatabase()
stringstream out;
for(const auto& [md5, cheat]: myCheatMap)
out << "\"" << md5 << "\" " << "\"" << cheat << "\"\n";
out << "\"" << md5 << "\" " << "\"" << cheat << "\"" << endl;
try { myOSystem.cheatFile().write(out); }
catch(...) { return; }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::loadCheats(string_view md5sum)
void CheatManager::loadCheats(const string& md5sum)
{
myPerFrameList.clear();
myCheatList.clear();
@ -271,11 +272,11 @@ void CheatManager::loadCheats(string_view md5sum)
// Set up any cheatcodes that was on the command line
// (and remove the key from the settings, so they won't get set again)
const string& cheats = myOSystem.settings().getString("cheat");
if(!cheats.empty())
if(cheats != "")
myOSystem.settings().setValue("cheat", "");
const auto& iter = myCheatMap.find(md5sum);
if(iter == myCheatMap.end() && cheats.empty())
if(iter == myCheatMap.end() && cheats == "")
return;
// Remember the cheats for this ROM
@ -286,7 +287,7 @@ void CheatManager::loadCheats(string_view md5sum)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheatManager::saveCheats(string_view md5sum)
void CheatManager::saveCheats(const string& md5sum)
{
ostringstream cheats;
for(uInt32 i = 0; i < myCheatList.size(); ++i)
@ -298,7 +299,7 @@ void CheatManager::saveCheats(string_view md5sum)
cheats << ",";
}
const bool changed = cheats.view() != myCurrentCheat;
bool changed = cheats.str() != myCurrentCheat;
// Only update the list if absolutely necessary
if(changed)
@ -310,8 +311,8 @@ void CheatManager::saveCheats(string_view md5sum)
myCheatMap.erase(iter);
// Add new entry only if there are any cheats defined
if(!cheats.view().empty())
myCheatMap.emplace(md5sum, cheats.view());
if(cheats.str() != "")
myCheatMap.emplace(md5sum, cheats.str());
}
// Update the dirty flag
@ -321,7 +322,7 @@ void CheatManager::saveCheats(string_view md5sum)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheatManager::isValidCode(string_view code)
bool CheatManager::isValidCode(const string& code) const
{
for(const auto c: code)
if(!isxdigit(c))

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -38,7 +38,6 @@ class CheatManager
{
public:
explicit CheatManager(OSystem& osystem);
~CheatManager() = default;
/**
Adds the specified cheat to an internal list.
@ -50,7 +49,7 @@ class CheatManager
@return Whether the cheat was created and enabled.
*/
bool add(string_view name, string_view code,
bool add(const string& name, const string& code,
bool enable = true, int idx = -1);
/**
@ -69,7 +68,7 @@ class CheatManager
@param code The actual cheatcode
@param enable Add or remove the cheat to the per-frame list
*/
void addPerFrame(string_view name, string_view code, bool enable);
void addPerFrame(const string& name, const string& code, bool enable);
/**
Creates and enables a one-shot cheat. One-shot cheats are the
@ -79,7 +78,7 @@ class CheatManager
@param name Name of the cheat (not absolutely required)
@param code The actual cheatcode (in hex)
*/
void addOneShot(string_view name, string_view code);
void addOneShot(const string& name, const string& code);
/**
Enable/disabled the cheat specified by the given code.
@ -87,7 +86,7 @@ class CheatManager
@param code The actual cheatcode to search for
@param enable Enable/disable the cheat
*/
void enable(string_view code, bool enable);
void enable(const string& code, bool enable);
/**
Returns the game cheatlist.
@ -112,17 +111,17 @@ class CheatManager
/**
Load cheats for ROM with given MD5sum to cheatlist(s).
*/
void loadCheats(string_view md5sum);
void loadCheats(const string& md5sum);
/**
Saves cheats for ROM with given MD5sum to cheat map.
*/
void saveCheats(string_view md5sum);
void saveCheats(const string& md5sum);
/**
Checks if a code is valid.
*/
static bool isValidCode(string_view code);
bool isValidCode(const string& code) const;
private:
/**
@ -133,14 +132,14 @@ class CheatManager
@return The cheat (if was created), else nullptr.
*/
shared_ptr<Cheat> createCheat(string_view name, string_view code) const;
shared_ptr<Cheat> createCheat(const string& name, const string& code) const;
/**
Parses a list of cheats and adds/enables each one.
@param cheats Comma-separated list of cheats (without any names)
*/
void parse(string_view cheats);
void parse(const string& cheats);
private:
OSystem& myOSystem;
@ -148,7 +147,7 @@ class CheatManager
CheatList myCheatList;
CheatList myPerFrameList;
std::map<string,string, std::less<>> myCheatMap;
std::map<string,string> myCheatMap;
string myCheatFile;
// This is set each time a new cheat/ROM is loaded, for later

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -21,14 +21,14 @@
#include "CheetahCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheetahCheat::CheetahCheat(OSystem& os, string_view name, string_view code)
CheetahCheat::CheetahCheat(OSystem& os, const string& name, const string& code)
: Cheat(os, name, code),
address{static_cast<uInt16>(0xf000 + BSPF::stoi<16>(code.substr(0, 3)))},
value{static_cast<uInt8>(BSPF::stoi<16>(code.substr(3, 2)))},
count{static_cast<uInt8>(BSPF::stoi<16>(code.substr(5, 1)) + 1)}
address{static_cast<uInt16>(0xf000 + unhex(code.substr(0, 3)))},
value{static_cast<uInt8>(unhex(code.substr(3, 2)))},
count{static_cast<uInt8>(unhex(code.substr(5, 1)) + 1)}
{
// Back up original data; we need this if the cheat is ever disabled
for(uInt8 i = 0; i < count; ++i)
for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem.console().cartridge().peek(address + i);
}
@ -42,7 +42,7 @@ bool CheetahCheat::enable()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheetahCheat::disable()
{
for(uInt8 i = 0; i < count; ++i)
for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, savedRom[i]);
return myEnabled = false;
@ -53,7 +53,7 @@ void CheetahCheat::evaluate()
{
if(!myEnabled)
{
for(uInt8 i = 0; i < count; ++i)
for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, value);
myEnabled = true;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,7 +23,7 @@
class CheetahCheat : public Cheat
{
public:
CheetahCheat(OSystem& os, string_view name, string_view code);
CheetahCheat(OSystem& os, const string& name, const string& code);
~CheetahCheat() override = default;
bool enable() override;
@ -31,7 +31,7 @@ class CheetahCheat : public Cheat
void evaluate() override;
private:
std::array<uInt8, 16> savedRom{};
std::array<uInt8, 16> savedRom;
uInt16 address{0};
uInt8 value{0};
uInt8 count{0};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,10 +23,10 @@
#include "RamCheat.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamCheat::RamCheat(OSystem& os, string_view name, string_view code)
RamCheat::RamCheat(OSystem& os, const string& name, const string& code)
: Cheat(os, name, code),
address{static_cast<uInt16>(BSPF::stoi<16>(myCode.substr(0, 2)))},
value{static_cast<uInt8>(BSPF::stoi<16>(myCode.substr(2, 2)))}
address{static_cast<uInt16>(unhex(myCode.substr(0, 2)))},
value{static_cast<uInt8>(unhex(myCode.substr(2, 2)))}
{
}
@ -55,5 +55,5 @@ bool RamCheat::disable()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamCheat::evaluate()
{
myOSystem.console().system().pokeOob(address, value);
myOSystem.console().system().poke(address, value);
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,7 +23,7 @@
class RamCheat : public Cheat
{
public:
RamCheat(OSystem& os, string_view name, string_view code);
RamCheat(OSystem& os, const string& name, const string& code);
~RamCheat() override = default;
bool enable() override;

View File

@ -7,10 +7,8 @@ MODULE_OBJS := \
src/cheat/BankRomCheat.o \
src/cheat/RamCheat.o
MODULE_TEST_OBJS =
MODULE_DIRS += \
src/cheat
# Include common rules
# Include common rules
include $(srcdir)/common.rules

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -29,20 +29,16 @@ AudioQueue::AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo)
{
const uInt8 sampleSize = myIsStereo ? 2 : 1;
myFragmentBuffer = make_unique<Int16[]>(
static_cast<size_t>(myFragmentSize) * sampleSize * (capacity + 2));
myFragmentBuffer = make_unique<Int16[]>(myFragmentSize * sampleSize * (capacity + 2));
for (uInt32 i = 0; i < capacity; ++i)
myFragmentQueue[i] = myAllFragments[i] = myFragmentBuffer.get() +
static_cast<size_t>(myFragmentSize) * sampleSize * i;
myFragmentQueue[i] = myAllFragments[i] = myFragmentBuffer.get() + i * sampleSize * myFragmentSize;
myAllFragments[capacity] = myFirstFragmentForEnqueue =
myFragmentBuffer.get() + static_cast<size_t>(myFragmentSize) * sampleSize *
capacity;
myFragmentBuffer.get() + capacity * sampleSize * myFragmentSize;
myAllFragments[capacity + 1] = myFirstFragmentForDequeue =
myFragmentBuffer.get() + static_cast<size_t>(myFragmentSize) * sampleSize *
(capacity + 1);
myFragmentBuffer.get() + (capacity + 1) * sampleSize * myFragmentSize;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -54,7 +50,7 @@ uInt32 AudioQueue::capacity() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 AudioQueue::size() const
{
const lock_guard<mutex> guard(myMutex);
lock_guard<mutex> guard(myMutex);
return mySize;
}
@ -74,7 +70,7 @@ uInt32 AudioQueue::fragmentSize() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int16* AudioQueue::enqueue(Int16* fragment)
{
const lock_guard<mutex> guard(myMutex);
lock_guard<mutex> guard(myMutex);
Int16* newFragment = nullptr;
@ -87,7 +83,7 @@ Int16* AudioQueue::enqueue(Int16* fragment)
return newFragment;
}
const auto capacity = static_cast<uInt8>(myFragmentQueue.size());
const uInt8 capacity = static_cast<uInt8>(myFragmentQueue.size());
const uInt8 fragmentIndex = (myNextFragment + mySize) % capacity;
newFragment = myFragmentQueue.at(fragmentIndex);
@ -105,7 +101,7 @@ Int16* AudioQueue::enqueue(Int16* fragment)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int16* AudioQueue::dequeue(Int16* fragment)
{
const lock_guard<mutex> guard(myMutex);
lock_guard<mutex> guard(myMutex);
if (mySize == 0) return nullptr;
@ -128,7 +124,7 @@ Int16* AudioQueue::dequeue(Int16* fragment)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioQueue::closeSink(Int16* fragment)
{
const lock_guard<mutex> guard(myMutex);
lock_guard<mutex> guard(myMutex);
if (myFirstFragmentForDequeue && fragment)
throw runtime_error("attempt to return unknown buffer on closeSink");

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -24,7 +24,7 @@
#include "StaggeredLogger.hxx"
/**
This class implements an audio queue that acts both like a ring buffer
This class implements a an audio queue that acts both like a ring buffer
and a pool of audio fragments. The TIA emulation core fills a fragment
with samples and then returns it to the queue, receiving a new fragment
in return. The sound driver removes fragments for playback from the
@ -46,7 +46,6 @@ class AudioQueue
@param isStereo Whether samples are stereo or mono.
*/
AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo);
~AudioQueue() = default;
/**
Capacity getter.
@ -134,6 +133,7 @@ class AudioQueue
StaggeredLogger myOverflowLogger{"audio buffer overflow", Logger::Level::INFO};
private:
AudioQueue() = delete;
AudioQueue(const AudioQueue&) = delete;
AudioQueue(AudioQueue&&) = delete;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -35,11 +35,11 @@ namespace {
constexpr AudioSettings::ResamplingQuality normalizeResamplingQuality(int numericResamplingQuality)
{
return (
numericResamplingQuality >= static_cast<int>(AudioSettings::ResamplingQuality::nearestNeighbour) &&
numericResamplingQuality >= static_cast<int>(AudioSettings::ResamplingQuality::nearestNeightbour) &&
numericResamplingQuality <= static_cast<int>(AudioSettings::ResamplingQuality::lanczos_3)
) ? static_cast<AudioSettings::ResamplingQuality>(numericResamplingQuality) : AudioSettings::DEFAULT_RESAMPLING_QUALITY;
}
} // namespace
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AudioSettings::AudioSettings(Settings& settings)
@ -51,10 +51,9 @@ AudioSettings::AudioSettings(Settings& settings)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::normalize(Settings& settings)
{
const int settingPreset = settings.getInt(SETTING_PRESET);
int settingPreset = settings.getInt(SETTING_PRESET);
const Preset preset = normalizedPreset(settingPreset);
if (static_cast<int>(preset) != settingPreset)
settings.setValue(SETTING_PRESET, static_cast<int>(DEFAULT_PRESET));
if (static_cast<int>(preset) != settingPreset) settings.setValue(SETTING_PRESET, static_cast<int>(DEFAULT_PRESET));
switch (settings.getInt(SETTING_SAMPLE_RATE)) {
case 44100:
@ -81,19 +80,18 @@ void AudioSettings::normalize(Settings& settings)
break;
}
const int settingBufferSize = settings.getInt(SETTING_BUFFER_SIZE);
int settingBufferSize = settings.getInt(SETTING_BUFFER_SIZE);
if (settingBufferSize < 0 || settingBufferSize > MAX_BUFFER_SIZE) settings.setValue(SETTING_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
const int settingHeadroom = settings.getInt(SETTING_HEADROOM);
int settingHeadroom = settings.getInt(SETTING_HEADROOM);
if (settingHeadroom < 0 || settingHeadroom > MAX_HEADROOM) settings.setValue(SETTING_HEADROOM, DEFAULT_HEADROOM);
const int settingResamplingQuality = settings.getInt(SETTING_RESAMPLING_QUALITY);
const ResamplingQuality resamplingQuality =
normalizeResamplingQuality(settingResamplingQuality);
int settingResamplingQuality = settings.getInt(SETTING_RESAMPLING_QUALITY);
const ResamplingQuality resamplingQuality = normalizeResamplingQuality(settingResamplingQuality);
if (static_cast<int>(resamplingQuality) != settingResamplingQuality)
settings.setValue(SETTING_RESAMPLING_QUALITY, static_cast<int>(DEFAULT_RESAMPLING_QUALITY));
const int settingVolume = settings.getInt(SETTING_VOLUME);
int settingVolume = settings.getInt(SETTING_VOLUME);
if (settingVolume < 0 || settingVolume > 100) settings.setValue(SETTING_VOLUME, DEFAULT_VOLUME);
}
@ -188,7 +186,7 @@ void AudioSettings::setPreset(AudioSettings::Preset preset)
myPresetFragmentSize = 1024;
myPresetBufferSize = 6;
myPresetHeadroom = 5;
myPresetResamplingQuality = ResamplingQuality::nearestNeighbour;
myPresetResamplingQuality = ResamplingQuality::nearestNeightbour;
break;
case Preset::highQualityMediumLag:

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -26,7 +26,7 @@ class AudioSettings
{
public:
enum class Preset: uInt8 {
enum class Preset {
custom = 1,
lowQualityMediumLag = 2,
highQualityMediumLag = 3,
@ -34,23 +34,23 @@ class AudioSettings
ultraQualityMinimalLag = 5
};
enum class ResamplingQuality: uInt8 {
nearestNeighbour = 1,
lanczos_2 = 2,
lanczos_3 = 3
enum class ResamplingQuality {
nearestNeightbour = 1,
lanczos_2 = 2,
lanczos_3 = 3
};
static constexpr string_view SETTING_PRESET = "audio.preset";
static constexpr string_view SETTING_SAMPLE_RATE = "audio.sample_rate";
static constexpr string_view SETTING_FRAGMENT_SIZE = "audio.fragment_size";
static constexpr string_view SETTING_BUFFER_SIZE = "audio.buffer_size";
static constexpr string_view SETTING_HEADROOM = "audio.headroom";
static constexpr string_view SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
static constexpr string_view SETTING_STEREO = "audio.stereo";
static constexpr string_view SETTING_VOLUME = "audio.volume";
static constexpr string_view SETTING_DEVICE = "audio.device";
static constexpr string_view SETTING_ENABLED = "audio.enabled";
static constexpr string_view SETTING_DPC_PITCH = "audio.dpc_pitch";
static constexpr const char* SETTING_PRESET = "audio.preset";
static constexpr const char* SETTING_SAMPLE_RATE = "audio.sample_rate";
static constexpr const char* SETTING_FRAGMENT_SIZE = "audio.fragment_size";
static constexpr const char* SETTING_BUFFER_SIZE = "audio.buffer_size";
static constexpr const char* SETTING_HEADROOM = "audio.headroom";
static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
static constexpr const char* SETTING_STEREO = "audio.stereo";
static constexpr const char* SETTING_VOLUME = "audio.volume";
static constexpr const char* SETTING_DEVICE = "audio.device";
static constexpr const char* SETTING_ENABLED = "audio.enabled";
static constexpr const char* SETTING_DPC_PITCH = "audio.dpc_pitch";
static constexpr Preset DEFAULT_PRESET = Preset::highQualityMediumLag;
static constexpr uInt32 DEFAULT_SAMPLE_RATE = 44100;
@ -127,7 +127,7 @@ class AudioSettings
private:
Settings& mySettings; // NOLINT: we want a reference here
Settings& mySettings;
Preset myPreset{Preset::custom};
@ -135,7 +135,7 @@ class AudioSettings
uInt32 myPresetFragmentSize{0};
uInt32 myPresetBufferSize{0};
uInt32 myPresetHeadroom{0};
ResamplingQuality myPresetResamplingQuality{ResamplingQuality::nearestNeighbour};
ResamplingQuality myPresetResamplingQuality{ResamplingQuality::nearestNeightbour};
bool myIsPersistent{true};
};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -24,7 +24,7 @@ string Base::toString(int value, Common::Base::Fmt outputBase)
{
static char vToS_buf[32]; // NOLINT : One place where C-style is acceptable
if(outputBase == Base::Fmt::DEFAULT)
if(outputBase == Base::Fmt::_DEFAULT)
outputBase = myDefaultBase;
switch(outputBase)
@ -47,83 +47,74 @@ string Base::toString(int value, Common::Base::Fmt outputBase)
case Base::Fmt::_10: // base 10: 3 or 5 bytes (depending on value)
if(value > -0x100 && value < 0x100)
std::ignore = std::snprintf(vToS_buf, 5, "%3d", static_cast<Int16>(value));
std::snprintf(vToS_buf, 5, "%3d", static_cast<Int16>(value));
else
std::ignore = std::snprintf(vToS_buf, 6, "%5d", value);
std::snprintf(vToS_buf, 6, "%5d", value);
break;
case Base::Fmt::_10_02: // base 10: 2 digits (with leading zero)
std::ignore = std::snprintf(vToS_buf, 3, "%02d", value);
std::snprintf(vToS_buf, 3, "%02d", value);
break;
case Base::Fmt::_10_3: // base 10: 3 digits
std::ignore = std::snprintf(vToS_buf, 4, "%3d", value);
std::snprintf(vToS_buf, 4, "%3d", value);
break;
case Base::Fmt::_10_4: // base 10: 4 digits
std::ignore = std::snprintf(vToS_buf, 5, "%4d", value);
std::snprintf(vToS_buf, 5, "%4d", value);
break;
case Base::Fmt::_10_5: // base 10: 5 digits
std::ignore = std::snprintf(vToS_buf, 6, "%5d", value);
std::snprintf(vToS_buf, 6, "%5d", value);
break;
case Base::Fmt::_10_6: // base 10: 6 digits
std::ignore = std::snprintf(vToS_buf, 7, "%6d", value);
std::snprintf(vToS_buf, 7, "%6d", value);
break;
case Base::Fmt::_10_8: // base 10: 8 digits
std::ignore = std::snprintf(vToS_buf, 9, "%8d", value);
std::snprintf(vToS_buf, 9, "%8d", value);
break;
case Base::Fmt::_16_1: // base 16: 1 byte wide
std::ignore = std::snprintf(
vToS_buf, 2, hexUppercase() ? "%1X" : "%1x", value);
std::snprintf(vToS_buf, 2, hexUppercase() ? "%1X" : "%1x", value);
break;
case Base::Fmt::_16_2: // base 16: 2 bytes wide
std::ignore = std::snprintf(
vToS_buf, 3, hexUppercase() ? "%02X" : "%02x", value);
std::snprintf(vToS_buf, 3, hexUppercase() ? "%02X" : "%02x", value);
break;
case Base::Fmt::_16_2_2:
std::ignore = std::snprintf(
vToS_buf, 6, hexUppercase() ? "%02X.%02X" : "%02x.%02x",
value >> 8, value & 0xff );
std::snprintf(vToS_buf, 6, hexUppercase() ? "%02X.%02X" : "%02x.%02x",
value >> 8, value & 0xff );
break;
case Base::Fmt::_16_3_2:
std::ignore = std::snprintf(
vToS_buf, 7, hexUppercase() ? "%03X.%02X" : "%03x.%02x",
value >> 8, value & 0xff );
std::snprintf(vToS_buf, 7, hexUppercase() ? "%03X.%02X" : "%03x.%02x",
value >> 8, value & 0xff );
break;
case Base::Fmt::_16_4: // base 16: 4 bytes wide
std::ignore = std::snprintf(
vToS_buf, 5, hexUppercase() ? "%04X" : "%04x", value);
std::snprintf(vToS_buf, 5, hexUppercase() ? "%04X" : "%04x", value);
break;
case Base::Fmt::_16_8: // base 16: 8 bytes wide
std::ignore = std::snprintf(
vToS_buf, 9, hexUppercase() ? "%08X" : "%08x", value);
std::snprintf(vToS_buf, 9, hexUppercase() ? "%08X" : "%08x", value);
break;
case Base::Fmt::_16: // base 16: 2, 4, 8 bytes (depending on value)
default:
if(value < 0x100)
std::ignore = std::snprintf(
vToS_buf, 3, hexUppercase() ? "%02X" : "%02x", value);
std::snprintf(vToS_buf, 3, hexUppercase() ? "%02X" : "%02x", value);
else if(value < 0x10000)
std::ignore = std::snprintf(
vToS_buf, 5, hexUppercase() ? "%04X" : "%04x", value);
std::snprintf(vToS_buf, 5, hexUppercase() ? "%04X" : "%04x", value);
else
std::ignore = std::snprintf(
vToS_buf, 9, hexUppercase() ? "%08X" : "%08x", value);
std::snprintf(vToS_buf, 9, hexUppercase() ? "%08X" : "%08x", value);
break;
}
return {vToS_buf};
return string(vToS_buf);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Base::Fmt Base::myDefaultBase = Base::Fmt::_16;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::ios_base::fmtflags Base::myHexflags = std::ios_base::hex; // NOLINT
std::ios_base::fmtflags Base::myHexflags = std::ios_base::hex;
} // namespace Common
} // Namespace Common

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -38,7 +38,7 @@ class Base
// The base to use for conversion from integers to strings
// Note that the actual number of places will be determined by
// the magnitude of the value itself in the general case
enum class Fmt: uInt8 {
enum class Fmt {
_16, // base 16: 2, 4, 8 bytes (depending on value)
_16_1, // base 16: 1 byte wide
_16_2, // base 16: 2 bytes wide
@ -56,7 +56,7 @@ class Base
_2, // base 2: 8 or 16 bits (depending on value)
_2_8, // base 2: 1 byte (8 bits) wide
_2_16, // base 2: 2 bytes (16 bits) wide
DEFAULT
_DEFAULT
};
public:
@ -72,31 +72,31 @@ class Base
static bool hexUppercase() { return myHexflags & std::ios_base::uppercase; }
/** Output HEX digits in 0.5/1/2/4 byte format */
static std::ostream& HEX1(std::ostream& os) {
static inline std::ostream& HEX1(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(1);
}
static std::ostream& HEX2(std::ostream& os) {
static inline std::ostream& HEX2(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(2) << std::setfill('0');
}
static std::ostream& HEX3(std::ostream& os)
static inline std::ostream& HEX3(std::ostream& os)
{
os.flags(myHexflags);
return os << std::setw(3) << std::setfill('0');
}
static std::ostream& HEX4(std::ostream& os) {
static inline std::ostream& HEX4(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(4) << std::setfill('0');
}
static std::ostream& HEX8(std::ostream& os) {
static inline std::ostream& HEX8(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(8) << std::setfill('0');
}
/** Convert integer to a string in the given base format */
static string toString(int value,
Common::Base::Fmt outputBase = Common::Base::Fmt::DEFAULT);
Common::Base::Fmt outputBase = Common::Base::Fmt::_DEFAULT);
private:
// Default format to use when none is specified
@ -108,7 +108,6 @@ class Base
private:
// Following constructors and assignment operators not supported
Base() = delete;
~Base() = delete;
Base(const Base&) = delete;
Base(Base&&) = delete;
Base& operator=(const Base&) = delete;

View File

@ -1,252 +0,0 @@
//============================================================================
//
// 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-2025 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.
//============================================================================
#include <cmath>
#include "OSystem.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "FBSurface.hxx"
#include "PNGLibrary.hxx"
#include "PropsSet.hxx"
#include "Bezel.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Bezel::Bezel(OSystem& osystem)
: myOSystem{osystem},
myFB{osystem.frameBuffer()}
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Bezel::getName(const string& path, const Properties& props)
{
string imageName;
int index = 1; // skip property name
do
{
imageName = getName(props, index);
if(imageName != EmptyString)
{
// Note: JPG does not support transparency
const string imagePath = path + imageName + ".png";
const FSNode node(imagePath);
if(node.exists())
break;
}
} while(index != -1);
return imageName;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Bezel::getName(const Properties& props, int& index)
{
if(++index == 1)
return props.get(PropType::Bezel_Name);
// Try to generate bezel name from cart name
const string& cartName = props.get(PropType::Cart_Name);
size_t pos = cartName.find_first_of('(');
if(pos == std::string::npos)
pos = cartName.length() + 1;
if(index < 10 && pos != std::string::npos && pos > 0)
{
// The following suffixes are from "The Official No-Intro Convention",
// covering all used combinations by "The Bezel Project" (except single ones)
// (Unl) = unlicensed (Homebrews)
const std::array<string, 8> suffixes = {
" (USA)", " (USA) (Proto)", " (USA) (Unl)", " (USA) (Hack)",
" (Europe)", " (Germany)", " (France) (Unl)", " (Australia)"
};
return cartName.substr(0, pos - 1) + suffixes[index - 2];
}
if(index == 10)
{
return "Atari-2600";
}
if(index == 11)
{
index = -1;
return "default";
}
return "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Bezel::getName(int& index) const
{
return getName(myOSystem.console().properties(), index);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Bezel::borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const
{
uInt32 *pixels{nullptr}, pitch{0};
mySurface->basePtr(pixels, pitch);
pixels += x + y * pitch;
for(uInt32 i = 0; i < size; ++i, pixels += step)
{
uInt8 r{0}, g{0}, b{0}, a{0};
myFB.getRGBA(*pixels, &r, &g, &b, &a);
if(a < 255) // transparent pixel?
return i;
}
return size - 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Bezel::load()
{
const Settings& settings = myOSystem.settings();
bool isValid = false;
string imageName;
#ifdef IMAGE_SUPPORT
const bool show = myOSystem.eventHandler().inTIAMode() &&
settings.getBool("bezel.show") &&
(settings.getBool("fullscreen") ||
settings.getBool("bezel.windowed"));
if(show)
{
if(!mySurface)
mySurface = myFB.allocateSurface(1, 1); // dummy size
try
{
const string& path = myOSystem.bezelDir().getPath();
VariantList metaData;
int index = 0;
do
{
imageName = getName(index);
if(imageName != EmptyString)
{
// Note: JPG does not support transparency
const string imagePath = path + imageName + ".png";
const FSNode node(imagePath);
if(node.exists())
{
isValid = true;
myOSystem.png().loadImage(imagePath, *mySurface, metaData);
break;
}
}
} while(index != -1);
}
catch(const runtime_error&) { cerr << "ERROR: Bezel load\n"; }
}
#else
const bool show = false;
#endif
if(isValid)
{
const Int32 w = mySurface->width();
const Int32 h = mySurface->height();
uInt32 top{0}, bottom{0}, left{0}, right{0};
if(settings.getBool("bezel.win.auto"))
{
// Determine transparent window inside bezel image
const uInt32 xCenter = w >> 1;
top = borderSize(xCenter, 0, h, w);
bottom = h - 1 - borderSize(xCenter, h - 1, h, -w);
const uInt32 yCenter = (bottom + top) >> 1;
left = borderSize(0, yCenter, w, 1);
right = w - 1 - borderSize(w - 1, yCenter, w, -1);
}
else
{
// BP: 13, 13, 0, 0%
// HY: 12, 12, 0, 0%
// P1: 25, 25, 11, 22%
// P2: 23, 23, 7, 20%
left = std::min(w - 1, static_cast<Int32>(w * settings.getInt("bezel.win.left") / 100. + .5)); // NOLINT
right = w - 1 - std::min(w - 1, static_cast<Int32>(w * settings.getInt("bezel.win.right") / 100. + .5)); // NOLINT
top = std::min(h - 1, static_cast<Int32>(h * settings.getInt("bezel.win.top") / 100. + .5)); // NOLINT
bottom = h - 1 - std::min(h - 1, static_cast<Int32>(h * settings.getInt("bezel.win.bottom") / 100. + .5)); // NOLINT
}
//cerr << (int)(right - left + 1) << " x " << (int)(bottom - top + 1) << " = "
// << double((int)(right - left + 1)) / double((int)(bottom - top + 1));
// Disable bezel is no transparent window was found
if(left < right && top < bottom)
myInfo = Info(Common::Size(w, h), Common::Rect(left, top, right, bottom));
else
{
if(mySurface)
myFB.deallocateSurface(mySurface);
mySurface = nullptr;
myInfo = Info();
myFB.showTextMessage("Invalid bezel image ('" + imageName + "')!");
isValid = false;
}
}
else
{
myInfo = Info();
if(show)
myFB.showTextMessage("No bezel image found");
}
return isValid;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Bezel::apply()
{
if(isShown())
{
const uInt32 bezelW =
std::min(myFB.screenSize().w,
static_cast<uInt32>(std::round(myFB.imageRect().w() * myInfo.ratioW())));
const uInt32 bezelH =
std::min(myFB.screenSize().h,
static_cast<uInt32>(std::round(myFB.imageRect().h() * myInfo.ratioH())));
// Position and scale bezel
mySurface->setDstSize(bezelW, bezelH);
mySurface->setDstPos((myFB.screenSize().w - bezelW) / 2, // center
(myFB.screenSize().h - bezelH) / 2);
mySurface->setScalingInterpolation(ScalingInterpolation::sharp);
// Note: Variable bezel window positions are handled in VideoModeHandler::Mode
// Enable blending to allow overlaying the bezel over the TIA output
mySurface->attributes().blending = true;
mySurface->attributes().blendalpha = 100;
mySurface->applyAttributes();
mySurface->setVisible(true);
}
else
if(mySurface)
mySurface->setVisible(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Bezel::render()
{
if(mySurface)
mySurface->render();
}

View File

@ -1,151 +0,0 @@
//============================================================================
//
// 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-2025 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 BEZEL_HXX
#define BEZEL_HXX
class OSystem;
class FBSurface;
class FrameBuffer;
class Properties;
#include "Rect.hxx"
/**
This class handles the bezels.
Bezels are loaded using a file name which is either a bezel name property or
is autogenerated from the cart name property. The bezels can be any size and
their transparent emulation window can be at any position. The position of
the window can be determined automatically.
+--------------------------------------+
| | display.h
+--------------------------------------+
| |
| +---------------+ |
| | window | |
| | | |
| | tia.h * zoom | |
| | | | bezel.h * zoom
| | | |
| +---------------+ |
| |
+--------------------------------------+ size
| |
+--------------------------------------+
The bezel and window sizes and their ratios are used for correct scaling.
@author Thomas Jentzsch
*/
class Bezel
{
public:
explicit Bezel(OSystem& osystem);
~Bezel() = default;
struct Info
{
private:
bool _isShown{false}; // Is bezel shown?
Common::Size _size{1, 1}; // Bezel size
Common::Rect _window{1, 1}; // Area of transparent TIA window inside bezel
public:
explicit Info() = default;
explicit Info(Common::Size size, Common::Rect window)
: _isShown{true}, _size{size}, _window{window} { }
bool isShown() const { return _isShown; }
Common::Size size() const { return _size; }
Common::Rect window() const { return _window; }
// Ratios between bezel sizes and TIA window sizes
double ratioW() const { return static_cast<double>(size().w) / window().w(); }
double ratioH() const { return static_cast<double>(size().h) / window().h(); }
};
// Structure access methods
const Info& info() const { return myInfo; }
bool isShown() const { return myInfo.isShown(); }
Common::Size size() const { return myInfo.size(); }
Common::Rect window() const { return myInfo.window(); }
// Ratio between bezel size and TIA window size
double ratioW() const { return myInfo.ratioW(); }
double ratioH() const { return myInfo.ratioH(); }
/*
Calculate size of a bezel border.
*/
uInt32 borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const;
/*
Load the bezel.
*/
bool load();
/*
Display scaled bezel.
*/
void apply();
/*
Render bezel surface
*/
void render();
/*
Generate bezel file name.
*/
static string getName(const string& path, const Properties& props);
private:
/*
Generate bezel file name.
*/
static string getName(const Properties& props, int& index);
/*
Generate bezel file name.
*/
string getName(int& index) const;
private:
// The parent system for the bezel
OSystem& myOSystem;
// Pointer to the FrameBuffer object
FrameBuffer& myFB;
// The bezel surface which blends over the TIA surface
shared_ptr<FBSurface> mySurface;
// Bezel info structure
Info myInfo;
private:
// Following constructors and assignment operators not supported
Bezel() = delete;
Bezel(const Bezel&) = delete;
Bezel(Bezel&&) = delete;
Bezel& operator=(const Bezel&) = delete;
Bezel& operator=(Bezel&&) = delete;
};
#endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,8 +23,6 @@
#include "Settings.hxx"
#include "StateManager.hxx"
#include "TIA.hxx"
#include "Cart.hxx"
#include "CartELF.hxx"
#include "DevSettingsHandler.hxx"
@ -43,17 +41,12 @@ void DevSettingsHandler::loadSettings(SettingsSet set)
myFrameStats[set] = settings.getBool(prefix + "stats");
myDetectedInfo[set] = settings.getBool(prefix + "detectedinfo");
// AtariVox/SaveKey/PlusROM access
myExternAccess[set] = settings.getBool(prefix + "extaccess");
myConsole[set] = settings.getString(prefix + "console") == "7800" ? 1 : 0;
myPlusROM[set] = devSettings ? settings.getBool("dev.plusroms.on") : true;
// Randomization
myRandomBank[set] = settings.getBool(prefix + "bankrandom");
myRandomizeTIA[set] = settings.getBool(prefix + "tiarandom");
myRandomizeRAM[set] = settings.getBool(prefix + "ramrandom");
myRandomizeCPU[set] = settings.getString(prefix + "cpurandom");
// Random hotspot peeks
myRandomHotspots[set] = devSettings ? settings.getBool("dev.hsrandom") : false;
// Undriven TIA pins
myUndrivenPins[set] = devSettings ? settings.getBool("dev.tiadriven") : false;
#ifdef DEBUGGER_SUPPORT
@ -64,16 +57,14 @@ void DevSettingsHandler::loadSettings(SettingsSet set)
#endif
// Thumb ARM emulation exception
myThumbException[set] = devSettings ? settings.getBool("dev.thumb.trapfatal") : false;
myArmSpeed[set] = devSettings ? settings.getInt("dev.arm.mips") : CartridgeELF::MIPS_MAX;
// AtariVox/SaveKey/PlusROM access
myExternAccess[set] = settings.getBool(prefix + "extaccess");
// TIA tab
myTIAType[set] = devSettings ? settings.getString("dev.tia.type") : "standard";
myPlInvPhase[set] = devSettings ? settings.getBool("dev.tia.plinvphase") : false;
myMsInvPhase[set] = devSettings ? settings.getBool("dev.tia.msinvphase") : false;
myBlInvPhase[set] = devSettings ? settings.getBool("dev.tia.blinvphase") : false;
myPlLateHMove[set] = devSettings ? settings.getBool("dev.tia.pllatehmove") : false;
myMsLateHMove[set] = devSettings ? settings.getBool("dev.tia.mslatehmove") : false;
myBlLateHMove[set] = devSettings ? settings.getBool("dev.tia.bllatehmove") : false;
myPFBits[set] = devSettings ? settings.getBool("dev.tia.delaypfbits") : false;
myPFColor[set] = devSettings ? settings.getBool("dev.tia.delaypfcolor") : false;
myPFScore[set] = devSettings ? settings.getBool("dev.tia.pfscoreglitch") : false;
@ -119,8 +110,6 @@ void DevSettingsHandler::saveSettings(SettingsSet set)
if(devSettings)
{
settings.setValue("dev.plusroms.on", myPlusROM[set]);
settings.setValue("dev.hsrandom", myRandomHotspots[set]);
// Undriven TIA pins
settings.setValue("dev.tiadriven", myUndrivenPins[set]);
#ifdef DEBUGGER_SUPPORT
@ -131,7 +120,6 @@ void DevSettingsHandler::saveSettings(SettingsSet set)
#endif
// Thumb ARM emulation exception
settings.setValue("dev.thumb.trapfatal", myThumbException[set]);
settings.setValue("dev.arm.mips", myArmSpeed[set]);
}
// AtariVox/SaveKey/PlusROM access
@ -146,9 +134,6 @@ void DevSettingsHandler::saveSettings(SettingsSet set)
settings.setValue("dev.tia.plinvphase", myPlInvPhase[set]);
settings.setValue("dev.tia.msinvphase", myMsInvPhase[set]);
settings.setValue("dev.tia.blinvphase", myBlInvPhase[set]);
settings.setValue("dev.tia.pllatehmove", myPlLateHMove[set]);
settings.setValue("dev.tia.mslatehmove", myMsLateHMove[set]);
settings.setValue("dev.tia.bllatehmove", myBlLateHMove[set]);
settings.setValue("dev.tia.delaypfbits", myPFBits[set]);
settings.setValue("dev.tia.delaypfcolor", myPFColor[set]);
settings.setValue("dev.tia.pfscoreglitch", myPFScore[set]);
@ -183,9 +168,7 @@ void DevSettingsHandler::applySettings(SettingsSet set)
if(myOSystem.hasConsole())
{
myOSystem.console().cartridge().enableRandomHotspots(myRandomHotspots[set]);
myOSystem.console().tia().driveUnusedPinsRandom(myUndrivenPins[set]);
myOSystem.console().cartridge().enablePlusROM(myPlusROM[set]);
// Notes:
// - thumb exceptions not updated, because set in cart constructor
// - other missing settings are used on-the-fly
@ -206,9 +189,6 @@ void DevSettingsHandler::applySettings(SettingsSet set)
myOSystem.console().tia().setPlInvertedPhaseClock(myPlInvPhase[set]);
myOSystem.console().tia().setMsInvertedPhaseClock(myMsInvPhase[set]);
myOSystem.console().tia().setBlInvertedPhaseClock(myBlInvPhase[set]);
myOSystem.console().tia().setPlShortLateHMove(myPlLateHMove[set]);
myOSystem.console().tia().setMsShortLateHMove(myMsLateHMove[set]);
myOSystem.console().tia().setBlShortLateHMove(myBlLateHMove[set]);
myOSystem.console().tia().setPFBitsDelay(myPFBits[set]);
myOSystem.console().tia().setPFColorDelay(myPFColor[set]);
myOSystem.console().tia().setPFScoreGlitch(myPFScore[set]);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -21,7 +21,6 @@
class OSystem;
#include <array>
#include "bspf.hxx"
/**
This class takes care of developer settings sets.
@ -31,14 +30,13 @@ class OSystem;
class DevSettingsHandler
{
public:
enum SettingsSet: uInt8 {
enum SettingsSet {
player,
developer,
numSets
};
explicit DevSettingsHandler(OSystem& osystem);
~DevSettingsHandler() = default;
void loadSettings(SettingsSet set);
void saveSettings(SettingsSet set);
@ -47,48 +45,42 @@ class DevSettingsHandler
protected:
OSystem& myOSystem;
// Emulator sets
std::array<bool, numSets> myFrameStats{};
std::array<bool, numSets> myDetectedInfo{};
std::array<bool, numSets> myExternAccess{};
std::array<int, numSets> myConsole{};
std::array<int, numSets> myPlusROM{};
std::array<bool, numSets> myRandomBank{};
std::array<bool, numSets> myRandomizeTIA{};
std::array<bool, numSets> myRandomizeRAM{};
std::array<string, numSets> myRandomizeCPU{};
std::array<bool, numSets> myColorLoss{};
std::array<bool, numSets> myTVJitter{};
std::array<int, numSets> myTVJitterSense{};
std::array<int, numSets> myTVJitterRec{};
std::array<bool, numSets> myDebugColors{};
std::array<bool, numSets> myRandomHotspots{};
std::array<bool, numSets> myUndrivenPins{};
std::array<bool, numSets> myFrameStats;
std::array<bool, numSets> myDetectedInfo;
std::array<bool, numSets> myExternAccess;
std::array<int, numSets> myConsole;
std::array<bool, numSets> myRandomBank;
std::array<bool, numSets> myRandomizeTIA;
std::array<bool, numSets> myRandomizeRAM;
std::array<string, numSets> myRandomizeCPU;
std::array<bool, numSets> myColorLoss;
std::array<bool, numSets> myTVJitter;
std::array<int, numSets> myTVJitterSense;
std::array<int, numSets> myTVJitterRec;
std::array<bool, numSets> myDebugColors;
std::array<bool, numSets> myUndrivenPins;
#ifdef DEBUGGER_SUPPORT
std::array<bool, numSets> myRWPortBreak{};
std::array<bool, numSets> myWRPortBreak{};
std::array<bool, numSets> myRWPortBreak;
std::array<bool, numSets> myWRPortBreak;
#endif
std::array<bool, numSets> myThumbException{};
std::array<int, numSets> myArmSpeed{};
std::array<bool, numSets> myThumbException;
// TIA sets
std::array<string, numSets> myTIAType{};
std::array<bool, numSets> myPlInvPhase{};
std::array<bool, numSets> myMsInvPhase{};
std::array<bool, numSets> myBlInvPhase{};
std::array<bool, numSets> myPlLateHMove{};
std::array<bool, numSets> myMsLateHMove{};
std::array<bool, numSets> myBlLateHMove{};
std::array<bool, numSets> myPFBits{};
std::array<bool, numSets> myPFColor{};
std::array<bool, numSets> myPFScore{};
std::array<bool, numSets> myBKColor{};
std::array<bool, numSets> myPlSwap{};
std::array<bool, numSets> myBlSwap{};
std::array<string, numSets> myTIAType;
std::array<bool, numSets> myPlInvPhase;
std::array<bool, numSets> myMsInvPhase;
std::array<bool, numSets> myBlInvPhase;
std::array<bool, numSets> myPFBits;
std::array<bool, numSets> myPFColor;
std::array<bool, numSets> myPFScore;
std::array<bool, numSets> myBKColor;
std::array<bool, numSets> myPlSwap;
std::array<bool, numSets> myBlSwap;
// States sets
std::array<bool, numSets> myTimeMachine{};
std::array<int, numSets> myStateSize{};
std::array<int, numSets> myUncompressed{};
std::array<string, numSets> myStateInterval{};
std::array<string, numSets> myStateHorizon{};
std::array<bool, numSets> myTimeMachine;
std::array<int, numSets> myStateSize;
std::array<int, numSets> myUncompressed;
std::array<string, numSets> myStateInterval;
std::array<string, numSets> myStateHorizon;
private:
void handleEnableDebugColors(bool enable);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -30,10 +30,9 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
#ifdef GUI_SUPPORT
{
ostringstream buf;
myQwertz = int{'y'} == static_cast<int>
(SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(KBDK_Z)));
myQwertz = int{'y'} == static_cast<int>(SDL_GetKeyFromScancode(SDL_Scancode(KBDK_Z)));
buf << "Keyboard: " << (myQwertz ? "QWERTZ" : "QWERTY");
Logger::debug(buf.view());
Logger::debug(buf.str());
}
#endif
@ -41,9 +40,8 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
{
ostringstream buf;
buf << "ERROR: Couldn't initialize SDL joystick support: "
<< SDL_GetError() << '\n';
Logger::error(buf.view());
buf << "ERROR: Couldn't initialize SDL joystick support: " << SDL_GetError() << endl;
Logger::error(buf.str());
}
Logger::debug("EventHandlerSDL2::EventHandlerSDL2 SDL_INIT_JOYSTICK");
#endif
@ -125,29 +123,25 @@ void EventHandlerSDL2::pollEvent()
case SDL_MOUSEBUTTONUP:
{
// ToDo: check support of more buttons and double-click
MouseButton b{MouseButton::NONE};
switch(myEvent.button.button)
{
case SDL_BUTTON_LEFT:
b = MouseButton::LEFT;
handleMouseButtonEvent(MouseButton::LEFT, myEvent.button.type == SDL_MOUSEBUTTONDOWN,
myEvent.button.x, myEvent.button.y);
break;
case SDL_BUTTON_RIGHT:
b = MouseButton::RIGHT;
break;
case SDL_BUTTON_MIDDLE:
b = MouseButton::MIDDLE;
handleMouseButtonEvent(MouseButton::RIGHT, myEvent.button.type == SDL_MOUSEBUTTONDOWN,
myEvent.button.x, myEvent.button.y);
break;
default:
break;
}
handleMouseButtonEvent(b, myEvent.button.type == SDL_MOUSEBUTTONDOWN,
myEvent.button.x, myEvent.button.y);
break;
}
case SDL_MOUSEWHEEL:
{
int x{0}, y{0};
int x, y;
SDL_GetMouseState(&x, &y); // we need mouse position too
if(myEvent.wheel.y < 0)
handleMouseButtonEvent(MouseButton::WHEELDOWN, true, x, y);
@ -265,7 +259,6 @@ EventHandlerSDL2::JoystickSDL2::JoystickSDL2(int idx)
{
ASSERT_MAIN_THREAD;
// NOLINTNEXTLINE: we want to initialize here, not in the member list
myStick = SDL_JoystickOpen(idx);
if(myStick)
{
@ -274,7 +267,7 @@ EventHandlerSDL2::JoystickSDL2::JoystickSDL2(int idx)
// it also appends " #x", where x seems to vary. Obviously this wreaks
// havoc with the idea that a joystick will always have the same name.
// So we truncate the number.
const char* const sdlname = SDL_JoystickName(myStick);
const char* sdlname = SDL_JoystickName(myStick);
const string& desc = BSPF::startsWithIgnoreCase(sdlname, "XInput Controller")
? "XInput Controller" : sdlname;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -17,6 +17,7 @@
#include <cmath>
#include "SDL_lib.hxx"
#include "bspf.hxx"
#include "Logger.hxx"
@ -83,7 +84,7 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
myNumDisplays = SDL_GetNumVideoDisplays();
// First get the maximum fullscreen desktop resolution
SDL_DisplayMode display{};
SDL_DisplayMode display;
for(int i = 0; i < myNumDisplays; ++i)
{
SDL_GetDesktopDisplayMode(i, &display);
@ -96,7 +97,8 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
s << "Supported video modes (" << numModes << ") for display " << i
<< " (" << SDL_GetDisplayName(i) << "):";
string lastRes;
string lastRes = "";
for(int m = 0; m < numModes; ++m)
{
SDL_DisplayMode mode;
@ -105,11 +107,11 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
SDL_GetDisplayMode(i, m, &mode);
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
if(lastRes != res.view())
if(lastRes != res.str())
{
Logger::debug(s.view());
Logger::debug(s.str());
s.str("");
lastRes = res.view();
lastRes = res.str();
s << " " << lastRes << ": ";
}
s << mode.refresh_rate << "Hz";
@ -118,7 +120,7 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
else
s << " ";
}
Logger::debug(s.view());
Logger::debug(s.str());
}
// Now get the maximum windowed desktop resolution
@ -133,7 +135,7 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
SDL_DestroyWindow(tmpWindow);
}
SDL_Rect r{};
SDL_Rect r;
for(int i = 0; i < myNumDisplays; ++i)
{
// Display bounds minus dock
@ -155,15 +157,13 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
string stellaName;
};
// Create name map for all currently known SDL renderers
static const std::array<RenderName, 8> RENDERER_NAMES = {{
{ "direct3d", "Direct3D" },
{ "direct3d11", "Direct3D 11" },
{ "direct3d12", "Direct3D 12" },
{ "metal", "Metal" },
{ "opengl", "OpenGL" },
{ "opengles", "OpenGL ES" },
{ "opengles2", "OpenGL ES 2" },
{ "software", "Software" }
static const std::array<RenderName, 6> RENDERER_NAMES = {{
{ "direct3d", "Direct3D" },
{ "metal", "Metal" },
{ "opengl", "OpenGL" },
{ "opengles", "OpenGLES" },
{ "opengles2", "OpenGLES2" },
{ "software", "Software" }
}};
const int numDrivers = SDL_GetNumRenderDrivers();
@ -174,11 +174,11 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
{
// Map SDL names into nicer Stella names (if available)
bool found = false;
for(const auto& render: RENDERER_NAMES)
for(size_t j = 0; j < RENDERER_NAMES.size(); ++j)
{
if(render.sdlName == info.name)
if(RENDERER_NAMES[j].sdlName == info.name)
{
VarList::push_back(renderers, render.stellaName, info.name);
VarList::push_back(renderers, RENDERER_NAMES[j].stellaName, info.name);
found = true;
break;
}
@ -261,7 +261,7 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
}
#ifdef ADAPTABLE_REFRESH_SUPPORT
SDL_DisplayMode adaptedSdlMode{};
SDL_DisplayMode adaptedSdlMode;
const int gameRefreshRate =
myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0;
const bool shouldAdapt = fullScreen
@ -284,12 +284,11 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
if(myWindow)
{
const int d = SDL_GetWindowDisplayIndex(myWindow);
int w{0}, h{0};
int w, h;
SDL_GetWindowSize(myWindow, &w, &h);
if(d != displayIndex ||
std::cmp_not_equal(w, mode.screenS.w) ||
std::cmp_not_equal(h, mode.screenS.h) || adaptRefresh)
if(d != displayIndex || static_cast<uInt32>(w) != mode.screenS.w ||
static_cast<uInt32>(h) != mode.screenS.h || adaptRefresh)
{
// Renderer has to be destroyed *before* the window gets destroyed to avoid memory leaks
SDL_DestroyRenderer(myRenderer);
@ -311,7 +310,7 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
mode.screenS.w, mode.screenS.h, flags);
if(myWindow == nullptr)
{
const string msg = "ERROR: Unable to open SDL window: " + string(SDL_GetError());
string msg = "ERROR: Unable to open SDL window: " + string(SDL_GetError());
Logger::error(msg);
return false;
}
@ -332,12 +331,8 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
ostringstream msg;
msg << "Display refresh rate changed to "
<< adaptedSdlMode.refresh_rate << " Hz " << "(" << adaptedSdlMode.w << "x" << adaptedSdlMode.h << ")";
Logger::info(msg.view());
SDL_DisplayMode setSdlMode;
SDL_GetWindowDisplayMode(myWindow, &setSdlMode);
cerr << setSdlMode.refresh_rate << "Hz\n";
<< adaptedSdlMode.refresh_rate << " Hz";
Logger::info(msg.str());
}
}
#endif
@ -363,8 +358,8 @@ bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
const int wantedRefreshRate =
myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0;
// Take care of rounded refresh rates (e.g. 59.94 Hz)
float factor = std::min(
static_cast<float>(currentRefreshRate) / wantedRefreshRate, static_cast<float>(currentRefreshRate) / (wantedRefreshRate - 1));
float factor = std::min(float(currentRefreshRate) / wantedRefreshRate,
float(currentRefreshRate) / (wantedRefreshRate - 1));
// Calculate difference taking care of integer factors (e.g. 100/120)
float bestDiff = std::abs(factor - std::round(factor)) / factor;
bool adapt = false;
@ -375,7 +370,7 @@ bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
// Check for integer factors 1 (60/50 Hz) and 2 (120/100 Hz)
for(int m = 1; m <= 2; ++m)
{
SDL_DisplayMode closestSdlMode{};
SDL_DisplayMode closestSdlMode;
sdlMode.refresh_rate = wantedRefreshRate * m;
if(SDL_GetClosestDisplayMode(displayIndex, &sdlMode, &closestSdlMode) == nullptr)
@ -383,9 +378,8 @@ bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
Logger::error("ERROR: Closest display mode could not be retrieved");
return adapt;
}
factor = std::min(
static_cast<float>(sdlMode.refresh_rate) / sdlMode.refresh_rate,
static_cast<float>(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1));
factor = std::min(float(sdlMode.refresh_rate) / sdlMode.refresh_rate,
float(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1));
const float diff = std::abs(factor - std::round(factor)) / factor;
if(diff < bestDiff)
{
@ -399,7 +393,7 @@ bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
// cerr << "required (" << currentRefreshRate << " Hz -> " << adaptedSdlMode.refresh_rate << " Hz)";
//else
// cerr << "not required/possible";
//cerr << '\n';
//cerr << endl;
// Only change if the display supports a better refresh rate
return adapt;
@ -417,7 +411,7 @@ bool FBBackendSDL2::createRenderer()
bool recreate = myRenderer == nullptr;
uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
const string& video = myOSystem.settings().getString("video"); // Render hint
SDL_RendererInfo renderInfo{};
SDL_RendererInfo renderInfo;
if(myOSystem.settings().getBool("vsync")
&& !myOSystem.settings().getBool("turbo")) // V'synced blits option
@ -430,11 +424,11 @@ bool FBBackendSDL2::createRenderer()
if(recreate)
{
//cerr << "Create new renderer for buffer type #" << int(myBufferType) << '\n';
//cerr << "Create new renderer for buffer type #" << int(myBufferType) << endl;
if(myRenderer)
SDL_DestroyRenderer(myRenderer);
if(!video.empty())
if(video != "")
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags);
@ -444,8 +438,8 @@ bool FBBackendSDL2::createRenderer()
if(myRenderer == nullptr)
{
Logger::error("ERROR: Unable to create SDL renderer: " +
string{SDL_GetError()});
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError());
Logger::error(msg);
return false;
}
}
@ -460,14 +454,14 @@ bool FBBackendSDL2::createRenderer()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::setTitle(string_view title)
void FBBackendSDL2::setTitle(const string& title)
{
ASSERT_MAIN_THREAD;
myScreenTitle = title;
if(myWindow)
SDL_SetWindowTitle(myWindow, string{title}.c_str());
SDL_SetWindowTitle(myWindow, title.c_str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -476,18 +470,18 @@ string FBBackendSDL2::about() const
ASSERT_MAIN_THREAD;
ostringstream out;
out << "Video system: " << SDL_GetCurrentVideoDriver() << '\n';
out << "Video system: " << SDL_GetCurrentVideoDriver() << endl;
SDL_RendererInfo info;
if(SDL_GetRendererInfo(myRenderer, &info) >= 0)
{
out << " Renderer: " << info.name << '\n';
out << " Renderer: " << info.name << endl;
if(info.max_texture_width > 0 && info.max_texture_height > 0)
out << " Max texture: " << info.max_texture_width << "x"
<< info.max_texture_height << '\n';
<< info.max_texture_height << endl;
out << " Flags: "
<< ((info.flags & SDL_RENDERER_PRESENTVSYNC) ? "+" : "-") << "vsync, "
<< ((info.flags & SDL_RENDERER_ACCELERATED) ? "+" : "-") << "accel"
<< '\n';
<< endl;
}
return out.str();
}
@ -573,7 +567,7 @@ unique_ptr<FBSurface> FBBackendSDL2::createSurface(
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::readPixels(uInt8* buffer, size_t pitch,
void FBBackendSDL2::readPixels(uInt8* pixels, uInt32 pitch,
const Common::Rect& rect) const
{
ASSERT_MAIN_THREAD;
@ -582,7 +576,7 @@ void FBBackendSDL2::readPixels(uInt8* buffer, size_t pitch,
r.x = rect.x(); r.y = rect.y();
r.w = rect.w(); r.h = rect.h();
SDL_RenderReadPixels(myRenderer, &r, 0, buffer, static_cast<int>(pitch));
SDL_RenderReadPixels(myRenderer, &r, 0, pixels, pitch);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -78,7 +78,7 @@ class FBBackendSDL2 : public FBBackend
@param title The title of the application / window
*/
void setTitle(string_view title) override;
void setTitle(const string& title) override;
/**
Shows or hides the cursor based on the given boolean value.
@ -98,21 +98,9 @@ class FBBackendSDL2 : public FBBackend
@param g The green component of the color
@param b The blue component of the color
*/
FORCE_INLINE void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override
inline void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override
{ SDL_GetRGB(pixel, myPixelFormat, r, g, b); }
/**
This method is called to retrieve the R/G/B/A data from the given pixel.
@param pixel The pixel containing R/G/B data
@param r The red component of the color
@param g The green component of the color
@param b The blue component of the color
@param a The alpha component of the color.
*/
FORCE_INLINE void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const override
{ SDL_GetRGBA(pixel, myPixelFormat, r, g, b, a); }
/**
This method is called to map a given R/G/B triple to the screen palette.
@ -120,20 +108,9 @@ class FBBackendSDL2 : public FBBackend
@param g The green component of the color.
@param b The blue component of the color.
*/
uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override
inline uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override
{ return SDL_MapRGB(myPixelFormat, r, g, b); }
/**
This method is called to map a given R/G/B/A triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
@param a The alpha component of the color.
*/
uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override
{ return SDL_MapRGBA(myPixelFormat, r, g, b, a); }
/**
This method is called to get a copy of the specified ARGB data from the
viewable FrameBuffer area. Note that this isn't the same as any
@ -144,7 +121,7 @@ class FBBackendSDL2 : public FBBackend
@param pitch The pitch (in bytes) for the pixel data
@param rect The bounding rectangle for the buffer
*/
void readPixels(uInt8* buffer, size_t pitch,
void readPixels(uInt8* buffer, uInt32 pitch,
const Common::Rect& rect) const override;
/**

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -38,7 +38,7 @@ namespace {
throw runtime_error("unreachable");
}
}
} // namespace
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
@ -48,7 +48,6 @@ FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
: myBackend{backend},
myInterpolationMode{inter}
{
//cerr << width << " x " << height << '\n';
createSurface(width, height, staticData);
}
@ -234,7 +233,6 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
mySurface = SDL_CreateRGBSurface(0, width, height,
pf.BitsPerPixel, pf.Rmask, pf.Gmask, pf.Bmask, pf.Amask);
//SDL_SetSurfaceBlendMode(mySurface, SDL_BLENDMODE_ADD); // default: SDL_BLENDMODE_BLEND
// We start out with the src and dst rectangles containing the same
// dimensions, indicating no scaling or re-positioning
@ -251,10 +249,9 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
myIsStatic = data != nullptr;
if(myIsStatic)
SDL_memcpy(mySurface->pixels, data,
static_cast<size_t>(mySurface->w) * mySurface->h * 4);
SDL_memcpy(mySurface->pixels, data, mySurface->w * mySurface->h * 4);
reload(); // NOLINT
reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -281,12 +278,8 @@ void FBSurfaceSDL2::applyAttributes()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setScalingInterpolation(ScalingInterpolation interpolation)
{
if (interpolation == ScalingInterpolation::sharp &&
(
static_cast<int>(mySrcGUIR.h()) >= myBackend.scaleY(myDstGUIR.h()) ||
static_cast<int>(mySrcGUIR.w()) >= myBackend.scaleX(myDstGUIR.w())
)
)
if (interpolation == ScalingInterpolation::sharp
&& mySrcGUIR.h() >= myDstGUIR.h())
interpolation = ScalingInterpolation::blur;
if (interpolation == myInterpolationMode) return;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -69,8 +69,8 @@ class FBSurfaceSDL2 : public FBSurface
void applyAttributes() override;
private:
bool setSrcPosInternal(uInt32 x, uInt32 y) {
if(std::cmp_not_equal(x, mySrcR.x) || std::cmp_not_equal(y, mySrcR.y))
inline bool setSrcPosInternal(uInt32 x, uInt32 y) {
if(x != static_cast<uInt32>(mySrcR.x) || y != static_cast<uInt32>(mySrcR.y))
{
mySrcR.x = x; mySrcR.y = y;
mySrcGUIR.moveTo(x, y);
@ -78,8 +78,8 @@ class FBSurfaceSDL2 : public FBSurface
}
return false;
}
bool setSrcSizeInternal(uInt32 w, uInt32 h) {
if(std::cmp_not_equal(w, mySrcR.w) || std::cmp_not_equal(h, mySrcR.h))
inline bool setSrcSizeInternal(uInt32 w, uInt32 h) {
if(w != static_cast<uInt32>(mySrcR.w) || h != static_cast<uInt32>(mySrcR.h))
{
mySrcR.w = w; mySrcR.h = h;
mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h);
@ -87,8 +87,8 @@ class FBSurfaceSDL2 : public FBSurface
}
return false;
}
bool setDstPosInternal(uInt32 x, uInt32 y) {
if(std::cmp_not_equal(x, myDstR.x) || std::cmp_not_equal(y, myDstR.y))
inline bool setDstPosInternal(uInt32 x, uInt32 y) {
if(x != static_cast<uInt32>(myDstR.x) || y != static_cast<uInt32>(myDstR.y))
{
myDstR.x = x; myDstR.y = y;
myDstGUIR.moveTo(x, y);
@ -96,8 +96,8 @@ class FBSurfaceSDL2 : public FBSurface
}
return false;
}
bool setDstSizeInternal(uInt32 w, uInt32 h) {
if(std::cmp_not_equal(w, myDstR.w) || std::cmp_not_equal(h, myDstR.h))
inline bool setDstSizeInternal(uInt32 w, uInt32 h) {
if(w != static_cast<uInt32>(myDstR.w) || h != static_cast<uInt32>(myDstR.h))
{
myDstR.w = w; myDstR.h = h;
myDstGUIR.setWidth(w); myDstGUIR.setHeight(h);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -38,44 +38,41 @@ class AbstractFSNode;
@author Stephen Anthony
*/
class FSNodeFactory
class FilesystemNodeFactory
{
public:
enum class Type: uInt8 { SYSTEM, ZIP };
enum class Type { SYSTEM, ZIP };
public:
static unique_ptr<AbstractFSNode> create(string_view path, Type type)
static unique_ptr<AbstractFSNode> create(const string& path, Type type)
{
switch(type)
{
case Type::SYSTEM:
#if defined(BSPF_UNIX) || defined(BSPF_MACOS)
return make_unique<FSNodePOSIX>(path);
return make_unique<FilesystemNodePOSIX>(path);
#elif defined(BSPF_WINDOWS)
return make_unique<FSNodeWINDOWS>(path);
return make_unique<FilesystemNodeWINDOWS>(path);
#elif defined(__LIB_RETRO__)
return make_unique<FSNodeLIBRETRO>(path);
return make_unique<FilesystemNodeLIBRETRO>(path);
#endif
break;
case Type::ZIP:
#if defined(ZIP_SUPPORT)
return make_unique<FSNodeZIP>(path);
return make_unique<FilesystemNodeZIP>(path);
#endif
break;
default:
break;
}
return nullptr; // satisfy compiler
return nullptr;
}
private:
// Following constructors and assignment operators not supported
FSNodeFactory() = delete;
~FSNodeFactory() = delete;
FSNodeFactory(const FSNodeFactory&) = delete;
FSNodeFactory(FSNodeFactory&&) = delete;
FSNodeFactory& operator=(const FSNodeFactory&) = delete;
FSNodeFactory& operator=(FSNodeFactory&&) = delete;
FilesystemNodeFactory() = delete;
FilesystemNodeFactory(const FilesystemNodeFactory&) = delete;
FilesystemNodeFactory(FilesystemNodeFactory&&) = delete;
FilesystemNodeFactory& operator=(const FilesystemNodeFactory&) = delete;
FilesystemNodeFactory& operator=(FilesystemNodeFactory&&) = delete;
};
#endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -21,11 +21,12 @@
#include "bspf.hxx"
#include "Bankswitch.hxx"
#include "OSystem.hxx"
#include "FSNodeFactory.hxx"
#include "FSNodeZIP.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FSNodeZIP::FSNodeZIP(string_view p)
FilesystemNodeZIP::FilesystemNodeZIP(const string& p)
{
// Extract ZIP file and virtual file (if specified)
const size_t pos = BSPF::findIgnoreCase(p, ".zip");
@ -35,18 +36,18 @@ FSNodeZIP::FSNodeZIP(string_view p)
_zipFile = p.substr(0, pos+4);
// Expand '~' to the users 'home' directory
if(_zipFile[0] == '~')
if (_zipFile[0] == '~')
{
#if defined(BSPF_UNIX) || defined(BSPF_MACOS)
const char* const home = std::getenv("HOME"); // NOLINT (not thread safe)
if(home != nullptr)
const char* home = std::getenv("HOME");
if (home != nullptr)
_zipFile.replace(0, 1, home);
#elif defined(BSPF_WINDOWS)
_zipFile.replace(0, 1, HomeFinder::getHomePath());
_zipFile.replace(0, 1, myHomeFinder.getHomePath());
#endif
}
// cerr << " => p: " << p << '\n';
// cerr << " => p: " << p << endl;
// Open file at least once to initialize the virtual file count
try
@ -99,28 +100,30 @@ FSNodeZIP::FSNodeZIP(string_view p)
// has direct access to the actual filesystem (aka, a 'System' node)
// Behind the scenes, this node is actually a platform-specific object
// for whatever system we are running on
_realNode = FSNodeFactory::create(_zipFile,
FSNodeFactory::Type::SYSTEM);
_realNode = FilesystemNodeFactory::create(_zipFile,
FilesystemNodeFactory::Type::SYSTEM);
setFlags(_zipFile, _virtualPath, _realNode);
// cerr << "==============================================================\n";
// cerr << _name << ", file: " << _isFile << ", dir: " << _isDirectory << "\n\n";
// cerr << _name << ", file: " << _isFile << ", dir: " << _isDirectory << endl << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FSNodeZIP::FSNodeZIP(const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode, size_t size, bool isdir)
FilesystemNodeZIP::FilesystemNodeZIP(
const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode, size_t size, bool isdir)
: _size{size},
_isDirectory{isdir},
_isFile{!isdir}
{
// cerr << "=> c'tor 2: " << zipfile << ", " << virtualpath << ", " << isdir << '\n';
// cerr << "=> c'tor 2: " << zipfile << ", " << virtualpath << ", " << isdir << endl;
setFlags(zipfile, virtualpath, realnode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FSNodeZIP::setFlags(const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode)
void FilesystemNodeZIP::setFlags(const string& zipfile,
const string& virtualpath,
const AbstractFSNodePtr& realnode)
{
_zipFile = zipfile;
_virtualPath = virtualpath;
@ -130,7 +133,7 @@ void FSNodeZIP::setFlags(const string& zipfile, const string& virtualpath,
_shortPath = _realNode->getShortPath();
// Is a file component present?
if(!_virtualPath.empty())
if(_virtualPath.size() != 0)
{
_path += ("/" + _virtualPath);
_shortPath += ("/" + _virtualPath);
@ -144,7 +147,7 @@ void FSNodeZIP::setFlags(const string& zipfile, const string& virtualpath,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FSNodeZIP::exists() const
bool FilesystemNodeZIP::exists() const
{
if(_realNode && _realNode->exists())
{
@ -162,7 +165,6 @@ bool FSNodeZIP::exists() const
catch(const runtime_error&)
{
// TODO: Actually present the error passed in back to the user
cerr << "ERROR: FSNodeZIP::exists()\n";
}
}
@ -170,7 +172,7 @@ bool FSNodeZIP::exists() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FSNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
{
// Files within ZIP archives don't contain children
if(!isDirectory() || _error != zip_error::NONE)
@ -178,6 +180,7 @@ bool FSNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
std::set<string> dirs;
myZipHandler->open(_zipFile);
// cerr << "CHILDREN: --------------------------------\n";
while(myZipHandler->hasNext())
{
// Only consider entries that start with '_virtualPath'
@ -189,37 +192,39 @@ bool FSNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
{
// First strip off the leading directory
const string& curr = name.substr(
_virtualPath.empty() ? 0 : _virtualPath.size()+1);
_virtualPath == "" ? 0 : _virtualPath.size()+1);
// cerr << " curr: " << curr << endl;
// Only add sub-directory entries once
const auto pos = curr.find_first_of("/\\");
if(pos != string::npos)
dirs.emplace(curr.substr(0, pos));
else
myList.emplace_back(new FSNodeZIP(_zipFile, name, _realNode, size, false));
myList.emplace_back(new FilesystemNodeZIP(_zipFile, name, _realNode, size, false));
}
}
for(const auto& dir: dirs)
{
// Prepend previous path
const string& vpath = !_virtualPath.empty() ? _virtualPath + "/" + dir : dir;
myList.emplace_back(new FSNodeZIP(_zipFile, vpath, _realNode, 0, true));
const string& vpath = _virtualPath != "" ? _virtualPath + "/" + dir : dir;
myList.emplace_back(new FilesystemNodeZIP(_zipFile, vpath, _realNode, 0, true));
}
// cerr << "list: \n";
// for(auto& s: myList)
// cerr << s->getPath() << " : isdir: " << s->isDirectory() << endl;
// cerr << "------------------------------------------\n";
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FSNodeZIP::read(ByteBuffer& buffer, size_t) const
size_t FilesystemNodeZIP::read(ByteBuffer& image, size_t) const
{
switch(_error)
{
using enum zip_error;
case NONE: break;
case NOT_A_FILE: throw runtime_error("ZIP file contains errors/not found");
case NOT_READABLE: throw runtime_error("ZIP file not readable");
case NO_ROMS: throw runtime_error("ZIP file doesn't contain any ROMs");
default: throw runtime_error("FSNodeZIP::read default case hit");
case zip_error::NONE: break;
case zip_error::NOT_A_FILE: throw runtime_error("ZIP file contains errors/not found");
case zip_error::NOT_READABLE: throw runtime_error("ZIP file not readable");
case zip_error::NO_ROMS: throw runtime_error("ZIP file doesn't contain any ROMs");
}
myZipHandler->open(_zipFile);
@ -231,58 +236,49 @@ size_t FSNodeZIP::read(ByteBuffer& buffer, size_t) const
found = name == _virtualPath;
}
return found ? myZipHandler->decompress(buffer) : 0;
return found ? myZipHandler->decompress(image) : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FSNodeZIP::read(stringstream& buffer) const
size_t FilesystemNodeZIP::read(stringstream& image) const
{
// For now, we just read into a buffer and store in the stream
// TODO: maybe there's a more efficient way to do this?
ByteBuffer read_buf;
const size_t size = read(read_buf, 0);
ByteBuffer buffer;
const size_t size = read(buffer, 0);
if(size > 0)
buffer.write(reinterpret_cast<char*>(read_buf.get()), size);
image.write(reinterpret_cast<char*>(buffer.get()), size);
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FSNodeZIP::write(const ByteBuffer& buffer, size_t) const
size_t FilesystemNodeZIP::write(const ByteBuffer& buffer, size_t size) const
{
// TODO: Not yet implemented
throw runtime_error("ZIP file not writable");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t FSNodeZIP::write(const stringstream& buffer) const
size_t FilesystemNodeZIP::write(const stringstream& buffer) const
{
// TODO: Not yet implemented
throw runtime_error("ZIP file not writable");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFSNodePtr FSNodeZIP::getParent() const
AbstractFSNodePtr FilesystemNodeZIP::getParent() const
{
if(_virtualPath.empty())
if(_virtualPath == "")
return _realNode ? _realNode->getParent() : nullptr;
// TODO: For some reason, getting the stem for normal paths and zip paths
// behaves differently. For now, we'll use the old method here.
auto STEM_FOR_ZIP = [](string_view s) {
const char* const start = s.data();
const char* cur = start + s.size() - 2;
const char* start = _path.c_str();
const char* end = lastPathComponent(_path);
while (cur >= start && !(*cur == '/' || *cur == '\\'))
--cur;
return s.substr(0, (cur + 1) - start - 1);
};
return make_unique<FSNodeZIP>(STEM_FOR_ZIP(_path));
return make_unique<FilesystemNodeZIP>(string(start, end - start - 1));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<ZipHandler> FSNodeZIP::myZipHandler = make_unique<ZipHandler>();
unique_ptr<ZipHandler> FilesystemNodeZIP::myZipHandler = make_unique<ZipHandler>();
#endif // ZIP_SUPPORT

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -32,19 +32,19 @@
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class FSNodeZIP : public AbstractFSNode
class FilesystemNodeZIP : public AbstractFSNode
{
public:
/**
* Creates a FSNodeZIP for a given path.
* Creates a FilesystemNodeZIP for a given path.
*
* @param path String with the path the new node should point to.
*/
explicit FSNodeZIP(string_view path);
explicit FilesystemNodeZIP(const string& path);
bool exists() const override;
const string& getName() const override { return _name; }
void setName(string_view name) override { _name = name; }
const string& getName() const override { return _name; }
void setName(const string& name) override { _name = name; }
const string& getPath() const override { return _path; }
string getShortPath() const override { return _shortPath; }
bool hasParent() const override { return true; }
@ -56,39 +56,39 @@ class FSNodeZIP : public AbstractFSNode
//////////////////////////////////////////////////////////
// For now, ZIP files cannot be modified in any way
bool makeDir() override { return false; }
bool rename(string_view) override { return false; }
bool rename(const string& newfile) override { return false; }
//////////////////////////////////////////////////////////
size_t getSize() const override { return _size; }
bool getChildren(AbstractFSList& list, ListMode mode) const override;
AbstractFSNodePtr getParent() const override;
size_t read(ByteBuffer& buffer, size_t) const override;
size_t read(ByteBuffer& buffer, size_t size) const override;
size_t read(stringstream& buffer) const override;
size_t write(const ByteBuffer& buffer, size_t) const override;
size_t write(const ByteBuffer& buffer, size_t size) const override;
size_t write(const stringstream& buffer) const override;
private:
FSNodeZIP(const string& zipfile, const string& virtualpath,
FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode, size_t size, bool isdir);
void setFlags(const string& zipfile, const string& virtualpath,
const AbstractFSNodePtr& realnode);
friend ostream& operator<<(ostream& os, const FSNodeZIP& node)
friend ostream& operator<<(ostream& os, const FilesystemNodeZIP& node)
{
os << "_zipFile: " << node._zipFile << '\n'
<< "_virtualPath: " << node._virtualPath << '\n'
<< "_name: " << node._name << '\n'
<< "_path: " << node._path << '\n'
<< "_size: " << node._size << '\n'
<< "_shortPath: " << node._shortPath << '\n';
os << "_zipFile: " << node._zipFile << endl
<< "_virtualPath: " << node._virtualPath << endl
<< "_name: " << node._name << endl
<< "_path: " << node._path << endl
<< "_size: " << node._size << endl
<< "_shortPath: " << node._shortPath << endl;
return os;
}
private:
/* Error types */
enum class zip_error: uInt8
enum class zip_error
{
NONE,
NOT_A_FILE,

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -46,27 +46,32 @@ void FpsMeter::render(uInt32 frameCount)
}
const size_t queueSize = myQueue.capacity();
entry e_first, e_last;
entry first, last;
e_last.frames = frameCount;
e_last.timestamp = high_resolution_clock::now();
last.frames = frameCount;
last.timestamp = high_resolution_clock::now();
if (myQueue.size() < queueSize) {
myQueue.push_back(e_last);
myQueue.push_back(last);
myFrameCount += frameCount;
e_first = myQueue.at(myQueueOffset);
first = myQueue.at(myQueueOffset);
} else {
myFrameCount = myFrameCount - myQueue.at(myQueueOffset).frames + frameCount;
myQueue.at(myQueueOffset) = e_last;
myQueue.at(myQueueOffset) = last;
myQueueOffset = (myQueueOffset + 1) % queueSize;
e_first = myQueue.at(myQueueOffset);
first = myQueue.at(myQueueOffset);
}
const float myTimeInterval =
duration_cast<duration<float>>(e_last.timestamp - e_first.timestamp).count();
duration_cast<duration<float>>(last.timestamp - first.timestamp).count();
if (myTimeInterval > 0)
myFps = (myFrameCount - e_first.frames) / myTimeInterval;
if (myTimeInterval > 0) myFps = (myFrameCount - first.frames) / myTimeInterval;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FpsMeter::fps() const
{
return myFps;
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -27,13 +27,12 @@ class FpsMeter
public:
explicit FpsMeter(uInt32 queueSize);
~FpsMeter() = default;
void reset(uInt32 garbageFrameLimit = 0);
void render(uInt32 frameCount);
float fps() const { return myFps; }
float fps() const;
private:

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -65,10 +65,9 @@ HighScoresManager::HighScoresManager(OSystem& osystem)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::setRepository(
shared_ptr<CompositeKeyValueRepositoryAtomic> repo)
void HighScoresManager::setRepository(shared_ptr<CompositeKeyValueRepositoryAtomic> repo)
{
myHighscoreRepository = std::move(repo);
myHighscoreRepository = repo;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -77,7 +76,7 @@ Int16 HighScoresManager::peek(uInt16 addr) const
if (myOSystem.hasConsole())
{
if(addr < 0x100U || myOSystem.console().cartridge().internalRamSize() == 0)
return myOSystem.console().system().peekOob(addr);
return myOSystem.console().system().peek(addr);
else
return myOSystem.console().cartridge().internalRamGetValue(addr);
}
@ -85,7 +84,7 @@ Int16 HighScoresManager::peek(uInt16 addr) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json HighScoresManager::properties(const Properties& props)
const json HighScoresManager::properties(const Properties& props) const
{
const string& property = props.get(PropType::Cart_Highscore);
@ -95,6 +94,7 @@ json HighScoresManager::properties(const Properties& props)
return json::parse(property);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json HighScoresManager::properties(json& jprops) const
{
@ -113,6 +113,7 @@ json HighScoresManager::properties(json& jprops) const
return jprops = properties(props);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::enabled() const
{
@ -122,16 +123,17 @@ bool HighScoresManager::enabled() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numVariations(const json& jprops)
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
{
const json jprops = properties(props);
json jprops = properties(props);
numVariationsR = numVariations(jprops);
@ -156,7 +158,7 @@ bool HighScoresManager::get(const Properties& props, uInt32& numVariationsR,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::set(Properties& props, uInt32 numVariations,
const ScoresProps& info)
const ScoresProps& info) const
{
json jprops = json::object();
@ -204,79 +206,85 @@ void HighScoresManager::set(Properties& props, uInt32 numVariations,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numDigits(const json& jprops)
uInt32 HighScoresManager::numDigits(const json& jprops) const
{
return min(getPropInt(jprops, SCORE_DIGITS, DEFAULT_DIGITS), MAX_SCORE_DIGITS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::trailingZeroes(const json& jprops)
uInt32 HighScoresManager::trailingZeroes(const json& jprops) const
{
return min(getPropInt(jprops, SCORE_TRAILING_ZEROES, DEFAULT_TRAILING), MAX_TRAILING);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::scoreBCD(const json& jprops)
bool HighScoresManager::scoreBCD(const json& jprops) const
{
return getPropBool(jprops, SCORE_BCD, DEFAULT_SCORE_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::scoreInvert(const json& jprops)
bool HighScoresManager::scoreInvert(const json& jprops) const
{
return getPropBool(jprops, SCORE_INVERTED, DEFAULT_SCORE_REVERSED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::varBCD(const json& jprops)
bool HighScoresManager::varBCD(const json& jprops) const
{
return getPropBool(jprops, VARIATIONS_BCD, DEFAULT_VARS_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::varZeroBased(const json& jprops)
bool HighScoresManager::varZeroBased(const json& jprops) const
{
return getPropBool(jprops, VARIATIONS_ZERO_BASED, DEFAULT_VARS_ZERO_BASED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::specialLabel(const json& jprops)
const string HighScoresManager::specialLabel(const json& jprops) const
{
return getPropStr(jprops, SPECIAL_LABEL);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::specialBCD(const json& jprops)
bool HighScoresManager::specialBCD(const json& jprops) const
{
return getPropBool(jprops, SPECIAL_BCD, DEFAULT_SPECIAL_BCD);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::specialZeroBased(const json& jprops)
bool HighScoresManager::specialZeroBased(const json& jprops) const
{
return getPropBool(jprops, SPECIAL_ZERO_BASED, DEFAULT_SPECIAL_ZERO_BASED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::notes(const json& jprops)
const string HighScoresManager::notes(const json& jprops) const
{
return getPropStr(jprops, NOTES);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::varAddress(const json& jprops)
uInt16 HighScoresManager::varAddress(const json& jprops) const
{
return getPropAddr(jprops, VARIATIONS_ADDRESS, DEFAULT_ADDRESS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::specialAddress(const json& jprops)
uInt16 HighScoresManager::specialAddress(const json& jprops) const
{
return getPropAddr(jprops, SPECIAL_ADDRESS, DEFAULT_ADDRESS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 HighScoresManager::numAddrBytes(const json& jprops)
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));
}
@ -290,7 +298,7 @@ Int32 HighScoresManager::numVariations() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::specialLabel() const
const string HighScoresManager::specialLabel() const
{
json jprops;
@ -313,7 +321,7 @@ Int32 HighScoresManager::variation(uInt16 addr, bool varBCD, bool zeroBased,
Int32 HighScoresManager::variation() const
{
json jprops;
const uInt16 addr = varAddress(properties(jprops));
uInt16 addr = varAddress(properties(jprops));
if(addr == DEFAULT_ADDRESS) {
if(numVariations() == 1)
@ -361,7 +369,7 @@ Int32 HighScoresManager::score(uInt32 numAddrBytes, uInt32 trailingZeroes,
Int32 HighScoresManager::score() const
{
json jprops;
const uInt32 numBytes = numAddrBytes(properties(jprops));
uInt32 numBytes = numAddrBytes(properties(jprops));
const ScoreAddresses scoreAddr = getPropScoreAddr(jprops);
if(static_cast<uInt32>(scoreAddr.size()) < numBytes)
@ -370,7 +378,7 @@ Int32 HighScoresManager::score() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::formattedScore(Int32 score, Int32 width) const
const string HighScoresManager::formattedScore(Int32 score, Int32 width) const
{
if(score <= 0)
return "";
@ -381,7 +389,8 @@ string HighScoresManager::formattedScore(Int32 score, Int32 width) const
if(scoreBCD(jprops))
{
digits = std::max(width, digits);
if(width > digits)
digits = width;
buf << std::setw(digits) << std::setfill(' ') << score;
}
else {
@ -393,7 +402,6 @@ string HighScoresManager::formattedScore(Int32 score, Int32 width) const
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::md5Props() const
{
json jprops;
@ -413,10 +421,9 @@ string HighScoresManager::md5Props() const
buf << specialAddress(jprops) << specialBCD(jprops) << specialZeroBased(jprops);
return MD5::hash(buf.view());
return MD5::hash(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::scoreInvert() const
{
json jprops;
@ -431,7 +438,7 @@ Int32 HighScoresManager::special() const
return NO_VALUE;
json jprops;
const uInt16 addr = specialAddress(properties(jprops));
uInt16 addr = specialAddress(properties(jprops));
if (addr == DEFAULT_ADDRESS)
return NO_VALUE;
@ -447,7 +454,7 @@ Int32 HighScoresManager::special() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::notes() const
const string HighScoresManager::notes() const
{
json jprops;
@ -455,14 +462,13 @@ string HighScoresManager::notes() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::convert(Int32 val, uInt32 maxVal, bool isBCD,
bool zeroBased)
Int32 HighScoresManager::convert(Int32 val, uInt32 maxVal, bool isBCD, bool zeroBased) const
{
//maxVal += zeroBased ? 0 : 1;
maxVal -= zeroBased ? 1 : 0;
const Int32 bits = isBCD
? ceil(log(maxVal) / BSPF::ln10 * 4)
: ceil(log(maxVal) / BSPF::ln2);
? ceil(log(maxVal) / log(10) * 4)
: ceil(log(maxVal) / log(2));
// limit to maxVal's bits
val %= 1 << bits;
@ -479,29 +485,30 @@ Int32 HighScoresManager::convert(Int32 val, uInt32 maxVal, bool isBCD,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool HighScoresManager::getPropBool(const json& jprops, string_view key,
bool defVal)
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, string_view key,
uInt32 defVal)
uInt32 HighScoresManager::getPropInt(const json& jprops, const string& key,
uInt32 defVal) const
{
return jprops.contains(key) ? jprops.at(key).get<uInt32>() : defVal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string HighScoresManager::getPropStr(const json& jprops, string_view key,
string_view defVal)
const string HighScoresManager::getPropStr(const json& jprops, const string& key,
const string& defVal) const
{
return jprops.contains(key) ? jprops.at(key).get<string>() : string{defVal};
return jprops.contains(key) ? jprops.at(key).get<string>() : defVal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::getPropAddr(const json& jprops, string_view key,
uInt16 defVal)
uInt16 HighScoresManager::getPropAddr(const json& jprops, const string& key,
uInt16 defVal) const
{
const string str = getPropStr(jprops, key);
@ -509,7 +516,7 @@ uInt16 HighScoresManager::getPropAddr(const json& jprops, string_view key,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HSM::ScoreAddresses HighScoresManager::getPropScoreAddr(const json& jprops)
const HSM::ScoreAddresses HighScoresManager::getPropScoreAddr(const json& jprops) const
{
ScoreAddresses scoreAddr{};
@ -536,16 +543,18 @@ HSM::ScoreAddresses HighScoresManager::getPropScoreAddr(const json& jprops)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 HighScoresManager::fromHexStr(string_view addr)
uInt16 HighScoresManager::fromHexStr(const string& addr) const
{
if(const auto pos = addr.find("0x") != std::string::npos)
addr = addr.substr(pos + 1);
string naked = addr;
return static_cast<uInt16>(BSPF::stoi<16>(addr));
if(const int pos = naked.find("0x") != std::string::npos)
naked = naked.substr(pos + 1);
return stringToIntBase16(naked);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 HighScoresManager::fromBCD(uInt8 bcd)
Int32 HighScoresManager::fromBCD(uInt8 bcd) const
{
// verify if score is legit
if ((bcd & 0xF0) >= 0xA0 || (bcd & 0xF) >= 0xA)
@ -569,7 +578,7 @@ string HighScoresManager::hash(const ScoresData& data) const
<< data.scores[r].date;
}
return MD5::hash(buf.view());
return MD5::hash(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -656,7 +665,7 @@ void HighScoresManager::loadHighScores(ScoresData& data)
clearHighScores(data);
buf << "Error: Invalid high scores data for variation " << data.variation << ".";
}
myOSystem.frameBuffer().showTextMessage(buf.view());
myOSystem.frameBuffer().showTextMessage(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -692,12 +701,12 @@ bool HighScoresManager::load(const json& hsData, ScoresData& data)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HighScoresManager::clearHighScores(ScoresData& data)
{
for(auto& s: data.scores)
for(uInt32 r = 0; r < NUM_RANKS; ++r)
{
s.score = 0;
s.special = 0;
s.name = "";
s.date = "";
data.scores[r].score = 0;
data.scores[r].special = 0;
data.scores[r].name = "";
data.scores[r].date = "";
}
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -65,7 +65,7 @@ namespace HSM {
bool specialZeroBased{false};
string notes;
// Addresses
ScoreAddresses scoreAddr{};
ScoreAddresses scoreAddr;
uInt16 varsAddr{0};
uInt16 specialAddr{0};
};
@ -112,8 +112,8 @@ class HighScoresManager
/**
Set the highscore data of game's properties
*/
static void set(Properties& props, uInt32 numVariations,
const HSM::ScoresProps& info);
void set(Properties& props, uInt32 numVariations,
const HSM::ScoresProps& info) const;
/**
Calculate the score from given parameters
@ -125,26 +125,24 @@ class HighScoresManager
// Convert the given value, using only the maximum bits required by maxVal
// and adjusted for BCD and zero based data
static Int32 convert(Int32 val, uInt32 maxVal, bool isBCD, bool zeroBased);
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
*/
static constexpr uInt32 numAddrBytes(Int32 digits, Int32 trailing) {
return (digits - trailing + 1) / 2;
}
uInt32 numAddrBytes(Int32 digits, Int32 trailing) const;
// Retrieve current values (using game's properties)
Int32 numVariations() const;
string specialLabel() const;
const string specialLabel() const;
Int32 variation() const;
Int32 score() const;
string formattedScore(Int32 score, Int32 width = -1) const;
const string formattedScore(Int32 score, Int32 width = -1) const;
bool scoreInvert() const;
Int32 special() const;
string notes() const;
const string notes() const;
// Get md5 property definition checksum
string md5Props() const;
@ -152,8 +150,8 @@ class HighScoresManager
// Peek into memory
Int16 peek(uInt16 addr) const;
void loadHighScores(HSM::ScoresData& data);
void saveHighScores(HSM::ScoresData& data) const;
void loadHighScores(HSM::ScoresData& scores);
void saveHighScores(HSM::ScoresData& scores) const;
private:
static const string VARIATIONS_COUNT;
@ -200,53 +198,53 @@ class HighScoresManager
Int32 variation(uInt16 addr, bool varBCD, bool zeroBased, uInt32 numVariations) const;
// Get individual highscore info from properties
static uInt32 numVariations(const json& jprops);
static uInt16 varAddress(const json& jprops);
static uInt16 specialAddress(const json& jprops);
static uInt32 numDigits(const json& jprops);
static uInt32 trailingZeroes(const json& jprops);
static bool scoreBCD(const json& jprops);
static bool scoreInvert(const json& jprops);
static bool varBCD(const json& jprops);
static bool varZeroBased(const json& jprops);
static string specialLabel(const json& jprops);
static bool specialBCD(const json& jprops);
static bool specialZeroBased(const json& jprops);
static string notes(const json& jprops);
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
static uInt32 numAddrBytes(const json& jprops);
uInt32 numAddrBytes(const json& jprops) const;
// Get properties
static json properties(const Properties& props);
const json properties(const Properties& props) const;
json properties(json& jprops) const;
// Get value from highscore properties for given key
static bool getPropBool(const json& jprops, string_view key,
bool defVal = false);
static uInt32 getPropInt(const json& jprops, string_view key,
uInt32 defVal = 0);
static string getPropStr(const json& jprops, string_view key,
string_view defVal = "");
static uInt16 getPropAddr(const json& jprops, string_view key,
uInt16 defVal = 0);
static HSM::ScoreAddresses getPropScoreAddr(const json& jprops);
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;
static uInt16 fromHexStr(string_view addr);
static Int32 fromBCD(uInt8 bcd);
uInt16 fromHexStr(const string& addr) const;
Int32 fromBCD(uInt8 bcd) const;
string hash(const HSM::ScoresData& data) const;
/**
Loads the current high scores for this game and variation from the given JSON object.
@param hsData The JSON to parse
@param data The loaded high score data
@param scores The loaded high score data
@return The result of the load. True on success, false on failure.
*/
static bool load(const json& hsData, HSM::ScoresData& data);
bool load(const json& hsData, HSM::ScoresData& scores);
static void clearHighScores(HSM::ScoresData& data);
void clearHighScores(HSM::ScoresData& data);
private:
// Reference to the osystem object
@ -263,5 +261,4 @@ class HighScoresManager
HighScoresManager& operator=(const HighScoresManager&) = delete;
HighScoresManager& operator=(HighScoresManager&&) = delete;
};
#endif

View File

@ -1,121 +0,0 @@
//============================================================================
//
// 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-2025 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.
//============================================================================
#ifdef IMAGE_SUPPORT
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
#include "nanojpeg_lib.hxx"
#include "tinyexif_lib.hxx"
#include "JPGLibrary.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JPGLibrary::JPGLibrary(OSystem& osystem)
: myOSystem{osystem}
{
njInit();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JPGLibrary::loadImage(const string& filename, FBSurface& surface,
VariantList& metaData)
{
std::ifstream in(filename, std::ios_base::binary | std::ios::ate);
if(!in.is_open())
throw runtime_error("No image found");
const size_t size = in.tellg();
in.clear();
in.seekg(0);
// Create space for the entire file
if(size > myFileBuffer.capacity())
myFileBuffer.reserve(size * 1.5);
if(!in.read(myFileBuffer.data(), size))
throw runtime_error("JPG image data reading failed");
if(njDecode(myFileBuffer.data(), static_cast<int>(size)))
throw runtime_error("Error decoding the JPG image");
// Read the entire image in one go
myReadInfo.buffer = njGetImage();
myReadInfo.width = njGetWidth();
myReadInfo.height = njGetHeight();
myReadInfo.pitch = myReadInfo.width * 3;
// Read the meta data we got
readMetaData(filename, metaData);
// Load image into the surface, setting the correct dimensions
loadImagetoSurface(surface);
// Cleanup
njDone();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JPGLibrary::loadImagetoSurface(FBSurface& surface)
{
// First determine if we need to resize the surface
const uInt32 iw = myReadInfo.width, ih = myReadInfo.height;
if(iw > surface.width() || ih > surface.height())
surface.resize(iw, ih);
// The source dimensions are set here; the destination dimensions are
// set by whoever owns the surface
surface.setSrcPos(0, 0);
surface.setSrcSize(iw, ih);
// Convert RGB triples into pixels and store in the surface
uInt32 *s_buf{nullptr}, s_pitch{0};
surface.basePtr(s_buf, s_pitch);
const uInt8* i_buf = myReadInfo.buffer;
const uInt32 i_pitch = myReadInfo.pitch;
const FrameBuffer& fb = myOSystem.frameBuffer();
for(uInt32 irow = 0; irow < ih; ++irow, i_buf += i_pitch, s_buf += s_pitch)
{
const uInt8* i_ptr = i_buf;
uInt32* s_ptr = s_buf;
for(uInt32 icol = 0; icol < myReadInfo.width; ++icol, i_ptr += 3)
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr + 1), *(i_ptr + 2));
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JPGLibrary::readMetaData(const string& filename, VariantList& metaData)
{
metaData.clear();
// open a stream to read just the necessary parts of the image file
std::ifstream in(filename, std::ifstream::binary);
// parse image EXIF metadata
const TinyEXIF::EXIFInfo imageEXIF(in);
if(imageEXIF.Fields)
{
// For now we only read the image description
if(!imageEXIF.ImageDescription.empty())
VarList::push_back(metaData, "ImageDescription", imageEXIF.ImageDescription);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::vector<char> JPGLibrary::myFileBuffer;
#endif // IMAGE_SUPPORT

View File

@ -1,96 +0,0 @@
//============================================================================
//
// 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-2025 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.
//============================================================================
#ifdef IMAGE_SUPPORT
#ifndef JPG_LIBRARY_HXX
#define JPG_LIBRARY_HXX
class OSystem;
class FBSurface;
#include "Variant.hxx"
#include "bspf.hxx"
/**
This class implements a thin wrapper around the nanojpeg library, and
abstracts all the irrelevant details other loading an actual image.
@author Thomas Jentzsch
*/
class JPGLibrary
{
public:
explicit JPGLibrary(OSystem& osystem);
~JPGLibrary() = default;
/**
Read a JPG image from the specified file into a FBSurface structure,
scaling the image to the surface bounds.
@param filename The filename to load the JPG image
@param surface The FBSurface into which to place the JPG data
@param metaData The meta data of the JPG image
@post On success, the FBSurface containing image data, otherwise a
runtime_error is thrown containing a more detailed
error message.
*/
void loadImage(const string& filename, FBSurface& surface,
VariantList& metaData);
private:
// Global OSystem object
OSystem& myOSystem;
// The following data remains between invocations of allocateStorage,
// and is only changed when absolutely necessary.
struct ReadInfoType {
unsigned char* buffer{nullptr};
uInt32 width{0}, height{0}, pitch{0};
};
ReadInfoType myReadInfo;
static std::vector<char> myFileBuffer;
/**
Load the JPG data from 'ReadInfo' into the FBSurface. The surface
is resized as necessary to accommodate the data.
@param surface The FBSurface into which to place the JPG data
*/
void loadImagetoSurface(FBSurface& surface);
/**
Read EXIF meta data chunks from the image.
@param filename The filename to load the JPG image
@param metaData The meta data of the JPG image
*/
static void readMetaData(const string& filename, VariantList& metaData);
private:
// Following constructors and assignment operators not supported
JPGLibrary() = delete;
JPGLibrary(const JPGLibrary&) = delete;
JPGLibrary(JPGLibrary&&) = delete;
JPGLibrary& operator=(const JPGLibrary&) = delete;
JPGLibrary& operator=(JPGLibrary&&) = delete;
};
#endif
#endif // IMAGE_SUPPORT

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -16,27 +16,28 @@
//============================================================================
#include "JoyMap.hxx"
#include "Logger.hxx"
#include "jsonDefinitions.hxx"
#include "Logger.hxx"
using json = nlohmann::json;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(Event::Type event, const JoyMapping& mapping)
void JoyMap::add(const Event::Type event, const JoyMapping& mapping)
{
myMap[mapping] = event;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(Event::Type event, EventMode mode, int button,
JoyAxis axis, JoyDir adir, int hat, JoyHatDir hdir)
void JoyMap::add(const Event::Type event, const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat, const JoyHatDir hdir)
{
add(event, JoyMapping(mode, button, axis, adir, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(Event::Type event, EventMode mode, int button,
int hat, JoyHatDir hdir)
void JoyMap::add(const Event::Type event, const EventMode mode, const int button,
const int hat, const JoyHatDir hdir)
{
add(event, JoyMapping(mode, button, hat, hdir));
}
@ -48,13 +49,15 @@ void JoyMap::erase(const JoyMapping& mapping)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::erase(EventMode mode, int button, JoyAxis axis, JoyDir adir)
void JoyMap::erase(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir)
{
erase(JoyMapping(mode, button, axis, adir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::erase(EventMode mode, int button, int hat, JoyHatDir hdir)
void JoyMap::erase(const EventMode mode, const int button,
const int hat, const JoyHatDir hdir)
{
erase(JoyMapping(mode, button, hat, hdir));
}
@ -79,15 +82,15 @@ Event::Type JoyMap::get(const JoyMapping& mapping) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type JoyMap::get(EventMode mode, int button,
JoyAxis axis, JoyDir adir) const
Event::Type JoyMap::get(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir) const
{
return get(JoyMapping(mode, button, axis, adir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type JoyMap::get(EventMode mode, int button,
int hat, JoyHatDir hdir) const
Event::Type JoyMap::get(const EventMode mode, const int button,
const int hat, const JoyHatDir hdir) const
{
return get(JoyMapping(mode, button, hat, hdir));
}
@ -95,18 +98,21 @@ Event::Type JoyMap::get(EventMode mode, int button,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JoyMap::check(const JoyMapping& mapping) const
{
return myMap.contains(mapping);
const auto find = myMap.find(mapping);
return (find != myMap.end());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JoyMap::check(EventMode mode, int button, JoyAxis axis, JoyDir adir,
int hat, JoyHatDir hdir) const
bool JoyMap::check(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat, const JoyHatDir hdir) const
{
return check(JoyMapping(mode, button, axis, adir, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string JoyMap::getDesc(Event::Type event, const JoyMapping& mapping)
string JoyMap::getDesc(const Event::Type event, const JoyMapping& mapping) const
{
ostringstream buf;
@ -152,8 +158,7 @@ string JoyMap::getDesc(Event::Type event, const JoyMapping& mapping)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string JoyMap::getEventMappingDesc(int stick, Event::Type event,
EventMode mode) const
string JoyMap::getEventMappingDesc(int stick, const Event::Type event, const EventMode mode) const
{
ostringstream buf;
@ -161,7 +166,7 @@ string JoyMap::getEventMappingDesc(int stick, Event::Type event,
{
if (_event == event && _mapping.mode == mode)
{
if(!buf.view().empty())
if(buf.str() != "")
buf << ", ";
buf << "C" << stick << getDesc(event, _mapping);
}
@ -170,8 +175,7 @@ string JoyMap::getEventMappingDesc(int stick, Event::Type event,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JoyMap::JoyMappingArray JoyMap::getEventMapping(Event::Type event,
EventMode mode) const
JoyMap::JoyMappingArray JoyMap::getEventMapping(const Event::Type event, const EventMode mode) const
{
JoyMappingArray map;
@ -183,31 +187,32 @@ JoyMap::JoyMappingArray JoyMap::getEventMapping(Event::Type event,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json JoyMap::saveMapping(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::ranges::sort(sortedMap, [](const MapType& a, const MapType& b)
{
// Event::Type first
if(a.first.button != b.first.button)
return a.first.button < b.first.button;
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.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.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.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;
if(a.first.hdir != b.first.hdir)
return a.first.hdir < b.first.hdir;
return a.second < b.second;
}
return a.second < b.second;
}
);
json eventMappings = json::array();
@ -238,26 +243,16 @@ json JoyMap::saveMapping(EventMode mode) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int JoyMap::loadMapping(const json& eventMappings, EventMode mode)
int JoyMap::loadMapping(const json& eventMappings, const EventMode mode)
{
int i = 0;
for(const json& eventMapping : eventMappings) {
const int button = eventMapping.contains("button")
? eventMapping.at("button").get<int>()
: JOY_CTRL_NONE;
const JoyAxis axis = eventMapping.contains("axis")
? eventMapping.at("axis").get<JoyAxis>()
: JoyAxis::NONE;
const JoyDir axisDirection = eventMapping.contains("axis")
? eventMapping.at("axisDirection").get<JoyDir>()
: JoyDir::NONE;
const int hat = eventMapping.contains("hat")
? eventMapping.at("hat").get<int>()
: -1;
const JoyHatDir hatDirection = eventMapping.contains("hat")
? eventMapping.at("hatDirection").get<JoyHatDir>()
: JoyHatDir::CENTER;
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;
JoyDir axisDirection = eventMapping.contains("axis") ? eventMapping.at("axisDirection").get<JoyDir>() : JoyDir::NONE;
int hat = eventMapping.contains("hat") ? eventMapping.at("hat").get<int>() : -1;
JoyHatDir hatDirection = eventMapping.contains("hat") ? eventMapping.at("hatDirection").get<JoyHatDir>() : JoyHatDir::CENTER;
try {
// avoid blocking mappings for NoType events
@ -284,17 +279,17 @@ int JoyMap::loadMapping(const json& eventMappings, EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json JoyMap::convertLegacyMapping(string lst)
json JoyMap::convertLegacyMapping(string list)
{
json eventMappings = json::array();
// Since istringstream swallows whitespace, we have to make the
// delimiters be spaces
std::ranges::replace(lst, '|', ' ');
std::ranges::replace(lst, ':', ' ');
std::ranges::replace(lst, ',', ' ');
std::replace(list.begin(), list.end(), '|', ' ');
std::replace(list.begin(), list.end(), ':', ' ');
std::replace(list.begin(), list.end(), ',', ' ');
istringstream buf(lst);
istringstream buf(list);
int event = 0, button = 0, axis = 0, adir = 0, hat = 0, hdir = 0;
while(buf >> event && buf >> button
@ -303,18 +298,18 @@ json JoyMap::convertLegacyMapping(string lst)
{
json eventMapping = json::object();
eventMapping["event"] = static_cast<Event::Type>(event);
eventMapping["event"] = Event::Type(event);
if(button != JOY_CTRL_NONE) eventMapping["button"] = button;
if(static_cast<JoyAxis>(axis) != JoyAxis::NONE) {
eventMapping["axis"] = static_cast<JoyAxis>(axis);
eventMapping["axisDirection"] = static_cast<JoyDir>(adir);
eventMapping["axis"] = JoyAxis(axis);
eventMapping["axisDirection"] = JoyDir(adir);
}
if(hat != -1) {
eventMapping["hat"] = hat;
eventMapping["hatDirection"] = static_cast<JoyHatDir>(hdir);
eventMapping["hatDirection"] = JoyHatDir(hdir);
}
eventMappings.push_back(eventMapping);
@ -324,7 +319,7 @@ json JoyMap::convertLegacyMapping(string lst)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::eraseMode(EventMode mode)
void JoyMap::eraseMode(const EventMode mode)
{
for(auto item = myMap.begin(); item != myMap.end();)
if(item->first.mode == mode) {
@ -335,7 +330,7 @@ void JoyMap::eraseMode(EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::eraseEvent(Event::Type event, EventMode mode)
void JoyMap::eraseEvent(const Event::Type event, const EventMode mode)
{
for(auto item = myMap.begin(); item != myMap.end();)
if(item->second == event && item->first.mode == mode) {

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -58,7 +58,6 @@ class JoyMap
axis{JoyAxis::NONE}, adir{JoyDir::NONE},
hat{c_hat}, hdir{c_hdir} { }
~JoyMapping() = default;
JoyMapping(const JoyMapping&) = default;
JoyMapping& operator=(const JoyMapping&) = default;
JoyMapping(JoyMapping&&) = default;
@ -78,61 +77,64 @@ class JoyMap
using JoyMappingArray = std::vector<JoyMapping>;
JoyMap() = default;
~JoyMap() = default;
/** Add new mapping for given event */
void add(Event::Type event, const JoyMapping& mapping);
void add(Event::Type event, EventMode mode, int button,
JoyAxis axis, JoyDir adir,
int hat = JOY_CTRL_NONE, JoyHatDir hdir = JoyHatDir::CENTER);
void add(Event::Type event, EventMode mode, int button,
int hat, JoyHatDir hdir);
void add(const Event::Type event, const JoyMapping& mapping);
void add(const Event::Type event, const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat = JOY_CTRL_NONE, const JoyHatDir hdir = JoyHatDir::CENTER);
void add(const Event::Type event, const EventMode mode, const int button,
const int hat, const JoyHatDir hdir);
/** Erase mapping */
void erase(const JoyMapping& mapping);
void erase(EventMode mode, int button, JoyAxis axis, JoyDir adir);
void erase(EventMode mode, int button, int hat, JoyHatDir hdir);
void erase(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir);
void erase(const EventMode mode, const int button,
const int hat, const JoyHatDir hdir);
/** Get event for mapping */
Event::Type get(const JoyMapping& mapping) const;
Event::Type get(EventMode mode, int button, JoyAxis axis = JoyAxis::NONE,
JoyDir adir = JoyDir::NONE) const;
Event::Type get(EventMode mode, int button, int hat, JoyHatDir hdir) const;
Event::Type get(const EventMode mode, const int button,
const JoyAxis axis = JoyAxis::NONE, const JoyDir adir = JoyDir::NONE) const;
Event::Type get(const EventMode mode, const int button,
const int hat, const JoyHatDir hdir) const;
/** Check if a mapping exists */
bool check(const JoyMapping& mapping) const;
bool check(EventMode mode, int button, JoyAxis axis, JoyDir adir,
int hat = JOY_CTRL_NONE, JoyHatDir hdir = JoyHatDir::CENTER) const;
bool check(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat = JOY_CTRL_NONE, const JoyHatDir hdir = JoyHatDir::CENTER) const;
/** Get mapping description */
string getEventMappingDesc(int stick, Event::Type event, EventMode mode) const;
string getEventMappingDesc(int stick, const Event::Type event, const EventMode mode) const;
JoyMappingArray getEventMapping(Event::Type event, EventMode mode) const;
JoyMappingArray getEventMapping(const Event::Type event, const EventMode mode) const;
nlohmann::json saveMapping(EventMode mode) const;
int loadMapping(const nlohmann::json& eventMappings, EventMode mode);
nlohmann::json saveMapping(const EventMode mode) const;
int loadMapping(const nlohmann::json& eventMappings, const EventMode mode);
static nlohmann::json convertLegacyMapping(string lst);
static nlohmann::json convertLegacyMapping(string list);
/** Erase all mappings for given mode */
void eraseMode(EventMode mode);
void eraseMode(const EventMode mode);
/** Erase given event's mapping for given mode */
void eraseEvent(Event::Type event, EventMode mode);
void eraseEvent(const Event::Type event, const EventMode mode);
/** clear all mappings for a modes */
// void clear() { myMap.clear(); }
size_t size() const { return myMap.size(); }
size_t size() { return myMap.size(); }
private:
static string getDesc(Event::Type event, const JoyMapping& mapping);
string getDesc(const Event::Type event, const JoyMapping& mapping) const;
struct JoyHash {
size_t operator()(const JoyMapping& m)const {
return std::hash<uInt64>()((static_cast<uInt64>(m.mode)) // 3 bits
+ ((static_cast<uInt64>(m.button)) * 7) // 3 bits
+ (((static_cast<uInt64>(m.axis)) << 0) // 3 bits
| ((static_cast<uInt64>(m.adir)) << 3) // 2 bits
| ((static_cast<uInt64>(m.hat )) << 5) // 1 bit
| ((static_cast<uInt64>(m.hdir)) << 6) // 2 bits
return std::hash<uInt64>()((uInt64(m.mode)) // 3 bits
+ ((uInt64(m.button)) * 7) // 3 bits
+ (((uInt64(m.axis)) << 0) // 2 bits
| ((uInt64(m.adir)) << 2) // 2 bits
| ((uInt64(m.hat )) << 4) // 1 bit
| ((uInt64(m.hdir)) << 5) // 2 bits
) * 61
);
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -18,17 +18,18 @@
#include "KeyMap.hxx"
#include "Logger.hxx"
#include "jsonDefinitions.hxx"
#include <map>
using json = nlohmann::json;
namespace {
json serializeModkeyMask(int mask)
{
if(mask == StellaMod::KBDM_NONE) return {};
if(mask == StellaMod::KBDM_NONE) return json(nullptr);
json serializedMask = json::array();
for(const StellaMod mod: {
for(StellaMod mod: {
StellaMod::KBDM_CTRL,
StellaMod::KBDM_SHIFT,
StellaMod::KBDM_ALT,
@ -65,16 +66,16 @@ namespace {
return mask;
}
} // namespace
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::add(Event::Type event, const Mapping& mapping)
void KeyMap::add(const Event::Type event, const Mapping& mapping)
{
myMap[convertMod(mapping)] = event;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::add(Event::Type event, EventMode mode, int key, int mod)
void KeyMap::add(const Event::Type event, const EventMode mode, const int key, const int mod)
{
add(event, Mapping(mode, key, mod));
}
@ -86,7 +87,7 @@ void KeyMap::erase(const Mapping& mapping)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::erase(EventMode mode, int key, int mod)
void KeyMap::erase(const EventMode mode, const int key, const int mod)
{
erase(Mapping(mode, key, mod));
}
@ -114,7 +115,7 @@ Event::Type KeyMap::get(const Mapping& mapping) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type KeyMap::get(EventMode mode, int key, int mod) const
Event::Type KeyMap::get(const EventMode mode, const int key, const int mod) const
{
return get(Mapping(mode, key, mod));
}
@ -122,17 +123,19 @@ Event::Type KeyMap::get(EventMode mode, int key, int mod) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool KeyMap::check(const Mapping& mapping) const
{
return myMap.contains(convertMod(mapping));
const auto find = myMap.find(convertMod(mapping));
return (find != myMap.end());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool KeyMap::check(EventMode mode, int key, int mod) const
bool KeyMap::check(const EventMode mode, const int key, const int mod) const
{
return check(Mapping(mode, key, mod));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string KeyMap::getDesc(const Mapping& mapping)
string KeyMap::getDesc(const Mapping& mapping) const
{
ostringstream buf;
#if defined(BSPF_MACOS) || defined(MACOS_KEYS)
@ -181,13 +184,13 @@ string KeyMap::getDesc(const Mapping& mapping)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string KeyMap::getDesc(EventMode mode, int key, int mod)
string KeyMap::getDesc(const EventMode mode, const int key, const int mod) const
{
return getDesc(Mapping(mode, key, mod));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string KeyMap::getEventMappingDesc(Event::Type event, EventMode mode) const
string KeyMap::getEventMappingDesc(const Event::Type event, const EventMode mode) const
{
ostringstream buf;
@ -195,7 +198,7 @@ string KeyMap::getEventMappingDesc(Event::Type event, EventMode mode) const
{
if (_event == event && _mapping.mode == mode)
{
if(!buf.view().empty())
if(buf.str() != "")
buf << ", ";
buf << getDesc(_mapping);
}
@ -204,25 +207,25 @@ string KeyMap::getEventMappingDesc(Event::Type event, EventMode mode) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyMap::MappingArray KeyMap::getEventMapping(Event::Type event,
EventMode mode) const
KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const EventMode mode) const
{
MappingArray ma;
MappingArray map;
for (const auto& [_mapping, _event]: myMap)
if (_event == event && _mapping.mode == mode)
ma.push_back(_mapping);
map.push_back(_mapping);
return ma;
return map;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json KeyMap::saveMapping(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::ranges::sort(sortedMap, [](const MapType& a, const MapType& b)
std::sort(sortedMap.begin(), sortedMap.end(),
[](const MapType& a, const MapType& b)
{
// Event::Type first
if(a.first.key != b.first.key)
@ -255,8 +258,7 @@ json KeyMap::saveMapping(EventMode mode) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int KeyMap::loadMapping(const json& mappings, EventMode mode)
{
int KeyMap::loadMapping(const json& mappings, const EventMode mode) {
int i = 0;
for(const json& mapping : mappings)
@ -283,28 +285,27 @@ int KeyMap::loadMapping(const json& mappings, EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json KeyMap::convertLegacyMapping(string_view lm)
json KeyMap::convertLegacyMapping(string list)
{
json convertedMapping = json::array();
// Since istringstream swallows whitespace, we have to make the
// delimiters be spaces
string lst{lm};
std::ranges::replace(lst, '|', ' ');
std::ranges::replace(lst, ':', ' ');
std::ranges::replace(lst, ',', ' ');
istringstream buf(lst);
std::replace(list.begin(), list.end(), '|', ' ');
std::replace(list.begin(), list.end(), ':', ' ');
std::replace(list.begin(), list.end(), ',', ' ');
istringstream buf(list);
int event = 0, key = 0, mod = 0;
while(buf >> event && buf >> key && buf >> mod)
{
json mapping = json::object();
mapping["event"] = static_cast<Event::Type>(event);
mapping["key"] = static_cast<StellaKey>(key);
mapping["event"] = Event::Type(event);
mapping["key"] = StellaKey(key);
if(static_cast<StellaMod>(mod) != StellaMod::KBDM_NONE)
mapping["mod"] = serializeModkeyMask(static_cast<StellaMod>(mod));
if(StellaMod(mod) != StellaMod::KBDM_NONE)
mapping["mod"] = serializeModkeyMask(StellaMod(mod));
convertedMapping.push_back(mapping);
}
@ -313,7 +314,7 @@ json KeyMap::convertLegacyMapping(string_view lm)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::eraseMode(EventMode mode)
void KeyMap::eraseMode(const EventMode mode)
{
for(auto item = myMap.begin(); item != myMap.end();)
if(item->first.mode == mode) {
@ -324,7 +325,7 @@ void KeyMap::eraseMode(EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KeyMap::eraseEvent(Event::Type event, EventMode mode)
void KeyMap::eraseEvent(const Event::Type event, const EventMode mode)
{
for(auto item = myMap.begin(); item != myMap.end();)
if(item->second == event && item->first.mode == mode) {
@ -335,7 +336,7 @@ void KeyMap::eraseEvent(Event::Type event, EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyMap::Mapping KeyMap::convertMod(const Mapping& mapping)
KeyMap::Mapping KeyMap::convertMod(const Mapping& mapping) const
{
Mapping m = mapping;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -42,7 +42,6 @@ class KeyMap
: mode{c_mode}, key{c_key}, mod{c_mod} { }
explicit Mapping(EventMode c_mode, int c_key, int c_mod)
: mode{c_mode}, key{static_cast<StellaKey>(c_key)}, mod{static_cast<StellaMod>(c_mod)} { }
~Mapping() = default;
Mapping(const Mapping&) = default;
Mapping& operator=(const Mapping&) = default;
Mapping(Mapping&&) = default;
@ -62,42 +61,41 @@ class KeyMap
using MappingArray = std::vector<Mapping>;
KeyMap() = default;
~KeyMap() = default;
/** Add new mapping for given event */
void add(Event::Type event, const Mapping& mapping);
void add(Event::Type event, EventMode mode, int key, int mod);
void add(const Event::Type event, const Mapping& mapping);
void add(const Event::Type event, const EventMode mode, const int key, const int mod);
/** Erase mapping */
void erase(const Mapping& mapping);
void erase(EventMode mode, int key, int mod);
void erase(const EventMode mode, const int key, const int mod);
/** Get event for mapping */
Event::Type get(const Mapping& mapping) const;
Event::Type get(EventMode mode, int key, int mod) const;
Event::Type get(const EventMode mode, const int key, const int mod) const;
/** Check if a mapping exists */
bool check(const Mapping& mapping) const;
bool check(EventMode mode, int key, int mod) const;
bool check(const EventMode mode, const int key, const int mod) const;
/** Get mapping description */
static string getDesc(const Mapping& mapping);
static string getDesc(EventMode mode, int key, int mod);
string getDesc(const Mapping& mapping) const;
string getDesc(const EventMode mode, const int key, const int mod) const;
/** Get the mapping description(s) for given event and mode */
string getEventMappingDesc(Event::Type event, EventMode mode) const;
string getEventMappingDesc(const Event::Type event, const EventMode mode) const;
MappingArray getEventMapping(Event::Type event, EventMode mode) const;
MappingArray getEventMapping(const Event::Type event, const EventMode mode) const;
nlohmann::json saveMapping(EventMode mode) const;
int loadMapping(const nlohmann::json& mapping, EventMode mode);
nlohmann::json saveMapping(const EventMode mode) const;
int loadMapping(const nlohmann::json& mapping, const EventMode mode);
static nlohmann::json convertLegacyMapping(string_view lm);
static nlohmann::json convertLegacyMapping(string list);
/** Erase all mappings for given mode */
void eraseMode(EventMode mode);
void eraseMode(const EventMode mode);
/** Erase given event's mapping for given mode */
void eraseEvent(Event::Type event, EventMode mode);
void eraseEvent(const Event::Type event, const EventMode mode);
/** clear all mappings for a modes */
// void clear() { myMap.clear(); }
size_t size() { return myMap.size(); }
@ -106,16 +104,16 @@ class KeyMap
private:
//** Convert modifiers */
static Mapping convertMod(const Mapping& mapping);
Mapping convertMod(const Mapping& mapping) const;
struct KeyHash {
size_t operator()(const Mapping& m) const {
return std::hash<uInt64>()((static_cast<uInt64>(m.mode)) // 3 bits
+ ((static_cast<uInt64>(m.key)) * 7) // 8 bits
+ (((static_cast<uInt64>((m.mod & KBDM_SHIFT) != 0) << 0)) // 1 bit
| ((static_cast<uInt64>((m.mod & KBDM_ALT ) != 0) << 1)) // 1 bit
| ((static_cast<uInt64>((m.mod & KBDM_GUI ) != 0) << 2)) // 1 bit
| ((static_cast<uInt64>((m.mod & KBDM_CTRL ) != 0) << 3)) // 1 bit
return std::hash<uInt64>()((uInt64(m.mode)) // 3 bits
+ ((uInt64(m.key)) * 7) // 8 bits
+ (((uInt64((m.mod & KBDM_SHIFT) != 0) << 0)) // 1 bit
| ((uInt64((m.mod & KBDM_ALT ) != 0) << 1)) // 1 bit
| ((uInt64((m.mod & KBDM_GUI ) != 0) << 2)) // 1 bit
| ((uInt64((m.mod & KBDM_CTRL ) != 0) << 3)) // 1 bit
) * 2047
);
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -59,8 +59,7 @@ class LinkedObjectPool
/*
Create a pool of size CAPACITY; the active list starts out empty.
*/
LinkedObjectPool() { resize(CAPACITY); }
~LinkedObjectPool() = default;
LinkedObjectPool<T, CAPACITY>() { resize(CAPACITY); }
/**
Return node data that the 'current' iterator points to.
@ -288,6 +287,6 @@ class LinkedObjectPool
LinkedObjectPool& operator=(LinkedObjectPool&&) = delete;
};
} // namespace Common
} // Namespace Common
#endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -26,46 +26,44 @@ Logger& Logger::instance()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::log(string_view message, Level level)
void Logger::log(const string& message, Level level)
{
instance().logMessage(message, level);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::error(string_view message)
void Logger::error(const string& message)
{
instance().logMessage(message, Level::ERR);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::info(string_view message)
void Logger::info(const string& message)
{
instance().logMessage(message, Level::INFO);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::debug(string_view message)
void Logger::debug(const string& message)
{
instance().logMessage(message, Level::DEBUG);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::logMessage(string_view message, Level level)
void Logger::logMessage(const string& message, Level level)
{
const std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::mutex> lock(mutex);
if(level == Logger::Level::ERR)
{
cout << message << '\n' << std::flush;
myLogMessages += message;
myLogMessages += "\n";
cout << message << endl << std::flush;
myLogMessages += message + "\n";
}
else if(static_cast<int>(level) <= myLogLevel ||
level == Logger::Level::ALWAYS)
{
if(myLogToConsole)
cout << message << '\n' << std::flush;
myLogMessages += message;
myLogMessages += "\n";
cout << message << endl << std::flush;
myLogMessages += message + "\n";
}
}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -27,7 +27,7 @@ class Logger {
public:
enum class Level: uInt8 {
enum class Level {
ERR = 0, // cannot use ERROR???
INFO = 1,
DEBUG = 2,
@ -40,13 +40,13 @@ class Logger {
static Logger& instance();
static void log(string_view message, Level level = Level::ALWAYS);
static void log(const string& message, Level level = Level::ALWAYS);
static void error(string_view message);
static void error(const string& message);
static void info(string_view message);
static void info(const string& message);
static void debug(string_view message);
static void debug(const string& message);
void setLogParameters(int logLevel, bool logToConsole);
void setLogParameters(Level logLevel, bool logToConsole);
@ -55,7 +55,6 @@ class Logger {
protected:
Logger() = default;
~Logger() = default;
private:
int myLogLevel{static_cast<int>(Level::MAX)};
@ -67,7 +66,7 @@ class Logger {
std::mutex mutex;
private:
void logMessage(string_view message, Level level);
void logMessage(const string& message, Level level);
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -181,15 +181,6 @@ class MediaFactory
#endif
}
static bool supportsURL()
{
#if defined(SDL_SUPPORT)
return SDLSupportsURL();
#else
return false;
#endif
}
static bool openURL(const string& url)
{
#if defined(SDL_SUPPORT)
@ -202,7 +193,6 @@ class MediaFactory
private:
// Following constructors and assignment operators not supported
MediaFactory() = delete;
~MediaFactory() = delete;
MediaFactory(const MediaFactory&) = delete;
MediaFactory(MediaFactory&&) = delete;
MediaFactory& operator=(const MediaFactory&) = delete;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -23,12 +23,12 @@
#include "MouseControl.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MouseControl::MouseControl(Console& console, string_view mode)
MouseControl::MouseControl(Console& console, const string& mode)
: myProps{console.properties()},
myLeftController{console.leftController()},
myRightController{console.rightController()}
{
istringstream m_axis(string{mode}); // TODO: fixed in C++20
istringstream m_axis(mode);
string m_mode;
m_axis >> m_mode;
@ -41,10 +41,8 @@ MouseControl::MouseControl(Console& console, string_view mode)
m_mode[0] >= '0' && m_mode[0] <= '8' &&
m_mode[1] >= '0' && m_mode[1] <= '8')
{
const auto xaxis = static_cast<MouseControl::Type>
(static_cast<int>(m_mode[0]) - '0');
const auto yaxis = static_cast<MouseControl::Type>
(static_cast<int>(m_mode[1]) - '0');
const MouseControl::Type xaxis = static_cast<MouseControl::Type>(static_cast<int>(m_mode[0]) - '0');
const MouseControl::Type yaxis = static_cast<MouseControl::Type>(static_cast<int>(m_mode[1]) - '0');
ostringstream msg;
Controller::Type xtype = Controller::Type::Joystick, ytype = Controller::Type::Joystick;
int xid = -1, yid = -1;
@ -53,52 +51,49 @@ MouseControl::MouseControl(Console& console, string_view mode)
Controller::Type& type, int& id) {
switch(axis)
{
using enum MouseControl::Type;
case NoControl:
case MouseControl::Type::NoControl:
msg << "not used";
break;
case LeftPaddleA:
case MouseControl::Type::LeftPaddleA:
type = Controller::Type::Paddles;
id = 0;
msg << "Left Paddle A";
break;
case LeftPaddleB:
case MouseControl::Type::LeftPaddleB:
type = Controller::Type::Paddles;
id = 1;
msg << "Left Paddle B";
break;
case RightPaddleA:
case MouseControl::Type::RightPaddleA:
type = Controller::Type::Paddles;
id = 2;
msg << "Right Paddle A";
break;
case RightPaddleB:
case MouseControl::Type::RightPaddleB:
type = Controller::Type::Paddles;
id = 3;
msg << "Right Paddle B";
break;
case LeftDriving:
case MouseControl::Type::LeftDriving:
type = Controller::Type::Driving;
id = 0;
msg << "Left Driving";
break;
case RightDriving:
case MouseControl::Type::RightDriving:
type = Controller::Type::Driving;
id = 1;
msg << "Right Driving";
break;
case LeftMindLink:
case MouseControl::Type::LeftMindLink:
type = Controller::Type::MindLink;
id = 0;
msg << "Left MindLink";
break;
case RightMindLink:
case MouseControl::Type::RightMindLink:
type = Controller::Type::MindLink;
id = 1;
msg << "Right MindLink";
break;
default:
break; // Not supposed to get here
}
};
@ -107,7 +102,7 @@ MouseControl::MouseControl(Console& console, string_view mode)
msg << ", Y-axis is ";
MControlToController(yaxis, ytype, yid);
myModeList.emplace_back(xtype, xid, ytype, yid, msg.view());
myModeList.emplace_back(xtype, xid, ytype, yid, msg.str());
}
// Now consider the possible modes for the mouse based on the left
@ -132,12 +127,12 @@ MouseControl::MouseControl(Console& console, string_view mode)
Paddles::setDigitalPaddleRange(m_range);
// If the mouse isn't used at all, we still need one item in the list
if(myModeList.empty())
if(myModeList.size() == 0)
myModeList.emplace_back("Mouse not used for current controllers");
#if 0
for(const auto& m: myModeList)
cerr << m << '\n';
cerr << m << endl;
#endif
}
@ -172,9 +167,9 @@ void MouseControl::addLeftControllerModes(bool noswap)
{
ostringstream msg;
msg << "Mouse is left " << myLeftController.name() << " controller";
const Controller::Type type = myLeftController.type();
const int id = noswap ? 0 : 1;
myModeList.emplace_back(type, id, type, id, msg.view());
Controller::Type type = myLeftController.type();
int id = noswap ? 0 : 1;
myModeList.emplace_back(type, id, type, id, msg.str());
}
}
}
@ -193,9 +188,9 @@ void MouseControl::addRightControllerModes(bool noswap)
{
ostringstream msg;
msg << "Mouse is right " << myRightController.name() << " controller";
const Controller::Type type = myRightController.type();
const int id = noswap ? 1 : 0;
myModeList.emplace_back(type, id, type, id, msg.view());
Controller::Type type = myRightController.type();
int id = noswap ? 1 : 0;
myModeList.emplace_back(type, id, type, id, msg.str());
}
}
}
@ -206,11 +201,11 @@ void MouseControl::addPaddleModes(int lport, int rport, int lname, int rname)
const Controller::Type type = Controller::Type::Paddles;
ostringstream msg;
msg << "Mouse is Paddle " << lname << " controller";
const MouseMode mode0(type, lport, type, lport, msg.view());
const MouseMode mode0(type, lport, type, lport, msg.str());
msg.str("");
msg << "Mouse is Paddle " << rname << " controller";
const MouseMode mode1(type, rport, type, rport, msg.view());
const MouseMode mode1(type, rport, type, rport, msg.str());
if(BSPF::equalsIgnoreCase(myProps.get(PropType::Controller_SwapPaddles), "NO"))
{

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -19,9 +19,9 @@
#define MOUSE_CONTROL_HXX
class Console;
class Controller;
class Properties;
#include "Control.hxx"
#include "bspf.hxx"
/**
@ -41,7 +41,7 @@ class MouseControl
/**
Enumeration of mouse axis control types
*/
enum class Type: uInt8
enum class Type
{
LeftPaddleA = 0, LeftPaddleB, RightPaddleA, RightPaddleB,
LeftDriving, RightDriving, LeftMindLink, RightMindLink,
@ -55,8 +55,7 @@ class MouseControl
@param console The console in use by the system
@param mode Contains information about how to use the mouse axes/buttons
*/
MouseControl(Console& console, string_view mode);
~MouseControl() = default;
MouseControl(Console& console, const string& mode);
/**
Cycle through each available mouse control mode
@ -74,7 +73,7 @@ class MouseControl
void addLeftControllerModes(bool noswap);
void addRightControllerModes(bool noswap);
void addPaddleModes(int lport, int rport, int lname, int rname);
static bool controllerSupportsMouse(Controller& controller);
bool controllerSupportsMouse(Controller& controller);
private:
const Properties& myProps;
@ -86,10 +85,10 @@ class MouseControl
int xid{-1}, yid{-1};
string message;
explicit MouseMode(string_view msg = "") : message{msg} { }
explicit MouseMode(const string& msg = "") : message{msg} { }
MouseMode(Controller::Type xt, int xi,
Controller::Type yt, int yi,
string_view msg)
const string& msg)
: xtype{xt}, ytype{yt}, xid{xi}, yid{yi}, message{msg} { }
friend ostream& operator<<(ostream& os, const MouseMode& mm)

View File

@ -8,16 +8,19 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 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.
//============================================================================
#include "Logger.hxx"
#include "OSystem.hxx"
#include "Console.hxx"
#include "Joystick.hxx"
#include "Paddles.hxx"
#include "MindLink.hxx"
#include "PointingDevice.hxx"
#include "Driving.hxx"
#include "Settings.hxx"
@ -45,7 +48,7 @@ PhysicalJoystickHandler::PhysicalJoystickHandler(
}
json mappings;
const string_view serializedMapping = myOSystem.settings().getString("joymap");
const string& serializedMapping = myOSystem.settings().getString("joymap");
try {
mappings = json::parse(serializedMapping);
@ -66,11 +69,11 @@ PhysicalJoystickHandler::PhysicalJoystickHandler(
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json PhysicalJoystickHandler::convertLegacyMapping(string_view mapping)
json PhysicalJoystickHandler::convertLegacyMapping(const string& mapping)
{
constexpr char CTRL_DELIM = '^';
istringstream buf(string{mapping}); // TODO: fixed in C++20
istringstream buf(mapping);
string joymap, joyname;
getline(buf, joymap, CTRL_DELIM); // event list size, ignore
@ -117,7 +120,7 @@ int PhysicalJoystickHandler::add(const PhysicalJoystickPtr& stick)
{
ostringstream name;
name << stick->name << " #" << count+1;
stick->name = name.view();
stick->name = name.str();
}
stick->type = PhysicalJoystick::Type::REGULAR;
}
@ -146,9 +149,11 @@ int PhysicalJoystickHandler::add(const PhysicalJoystickPtr& stick)
// We're potentially swapping out an input device behind the back of
// the Event system, so we make sure all Stelladaptor-generated events
// are reset
for(int port = 0; port < NUM_PORTS; ++port) // NOLINT
for(int axis = 0; axis < NUM_SA_AXIS; ++axis) // NOLINT
for(int port = 0; port < NUM_PORTS; ++port)
{
for(int axis = 0; axis < NUM_SA_AXIS; ++axis)
myEvent.set(SA_Axis[port][axis], 0);
}
return stick->ID;
}
@ -166,16 +171,16 @@ void PhysicalJoystickHandler::addToDatabase(const PhysicalJoystickPtr& stick)
}
else // adding for the first time
{
const StickInfo info("", stick);
StickInfo info("", stick);
myDatabase.emplace(stick->name, info);
setStickDefaultMapping(stick->ID, Event::NoType, EventMode::kMenuMode);
setStickDefaultMapping(stick->ID, Event::NoType, EventMode::kEmulationMode);
}
ostringstream buf;
buf << "Added joystick " << stick->ID << ":\n"
<< " " << stick->about() << '\n';
Logger::info(buf.view());
buf << "Added joystick " << stick->ID << ":" << endl
<< " " << stick->about() << endl;
Logger::info(buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -188,15 +193,15 @@ bool PhysicalJoystickHandler::remove(int id)
// So we use the 'active' joystick list to access them
try
{
const PhysicalJoystickPtr stick = mySticks.at(id);
PhysicalJoystickPtr stick = mySticks.at(id);
const auto it = myDatabase.find(stick->name);
if(it != myDatabase.end() && it->second.joy == stick)
{
ostringstream buf;
buf << "Removed joystick " << mySticks[id]->ID << ":\n"
<< " " << mySticks[id]->about() << '\n';
Logger::info(buf.view());
buf << "Removed joystick " << mySticks[id]->ID << ":" << endl
<< " " << mySticks[id]->about() << endl;
Logger::info(buf.str());
// Remove joystick, but remember mapping
it->second.mapping = stick->getMap();
@ -208,13 +213,14 @@ bool PhysicalJoystickHandler::remove(int id)
}
catch(const std::out_of_range&)
{
return false;
// fall through to indicate remove failed
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::remove(string_view name)
bool PhysicalJoystickHandler::remove(const string& name)
{
const auto it = myDatabase.find(name);
if(it != myDatabase.end() && it->second.joy == nullptr)
@ -226,18 +232,7 @@ bool PhysicalJoystickHandler::remove(string_view name)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setPort(string_view name, PhysicalJoystick::Port port)
{
const auto it = myDatabase.find(name);
if(it != myDatabase.end() && it->second.joy != nullptr)
{
it->second.joy->setPort(port);
// TODO: update mappings
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::mapStelladaptors(string_view saport, int ID)
bool PhysicalJoystickHandler::mapStelladaptors(const string& saport, int ID)
{
bool erased = false;
// saport will have two values:
@ -262,9 +257,9 @@ bool PhysicalJoystickHandler::mapStelladaptors(string_view saport, int ID)
{
// Erase a previously added Stelladapter with a higher ID
ostringstream buf;
buf << "Erased joystick " << _stick->ID << ":\n"
<< " " << _stick->about() << '\n';
Logger::info(buf.view());
buf << "Erased joystick " << _stick->ID << ":" << endl
<< " " << _stick->about() << endl;
Logger::info(buf.str());
_stick->name.erase(pos);
erased = true;
@ -305,7 +300,7 @@ bool PhysicalJoystickHandler::mapStelladaptors(string_view saport, int ID)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::hasStelladaptors() const
{
for(const auto& [_id, _joyptr] : mySticks)
for(auto& [_id, _joyptr] : mySticks)
{
// remove previously added emulated ports
const size_t pos = _joyptr->name.find(" (emulates ");
@ -326,7 +321,8 @@ bool PhysicalJoystickHandler::hasStelladaptors() const
// 2. reset all events to default (event == Event::NoType, updateDefault == false)
// 3. reset one event to default (event != Event::NoType)
void PhysicalJoystickHandler::setDefaultAction(int stick,
EventMapping map, Event::Type event, EventMode mode, bool updateDefaults)
EventMapping map, Event::Type event,
EventMode mode, bool updateDefaults)
{
const PhysicalJoystickPtr j = joy(stick);
@ -338,7 +334,7 @@ void PhysicalJoystickHandler::setDefaultAction(int stick,
{
// if there is no existing mapping for the event and
// the default mapping for the event is unused, set default key for event
if(j->joyMap.getEventMapping(map.event, mode).empty() &&
if(j->joyMap.getEventMapping(map.event, mode).size() == 0 &&
!j->joyMap.check(mode, map.button, map.axis, map.adir, map.hat, map.hdir))
{
if (map.hat == JOY_CTRL_NONE)
@ -359,57 +355,50 @@ void PhysicalJoystickHandler::setDefaultAction(int stick,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setStickDefaultMapping(
int stick, Event::Type event, EventMode mode, bool updateDefaults)
void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type event,
EventMode mode, bool updateDefaults)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
switch(mode)
switch (mode)
{
case EventMode::kEmulationMode:
{
// A regular joystick defaults to left or right based on
// the defined port or stick number being even or odd;
// 'daptor' joysticks request a specific port
bool useLeftMappings = true;
if(j->type == PhysicalJoystick::Type::REGULAR)
{
useLeftMappings = j->port == PhysicalJoystick::Port::LEFT
|| (j->port == PhysicalJoystick::Port::AUTO && (stick % 2) == 0);
}
else
useLeftMappings =
j->type == PhysicalJoystick::Type::LEFT_STELLADAPTOR ||
j->type == PhysicalJoystick::Type::LEFT_2600DAPTOR;
// 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
for(const auto& item : DefaultLeftJoystickMapping)
for (const auto& item : DefaultLeftJoystickMapping)
setDefaultAction(stick, item, event, EventMode::kJoystickMode, updateDefaults);
for(const auto& item : DefaultLeftKeyboardMapping)
for (const auto& item : DefaultLeftKeyboardMapping)
setDefaultAction(stick, item, event, EventMode::kKeyboardMode, updateDefaults);
for(const auto& item : DefaultLeftDrivingMapping)
for (const auto& item : DefaultLeftDrivingMapping)
setDefaultAction(stick, item, event, EventMode::kDrivingMode, updateDefaults);
}
else
{
// put all controller events into their own mode's mappings
for(const auto& item : DefaultRightJoystickMapping)
for (const auto& item : DefaultRightJoystickMapping)
setDefaultAction(stick, item, event, EventMode::kJoystickMode, updateDefaults);
for(const auto& item : DefaultRightKeyboardMapping)
for (const auto& item : DefaultRightKeyboardMapping)
setDefaultAction(stick, item, event, EventMode::kKeyboardMode, updateDefaults);
for(const auto& item : DefaultRightDrivingMapping)
for (const auto& item : DefaultRightDrivingMapping)
setDefaultAction(stick, item, event, EventMode::kDrivingMode, updateDefaults);
}
#if defined(RETRON77)
#if defined(RETRON77)
constexpr bool retron77 = true;
#else
#else
constexpr bool retron77 = false;
#endif
#endif
// Regular joysticks can only be used by one player at a time,
// so we need to separate the paddles onto different
@ -418,16 +407,16 @@ void PhysicalJoystickHandler::setStickDefaultMapping(
// and 2600-daptors support two players natively.
const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR && !retron77) ? 1 : 2;
if(paddlesPerJoystick == 2)
if( paddlesPerJoystick == 2 )
{
if(useLeftMappings)
if( useLeftMappings )
{
for(const auto& item : DefaultLeftPaddlesMapping)
for (const auto& item : DefaultLeftPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for(const auto& item : DefaultRightPaddlesMapping)
for (const auto& item : DefaultRightPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
@ -444,29 +433,29 @@ void PhysicalJoystickHandler::setStickDefaultMapping(
const bool useLeftPaddleMappings = (stick % 4) < 2;
const bool useAPaddleMappings = (stick % 2) == 0;
if(useLeftPaddleMappings)
if( useLeftPaddleMappings )
{
if(useAPaddleMappings)
if( useAPaddleMappings )
{
for(const auto& item : DefaultLeftAPaddlesMapping)
for (const auto& item : DefaultLeftAPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for(const auto& item : DefaultLeftBPaddlesMapping)
for (const auto& item : DefaultLeftBPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
else
{
if(useAPaddleMappings)
if( useAPaddleMappings )
{
for(const auto& item : DefaultRightAPaddlesMapping)
for (const auto& item : DefaultRightAPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for(const auto& item : DefaultRightBPaddlesMapping)
for (const auto& item : DefaultRightBPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
@ -480,7 +469,7 @@ void PhysicalJoystickHandler::setStickDefaultMapping(
}
case EventMode::kMenuMode:
for(const auto& item : DefaultMenuMapping)
for (const auto& item : DefaultMenuMapping)
setDefaultAction(stick, item, event, EventMode::kMenuMode, updateDefaults);
break;
@ -499,71 +488,45 @@ void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mod
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::defineControllerMappings(
Controller::Type type, Controller::Jack port, const Properties& properties,
Controller::Type qtType1, Controller::Type qtType2)
{
// Determine controller events to use
if(type == Controller::Type::QuadTari)
{
if(port == Controller::Jack::Left)
{
myLeftMode = getMode(qtType1);
myLeft2ndMode = getMode(qtType2);
}
else
{
myRightMode = getMode(qtType1);
myRight2ndMode = getMode(qtType2);
}
}
else
{
const EventMode mode = getMode(type);
if(port == Controller::Jack::Left)
myLeftMode = mode;
else
myRightMode = mode;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalJoystickHandler::getMode(const Properties& properties,
PropType propType)
{
const string& propName = properties.get(propType);
if(!propName.empty())
return getMode(Controller::getType(propName));
return EventMode::kJoystickMode;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalJoystickHandler::getMode(Controller::Type type)
void PhysicalJoystickHandler::defineControllerMappings(const Controller::Type type, Controller::Jack port)
{
// determine controller events to use
switch(type)
{
using enum Controller::Type;
case Keyboard:
case KidVid:
return EventMode::kKeyboardMode;
case Controller::Type::Keyboard:
case Controller::Type::KidVid:
if(port == Controller::Jack::Left)
myLeftMode = EventMode::kKeyboardMode;
else
myRightMode = EventMode::kKeyboardMode;
break;
case Paddles:
case PaddlesIAxDr:
case PaddlesIAxis:
return EventMode::kPaddlesMode;
case Controller::Type::Paddles:
case Controller::Type::PaddlesIAxDr:
case Controller::Type::PaddlesIAxis:
if(port == Controller::Jack::Left)
myLeftMode = EventMode::kPaddlesMode;
else
myRightMode = EventMode::kPaddlesMode;
break;
case CompuMate:
return EventMode::kCompuMateMode;
case Controller::Type::CompuMate:
myLeftMode = myRightMode = EventMode::kCompuMateMode;
break;
case Driving:
return EventMode::kDrivingMode;
case Controller::Type::Driving:
if(port == Controller::Jack::Left)
myLeftMode = EventMode::kDrivingMode;
else
myRightMode = EventMode::kDrivingMode;
break;
default:
// let's use joystick then
return EventMode::kJoystickMode;
if(port == Controller::Jack::Left)
myLeftMode = EventMode::kJoystickMode;
else
myRightMode = EventMode::kJoystickMode;
}
}
@ -580,38 +543,8 @@ void PhysicalJoystickHandler::enableEmulationMappings()
enableCommonMappings();
// Process in increasing priority order, so that in case of mapping clashes
// the higher priority controller has preference
switch(myRight2ndMode)
{
case EventMode::kPaddlesMode:
enableMappings(QTPaddles4Events, EventMode::kPaddlesMode);
break;
case EventMode::kEmulationMode: // no QuadTari
break;
default:
enableMappings(QTJoystick4Events, EventMode::kJoystickMode);
break;
}
switch(myLeft2ndMode)
{
case EventMode::kPaddlesMode:
enableMappings(QTPaddles3Events, EventMode::kPaddlesMode);
break;
case EventMode::kEmulationMode: // no QuadTari
break;
default:
enableMappings(QTJoystick3Events, EventMode::kJoystickMode);
break;
}
// enable right mode first, so that in case of mapping clashes the left controller has preference
switch(myRightMode)
switch (myRightMode)
{
case EventMode::kPaddlesMode:
enableMappings(RightPaddlesEvents, EventMode::kPaddlesMode);
@ -630,7 +563,7 @@ void PhysicalJoystickHandler::enableEmulationMappings()
break;
}
switch(myLeftMode)
switch (myLeftMode)
{
case EventMode::kPaddlesMode:
enableMappings(LeftPaddlesEvents, EventMode::kPaddlesMode);
@ -655,7 +588,7 @@ void PhysicalJoystickHandler::enableCommonMappings()
{
for (int i = Event::NoType + 1; i < Event::LastType; i++)
{
const auto event = static_cast<Event::Type>(i);
const Event::Type event = static_cast<Event::Type>(i);
if(isCommonEvent(event))
enableMapping(event, EventMode::kCommonMode);
@ -670,14 +603,14 @@ void PhysicalJoystickHandler::enableMappings(const Event::EventSet& events, Even
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::enableMapping(Event::Type event, EventMode mode)
void PhysicalJoystickHandler::enableMapping(const Event::Type event, EventMode mode)
{
// copy from controller mode into emulation mode
for (auto& stick : mySticks)
{
const PhysicalJoystickPtr j = stick.second;
const JoyMap::JoyMappingArray joyMappings = j->joyMap.getEventMapping(event, mode);
JoyMap::JoyMappingArray joyMappings = j->joyMap.getEventMapping(event, mode);
for (const auto& mapping : joyMappings)
j->joyMap.add(event, EventMode::kEmulationMode, mapping.button,
@ -686,7 +619,7 @@ void PhysicalJoystickHandler::enableMapping(Event::Type event, EventMode mode)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalJoystickHandler::getEventMode(Event::Type event, EventMode mode)
EventMode PhysicalJoystickHandler::getEventMode(const Event::Type event, const EventMode mode) const
{
if(mode == EventMode::kEmulationMode)
{
@ -710,42 +643,37 @@ EventMode PhysicalJoystickHandler::getEventMode(Event::Type event, EventMode mod
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isJoystickEvent(Event::Type event)
bool PhysicalJoystickHandler::isJoystickEvent(const Event::Type event) const
{
return LeftJoystickEvents.contains(event)
|| QTJoystick3Events.contains(event)
|| RightJoystickEvents.contains(event)
|| QTJoystick4Events.contains(event);
return LeftJoystickEvents.find(event) != LeftJoystickEvents.end()
|| RightJoystickEvents.find(event) != RightJoystickEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isPaddleEvent(Event::Type event)
bool PhysicalJoystickHandler::isPaddleEvent(const Event::Type event) const
{
return LeftPaddlesEvents.contains(event)
|| QTPaddles3Events.contains(event)
|| RightPaddlesEvents.contains(event)
|| QTPaddles4Events.contains(event);
return LeftPaddlesEvents.find(event) != LeftPaddlesEvents.end()
|| RightPaddlesEvents.find(event) != RightPaddlesEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isKeyboardEvent(Event::Type event)
bool PhysicalJoystickHandler::isKeyboardEvent(const Event::Type event) const
{
return LeftKeyboardEvents.contains(event)
|| RightKeyboardEvents.contains(event);
return LeftKeyboardEvents.find(event) != LeftKeyboardEvents.end()
|| RightKeyboardEvents.find(event) != RightKeyboardEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isDrivingEvent(Event::Type event)
bool PhysicalJoystickHandler::isDrivingEvent(const Event::Type event) const
{
return LeftDrivingEvents.contains(event)
|| RightDrivingEvents.contains(event);
return LeftDrivingEvents.find(event) != LeftDrivingEvents.end()
|| RightDrivingEvents.find(event) != RightDrivingEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isCommonEvent(Event::Type event)
bool PhysicalJoystickHandler::isCommonEvent(const Event::Type event) const
{
return !(isJoystickEvent(event) || isPaddleEvent(event)
|| isKeyboardEvent(event) || isDrivingEvent(event));
return !(isJoystickEvent(event) || isPaddleEvent(event) || isKeyboardEvent(event) || isDrivingEvent(event));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -787,7 +715,7 @@ void PhysicalJoystickHandler::saveMapping()
for(const auto& [_name, _info]: myDatabase)
{
const json map = _info.joy ? _info.joy->getMap() : _info.mapping;
json map = _info.joy ? _info.joy->getMap() : _info.mapping;
if (!map.is_null()) mapping.emplace_back(map);
}
@ -806,9 +734,9 @@ string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode
if(_joyptr)
{
//Joystick mapping / labeling
if(!_joyptr->joyMap.getEventMapping(event, evMode).empty())
if(_joyptr->joyMap.getEventMapping(event, evMode).size())
{
if(!buf.view().empty())
if(buf.str() != "")
buf << ", ";
buf << _joyptr->joyMap.getEventMappingDesc(_id, event, evMode);
}
@ -818,8 +746,8 @@ string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addJoyMapping(Event::Type event, EventMode mode,
int stick, int button, JoyAxis axis, JoyDir adir)
bool PhysicalJoystickHandler::addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis, JoyDir adir)
{
const PhysicalJoystickPtr j = joy(stick);
@ -859,8 +787,8 @@ bool PhysicalJoystickHandler::addJoyMapping(Event::Type event, EventMode mode,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addJoyHatMapping(Event::Type event, EventMode mode,
int stick, int button, int hat, JoyHatDir hdir)
bool PhysicalJoystickHandler::addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, int hat, JoyHatDir hdir)
{
const PhysicalJoystickPtr j = joy(stick);
@ -901,18 +829,17 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
if(j)
{
//int button = j->buttonLast;
//int button = j->buttonLast[stick];
switch(j->type)
{
using enum PhysicalJoystick::Type;
// Since the various controller classes deal with Stelladaptor
// devices differently, we send the raw X and Y axis data directly,
// and let the controller handle it
// These events don't have to pass through handleEvent, since
// they can never be remapped
case LEFT_STELLADAPTOR:
case LEFT_2600DAPTOR:
case PhysicalJoystick::Type::LEFT_STELLADAPTOR:
case PhysicalJoystick::Type::LEFT_2600DAPTOR:
if(myOSystem.hasConsole()
&& myOSystem.console().leftController().type() == Controller::Type::Driving)
{
@ -923,8 +850,8 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
handleRegularAxisEvent(j, stick, axis, value);
break; // axis on left controller (0)
case RIGHT_STELLADAPTOR:
case RIGHT_2600DAPTOR:
case PhysicalJoystick::Type::RIGHT_STELLADAPTOR:
case PhysicalJoystick::Type::RIGHT_2600DAPTOR:
if(myOSystem.hasConsole()
&& myOSystem.console().rightController().type() == Controller::Type::Driving)
{
@ -945,7 +872,7 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
void PhysicalJoystickHandler::handleRegularAxisEvent(const PhysicalJoystickPtr& j,
int stick, int axis, int value)
{
const int button = j->buttonLast;
const int button = j->buttonLast[stick];
if(myHandler.state() == EventHandlerState::EMULATION)
{
@ -1025,7 +952,7 @@ void PhysicalJoystickHandler::handleBtnEvent(int stick, int button, bool pressed
if(j)
{
j->buttonLast = pressed ? button : JOY_CTRL_NONE;
j->buttonLast[stick] = pressed ? button : JOY_CTRL_NONE;
// Handle buttons which switch eventhandler state
if(!pressed && myHandler.changeStateByEvent(j->joyMap.get(EventMode::kEmulationMode, button)))
@ -1052,7 +979,7 @@ void PhysicalJoystickHandler::handleHatEvent(int stick, int hat, int value)
if(j)
{
const int button = j->buttonLast;
const int button = j->buttonLast[stick];
if(myHandler.state() == EventHandlerState::EMULATION)
{
@ -1087,35 +1014,29 @@ void PhysicalJoystickHandler::handleHatEvent(int stick, int hat, int value)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::MinStrickInfoList PhysicalJoystickHandler::minStickList() const
VariantList PhysicalJoystickHandler::database() const
{
MinStrickInfoList list;
VariantList db;
for(const auto& [_name, _info]: myDatabase)
VarList::push_back(db, _name, _info.joy ? _info.joy->ID : -1);
for(const auto& [_name, _info] : myDatabase)
{
const MinStrickInfo stick(_name,
_info.joy ? _info.joy->ID : -1,
_info.joy ? _info.joy->port : PhysicalJoystick::Port::AUTO);
list.push_back(stick);
}
return list;
return db;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
{
os << "---------------------------------------------------------\n"
<< "joy database:\n";
os << "---------------------------------------------------------" << endl
<< "joy database:" << endl;
for(const auto& [_name, _info]: jh.myDatabase)
os << _name << '\n' << _info << "\n\n";
os << _name << endl << _info << endl << endl;
os << "---------------------\n"
<< "joy active:\n";
os << "---------------------" << endl
<< "joy active:" << endl;
for(const auto& [_id, _joyptr]: jh.mySticks)
os << _id << ": " << *_joyptr << '\n';
os << _id << ": " << *_joyptr << endl;
os << "---------------------------------------------------------"
<< "\n\n\n";
<< endl << endl << endl;
return os;
}
@ -1123,9 +1044,8 @@ ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDigitalDeadZone(int direction)
{
const int deadZone =
BSPF::clamp(myOSystem.settings().getInt("joydeadzone") + direction,
Controller::MIN_DIGITAL_DEADZONE, Controller::MAX_DIGITAL_DEADZONE);
int deadZone = BSPF::clamp(myOSystem.settings().getInt("joydeadzone") + direction,
Controller::MIN_DIGITAL_DEADZONE, Controller::MAX_DIGITAL_DEADZONE);
myOSystem.settings().setValue("joydeadzone", deadZone);
Controller::setDigitalDeadZone(deadZone);
@ -1133,17 +1053,15 @@ void PhysicalJoystickHandler::changeDigitalDeadZone(int direction)
ostringstream ss;
ss << std::round(Controller::digitalDeadZoneValue(deadZone) * 100.F / 32768) << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Digital controller dead zone", ss.view(), deadZone,
Controller::MIN_DIGITAL_DEADZONE, Controller::MAX_DIGITAL_DEADZONE);
myOSystem.frameBuffer().showGaugeMessage("Digital controller dead zone", ss. str(), deadZone,
Controller::MIN_DIGITAL_DEADZONE, Controller::MAX_DIGITAL_DEADZONE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeAnalogPaddleDeadZone(int direction)
{
const int deadZone =
BSPF::clamp(myOSystem.settings().getInt("adeadzone") + direction,
Controller::MIN_ANALOG_DEADZONE, Controller::MAX_ANALOG_DEADZONE);
int deadZone = BSPF::clamp(myOSystem.settings().getInt("adeadzone") + direction,
Controller::MIN_ANALOG_DEADZONE, Controller::MAX_ANALOG_DEADZONE);
myOSystem.settings().setValue("adeadzone", deadZone);
Controller::setAnalogDeadZone(deadZone);
@ -1151,17 +1069,15 @@ void PhysicalJoystickHandler::changeAnalogPaddleDeadZone(int direction)
ostringstream ss;
ss << std::round(Controller::analogDeadZoneValue(deadZone) * 100.F / 32768) << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Analog controller dead zone", ss.view(), deadZone,
Controller::MIN_ANALOG_DEADZONE, Controller::MAX_ANALOG_DEADZONE);
myOSystem.frameBuffer().showGaugeMessage("Analog controller dead zone", ss.str(), deadZone,
Controller::MIN_ANALOG_DEADZONE, Controller::MAX_ANALOG_DEADZONE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeAnalogPaddleSensitivity(int direction)
{
const int sense =
BSPF::clamp(myOSystem.settings().getInt("psense") + direction,
Paddles::MIN_ANALOG_SENSE, Paddles::MAX_ANALOG_SENSE);
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);
@ -1169,17 +1085,15 @@ void PhysicalJoystickHandler::changeAnalogPaddleSensitivity(int direction)
ostringstream ss;
ss << std::round(Paddles::analogSensitivityValue(sense) * 100.F) << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Analog paddle sensitivity", ss.view(), sense,
Paddles::MIN_ANALOG_SENSE, Paddles::MAX_ANALOG_SENSE);
myOSystem.frameBuffer().showGaugeMessage("Analog paddle sensitivity", ss.str(), sense,
Paddles::MIN_ANALOG_SENSE, Paddles::MAX_ANALOG_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeAnalogPaddleLinearity(int direction)
{
const int linear =
BSPF::clamp(myOSystem.settings().getInt("plinear") + direction * 5,
Paddles::MIN_ANALOG_LINEARITY, Paddles::MAX_ANALOG_LINEARITY);
int linear = BSPF::clamp(myOSystem.settings().getInt("plinear") + direction * 5,
Paddles::MIN_ANALOG_LINEARITY, Paddles::MAX_ANALOG_LINEARITY);
myOSystem.settings().setValue("plinear", linear);
Paddles::setAnalogLinearity(linear);
@ -1190,17 +1104,15 @@ void PhysicalJoystickHandler::changeAnalogPaddleLinearity(int direction)
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage(
"Analog paddle linearity", ss.view(), linear,
Paddles::MIN_ANALOG_LINEARITY, Paddles::MAX_ANALOG_LINEARITY);
myOSystem.frameBuffer().showGaugeMessage("Analog paddle linearity", ss.str(), linear,
Paddles::MIN_ANALOG_LINEARITY, Paddles::MAX_ANALOG_LINEARITY);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changePaddleDejitterAveraging(int direction)
{
const int dejitter =
BSPF::clamp(myOSystem.settings().getInt("dejitter.base") + direction,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
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);
@ -1211,17 +1123,16 @@ void PhysicalJoystickHandler::changePaddleDejitterAveraging(int direction)
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage(
"Analog paddle dejitter averaging", ss.view(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
myOSystem.frameBuffer().showGaugeMessage("Analog paddle dejitter averaging",
ss.str(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changePaddleDejitterReaction(int direction)
{
const int dejitter =
BSPF::clamp(myOSystem.settings().getInt("dejitter.diff") + direction,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
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);
@ -1232,17 +1143,16 @@ void PhysicalJoystickHandler::changePaddleDejitterReaction(int direction)
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage(
"Analog paddle dejitter reaction", ss.view(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
myOSystem.frameBuffer().showGaugeMessage("Analog paddle dejitter reaction",
ss.str(), dejitter,
Paddles::MIN_DEJITTER, Paddles::MAX_DEJITTER);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDigitalPaddleSensitivity(int direction)
{
const int sense =
BSPF::clamp(myOSystem.settings().getInt("dsense") + direction,
Paddles::MIN_DIGITAL_SENSE, Paddles::MAX_DIGITAL_SENSE);
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);
@ -1253,17 +1163,16 @@ void PhysicalJoystickHandler::changeDigitalPaddleSensitivity(int direction)
else
ss << "Off";
myOSystem.frameBuffer().showGaugeMessage(
"Digital sensitivity", ss.view(), sense,
Paddles::MIN_DIGITAL_SENSE, Paddles::MAX_DIGITAL_SENSE);
myOSystem.frameBuffer().showGaugeMessage("Digital sensitivity",
ss.str(), sense,
Paddles::MIN_DIGITAL_SENSE, Paddles::MAX_DIGITAL_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeMousePaddleSensitivity(int direction)
{
const int sense =
BSPF::clamp(myOSystem.settings().getInt("msense") + direction,
Controller::MIN_MOUSE_SENSE, Controller::MAX_MOUSE_SENSE);
int sense = BSPF::clamp(myOSystem.settings().getInt("msense") + direction,
Controller::MIN_MOUSE_SENSE, Controller::MAX_MOUSE_SENSE);
myOSystem.settings().setValue("msense", sense);
Controller::setMouseSensitivity(sense);
@ -1271,17 +1180,16 @@ void PhysicalJoystickHandler::changeMousePaddleSensitivity(int direction)
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Mouse paddle sensitivity", ss.view(), sense,
Controller::MIN_MOUSE_SENSE, Controller::MAX_MOUSE_SENSE);
myOSystem.frameBuffer().showGaugeMessage("Mouse paddle sensitivity",
ss.str(), sense,
Controller::MIN_MOUSE_SENSE, Controller::MAX_MOUSE_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeMouseTrackballSensitivity(int direction)
{
const int sense =
BSPF::clamp(myOSystem.settings().getInt("tsense") + direction,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
int sense = BSPF::clamp(myOSystem.settings().getInt("tsense") + direction,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
myOSystem.settings().setValue("tsense", sense);
PointingDevice::setSensitivity(sense);
@ -1289,17 +1197,16 @@ void PhysicalJoystickHandler::changeMouseTrackballSensitivity(int direction)
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Mouse trackball sensitivity", ss.view(), sense,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
myOSystem.frameBuffer().showGaugeMessage("Mouse trackball sensitivity",
ss.str(), sense,
PointingDevice::MIN_SENSE, PointingDevice::MAX_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::changeDrivingSensitivity(int direction)
{
const int sense =
BSPF::clamp(myOSystem.settings().getInt("dcsense") + direction,
Driving::MIN_SENSE, Driving::MAX_SENSE);
int sense = BSPF::clamp(myOSystem.settings().getInt("dcsense") + direction,
Driving::MIN_SENSE, Driving::MAX_SENSE);
myOSystem.settings().setValue("dcsense", sense);
Driving::setSensitivity(sense);
@ -1307,9 +1214,9 @@ void PhysicalJoystickHandler::changeDrivingSensitivity(int direction)
ostringstream ss;
ss << sense * 10 << "%";
myOSystem.frameBuffer().showGaugeMessage(
"Driving controller sensitivity", ss.view(), sense,
Driving::MIN_SENSE, Driving::MAX_SENSE);
myOSystem.frameBuffer().showGaugeMessage("Driving controller sensitivity",
ss.str(), sense,
Driving::MIN_SENSE, Driving::MAX_SENSE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1484,22 +1391,13 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRight
PhysicalJoystickHandler::EventMappingArray
PhysicalJoystickHandler::DefaultCommonMapping = {
// valid for all joysticks
// Note: buttons 0..2 are used by controllers!
#if defined(RETRON77)
{Event::CmdMenuMode, 3}, // Button "Y" / "4"
{Event::ExitMode, 4}, // Left Shoulder Button
{Event::OptionsMenuMode, 5}, // Right Shoulder Button
{Event::RewindPause, 7}, // Right Trigger Button
{Event::ConsoleSelect, 8}, // Button "Select"
{Event::ConsoleReset, 9}, // Button "Start"
#else
{Event::ConsoleSelect, 8}, // Button "Select"
{Event::ConsoleReset, 9}, // Button "Start"
{Event::ConsoleColorToggle, 3}, // Button "Y" / "4"
{Event::ConsoleLeftDiffToggle, 4}, // Left Shoulder Button
{Event::ConsoleRightDiffToggle, 5}, // Right Shoulder Button
{Event::CmdMenuMode, 6}, // Left Trigger Button
{Event::OptionsMenuMode, 7}, // Right Trigger Button
{Event::CmdMenuMode, 3}, // Note: buttons 0..2 are used by controllers!
{Event::ExitMode, 4},
{Event::OptionsMenuMode, 5},
{Event::RewindPause, 6},
{Event::ConsoleSelect, 7},
{Event::ConsoleReset, 8},
#endif
};
@ -1518,9 +1416,7 @@ PhysicalJoystickHandler::DefaultMenuMapping = {
{Event::UINavNext, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
{Event::UITabNext, 0, JoyAxis::X, JoyDir::POS},
{Event::UIUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
{Event::UIOK, 0 , JoyAxis::Y, JoyDir::NEG},
{Event::UIDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
{Event::UICancel, 0 , JoyAxis::Y, JoyDir::POS},
{Event::UINavPrev, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::LEFT},
{Event::UINavNext, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::RIGHT},

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -46,18 +46,6 @@ using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>;
*/
class PhysicalJoystickHandler
{
public:
struct MinStrickInfo
{
string name;
int ID;
PhysicalJoystick::Port port;
explicit MinStrickInfo(string_view _name, int _id, PhysicalJoystick::Port _port)
: name{_name}, ID{_id}, port{_port} {}
};
using MinStrickInfoList = std::vector<MinStrickInfo>;
private:
struct StickInfo
{
@ -66,13 +54,13 @@ class PhysicalJoystickHandler
// on the 'mapping' instance variable; there lay dragons ...
// https://json.nlohmann.me/home/faq/#brace-initialization-yields-arrays
explicit StickInfo(nlohmann::json map, PhysicalJoystickPtr stick = nullptr)
: mapping(map), joy{std::move(stick)} {} // NOLINT
: mapping(map), joy{std::move(stick)} {}
nlohmann::json mapping;
PhysicalJoystickPtr joy;
friend ostream& operator<<(ostream& os, const StickInfo& si) {
os << " joy: " << si.joy << "\n map: " << si.mapping;
os << " joy: " << si.joy << endl << " map: " << si.mapping;
return os;
}
};
@ -80,22 +68,18 @@ class PhysicalJoystickHandler
public:
PhysicalJoystickHandler(OSystem& system, EventHandler& handler, Event& event);
static nlohmann::json convertLegacyMapping(string_view mapping);
static nlohmann::json convertLegacyMapping(const string& mapping);
/** Return stick ID on success, -1 on failure. */
int add(const PhysicalJoystickPtr& stick);
bool remove(int id);
bool remove(string_view name);
void setPort(string_view name, PhysicalJoystick::Port port);
bool mapStelladaptors(string_view saport, int ID = -1);
bool remove(const string& name);
bool mapStelladaptors(const string& saport, int ID = -1);
bool hasStelladaptors() const;
void setDefaultMapping(Event::Type event, EventMode mode);
void setDefaultMapping(Event::Type type, EventMode mode);
/** define mappings for current controllers */
void defineControllerMappings(Controller::Type type, Controller::Jack port,
const Properties& properties,
Controller::Type qtType1 = Controller::Type::Unknown,
Controller::Type qtType2 = Controller::Type::Unknown);
void defineControllerMappings(const Controller::Type type, Controller::Jack port);
/** enable mappings for emulation mode */
void enableEmulationMappings();
@ -127,8 +111,8 @@ class PhysicalJoystickHandler
return j->joyMap.get(mode, button, hat, hatDir);
}
/** Returns a list containing minimal controller info (name, ID, port). */
MinStrickInfoList minStickList() const;
/** Returns a list of pairs consisting of joystick name and associated ID. */
VariantList database() const;
void changeDigitalDeadZone(int direction = +1);
void changeAnalogPaddleDeadZone(int direction = +1);
@ -142,12 +126,12 @@ class PhysicalJoystickHandler
void changeDrivingSensitivity(int direction = +1);
private:
using StickDatabase = std::map<string, StickInfo, std::less<>>;
using StickDatabase = std::map<string,StickInfo>;
using StickList = std::map<int, PhysicalJoystickPtr>;
OSystem& myOSystem; // NOLINT: we want a reference here
EventHandler& myHandler; // NOLINT: we want a reference here
Event& myEvent; // NOLINT: we want a reference here
OSystem& myOSystem;
EventHandler& myHandler;
Event& myEvent;
// Contains all joysticks that Stella knows about, indexed by name
StickDatabase myDatabase;
@ -157,7 +141,7 @@ class PhysicalJoystickHandler
// Get joystick corresponding to given id (or nullptr if it doesn't exist)
// Make this inline so it's as fast as possible
PhysicalJoystickPtr joy(int id) const {
const PhysicalJoystickPtr joy(int id) const {
const auto& i = mySticks.find(id);
return i != mySticks.cend() ? i->second : nullptr;
}
@ -166,15 +150,13 @@ class PhysicalJoystickHandler
void addToDatabase(const PhysicalJoystickPtr& stick);
// Set default mapping for given joystick when no mappings already exist
void setStickDefaultMapping(int stick, Event::Type event, EventMode mode,
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode,
bool updateDefaults = false);
friend ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh);
static constexpr JoyDir convertAxisValue(int value) {
return value == static_cast<int>(JoyDir::NONE)
? JoyDir::NONE
: value > 0 ? JoyDir::POS : JoyDir::NEG;
JoyDir convertAxisValue(int value) const {
return value == int(JoyDir::NONE) ? JoyDir::NONE : value > 0 ? JoyDir::POS : JoyDir::NEG;
}
// Handle regular axis events (besides special Stelladaptor handling)
@ -197,31 +179,23 @@ class PhysicalJoystickHandler
EventMode mode = EventMode::kEmulationMode,
bool updateDefaults = false);
/** return event mode for given property */
static EventMode getMode(const Properties& properties, PropType propType);
/** return event mode for given controller type */
static EventMode getMode(Controller::Type type);
/** returns the event's controller mode */
static EventMode getEventMode(Event::Type event, EventMode mode);
EventMode getEventMode(const Event::Type event, const EventMode mode) const;
/** Checks event type. */
static bool isJoystickEvent(Event::Type event);
static bool isPaddleEvent(Event::Type event);
static bool isKeyboardEvent(Event::Type event);
static bool isDrivingEvent(Event::Type event);
static bool isCommonEvent(Event::Type event);
bool isJoystickEvent(const Event::Type event) const;
bool isPaddleEvent(const Event::Type event) const;
bool isKeyboardEvent(const Event::Type event) const;
bool isDrivingEvent(const Event::Type event) const;
bool isCommonEvent(const Event::Type event) const;
void enableCommonMappings();
void enableMappings(const Event::EventSet& events, EventMode mode);
void enableMapping(Event::Type event, EventMode mode);
void enableMapping(const Event::Type event, EventMode mode);
private:
EventMode myLeftMode{EventMode::kEmulationMode};
EventMode myRightMode{EventMode::kEmulationMode};
// Additional modes for QuadTari controller
EventMode myLeft2ndMode{EventMode::kEmulationMode};
EventMode myRight2ndMode{EventMode::kEmulationMode};
// Controller menu and common emulation mappings
static EventMappingArray DefaultMenuMapping;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -44,7 +44,7 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
: myOSystem{system},
myHandler{handler}
{
const Int32 version = myOSystem.settings().getInt("event_ver");
Int32 version = myOSystem.settings().getInt("event_ver");
bool updateDefaults = false;
// Compare if event list version has changed so that key maps became invalid
@ -70,28 +70,10 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler&
#ifdef DEBUGGER_SUPPORT
setDefaultMapping(Event::NoType, EventMode::kPromptMode, updateDefaults);
#endif
#ifdef DEBUG_BUILD
verifyDefaultMapping(DefaultCommonMapping, EventMode::kEmulationMode, "EmulationMode");
verifyDefaultMapping(DefaultMenuMapping, EventMode::kMenuMode, "MenuMode");
#endif
}
#ifdef DEBUG_BUILD
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::verifyDefaultMapping(
PhysicalKeyboardHandler::EventMappingArray mapping, EventMode mode, string_view name)
{
for(const auto& item1 : mapping)
for(const auto& item2 : mapping)
if(item1.event != item2.event && item1.key == item2.key && item1.mod == item2.mod)
cerr << "ERROR! Duplicate hotkey mapping found: " << name << ", "
<< myKeyMap.getDesc(KeyMap::Mapping(mode, item1.key, item1.mod)) << "\n";
}
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::loadSerializedMappings(
string_view serializedMapping, EventMode mode)
void PhysicalKeyboardHandler::loadSerializedMappings(const string& serializedMapping, EventMode mode)
{
json mapping;
@ -163,7 +145,7 @@ void PhysicalKeyboardHandler::setDefaultKey(EventMapping map, Event::Type event,
{
// if there is no existing mapping for the event and
// the default mapping for the event is unused, set default key for event
if (myKeyMap.getEventMapping(map.event, mode).empty() &&
if (myKeyMap.getEventMapping(map.event, mode).size() == 0 &&
!isMappingUsed(mode, map))
{
addMapping(map.event, mode, map.key, static_cast<StellaMod>(map.mod));
@ -235,65 +217,68 @@ void PhysicalKeyboardHandler::setDefaultMapping(Event::Type event, EventMode mod
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::defineControllerMappings(
Controller::Type type, Controller::Jack port, const Properties& properties,
Controller::Type qtType1, Controller::Type qtType2)
const Controller::Type type, Controller::Jack port, const Properties& properties)
{
// Determine controller events to use
if(type == Controller::Type::QuadTari)
{
if(port == Controller::Jack::Left)
{
myLeftMode = getMode(qtType1);
myLeft2ndMode = getMode(qtType2);
}
else
{
myRightMode = getMode(qtType1);
myRight2ndMode = getMode(qtType2);
}
}
else
{
const EventMode mode = getMode(type);
if(port == Controller::Jack::Left)
myLeftMode = mode;
else
myRightMode = mode;
//const string& test = myOSystem.settings().getString("aq");
// determine controller events to use
switch(type)
{
case Controller::Type::QuadTari:
if(port == Controller::Jack::Left)
{
myLeftMode = getMode(properties, PropType::Controller_Left1);
myLeft2ndMode = getMode(properties, PropType::Controller_Left2);
}
else
{
myRightMode = getMode(properties, PropType::Controller_Right1);
myRight2ndMode = getMode(properties, PropType::Controller_Right2);
}
break;
default:
{
const EventMode mode = getMode(type);
if(port == Controller::Jack::Left)
myLeftMode = mode;
else
myRightMode = mode;
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalKeyboardHandler::getMode(const Properties& properties,
PropType propType)
EventMode PhysicalKeyboardHandler::getMode(const Properties& properties, const PropType propType)
{
const string& propName = properties.get(propType);
if(!propName.empty())
return getMode(Controller::getType(propName));
return EventMode::kJoystickMode;
return getMode(Controller::Type::Joystick);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalKeyboardHandler::getMode(Controller::Type type)
EventMode PhysicalKeyboardHandler::getMode(const Controller::Type type)
{
switch(type)
{
using enum Controller::Type;
case Keyboard:
case KidVid:
case Controller::Type::Keyboard:
case Controller::Type::KidVid:
return EventMode::kKeyboardMode;
case Paddles:
case PaddlesIAxDr:
case PaddlesIAxis:
case Controller::Type::Paddles:
case Controller::Type::PaddlesIAxDr:
case Controller::Type::PaddlesIAxis:
return EventMode::kPaddlesMode;
case CompuMate:
case Controller::Type::CompuMate:
return EventMode::kCompuMateMode;
case Driving:
case Controller::Type::Driving:
return EventMode::kDrivingMode;
default:
@ -392,7 +377,7 @@ void PhysicalKeyboardHandler::enableCommonMappings()
{
for (int i = Event::NoType + 1; i < Event::LastType; i++)
{
const auto event = static_cast<Event::Type>(i);
const Event::Type event = static_cast<Event::Type>(i);
if (isCommonEvent(event))
enableMapping(event, EventMode::kCommonMode);
@ -408,17 +393,19 @@ void PhysicalKeyboardHandler::enableMappings(const Event::EventSet& events,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::enableMapping(Event::Type event, EventMode mode)
void PhysicalKeyboardHandler::enableMapping(const Event::Type event,
EventMode mode)
{
// copy from controller mode into emulation mode
const KeyMap::MappingArray mappings = myKeyMap.getEventMapping(event, mode);
KeyMap::MappingArray mappings = myKeyMap.getEventMapping(event, mode);
for (const auto& mapping : mappings)
myKeyMap.add(event, EventMode::kEmulationMode, mapping.key, mapping.mod);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalKeyboardHandler::getEventMode(Event::Type event, EventMode mode)
EventMode PhysicalKeyboardHandler::getEventMode(const Event::Type event,
const EventMode mode) const
{
if (mode == EventMode::kEmulationMode)
{
@ -442,42 +429,41 @@ EventMode PhysicalKeyboardHandler::getEventMode(Event::Type event, EventMode mod
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::isJoystickEvent(Event::Type event)
bool PhysicalKeyboardHandler::isJoystickEvent(const Event::Type event) const
{
return LeftJoystickEvents.contains(event)
|| QTJoystick3Events.contains(event)
|| RightJoystickEvents.contains(event)
|| QTJoystick4Events.contains(event);
return LeftJoystickEvents.find(event) != LeftJoystickEvents.end()
|| QTJoystick3Events.find(event) != QTJoystick3Events.end()
|| RightJoystickEvents.find(event) != RightJoystickEvents.end()
|| QTJoystick4Events.find(event) != QTJoystick4Events.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::isPaddleEvent(Event::Type event)
bool PhysicalKeyboardHandler::isPaddleEvent(const Event::Type event) const
{
return LeftPaddlesEvents.contains(event)
|| QTPaddles3Events.contains(event)
|| RightPaddlesEvents.contains(event)
|| QTPaddles4Events.contains(event);
return LeftPaddlesEvents.find(event) != LeftPaddlesEvents.end()
|| QTPaddles3Events.find(event) != QTPaddles3Events.end()
|| RightPaddlesEvents.find(event) != RightPaddlesEvents.end()
|| QTPaddles4Events.find(event) != QTPaddles4Events.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::isKeyboardEvent(Event::Type event)
bool PhysicalKeyboardHandler::isDrivingEvent(const Event::Type event) const
{
return LeftKeyboardEvents.contains(event)
|| RightKeyboardEvents.contains(event);
return LeftDrivingEvents.find(event) != LeftDrivingEvents.end()
|| RightDrivingEvents.find(event) != RightDrivingEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::isDrivingEvent(Event::Type event)
bool PhysicalKeyboardHandler::isKeyboardEvent(const Event::Type event) const
{
return LeftDrivingEvents.contains(event)
|| RightDrivingEvents.contains(event);
return LeftKeyboardEvents.find(event) != LeftKeyboardEvents.end()
|| RightKeyboardEvents.find(event) != RightKeyboardEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::isCommonEvent(Event::Type event)
bool PhysicalKeyboardHandler::isCommonEvent(const Event::Type event) const
{
return !(isJoystickEvent(event) || isPaddleEvent(event)
|| isKeyboardEvent(event) || isDrivingEvent(event));
return !(isJoystickEvent(event) || isPaddleEvent(event) || isKeyboardEvent(event));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -613,7 +599,7 @@ void PhysicalKeyboardHandler::toggleModKeys(bool toggle)
ostringstream ss;
ss << "Modifier key combos ";
ss << (modCombo ? "enabled" : "disabled");
myOSystem.frameBuffer().showTextMessage(ss.view());
myOSystem.frameBuffer().showTextMessage(ss.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -640,7 +626,6 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
#endif
{ Event::OptionsMenuMode, KBDK_TAB },
{ Event::CmdMenuMode, KBDK_BACKSLASH },
{ Event::ToggleBezel, KBDK_B, KBDM_CTRL },
{ Event::TimeMachineMode, KBDK_T, KBDM_SHIFT },
{ Event::DebuggerMode, KBDK_GRAVE },
{ Event::PlusRomsSetupMode, KBDK_P, KBDM_SHIFT | KBDM_CTRL | MOD3 },
@ -659,7 +644,7 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3 },
{ Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3 },
{ Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3 },
{ Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_SHIFT | KBDM_CTRL },
{ Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_CTRL },
{ Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3 },
{ Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3 },
{ Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL },
@ -677,8 +662,6 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3 },
{ Event::PhosphorIncrease, KBDK_4, MOD3 },
{ Event::TogglePhosphor, KBDK_P, MOD3 },
//{ Event::PhosphorModeDecrease, KBDK_P, KBDM_SHIFT | KBDM_CTRL | MOD3 },
{ Event::PhosphorModeIncrease, KBDK_P, KBDM_CTRL | MOD3 },
{ Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 },
{ Event::ScanlinesIncrease, KBDK_5, MOD3 },
{ Event::PreviousScanlineMask, KBDK_6, KBDM_SHIFT | MOD3 },
@ -726,7 +709,7 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::ToggleFrameStats, KBDK_L, MOD3 },
{ Event::ToggleTimeMachine, KBDK_T, MOD3 },
#ifdef IMAGE_SUPPORT
#ifdef PNG_SUPPORT
{ Event::ToggleContSnapshots, KBDK_S, MOD3 | KBDM_CTRL },
{ Event::ToggleContSnapshotsFrame, KBDK_S, KBDM_SHIFT | MOD3 | KBDM_CTRL },
#endif
@ -853,7 +836,6 @@ PhysicalKeyboardHandler::DefaultMenuMapping = {
{Event::UITabPrev, KBDK_TAB, KBDM_SHIFT | KBDM_CTRL},
{Event::UITabNext, KBDK_TAB, KBDM_CTRL},
{Event::ToggleUIPalette, KBDK_T, MOD3},
{Event::ToggleFullScreen, KBDK_RETURN, MOD3},
#ifdef BSPF_MACOS
@ -980,9 +962,11 @@ PhysicalKeyboardHandler::FixedPromptMapping = {
{Event::UIDown, KBDK_DOWN, KBDM_SHIFT},
{Event::UILeft, KBDK_DOWN},
{Event::UIRight, KBDK_UP},
};
#endif // DEBUGGER_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultJoystickMapping = {
{Event::LeftJoystickUp, KBDK_UP},

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
@ -24,7 +24,6 @@ class OSystem;
class EventHandler;
#include "bspf.hxx"
#include "Control.hxx"
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "Props.hxx"
@ -47,17 +46,13 @@ class PhysicalKeyboardHandler
PhysicalKeyboardHandler(OSystem& system, EventHandler& handler);
void loadSerializedMappings(string_view serializedMappings, EventMode mode);
void loadSerializedMappings(const string& serializedMappings, EventMode mode);
void setDefaultMapping(Event::Type event, EventMode mode,
bool updateDefaults = false);
void setDefaultMapping(Event::Type type, EventMode mode, bool updateDefaults = false);
/** define mappings for current controllers */
void defineControllerMappings(Controller::Type type,
Controller::Jack port,
const Properties& properties,
Controller::Type qtType1 = Controller::Type::Unknown,
Controller::Type qtType2 = Controller::Type::Unknown);
void defineControllerMappings(const Controller::Type type, Controller::Jack port,
const Properties& properties);
/** enable mappings for emulation mode */
void enableEmulationMappings();
@ -78,10 +73,6 @@ class PhysicalKeyboardHandler
return myKeyMap.get(mode, key, mod);
}
bool checkEventForKey(EventMode mode, StellaKey key, StellaMod mod) const {
return myKeyMap.check(mode, key, mod);
}
#ifdef BSPF_UNIX
/** See comments on 'myAltKeyCounter' for more information. */
uInt8& altKeyCount() { return myAltKeyCounter; }
@ -109,28 +100,23 @@ class PhysicalKeyboardHandler
EventMode mode = EventMode::kEmulationMode, bool updateDefaults = false);
/** returns the event's controller mode */
static EventMode getEventMode(Event::Type event, EventMode mode);
EventMode getEventMode(const Event::Type event, const EventMode mode) const;
/** Checks event type. */
static bool isJoystickEvent(Event::Type event);
static bool isPaddleEvent(Event::Type event);
static bool isKeyboardEvent(Event::Type event);
static bool isDrivingEvent(Event::Type event);
static bool isCommonEvent(Event::Type event);
bool isJoystickEvent(const Event::Type event) const;
bool isPaddleEvent(const Event::Type event) const;
bool isKeyboardEvent(const Event::Type event) const;
bool isDrivingEvent(const Event::Type event) const;
bool isCommonEvent(const Event::Type event) const;
void enableCommonMappings();
void enableMappings(const Event::EventSet& events, EventMode mode);
void enableMapping(Event::Type event, EventMode mode);
void enableMapping(const Event::Type event, EventMode mode);
/** return event mode for given property */
static EventMode getMode(const Properties& properties, PropType propType);
EventMode getMode(const Properties& properties, const PropType propType);
/** return event mode for given controller type */
static EventMode getMode(Controller::Type type);
#ifdef DEBUG_BUILD
void verifyDefaultMapping(PhysicalKeyboardHandler::EventMappingArray mapping,
EventMode mode, string_view name);
#endif
EventMode getMode(const Controller::Type type);
private:
OSystem& myOSystem;

View File

@ -8,23 +8,28 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony
// Copyright (c) 1995-2022 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.
//============================================================================
#ifdef IMAGE_SUPPORT
#if defined(PNG_SUPPORT)
#include <cmath>
#include "bspf.hxx"
#include "OSystem.hxx"
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
#include "Props.hxx"
#include "Settings.hxx"
#include "TIASurface.hxx"
#include "Version.hxx"
#include "PNGLibrary.hxx"
#include "Rect.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::PNGLibrary(OSystem& osystem)
@ -33,35 +38,34 @@ PNGLibrary::PNGLibrary(OSystem& osystem)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
VariantList& metaData)
void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
{
png_structp png_ptr{nullptr};
png_infop info_ptr{nullptr};
png_uint_32 iwidth{0}, iheight{0};
int bit_depth{0}, color_type{0}, interlace_type{0};
bool hasAlpha = false;
const auto loadImageERROR = [&](string_view s) {
const auto loadImageERROR = [&](const char* s) {
if(png_ptr)
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : nullptr, nullptr);
throw runtime_error(string{s});
if(s)
throw runtime_error(s);
};
std::ifstream in(filename, std::ios_base::binary);
if(!in.is_open())
loadImageERROR("No image found");
loadImageERROR("No snapshot found");
// Create the PNG loading context structure
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
png_user_error, png_user_warn);
if(png_ptr == nullptr)
loadImageERROR("Couldn't allocate memory for PNG image");
loadImageERROR("Couldn't allocate memory for PNG file");
// Allocate/initialize the memory for image information. REQUIRED.
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == nullptr)
loadImageERROR("Couldn't create image information for PNG image");
loadImageERROR("Couldn't create image information for PNG file");
// Set up the input control
png_set_read_fn(png_ptr, &in, png_read_data);
@ -78,25 +82,18 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
// byte into separate bytes (useful for paletted and grayscale images).
png_set_packing(png_ptr);
// Alpha channel is supported
// Only normal RBG(A) images are supported (without the alpha channel)
if(color_type == PNG_COLOR_TYPE_RGBA)
{
hasAlpha = true;
png_set_strip_alpha(png_ptr);
}
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
// TODO: preserve alpha
png_set_gray_to_rgb(png_ptr);
}
else if(color_type == PNG_COLOR_TYPE_PALETTE)
{
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
hasAlpha = true;
}
else
png_set_palette_to_rgb(png_ptr);
png_set_palette_to_rgb(png_ptr);
}
else if(color_type != PNG_COLOR_TYPE_RGB)
{
@ -104,8 +101,8 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
}
// Create/initialize storage area for the current image
if(!allocateStorage(iwidth, iheight, hasAlpha))
loadImageERROR("Not enough memory to read PNG image");
if(!allocateStorage(iwidth, iheight))
loadImageERROR("Not enough memory to read PNG file");
// The PNG read function expects an array of rows, not a single 1-D array
for(uInt32 irow = 0, offset = 0; irow < ReadInfo.height; ++irow, offset += ReadInfo.pitch)
@ -117,11 +114,8 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
// We're finished reading
png_read_end(png_ptr, info_ptr);
// Read the meta data we got
readMetaData(png_ptr, info_ptr, metaData);
// Load image into the surface, setting the correct dimensions
loadImagetoSurface(surface, hasAlpha);
loadImagetoSurface(surface);
// Cleanup
if(png_ptr)
@ -129,7 +123,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImage(const string& filename, const VariantList& metaData)
void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
{
std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open())
@ -143,31 +137,31 @@ void PNGLibrary::saveImage(const string& filename, const VariantList& metaData)
fb.scaleX(rectUnscaled.w()), fb.scaleY(rectUnscaled.h())
);
const size_t width = rect.w(), height = rect.h();
const png_uint_32 width = rect.w(), height = rect.h();
// Get framebuffer pixel data (we get ABGR format)
vector<png_byte> buffer(width * height * 4);
fb.readPixels(buffer.data(), width * 4, rect);
fb.readPixels(buffer.data(), width*4, rect);
// Set up pointers into "buffer" byte array
vector<png_bytep> rows(height);
for(size_t k = 0; k < height; ++k)
rows[k] = buffer.data() + k * width * 4;
for(png_uint_32 k = 0; k < height; ++k)
rows[k] = buffer.data() + k*width*4;
// And save the image
saveImageToDisk(out, rows, width, height, metaData);
saveImageToDisk(out, rows, width, height, comments);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
const Common::Rect& rect, const VariantList& metaData)
const Common::Rect& rect, const VariantList& comments)
{
std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open())
throw runtime_error("ERROR: Couldn't create snapshot file");
// Do we want the entire surface or just a section?
size_t width = rect.w(), height = rect.h();
png_uint_32 width = rect.w(), height = rect.h();
if(rect.empty())
{
width = surface.width();
@ -176,28 +170,29 @@ void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
// Get the surface pixel data (we get ABGR format)
vector<png_byte> buffer(width * height * 4);
surface.readPixels(buffer.data(), static_cast<uInt32>(width), rect);
surface.readPixels(buffer.data(), width, rect);
// Set up pointers into "buffer" byte array
vector<png_bytep> rows(height);
for(size_t k = 0; k < height; ++k)
rows[k] = buffer.data() + k * width * 4;
for(png_uint_32 k = 0; k < height; ++k)
rows[k] = buffer.data() + k*width*4;
// And save the image
saveImageToDisk(out, rows, width, height, metaData);
saveImageToDisk(out, rows, width, height, comments);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
size_t width, size_t height, const VariantList& metaData)
png_uint_32 width, png_uint_32 height, const VariantList& comments)
{
png_structp png_ptr{nullptr};
png_infop info_ptr{nullptr};
png_structp png_ptr = nullptr;
png_infop info_ptr = nullptr;
const auto saveImageERROR = [&](string_view s) {
const auto saveImageERROR = [&](const char* s) {
if(png_ptr)
png_destroy_write_struct(&png_ptr, &info_ptr);
throw runtime_error(string{s});
if(s)
throw runtime_error(s);
};
// Create the PNG saving context structure
@ -215,13 +210,12 @@ void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& ro
png_set_write_fn(png_ptr, &out, png_write_data, png_io_flush);
// Write PNG header info
png_set_IHDR(png_ptr, info_ptr,
static_cast<png_uint_32>(width), static_cast<png_uint_32>(height), 8,
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
// Write meta data
writeMetaData(png_ptr, info_ptr, metaData);
// Write comments
writeComments(png_ptr, info_ptr, comments);
// Write the file header information. REQUIRED
png_write_info(png_ptr, info_ptr);
@ -273,7 +267,7 @@ void PNGLibrary::toggleContinuousSnapshots(bool perFrame)
buf << "Enabling snapshots in " << interval << " second intervals";
interval *= static_cast<uInt32>(myOSystem.frameRate());
}
myOSystem.frameBuffer().showTextMessage(buf.view());
myOSystem.frameBuffer().showTextMessage(buf.str());
setContinuousSnapInterval(interval);
}
else
@ -282,7 +276,7 @@ void PNGLibrary::toggleContinuousSnapshots(bool perFrame)
buf << "Disabling snapshots, generated "
<< (mySnapCounter / mySnapInterval)
<< " files";
myOSystem.frameBuffer().showTextMessage(buf.view());
myOSystem.frameBuffer().showTextMessage(buf.str());
setContinuousSnapInterval(0);
}
}
@ -302,9 +296,9 @@ void PNGLibrary::takeSnapshot(uInt32 number)
// Figure out the correct snapshot name
string filename;
const string sspath = myOSystem.snapshotSaveDir().getPath() +
(myOSystem.settings().getString("snapname") != "int"
? myOSystem.romFile().getNameWithExt("")
string sspath = myOSystem.snapshotSaveDir().getPath() +
(myOSystem.settings().getString("snapname") != "int" ?
myOSystem.romFile().getNameWithExt("")
: myOSystem.console().properties().get(PropType::Cart_Name));
// Check whether we want multiple snapshots created
@ -313,14 +307,14 @@ void PNGLibrary::takeSnapshot(uInt32 number)
ostringstream buf;
buf << sspath << "_" << std::hex << std::setw(8) << std::setfill('0')
<< number << ".png";
filename = buf.view();
filename = buf.str();
}
else if(!myOSystem.settings().getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = sspath + ".png";
const FSNode node(filename);
FilesystemNode node(filename);
if(node.exists())
{
ostringstream buf;
@ -328,29 +322,28 @@ void PNGLibrary::takeSnapshot(uInt32 number)
{
buf.str("");
buf << sspath << "_" << i << ".png";
const FSNode next(buf.view());
FilesystemNode next(buf.str());
if(!next.exists())
break;
}
filename = buf.view();
filename = buf.str();
}
}
else
filename = sspath + ".png";
// Some text fields to add to the PNG snapshot
VariantList metaData;
VariantList comments;
ostringstream version;
VarList::push_back(metaData, "Title", "Snapshot");
version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
<< BSPF::ARCH << "]";
VarList::push_back(metaData, "Software", version.view());
VarList::push_back(comments, "Software", version.str());
const string& name = (myOSystem.settings().getString("snapname") == "int")
? myOSystem.console().properties().get(PropType::Cart_Name)
: myOSystem.romFile().getName();
VarList::push_back(metaData, "ROM Name", name);
VarList::push_back(metaData, "ROM MD5", myOSystem.console().properties().get(PropType::Cart_MD5));
VarList::push_back(metaData, "TV Effects", myOSystem.frameBuffer().tiaSurface().effectsInfo());
VarList::push_back(comments, "ROM Name", name);
VarList::push_back(comments, "ROM MD5", myOSystem.console().properties().get(PropType::Cart_MD5));
VarList::push_back(comments, "TV Effects", myOSystem.frameBuffer().tiaSurface().effectsInfo());
// Now create a PNG snapshot
string message = "Snapshot saved";
@ -359,9 +352,8 @@ void PNGLibrary::takeSnapshot(uInt32 number)
try
{
Common::Rect rect;
const FBSurface& surface =
myOSystem.frameBuffer().tiaSurface().baseSurface(rect);
PNGLibrary::saveImage(filename, surface, rect, metaData);
const FBSurface& surface = myOSystem.frameBuffer().tiaSurface().baseSurface(rect);
myOSystem.png().saveImage(filename, surface, rect, comments);
}
catch(const runtime_error& e)
{
@ -376,7 +368,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
try
{
PNGLibrary::saveImage(filename, metaData);
myOSystem.png().saveImage(filename, comments);
}
catch(const runtime_error& e)
{
@ -390,26 +382,26 @@ void PNGLibrary::takeSnapshot(uInt32 number)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::allocateStorage(size_t width, size_t height, bool hasAlpha)
bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
{
// Create space for the entire image (3(4) bytes per pixel in RGB(A) format)
const size_t req_buffer_size = width * height * (hasAlpha ? 4 : 3);
if(req_buffer_size > ReadInfo.buffer.capacity())
ReadInfo.buffer.resize(req_buffer_size * 1.5);
// Create space for the entire image (3 bytes per pixel in RGB format)
const size_t req_buffer_size = w * h * 3;
if(req_buffer_size > ReadInfo.buffer.size())
ReadInfo.buffer.resize(req_buffer_size);
const size_t req_row_size = height;
if(req_row_size > ReadInfo.row_pointers.capacity())
ReadInfo.row_pointers.resize(req_row_size * 1.5);
const size_t req_row_size = h;
if(req_row_size > ReadInfo.row_pointers.size())
ReadInfo.row_pointers.resize(req_row_size);
ReadInfo.width = static_cast<png_uint_32>(width);
ReadInfo.height = static_cast<png_uint_32>(height);
ReadInfo.pitch = static_cast<png_uint_32>(width * (hasAlpha ? 4 : 3));
ReadInfo.width = w;
ReadInfo.height = h;
ReadInfo.pitch = w * 3;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::loadImagetoSurface(FBSurface& surface, bool hasAlpha)
void PNGLibrary::loadImagetoSurface(FBSurface& surface)
{
// First determine if we need to resize the surface
const uInt32 iw = ReadInfo.width, ih = ReadInfo.height;
@ -422,7 +414,7 @@ void PNGLibrary::loadImagetoSurface(FBSurface& surface, bool hasAlpha)
surface.setSrcSize(iw, ih);
// Convert RGB triples into pixels and store in the surface
uInt32 *s_buf{nullptr}, s_pitch{0};
uInt32 *s_buf, s_pitch;
surface.basePtr(s_buf, s_pitch);
const uInt8* i_buf = ReadInfo.buffer.data();
const uInt32 i_pitch = ReadInfo.pitch;
@ -432,78 +424,58 @@ void PNGLibrary::loadImagetoSurface(FBSurface& surface, bool hasAlpha)
{
const uInt8* i_ptr = i_buf;
uInt32* s_ptr = s_buf;
if(hasAlpha)
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 4)
*s_ptr++ = fb.mapRGBA(*i_ptr, *(i_ptr+1), *(i_ptr+2), *(i_ptr+3));
else
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 3)
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 3)
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::writeMetaData(png_structp png_ptr, png_infop info_ptr,
const VariantList& metaData)
void PNGLibrary::writeComments(const png_structp png_ptr, png_infop info_ptr,
const VariantList& comments)
{
const size_t numMetaData = metaData.size();
if(numMetaData == 0)
const uInt32 numComments = static_cast<uInt32>(comments.size());
if(numComments == 0)
return;
vector<png_text> text_ptr(numMetaData);
for(size_t i = 0; i < numMetaData; ++i)
vector<png_text> text_ptr(numComments);
for(uInt32 i = 0; i < numComments; ++i)
{
text_ptr[i].key = const_cast<char*>(metaData[i].first.c_str());
text_ptr[i].text = const_cast<char*>(metaData[i].second.toCString());
text_ptr[i].key = const_cast<char*>(comments[i].first.c_str());
text_ptr[i].text = const_cast<char*>(comments[i].second.toCString());
text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[i].text_length = 0;
}
png_set_text(png_ptr, info_ptr, text_ptr.data(), static_cast<int>(numMetaData));
png_set_text(png_ptr, info_ptr, text_ptr.data(), numComments);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::readMetaData(png_structp png_ptr, png_infop info_ptr,
VariantList& metaData)
{
png_textp text_ptr{nullptr};
int numMetaData{0};
png_get_text(png_ptr, info_ptr, &text_ptr, &numMetaData);
metaData.clear();
for(int i = 0; i < numMetaData; ++i)
{
VarList::push_back(metaData, text_ptr[i].key, text_ptr[i].text);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_read_data(png_structp ctx, png_bytep area, png_size_t size)
void PNGLibrary::png_read_data(const png_structp ctx, png_bytep area, png_size_t size)
{
(static_cast<std::ifstream*>(png_get_io_ptr(ctx)))->read(
reinterpret_cast<char *>(area), size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
void PNGLibrary::png_write_data(const png_structp ctx, png_bytep area, png_size_t size)
{
(static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->write(
reinterpret_cast<const char *>(area), size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_io_flush(png_structp ctx)
void PNGLibrary::png_io_flush(const png_structp ctx)
{
(static_cast<std::ofstream*>(png_get_io_ptr(ctx)))->flush();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_user_warn(png_structp ctx, png_const_charp str)
void PNGLibrary::png_user_warn(const png_structp ctx, png_const_charp str)
{
throw runtime_error(string("PNGLibrary warning: ") + str);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::png_user_error(png_structp ctx, png_const_charp str)
void PNGLibrary::png_user_error(const png_structp ctx, png_const_charp str)
{
throw runtime_error(string("PNGLibrary error: ") + str);
}
@ -511,4 +483,4 @@ void PNGLibrary::png_user_error(png_structp ctx, png_const_charp str)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::ReadInfoType PNGLibrary::ReadInfo;
#endif // IMAGE_SUPPORT
#endif // PNG_SUPPORT

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