make speedup/turbo configurable + misc #339

Add Speedup / Turbo configuration panel which allows setting the
throttle or number of frames to skip for when the speed key is pressed
or turbo is enabled (which just presses the speed key.)

Throttle and frame-skip are mutually exclusive, throttle must be 0 (no
throttle) when number of frames to skip is non-zero. The dialog controls
handle this.

This is implemented in the core in GBA.cpp, GB.cpp and ConfigManager.

Two new options are added both in ConfigManager and in the wx options,
speedup_throttle and speedup_frame_skip, the defaults are:

```
speedup_throttle   = 0 (no throttle)
speedup_frame_skip = 9
```

this was the original behavior.

Add support for unsigned ints to wx/opts.cpp for these and for throttle,
this requires a new validator wxUIntValidator to use them in spin
controls.

Clean up appearance of the throttle spin control in the General dialog.

Maximum throttle and speedup_throttle is 600, values much over 500 will
not behave differently from 0 on modern hardware.

Maximum frame skip is 30 at the moment.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
Rafael Kitover 2019-02-02 17:50:23 -08:00
parent 5379708fcc
commit 16dd5d4068
No known key found for this signature in database
GPG Key ID: 08AB596679D86240
14 changed files with 527 additions and 70 deletions

View File

@ -104,8 +104,9 @@ enum named_opts
OPT_THREAD_PRIORITY, OPT_THREAD_PRIORITY,
OPT_VIDEO_OPTION, OPT_VIDEO_OPTION,
OPT_WINDOW_POSITION_X, OPT_WINDOW_POSITION_X,
OPT_WINDOW_POSITION_Y OPT_WINDOW_POSITION_Y,
OPT_SPEEDUP_THROTTLE,
OPT_SPEEDUP_FRAME_SKIP
}; };
#define SOUND_MAX_VOLUME 2.0 #define SOUND_MAX_VOLUME 2.0
@ -242,7 +243,9 @@ int* rewindSerials = NULL;
uint32_t autoFrameSkipLastTime; uint32_t autoFrameSkipLastTime;
uint32_t movieLastJoypad; uint32_t movieLastJoypad;
uint32_t movieNextJoypad; uint32_t movieNextJoypad;
int throttle; uint32_t throttle = 100;
uint32_t speedup_throttle = 0;
uint32_t speedup_frame_skip = 9;
const char* preparedCheatCodes[MAX_CHEATS]; const char* preparedCheatCodes[MAX_CHEATS];
@ -374,6 +377,8 @@ struct option argOptions[] = {
{ "synchronize", required_argument, 0, OPT_SYNCHRONIZE }, { "synchronize", required_argument, 0, OPT_SYNCHRONIZE },
{ "thread-priority", required_argument, 0, OPT_THREAD_PRIORITY }, { "thread-priority", required_argument, 0, OPT_THREAD_PRIORITY },
{ "throttle", required_argument, 0, 'T' }, { "throttle", required_argument, 0, 'T' },
{ "speedup_throttle", required_argument, 0, OPT_SPEEDUP_THROTTLE },
{ "speedup_frame_skip", required_argument, 0, OPT_SPEEDUP_FRAME_SKIP },
{ "triple-buffering", no_argument, &tripleBuffering, 1 }, { "triple-buffering", no_argument, &tripleBuffering, 1 },
{ "use-bios", no_argument, &useBios, 1 }, { "use-bios", no_argument, &useBios, 1 },
{ "use-bios-file-gb", no_argument, &useBiosFileGB, 1 }, { "use-bios-file-gb", no_argument, &useBiosFileGB, 1 },
@ -527,6 +532,8 @@ void LoadConfig()
soundRecordDir = ReadPrefString("soundRecordDir"); soundRecordDir = ReadPrefString("soundRecordDir");
threadPriority = ReadPref("priority", 2); threadPriority = ReadPref("priority", 2);
throttle = ReadPref("throttle", 100); throttle = ReadPref("throttle", 100);
speedup_throttle = ReadPref("speedup_throttle", 0);
speedup_frame_skip = ReadPref("speedup_frame_skip", 9);
tripleBuffering = ReadPref("tripleBuffering", 0); tripleBuffering = ReadPref("tripleBuffering", 0);
useBios = ReadPrefHex("useBiosGBA"); useBios = ReadPrefHex("useBiosGBA");
useBiosFileGB = ReadPref("useBiosGB", 0); useBiosFileGB = ReadPref("useBiosGB", 0);
@ -971,6 +978,10 @@ int ReadOpts(int argc, char ** argv)
filter = kStretch2x; filter = kStretch2x;
} }
break; break;
case 'T':
if (optarg)
throttle = atoi(optarg);
break;
case 'I': case 'I':
if (optarg) { if (optarg) {
ifbType = (IFBFilter)atoi(optarg); ifbType = (IFBFilter)atoi(optarg);
@ -1339,6 +1350,14 @@ int ReadOpts(int argc, char ** argv)
// --dotcode-file-name-save // --dotcode-file-name-save
saveDotCodeFile = optarg; saveDotCodeFile = optarg;
break; break;
case OPT_SPEEDUP_THROTTLE:
if (optarg)
speedup_throttle = atoi(optarg);
break;
case OPT_SPEEDUP_FRAME_SKIP:
if (optarg)
speedup_frame_skip = atoi(optarg);
break;
} }
} }
return op; return op;

View File

@ -141,7 +141,9 @@ extern int winPauseNextFrame;
extern uint32_t autoFrameSkipLastTime; extern uint32_t autoFrameSkipLastTime;
extern uint32_t movieLastJoypad; extern uint32_t movieLastJoypad;
extern uint32_t movieNextJoypad; extern uint32_t movieNextJoypad;
extern int throttle; extern uint32_t throttle;
extern uint32_t speedup_throttle;
extern uint32_t speedup_frame_skip;
extern int preparedCheats; extern int preparedCheats;
extern const char *preparedCheatCodes[MAX_CHEATS]; extern const char *preparedCheatCodes[MAX_CHEATS];

View File

@ -10,6 +10,7 @@
#include "../Util.h" #include "../Util.h"
#include "../common/ConfigManager.h" #include "../common/ConfigManager.h"
#include "../gba/GBALink.h" #include "../gba/GBALink.h"
#include "../gba/Sound.h"
#include "gb.h" #include "gb.h"
#include "gbCheats.h" #include "gbCheats.h"
#include "gbGlobals.h" #include "gbGlobals.h"
@ -4842,8 +4843,32 @@ void gbEmulate(int ticksToStop)
if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) {
int framesToSkip = systemFrameSkip; int framesToSkip = systemFrameSkip;
if (speedup) static bool speedup_throttle_set = false;
framesToSkip = 9; // try 6 FPS during speedup static uint32_t last_throttle;
if ((gbJoymask[0] >> 10) & 1) {
if (speedup_throttle != 0) {
if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle;
throttle = speedup_throttle;
soundSetThrottle(speedup_throttle);
speedup_throttle_set = true;
}
}
else {
if (speedup_frame_skip)
framesToSkip = speedup_frame_skip;
speedup_throttle_set = false;
}
}
else if (speedup_throttle_set) {
throttle = last_throttle;
soundSetThrottle(last_throttle);
speedup_throttle_set = false;
}
//gbLcdTicksDelayed = gbLcdTicks+1; //gbLcdTicksDelayed = gbLcdTicks+1;
gbLCDChangeHappened = false; gbLCDChangeHappened = false;
switch (gbLcdModeDelayed) { switch (gbLcdModeDelayed) {
@ -4919,7 +4944,11 @@ void gbEmulate(int ticksToStop)
newmask = (gbJoymask[0] >> 10); newmask = (gbJoymask[0] >> 10);
speedup = (newmask & 1) ? true : false; speedup = false;
if (newmask & 1 && speedup_throttle == 0)
speedup = true;
gbCapture = (newmask & 2) ? true : false; gbCapture = (newmask & 2) ? true : false;
if (gbCapture && !gbCapturePrevious) { if (gbCapture && !gbCapturePrevious) {

View File

@ -3756,8 +3756,31 @@ void CPULoop(int ticks)
} }
} else { } else {
int framesToSkip = systemFrameSkip; int framesToSkip = systemFrameSkip;
if (speedup) static bool speedup_throttle_set = false;
framesToSkip = 9; // try 6 FPS during speedup static uint32_t last_throttle;
if ((joy >> 10) & 1) {
if (speedup_throttle != 0) {
if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle;
throttle = speedup_throttle;
soundSetThrottle(speedup_throttle);
speedup_throttle_set = true;
}
}
else {
if (speedup_frame_skip)
framesToSkip = speedup_frame_skip;
speedup_throttle_set = false;
}
}
else if (speedup_throttle_set) {
throttle = last_throttle;
soundSetThrottle(last_throttle);
speedup_throttle_set = false;
}
if (DISPSTAT & 2) { if (DISPSTAT & 2) {
// if in H-Blank, leave it and move to drawing mode // if in H-Blank, leave it and move to drawing mode
@ -3788,7 +3811,12 @@ void CPULoop(int ticks)
// If no (m) code is enabled, apply the cheats at each LCDline // If no (m) code is enabled, apply the cheats at each LCDline
if ((cheatsEnabled) && (mastercode == 0)) if ((cheatsEnabled) && (mastercode == 0))
remainingTicks += cheatsCheckKeys(P1 ^ 0x3FF, ext); remainingTicks += cheatsCheckKeys(P1 ^ 0x3FF, ext);
speedup = (ext & 1) ? true : false;
speedup = false;
if (ext & 1 && speedup_throttle == 0)
speedup = true;
capture = (ext & 2) ? true : false; capture = (ext & 2) ? true : false;
if (capture && !capturePrevious) { if (capture && !capturePrevious) {

View File

@ -432,6 +432,7 @@ SET(XRC_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/xrc/PaletteViewer.xrc ${CMAKE_CURRENT_SOURCE_DIR}/xrc/PaletteViewer.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/SoundConfig.xrc ${CMAKE_CURRENT_SOURCE_DIR}/xrc/SoundConfig.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/TileViewer.xrc ${CMAKE_CURRENT_SOURCE_DIR}/xrc/TileViewer.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/SpeedupConfig.xrc
) )
# wxrc does not support xrs files in -c output (> 10x compression) # wxrc does not support xrs files in -c output (> 10x compression)

View File

@ -2041,6 +2041,14 @@ EVT_HANDLER(GeneralConfigure, "General options...")
} }
} }
EVT_HANDLER(SpeedupConfigure, "Speedup / Turbo options...")
{
wxDialog* dlg = GetXRCDialog("SpeedupConfig");
if (ShowModal(dlg) == wxID_OK)
update_opts();
}
EVT_HANDLER(GameBoyConfigure, "Game Boy options...") EVT_HANDLER(GameBoyConfigure, "Game Boy options...")
{ {
wxDialog* dlg = GetXRCDialog("GameBoyConfig"); wxDialog* dlg = GetXRCDialog("GameBoyConfig");

View File

@ -17,6 +17,7 @@
#include <wx/filepicker.h> #include <wx/filepicker.h>
#include <wx/progdlg.h> #include <wx/progdlg.h>
#include <wx/spinctrl.h> #include <wx/spinctrl.h>
#include <wx/valnum.h>
#include <wx/stockitem.h> #include <wx/stockitem.h>
#include <wx/tokenzr.h> #include <wx/tokenzr.h>
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
@ -2221,70 +2222,23 @@ public:
DoSetThrottleSel(thr->GetValue()); DoSetThrottleSel(thr->GetValue());
} }
void DoSetThrottleSel(int val) void DoSetThrottleSel(uint32_t val)
{ {
switch (val) { if (val <= 600)
case 0: thrsel->SetSelection(std::round((double)val / 25));
thrsel->SetSelection(1); else
break; thrsel->SetSelection(100 / 25);
case 25:
thrsel->SetSelection(2);
break;
case 50:
thrsel->SetSelection(3);
break;
case 100:
thrsel->SetSelection(4);
break;
case 150:
thrsel->SetSelection(5);
break;
case 200:
thrsel->SetSelection(6);
break;
default:
thrsel->SetSelection(0);
break;
}
} }
// set thr from thrsel // set thr from thrsel
void SetThrottle(wxCommandEvent& evt) void SetThrottle(wxCommandEvent& evt)
{ {
switch (thrsel->GetSelection()) { uint32_t val = thrsel->GetSelection() * 25;
case 0: // blank; leave it alone
break;
case 1: if (val <= 600)
thr->SetValue(0); thr->SetValue(val);
break; else
case 2:
thr->SetValue(25);
break;
case 3:
thr->SetValue(50);
break;
case 4:
thr->SetValue(100); thr->SetValue(100);
break;
case 5:
thr->SetValue(150);
break;
case 6:
thr->SetValue(200);
break;
}
} }
// since this is not the actual dialog, derived from wxDialog, which is // since this is not the actual dialog, derived from wxDialog, which is
@ -2298,6 +2252,102 @@ public:
} }
} throttle_ctrl; } throttle_ctrl;
// manage speedup key frame skip spinctrl/canned setting choice interaction
static class SpeedupFrameSkipCtrl_t : public wxEvtHandler {
public:
wxSpinCtrl* speedup_frame_skip_spin;
wxChoice* speedup_frame_skip_sel;
void SetSpeedupFrameSkipSel(wxSpinEvent& evt);
void DoSetSpeedupFrameSkipSel(uint32_t val);
void SetSpeedupFrameSkip(wxCommandEvent& evt);
void Init(wxShowEvent& ev);
} speedup_frame_skip_ctrl;
// manage speedup key throttle spinctrl/canned setting choice interaction
static class SpeedupThrottleCtrl_t : public wxEvtHandler {
public:
wxSpinCtrl* speedup_throttle_spin;
wxChoice* speedup_throttle_sel;
// set speedup_throttle_sel from speedup_throttle
void SetSpeedupThrottleSel(wxSpinEvent& evt)
{
DoSetSpeedupThrottleSel(speedup_throttle_spin->GetValue());
}
void DoSetSpeedupThrottleSel(uint32_t val)
{
if (val > 0 && val <= 600) {
speedup_throttle_sel->SetSelection(std::round((double)val / 25));
speedup_frame_skip_ctrl.DoSetSpeedupFrameSkipSel(0);
wxCommandEvent nil;
speedup_frame_skip_ctrl.SetSpeedupFrameSkip(nil);
}
else
speedup_throttle_sel->SetSelection(0);
}
// set speedup_throttle from speedup_throttle_sel
void SetSpeedupThrottle(wxCommandEvent& evt)
{
uint32_t val = speedup_throttle_sel->GetSelection() * 25;
if (val > 0 && val <= 600) {
speedup_throttle_spin->SetValue(val);
speedup_frame_skip_ctrl.DoSetSpeedupFrameSkipSel(0);
wxCommandEvent nil;
speedup_frame_skip_ctrl.SetSpeedupFrameSkip(nil);
}
else
speedup_throttle_spin->SetValue(0);
}
void Init(wxShowEvent& ev)
{
ev.Skip();
DoSetSpeedupThrottleSel(speedup_throttle);
}
} speedup_throttle_ctrl;
// set speedup_frame_skip_sel from speedup_frame_skip
void SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkipSel(wxSpinEvent& evt)
{
DoSetSpeedupFrameSkipSel(speedup_frame_skip_spin->GetValue());
}
void SpeedupFrameSkipCtrl_t::DoSetSpeedupFrameSkipSel(uint32_t val)
{
if (val > 0 && val <= 30) {
speedup_frame_skip_sel->SetSelection(val);
speedup_throttle_ctrl.DoSetSpeedupThrottleSel(0);
wxCommandEvent nil;
speedup_throttle_ctrl.SetSpeedupThrottle(nil);
}
else
speedup_frame_skip_sel->SetSelection(0);
}
// set speedup_frame_skip from speedup_frame_skip_sel
void SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkip(wxCommandEvent& evt)
{
uint32_t val = speedup_frame_skip_sel->GetSelection();
if (val > 0 && val <= 30) {
speedup_frame_skip_spin->SetValue(val);
speedup_throttle_ctrl.DoSetSpeedupThrottleSel(0);
wxCommandEvent nil;
speedup_throttle_ctrl.SetSpeedupThrottle(nil);
}
else
speedup_frame_skip_spin->SetValue(0);
}
void SpeedupFrameSkipCtrl_t::Init(wxShowEvent& ev)
{
ev.Skip();
DoSetSpeedupFrameSkipSel(speedup_frame_skip);
}
///////////////////////////// /////////////////////////////
//Check if a pointer from the XRC file is valid. If it's not, throw an error telling the user. //Check if a pointer from the XRC file is valid. If it's not, throw an error telling the user.
template <typename T> template <typename T>
@ -3207,6 +3257,11 @@ bool MainFrame::BindControls()
sc = SafeXRCCTRL<wxSpinCtrl>(d, n); \ sc = SafeXRCCTRL<wxSpinCtrl>(d, n); \
sc->SetValidator(wxGenericValidator(&o)); \ sc->SetValidator(wxGenericValidator(&o)); \
} while (0) } while (0)
#define getsc_uint(n, o) \
do { \
sc = SafeXRCCTRL<wxSpinCtrl>(d, n); \
sc->SetValidator(wxUIntValidator(&o)); \
} while (0)
{ {
// Online Auto Update check frequency // Online Auto Update check frequency
getrbi("UpdateNever", gopts.onlineupdates, 0); getrbi("UpdateNever", gopts.onlineupdates, 0);
@ -3215,7 +3270,7 @@ bool MainFrame::BindControls()
getrbi("PNG", captureFormat, 0); getrbi("PNG", captureFormat, 0);
getrbi("BMP", captureFormat, 1); getrbi("BMP", captureFormat, 1);
getsc("RewindInterval", gopts.rewind_interval); getsc("RewindInterval", gopts.rewind_interval);
getsc("Throttle", throttle); getsc_uint("Throttle", throttle);
throttle_ctrl.thr = sc; throttle_ctrl.thr = sc;
throttle_ctrl.thrsel = SafeXRCCTRL<wxChoice>(d, "ThrottleSel"); throttle_ctrl.thrsel = SafeXRCCTRL<wxChoice>(d, "ThrottleSel");
throttle_ctrl.thr->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, throttle_ctrl.thr->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
@ -3228,6 +3283,35 @@ bool MainFrame::BindControls()
NULL, &throttle_ctrl); NULL, &throttle_ctrl);
d->Fit(); d->Fit();
} }
// SpeedUp Key Config
d = LoadXRCDialog("SpeedupConfig");
{
getsc_uint("SpeedupThrottle", speedup_throttle);
speedup_throttle_ctrl.speedup_throttle_spin = sc;
speedup_throttle_ctrl.speedup_throttle_sel = SafeXRCCTRL<wxChoice>(d, "SpeedupThrottleSel");
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottleSel),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.speedup_throttle_sel->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
d->Connect(wxEVT_SHOW, wxShowEventHandler(SpeedupThrottleCtrl_t::Init),
NULL, &speedup_throttle_ctrl);
d->Fit();
getsc_uint("SpeedupFrameSkip", speedup_frame_skip);
speedup_frame_skip_ctrl.speedup_frame_skip_spin = sc;
speedup_frame_skip_ctrl.speedup_frame_skip_sel = SafeXRCCTRL<wxChoice>(d, "SpeedupFrameSkipSel");
speedup_frame_skip_ctrl.speedup_frame_skip_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkipSel),
NULL, &speedup_frame_skip_ctrl);
speedup_frame_skip_ctrl.speedup_frame_skip_sel->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkip),
NULL, &speedup_frame_skip_ctrl);
d->Connect(wxEVT_SHOW, wxShowEventHandler(SpeedupFrameSkipCtrl_t::Init),
NULL, &speedup_frame_skip_ctrl);
d->Fit();
}
#define getcbbe(n, o) getbe(n, o, cb, wxCheckBox, CB) #define getcbbe(n, o) getbe(n, o, cb, wxCheckBox, CB)
wxBoolIntEnValidator* bienval; wxBoolIntEnValidator* bienval;
#define getbie(n, o, v, cv, t, wt) \ #define getbie(n, o, v, cv, t, wt) \

View File

@ -26,6 +26,10 @@
{ \ { \
wxT(c), (n), d, NULL, NULL, wxT(""), min, max, NULL, &v \ wxT(c), (n), d, NULL, NULL, wxT(""), min, max, NULL, &v \
} }
#define UINTOPT(c, n, d, v, min, max) \
{ \
wxT(c), (n), d, NULL, NULL, wxT(""), min, max, NULL, NULL, &v \
}
#define BOOLOPT(c, n, d, v) \ #define BOOLOPT(c, n, d, v) \
{ \ { \
wxT(c), (n), d, NULL, NULL, wxT(""), 0, 0, &v \ wxT(c), (n), d, NULL, NULL, wxT(""), 0, 0, &v \
@ -252,7 +256,9 @@ opt_desc opts[] = {
INTOPT("preferences/skipBios", "SkipIntro", wxTRANSLATE("Skip BIOS initialization"), skipBios, 0, 1), INTOPT("preferences/skipBios", "SkipIntro", wxTRANSLATE("Skip BIOS initialization"), skipBios, 0, 1),
INTOPT("preferences/skipSaveGameCheats", "", wxTRANSLATE("Do not overwrite cheat list when loading state"), skipSaveGameCheats, 0, 1), INTOPT("preferences/skipSaveGameCheats", "", wxTRANSLATE("Do not overwrite cheat list when loading state"), skipSaveGameCheats, 0, 1),
INTOPT("preferences/skipSaveGameBattery", "", wxTRANSLATE("Do not overwrite native (battery) save when loading state"), skipSaveGameBattery, 0, 1), INTOPT("preferences/skipSaveGameBattery", "", wxTRANSLATE("Do not overwrite native (battery) save when loading state"), skipSaveGameBattery, 0, 1),
INTOPT("preferences/throttle", "", wxTRANSLATE("Throttle game speed, even when accelerated (0-1000%, 0 = disabled)"), throttle, 0, 1000), UINTOPT("preferences/throttle", "", wxTRANSLATE("Throttle game speed, even when accelerated (0-500%, 0 = no throttle)"), throttle, 0, 600),
UINTOPT("preferences/speedupThrottle", "", wxTRANSLATE("Set throttle for speedup key (0-600%, 0 = no throttle)"), speedup_throttle, 0, 600),
UINTOPT("preferences/speedupFrameSkip", "", wxTRANSLATE("Set frame skip for speedup key (0-30)"), speedup_frame_skip, 0, 30),
INTOPT("preferences/useBiosGB", "BootRomGB", wxTRANSLATE("Use the specified BIOS file for GB"), useBiosFileGB, 0, 1), INTOPT("preferences/useBiosGB", "BootRomGB", wxTRANSLATE("Use the specified BIOS file for GB"), useBiosFileGB, 0, 1),
INTOPT("preferences/useBiosGBA", "BootRomEn", wxTRANSLATE("Use the specified BIOS file"), useBiosFileGBA, 0, 1), INTOPT("preferences/useBiosGBA", "BootRomEn", wxTRANSLATE("Use the specified BIOS file"), useBiosFileGBA, 0, 1),
INTOPT("preferences/useBiosGBC", "BootRomGBC", wxTRANSLATE("Use the specified BIOS file for GBC"), useBiosFileGBC, 0, 1), INTOPT("preferences/useBiosGBC", "BootRomGBC", wxTRANSLATE("Use the specified BIOS file for GBC"), useBiosFileGBC, 0, 1),
@ -525,6 +531,15 @@ void load_opts()
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curdouble, opt.opt, opt.min, opt.max); wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curdouble, opt.opt, opt.min, opt.max);
} else } else
*opt.doubleopt = opt.curdouble; *opt.doubleopt = opt.curdouble;
} else if (opt.uintopt) {
int val;
cfg->Read(opt.opt, &val, *opt.uintopt);
opt.curuint = val;
if (opt.curuint < opt.min || opt.curuint > opt.max) {
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curuint, opt.opt, opt.min, opt.max);
} else
*opt.uintopt = opt.curuint;
} else if (opt.boolopt) { } else if (opt.boolopt) {
cfg->Read(opt.opt, opt.boolopt, *opt.boolopt); cfg->Read(opt.opt, opt.boolopt, *opt.boolopt);
opt.curbool = *opt.boolopt; opt.curbool = *opt.boolopt;
@ -648,6 +663,9 @@ void update_opts()
} else if (opt.doubleopt) { } else if (opt.doubleopt) {
if (*opt.doubleopt != opt.curdouble) if (*opt.doubleopt != opt.curdouble)
cfg->Write(opt.opt, (opt.curdouble = *opt.doubleopt)); cfg->Write(opt.opt, (opt.curdouble = *opt.doubleopt));
} else if (opt.uintopt) {
if (*opt.uintopt != opt.curuint)
cfg->Write(opt.opt, (opt.curuint = *opt.uintopt));
} else if (opt.boolopt) { } else if (opt.boolopt) {
if (*opt.boolopt != opt.curbool) if (*opt.boolopt != opt.curbool)
cfg->Write(opt.opt, (opt.curbool = *opt.boolopt)); cfg->Write(opt.opt, (opt.curbool = *opt.boolopt));
@ -802,6 +820,14 @@ bool opt_set(const wxString& name, const wxString& val)
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), dval, name, opt->min, opt->max); wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), dval, name, opt->min, opt->max);
else else
*opt->doubleopt = dval; *opt->doubleopt = dval;
} else if (opt->uintopt) {
const wxString s(val);
unsigned long uival;
if (!s.ToULong(&uival) || uival < opt->min || uival > opt->max)
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), uival, name, opt->min, opt->max);
else
*opt->uintopt = (uint32_t)uival;
} else { } else {
// GB/Palette[0-2] is virtual // GB/Palette[0-2] is virtual
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {

View File

@ -93,10 +93,12 @@ extern struct opt_desc {
double min, max; double min, max;
bool* boolopt; bool* boolopt;
double* doubleopt; double* doubleopt;
uint32_t* uintopt;
// current configured value // current configured value
wxString curstr; wxString curstr;
int curint; int curint;
double curdouble; double curdouble;
uint32_t curuint;
#define curbool curint #define curbool curint
} opts[]; } opts[];
extern const int num_opts; extern const int num_opts;

View File

@ -72,6 +72,17 @@ protected:
wxString str_val; wxString str_val;
}; };
class wxUIntValidator : public wxValidator {
public:
wxUIntValidator(uint32_t* _val);
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow* parent);
wxObject* Clone() const;
protected:
uint32_t* uint_val;
};
// boolean copy-only validator with reversed value // boolean copy-only validator with reversed value
// may be attached to radio button or checkbox // may be attached to radio button or checkbox
class wxBoolRevValidator : public wxValidator { class wxBoolRevValidator : public wxValidator {

View File

@ -1,6 +1,7 @@
// utility widgets // utility widgets
#include "wx/wxmisc.h" #include "wx/wxmisc.h"
#include <wx/wx.h> #include <wx/wx.h>
#include <wx/spinctrl.h>
wxFarRadio::wxFarRadio() wxFarRadio::wxFarRadio()
: wxCheckBox() : wxCheckBox()
@ -429,3 +430,50 @@ wxObject* wxPositiveDoubleValidator::Clone() const
{ {
return new wxPositiveDoubleValidator(double_val); return new wxPositiveDoubleValidator(double_val);
} }
wxUIntValidator::wxUIntValidator(uint32_t* _val)
: uint_val(_val)
{
if (uint_val)
TransferToWindow();
}
bool wxUIntValidator::TransferToWindow()
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl && uint_val) {
ctrl->SetValue(*uint_val);
return true;
}
return false;
}
bool wxUIntValidator::TransferFromWindow()
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl && uint_val) {
*uint_val = ctrl->GetValue();
return true;
}
return false;
}
bool wxUIntValidator::Validate(wxWindow* parent)
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl) {
if (ctrl->GetValue() >= 0) {
return true;
}
return false;
}
return false;
}
wxObject* wxUIntValidator::Clone() const
{
return new wxUIntValidator(uint_val);
}

View File

@ -149,14 +149,33 @@
<object class="sizeritem"> <object class="sizeritem">
<object class="wxChoice" name="ThrottleSel"> <object class="wxChoice" name="ThrottleSel">
<content> <content>
<item/> <item>None</item>
<item>No throttle</item>
<item>25%</item> <item>25%</item>
<item>50%</item> <item>50%</item>
<item>75%</item>
<item>100%</item> <item>100%</item>
<item>125%</item>
<item>150%</item> <item>150%</item>
<item>175%</item>
<item>200%</item> <item>200%</item>
<item>225%</item>
<item>250%</item>
<item>275%</item>
<item>300%</item>
<item>325%</item>
<item>350%</item>
<item>375%</item>
<item>400%</item>
<item>425%</item>
<item>450%</item>
<item>475%</item>
<item>500%</item>
<item>525%</item>
<item>550%</item>
<item>575%</item>
<item>600%</item>
</content> </content>
<size>40,-1d</size>
</object> </object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag> <flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border> <border>5</border>

View File

@ -457,6 +457,9 @@
<object class="wxMenuItem" name="GeneralConfigure"> <object class="wxMenuItem" name="GeneralConfigure">
<label>_General ...</label> <label>_General ...</label>
</object> </object>
<object class="wxMenuItem" name="SpeedupConfigure">
<label>_Speedup / Turbo ...</label>
</object>
<object class="wxMenuItem" name="EmulatorDirectories"> <object class="wxMenuItem" name="EmulatorDirectories">
<label>D_irectories ...</label> <label>D_irectories ...</label>
</object> </object>

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.5.3.0">
<object class="wxDialog" name="SpeedupConfig">
<title>SpeedUp / Turbo Settings</title>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxStaticText">
<label>Speedup Throttle</label>
<font>
<weight>bold</weight>
</font>
</object>
<flag>wxALL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxALL|wxEXPAND</flag>
<border>5</border>
<object class="wxBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<object class="wxStaticText">
<label>% of normal:</label>
<size>60,-1d</size>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxSpinCtrl" name="SpeedupThrottle">
<value>0</value>
<min>0</min>
<max>600</max>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxChoice" name="SpeedupThrottleSel">
<content>
<item>None</item>
<item>25%</item>
<item>50%</item>
<item>75%</item>
<item>100%</item>
<item>125%</item>
<item>150%</item>
<item>175%</item>
<item>200%</item>
<item>225%</item>
<item>250%</item>
<item>275%</item>
<item>300%</item>
<item>325%</item>
<item>350%</item>
<item>375%</item>
<item>400%</item>
<item>425%</item>
<item>450%</item>
<item>475%</item>
<item>500%</item>
<item>525%</item>
<item>550%</item>
<item>575%</item>
<item>600%</item>
</content>
<size>40,-1d</size>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
</object>
</object>
</object>
<flag>wxALL|wxEXPAND</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxStaticText">
<label>Speedup Frame Skip</label>
<font>
<weight>bold</weight>
</font>
</object>
<flag>wxALL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxALL|wxEXPAND</flag>
<border>5</border>
<object class="wxBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<object class="wxStaticText">
<label># of frames:</label>
<size>60,-1d</size>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxSpinCtrl" name="SpeedupFrameSkip">
<value>9</value>
<min>0</min>
<max>30</max>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxChoice" name="SpeedupFrameSkipSel">
<content>
<item>None</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
<item>20</item>
<item>21</item>
<item>22</item>
<item>23</item>
<item>24</item>
<item>25</item>
<item>26</item>
<item>27</item>
<item>28</item>
<item>29</item>
<item>30</item>
</content>
<size>40,-1d</size>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
<border>5</border>
</object>
</object>
</object>
</object>
<flag>wxALL|wxEXPAND</flag>
<border>5</border>
</object>
<object class="sizeritem">
<flag>wxALL|wxEXPAND</flag>
<border>5</border>
<object class="wxStdDialogButtonSizer">
<object class="button">
<object class="wxButton" name="wxID_OK"/>
</object>
<object class="button">
<object class="wxButton" name="wxID_CANCEL"/>
</object>
</object>
</object>
</object>
</object>
</resource>