[config] Move VbamOption and wxGameControl to config namespace

This commit is contained in:
Fabrice de Gans 2022-10-15 15:35:51 -07:00 committed by Rafael Kitover
parent 297d7c06c4
commit 8c1bd84f40
19 changed files with 2513 additions and 2292 deletions

File diff suppressed because it is too large Load Diff

View File

@ -736,14 +736,14 @@ set(
opts.cpp
sys.cpp
panel.cpp
vbam-options.cpp
vbam-options-static.cpp
viewsupt.cpp
wayland.cpp
strutils.cpp
wxutil.cpp
config/game-control.cpp
config/internal/option-internal.cpp
config/option.cpp
config/user-input.cpp
widgets/gamecontrol.cpp
widgets/keyedit.cpp
widgets/joyedit.cpp
widgets/sdljoy.cpp
@ -777,15 +777,15 @@ set(
filters.h
ioregs.h
opts.h
vbam-options.h
vbam-options-internal.h
viewsupt.h
wxhead.h
wayland.h
wxutil.h
config/game-control.h
config/internal/option-internal.h
config/option.h
config/user-input.h
widgets/dpi-support.h
widgets/wx/gamecontrol.h
widgets/wx/keyedit.h
widgets/wx/joyedit.h
widgets/wx/sdljoy.h

View File

@ -0,0 +1,298 @@
#include "config/game-control.h"
#include "../strutils.h"
#include "opts.h"
#include "wx/log.h"
#include "wxlogdebug.h"
namespace config {
namespace {
constexpr uint32_t kBitKeyA = (1 << 0);
constexpr uint32_t kBitKeyB = (1 << 1);
constexpr uint32_t kBitKeySelect = (1 << 2);
constexpr uint32_t kBitKeyStart = (1 << 3);
constexpr uint32_t kBitKeyRight = (1 << 4);
constexpr uint32_t kBitKeyLeft = (1 << 5);
constexpr uint32_t kBitKeyUp = (1 << 6);
constexpr uint32_t kBitKeyDown = (1 << 7);
constexpr uint32_t kBitKeyR = (1 << 8);
constexpr uint32_t kBitKeyL = (1 << 9);
constexpr uint32_t kBitKeySpeed = (1 << 10);
constexpr uint32_t kBitKeyCapture = (1 << 11);
constexpr uint32_t kBitKeyGameShark = (1 << 12);
constexpr uint32_t kBitKeyAutoA = (1 << 13);
constexpr uint32_t kBitKeyAutoB = (1 << 14);
constexpr uint32_t kBitKeyMotionUp = (1 << 15);
constexpr uint32_t kBitKeyMotionDown = (1 << 16);
constexpr uint32_t kBitKeyMotionLeft = (1 << 17);
constexpr uint32_t kBitKeyMotionRight = (1 << 18);
constexpr uint32_t kBitKeyMotionIn = (1 << 19);
constexpr uint32_t kBitKeyMotionOut = (1 << 20);
// clang-format off
constexpr std::array<uint32_t, kNbGameKeys> kBitMask = {
kBitKeyUp,
kBitKeyDown,
kBitKeyLeft,
kBitKeyRight,
kBitKeyA,
kBitKeyB,
kBitKeyL,
kBitKeyR,
kBitKeySelect,
kBitKeyStart,
kBitKeyMotionUp,
kBitKeyMotionDown,
kBitKeyMotionLeft,
kBitKeyMotionRight,
kBitKeyMotionIn,
kBitKeyMotionOut,
kBitKeyAutoA,
kBitKeyAutoB,
kBitKeySpeed,
kBitKeyCapture,
kBitKeyGameShark,
};
// clang-format on
inline int GameKeyToInt(const GameKey& game_key) {
return static_cast<int>(game_key);
}
// Returns true if `joypad` is in a valid joypad range.
inline bool JoypadInRange(const int& joypad) {
constexpr int kMinJoypadIndex = 0;
return joypad >= kMinJoypadIndex && joypad < kNbJoypads;
}
} // namespace
// clang-format off
wxString GameKeyToString(const GameKey& game_key) {
// Note: this must match GUI widget names or GUI won't work
// This array's order determines tab order as well
static const std::array<wxString, kNbGameKeys> kGameKeyStrings = {
"Up",
"Down",
"Left",
"Right",
"A",
"B",
"L",
"R",
"Select",
"Start",
"MotionUp",
"MotionDown",
"MotionLeft",
"MotionRight",
"MotionIn",
"MotionOut",
"AutoA",
"AutoB",
"Speed",
"Capture",
"GS",
};
return kGameKeyStrings[GameKeyToInt(game_key)];
}
nonstd::optional<GameKey> StringToGameKey(const wxString& input) {
static const std::map<wxString, GameKey> kStringToGameKey = {
{ "Up", GameKey::Up },
{ "Down", GameKey::Down },
{ "Left", GameKey::Left },
{ "Right", GameKey::Right },
{ "A", GameKey::A },
{ "B", GameKey::B },
{ "L", GameKey::L },
{ "R", GameKey::R },
{ "Select", GameKey::Select },
{ "Start", GameKey::Start },
{ "MotionUp", GameKey::MotionUp },
{ "MotionDown", GameKey::MotionDown },
{ "MotionLeft", GameKey::MotionLeft },
{ "MotionRight", GameKey::MotionRight },
{ "MotionIn", GameKey::MotionIn },
{ "MotionOut", GameKey::MotionOut },
{ "AutoA", GameKey::AutoA },
{ "AutoB", GameKey::AutoB },
{ "Speed", GameKey::Speed },
{ "Capture", GameKey::Capture },
{ "GS", GameKey::Gameshark },
};
const auto iter = kStringToGameKey.find(input);
if (iter == kStringToGameKey.end()) {
return nonstd::nullopt;
}
return iter->second;
}
// clang-format on
// static
nonstd::optional<GameControl> GameControl::FromString(const wxString& name) {
static const wxString kJoypad("Joypad");
if (!wxStrncmp(name, kJoypad, kJoypad.size())) {
wxLogDebug("Doesn't start with joypad");
return nonstd::nullopt;
}
auto parts = strutils::split(name, "/");
if (parts.size() != 3) {
wxLogDebug("Wrong split size: %d", parts.size());
return nonstd::nullopt;
}
const int joypad = parts[1][0] - wxT('1');
if (!JoypadInRange(joypad)) {
wxLogDebug("Wrong joypad index: %d", joypad);
return nonstd::nullopt;
}
nonstd::optional<GameKey> game_key = StringToGameKey(parts[2]);
if (!game_key) {
wxLogDebug("Failed to parse game_key: %s", parts[2]);
return nonstd::nullopt;
}
return GameControl(joypad, game_key.value());
}
GameControl::GameControl(int joypad, GameKey game_key)
: joypad_(joypad),
game_key_(game_key),
config_string_(wxString::Format("Joypad/%d/%s",
joypad_ + 1,
GameKeyToString(game_key_))) {
assert(JoypadInRange(joypad_));
}
GameControl::~GameControl() = default;
bool GameControl::operator==(const GameControl& other) const {
return joypad_ == other.joypad_ && game_key_ == other.game_key_;
}
bool GameControl::operator!=(const GameControl& other) const {
return !(*this == other);
}
bool GameControl::operator<(const GameControl& other) const {
if (joypad_ != other.joypad_) {
return joypad_ < other.joypad_;
}
if (game_key_ != other.game_key_) {
return game_key_ < other.game_key_;
}
return false;
}
bool GameControl::operator<=(const GameControl& other) const {
return !(*this > other);
}
bool GameControl::operator>(const GameControl& other) const {
return other < *this;
}
bool GameControl::operator>=(const GameControl& other) const {
return !(*this < other);
}
GameControlState& GameControlState::Instance() {
static GameControlState g_game_control_state;
return g_game_control_state;
}
GameControlState::GameControlState() : joypads_({0, 0, 0, 0}) {}
GameControlState::~GameControlState() = default;
bool GameControlState::OnInputPressed(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);
if (game_keys == input_bindings_.end()) {
// No associated game control for `user_input`.
return false;
}
auto iter = keys_pressed_.find(user_input);
if (iter != keys_pressed_.end()) {
// Double press is noop.
return true;
}
// Remember the key pressed.
keys_pressed_.emplace(user_input);
// Update all corresponding controls.
for (const GameControl& game_control : game_keys->second) {
active_controls_[game_control].emplace(user_input);
joypads_[game_control.joypad_] |=
kBitMask[GameKeyToInt(game_control.game_key_)];
}
return true;
}
bool GameControlState::OnInputReleased(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);
if (game_keys == input_bindings_.end()) {
// No associated game control for `user_input`.
return false;
}
auto iter = keys_pressed_.find(user_input);
if (iter == keys_pressed_.end()) {
// Double release is noop.
return true;
}
// Release the key pressed.
keys_pressed_.erase(iter);
// Update all corresponding controls.
for (const GameControl& game_control : game_keys->second) {
auto active_controls = active_controls_.find(game_control);
if (active_controls == active_controls_.end()) {
// This should never happen.
assert(false);
return true;
}
active_controls->second.erase(user_input);
if (active_controls->second.empty()) {
// Actually release control.
active_controls_.erase(active_controls);
joypads_[game_control.joypad_] &=
~kBitMask[GameKeyToInt(game_control.game_key_)];
}
}
return true;
}
void GameControlState::Reset() {
active_controls_.clear();
keys_pressed_.clear();
joypads_.fill(0);
}
void GameControlState::OnGameBindingsChanged() {
// We should reset to ensure no key remains accidentally pressed following a
// configuration change.
Reset();
input_bindings_.clear();
for (const auto& iter : gopts.game_control_bindings) {
for (const auto& user_input : iter.second) {
input_bindings_[user_input].emplace(iter.first);
}
}
}
uint32_t GameControlState::GetJoypad(int joypad) const {
assert(JoypadInRange(joypad));
return joypads_[joypad];
}
} // namespace config

View File

@ -0,0 +1,144 @@
#ifndef VBAM_WX_CONFIG_GAME_CONTROL_H_
#define VBAM_WX_CONFIG_GAME_CONTROL_H_
#include <array>
#include <map>
#include "nonstd/optional.hpp"
#include <set>
#include <wx/string.h>
#include "config/user-input.h"
namespace config {
// Forward declaration.
class GameControlState;
//clang-format off
// Represents an in-game input.
enum class GameKey {
Up = 0,
Down,
Left,
Right,
A,
B,
L,
R,
Select,
Start,
MotionUp,
MotionDown,
MotionLeft,
MotionRight,
MotionIn,
MotionOut,
AutoA,
AutoB,
Speed,
Capture,
Gameshark,
Last = Gameshark
};
inline constexpr int kNbGameKeys = static_cast<size_t>(GameKey::Last) + 1;
inline constexpr int kNbJoypads = 4;
inline constexpr std::array<GameKey, kNbGameKeys> kAllGameKeys = {
GameKey::Up,
GameKey::Down,
GameKey::Left,
GameKey::Right,
GameKey::A,
GameKey::B,
GameKey::L,
GameKey::R,
GameKey::Select,
GameKey::Start,
GameKey::MotionUp,
GameKey::MotionDown,
GameKey::MotionLeft,
GameKey::MotionRight,
GameKey::MotionIn,
GameKey::MotionOut,
GameKey::AutoA,
GameKey::AutoB,
GameKey::Speed,
GameKey::Capture,
GameKey::Gameshark,
};
//clang-format on
// Conversion utility method. Returns empty string on failure.
// This is O(1).
wxString GameKeyToString(const GameKey& game_key);
// Conversion utility method. Returns std::nullopt on failure.
// This is O(log(kNbGameKeys)).
nonstd::optional<GameKey> StringToGameKey(const wxString& input);
// Abstraction for an in-game control, wich is made of a player index (from 0
// to 3), and a GameKey.
class GameControl {
public:
// Converts a string to a GameControl. Returns std::nullopt on failure.
static nonstd::optional<GameControl> FromString(const wxString& name);
GameControl(int joypad, GameKey game_key);
~GameControl();
wxString ToString() const { return config_string_; };
bool operator==(const GameControl& other) const;
bool operator!=(const GameControl& other) const;
bool operator<(const GameControl& other) const;
bool operator<=(const GameControl& other) const;
bool operator>(const GameControl& other) const;
bool operator>=(const GameControl& other) const;
private:
const int joypad_;
const GameKey game_key_;
const wxString config_string_;
friend class GameControlState;
};
// Tracks in-game input and computes the joypad value used to send control input
// data to the emulator.
class GameControlState {
public:
// This is a global singleton.
static GameControlState& Instance();
// Disable copy constructor and assignment operator.
GameControlState(const GameControlState&) = delete;
GameControlState& operator=(const GameControlState&) = delete;
// Processes `user_input` and updates the internal tracking state.
// Returns true if `user_input` corresponds to a game input.
bool OnInputPressed(const config::UserInput& user_input);
bool OnInputReleased(const config::UserInput& user_input);
// Clears all input.
void Reset();
// Recomputes internal bindinds. This is a potentially slow operation and
// should only be called when the game input configuration has been changed.
void OnGameBindingsChanged();
uint32_t GetJoypad(int joypad) const;
private:
GameControlState();
~GameControlState();
std::map<config::UserInput, std::set<GameControl>> input_bindings_;
std::map<GameControl, std::set<config::UserInput>> active_controls_;
std::set<config::UserInput> keys_pressed_;
std::array<uint32_t, kNbJoypads> joypads_;
};
} // namespace config
#endif // VBAM_WX_CONFIG_GAME_CONTROL_H_

View File

@ -0,0 +1,873 @@
#include "config/option.h"
// Helper implementation file to define and compile all of these huge constants
// separately. These should not be updated very often, so having these in a
// separate file improves incremental build time.
#include <wx/log.h>
#include <algorithm>
#include "../common/ConfigManager.h"
#include "../gb/gbGlobals.h"
#include "opts.h"
#define VBAM_OPTION_INTERNAL_INCLUDE
#include "config/internal/option-internal.h"
#undef VBAM_OPTION_INTERNAL_INCLUDE
namespace config {
namespace {
// clang-format off
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class FilterFunction {
kNone,
k2xsai,
kSuper2xsai,
kSupereagle,
kPixelate,
kAdvmame,
kBilinear,
kBilinearplus,
kScanlines,
kTvmode,
kHQ2x,
kLQ2x,
kSimple2x,
kSimple3x,
kHQ3x,
kSimple4x,
kHQ4x,
kXbrz2x,
kXbrz3x,
kXbrz4x,
kXbrz5x,
kXbrz6x,
kPlugin, // This must always be last.
// Do not add anything under here.
kLast,
};
constexpr size_t kNbFilterFunctions = static_cast<size_t>(FilterFunction::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbFilterFunctions is automatically updated.
static const std::array<wxString, kNbFilterFunctions> kFilterStrings = {
"none",
"2xsai",
"super2xsai",
"supereagle",
"pixelate",
"advmame",
"bilinear",
"bilinearplus",
"scanlines",
"tvmode",
"hq2x",
"lq2x",
"simple2x",
"simple3x",
"hq3x",
"simple4x",
"hq4x",
"xbrz2x",
"xbrz3x",
"xbrz4x",
"xbrz5x",
"xbrz6x",
"plugin",
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class Interframe {
kNone = 0,
kSmart,
kMotionBlur,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbInterframes = static_cast<size_t>(Interframe::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbInterframes is automatically updated.
static const std::array<wxString, kNbInterframes> kInterframeStrings = {
"none",
"smart",
"motionblur",
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class RenderMethod {
kSimple = 0,
kOpenGL,
#ifdef __WXMSW__
kDirect3d,
#elif defined(__WXMAC__)
kQuartz2d,
#endif
// Do not add anything under here.
kLast,
};
constexpr size_t kNbRenderMethods = static_cast<size_t>(RenderMethod::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbRenderMethods is automatically updated.
static const std::array<wxString, kNbRenderMethods> kRenderMethodStrings = {
"simple",
"opengl",
#ifdef __WXMSW__
"direct3d",
#elif defined(__WXMAC__)
"quartz2d",
#endif
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
// TODO: DirectSound and XAudio2 should only be used on Windows.
enum class AudioApi {
kSdl = 0,
kOpenAL,
kDirectSound,
kXAudio2,
kFaudio,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbAudioApis is automatically updated.
static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
"sdl",
"openal",
"directsound",
"xaudio2",
"faudio",
};
enum class SoundQuality {
k48kHz = 0,
k44kHz,
k22kHz,
k11kHz,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbSoundQualities = static_cast<size_t>(SoundQuality::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbSoundQualities is automatically updated.
static const std::array<wxString, kNbSoundQualities> kSoundQualityStrings = {
"48",
"44",
"22",
"11",
};
// clang-format on
// Builds the "all enum values" string for a given array defined above.
template <std::size_t SIZE>
wxString AllEnumValuesForArray(const std::array<wxString, SIZE>& input) {
// 15 characters per option is a reasonable higher bound. Probably.
static constexpr size_t kMaxOptionLength = 15u;
wxString all_options;
all_options.reserve(kMaxOptionLength * SIZE);
std::for_each(input.cbegin(), input.cend(),
[&all_options](const auto& elt) {
all_options.append(elt);
all_options.append('|');
});
// Remove last value
all_options.RemoveLast(1u);
return all_options;
}
} // namespace
// static
std::array<Option, kNbOptions>& Option::AllOptions() {
// These MUST follow the same order as the definitions in OptionID.
// Adding an option without adding to this array will result in a compiler
// error since kNbOptions is automatically updated.
// This will be initialized on the first call, in load_opts(), ensuring the
// translation initialization has already happened.
static std::array<Option, kNbOptions> g_all_opts = {
/// Display
Option(OptionID::kDisplayBilinear, &gopts.bilinear),
Option(OptionID::kDisplayFilter, &gopts.filter),
Option(OptionID::kDisplayFilterPlugin, &gopts.filter_plugin),
Option(OptionID::kDisplayIFB, &gopts.ifb),
Option(OptionID::kDisplayKeepOnTop, &gopts.keep_on_top),
Option(OptionID::kDisplayMaxThreads, &gopts.max_threads, 1, 256),
Option(OptionID::kDisplayRenderMethod, &gopts.render_method),
Option(OptionID::kDisplayScale, &gopts.video_scale, 1, 6),
Option(OptionID::kDisplayStretch, &gopts.retain_aspect),
/// GB
Option(OptionID::kGBBiosFile, &gopts.gb_bios),
Option(OptionID::kGBColorOption, &gbColorOption, 0, 1),
Option(OptionID::kGBColorizerHack, &colorizerHack, 0, 1),
Option(OptionID::kGBLCDFilter, &gbLcdFilter),
Option(OptionID::kGBGBCBiosFile, &gopts.gbc_bios),
Option(OptionID::kGBPalette0, systemGbPalette),
Option(OptionID::kGBPalette1, systemGbPalette + 8),
Option(OptionID::kGBPalette2, systemGbPalette + 16),
Option(OptionID::kGBPrintAutoPage, &gopts.print_auto_page),
Option(OptionID::kGBPrintScreenCap, &gopts.print_screen_cap),
Option(OptionID::kGBROMDir, &gopts.gb_rom_dir),
Option(OptionID::kGBGBCROMDir, &gopts.gbc_rom_dir),
/// GBA
Option(OptionID::kGBABiosFile, &gopts.gba_bios),
Option(OptionID::kGBALCDFilter, &gbaLcdFilter),
#ifndef NO_LINK
Option(OptionID::kGBALinkAuto, &gopts.link_auto),
Option(OptionID::kGBALinkFast, &linkHacks, 0, 1),
Option(OptionID::kGBALinkHost, &gopts.link_host),
Option(OptionID::kGBAServerIP, &gopts.server_ip),
Option(OptionID::kGBALinkPort, &gopts.link_port, 0, 65535),
Option(OptionID::kGBALinkProto, &gopts.link_proto, 0, 1),
Option(OptionID::kGBALinkTimeout, &linkTimeout, 0, 9999999),
Option(OptionID::kGBALinkType, &gopts.gba_link_type, 0, 5),
#endif
Option(OptionID::kGBAROMDir, &gopts.gba_rom_dir),
/// General
Option(OptionID::kGeneralAutoLoadLastState, &gopts.autoload_state),
Option(OptionID::kGeneralBatteryDir, &gopts.battery_dir),
Option(OptionID::kGeneralFreezeRecent, &gopts.recent_freeze),
Option(OptionID::kGeneralRecordingDir, &gopts.recording_dir),
Option(OptionID::kGeneralRewindInterval, &gopts.rewind_interval, 0,
600),
Option(OptionID::kGeneralScreenshotDir, &gopts.scrshot_dir),
Option(OptionID::kGeneralStateDir, &gopts.state_dir),
Option(OptionID::kGeneralStatusBar, &gopts.statusbar, 0, 1),
/// Joypad
Option(OptionID::kJoypad),
Option(OptionID::kJoypadAutofireThrottle, &gopts.autofire_rate, 1,
1000),
Option(OptionID::kJoypadDefault, &gopts.default_stick, 1, 4),
/// Keyboard
Option(OptionID::kKeyboard),
// Core
Option(OptionID::kpreferencesagbPrint, &agbPrint, 0, 1),
Option(OptionID::kpreferencesautoFrameSkip, &autoFrameSkip, 0, 1),
Option(OptionID::kpreferencesautoPatch, &autoPatch, 0, 1),
Option(OptionID::kpreferencesautoSaveLoadCheatList,
&gopts.autoload_cheats),
Option(OptionID::kpreferencesborderAutomatic, &gbBorderAutomatic, 0, 1),
Option(OptionID::kpreferencesborderOn, &gbBorderOn, 0, 1),
Option(OptionID::kpreferencescaptureFormat, &captureFormat, 0, 1),
Option(OptionID::kpreferencescheatsEnabled, &cheatsEnabled, 0, 1),
#ifdef MMX
Option(OptionID::kpreferencesenableMMX, &enableMMX, 0, 1),
#endif
Option(OptionID::kpreferencesdisableStatus, &disableStatusMessages, 0,
1),
Option(OptionID::kpreferencesemulatorType, &gbEmulatorType, 0, 5),
Option(OptionID::kpreferencesflashSize, &optFlashSize, 0, 1),
Option(OptionID::kpreferencesframeSkip, &frameSkip, -1, 9),
Option(OptionID::kpreferencesfsColorDepth, &fsColorDepth, 0, 999),
Option(OptionID::kpreferencesfsFrequency, &fsFrequency, 0, 999),
Option(OptionID::kpreferencesfsHeight, &fsHeight, 0, 99999),
Option(OptionID::kpreferencesfsWidth, &fsWidth, 0, 99999),
Option(OptionID::kpreferencesgbPaletteOption, &gbPaletteOption, 0, 2),
Option(OptionID::kpreferencesgbPrinter, &winGbPrinterEnabled, 0, 1),
Option(OptionID::kpreferencesgdbBreakOnLoad, &gdbBreakOnLoad, 0, 1),
Option(OptionID::kpreferencesgdbPort, &gdbPort, 0, 65535),
#ifndef NO_LINK
Option(OptionID::kpreferencesLinkNumPlayers, &linkNumPlayers, 2, 4),
#endif
Option(OptionID::kpreferencesmaxScale, &maxScale, 0, 100),
Option(OptionID::kpreferencespauseWhenInactive, &pauseWhenInactive, 0,
1),
Option(OptionID::kpreferencesrtcEnabled, &rtcEnabled, 0, 1),
Option(OptionID::kpreferencessaveType, &cpuSaveType, 0, 5),
Option(OptionID::kpreferencesshowSpeed, &showSpeed, 0, 2),
Option(OptionID::kpreferencesshowSpeedTransparent,
&showSpeedTransparent, 0, 1),
Option(OptionID::kpreferencesskipBios, &skipBios, 0, 1),
Option(OptionID::kpreferencesskipSaveGameCheats, &skipSaveGameCheats, 0,
1),
Option(OptionID::kpreferencesskipSaveGameBattery, &skipSaveGameBattery,
0, 1),
Option(OptionID::kpreferencesthrottle, &throttle, 0, 450),
Option(OptionID::kpreferencesspeedupThrottle, &speedup_throttle, 0,
3000),
Option(OptionID::kpreferencesspeedupFrameSkip, &speedup_frame_skip, 0,
300),
Option(OptionID::kpreferencesspeedupThrottleFrameSkip,
&speedup_throttle_frame_skip),
Option(OptionID::kpreferencesuseBiosGB, &useBiosFileGB, 0, 1),
Option(OptionID::kpreferencesuseBiosGBA, &useBiosFileGBA, 0, 1),
Option(OptionID::kpreferencesuseBiosGBC, &useBiosFileGBC, 0, 1),
Option(OptionID::kpreferencesvsync, &vsync, 0, 1),
/// Geometry
Option(OptionID::kgeometryfullScreen, &fullScreen, 0, 1),
Option(OptionID::kgeometryisMaximized, &windowMaximized, 0, 1),
Option(OptionID::kgeometrywindowHeight, &windowHeight, 0, 99999),
Option(OptionID::kgeometrywindowWidth, &windowWidth, 0, 99999),
Option(OptionID::kgeometrywindowX, &windowPositionX, -1, 99999),
Option(OptionID::kgeometrywindowY, &windowPositionY, -1, 99999),
/// UI
Option(OptionID::kuiallowKeyboardBackgroundInput,
&allowKeyboardBackgroundInput),
Option(OptionID::kuiallowJoystickBackgroundInput,
&allowJoystickBackgroundInput),
Option(OptionID::kuihideMenuBar, &gopts.hide_menu_bar),
/// Sound
Option(OptionID::kSoundAudioAPI, &gopts.audio_api),
Option(OptionID::kSoundAudioDevice, &gopts.audio_dev),
Option(OptionID::kSoundBuffers, &gopts.audio_buffers, 2, 10),
Option(OptionID::kSoundEnable, &gopts.sound_en, 0, 0x30f),
Option(OptionID::kSoundGBAFiltering, &gopts.gba_sound_filter, 0, 100),
Option(OptionID::kSoundGBAInterpolation, &gopts.soundInterpolation),
Option(OptionID::kSoundGBDeclicking, &gopts.gb_declick),
Option(OptionID::kSoundGBEcho, &gopts.gb_echo, 0, 100),
Option(OptionID::kSoundGBEnableEffects,
&gopts.gb_effects_config_enabled),
Option(OptionID::kSoundGBStereo, &gopts.gb_stereo, 0, 100),
Option(OptionID::kSoundGBSurround, &gopts.gb_effects_config_surround),
Option(OptionID::kSoundQuality, &gopts.sound_qual),
Option(OptionID::kSoundVolume, &gopts.sound_vol, 0, 200),
};
return g_all_opts;
}
namespace internal {
// These MUST follow the same order as the definitions in OptionID.
// Adding an option without adding to this array will result in a compiler
// error since kNbOptions is automatically updated.
const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
/// Display
OptionData{"Display/Bilinear", "Bilinear",
_("Use bilinear filter with 3d renderer"), Option::Type::kBool},
OptionData{"Display/Filter", "", _("Full-screen filter to apply"),
Option::Type::kFilter},
OptionData{"Display/FilterPlugin", "", _("Filter plugin library"),
Option::Type::kString},
OptionData{"Display/IFB", "", _("Interframe blending function"),
Option::Type::kInterframe},
OptionData{"Display/KeepOnTop", "KeepOnTop", _("Keep window on top"),
Option::Type::kBool},
OptionData{"Display/MaxThreads", "Multithread",
_("Maximum number of threads to run filters in"),
Option::Type::kInt},
OptionData{"Display/RenderMethod", "",
_("Render method; if unsupported, simple method will be used"),
Option::Type::kRenderMethod},
OptionData{"Display/Scale", "", _("Default scale factor"),
Option::Type::kDouble},
OptionData{"Display/Stretch", "RetainAspect",
_("Retain aspect ratio when resizing"), Option::Type::kBool},
/// GB
OptionData{"GB/BiosFile", "", _("BIOS file to use for GB, if enabled"),
Option::Type::kString},
OptionData{"GB/ColorOption", "GBColorOption",
_("GB color enhancement, if enabled"), Option::Type::kInt},
OptionData{"GB/ColorizerHack", "ColorizerHack",
_("Enable DX Colorization Hacks"), Option::Type::kInt},
OptionData{"GB/LCDFilter", "GBLcdFilter", _("Apply LCD filter, if enabled"),
Option::Type::kBool},
OptionData{"GB/GBCBiosFile", "", _("BIOS file to use for GBC, if enabled"),
Option::Type::kString},
OptionData{"GB/Palette0", "",
_("The default palette, as 8 comma-separated 4-digit hex "
"integers (rgb555)."),
Option::Type::kGbPalette},
OptionData{"GB/Palette1", "",
_("The first user palette, as 8 comma-separated 4-digit hex "
"integers (rgb555)."),
Option::Type::kGbPalette},
OptionData{"GB/Palette2", "",
_("The second user palette, as 8 comma-separated 4-digit hex "
"integers (rgb555)."),
Option::Type::kGbPalette},
OptionData{"GB/PrintAutoPage", "PrintGather",
_("Automatically gather a full page before printing"),
Option::Type::kBool},
OptionData{
"GB/PrintScreenCap", "PrintSnap",
_("Automatically save printouts as screen captures with -print suffix"),
Option::Type::kBool},
OptionData{"GB/ROMDir", "", _("Directory to look for ROM files"),
Option::Type::kString},
OptionData{"GB/GBCROMDir", "", _("Directory to look for GBC ROM files"),
Option::Type::kString},
/// GBA
OptionData{"GBA/BiosFile", "", _("BIOS file to use, if enabled"),
Option::Type::kString},
OptionData{"GBA/LCDFilter", "GBALcdFilter",
_("Apply LCD filter, if enabled"), Option::Type::kBool},
#ifndef NO_LINK
OptionData{"GBA/LinkAuto", "LinkAuto", _("Enable link at boot"),
Option::Type::kBool},
OptionData{"GBA/LinkFast", "SpeedOn",
_("Enable faster network protocol by default"),
Option::Type::kInt},
OptionData{"GBA/LinkHost", "", _("Default network link client host"),
Option::Type::kString},
OptionData{"GBA/ServerIP", "", _("Default network link server IP to bind"),
Option::Type::kString},
OptionData{"GBA/LinkPort", "",
_("Default network link port (server and client)"),
Option::Type::kUnsigned},
OptionData{"GBA/LinkProto", "LinkProto", _("Default network protocol"),
Option::Type::kInt},
OptionData{"GBA/LinkTimeout", "LinkTimeout", _("Link timeout (ms)"),
Option::Type::kInt},
OptionData{"GBA/LinkType", "LinkType", _("Link cable type"),
Option::Type::kInt},
#endif
OptionData{"GBA/ROMDir", "", _("Directory to look for ROM files"),
Option::Type::kString},
/// General
OptionData{"General/AutoLoadLastState", "",
_("Automatically load last saved state"), Option::Type::kBool},
OptionData{"General/BatteryDir", "",
_("Directory to store game save files (relative paths are "
"relative to ROM; blank is config dir)"),
Option::Type::kString},
OptionData{"General/FreezeRecent", "", _("Freeze recent load list"),
Option::Type::kBool},
OptionData{"General/RecordingDir", "",
_("Directory to store A/V and game recordings (relative paths "
"are relative to ROM)"),
Option::Type::kString},
OptionData{"General/RewindInterval", "",
_("Number of seconds between rewind snapshots (0 to disable)"),
Option::Type::kInt},
OptionData{"General/ScreenshotDir", "",
_("Directory to store screenshots (relative paths are relative "
"to ROM)"),
Option::Type::kString},
OptionData{"General/StateDir", "",
_("Directory to store saved state files (relative paths are "
"relative to BatteryDir)"),
Option::Type::kString},
OptionData{"General/StatusBar", "StatusBar", _("Enable status bar"),
Option::Type::kInt},
/// Joypad
OptionData{"Joypad/*/*", "",
_("The parameter Joypad/<n>/<button> contains a comma-separated "
"list of key names which map to joypad #<n> button <button>. "
"Button is one of Up, Down, Left, Right, A, B, L, R, Select, "
"Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, "
"AutoB, Speed, Capture, GS"),
Option::Type::kNone},
OptionData{"Joypad/AutofireThrottle", "",
_("The autofire toggle period, in frames (1/60 s)"),
Option::Type::kInt},
OptionData{"Joypad/Default", "",
_("The number of the stick to use in single-player mode"),
Option::Type::kInt},
/// Keyboard
OptionData{"Keyboard/*", "",
_("The parameter Keyboard/<cmd> contains a comma-separated list "
"of key names (e.g. Alt-Shift-F1). When the named key is "
"pressed, the command <cmd> is executed."),
Option::Type::kNone},
// Core
OptionData{"preferences/agbPrint", "AGBPrinter",
_("Enable AGB debug print"), Option::Type::kInt},
OptionData{"preferences/autoFrameSkip", "FrameSkipAuto",
_("Auto skip frames."), Option::Type::kInt},
OptionData{"preferences/autoPatch", "ApplyPatches",
_("Apply IPS/UPS/IPF patches if found"), Option::Type::kInt},
OptionData{"preferences/autoSaveLoadCheatList", "",
_("Automatically save and load cheat list"),
Option::Type::kBool},
OptionData{"preferences/borderAutomatic", "",
_("Automatically enable border for Super GameBoy games"),
Option::Type::kInt},
OptionData{"preferences/borderOn", "", _("Always enable border"),
Option::Type::kInt},
OptionData{"preferences/captureFormat", "", _("Screen capture file format"),
Option::Type::kInt},
OptionData{"preferences/cheatsEnabled", "", _("Enable cheats"),
Option::Type::kInt},
#ifdef MMX
OptionData{"preferences/enableMMX", "MMX", _("Enable MMX"),
Option::Type::kInt},
#endif
OptionData{"preferences/disableStatus", "NoStatusMsg",
_("Disable on-screen status messages"), Option::Type::kInt},
OptionData{"preferences/emulatorType", "", _("Type of system to emulate"),
Option::Type::kInt},
OptionData{"preferences/flashSize", "", _("Flash size 0 = 64KB 1 = 128KB"),
Option::Type::kInt},
OptionData{"preferences/frameSkip", "FrameSkip",
_("Skip frames. Values are 0-9 or -1 to skip automatically "
"based on time."),
Option::Type::kInt},
OptionData{"preferences/fsColorDepth", "",
_("Fullscreen mode color depth (0 = any)"), Option::Type::kInt},
OptionData{"preferences/fsFrequency", "",
_("Fullscreen mode frequency (0 = any)"), Option::Type::kInt},
OptionData{"preferences/fsHeight", "",
_("Fullscreen mode height (0 = desktop)"), Option::Type::kInt},
OptionData{"preferences/fsWidth", "",
_("Fullscreen mode width (0 = desktop)"), Option::Type::kInt},
OptionData{"preferences/gbPaletteOption", "", _("The palette to use"),
Option::Type::kInt},
OptionData{"preferences/gbPrinter", "Printer",
_("Enable printer emulation"), Option::Type::kInt},
OptionData{"preferences/gdbBreakOnLoad", "DebugGDBBreakOnLoad",
_("Break into GDB after loading the game."), Option::Type::kInt},
OptionData{"preferences/gdbPort", "DebugGDBPort",
_("Port to connect GDB to."), Option::Type::kInt},
#ifndef NO_LINK
OptionData{"preferences/LinkNumPlayers", "",
_("Number of players in network"), Option::Type::kInt},
#endif
OptionData{"preferences/maxScale", "",
_("Maximum scale factor (0 = no limit)"), Option::Type::kInt},
OptionData{"preferences/pauseWhenInactive", "PauseWhenInactive",
_("Pause game when main window loses focus"),
Option::Type::kInt},
OptionData{"preferences/rtcEnabled", "RTC",
_("Enable RTC (vba-over.ini override is rtcEnabled"),
Option::Type::kInt},
OptionData{"preferences/saveType", "",
_("Native save (\"battery\") hardware type"),
Option::Type::kInt},
OptionData{"preferences/showSpeed", "", _("Show speed indicator"),
Option::Type::kInt},
OptionData{"preferences/showSpeedTransparent", "Transparent",
_("Draw on-screen messages transparently"), Option::Type::kInt},
OptionData{"preferences/skipBios", "SkipIntro",
_("Skip BIOS initialization"), Option::Type::kInt},
OptionData{"preferences/skipSaveGameCheats", "",
_("Do not overwrite cheat list when loading state"),
Option::Type::kInt},
OptionData{"preferences/skipSaveGameBattery", "",
_("Do not overwrite native (battery) save when loading state"),
Option::Type::kInt},
OptionData{"preferences/throttle", "",
_("Throttle game speed, even when accelerated (0-450%, 0 = no "
"throttle)"),
Option::Type::kUnsigned},
OptionData{"preferences/speedupThrottle", "",
_("Set throttle for speedup key (0-3000%, 0 = no throttle)"),
Option::Type::kUnsigned},
OptionData{"preferences/speedupFrameSkip", "",
_("Number of frames to skip with speedup (instead of speedup "
"throttle)"),
Option::Type::kUnsigned},
OptionData{"preferences/speedupThrottleFrameSkip", "",
_("Use frame skip for speedup throttle"), Option::Type::kBool},
OptionData{"preferences/useBiosGB", "BootRomGB",
_("Use the specified BIOS file for GB"), Option::Type::kInt},
OptionData{"preferences/useBiosGBA", "BootRomEn",
_("Use the specified BIOS file"), Option::Type::kInt},
OptionData{"preferences/useBiosGBC", "BootRomGBC",
_("Use the specified BIOS file for GBC"), Option::Type::kInt},
OptionData{"preferences/vsync", "VSync", _("Wait for vertical sync"),
Option::Type::kInt},
/// Geometry
OptionData{"geometry/fullScreen", "Fullscreen",
_("Enter fullscreen mode at startup"), Option::Type::kInt},
OptionData{"geometry/isMaximized", "Maximized", _("Window maximized"),
Option::Type::kInt},
OptionData{"geometry/windowHeight", "Height", _("Window height at startup"),
Option::Type::kUnsigned},
OptionData{"geometry/windowWidth", "Width", _("Window width at startup"),
Option::Type::kUnsigned},
OptionData{"geometry/windowX", "X", _("Window axis X position at startup"),
Option::Type::kInt},
OptionData{"geometry/windowY", "Y", _("Window axis Y position at startup"),
Option::Type::kInt},
/// UI
OptionData{
"ui/allowKeyboardBackgroundInput", "AllowKeyboardBackgroundInput",
_("Capture key events while on background"), Option::Type::kBool},
OptionData{
"ui/allowJoystickBackgroundInput", "AllowJoystickBackgroundInput",
_("Capture joy events while on background"), Option::Type::kBool},
OptionData{"ui/hideMenuBar", "", _("Hide menu bar when mouse is inactive"),
Option::Type::kBool},
/// Sound
OptionData{"Sound/AudioAPI", "",
_("Sound API; if unsupported, default API will be used"),
Option::Type::kAudioApi},
OptionData{"Sound/AudioDevice", "",
_("Device ID of chosen audio device for chosen driver"),
Option::Type::kString},
OptionData{"Sound/Buffers", "", _("Number of sound buffers"),
Option::Type::kInt},
OptionData{"Sound/Enable", "", _("Bit mask of sound channels to enable"),
Option::Type::kInt},
OptionData{"Sound/GBAFiltering", "", _("GBA sound filtering (%)"),
Option::Type::kInt},
OptionData{"Sound/GBAInterpolation", "GBASoundInterpolation",
_("GBA sound interpolation"), Option::Type::kBool},
OptionData{"Sound/GBDeclicking", "GBDeclicking", _("GB sound declicking"),
Option::Type::kBool},
OptionData{"Sound/GBEcho", "", _("GB echo effect (%)"), Option::Type::kInt},
OptionData{"Sound/GBEnableEffects", "GBEnhanceSound",
_("Enable GB sound effects"), Option::Type::kBool},
OptionData{"Sound/GBStereo", "", _("GB stereo effect (%)"),
Option::Type::kInt},
OptionData{"Sound/GBSurround", "GBSurround",
_("GB surround sound effect (%)"), Option::Type::kBool},
OptionData{"Sound/Quality", "", _("Sound sample rate (kHz)"),
Option::Type::kSoundQuality},
OptionData{"Sound/Volume", "", _("Sound volume (%)"), Option::Type::kInt},
// Last. This should never be used, it actually maps to OptionID::kLast.
// This is to prevent a memory access violation error in case something
// attempts to instantiate a OptionID::kLast. It will trigger a check
// in the Option constructor, but that is after the constructor has
// accessed this entry.
OptionData{"", "", "", Option::Type::kNone},
};
nonstd::optional<OptionID> StringToOptionId(const wxString& input) {
static std::map<wxString, OptionID> kStringToOptionId;
if (kStringToOptionId.empty()) {
for (size_t i = 0; i < kNbOptions; i++) {
kStringToOptionId.emplace(kAllOptionsData[i].config_name,
static_cast<OptionID>(i));
}
assert(kStringToOptionId.size() == kNbOptions);
}
const auto iter = kStringToOptionId.find(input);
if (iter == kStringToOptionId.end()) {
return nonstd::nullopt;
}
return iter->second;
}
wxString FilterToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbFilterFunctions);
return kFilterStrings[value];
}
wxString InterframeToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbInterframes);
return kInterframeStrings[value];
}
wxString RenderMethodToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbRenderMethods);
return kRenderMethodStrings[value];
}
wxString AudioApiToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbAudioApis);
return kAudioApiStrings[value];
}
wxString SoundQualityToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbSoundQualities);
return kSoundQualityStrings[value];
}
int StringToFilter(const wxString& config_name, const wxString& input) {
static std::map<wxString, FilterFunction> kStringToFilter;
if (kStringToFilter.empty()) {
for (size_t i = 0; i < kNbFilterFunctions; i++) {
kStringToFilter.emplace(kFilterStrings[i],
static_cast<FilterFunction>(i));
}
assert(kStringToFilter.size() == kNbFilterFunctions);
}
const auto iter = kStringToFilter.find(input);
if (iter == kStringToFilter.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input, config_name,
AllEnumValuesForType(Option::Type::kFilter));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToInterframe(const wxString& config_name, const wxString& input) {
static std::map<wxString, Interframe> kStringToInterframe;
if (kStringToInterframe.empty()) {
for (size_t i = 0; i < kNbInterframes; i++) {
kStringToInterframe.emplace(kInterframeStrings[i],
static_cast<Interframe>(i));
}
assert(kStringToInterframe.size() == kNbInterframes);
}
const auto iter = kStringToInterframe.find(input);
if (iter == kStringToInterframe.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input, config_name,
AllEnumValuesForType(Option::Type::kInterframe));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToRenderMethod(const wxString& config_name, const wxString& input) {
static std::map<wxString, RenderMethod> kStringToRenderMethod;
if (kStringToRenderMethod.empty()) {
for (size_t i = 0; i < kNbRenderMethods; i++) {
kStringToRenderMethod.emplace(kRenderMethodStrings[i],
static_cast<RenderMethod>(i));
}
assert(kStringToRenderMethod.size() == kNbRenderMethods);
}
const auto iter = kStringToRenderMethod.find(input);
if (iter == kStringToRenderMethod.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input, config_name,
AllEnumValuesForType(Option::Type::kRenderMethod));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToAudioApi(const wxString& config_name, const wxString& input) {
static std::map<wxString, AudioApi> kStringToAudioApi;
if (kStringToAudioApi.empty()) {
for (size_t i = 0; i < kNbAudioApis; i++) {
kStringToAudioApi.emplace(kAudioApiStrings[i],
static_cast<AudioApi>(i));
}
assert(kStringToAudioApi.size() == kNbAudioApis);
}
const auto iter = kStringToAudioApi.find(input);
if (iter == kStringToAudioApi.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input, config_name,
AllEnumValuesForType(Option::Type::kAudioApi));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToSoundQuality(const wxString& config_name, const wxString& input) {
static std::map<wxString, SoundQuality> kStringToSoundQuality;
if (kStringToSoundQuality.empty()) {
for (size_t i = 0; i < kNbSoundQualities; i++) {
kStringToSoundQuality.emplace(kSoundQualityStrings[i],
static_cast<SoundQuality>(i));
}
assert(kStringToSoundQuality.size() == kNbSoundQualities);
}
const auto iter = kStringToSoundQuality.find(input);
if (iter == kStringToSoundQuality.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input, config_name,
AllEnumValuesForType(Option::Type::kSoundQuality));
return 0;
}
return static_cast<int>(iter->second);
}
wxString AllEnumValuesForType(Option::Type type) {
switch (type) {
case Option::Type::kFilter: {
static const wxString kAllFilterValues =
AllEnumValuesForArray(kFilterStrings);
return kAllFilterValues;
}
case Option::Type::kInterframe: {
static const wxString kAllInterframeValues =
AllEnumValuesForArray(kInterframeStrings);
return kAllInterframeValues;
}
case Option::Type::kRenderMethod: {
static const wxString kAllRenderValues =
AllEnumValuesForArray(kRenderMethodStrings);
return kAllRenderValues;
}
case Option::Type::kAudioApi: {
static const wxString kAllAudioApiValues =
AllEnumValuesForArray(kAudioApiStrings);
return kAllAudioApiValues;
}
case Option::Type::kSoundQuality: {
static const wxString kAllSoundQualityValues =
AllEnumValuesForArray(kSoundQualityStrings);
return kAllSoundQualityValues;
}
// We don't use default here to explicitly trigger a compiler warning
// when adding a new value.
case Option::Type::kNone:
case Option::Type::kBool:
case Option::Type::kDouble:
case Option::Type::kInt:
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
return wxEmptyString;
}
assert(false);
return wxEmptyString;
}
int MaxForType(Option::Type type) {
switch (type) {
case Option::Type::kFilter:
return kNbFilterFunctions;
case Option::Type::kInterframe:
return kNbInterframes;
case Option::Type::kRenderMethod:
return kNbRenderMethods;
case Option::Type::kAudioApi:
return kNbAudioApis;
case Option::Type::kSoundQuality:
return kNbSoundQualities;
// We don't use default here to explicitly trigger a compiler warning
// when adding a new value.
case Option::Type::kNone:
case Option::Type::kBool:
case Option::Type::kDouble:
case Option::Type::kInt:
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
return 0;
}
assert(false);
return 0;
}
} // namespace internal
} // namespace config

View File

@ -1,28 +1,29 @@
#ifndef VBAM_OPTIONS_INTERNAL_INCLUDE
#error "Do not include "vbam-options-internal.h" outside of the implementation."
#ifndef VBAM_OPTION_INTERNAL_INCLUDE
#error "Do not include "config/internal/option-internal.h" outside of the implementation."
#endif
#include <wx/string.h>
#include <array>
#include <string>
#include <wx/string.h>
#include "config/option.h"
#include "nonstd/optional.hpp"
#include "vbam-options.h"
namespace config {
namespace internal {
struct VbamOptionData {
struct OptionData {
const wxString config_name;
const wxString command;
const wxString ux_helper;
const VbamOption::Type type;
const Option::Type type;
};
// Static data to initialize global values.
extern const std::array<VbamOptionData, kNbOptions + 1> kAllOptionsData;
extern const std::array<OptionData, kNbOptions + 1> kAllOptionsData;
// Conversion utilities.
nonstd::optional<VbamOptionID> StringToOptionId(const wxString& input);
nonstd::optional<OptionID> StringToOptionId(const wxString& input);
wxString FilterToString(int value);
wxString InterframeToString(int value);
wxString RenderMethodToString(int value);
@ -34,9 +35,10 @@ int StringToRenderMethod(const wxString& config_name, const wxString& input);
int StringToAudioApi(const wxString& config_name, const wxString& input);
int StringToSoundQuality(const wxString& config_name, const wxString& input);
wxString AllEnumValuesForType(VbamOption::Type type);
wxString AllEnumValuesForType(Option::Type type);
// Max value for enum types.
int MaxForType(VbamOption::Type type);
int MaxForType(Option::Type type);
} // namespace internal
} // namespace config

389
src/wx/config/option.cpp Normal file
View File

@ -0,0 +1,389 @@
#include "config/option.h"
#include "nonstd/variant.hpp"
#include <wx/log.h>
#include <wx/translation.h>
#define VBAM_OPTION_INTERNAL_INCLUDE
#include "config/internal/option-internal.h"
#undef VBAM_OPTION_INTERNAL_INCLUDE
namespace config {
// static
Option const* Option::FindByName(const wxString& config_name) {
nonstd::optional<OptionID> option_id =
internal::StringToOptionId(config_name);
if (!option_id) {
return nullptr;
}
return &FindByID(option_id.value());
}
// static
Option& Option::FindByID(OptionID id) {
assert(id != OptionID::Last);
return AllOptions()[static_cast<size_t>(id)];
}
Option::~Option() = default;
Option::Option(OptionID id)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_none());
}
Option::Option(OptionID id, bool* option)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_bool());
}
Option::Option(OptionID id, double* option, double min, double max)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_double());
// Validate the initial value.
SetDouble(*option);
}
Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_int());
// Validate the initial value.
SetInt(*option);
}
Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != OptionID::Last);
assert(is_unsigned());
// Validate the initial value.
SetUnsigned(*option);
}
Option::Option(OptionID id, wxString* option)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_string());
}
Option::Option(OptionID id, int* option)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(0),
max_(internal::MaxForType(type_)) {
assert(id != OptionID::Last);
assert(is_filter() || is_interframe() || is_render_method() ||
is_audio_api() || is_sound_quality());
// Validate the initial value.
SetEnumInt(*option);
}
Option::Option(OptionID id, uint16_t* option)
: id_(id),
config_name_(
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != OptionID::Last);
assert(is_gb_palette());
}
bool Option::GetBool() const {
assert(is_bool());
return *(nonstd::get<bool*>(value_));
}
double Option::GetDouble() const {
assert(is_double());
return *(nonstd::get<double*>(value_));
}
int32_t Option::GetInt() const {
assert(is_int());
return *(nonstd::get<int32_t*>(value_));
}
uint32_t Option::GetUnsigned() const {
assert(is_unsigned());
return *(nonstd::get<uint32_t*>(value_));
}
const wxString Option::GetString() const {
assert(is_string());
return *(nonstd::get<wxString*>(value_));
}
wxString Option::GetEnumString() const {
switch (type_) {
case Option::Type::kFilter:
return internal::FilterToString(*(nonstd::get<int32_t*>(value_)));
case Option::Type::kInterframe:
return internal::InterframeToString(
*(nonstd::get<int32_t*>(value_)));
case Option::Type::kRenderMethod:
return internal::RenderMethodToString(
*(nonstd::get<int32_t*>(value_)));
case Option::Type::kAudioApi:
return internal::AudioApiToString(*(nonstd::get<int32_t*>(value_)));
case Option::Type::kSoundQuality:
return internal::SoundQualityToString(
*(nonstd::get<int32_t*>(value_)));
// We don't use default here to explicitly trigger a compiler warning
// when adding a new value.
case Option::Type::kNone:
case Option::Type::kBool:
case Option::Type::kDouble:
case Option::Type::kInt:
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
return wxEmptyString;
}
assert(false);
return wxEmptyString;
}
wxString Option::GetGbPaletteString() const {
assert(is_gb_palette());
wxString palette_string;
uint16_t const* value = nonstd::get<uint16_t*>(value_);
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X", value[0],
value[1], value[2], value[3], value[4], value[5],
value[6], value[7]);
return palette_string;
}
void Option::SetBool(bool value) const {
assert(is_bool());
*nonstd::get<bool*>(value_) = value;
}
void Option::SetDouble(double value) const {
assert(is_double());
if (value < nonstd::get<double>(min_) ||
value > nonstd::get<double>(max_)) {
wxLogWarning(
_("Invalid value %f for option %s; valid values are %f - %f"),
value, config_name_, nonstd::get<double>(min_),
nonstd::get<double>(max_));
return;
}
*nonstd::get<double*>(value_) = value;
}
void Option::SetInt(int32_t value) const {
assert(is_int());
if (value < nonstd::get<int32_t>(min_) ||
value > nonstd::get<int32_t>(max_)) {
wxLogWarning(
_("Invalid value %d for option %s; valid values are %d - %d"),
value, config_name_, nonstd::get<int32_t>(min_),
nonstd::get<int32_t>(max_));
return;
}
*nonstd::get<int32_t*>(value_) = value;
}
void Option::SetUnsigned(uint32_t value) const {
assert(is_unsigned());
if (value < nonstd::get<uint32_t>(min_) ||
value > nonstd::get<uint32_t>(max_)) {
wxLogWarning(
_("Invalid value %d for option %s; valid values are %d - %d"),
value, config_name_, nonstd::get<uint32_t>(min_),
nonstd::get<uint32_t>(max_));
return;
}
*nonstd::get<uint32_t*>(value_) = value;
}
void Option::SetString(const wxString& value) const {
assert(is_string());
*nonstd::get<wxString*>(value_) = value;
}
void Option::SetEnumString(const wxString& value) const {
switch (type_) {
case Option::Type::kFilter:
SetEnumInt(internal::StringToFilter(config_name_, value));
return;
case Option::Type::kInterframe:
SetEnumInt(internal::StringToInterframe(config_name_, value));
return;
case Option::Type::kRenderMethod:
SetEnumInt(internal::StringToRenderMethod(config_name_, value));
return;
case Option::Type::kAudioApi:
SetEnumInt(internal::StringToAudioApi(config_name_, value));
return;
case Option::Type::kSoundQuality:
SetEnumInt(internal::StringToSoundQuality(config_name_, value));
return;
// We don't use default here to explicitly trigger a compiler warning
// when adding a new value.
case Option::Type::kNone:
case Option::Type::kBool:
case Option::Type::kDouble:
case Option::Type::kInt:
case Option::Type::kUnsigned:
case Option::Type::kString:
case Option::Type::kGbPalette:
assert(false);
return;
}
}
void Option::SetEnumInt(int value) const {
assert(is_filter() || is_interframe() || is_render_method() ||
is_audio_api() || is_sound_quality());
if (value < nonstd::get<int32_t>(min_) ||
value > nonstd::get<int32_t>(max_)) {
wxLogWarning(_("Invalid value %d for option %s; valid values are %s"),
value, config_name_,
internal::AllEnumValuesForType(type_));
return;
}
*nonstd::get<int32_t*>(value_) = value;
}
void Option::SetGbPalette(const wxString& value) const {
assert(is_gb_palette());
// 8 values of 4 chars and 7 commas.
static constexpr size_t kPaletteStringSize = 8 * 4 + 7;
if (value.size() != kPaletteStringSize) {
wxLogWarning(_("Invalid value %s for option %s"), value, config_name_);
return;
}
uint16_t* dest = nonstd::get<uint16_t*>(value_);
for (size_t i = 0; i < 8; i++) {
wxString number = value.substr(i * 5, 4);
long temp = 0;
if (number.ToLong(&temp, 16)) {
dest[i] = temp;
}
}
}
wxString Option::ToHelperString() const {
wxString helper_string = config_name_;
switch (type_) {
case Option::Type::kNone:
break;
case Option::Type::kBool:
helper_string.Append(" (flag)");
break;
case Option::Type::kDouble:
helper_string.Append(" (decimal)");
break;
case Option::Type::kInt:
helper_string.Append(" (int)");
break;
case Option::Type::kUnsigned:
helper_string.Append(" (unsigned)");
break;
case Option::Type::kString:
helper_string.Append(" (string)");
break;
case Option::Type::kFilter:
case Option::Type::kInterframe:
case Option::Type::kRenderMethod:
case Option::Type::kAudioApi:
case Option::Type::kSoundQuality:
helper_string.Append(" (");
helper_string.Append(internal::AllEnumValuesForType(type_));
helper_string.Append(")");
break;
case Option::Type::kGbPalette:
helper_string.Append(" (XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX)");
break;
}
helper_string.Append("\n\t");
helper_string.Append(ux_helper_);
helper_string.Append("\n");
return helper_string;
}
} // namespace config

View File

@ -1,5 +1,5 @@
#ifndef VBAM_OPTIONS_H
#define VBAM_OPTIONS_H
#ifndef VBAM_WX_CONFIG_OPTIONS_H_
#define VBAM_WX_CONFIG_OPTIONS_H_
#include "nonstd/variant.hpp"
@ -7,7 +7,9 @@
#include <wx/string.h>
enum class VbamOptionID {
namespace config {
enum class OptionID {
// Display
kDisplayBilinear = 0,
kDisplayFilter,
@ -144,23 +146,23 @@ enum class VbamOptionID {
Last,
};
constexpr size_t kNbOptions = static_cast<size_t>(VbamOptionID::Last);
constexpr size_t kNbOptions = static_cast<size_t>(OptionID::Last);
// Represents a single option saved in the INI file. VbamOption does not own the
// Represents a single option saved in the INI file. Option does not own the
// individual option, but keeps a pointer to where the data is actually saved.
//
// Ideally, options in the UI code should only be accessed and set via this
// class, which should also take care of updating the INI file when
// VbamOption::Set*() is called. This should also handle keyboard and joystick
// Option::Set*() is called. This should also handle keyboard and joystick
// configuration so option parsing can be done in a uniform manner. If we ever
// get to that point, we would be able to remove most update_opts() calls and
// have individual UI elements access the option via
// VbamOption::FindOptionByID().
// Option::FindByID().
//
// The implementation for this class is largely inspired by base::Value in
// Chromium.
// https://source.chromium.org/chromium/chromium/src/+/main:base/values.h
class VbamOption {
class Option {
public:
enum class Type {
kNone = 0,
@ -177,15 +179,15 @@ public:
kGbPalette,
};
static std::array<VbamOption, kNbOptions>& AllOptions();
static std::array<Option, kNbOptions>& AllOptions();
// O(log(kNbOptions))
static VbamOption const* FindOptionByName(const wxString& config_name);
static Option const* FindByName(const wxString& config_name);
// O(1)
static VbamOption& FindOptionByID(VbamOptionID id);
static Option& FindByID(OptionID id);
~VbamOption();
~Option();
// Accessors.
const wxString& config_name() const { return config_name_; }
@ -235,37 +237,38 @@ public:
private:
// Disable copy and assignment. Every individual option is unique.
VbamOption(const VbamOption&) = delete;
VbamOption& operator=(const VbamOption&) = delete;
Option(const Option&) = delete;
Option& operator=(const Option&) = delete;
VbamOption(VbamOptionID id);
VbamOption(VbamOptionID id, bool* option);
VbamOption(VbamOptionID id, double* option, double min, double max);
VbamOption(VbamOptionID id, int32_t* option, int32_t min, int32_t max);
VbamOption(VbamOptionID id, uint32_t* option, uint32_t min, uint32_t max);
VbamOption(VbamOptionID id, wxString* option);
VbamOption(VbamOptionID id, int* option);
VbamOption(VbamOptionID id, uint16_t* option);
Option(OptionID id);
Option(OptionID id, bool* option);
Option(OptionID id, double* option, double min, double max);
Option(OptionID id, int32_t* option, int32_t min, int32_t max);
Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max);
Option(OptionID id, wxString* option);
Option(OptionID id, int* option);
Option(OptionID id, uint16_t* option);
const VbamOptionID id_;
const OptionID id_;
const wxString config_name_;
const wxString command_;
const wxString ux_helper_;
const Type type_;
const nonstd::variant<
nonstd::monostate,
bool*,
double*,
int32_t*,
uint32_t*,
wxString*,
uint16_t*>
const nonstd::variant<nonstd::monostate,
bool*,
double*,
int32_t*,
uint32_t*,
wxString*,
uint16_t*>
value_;
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> min_;
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> max_;
};
#endif /* VBAM_OPTIONS_H */
} // namespace config
#endif // VBAM_WX_CONFIG_OPTIONS_H_

View File

@ -23,11 +23,11 @@
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include "../gba/CheatSearch.h"
#include "config/game-control.h"
#include "config/option.h"
#include "config/user-input.h"
#include "opts.h"
#include "vbam-options.h"
#include "wx/gamecontrol.h"
#include "../gba/CheatSearch.h"
#if defined(__WXGTK__)
#include "wayland.h"
@ -1673,8 +1673,8 @@ public:
// For the individual clear buttons, we assume their name is
// "Clear" + control_name
// ClearUp for Up; ClearR for R etc
for (const wxGameKey& game_key : kAllGameKeys) {
const wxString control_name = GameKeyToString(game_key);
for (const config::GameKey& game_key : config::kAllGameKeys) {
const wxString control_name = config::GameKeyToString(game_key);
wxJoyKeyTextCtrl* tc = XRCCTRL_D(*p, control_name, wxJoyKeyTextCtrl);
wxString singleClearButton("Clear" + control_name);
if (ev.GetId() == XRCID(singleClearButton.c_str())) {
@ -1683,17 +1683,16 @@ public:
}
}
for (const wxGameKey& game_key : kAllGameKeys) {
wxJoyKeyTextCtrl* tc =
XRCCTRL_D(*p, GameKeyToString(game_key), wxJoyKeyTextCtrl);
for (const config::GameKey& game_key : config::kAllGameKeys) {
wxJoyKeyTextCtrl* tc = XRCCTRL_D(
*p, config::GameKeyToString(game_key), wxJoyKeyTextCtrl);
if (clear) {
tc->SetValue(wxEmptyString);
} else {
tc->SetValue(
config::UserInput::SpanToString(
kDefaultBindings.find(
wxGameControl(0, game_key))->second));
tc->SetValue(config::UserInput::SpanToString(
kDefaultBindings.find(config::GameControl(0, game_key))
->second));
}
}
}
@ -2977,7 +2976,8 @@ bool MainFrame::BindControls()
checkable_mi_t cmi = { cmdtab[i].cmd_id, mi, 0, 0 };
checkable_mi.push_back(cmi);
for (const VbamOption& option : VbamOption::AllOptions()) {
for (const config::Option& option :
config::Option::AllOptions()) {
if (cmdtab[i].cmd == option.command()) {
if (option.is_int()) {
MenuOptionIntMask(
@ -3861,8 +3861,8 @@ bool MainFrame::BindControls()
cb->SetValidator(wxBoolIntValidator(&gopts.default_stick, i + 1));
wxWindow *prev = NULL, *prevp = NULL;
for (const wxGameKey& game_key : kAllGameKeys) {
const wxString control_name = GameKeyToString(game_key);
for (const config::GameKey& game_key : config::kAllGameKeys) {
const wxString control_name = config::GameKeyToString(game_key);
wxJoyKeyTextCtrl* tc = XRCCTRL_D(*w, control_name, wxJoyKeyTextCtrl);
CheckThrowXRCError(tc, control_name);
wxWindow* p = tc->GetParent();
@ -3872,7 +3872,8 @@ bool MainFrame::BindControls()
prev = tc;
prevp = p;
tc->SetValidator(wxJoyKeyValidator(wxGameControl(i, game_key)));
tc->SetValidator(
wxJoyKeyValidator(config::GameControl(i, game_key)));
}
JoyPadConfigHandler[i].p = w;
@ -3882,8 +3883,8 @@ bool MainFrame::BindControls()
w->Connect(XRCID("Clear"), wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(JoyPadConfig_t::JoypadConfigButtons),
NULL, &JoyPadConfigHandler[i]);
for (const wxGameKey& game_key : kAllGameKeys) {
const wxString control_name = GameKeyToString(game_key);
for (const config::GameKey& game_key : config::kAllGameKeys) {
const wxString control_name = config::GameKeyToString(game_key);
w->Connect(XRCID(wxString("Clear" + control_name).c_str()),
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(JoyPadConfig_t::JoypadConfigButtons),

View File

@ -6,7 +6,7 @@
#include <wx/log.h>
#include <wx/display.h>
#include "vbam-options.h"
#include "config/option.h"
#include "wxvbam.h"
#include "strutils.h"
@ -115,138 +115,138 @@ const wxAcceleratorEntryUnicode default_accels[] = {
};
const int num_def_accels = sizeof(default_accels) / sizeof(default_accels[0]);
const std::map<wxGameControl, std::set<config::UserInput>> kDefaultBindings = {
{ wxGameControl(0, wxGameKey::Up), {
const std::map<config::GameControl, std::set<config::UserInput>> kDefaultBindings = {
{ config::GameControl(0, config::GameKey::Up), {
WJKB(wxT('W')),
WJKB(11, wxJoyControl::Button, 1),
WJKB(1, wxJoyControl::AxisMinus, 1),
WJKB(3, wxJoyControl::AxisMinus, 1),
}},
{ wxGameControl(0, wxGameKey::Down), {
{ config::GameControl(0, config::GameKey::Down), {
WJKB(wxT('S')),
WJKB(12, wxJoyControl::Button, 1),
WJKB(1, wxJoyControl::AxisPlus, 1),
WJKB(3, wxJoyControl::AxisPlus, 1),
}},
{ wxGameControl(0, wxGameKey::Left), {
{ config::GameControl(0, config::GameKey::Left), {
WJKB(wxT('A')),
WJKB(13, wxJoyControl::Button, 1),
WJKB(0, wxJoyControl::AxisMinus, 1),
WJKB(2, wxJoyControl::AxisMinus, 1),
}},
{ wxGameControl(0, wxGameKey::Right), {
{ config::GameControl(0, config::GameKey::Right), {
WJKB(wxT('D')),
WJKB(14, wxJoyControl::Button, 1),
WJKB(0, wxJoyControl::AxisPlus, 1),
WJKB(2, wxJoyControl::AxisPlus, 1),
}},
{ wxGameControl(0, wxGameKey::A), {
{ config::GameControl(0, config::GameKey::A), {
WJKB(wxT('L')),
WJKB(0, wxJoyControl::Button, 1),
}},
{ wxGameControl(0, wxGameKey::B), {
{ config::GameControl(0, config::GameKey::B), {
WJKB(wxT('K')),
WJKB(1, wxJoyControl::Button, 1),
}},
{ wxGameControl(0, wxGameKey::L), {
{ config::GameControl(0, config::GameKey::L), {
WJKB(wxT('I')),
WJKB(2, wxJoyControl::Button, 1),
WJKB(9, wxJoyControl::Button, 1),
WJKB(4, wxJoyControl::AxisPlus, 1),
}},
{ wxGameControl(0, wxGameKey::R), {
{ config::GameControl(0, config::GameKey::R), {
WJKB(wxT('O')),
WJKB(3, wxJoyControl::Button, 1),
WJKB(10, wxJoyControl::Button, 1),
WJKB(5, wxJoyControl::AxisPlus, 1),
}},
{ wxGameControl(0, wxGameKey::Select), {
{ config::GameControl(0, config::GameKey::Select), {
WJKB(WXK_BACK),
WJKB(4, wxJoyControl::Button, 1),
}},
{ wxGameControl(0, wxGameKey::Start), {
{ config::GameControl(0, config::GameKey::Start), {
WJKB(WXK_RETURN),
WJKB(6, wxJoyControl::Button, 1),
}},
{ wxGameControl(0, wxGameKey::MotionUp), {}},
{ wxGameControl(0, wxGameKey::MotionDown), {}},
{ wxGameControl(0, wxGameKey::MotionLeft), {}},
{ wxGameControl(0, wxGameKey::MotionRight), {}},
{ wxGameControl(0, wxGameKey::MotionIn), {}},
{ wxGameControl(0, wxGameKey::MotionOut), {}},
{ wxGameControl(0, wxGameKey::AutoA), {}},
{ wxGameControl(0, wxGameKey::AutoB), {}},
{ wxGameControl(0, wxGameKey::Speed), {
{ config::GameControl(0, config::GameKey::MotionUp), {}},
{ config::GameControl(0, config::GameKey::MotionDown), {}},
{ config::GameControl(0, config::GameKey::MotionLeft), {}},
{ config::GameControl(0, config::GameKey::MotionRight), {}},
{ config::GameControl(0, config::GameKey::MotionIn), {}},
{ config::GameControl(0, config::GameKey::MotionOut), {}},
{ config::GameControl(0, config::GameKey::AutoA), {}},
{ config::GameControl(0, config::GameKey::AutoB), {}},
{ config::GameControl(0, config::GameKey::Speed), {
WJKB(WXK_SPACE),
}},
{ wxGameControl(0, wxGameKey::Capture), {}},
{ wxGameControl(0, wxGameKey::Gameshark), {}},
{ config::GameControl(0, config::GameKey::Capture), {}},
{ config::GameControl(0, config::GameKey::Gameshark), {}},
{ wxGameControl(1, wxGameKey::Up), {}},
{ wxGameControl(1, wxGameKey::Down), {}},
{ wxGameControl(1, wxGameKey::Left), {}},
{ wxGameControl(1, wxGameKey::Right), {}},
{ wxGameControl(1, wxGameKey::A), {}},
{ wxGameControl(1, wxGameKey::B), {}},
{ wxGameControl(1, wxGameKey::L), {}},
{ wxGameControl(1, wxGameKey::R), {}},
{ wxGameControl(1, wxGameKey::Select), {}},
{ wxGameControl(1, wxGameKey::Start), {}},
{ wxGameControl(1, wxGameKey::MotionUp), {}},
{ wxGameControl(1, wxGameKey::MotionDown), {}},
{ wxGameControl(1, wxGameKey::MotionLeft), {}},
{ wxGameControl(1, wxGameKey::MotionRight), {}},
{ wxGameControl(1, wxGameKey::MotionIn), {}},
{ wxGameControl(1, wxGameKey::MotionOut), {}},
{ wxGameControl(1, wxGameKey::AutoA), {}},
{ wxGameControl(1, wxGameKey::AutoB), {}},
{ wxGameControl(1, wxGameKey::Speed), {}},
{ wxGameControl(1, wxGameKey::Capture), {}},
{ wxGameControl(1, wxGameKey::Gameshark), {}},
{ config::GameControl(1, config::GameKey::Up), {}},
{ config::GameControl(1, config::GameKey::Down), {}},
{ config::GameControl(1, config::GameKey::Left), {}},
{ config::GameControl(1, config::GameKey::Right), {}},
{ config::GameControl(1, config::GameKey::A), {}},
{ config::GameControl(1, config::GameKey::B), {}},
{ config::GameControl(1, config::GameKey::L), {}},
{ config::GameControl(1, config::GameKey::R), {}},
{ config::GameControl(1, config::GameKey::Select), {}},
{ config::GameControl(1, config::GameKey::Start), {}},
{ config::GameControl(1, config::GameKey::MotionUp), {}},
{ config::GameControl(1, config::GameKey::MotionDown), {}},
{ config::GameControl(1, config::GameKey::MotionLeft), {}},
{ config::GameControl(1, config::GameKey::MotionRight), {}},
{ config::GameControl(1, config::GameKey::MotionIn), {}},
{ config::GameControl(1, config::GameKey::MotionOut), {}},
{ config::GameControl(1, config::GameKey::AutoA), {}},
{ config::GameControl(1, config::GameKey::AutoB), {}},
{ config::GameControl(1, config::GameKey::Speed), {}},
{ config::GameControl(1, config::GameKey::Capture), {}},
{ config::GameControl(1, config::GameKey::Gameshark), {}},
{ wxGameControl(2, wxGameKey::Up), {}},
{ wxGameControl(2, wxGameKey::Down), {}},
{ wxGameControl(2, wxGameKey::Left), {}},
{ wxGameControl(2, wxGameKey::Right), {}},
{ wxGameControl(2, wxGameKey::A), {}},
{ wxGameControl(2, wxGameKey::B), {}},
{ wxGameControl(2, wxGameKey::L), {}},
{ wxGameControl(2, wxGameKey::R), {}},
{ wxGameControl(2, wxGameKey::Select), {}},
{ wxGameControl(2, wxGameKey::Start), {}},
{ wxGameControl(2, wxGameKey::MotionUp), {}},
{ wxGameControl(2, wxGameKey::MotionDown), {}},
{ wxGameControl(2, wxGameKey::MotionLeft), {}},
{ wxGameControl(2, wxGameKey::MotionRight), {}},
{ wxGameControl(2, wxGameKey::MotionIn), {}},
{ wxGameControl(2, wxGameKey::MotionOut), {}},
{ wxGameControl(2, wxGameKey::AutoA), {}},
{ wxGameControl(2, wxGameKey::AutoB), {}},
{ wxGameControl(2, wxGameKey::Speed), {}},
{ wxGameControl(2, wxGameKey::Capture), {}},
{ wxGameControl(2, wxGameKey::Gameshark), {}},
{ config::GameControl(2, config::GameKey::Up), {}},
{ config::GameControl(2, config::GameKey::Down), {}},
{ config::GameControl(2, config::GameKey::Left), {}},
{ config::GameControl(2, config::GameKey::Right), {}},
{ config::GameControl(2, config::GameKey::A), {}},
{ config::GameControl(2, config::GameKey::B), {}},
{ config::GameControl(2, config::GameKey::L), {}},
{ config::GameControl(2, config::GameKey::R), {}},
{ config::GameControl(2, config::GameKey::Select), {}},
{ config::GameControl(2, config::GameKey::Start), {}},
{ config::GameControl(2, config::GameKey::MotionUp), {}},
{ config::GameControl(2, config::GameKey::MotionDown), {}},
{ config::GameControl(2, config::GameKey::MotionLeft), {}},
{ config::GameControl(2, config::GameKey::MotionRight), {}},
{ config::GameControl(2, config::GameKey::MotionIn), {}},
{ config::GameControl(2, config::GameKey::MotionOut), {}},
{ config::GameControl(2, config::GameKey::AutoA), {}},
{ config::GameControl(2, config::GameKey::AutoB), {}},
{ config::GameControl(2, config::GameKey::Speed), {}},
{ config::GameControl(2, config::GameKey::Capture), {}},
{ config::GameControl(2, config::GameKey::Gameshark), {}},
{ wxGameControl(3, wxGameKey::Up), {}},
{ wxGameControl(3, wxGameKey::Down), {}},
{ wxGameControl(3, wxGameKey::Left), {}},
{ wxGameControl(3, wxGameKey::Right), {}},
{ wxGameControl(3, wxGameKey::A), {}},
{ wxGameControl(3, wxGameKey::B), {}},
{ wxGameControl(3, wxGameKey::L), {}},
{ wxGameControl(3, wxGameKey::R), {}},
{ wxGameControl(3, wxGameKey::Select), {}},
{ wxGameControl(3, wxGameKey::Start), {}},
{ wxGameControl(3, wxGameKey::MotionUp), {}},
{ wxGameControl(3, wxGameKey::MotionDown), {}},
{ wxGameControl(3, wxGameKey::MotionLeft), {}},
{ wxGameControl(3, wxGameKey::MotionRight), {}},
{ wxGameControl(3, wxGameKey::MotionIn), {}},
{ wxGameControl(3, wxGameKey::MotionOut), {}},
{ wxGameControl(3, wxGameKey::AutoA), {}},
{ wxGameControl(3, wxGameKey::AutoB), {}},
{ wxGameControl(3, wxGameKey::Speed), {}},
{ wxGameControl(3, wxGameKey::Capture), {}},
{ wxGameControl(3, wxGameKey::Gameshark), {}},
{ config::GameControl(3, config::GameKey::Up), {}},
{ config::GameControl(3, config::GameKey::Down), {}},
{ config::GameControl(3, config::GameKey::Left), {}},
{ config::GameControl(3, config::GameKey::Right), {}},
{ config::GameControl(3, config::GameKey::A), {}},
{ config::GameControl(3, config::GameKey::B), {}},
{ config::GameControl(3, config::GameKey::L), {}},
{ config::GameControl(3, config::GameKey::R), {}},
{ config::GameControl(3, config::GameKey::Select), {}},
{ config::GameControl(3, config::GameKey::Start), {}},
{ config::GameControl(3, config::GameKey::MotionUp), {}},
{ config::GameControl(3, config::GameKey::MotionDown), {}},
{ config::GameControl(3, config::GameKey::MotionLeft), {}},
{ config::GameControl(3, config::GameKey::MotionRight), {}},
{ config::GameControl(3, config::GameKey::MotionIn), {}},
{ config::GameControl(3, config::GameKey::MotionOut), {}},
{ config::GameControl(3, config::GameKey::AutoA), {}},
{ config::GameControl(3, config::GameKey::AutoB), {}},
{ config::GameControl(3, config::GameKey::Speed), {}},
{ config::GameControl(3, config::GameKey::Capture), {}},
{ config::GameControl(3, config::GameKey::Gameshark), {}},
};
wxAcceleratorEntry_v sys_accels;
@ -367,7 +367,7 @@ void load_opts()
for (cont = cfg->GetFirstEntry(e, key_idx); cont;
cont = cfg->GetNextEntry(e, key_idx)) {
if (!StringToGameKey(e)) {
if (!config::StringToGameKey(e)) {
s.append(e);
//wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
@ -403,7 +403,7 @@ void load_opts()
} else {
s.append(wxT('/'));
s.append(e);
if (!VbamOption::FindOptionByName(s) && s != wxT("General/LastUpdated") && s != wxT("General/LastUpdatedFileName")) {
if (!config::Option::FindByName(s) && s != wxT("General/LastUpdated") && s != wxT("General/LastUpdatedFileName")) {
//wxLogWarning(_("Invalid option %s present; removing if possible"), s.c_str());
item_del.push_back(s);
}
@ -425,46 +425,46 @@ void load_opts()
cfg->SetRecordDefaults();
// First access here will also initialize translations.
for (const VbamOption& opt : VbamOption::AllOptions()) {
for (const config::Option& opt : config::Option::AllOptions()) {
switch (opt.type()) {
case VbamOption::Type::kNone:
case config::Option::Type::kNone:
// Keyboard or Joystick. Handled separately for now.
break;
case VbamOption::Type::kBool: {
case config::Option::Type::kBool: {
bool temp;
cfg->Read(opt.config_name(), &temp, opt.GetBool());
opt.SetBool(temp);
break;
}
case VbamOption::Type::kDouble: {
case config::Option::Type::kDouble: {
double temp;
cfg->Read(opt.config_name(), &temp, opt.GetDouble());
opt.SetDouble(temp);
break;
}
case VbamOption::Type::kInt: {
case config::Option::Type::kInt: {
int32_t temp;
cfg->Read(opt.config_name(), &temp, opt.GetInt());
opt.SetInt(temp);
break;
}
case VbamOption::Type::kUnsigned: {
case config::Option::Type::kUnsigned: {
int temp;
cfg->Read(opt.config_name(), &temp, opt.GetUnsigned());
opt.SetUnsigned(temp);
break;
}
case VbamOption::Type::kString: {
case config::Option::Type::kString: {
wxString temp;
cfg->Read(opt.config_name(), &temp, opt.GetString());
opt.SetString(temp);
break;
}
case VbamOption::Type::kFilter:
case VbamOption::Type::kInterframe:
case VbamOption::Type::kRenderMethod:
case VbamOption::Type::kAudioApi:
case VbamOption::Type::kSoundQuality: {
case config::Option::Type::kFilter:
case config::Option::Type::kInterframe:
case config::Option::Type::kRenderMethod:
case config::Option::Type::kAudioApi:
case config::Option::Type::kSoundQuality: {
wxString temp;
if (cfg->Read(opt.config_name(), &temp) && !temp.empty()) {
opt.SetEnumString(temp.MakeLower());
@ -473,7 +473,7 @@ void load_opts()
cfg->Write(opt.config_name(), opt.GetEnumString());
break;
}
case VbamOption::Type::kGbPalette: {
case config::Option::Type::kGbPalette: {
wxString temp;
cfg->Read(opt.config_name(), &temp, opt.GetGbPaletteString());
opt.SetGbPalette(temp);
@ -539,34 +539,34 @@ void update_opts()
{
wxFileConfig* cfg = wxGetApp().cfg;
for (const VbamOption& opt : VbamOption::AllOptions()) {
for (const config::Option& opt : config::Option::AllOptions()) {
switch (opt.type()) {
case VbamOption::Type::kNone:
case config::Option::Type::kNone:
// Keyboard and Joypad are handled separately.
break;
case VbamOption::Type::kBool:
case config::Option::Type::kBool:
cfg->Write(opt.config_name(), opt.GetBool());
break;
case VbamOption::Type::kDouble:
case config::Option::Type::kDouble:
cfg->Write(opt.config_name(), opt.GetDouble());
break;
case VbamOption::Type::kInt:
case config::Option::Type::kInt:
cfg->Write(opt.config_name(), opt.GetInt());
break;
case VbamOption::Type::kUnsigned:
case config::Option::Type::kUnsigned:
cfg->Write(opt.config_name(), opt.GetUnsigned());
break;
case VbamOption::Type::kString:
case config::Option::Type::kString:
cfg->Write(opt.config_name(), opt.GetString());
break;
case VbamOption::Type::kFilter:
case VbamOption::Type::kInterframe:
case VbamOption::Type::kRenderMethod:
case VbamOption::Type::kAudioApi:
case VbamOption::Type::kSoundQuality:
case config::Option::Type::kFilter:
case config::Option::Type::kInterframe:
case config::Option::Type::kRenderMethod:
case config::Option::Type::kAudioApi:
case config::Option::Type::kSoundQuality:
cfg->Write(opt.config_name(), opt.GetEnumString());
break;
case VbamOption::Type::kGbPalette:
case config::Option::Type::kGbPalette:
cfg->Write(opt.config_name(), opt.GetGbPaletteString());
break;
}
@ -587,7 +587,7 @@ void update_opts()
}
}
if (game_bindings_changed) {
wxGameControlState::Instance().OnGameBindingsChanged();
config::GameControlState::Instance().OnGameBindingsChanged();
}
// for keyboard, first remove any commands that aren't bound at all
@ -656,16 +656,16 @@ void update_opts()
}
void opt_set(const wxString& name, const wxString& val) {
VbamOption const* opt = VbamOption::FindOptionByName(name);
config::Option const* opt = config::Option::FindByName(name);
// opt->is_none() means it is Keyboard or Joypad.
if (opt && !opt->is_none()) {
switch (opt->type()) {
case VbamOption::Type::kNone:
case config::Option::Type::kNone:
// This should never happen.
assert(false);
return;
case VbamOption::Type::kBool:
case config::Option::Type::kBool:
if (val != '0' && val != '1') {
wxLogWarning(_("Invalid value %s for option %s"),
name.c_str(), val.c_str());
@ -673,7 +673,7 @@ void opt_set(const wxString& name, const wxString& val) {
}
opt->SetBool(val == '1');
return;
case VbamOption::Type::kDouble: {
case config::Option::Type::kDouble: {
double value;
if (!val.ToDouble(&value)) {
wxLogWarning(_("Invalid value %s for option %s"), val, name.c_str());
@ -682,7 +682,7 @@ void opt_set(const wxString& name, const wxString& val) {
opt->SetDouble(value);
return;
}
case VbamOption::Type::kInt: {
case config::Option::Type::kInt: {
long value;
if (!val.ToLong(&value)) {
wxLogWarning(_("Invalid value %s for option %s"), val, name.c_str());
@ -691,7 +691,7 @@ void opt_set(const wxString& name, const wxString& val) {
opt->SetInt(static_cast<int32_t>(value));
return;
}
case VbamOption::Type::kUnsigned: {
case config::Option::Type::kUnsigned: {
unsigned long value;
if (!val.ToULong(&value)) {
wxLogWarning(_("Invalid value %s for option %s"), val, name.c_str());
@ -700,17 +700,17 @@ void opt_set(const wxString& name, const wxString& val) {
opt->SetUnsigned(static_cast<uint32_t>(value));
return;
}
case VbamOption::Type::kString:
case config::Option::Type::kString:
opt->SetString(val);
return;
case VbamOption::Type::kFilter:
case VbamOption::Type::kInterframe:
case VbamOption::Type::kRenderMethod:
case VbamOption::Type::kAudioApi:
case VbamOption::Type::kSoundQuality:
case config::Option::Type::kFilter:
case config::Option::Type::kInterframe:
case config::Option::Type::kRenderMethod:
case config::Option::Type::kAudioApi:
case config::Option::Type::kSoundQuality:
opt->SetEnumString(val);
return;
case VbamOption::Type::kGbPalette:
case config::Option::Type::kGbPalette:
opt->SetGbPalette(val);
return;
}
@ -754,8 +754,8 @@ void opt_set(const wxString& name, const wxString& val) {
return;
}
const nonstd::optional<wxGameControl> game_control =
wxGameControl::FromString(name);
const nonstd::optional<config::GameControl> game_control =
config::GameControl::FromString(name);
if (game_control) {
if (val.empty()) {
gopts.game_control_bindings[game_control.value()].clear();

View File

@ -6,15 +6,16 @@
#include <wx/string.h>
#include <wx/vidmode.h>
#include "wx/gamecontrol.h"
#include "wx/keyedit.h"
#include "config/game-control.h"
#include "config/user-input.h"
#include "wx/keyedit.h"
// Forward declaration.
class wxFileHistory;
// Default joystick bindings.
extern const std::map<wxGameControl, std::set<config::UserInput>> kDefaultBindings;
extern const std::map<config::GameControl, std::set<config::UserInput>>
kDefaultBindings;
extern struct opts_t {
opts_t();
@ -64,7 +65,8 @@ extern struct opts_t {
int statusbar;
/// Joypad
std::map<wxGameControl, std::set<config::UserInput>> game_control_bindings;
std::map<config::GameControl, std::set<config::UserInput>>
game_control_bindings;
int autofire_rate;
int default_stick;

View File

@ -17,21 +17,21 @@
#include <wx/menu.h>
#include <SDL_joystick.h>
#include "../common/version_cpp.h"
#include "../common/Patch.h"
#include "../common/version_cpp.h"
#include "../gb/gbPrinter.h"
#include "../gba/RTC.h"
#include "../gba/agbprint.h"
#include "../sdl/text.h"
#include "background-input.h"
#include "config/game-control.h"
#include "config/user-input.h"
#include "drawing.h"
#include "filters.h"
#include "wx/joyedit.h"
#include "wx/gamecontrol.h"
#include "wxvbam.h"
#include "wxutil.h"
#include "wayland.h"
#include "background-input.h"
#include "wx/joyedit.h"
#include "wxutil.h"
#include "wxvbam.h"
#ifdef __WXMSW__
#include <windows.h>
@ -958,7 +958,7 @@ GameArea::~GameArea()
void GameArea::OnKillFocus(wxFocusEvent& ev)
{
wxGameControlState::Instance().Reset();
config::GameControlState::Instance().Reset();
ev.Skip();
}
@ -978,7 +978,7 @@ void GameArea::Pause()
// when the game is paused like this, we should not allow any
// input to remain pressed, because they could be released
// outside of the game zone and we would not know about it.
wxGameControlState::Instance().Reset();
config::GameControlState::Instance().Reset();
if (loaded != IMAGE_UNKNOWN)
soundPause();
@ -1212,9 +1212,9 @@ void GameArea::OnIdle(wxIdleEvent& event)
static bool process_user_input(bool down, const config::UserInput& user_input)
{
if (down)
return wxGameControlState::Instance().OnInputPressed(user_input);
return config::GameControlState::Instance().OnInputPressed(user_input);
else
return wxGameControlState::Instance().OnInputReleased(user_input);
return config::GameControlState::Instance().OnInputReleased(user_input);
}
static void draw_black_background(wxWindow* win) {

View File

@ -1,5 +1,5 @@
#include "../common/SoundSDL.h"
#include "wx/gamecontrol.h"
#include "config/game-control.h"
#include "wxvbam.h"
#include "SDL.h"
#include <wx/ffile.h>
@ -331,7 +331,7 @@ uint32_t systemReadJoypad(int joy)
if (joy < 0 || joy > 3)
joy = gopts.default_stick - 1;
uint32_t ret = wxGameControlState::Instance().GetJoypad(joy);
uint32_t ret = config::GameControlState::Instance().GetJoypad(joy);
if (turbo)
ret |= KEYM_SPEED;
@ -641,7 +641,9 @@ void systemUpdateSolarSensor()
void systemUpdateMotionSensor()
{
for (int i = 0; i < 4; i++) {
const uint32_t joy_value = wxGameControlState::Instance().GetJoypad(i);
const uint32_t joy_value =
config::GameControlState::Instance().GetJoypad(i);
if (!sensorx[i])
sensorx[i] = 2047;

View File

@ -1,698 +0,0 @@
#include "vbam-options.h"
// Helper implementation file to define and compile all of these huge constants
// separately. These should not be updated very often, so having these in a
// separate file improves incremental build time.
#include <algorithm>
#include <wx/log.h>
#include "../common/ConfigManager.h"
#include "../gb/gbGlobals.h"
#include "opts.h"
#define VBAM_OPTIONS_INTERNAL_INCLUDE
#include "vbam-options-internal.h"
#undef VBAM_OPTIONS_INTERNAL_INCLUDE
namespace {
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class FilterFunction {
kNone,
k2xsai,
kSuper2xsai,
kSupereagle,
kPixelate,
kAdvmame,
kBilinear,
kBilinearplus,
kScanlines,
kTvmode,
kHQ2x,
kLQ2x,
kSimple2x,
kSimple3x,
kHQ3x,
kSimple4x,
kHQ4x,
kXbrz2x,
kXbrz3x,
kXbrz4x,
kXbrz5x,
kXbrz6x,
kPlugin, // This must always be last.
// Do not add anything under here.
kLast,
};
constexpr size_t kNbFilterFunctions = static_cast<size_t>(FilterFunction::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbFilterFunctions is automatically updated.
static const std::array<wxString, kNbFilterFunctions> kFilterStrings = {
"none",
"2xsai",
"super2xsai",
"supereagle",
"pixelate",
"advmame",
"bilinear",
"bilinearplus",
"scanlines",
"tvmode",
"hq2x",
"lq2x",
"simple2x",
"simple3x",
"hq3x",
"simple4x",
"hq4x",
"xbrz2x",
"xbrz3x",
"xbrz4x",
"xbrz5x",
"xbrz6x",
"plugin",
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class Interframe {
kNone = 0,
kSmart,
kMotionBlur,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbInterframes = static_cast<size_t>(Interframe::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbInterframes is automatically updated.
static const std::array<wxString, kNbInterframes> kInterframeStrings = {
"none",
"smart",
"motionblur",
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
enum class RenderMethod {
kSimple = 0,
kOpenGL,
#ifdef __WXMSW__
kDirect3d,
#elif defined(__WXMAC__)
kQuartz2d,
#endif
// Do not add anything under here.
kLast,
};
constexpr size_t kNbRenderMethods = static_cast<size_t>(RenderMethod::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbRenderMethods is automatically updated.
static const std::array<wxString, kNbRenderMethods> kRenderMethodStrings = {
"simple",
"opengl",
#ifdef __WXMSW__
"direct3d",
#elif defined(__WXMAC__)
"quartz2d",
#endif
};
// This enum must be kept in sync with the one in wxvbam.h
// TODO: These 2 enums should be unified and a validator created for this enum.
// TODO: DirectSound and XAudio2 should only be used on Windows.
enum class AudioApi {
kSdl = 0,
kOpenAL,
kDirectSound,
kXAudio2,
kFaudio,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbAudioApis is automatically updated.
static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
"sdl",
"openal",
"directsound",
"xaudio2",
"faudio",
};
enum class SoundQuality {
k48kHz = 0,
k44kHz,
k22kHz,
k11kHz,
// Do not add anything under here.
kLast,
};
constexpr size_t kNbSoundQualities = static_cast<size_t>(SoundQuality::kLast);
// These MUST follow the same order as the definitions of the enum above.
// Adding an option without adding to this array will result in a compiler
// error since kNbSoundQualities is automatically updated.
static const std::array<wxString, kNbSoundQualities> kSoundQualityStrings = {
"48",
"44",
"22",
"11",
};
// Builds the "all enum values" string for a given array defined above.
template<std::size_t SIZE>
wxString AllEnumValuesForArray(const std::array<wxString, SIZE>& input) {
// 15 characters per option is a reasonable higher bound. Probably.
static constexpr size_t kMaxOptionLength = 15u;
wxString all_options;
all_options.reserve(kMaxOptionLength * SIZE);
std::for_each(input.cbegin(), input.cend(), [&all_options](const auto& elt) {
all_options.append(elt);
all_options.append('|');
});
// Remove last value
all_options.RemoveLast(1u);
return all_options;
}
} // namespace
// static
std::array<VbamOption, kNbOptions>& VbamOption::AllOptions() {
// These MUST follow the same order as the definitions in VbamOptionID.
// Adding an option without adding to this array will result in a compiler
// error since kNbOptions is automatically updated.
// This will be initialized on the first call, in load_opts(), ensuring the
// translation initialization has already happened.
static std::array<VbamOption, kNbOptions> g_all_opts = {
/// Display
VbamOption(VbamOptionID::kDisplayBilinear, &gopts.bilinear),
VbamOption(VbamOptionID::kDisplayFilter, &gopts.filter),
VbamOption(VbamOptionID::kDisplayFilterPlugin, &gopts.filter_plugin),
VbamOption(VbamOptionID::kDisplayIFB, &gopts.ifb),
VbamOption(VbamOptionID::kDisplayKeepOnTop, &gopts.keep_on_top),
VbamOption(VbamOptionID::kDisplayMaxThreads, &gopts.max_threads, 1, 256),
VbamOption(VbamOptionID::kDisplayRenderMethod, &gopts.render_method),
VbamOption(VbamOptionID::kDisplayScale, &gopts.video_scale, 1, 6),
VbamOption(VbamOptionID::kDisplayStretch, &gopts.retain_aspect),
/// GB
VbamOption(VbamOptionID::kGBBiosFile, &gopts.gb_bios),
VbamOption(VbamOptionID::kGBColorOption, &gbColorOption, 0, 1),
VbamOption(VbamOptionID::kGBColorizerHack, &colorizerHack, 0, 1),
VbamOption(VbamOptionID::kGBLCDFilter, &gbLcdFilter),
VbamOption(VbamOptionID::kGBGBCBiosFile, &gopts.gbc_bios),
VbamOption(VbamOptionID::kGBPalette0, systemGbPalette),
VbamOption(VbamOptionID::kGBPalette1, systemGbPalette + 8),
VbamOption(VbamOptionID::kGBPalette2, systemGbPalette + 16),
VbamOption(VbamOptionID::kGBPrintAutoPage, &gopts.print_auto_page),
VbamOption(VbamOptionID::kGBPrintScreenCap, &gopts.print_screen_cap),
VbamOption(VbamOptionID::kGBROMDir, &gopts.gb_rom_dir),
VbamOption(VbamOptionID::kGBGBCROMDir, &gopts.gbc_rom_dir),
/// GBA
VbamOption(VbamOptionID::kGBABiosFile, &gopts.gba_bios),
VbamOption(VbamOptionID::kGBALCDFilter, &gbaLcdFilter),
#ifndef NO_LINK
VbamOption(VbamOptionID::kGBALinkAuto, &gopts.link_auto),
VbamOption(VbamOptionID::kGBALinkFast, &linkHacks, 0, 1),
VbamOption(VbamOptionID::kGBALinkHost, &gopts.link_host),
VbamOption(VbamOptionID::kGBAServerIP, &gopts.server_ip),
VbamOption(VbamOptionID::kGBALinkPort, &gopts.link_port, 0, 65535),
VbamOption(VbamOptionID::kGBALinkProto, &gopts.link_proto, 0, 1),
VbamOption(VbamOptionID::kGBALinkTimeout, &linkTimeout, 0, 9999999),
VbamOption(VbamOptionID::kGBALinkType, &gopts.gba_link_type, 0, 5),
#endif
VbamOption(VbamOptionID::kGBAROMDir, &gopts.gba_rom_dir),
/// General
VbamOption(VbamOptionID::kGeneralAutoLoadLastState, &gopts.autoload_state),
VbamOption(VbamOptionID::kGeneralBatteryDir, &gopts.battery_dir),
VbamOption(VbamOptionID::kGeneralFreezeRecent, &gopts.recent_freeze),
VbamOption(VbamOptionID::kGeneralRecordingDir, &gopts.recording_dir),
VbamOption(VbamOptionID::kGeneralRewindInterval, &gopts.rewind_interval, 0, 600),
VbamOption(VbamOptionID::kGeneralScreenshotDir, &gopts.scrshot_dir),
VbamOption(VbamOptionID::kGeneralStateDir, &gopts.state_dir),
VbamOption(VbamOptionID::kGeneralStatusBar, &gopts.statusbar, 0, 1),
/// Joypad
VbamOption(VbamOptionID::kJoypad),
VbamOption(VbamOptionID::kJoypadAutofireThrottle, &gopts.autofire_rate, 1, 1000),
VbamOption(VbamOptionID::kJoypadDefault, &gopts.default_stick, 1, 4),
/// Keyboard
VbamOption(VbamOptionID::kKeyboard),
// Core
VbamOption(VbamOptionID::kpreferencesagbPrint, &agbPrint, 0, 1),
VbamOption(VbamOptionID::kpreferencesautoFrameSkip, &autoFrameSkip, 0, 1),
VbamOption(VbamOptionID::kpreferencesautoPatch, &autoPatch, 0, 1),
VbamOption(VbamOptionID::kpreferencesautoSaveLoadCheatList, &gopts.autoload_cheats),
VbamOption(VbamOptionID::kpreferencesborderAutomatic, &gbBorderAutomatic, 0, 1),
VbamOption(VbamOptionID::kpreferencesborderOn, &gbBorderOn, 0, 1),
VbamOption(VbamOptionID::kpreferencescaptureFormat, &captureFormat, 0, 1),
VbamOption(VbamOptionID::kpreferencescheatsEnabled, &cheatsEnabled, 0, 1),
#ifdef MMX
VbamOption(VbamOptionID::kpreferencesenableMMX, &enableMMX, 0, 1),
#endif
VbamOption(VbamOptionID::kpreferencesdisableStatus, &disableStatusMessages, 0, 1),
VbamOption(VbamOptionID::kpreferencesemulatorType, &gbEmulatorType, 0, 5),
VbamOption(VbamOptionID::kpreferencesflashSize, &optFlashSize, 0, 1),
VbamOption(VbamOptionID::kpreferencesframeSkip, &frameSkip, -1, 9),
VbamOption(VbamOptionID::kpreferencesfsColorDepth, &fsColorDepth, 0, 999),
VbamOption(VbamOptionID::kpreferencesfsFrequency, &fsFrequency, 0, 999),
VbamOption(VbamOptionID::kpreferencesfsHeight, &fsHeight, 0, 99999),
VbamOption(VbamOptionID::kpreferencesfsWidth, &fsWidth, 0, 99999),
VbamOption(VbamOptionID::kpreferencesgbPaletteOption, &gbPaletteOption, 0, 2),
VbamOption(VbamOptionID::kpreferencesgbPrinter, &winGbPrinterEnabled, 0, 1),
VbamOption(VbamOptionID::kpreferencesgdbBreakOnLoad, &gdbBreakOnLoad, 0, 1),
VbamOption(VbamOptionID::kpreferencesgdbPort, &gdbPort, 0, 65535),
#ifndef NO_LINK
VbamOption(VbamOptionID::kpreferencesLinkNumPlayers, &linkNumPlayers, 2, 4),
#endif
VbamOption(VbamOptionID::kpreferencesmaxScale, &maxScale, 0, 100),
VbamOption(VbamOptionID::kpreferencespauseWhenInactive, &pauseWhenInactive, 0, 1),
VbamOption(VbamOptionID::kpreferencesrtcEnabled, &rtcEnabled, 0, 1),
VbamOption(VbamOptionID::kpreferencessaveType, &cpuSaveType, 0, 5),
VbamOption(VbamOptionID::kpreferencesshowSpeed, &showSpeed, 0, 2),
VbamOption(VbamOptionID::kpreferencesshowSpeedTransparent, &showSpeedTransparent, 0, 1),
VbamOption(VbamOptionID::kpreferencesskipBios, &skipBios, 0, 1),
VbamOption(VbamOptionID::kpreferencesskipSaveGameCheats, &skipSaveGameCheats, 0, 1),
VbamOption(VbamOptionID::kpreferencesskipSaveGameBattery, &skipSaveGameBattery, 0, 1),
VbamOption(VbamOptionID::kpreferencesthrottle, &throttle, 0, 450),
VbamOption(VbamOptionID::kpreferencesspeedupThrottle, &speedup_throttle, 0, 3000),
VbamOption(VbamOptionID::kpreferencesspeedupFrameSkip, &speedup_frame_skip, 0, 300),
VbamOption(VbamOptionID::kpreferencesspeedupThrottleFrameSkip, &speedup_throttle_frame_skip),
VbamOption(VbamOptionID::kpreferencesuseBiosGB, &useBiosFileGB, 0, 1),
VbamOption(VbamOptionID::kpreferencesuseBiosGBA, &useBiosFileGBA, 0, 1),
VbamOption(VbamOptionID::kpreferencesuseBiosGBC, &useBiosFileGBC, 0, 1),
VbamOption(VbamOptionID::kpreferencesvsync, &vsync, 0, 1),
/// Geometry
VbamOption(VbamOptionID::kgeometryfullScreen, &fullScreen, 0, 1),
VbamOption(VbamOptionID::kgeometryisMaximized, &windowMaximized, 0, 1),
VbamOption(VbamOptionID::kgeometrywindowHeight, &windowHeight, 0, 99999),
VbamOption(VbamOptionID::kgeometrywindowWidth, &windowWidth, 0, 99999),
VbamOption(VbamOptionID::kgeometrywindowX, &windowPositionX, -1, 99999),
VbamOption(VbamOptionID::kgeometrywindowY, &windowPositionY, -1, 99999),
/// UI
VbamOption(VbamOptionID::kuiallowKeyboardBackgroundInput, &allowKeyboardBackgroundInput),
VbamOption(VbamOptionID::kuiallowJoystickBackgroundInput, &allowJoystickBackgroundInput),
VbamOption(VbamOptionID::kuihideMenuBar, &gopts.hide_menu_bar),
/// Sound
VbamOption(VbamOptionID::kSoundAudioAPI, &gopts.audio_api),
VbamOption(VbamOptionID::kSoundAudioDevice, &gopts.audio_dev),
VbamOption(VbamOptionID::kSoundBuffers, &gopts.audio_buffers, 2, 10),
VbamOption(VbamOptionID::kSoundEnable, &gopts.sound_en, 0, 0x30f),
VbamOption(VbamOptionID::kSoundGBAFiltering, &gopts.gba_sound_filter, 0, 100),
VbamOption(VbamOptionID::kSoundGBAInterpolation, &gopts.soundInterpolation),
VbamOption(VbamOptionID::kSoundGBDeclicking, &gopts.gb_declick),
VbamOption(VbamOptionID::kSoundGBEcho, &gopts.gb_echo, 0, 100),
VbamOption(VbamOptionID::kSoundGBEnableEffects, &gopts.gb_effects_config_enabled),
VbamOption(VbamOptionID::kSoundGBStereo, &gopts.gb_stereo, 0, 100),
VbamOption(VbamOptionID::kSoundGBSurround, &gopts.gb_effects_config_surround),
VbamOption(VbamOptionID::kSoundQuality, &gopts.sound_qual),
VbamOption(VbamOptionID::kSoundVolume, &gopts.sound_vol, 0, 200),
};
return g_all_opts;
}
namespace internal {
// These MUST follow the same order as the definitions in VbamOptionID.
// Adding an option without adding to this array will result in a compiler
// error since kNbOptions is automatically updated.
const std::array<VbamOptionData, kNbOptions + 1> kAllOptionsData = {
/// Display
VbamOptionData { "Display/Bilinear", "Bilinear", _("Use bilinear filter with 3d renderer"), VbamOption::Type::kBool },
VbamOptionData { "Display/Filter", "", _("Full-screen filter to apply"), VbamOption::Type::kFilter },
VbamOptionData { "Display/FilterPlugin", "", _("Filter plugin library"), VbamOption::Type::kString },
VbamOptionData { "Display/IFB", "", _("Interframe blending function"), VbamOption::Type::kInterframe },
VbamOptionData { "Display/KeepOnTop", "KeepOnTop", _("Keep window on top"), VbamOption::Type::kBool },
VbamOptionData { "Display/MaxThreads", "Multithread", _("Maximum number of threads to run filters in"), VbamOption::Type::kInt },
VbamOptionData { "Display/RenderMethod", "", _("Render method; if unsupported, simple method will be used"), VbamOption::Type::kRenderMethod },
VbamOptionData { "Display/Scale", "", _("Default scale factor"), VbamOption::Type::kDouble },
VbamOptionData { "Display/Stretch", "RetainAspect", _("Retain aspect ratio when resizing"), VbamOption::Type::kBool },
/// GB
VbamOptionData { "GB/BiosFile", "", _("BIOS file to use for GB, if enabled"), VbamOption::Type::kString },
VbamOptionData { "GB/ColorOption", "GBColorOption", _("GB color enhancement, if enabled"), VbamOption::Type::kInt },
VbamOptionData { "GB/ColorizerHack", "ColorizerHack", _("Enable DX Colorization Hacks"), VbamOption::Type::kInt },
VbamOptionData { "GB/LCDFilter", "GBLcdFilter", _("Apply LCD filter, if enabled"), VbamOption::Type::kBool },
VbamOptionData { "GB/GBCBiosFile", "", _("BIOS file to use for GBC, if enabled"), VbamOption::Type::kString },
VbamOptionData { "GB/Palette0", "", _("The default palette, as 8 comma-separated 4-digit hex integers (rgb555)."), VbamOption::Type::kGbPalette },
VbamOptionData { "GB/Palette1", "", _("The first user palette, as 8 comma-separated 4-digit hex integers (rgb555)."), VbamOption::Type::kGbPalette },
VbamOptionData { "GB/Palette2", "", _("The second user palette, as 8 comma-separated 4-digit hex integers (rgb555)."), VbamOption::Type::kGbPalette },
VbamOptionData { "GB/PrintAutoPage", "PrintGather", _("Automatically gather a full page before printing"), VbamOption::Type::kBool },
VbamOptionData { "GB/PrintScreenCap", "PrintSnap", _("Automatically save printouts as screen captures with -print suffix"), VbamOption::Type::kBool },
VbamOptionData { "GB/ROMDir", "", _("Directory to look for ROM files"), VbamOption::Type::kString },
VbamOptionData { "GB/GBCROMDir", "", _("Directory to look for GBC ROM files"), VbamOption::Type::kString },
/// GBA
VbamOptionData { "GBA/BiosFile", "", _("BIOS file to use, if enabled"), VbamOption::Type::kString },
VbamOptionData { "GBA/LCDFilter", "GBALcdFilter", _("Apply LCD filter, if enabled"), VbamOption::Type::kBool },
#ifndef NO_LINK
VbamOptionData { "GBA/LinkAuto", "LinkAuto", _("Enable link at boot"), VbamOption::Type::kBool },
VbamOptionData { "GBA/LinkFast", "SpeedOn", _("Enable faster network protocol by default"), VbamOption::Type::kInt },
VbamOptionData { "GBA/LinkHost", "", _("Default network link client host"), VbamOption::Type::kString },
VbamOptionData { "GBA/ServerIP", "", _("Default network link server IP to bind"), VbamOption::Type::kString },
VbamOptionData { "GBA/LinkPort", "", _("Default network link port (server and client)"), VbamOption::Type::kUnsigned },
VbamOptionData { "GBA/LinkProto", "LinkProto", _("Default network protocol"), VbamOption::Type::kInt },
VbamOptionData { "GBA/LinkTimeout", "LinkTimeout", _("Link timeout (ms)"), VbamOption::Type::kInt },
VbamOptionData { "GBA/LinkType", "LinkType", _("Link cable type"), VbamOption::Type::kInt },
#endif
VbamOptionData { "GBA/ROMDir", "", _("Directory to look for ROM files"), VbamOption::Type::kString },
/// General
VbamOptionData { "General/AutoLoadLastState", "", _("Automatically load last saved state"), VbamOption::Type::kBool },
VbamOptionData { "General/BatteryDir", "", _("Directory to store game save files (relative paths are relative to ROM; blank is config dir)"), VbamOption::Type::kString },
VbamOptionData { "General/FreezeRecent", "", _("Freeze recent load list"), VbamOption::Type::kBool },
VbamOptionData { "General/RecordingDir", "", _("Directory to store A/V and game recordings (relative paths are relative to ROM)"), VbamOption::Type::kString },
VbamOptionData { "General/RewindInterval", "", _("Number of seconds between rewind snapshots (0 to disable)"), VbamOption::Type::kInt },
VbamOptionData { "General/ScreenshotDir", "", _("Directory to store screenshots (relative paths are relative to ROM)"), VbamOption::Type::kString },
VbamOptionData { "General/StateDir", "", _("Directory to store saved state files (relative paths are relative to BatteryDir)"), VbamOption::Type::kString },
VbamOptionData { "General/StatusBar", "StatusBar", _("Enable status bar"), VbamOption::Type::kInt },
/// Joypad
VbamOptionData { "Joypad/*/*", "", _("The parameter Joypad/<n>/<button> contains a comma-separated list of key names which map to joypad #<n> button <button>. Button is one of Up, Down, Left, Right, A, B, L, R, Select, Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, AutoB, Speed, Capture, GS"), VbamOption::Type::kNone },
VbamOptionData { "Joypad/AutofireThrottle", "", _("The autofire toggle period, in frames (1/60 s)"), VbamOption::Type::kInt },
VbamOptionData { "Joypad/Default", "", _("The number of the stick to use in single-player mode"), VbamOption::Type::kInt },
/// Keyboard
VbamOptionData { "Keyboard/*", "", _("The parameter Keyboard/<cmd> contains a comma-separated list of key names (e.g. Alt-Shift-F1). When the named key is pressed, the command <cmd> is executed."), VbamOption::Type::kNone },
// Core
VbamOptionData { "preferences/agbPrint", "AGBPrinter", _("Enable AGB debug print"), VbamOption::Type::kInt },
VbamOptionData { "preferences/autoFrameSkip", "FrameSkipAuto", _("Auto skip frames."), VbamOption::Type::kInt },
VbamOptionData { "preferences/autoPatch", "ApplyPatches", _("Apply IPS/UPS/IPF patches if found"), VbamOption::Type::kInt },
VbamOptionData { "preferences/autoSaveLoadCheatList", "", _("Automatically save and load cheat list"), VbamOption::Type::kBool },
VbamOptionData { "preferences/borderAutomatic", "", _("Automatically enable border for Super GameBoy games"), VbamOption::Type::kInt },
VbamOptionData { "preferences/borderOn", "", _("Always enable border"), VbamOption::Type::kInt },
VbamOptionData { "preferences/captureFormat", "", _("Screen capture file format"), VbamOption::Type::kInt },
VbamOptionData { "preferences/cheatsEnabled", "", _("Enable cheats"), VbamOption::Type::kInt },
#ifdef MMX
VbamOptionData { "preferences/enableMMX", "MMX", _("Enable MMX"), VbamOption::Type::kInt },
#endif
VbamOptionData { "preferences/disableStatus", "NoStatusMsg", _("Disable on-screen status messages"), VbamOption::Type::kInt },
VbamOptionData { "preferences/emulatorType", "", _("Type of system to emulate"), VbamOption::Type::kInt },
VbamOptionData { "preferences/flashSize", "", _("Flash size 0 = 64KB 1 = 128KB"), VbamOption::Type::kInt },
VbamOptionData { "preferences/frameSkip", "FrameSkip", _("Skip frames. Values are 0-9 or -1 to skip automatically based on time."), VbamOption::Type::kInt },
VbamOptionData { "preferences/fsColorDepth", "", _("Fullscreen mode color depth (0 = any)"), VbamOption::Type::kInt },
VbamOptionData { "preferences/fsFrequency", "", _("Fullscreen mode frequency (0 = any)"), VbamOption::Type::kInt },
VbamOptionData { "preferences/fsHeight", "", _("Fullscreen mode height (0 = desktop)"), VbamOption::Type::kInt },
VbamOptionData { "preferences/fsWidth", "", _("Fullscreen mode width (0 = desktop)"), VbamOption::Type::kInt },
VbamOptionData { "preferences/gbPaletteOption", "", _("The palette to use"), VbamOption::Type::kInt },
VbamOptionData { "preferences/gbPrinter", "Printer", _("Enable printer emulation"), VbamOption::Type::kInt },
VbamOptionData { "preferences/gdbBreakOnLoad", "DebugGDBBreakOnLoad", _("Break into GDB after loading the game."), VbamOption::Type::kInt },
VbamOptionData { "preferences/gdbPort", "DebugGDBPort", _("Port to connect GDB to."), VbamOption::Type::kInt },
#ifndef NO_LINK
VbamOptionData { "preferences/LinkNumPlayers", "", _("Number of players in network"), VbamOption::Type::kInt },
#endif
VbamOptionData { "preferences/maxScale", "", _("Maximum scale factor (0 = no limit)"), VbamOption::Type::kInt },
VbamOptionData { "preferences/pauseWhenInactive", "PauseWhenInactive", _("Pause game when main window loses focus"), VbamOption::Type::kInt },
VbamOptionData { "preferences/rtcEnabled", "RTC", _("Enable RTC (vba-over.ini override is rtcEnabled"), VbamOption::Type::kInt },
VbamOptionData { "preferences/saveType", "", _("Native save (\"battery\") hardware type"), VbamOption::Type::kInt },
VbamOptionData { "preferences/showSpeed", "", _("Show speed indicator"), VbamOption::Type::kInt },
VbamOptionData { "preferences/showSpeedTransparent", "Transparent", _("Draw on-screen messages transparently"), VbamOption::Type::kInt },
VbamOptionData { "preferences/skipBios", "SkipIntro", _("Skip BIOS initialization"), VbamOption::Type::kInt },
VbamOptionData { "preferences/skipSaveGameCheats", "", _("Do not overwrite cheat list when loading state"), VbamOption::Type::kInt },
VbamOptionData { "preferences/skipSaveGameBattery", "", _("Do not overwrite native (battery) save when loading state"), VbamOption::Type::kInt },
VbamOptionData { "preferences/throttle", "", _("Throttle game speed, even when accelerated (0-450%, 0 = no throttle)"), VbamOption::Type::kUnsigned },
VbamOptionData { "preferences/speedupThrottle", "", _("Set throttle for speedup key (0-3000%, 0 = no throttle)"), VbamOption::Type::kUnsigned },
VbamOptionData { "preferences/speedupFrameSkip", "", _("Number of frames to skip with speedup (instead of speedup throttle)"), VbamOption::Type::kUnsigned },
VbamOptionData { "preferences/speedupThrottleFrameSkip", "", _("Use frame skip for speedup throttle"), VbamOption::Type::kBool },
VbamOptionData { "preferences/useBiosGB", "BootRomGB", _("Use the specified BIOS file for GB"), VbamOption::Type::kInt },
VbamOptionData { "preferences/useBiosGBA", "BootRomEn", _("Use the specified BIOS file"), VbamOption::Type::kInt },
VbamOptionData { "preferences/useBiosGBC", "BootRomGBC", _("Use the specified BIOS file for GBC"), VbamOption::Type::kInt },
VbamOptionData { "preferences/vsync", "VSync", _("Wait for vertical sync"), VbamOption::Type::kInt },
/// Geometry
VbamOptionData { "geometry/fullScreen", "Fullscreen", _("Enter fullscreen mode at startup"), VbamOption::Type::kInt },
VbamOptionData { "geometry/isMaximized", "Maximized", _("Window maximized"), VbamOption::Type::kInt },
VbamOptionData { "geometry/windowHeight", "Height", _("Window height at startup"), VbamOption::Type::kUnsigned },
VbamOptionData { "geometry/windowWidth", "Width", _("Window width at startup"), VbamOption::Type::kUnsigned },
VbamOptionData { "geometry/windowX", "X", _("Window axis X position at startup"), VbamOption::Type::kInt },
VbamOptionData { "geometry/windowY", "Y", _("Window axis Y position at startup"), VbamOption::Type::kInt },
/// UI
VbamOptionData { "ui/allowKeyboardBackgroundInput", "AllowKeyboardBackgroundInput", _("Capture key events while on background"), VbamOption::Type::kBool },
VbamOptionData { "ui/allowJoystickBackgroundInput", "AllowJoystickBackgroundInput", _("Capture joy events while on background"), VbamOption::Type::kBool },
VbamOptionData { "ui/hideMenuBar", "", _("Hide menu bar when mouse is inactive"), VbamOption::Type::kBool },
/// Sound
VbamOptionData { "Sound/AudioAPI", "", _("Sound API; if unsupported, default API will be used"), VbamOption::Type::kAudioApi },
VbamOptionData { "Sound/AudioDevice", "", _("Device ID of chosen audio device for chosen driver"), VbamOption::Type::kString },
VbamOptionData { "Sound/Buffers", "", _("Number of sound buffers"), VbamOption::Type::kInt },
VbamOptionData { "Sound/Enable", "", _("Bit mask of sound channels to enable"), VbamOption::Type::kInt },
VbamOptionData { "Sound/GBAFiltering", "", _("GBA sound filtering (%)"), VbamOption::Type::kInt },
VbamOptionData { "Sound/GBAInterpolation", "GBASoundInterpolation", _("GBA sound interpolation"), VbamOption::Type::kBool },
VbamOptionData { "Sound/GBDeclicking", "GBDeclicking", _("GB sound declicking"), VbamOption::Type::kBool },
VbamOptionData { "Sound/GBEcho", "", _("GB echo effect (%)"), VbamOption::Type::kInt },
VbamOptionData { "Sound/GBEnableEffects", "GBEnhanceSound", _("Enable GB sound effects"), VbamOption::Type::kBool },
VbamOptionData { "Sound/GBStereo", "", _("GB stereo effect (%)"), VbamOption::Type::kInt },
VbamOptionData { "Sound/GBSurround", "GBSurround", _("GB surround sound effect (%)"), VbamOption::Type::kBool },
VbamOptionData { "Sound/Quality", "", _("Sound sample rate (kHz)"), VbamOption::Type::kSoundQuality },
VbamOptionData { "Sound/Volume", "", _("Sound volume (%)"), VbamOption::Type::kInt },
// Last. This should never be used, it actually maps to VbamOptionID::kLast.
// This is to prevent a memory access violation error in case something
// attempts to instantiate a VbamOptionID::kLast. It will trigger a check
// in the VbamOption constructor, but that is after the constructor has
// accessed this entry.
VbamOptionData { "", "", "", VbamOption::Type::kNone },
};
nonstd::optional<VbamOptionID> StringToOptionId(const wxString& input) {
static std::map<wxString, VbamOptionID> kStringToOptionId;
if (kStringToOptionId.empty()) {
for (size_t i = 0; i < kNbOptions; i++) {
kStringToOptionId.emplace(
kAllOptionsData[i].config_name, static_cast<VbamOptionID>(i));
}
assert(kStringToOptionId.size() == kNbOptions);
}
const auto iter = kStringToOptionId.find(input);
if (iter == kStringToOptionId.end()) {
return nonstd::nullopt;
}
return iter->second;
}
wxString FilterToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbFilterFunctions);
return kFilterStrings[value];
}
wxString InterframeToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbInterframes);
return kInterframeStrings[value];
}
wxString RenderMethodToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbRenderMethods);
return kRenderMethodStrings[value];
}
wxString AudioApiToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbAudioApis);
return kAudioApiStrings[value];
}
wxString SoundQualityToString(int value) {
assert(value >= 0 && static_cast<size_t>(value) < kNbSoundQualities);
return kSoundQualityStrings[value];
}
int StringToFilter(const wxString& config_name, const wxString& input) {
static std::map<wxString, FilterFunction> kStringToFilter;
if (kStringToFilter.empty()) {
for (size_t i = 0; i < kNbFilterFunctions; i++) {
kStringToFilter.emplace(
kFilterStrings[i], static_cast<FilterFunction>(i));
}
assert(kStringToFilter.size() == kNbFilterFunctions);
}
const auto iter = kStringToFilter.find(input);
if (iter == kStringToFilter.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input,
config_name,
AllEnumValuesForType(VbamOption::Type::kFilter));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToInterframe(const wxString& config_name, const wxString& input) {
static std::map<wxString, Interframe> kStringToInterframe;
if (kStringToInterframe.empty()) {
for (size_t i = 0; i < kNbInterframes; i++) {
kStringToInterframe.emplace(
kInterframeStrings[i], static_cast<Interframe>(i));
}
assert(kStringToInterframe.size() == kNbInterframes);
}
const auto iter = kStringToInterframe.find(input);
if (iter == kStringToInterframe.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input,
config_name,
AllEnumValuesForType(VbamOption::Type::kInterframe));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToRenderMethod(const wxString& config_name, const wxString& input) {
static std::map<wxString, RenderMethod> kStringToRenderMethod;
if (kStringToRenderMethod.empty()) {
for (size_t i = 0; i < kNbRenderMethods; i++) {
kStringToRenderMethod.emplace(kRenderMethodStrings[i], static_cast<RenderMethod>(i));
}
assert(kStringToRenderMethod.size() == kNbRenderMethods);
}
const auto iter = kStringToRenderMethod.find(input);
if (iter == kStringToRenderMethod.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input,
config_name,
AllEnumValuesForType(VbamOption::Type::kRenderMethod));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToAudioApi(const wxString& config_name, const wxString& input) {
static std::map<wxString, AudioApi> kStringToAudioApi;
if (kStringToAudioApi.empty()) {
for (size_t i = 0; i < kNbAudioApis; i++) {
kStringToAudioApi.emplace(kAudioApiStrings[i], static_cast<AudioApi>(i));
}
assert(kStringToAudioApi.size() == kNbAudioApis);
}
const auto iter = kStringToAudioApi.find(input);
if (iter == kStringToAudioApi.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input,
config_name,
AllEnumValuesForType(VbamOption::Type::kAudioApi));
return 0;
}
return static_cast<int>(iter->second);
}
int StringToSoundQuality(const wxString& config_name, const wxString& input) {
static std::map<wxString, SoundQuality> kStringToSoundQuality;
if (kStringToSoundQuality.empty()) {
for (size_t i = 0; i < kNbSoundQualities; i++) {
kStringToSoundQuality.emplace(kSoundQualityStrings[i], static_cast<SoundQuality>(i));
}
assert(kStringToSoundQuality.size() == kNbSoundQualities);
}
const auto iter = kStringToSoundQuality.find(input);
if (iter == kStringToSoundQuality.end()) {
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
input,
config_name,
AllEnumValuesForType(VbamOption::Type::kSoundQuality));
return 0;
}
return static_cast<int>(iter->second);
}
wxString AllEnumValuesForType(VbamOption::Type type) {
switch (type) {
case VbamOption::Type::kFilter: {
static const wxString kAllFilterValues = AllEnumValuesForArray(kFilterStrings);
return kAllFilterValues;
}
case VbamOption::Type::kInterframe: {
static const wxString kAllInterframeValues = AllEnumValuesForArray(kInterframeStrings);
return kAllInterframeValues;
}
case VbamOption::Type::kRenderMethod: {
static const wxString kAllRenderValues = AllEnumValuesForArray(kRenderMethodStrings);
return kAllRenderValues;
}
case VbamOption::Type::kAudioApi: {
static const wxString kAllAudioApiValues = AllEnumValuesForArray(kAudioApiStrings);
return kAllAudioApiValues;
}
case VbamOption::Type::kSoundQuality: {
static const wxString kAllSoundQualityValues = AllEnumValuesForArray(kSoundQualityStrings);
return kAllSoundQualityValues;
}
// We don't use default here to explicitly trigger a compiler warning when
// adding a new value.
case VbamOption::Type::kNone:
case VbamOption::Type::kBool:
case VbamOption::Type::kDouble:
case VbamOption::Type::kInt:
case VbamOption::Type::kUnsigned:
case VbamOption::Type::kString:
case VbamOption::Type::kGbPalette:
assert(false);
return wxEmptyString;
}
assert(false);
return wxEmptyString;
}
int MaxForType(VbamOption::Type type) {
switch (type) {
case VbamOption::Type::kFilter:
return kNbFilterFunctions;
case VbamOption::Type::kInterframe:
return kNbInterframes;
case VbamOption::Type::kRenderMethod:
return kNbRenderMethods;
case VbamOption::Type::kAudioApi:
return kNbAudioApis;
case VbamOption::Type::kSoundQuality:
return kNbSoundQualities;
// We don't use default here to explicitly trigger a compiler warning when
// adding a new value.
case VbamOption::Type::kNone:
case VbamOption::Type::kBool:
case VbamOption::Type::kDouble:
case VbamOption::Type::kInt:
case VbamOption::Type::kUnsigned:
case VbamOption::Type::kString:
case VbamOption::Type::kGbPalette:
assert(false);
return 0;
}
assert(false);
return 0;
}
} // namespace internal

View File

@ -1,369 +0,0 @@
#include "vbam-options.h"
#include "nonstd/variant.hpp"
#include <wx/log.h>
#include <wx/translation.h>
#define VBAM_OPTIONS_INTERNAL_INCLUDE
#include "vbam-options-internal.h"
#undef VBAM_OPTIONS_INTERNAL_INCLUDE
// static
VbamOption const* VbamOption::FindOptionByName(const wxString &config_name) {
nonstd::optional<VbamOptionID> option_id = internal::StringToOptionId(config_name);
if (!option_id) {
return nullptr;
}
return &FindOptionByID(option_id.value());
}
// static
VbamOption& VbamOption::FindOptionByID(VbamOptionID id) {
assert (id != VbamOptionID::Last);
return AllOptions()[static_cast<size_t>(id)];
}
VbamOption::~VbamOption() = default;
VbamOption::VbamOption(VbamOptionID id) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(),
min_(),
max_() {
assert(id != VbamOptionID::Last);
assert(is_none());
}
VbamOption::VbamOption(VbamOptionID id, bool* option) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != VbamOptionID::Last);
assert(is_bool());
}
VbamOption::VbamOption(VbamOptionID id, double* option, double min, double max) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != VbamOptionID::Last);
assert(is_double());
// Validate the initial value.
SetDouble(*option);
}
VbamOption::VbamOption(VbamOptionID id, int32_t* option, int32_t min, int32_t max) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != VbamOptionID::Last);
assert(is_int());
// Validate the initial value.
SetInt(*option);
}
VbamOption::VbamOption(VbamOptionID id, uint32_t* option, uint32_t min, uint32_t max) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(min),
max_(max) {
assert(id != VbamOptionID::Last);
assert(is_unsigned());
// Validate the initial value.
SetUnsigned(*option);
}
VbamOption::VbamOption(VbamOptionID id, wxString* option) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != VbamOptionID::Last);
assert(is_string());
}
VbamOption::VbamOption(VbamOptionID id, int* option) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(0),
max_(internal::MaxForType(type_)) {
assert(id != VbamOptionID::Last);
assert(is_filter() || is_interframe() || is_render_method() || is_audio_api() || is_sound_quality());
// Validate the initial value.
SetEnumInt(*option);
}
VbamOption::VbamOption(VbamOptionID id, uint16_t* option) :
id_(id),
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
type_(internal::kAllOptionsData[static_cast<size_t>(id)].type),
value_(option),
min_(),
max_() {
assert(id != VbamOptionID::Last);
assert(is_gb_palette());
}
bool VbamOption::GetBool() const {
assert(is_bool());
return *(nonstd::get<bool*>(value_));
}
double VbamOption::GetDouble() const {
assert(is_double());
return *(nonstd::get<double*>(value_));
}
int32_t VbamOption::GetInt() const {
assert(is_int());
return *(nonstd::get<int32_t*>(value_));
}
uint32_t VbamOption::GetUnsigned() const {
assert(is_unsigned());
return *(nonstd::get<uint32_t*>(value_));
}
const wxString VbamOption::GetString() const {
assert(is_string());
return *(nonstd::get<wxString*>(value_));
}
wxString VbamOption::GetEnumString() const {
switch (type_) {
case VbamOption::Type::kFilter:
return internal::FilterToString(*(nonstd::get<int32_t*>(value_)));
case VbamOption::Type::kInterframe:
return internal::InterframeToString(*(nonstd::get<int32_t*>(value_)));
case VbamOption::Type::kRenderMethod:
return internal::RenderMethodToString(*(nonstd::get<int32_t*>(value_)));
case VbamOption::Type::kAudioApi:
return internal::AudioApiToString(*(nonstd::get<int32_t*>(value_)));
case VbamOption::Type::kSoundQuality:
return internal::SoundQualityToString(*(nonstd::get<int32_t*>(value_)));
// We don't use default here to explicitly trigger a compiler warning when
// adding a new value.
case VbamOption::Type::kNone:
case VbamOption::Type::kBool:
case VbamOption::Type::kDouble:
case VbamOption::Type::kInt:
case VbamOption::Type::kUnsigned:
case VbamOption::Type::kString:
case VbamOption::Type::kGbPalette:
assert(false);
return wxEmptyString;
}
assert(false);
return wxEmptyString;
}
wxString VbamOption::GetGbPaletteString() const {
assert(is_gb_palette());
wxString palette_string;
uint16_t const* value = nonstd::get<uint16_t*>(value_);
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X",
value[0], value[1], value[2], value[3],
value[4], value[5], value[6], value[7]);
return palette_string;
}
void VbamOption::SetBool(bool value) const {
assert(is_bool());
*nonstd::get<bool*>(value_) = value;
}
void VbamOption::SetDouble(double value) const {
assert(is_double());
if (value < nonstd::get<double>(min_) || value > nonstd::get<double>(max_)) {
wxLogWarning(
_("Invalid value %f for option %s; valid values are %f - %f"),
value,
config_name_,
nonstd::get<double>(min_),
nonstd::get<double>(max_));
return;
}
*nonstd::get<double*>(value_) = value;
}
void VbamOption::SetInt(int32_t value) const {
assert(is_int());
if (value < nonstd::get<int32_t>(min_) || value > nonstd::get<int32_t>(max_)) {
wxLogWarning(
_("Invalid value %d for option %s; valid values are %d - %d"),
value,
config_name_,
nonstd::get<int32_t>(min_),
nonstd::get<int32_t>(max_));
return;
}
*nonstd::get<int32_t*>(value_) = value;
}
void VbamOption::SetUnsigned(uint32_t value) const {
assert(is_unsigned());
if (value < nonstd::get<uint32_t>(min_) || value > nonstd::get<uint32_t>(max_)) {
wxLogWarning(
_("Invalid value %d for option %s; valid values are %d - %d"),
value,
config_name_,
nonstd::get<uint32_t>(min_),
nonstd::get<uint32_t>(max_));
return;
}
*nonstd::get<uint32_t*>(value_) = value;
}
void VbamOption::SetString(const wxString& value) const {
assert(is_string());
*nonstd::get<wxString*>(value_) = value;
}
void VbamOption::SetEnumString(const wxString& value) const {
switch (type_) {
case VbamOption::Type::kFilter:
SetEnumInt(internal::StringToFilter(config_name_, value));
return;
case VbamOption::Type::kInterframe:
SetEnumInt(internal::StringToInterframe(config_name_, value));
return;
case VbamOption::Type::kRenderMethod:
SetEnumInt(internal::StringToRenderMethod(config_name_, value));
return;
case VbamOption::Type::kAudioApi:
SetEnumInt(internal::StringToAudioApi(config_name_, value));
return;
case VbamOption::Type::kSoundQuality:
SetEnumInt(internal::StringToSoundQuality(config_name_, value));
return;
// We don't use default here to explicitly trigger a compiler warning when
// adding a new value.
case VbamOption::Type::kNone:
case VbamOption::Type::kBool:
case VbamOption::Type::kDouble:
case VbamOption::Type::kInt:
case VbamOption::Type::kUnsigned:
case VbamOption::Type::kString:
case VbamOption::Type::kGbPalette:
assert(false);
return;
}
}
void VbamOption::SetEnumInt(int value) const {
assert(is_filter() || is_interframe() || is_render_method() || is_audio_api() || is_sound_quality());
if (value < nonstd::get<int32_t>(min_) || value > nonstd::get<int32_t>(max_)) {
wxLogWarning(
_("Invalid value %d for option %s; valid values are %s"),
value,
config_name_,
internal::AllEnumValuesForType(type_));
return;
}
*nonstd::get<int32_t*>(value_) = value;
}
void VbamOption::SetGbPalette(const wxString& value) const {
assert(is_gb_palette());
// 8 values of 4 chars and 7 commas.
static constexpr size_t kPaletteStringSize = 8 * 4 + 7;
if (value.size() != kPaletteStringSize) {
wxLogWarning(_("Invalid value %s for option %s"),
value,
config_name_);
return;
}
uint16_t* dest = nonstd::get<uint16_t*>(value_);
for (size_t i = 0; i < 8; i++) {
wxString number = value.substr(i * 5, 4);
long temp = 0;
if (number.ToLong(&temp, 16)) {
dest[i] = temp;
}
}
}
wxString VbamOption::ToHelperString() const {
wxString helper_string = config_name_;
switch (type_) {
case VbamOption::Type::kNone:
break;
case VbamOption::Type::kBool:
helper_string.Append(" (flag)");
break;
case VbamOption::Type::kDouble:
helper_string.Append(" (decimal)");
break;
case VbamOption::Type::kInt:
helper_string.Append(" (int)");
break;
case VbamOption::Type::kUnsigned:
helper_string.Append(" (unsigned)");
break;
case VbamOption::Type::kString:
helper_string.Append(" (string)");
break;
case VbamOption::Type::kFilter:
case VbamOption::Type::kInterframe:
case VbamOption::Type::kRenderMethod:
case VbamOption::Type::kAudioApi:
case VbamOption::Type::kSoundQuality:
helper_string.Append(" (");
helper_string.Append(internal::AllEnumValuesForType(type_));
helper_string.Append(")");
break;
case VbamOption::Type::kGbPalette:
helper_string.Append(" (XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX)");
break;
}
helper_string.Append("\n\t");
helper_string.Append(ux_helper_);
helper_string.Append("\n");
return helper_string;
}

View File

@ -1,291 +0,0 @@
#include "wx/gamecontrol.h"
#include "../strutils.h"
#include "opts.h"
#include "wx/log.h"
#include "wxlogdebug.h"
namespace {
constexpr uint32_t kBitKeyA = (1 << 0);
constexpr uint32_t kBitKeyB = (1 << 1);
constexpr uint32_t kBitKeySelect = (1 << 2);
constexpr uint32_t kBitKeyStart = (1 << 3);
constexpr uint32_t kBitKeyRight = (1 << 4);
constexpr uint32_t kBitKeyLeft = (1 << 5);
constexpr uint32_t kBitKeyUp = (1 << 6);
constexpr uint32_t kBitKeyDown = (1 << 7);
constexpr uint32_t kBitKeyR = (1 << 8);
constexpr uint32_t kBitKeyL = (1 << 9);
constexpr uint32_t kBitKeySpeed = (1 << 10);
constexpr uint32_t kBitKeyCapture = (1 << 11);
constexpr uint32_t kBitKeyGameShark = (1 << 12);
constexpr uint32_t kBitKeyAutoA = (1 << 13);
constexpr uint32_t kBitKeyAutoB = (1 << 14);
constexpr uint32_t kBitKeyMotionUp = (1 << 15);
constexpr uint32_t kBitKeyMotionDown = (1 << 16);
constexpr uint32_t kBitKeyMotionLeft = (1 << 17);
constexpr uint32_t kBitKeyMotionRight = (1 << 18);
constexpr uint32_t kBitKeyMotionIn = (1 << 19);
constexpr uint32_t kBitKeyMotionOut = (1 << 20);
constexpr std::array<uint32_t, kNbGameKeys> kBitMask = {
kBitKeyUp,
kBitKeyDown,
kBitKeyLeft,
kBitKeyRight,
kBitKeyA,
kBitKeyB,
kBitKeyL,
kBitKeyR,
kBitKeySelect,
kBitKeyStart,
kBitKeyMotionUp,
kBitKeyMotionDown,
kBitKeyMotionLeft,
kBitKeyMotionRight,
kBitKeyMotionIn,
kBitKeyMotionOut,
kBitKeyAutoA,
kBitKeyAutoB,
kBitKeySpeed,
kBitKeyCapture,
kBitKeyGameShark,
};
inline int GameKeyToInt(const wxGameKey& game_key) {
return static_cast<std::underlying_type<wxGameKey>::type>(
game_key);
}
// Returns true if `joypad` is in a valid joypad range.
inline bool JoypadInRange(const int& joypad) {
constexpr int kMinJoypadIndex = 0;
return joypad >= kMinJoypadIndex && joypad < kNbJoypads;
}
} // namespace
wxString GameKeyToString(const wxGameKey& game_key) {
// Note: this must match GUI widget names or GUI won't work
// This array's order determines tab order as well
static const std::array<wxString, kNbGameKeys> kGameKeyStrings = {
wxT("Up"),
wxT("Down"),
wxT("Left"),
wxT("Right"),
wxT("A"),
wxT("B"),
wxT("L"),
wxT("R"),
wxT("Select"),
wxT("Start"),
wxT("MotionUp"),
wxT("MotionDown"),
wxT("MotionLeft"),
wxT("MotionRight"),
wxT("MotionIn"),
wxT("MotionOut"),
wxT("AutoA"),
wxT("AutoB"),
wxT("Speed"),
wxT("Capture"),
wxT("GS"),
};
return kGameKeyStrings[GameKeyToInt(game_key)];
}
nonstd::optional<wxGameKey> StringToGameKey(const wxString& input) {
static const std::map<wxString, wxGameKey> kStringToGameKey = {
{ wxT("Up"), wxGameKey::Up },
{ wxT("Down"), wxGameKey::Down },
{ wxT("Left"), wxGameKey::Left },
{ wxT("Right"), wxGameKey::Right },
{ wxT("A"), wxGameKey::A },
{ wxT("B"), wxGameKey::B },
{ wxT("L"), wxGameKey::L },
{ wxT("R"), wxGameKey::R },
{ wxT("Select"), wxGameKey::Select },
{ wxT("Start"), wxGameKey::Start },
{ wxT("MotionUp"), wxGameKey::MotionUp },
{ wxT("MotionDown"), wxGameKey::MotionDown },
{ wxT("MotionLeft"), wxGameKey::MotionLeft },
{ wxT("MotionRight"), wxGameKey::MotionRight },
{ wxT("MotionIn"), wxGameKey::MotionIn },
{ wxT("MotionOut"), wxGameKey::MotionOut },
{ wxT("AutoA"), wxGameKey::AutoA },
{ wxT("AutoB"), wxGameKey::AutoB },
{ wxT("Speed"), wxGameKey::Speed },
{ wxT("Capture"), wxGameKey::Capture },
{ wxT("GS"), wxGameKey::Gameshark },
};
const auto iter = kStringToGameKey.find(input);
if (iter == kStringToGameKey.end()) {
return nonstd::nullopt;
}
return iter->second;
}
// static
nonstd::optional<wxGameControl> wxGameControl::FromString(const wxString &name) {
static const wxString kJoypad(wxT("Joypad"));
if (!wxStrncmp(name, kJoypad, kJoypad.size())) {
wxLogDebug("Doesn't start with joypad");
return nonstd::nullopt;
}
auto parts = strutils::split(name, wxT("/"));
if (parts.size() != 3) {
wxLogDebug("Wrong split size: %d", parts.size());
return nonstd::nullopt;
}
const int joypad = parts[1][0] - wxT('1');
if (!JoypadInRange(joypad)) {
wxLogDebug("Wrong joypad index: %d", joypad);
return nonstd::nullopt;
}
nonstd::optional<wxGameKey> game_key = StringToGameKey(parts[2]);
if (!game_key) {
wxLogDebug("Failed to parse game_key: %s", parts[2]);
return nonstd::nullopt;
}
return wxGameControl(joypad, game_key.value());
}
wxGameControl::wxGameControl(int joypad, wxGameKey game_key) :
joypad_(joypad),
game_key_(game_key),
config_string_(wxString::Format(
wxT("Joypad/%d/%s"), joypad_ + 1, GameKeyToString(game_key_))) {
assert(JoypadInRange(joypad_));
}
wxGameControl::~wxGameControl() = default;
bool wxGameControl::operator==(const wxGameControl& other) const {
return joypad_ == other.joypad_ && game_key_ == other.game_key_;
}
bool wxGameControl::operator!=(const wxGameControl& other) const {
return !(*this == other);
}
bool wxGameControl::operator<(const wxGameControl& other) const {
if (joypad_ != other.joypad_) {
return joypad_ < other.joypad_;
}
if (game_key_ != other.game_key_) {
return game_key_ < other.game_key_;
}
return false;
}
bool wxGameControl::operator<=(const wxGameControl& other) const {
return !(*this > other);
}
bool wxGameControl::operator>(const wxGameControl& other) const {
return other < *this;
}
bool wxGameControl::operator>=(const wxGameControl& other) const {
return !(*this < other);
}
wxGameControlState& wxGameControlState::Instance() {
static wxGameControlState g_game_control_state;
return g_game_control_state;
}
wxGameControlState::wxGameControlState() : joypads_({0, 0, 0, 0}) {}
wxGameControlState::~wxGameControlState() = default;
bool wxGameControlState::OnInputPressed(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);
if (game_keys == input_bindings_.end()) {
// No associated game control for `user_input`.
return false;
}
auto iter = keys_pressed_.find(user_input);
if (iter != keys_pressed_.end()) {
// Double press is noop.
return true;
}
// Remember the key pressed.
keys_pressed_.emplace(user_input);
// Update all corresponding controls.
for (const wxGameControl& game_control : game_keys->second) {
active_controls_[game_control].emplace(user_input);
joypads_[game_control.joypad_] |=
kBitMask[GameKeyToInt(game_control.game_key_)];
}
return true;
}
bool wxGameControlState::OnInputReleased(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);
if (game_keys == input_bindings_.end()) {
// No associated game control for `user_input`.
return false;
}
auto iter = keys_pressed_.find(user_input);
if (iter == keys_pressed_.end()) {
// Double release is noop.
return true;
}
// Release the key pressed.
keys_pressed_.erase(iter);
// Update all corresponding controls.
for (const wxGameControl& game_control : game_keys->second) {
auto active_controls = active_controls_.find(game_control);
if (active_controls == active_controls_.end()) {
// This should never happen.
assert(false);
return true;
}
active_controls->second.erase(user_input);
if (active_controls->second.empty()) {
// Actually release control.
active_controls_.erase(active_controls);
joypads_[game_control.joypad_] &=
~kBitMask[GameKeyToInt(game_control.game_key_)];
}
}
return true;
}
void wxGameControlState::Reset() {
active_controls_.clear();
keys_pressed_.clear();
joypads_.fill(0);
}
void wxGameControlState::OnGameBindingsChanged() {
// We should reset to ensure no key remains accidentally pressed following a
// configuration change.
Reset();
input_bindings_.clear();
for (const auto& iter : gopts.game_control_bindings) {
for (const auto& user_input : iter.second) {
input_bindings_[user_input].emplace(iter.first);
}
}
}
uint32_t wxGameControlState::GetJoypad(int joypad) const {
assert(JoypadInRange(joypad));
return joypads_[joypad];
}

View File

@ -1,139 +0,0 @@
#ifndef _WX_GAME_CONTROL_H_
#define _WX_GAME_CONTROL_H_
#include <array>
#include <map>
#include "nonstd/optional.hpp"
#include <set>
#include <wx/string.h>
#include "config/user-input.h"
// Forward declaration.
class wxGameControlState;
// Represents an in-game input.
enum class wxGameKey {
Up = 0,
Down,
Left,
Right,
A,
B,
L,
R,
Select,
Start,
MotionUp,
MotionDown,
MotionLeft,
MotionRight,
MotionIn,
MotionOut,
AutoA,
AutoB,
Speed,
Capture,
Gameshark,
Last = Gameshark
};
inline constexpr int kNbGameKeys =
static_cast<std::underlying_type<wxGameKey>::type>(wxGameKey::Last) + 1;
inline constexpr int kNbJoypads = 4;
inline constexpr std::array<wxGameKey, kNbGameKeys> kAllGameKeys = {
wxGameKey::Up,
wxGameKey::Down,
wxGameKey::Left,
wxGameKey::Right,
wxGameKey::A,
wxGameKey::B,
wxGameKey::L,
wxGameKey::R,
wxGameKey::Select,
wxGameKey::Start,
wxGameKey::MotionUp,
wxGameKey::MotionDown,
wxGameKey::MotionLeft,
wxGameKey::MotionRight,
wxGameKey::MotionIn,
wxGameKey::MotionOut,
wxGameKey::AutoA,
wxGameKey::AutoB,
wxGameKey::Speed,
wxGameKey::Capture,
wxGameKey::Gameshark,
};
// Conversion utility method. Returns empty string on failure.
// This is O(1).
wxString GameKeyToString(const wxGameKey& game_key);
// Conversion utility method. Returns std::nullopt on failure.
// This is O(log(kNbGameKeys)).
nonstd::optional<wxGameKey> StringToGameKey(const wxString& input);
// Abstraction for an in-game control, wich is made of a player index (from 0
// to 3), and a wxGameKey.
class wxGameControl {
public:
// Converts a string to a wxGameControl. Returns std::nullopt on failure.
static nonstd::optional<wxGameControl> FromString(const wxString& name);
wxGameControl(int joypad, wxGameKey game_key);
~wxGameControl();
wxString ToString() const { return config_string_; };
bool operator==(const wxGameControl& other) const;
bool operator!=(const wxGameControl& other) const;
bool operator<(const wxGameControl& other) const;
bool operator<=(const wxGameControl& other) const;
bool operator>(const wxGameControl& other) const;
bool operator>=(const wxGameControl& other) const;
private:
const int joypad_;
const wxGameKey game_key_;
const wxString config_string_;
friend class wxGameControlState;
};
// Tracks in-game input and computes the joypad value used to send control input
// data to the emulator.
class wxGameControlState {
public:
// This is a global singleton.
static wxGameControlState& Instance();
// Disable copy constructor and assignment operator.
wxGameControlState(const wxGameControlState&) = delete;
wxGameControlState& operator=(const wxGameControlState&) = delete;
// Processes `user_input` and updates the internal tracking state.
// Returns true if `user_input` corresponds to a game input.
bool OnInputPressed(const config::UserInput& user_input);
bool OnInputReleased(const config::UserInput& user_input);
// Clears all input.
void Reset();
// Recomputes internal bindinds. This is a potentially slow operation and
// should only be called when the game input configuration has been changed.
void OnGameBindingsChanged();
uint32_t GetJoypad(int joypad) const;
private:
wxGameControlState();
~wxGameControlState();
std::map<config::UserInput, std::set<wxGameControl>> input_bindings_;
std::map<wxGameControl, std::set<config::UserInput>> active_controls_;
std::set<config::UserInput> keys_pressed_;
std::array<uint32_t, kNbJoypads> joypads_;
};
#endif // _WX_GAME_CONTROL_H_

View File

@ -5,7 +5,7 @@
// The value is the symbolic name of the key pressed
// Supports manual clearing (bs), multiple keys in widget, automatic tab on key
#include "wx/gamecontrol.h"
#include "config/game-control.h"
#include "wx/keyedit.h"
#include "wx/sdljoy.h"
@ -41,7 +41,7 @@ protected:
// A simple copy-only validator
class wxJoyKeyValidator : public wxValidator {
public:
wxJoyKeyValidator(const wxGameControl v)
wxJoyKeyValidator(const config::GameControl v)
: wxValidator()
, val_(v)
{
@ -64,7 +64,7 @@ public:
}
protected:
const wxGameControl val_;
const config::GameControl val_;
DECLARE_CLASS(wxJoyKeyValidator)
};

View File

@ -32,9 +32,9 @@
#include "builtin-over.h"
#include "config/user-input.h"
#include "strutils.h"
#include "vbam-options.h"
#include "config/game-control.h"
#include "config/option.h"
#include "wayland.h"
#include "wx/gamecontrol.h"
#ifdef __WXMSW__
@ -465,7 +465,7 @@ bool wxvbamApp::OnInit() {
// Initialize game bindings here, after defaults bindings, vbam.ini bindings
// and command line overrides have been applied.
wxGameControlState::Instance().OnGameBindingsChanged();
config::GameControlState::Instance().OnGameBindingsChanged();
// create the main window
int x = windowPositionX;
@ -669,7 +669,7 @@ bool wxvbamApp::OnCmdLineParsed(wxCmdLineParser& cl)
" configuration changes are made in the user interface.\n\n"
"For flag options, true and false are specified as 1 and 0, respectively.\n\n"));
for (const VbamOption& opt : VbamOption::AllOptions()) {
for (const config::Option& opt : config::Option::AllOptions()) {
wxPrintf("%s\n", opt.ToHelperString());
}