Merge branch 'master' of github.com:TASEmulators/fceux

This commit is contained in:
Alexey 'Cluster' Avdyukhin 2023-04-28 15:02:22 +04:00
commit d72d9daebb
35 changed files with 1574 additions and 436 deletions

View File

@ -61,6 +61,13 @@ echo '****************************************'
sudo apt-get --assume-yes install libminizip-dev
pkg-config --cflags --libs minizip
# Install libarchive-dev
echo '****************************************'
echo 'Install Dependency libarchive-dev'
echo '****************************************'
sudo apt-get --assume-yes install libarchive-dev
pkg-config --cflags --libs libarchive
# GTK+-2 is no longer needed
#sudo apt-get install libgtk2.0-dev

View File

@ -70,6 +70,12 @@ echo 'Install Dependency minizip'
echo '****************************************'
brew install minizip
echo '****************************************'
echo 'Install Optional Dependency libarchive'
echo '****************************************'
brew install libarchive
LIBARCHIVE_PATH=`brew --prefix libarchive`;
echo '****************************************'
echo 'Install Optional Dependency x264'
echo '****************************************'
@ -87,7 +93,7 @@ brew install ffmpeg
#brew install zlib # Already installed in appveyor macOS
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:$LIBARCHIVE_PATH:
ls -ltr $HOME/Qt;

View File

@ -24,28 +24,33 @@ mkdir bin
set SDL_VERSION=2.24.1
set FFMPEG_VERSION=5.1.2
set LIBARCHIVE_VERSION=3.6.2
curl -s -LO https://github.com/libsdl-org/SDL/releases/download/release-%SDL_VERSION%/SDL2-devel-%SDL_VERSION%-VC.zip
curl -s -LO https://github.com/GyanD/codexffmpeg/releases/download/%FFMPEG_VERSION%/ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip
curl -s -LO https://www.libarchive.org/downloads/libarchive-v%LIBARCHIVE_VERSION%-amd64.zip
REM rmdir /q /s SDL2
powershell -command "Expand-Archive" SDL2-devel-%SDL_VERSION%-VC.zip .
powershell -command "Expand-Archive" ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip
powershell -command "Expand-Archive" libarchive-v%LIBARCHIVE_VERSION%-amd64.zip
rename SDL2-%SDL_VERSION% SDL2
move ffmpeg-%FFMPEG_VERSION%-full_build-shared\ffmpeg-%FFMPEG_VERSION%-full_build-shared ffmpeg
rmdir ffmpeg-%FFMPEG_VERSION%-full_build-shared
del ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip
move libarchive-v%LIBARCHIVE_VERSION%-amd64\libarchive libarchive
set SDL_INSTALL_PREFIX=%CD%
set FFMPEG_INSTALL_PREFIX=%CD%
set LIBARCHIVE_INSTALL_PREFIX=%CD%
set PUBLIC_RELEASE=0
IF DEFINED FCEU_RELEASE_VERSION (set PUBLIC_RELEASE=1)
REM cmake -h
REM cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% ..
cmake -DQT6=0 -DPUBLIC_RELEASE=%PUBLIC_RELEASE% -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" ..
cmake -DQT6=0 -DPUBLIC_RELEASE=%PUBLIC_RELEASE% -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DLIBARCHIVE_INSTALL_PREFIX=%LIBARCHIVE_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" ..
REM nmake
msbuild /m fceux.sln /p:Configuration=Release
@ -53,9 +58,10 @@ if %ERRORLEVEL% NEQ 0 EXIT /B 1
copy src\Release\fceux.exe bin\qfceux.exe
copy %PROJECT_ROOT%\src\auxlib.lua bin\.
REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\.
REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua5.1.dll bin\.
copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\.
copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua5.1.dll bin\.
copy %SDL_INSTALL_PREFIX%\SDL2\lib\x64\SDL2.dll bin\.
copy %LIBARCHIVE_INSTALL_PREFIX%\libarchive\bin\archive.dll bin\.
copy %FFMPEG_INSTALL_PREFIX%\ffmpeg\bin\*.dll bin\.
windeployqt --no-compiler-runtime bin\qfceux.exe

View File

@ -41,12 +41,15 @@ if(WIN32)
add_definitions( -DMSVC -D_CRT_SECURE_NO_WARNINGS )
add_definitions( -D__SDL__ -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS )
add_definitions( -DFCEUDEF_DEBUGGER )
add_definitions( -D_USE_LIBARCHIVE )
add_definitions( /wd4267 /wd4244 )
#add_definitions( /wd4018 ) # Integer comparison sign mismatch warnings
include_directories( ${SDL_INSTALL_PREFIX}/SDL2/include )
include_directories( ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/include )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib )
set( OPENGL_LDFLAGS OpenGL::GL )
set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib )
set( LIBARCHIVE_LDFLAGS ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/lib/archive.lib )
set( SYS_LIBS wsock32 ws2_32 vfw32 Htmlhelp )
set(APP_ICON_RESOURCES_WINDOWS ${CMAKE_SOURCE_DIR}/icons/fceux.rc )
@ -110,6 +113,13 @@ else(WIN32)
add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} )
endif()
pkg_check_modules( LIBARCHIVE libarchive)
if ( ${LIBARCHIVE_FOUND} )
message( STATUS "Using System Libarchive Library ${LIBARCHIVE_VERSION}" )
add_definitions( -D_USE_LIBARCHIVE ${LIBARCHIVE_CFLAGS} )
endif()
pkg_check_modules( X264 x264)
if ( ${X264_FOUND} )
@ -643,7 +653,7 @@ target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS}
${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS}
${SDL2_LDFLAGS}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${LIBARCHIVE_LDFLAGS}
${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS}
${SYS_LIBS}
)

View File

@ -27,6 +27,7 @@ static void (*WSync)(void);
static readfunc defread;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE=0;
static uint8 hasBattery = 0;
static DECLFW(LatchWrite) {
latche = A;
@ -358,19 +359,16 @@ void Mapper217_Init(CartInfo *info) {
}
//------------------ Map 227 ---------------------------
static void M227Sync(void) {
uint32 S = latche & 1;
uint32 p = ((latche >> 2) & 0x1F) + ((latche & 0x100) >> 3);
uint32 L = (latche >> 9) & 1;
// ok, according to nesdev wiki (refrenced to the nesdev dumping thread) there is a CHR write protection bit7.
// however, this bit clearly determined a specific PRG layout for some game but does not meant to have additional
// functionality. as I see from the menu code, it disables the chr writing before run an actual game.
// this fix here makes happy both waixing rpgs and multigame menus at once. can't veryfy it on a hardware
// but if I find some i'll definitly do this.
if ((latche & 0xF000) == 0xF000)
// Only Waixing appear to have battery flag enabled, while multicarts don't.
// Multicarts needs CHR-RAM protect in NROM modes, so only apply CHR-RAM protect
// on non battery-enabled carts.
if (!hasBattery && (latche & 0x80) == 0x80)
/* CHR-RAM write protect hack, needed for some multicarts */
SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0);
else
SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1);
@ -409,6 +407,7 @@ static void M227Sync(void) {
void Mapper227_Init(CartInfo *info) {
Latch_Init(info, M227Sync, NULL, 0x0000, 0x8000, 0xFFFF, 1);
hasBattery = info->battery;
}
//------------------ Map 229 ---------------------------

View File

@ -52,17 +52,17 @@
#include <cctype>
uint16 debugLastAddress = 0; // used by 'T' and 'R' conditions
uint8 debugLastOpcode; // used to evaluate 'W' condition
uint8 debugLastOpcode = 0; // used to evaluate 'W' condition
// Next non-whitespace character in string
char next;
static char next = 0;
int ishex(char c)
static int ishex(char c)
{
return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
void scan(const char** str)
static void scan(const char** str)
{
do
{
@ -71,40 +71,37 @@ void scan(const char** str)
} while (isspace(next));
}
// Frees a condition and all of it's sub conditions
void freeTree(Condition* c)
{
if (c->lhs) freeTree(c->lhs);
if (c->rhs) freeTree(c->rhs);
free(c);
}
// Generic function to handle all infix operators but the last one in the precedence hierarchy. : '(' E ')'
Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**))
static Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**))
{
Condition* t = nextPart(str);
Condition* t1;
Condition* mid;
int op;
if (t == nullptr)
{
return nullptr;
}
while ((op = operators(str)))
{
scan(str);
t1 = nextPart(str);
if (t1 == 0)
if (t1 == nullptr)
{
if(t)
freeTree(t);
delete t;
return 0;
}
mid = (Condition*)FCEU_dmalloc(sizeof(Condition));
if (!mid)
return NULL;
memset(mid, 0, sizeof(Condition));
mid = new Condition();
if (mid == nullptr)
{
delete t;
delete t1;
return nullptr;
}
mid->lhs = t;
mid->rhs = t1;
@ -117,7 +114,7 @@ Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), i
}
// Generic handler for two-character operators
int TwoCharOperator(const char** str, char c1, char c2, int op)
static int TwoCharOperator(const char** str, char c1, char c2, int op)
{
if (next == c1 && **str == c2)
{
@ -131,43 +128,43 @@ int TwoCharOperator(const char** str, char c1, char c2, int op)
}
// Determines if a character is a flag
int isFlag(char c)
static int isFlag(char c)
{
return c == 'N' || c == 'I' || c == 'C' || c == 'V' || c == 'Z' || c == 'B' || c == 'U' || c == 'D';
}
// Determines if a character is a register
int isRegister(char c)
static int isRegister(char c)
{
return c == 'A' || c == 'X' || c == 'Y' || c == 'P' || c == 'S';
}
// Determines if a character is for PC bank
int isPCBank(char c)
static int isPCBank(char c)
{
return c == 'K';
}
// Determines if a character is for Data bank
int isDataBank(char c)
static int isDataBank(char c)
{
return c == 'T';
}
// Determines if a character is for value read
int isValueRead(char c)
static int isValueRead(char c)
{
return c == 'R';
}
// Determines if a character is for value write
int isValueWrite(char c)
static int isValueWrite(char c)
{
return c == 'W';
}
// Reads a hexadecimal number from str
int getNumber(unsigned int* number, const char** str)
static int getNumber(unsigned int* number, const char** str)
{
// char buffer[5];
@ -185,10 +182,10 @@ int getNumber(unsigned int* number, const char** str)
return 1;
}
Condition* Connect(const char** str);
static Condition* Connect(const char** str);
// Handles the following part of the grammar: '(' E ')'
Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar)
static Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar)
{
if (next == openPar)
{
@ -216,7 +213,7 @@ Condition* Parentheses(const char** str, Condition* c, char openPar, char closeP
* Check for primitives
* Flags, Registers, Numbers, Addresses and parentheses
*/
Condition* Primitive(const char** str, Condition* c)
static Condition* Primitive(const char** str, Condition* c)
{
if (isFlag(next)) /* Flags */
{
@ -394,24 +391,22 @@ Condition* Primitive(const char** str, Condition* c)
}
/* Handle * and / operators */
Condition* Term(const char** str)
static Condition* Term(const char** str)
{
Condition* t;
Condition* t1;
Condition* mid;
t = (Condition*)FCEU_dmalloc(sizeof(Condition));
t = new Condition();
if (!t)
if (t == nullptr)
{
return NULL;
}
memset(t, 0, sizeof(Condition));
if (!Primitive(str, t))
{
freeTree(t);
delete t;
return 0;
}
@ -421,22 +416,25 @@ Condition* Term(const char** str)
scan(str);
if (!(t1 = (Condition*)FCEU_dmalloc(sizeof(Condition))))
return NULL;
memset(t1, 0, sizeof(Condition));
if ((t1 = new Condition()) == nullptr)
{
delete t;
return nullptr;
}
if (!Primitive(str, t1))
{
freeTree(t);
freeTree(t1);
delete t;
delete t1;
return 0;
}
if (!(mid = (Condition*)FCEU_dmalloc(sizeof(Condition))))
return NULL;
memset(mid, 0, sizeof(Condition));
if ((mid = new Condition()) == nullptr)
{
delete t;
delete t1;
return nullptr;
}
mid->lhs = t;
mid->rhs = t1;
@ -449,7 +447,7 @@ Condition* Term(const char** str)
}
/* Check for + and - operators */
int SumOperators(const char** str)
static int SumOperators(const char** str)
{
switch (next)
{
@ -460,13 +458,13 @@ int SumOperators(const char** str)
}
/* Handle + and - operators */
Condition* Sum(const char** str)
static Condition* Sum(const char** str)
{
return InfixOperator(str, Term, SumOperators);
}
/* Check for <=, =>, ==, !=, > and < operators */
int CompareOperators(const char** str)
static int CompareOperators(const char** str)
{
int val = TwoCharOperator(str, '=', '=', OP_EQ);
if (val) return val;
@ -490,13 +488,13 @@ int CompareOperators(const char** str)
}
/* Handle <=, =>, ==, !=, > and < operators */
Condition* Compare(const char** str)
static Condition* Compare(const char** str)
{
return InfixOperator(str, Sum, CompareOperators);
}
/* Check for || or && operators */
int ConnectOperators(const char** str)
static int ConnectOperators(const char** str)
{
int val = TwoCharOperator(str, '|', '|', OP_OR);
if(val) return val;
@ -508,7 +506,7 @@ int ConnectOperators(const char** str)
}
/* Handle || and && operators */
Condition* Connect(const char** str)
static Condition* Connect(const char** str)
{
return InfixOperator(str, Compare, ConnectOperators);
}
@ -521,6 +519,10 @@ Condition* generateCondition(const char* str)
scan(&str);
c = Connect(&str);
if (!c || next != 0) return 0;
if (!c || next != 0)
{
if (c) delete c;
return 0;
}
else return c;
}

View File

@ -61,9 +61,28 @@ struct Condition
unsigned int type2;
unsigned int value2;
Condition(void)
{
op = 0;
lhs = rhs = nullptr;
type1 = value1 = 0;
type2 = value2 = 0;
};
~Condition(void)
{
if (lhs)
{
delete lhs;
}
if (rhs)
{
delete rhs;
}
}
};
void freeTree(Condition* c);
Condition* generateCondition(const char* str);
#endif

View File

@ -19,10 +19,15 @@ unsigned int debuggerPageSize = 14;
int vblankScanLines = 0; //Used to calculate scanlines 240-261 (vblank)
int vblankPixel = 0; //Used to calculate the pixels in vblank
int offsetStringToInt(unsigned int type, const char* offsetBuffer)
int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk)
{
int offset = -1;
if (conversionOk)
{
*conversionOk = false;
}
if (sscanf(offsetBuffer,"%7X",(unsigned int *)&offset) == EOF)
{
return -1;
@ -30,14 +35,26 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer)
if (type & BT_P)
{
if (conversionOk)
{
*conversionOk = (offset >= 0) && (offset < 0x4000);
}
return offset & 0x3FFF;
}
else if (type & BT_S)
{
if (conversionOk)
{
*conversionOk = (offset >= 0) && (offset < 0x100);
}
return offset & 0x00FF;
}
else if (type & BT_R)
{
if (conversionOk)
{
*conversionOk = (offset >= 0);
}
return offset;
}
else // BT_C
@ -46,6 +63,10 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer)
if (sym)
{
if (conversionOk)
{
*conversionOk = true;
}
return sym->offset() & 0xFFFF;
}
@ -56,21 +77,26 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer)
type = GameInfo->type;
}
if (type == GIT_NSF) { //NSF Breakpoint keywords
if (strcmp(offsetBuffer,"LOAD") == 0) return (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8));
if (strcmp(offsetBuffer,"INIT") == 0) return (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8));
if (strcmp(offsetBuffer,"PLAY") == 0) return (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8));
if (strcmp(offsetBuffer,"LOAD") == 0) offset = (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8));
else if (strcmp(offsetBuffer,"INIT") == 0) offset = (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8));
else if (strcmp(offsetBuffer,"PLAY") == 0) offset = (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8));
}
else if (type == GIT_FDS) { //FDS Breakpoint keywords
if (strcmp(offsetBuffer,"NMI1") == 0) return (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8));
if (strcmp(offsetBuffer,"NMI2") == 0) return (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8));
if (strcmp(offsetBuffer,"NMI3") == 0) return (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8));
if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8));
if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8));
if (strcmp(offsetBuffer,"NMI1") == 0) offset = (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8));
else if (strcmp(offsetBuffer,"NMI2") == 0) offset = (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8));
else if (strcmp(offsetBuffer,"NMI3") == 0) offset = (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8));
else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8));
else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8));
}
else { //NES Breakpoint keywords
if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) return (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8));
if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8));
if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8));
if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) offset = (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8));
else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8));
else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8));
}
if (conversionOk)
{
*conversionOk = (offset >= 0) && (offset < 0x10000);
}
}
@ -139,7 +165,7 @@ int checkCondition(const char* condition, int num)
// Remove the old breakpoint condition before adding a new condition.
if (watchpoint[num].cond)
{
freeTree(watchpoint[num].cond);
delete watchpoint[num].cond;
free(watchpoint[num].condText);
watchpoint[num].cond = 0;
watchpoint[num].condText = 0;
@ -169,7 +195,7 @@ int checkCondition(const char* condition, int num)
// Remove the old breakpoint condition
if (watchpoint[num].cond)
{
freeTree(watchpoint[num].cond);
delete watchpoint[num].cond;
free(watchpoint[num].condText);
watchpoint[num].cond = 0;
watchpoint[num].condText = 0;

View File

@ -172,7 +172,7 @@ DebuggerState &FCEUI_Debugger();
//#define WRITE_BREAKPOINT 16
//#define EXECUTE_BREAKPOINT 32
int offsetStringToInt(unsigned int type, const char* offsetBuffer);
int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk = nullptr);
unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable);
#endif

View File

@ -346,7 +346,7 @@ void FCEU_DrawRecordingStatus(uint8* XBuf)
hasPlayRecIcon = true;
}
if(FCEUI_EmulationPaused())
if( EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) )
drawstatus(XBuf-ClipSidesOffset,3,28,hasPlayRecIcon?-16:0);
}
}

View File

@ -273,6 +273,8 @@ void FCEUI_ClearEmulationFrameStepped();
void FCEUI_SetEmulationPaused(int val);
///toggles the paused bit (bit0) for EmulationPaused. caused FCEUD_DebugUpdate() to fire if the emulation pauses
void FCEUI_ToggleEmulationPause();
void FCEUI_PauseForDuration(int secs);
int FCEUI_PauseFramesRemaining();
//indicates whether input aids should be drawn (such as crosshairs, etc; usually in fullscreen mode)
bool FCEUD_ShouldDrawInputAids();

View File

@ -30,9 +30,14 @@
#endif
#ifdef _USE_X264
#include <cstdint>
#include "x264.h"
#endif
#ifdef _USE_LIBARCHIVE
#include <archive.h>
#endif
#ifdef _USE_LIBAV
#ifdef __cplusplus
extern "C"
@ -201,6 +206,23 @@ AboutWindow::AboutWindow(QWidget *parent)
sprintf( stmp, " Compiled with zlib %s\n", ZLIB_VERSION );
credits->insertPlainText( stmp );
#endif
#ifdef _USE_LIBARCHIVE
sprintf( stmp, " Compiled with libarchive %s\n", ARCHIVE_VERSION_ONLY_STRING );
credits->insertPlainText( stmp );
const char *libArcName[] = { "zlib", "liblzma", "bzlib", "liblz4", "libzstd", nullptr };
const char *libArcVersion[] = { archive_zlib_version(), archive_liblzma_version(),
archive_bzlib_version(), archive_liblz4_version(), archive_libzstd_version(), nullptr };
i=0;
while (libArcName[i])
{
if (libArcVersion[i])
{
sprintf( stmp, " %s %s\n", libArcName[i], libArcVersion[i]);
credits->insertPlainText( stmp );
}
i++;
}
#endif
#ifdef _S9XLUA_H
sprintf( stmp, " Compiled with %s\n", LUA_RELEASE );

View File

@ -54,7 +54,6 @@ void openCheatDialog(QWidget *parent)
{
win->activateWindow();
win->raise();
win->setFocus();
return;
}
win = new GuiCheatsDialog_t(parent);
@ -705,14 +704,10 @@ int GuiCheatsDialog_t::activeCheatListCB(const char *name, uint32 a, uint8 v, in
if (item == NULL)
{
item = new QTreeWidgetItem();
actvCheatList->addTopLevelItem(item);
item = new QTreeWidgetItem(actvCheatList);
}
//item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren);
item->setCheckState(0, s ? Qt::Checked : Qt::Unchecked);
item->setText(0, tr(codeStr));
@ -738,8 +733,6 @@ void GuiCheatsDialog_t::showActiveCheatList(bool redraw)
enaCheats->setChecked(!globalCheatDisabled);
actvCheatRedraw = redraw;
if (redraw)
{
actvCheatList->clear();
@ -747,6 +740,8 @@ void GuiCheatsDialog_t::showActiveCheatList(bool redraw)
actvCheatIdx = 0;
FCEUI_ListCheats(::activeCheatListCB, (void *)this);
actvCheatList->viewport()->update();
}
//----------------------------------------------------------------------------
void GuiCheatsDialog_t::openCheatFile(void)

View File

@ -72,7 +72,6 @@ protected:
int fontCharWidth;
int actvCheatIdx;
bool actvCheatRedraw;
bool pauseWhileActive;
bool wasPausedByCheats;

View File

@ -63,7 +63,6 @@ int openCDLWindow( QWidget *parent )
{
cdlWin->activateWindow();
cdlWin->raise();
cdlWin->setFocus();
}
else
{

View File

@ -172,7 +172,7 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent)
loadDisplayViews();
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
dbgWin = this;
@ -373,7 +373,7 @@ void ConsoleDebugger::ld65ImportDebug(void)
debugSymbolTable.ld65LoadDebugFile( filename.toStdString().c_str() );
queueUpdate();
queueUpdate(QAsmView::UPDATE_ALL);
return;
}
@ -1834,28 +1834,26 @@ void ConsoleDebugger::selBmAddrChanged(const QString &txt)
//printf("selBmAddrVal = %04X\n", selBmAddrVal );
}
//----------------------------------------------------------------------------
void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept )
DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo *wpIn, QWidget *parent)
: QDialog(parent)
{
int ret;
QDialog dialog(this);
editIdx = editIndex;
wp = wpIn;
QHBoxLayout *hbox;
QVBoxLayout *mainLayout, *vbox;
QLabel *lbl;
QLineEdit *addr1, *addr2, *cond, *name;
QCheckBox *forbidChkBox, *rbp, *wbp, *xbp, *ebp;
QGridLayout *grid;
QFrame *frame;
QGroupBox *gbox;
QPushButton *okButton, *cancelButton;
QRadioButton *cpu_radio, *ppu_radio, *oam_radio, *rom_radio;
if ( editIdx >= 0 )
{
dialog.setWindowTitle( tr("Edit Breakpoint") );
setWindowTitle( tr("Edit Breakpoint") );
}
else
{
dialog.setWindowTitle( tr("Add Breakpoint") );
setWindowTitle( tr("Add Breakpoint") );
}
hbox = new QHBoxLayout();
@ -1874,6 +1872,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
hbox->addWidget( lbl );
hbox->addWidget( addr2 );
connect( addr1, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &)));
connect( addr2, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &)));
forbidChkBox = new QCheckBox( tr("Forbid") );
hbox->addWidget( forbidChkBox );
@ -1906,6 +1907,11 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
rom_radio = new QRadioButton( tr("ROM") );
cpu_radio->setChecked(true);
connect( cpu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( ppu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( oam_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( rom_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
gbox->setLayout( hbox );
hbox->addWidget( cpu_radio );
hbox->addWidget( ppu_radio );
@ -1917,6 +1923,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
mainLayout->addLayout( grid );
lbl = new QLabel( tr("Condition") );
cond = new QLineEdit();
condValid = true;
connect( cond, SIGNAL(textChanged(const QString &)), this, SLOT(conditionTextChanged(const QString &)));
grid->addWidget( lbl, 0, 0 );
grid->addWidget( cond, 0, 1 );
@ -1928,15 +1937,17 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
grid->addWidget( name, 1, 1 );
hbox = new QHBoxLayout();
msgLbl = new QLabel();
okButton = new QPushButton( tr("OK") );
cancelButton = new QPushButton( tr("Cancel") );
mainLayout->addLayout( hbox );
hbox->addWidget( cancelButton );
hbox->addWidget( okButton );
hbox->addWidget( msgLbl, 5 );
hbox->addWidget( cancelButton, 1 );
hbox->addWidget( okButton, 1 );
connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) );
connect( okButton, SIGNAL(clicked(void)), this, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), this, SLOT(reject(void)) );
okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) );
cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) );
@ -2036,22 +2047,168 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
ebp->setChecked(true);
}
dialog.setLayout( mainLayout );
setLayout( mainLayout );
if ( forceAccept )
connect( this , SIGNAL(finished(int)), this, SLOT(closeWindow(int)) );
}
//----------------------------------------------------------------------------
DebuggerBreakpointEditor::~DebuggerBreakpointEditor(void)
{
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::closeEvent(QCloseEvent *event)
{
//printf("Close Window Event\n");
done(QDialog::Rejected);
deleteLater();
event->accept();
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::closeWindow(int ret)
{
//printf("Close Window %i\n", ret);
if ( ret == QDialog::Accepted )
{
ret = QDialog::Accepted;
loadBreakpoint();
}
deleteLater();
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::checkDataValid(void)
{
int type = 0;
bool startAddrValid = false;
bool endAddrValid = false;
bool allEntriesValid = false;
int addrLowerBound = 0;
int addrUpperBound = 0;
int start_addr = 0, end_addr = 0;
if ( cpu_radio->isChecked() )
{
type |= BT_C;
addrLowerBound = 0;
addrUpperBound = 0x10000;
}
else if ( ppu_radio->isChecked() )
{
type |= BT_P;
addrLowerBound = 0;
addrUpperBound = 0x4000;
}
else if ( oam_radio->isChecked() )
{
type |= BT_S;
addrLowerBound = 0;
addrUpperBound = 0x100;
}
else if ( rom_radio->isChecked() )
{
type |= BT_R;
addrLowerBound = 0;
addrUpperBound = 0x10000;
if (GameInfo != nullptr)
{
addrUpperBound = 16+PRGsize[0]+CHRsize[0];
}
}
if ( addr1->text().size() > 0 )
{
bool convOk = false;
start_addr = offsetStringToInt( type, addr1->text().toStdString().c_str(), &convOk );
//printf("StartAddr:0x%04X Upper:0x%04X\n", start_addr, addrUpperBound);
startAddrValid = convOk && (start_addr >= addrLowerBound) && (start_addr < addrUpperBound);
}
else
{
ret = dialog.exec();
startAddrValid = false;
}
if ( ret == QDialog::Accepted )
if ( addr2->text().size() > 0 )
{
bool convOk = false;
end_addr = offsetStringToInt( type, addr2->text().toStdString().c_str(), &convOk );
endAddrValid = convOk && (end_addr >= addrLowerBound) &&
(end_addr < addrUpperBound) && (start_addr < end_addr);
}
else
{
endAddrValid = true;
}
allEntriesValid = startAddrValid && endAddrValid && condValid;
okButton->setEnabled( allEntriesValid );
if (allEntriesValid)
{
msgLbl->clear();
}
else if (!startAddrValid)
{
msgLbl->setText(tr("Start Address Invalid"));
}
else if (!endAddrValid)
{
msgLbl->setText(tr("End Address Invalid"));
}
else if (!condValid)
{
msgLbl->setText(tr("Condition Invalid"));
}
else
{
msgLbl->clear();
}
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::typeChanged(bool checked)
{
if (checked)
{
checkDataValid();
}
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::addressTextChanged(const QString &txt)
{
checkDataValid();
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::conditionTextChanged(const QString &txt)
{
if ( txt.size() > 0 )
{
Condition *c = generateCondition( txt.toStdString().c_str() );
condValid = (c != nullptr);
if (c)
{
delete c; c = nullptr;
}
}
else
{
condValid = true;
}
checkDataValid();
}
//----------------------------------------------------------------------------
void DebuggerBreakpointEditor::loadBreakpoint(void)
{
int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot;
std::string s;
FCEU_WRAPPER_LOCK();
slot = (editIdx < 0) ? numWPs : editIdx;
if ( cpu_radio->isChecked() )
@ -2126,10 +2283,35 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
numWPs++;
}
//bpListUpdate( false );
}
}
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
//int editIndex, watchpointinfo *wp, bool forceAccept,
//----------------------------------------------------------------------------
void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept )
{
int ret;
DebuggerBreakpointEditor *dialog = new DebuggerBreakpointEditor( editIdx, wp, this );
if ( forceAccept )
{
dialog->loadBreakpoint();
dialog->deleteLater();
ret = QDialog::Accepted;
}
else
{
ret = dialog->exec();
}
if (ret == QDialog::Accepted)
{
bpListUpdate( false );
}
}
}
}
//----------------------------------------------------------------------------
void ConsoleDebugger::openDebugSymbolEditWindow( int addr )
@ -2439,7 +2621,7 @@ static void DeleteBreak(int sel)
if (watchpoint[sel].cond)
{
freeTree(watchpoint[sel].cond);
delete watchpoint[sel].cond;
}
if (watchpoint[sel].condText)
{
@ -2488,7 +2670,7 @@ void debuggerClearAllBreakpoints(void)
{
if (watchpoint[i].cond)
{
freeTree(watchpoint[i].cond);
delete watchpoint[i].cond;
}
if (watchpoint[i].condText)
{
@ -2898,7 +3080,7 @@ void ConsoleDebugger::debugStepBackCB(void)
{
FCEU_WRAPPER_LOCK();
FCEUD_TraceLoggerBackUpInstruction();
updateWindowData();
updateWindowData(QAsmView::UPDATE_ALL);
hexEditorUpdateMemoryValues(true);
hexEditorRequestUpdateAll();
lastBpIdx = BREAK_TYPE_STEP;
@ -3093,8 +3275,7 @@ void ConsoleDebugger::seekPCCB (void)
setRegsFromEntry();
//updateAllDebugWindows();
}
windowUpdateReq = true;
//asmView->scrollToPC();
windowUpdateReq = QAsmView::UPDATE_ALL;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::openChangePcDialog(void)
@ -3148,7 +3329,7 @@ void ConsoleDebugger::openChangePcDialog(void)
{
X.PC = sbox->value();
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
}
}
//----------------------------------------------------------------------------
@ -4142,20 +4323,25 @@ void ConsoleDebugger::updateRegisterView(void)
ppuScrollY->setText( tr(stmp) );
}
//----------------------------------------------------------------------------
void ConsoleDebugger::updateWindowData(void)
void ConsoleDebugger::updateWindowData(enum QAsmView::UpdateType type)
{
if (type == QAsmView::UPDATE_ALL)
{
asmView->updateAssemblyView();
asmView->scrollToPC();
updateRegisterView();
} else if (type == QAsmView::UPDATE_NO_SCROLL)
{
asmView->updateAssemblyView();
updateRegisterView();
}
windowUpdateReq = false;
windowUpdateReq = QAsmView::UPDATE_NONE;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::queueUpdate(void)
void ConsoleDebugger::queueUpdate(enum QAsmView::UpdateType type)
{
windowUpdateReq = true;
windowUpdateReq = type;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::updatePeriodic(void)
@ -4173,10 +4359,10 @@ void ConsoleDebugger::updatePeriodic(void)
bpNotifyReq = false;
}
if ( windowUpdateReq )
if ( windowUpdateReq != QAsmView::UPDATE_NONE )
{
FCEU_WRAPPER_LOCK();
updateWindowData();
updateWindowData(windowUpdateReq);
FCEU_WRAPPER_UNLOCK();
}
asmView->update();
@ -4346,7 +4532,7 @@ void ConsoleDebugger::breakPointNotify( int bpNum )
}
}
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::hbarChanged(int value)
@ -4461,7 +4647,6 @@ void debuggerWindowSetFocus(bool val)
{
dbgWin->activateWindow();
dbgWin->raise();
dbgWin->setFocus();
}
}
//----------------------------------------------------------------------------
@ -4470,11 +4655,11 @@ bool debuggerWaitingAtBreakpoint(void)
return waitingAtBp;
}
//----------------------------------------------------------------------------
void updateAllDebuggerWindows( void )
void updateAllDebuggerWindows( enum QAsmView::UpdateType type )
{
if ( dbgWin )
{
dbgWin->queueUpdate();
dbgWin->queueUpdate(type);
}
}
//----------------------------------------------------------------------------

View File

@ -167,6 +167,8 @@ class QAsmView : public QWidget
QFont getFont(void){ return font; };
enum UpdateType { UPDATE_NONE, UPDATE_ALL, UPDATE_NO_SCROLL };
protected:
bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
@ -420,6 +422,51 @@ class DebugBreakOnDialog : public QDialog
void resetDeltas(void);
};
class DebuggerBreakpointEditor : public QDialog
{
Q_OBJECT
public:
DebuggerBreakpointEditor(int editIndex = -1, watchpointinfo *wpIn = nullptr, QWidget *parent = 0);
~DebuggerBreakpointEditor(void);
void loadBreakpoint(void);
protected:
void closeEvent(QCloseEvent *event) override;
void checkDataValid(void);
private:
int editIdx;
watchpointinfo *wp;
QLineEdit *addr1;
QLineEdit *addr2;
QLineEdit *cond;
QLineEdit *name;
QCheckBox *forbidChkBox;
QCheckBox *rbp;
QCheckBox *wbp;
QCheckBox *xbp;
QCheckBox *ebp;
QLabel *msgLbl;
QPushButton *okButton;
QPushButton *cancelButton;
QRadioButton *cpu_radio;
QRadioButton *ppu_radio;
QRadioButton *oam_radio;
QRadioButton *rom_radio;
bool condValid;
private slots:
void closeWindow(int ret);
void typeChanged(bool checked);
void addressTextChanged( const QString &text );
void conditionTextChanged( const QString &text );
};
class ConsoleDebugger : public QDialog
{
Q_OBJECT
@ -428,7 +475,7 @@ class ConsoleDebugger : public QDialog
ConsoleDebugger(QWidget *parent = 0);
~ConsoleDebugger(void);
void updateWindowData(void);
void updateWindowData(enum QAsmView::UpdateType type);
void updateRegisterView(void);
void updateTabVisibility(void);
void breakPointNotify(int bpNum);
@ -437,7 +484,7 @@ class ConsoleDebugger : public QDialog
void setBookmarkSelectedAddress( int addr );
int getBookmarkSelectedAddress(void){ return selBmAddrVal; };
void edit_BM_name( int addr );
void queueUpdate(void);
void queueUpdate(enum QAsmView::UpdateType type);
QLabel *asmLineSelLbl;
@ -530,7 +577,8 @@ class ConsoleDebugger : public QDialog
ColorMenuItem *pcColorAct;
int selBmAddrVal;
bool windowUpdateReq;
enum QAsmView::UpdateType windowUpdateReq;
bool startedTraceLogger;
private:
@ -625,6 +673,6 @@ void saveGameDebugBreakpoints( bool force = false );
void loadGameDebugBreakpoints(void);
void debuggerClearAllBreakpoints(void);
void debuggerClearAllBookmarks(void);
void updateAllDebuggerWindows(void);
void updateAllDebuggerWindows(enum QAsmView::UpdateType type);
extern debuggerBookmarkManager_t dbgBmMgr;

View File

@ -2388,7 +2388,7 @@ void consoleWin_t::openROMFile(void)
QDir d;
const QStringList filters(
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP)",
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP, *.7z *.7zip)",
"NES files (*.nes *.NES)",
"NSF files (*.nsf *.NSF)",
"UNF files (*.unf *.UNF *.unif *.UNIF)",
@ -2758,14 +2758,14 @@ void consoleWin_t::loadState9(void){ loadState(9); }
void consoleWin_t::loadPrevState(void)
{
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 );
FCEU_StateRecorderLoadPrevState();
FCEU_WRAPPER_UNLOCK();
}
void consoleWin_t::loadNextState(void)
{
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 );
FCEU_StateRecorderLoadNextState();
FCEU_WRAPPER_UNLOCK();
}

View File

@ -126,7 +126,6 @@ int openFamilyKeyboardDialog(QWidget *parent)
{
fkbWin->activateWindow();
fkbWin->raise();
fkbWin->setFocus();
}
else
{

View File

@ -42,6 +42,7 @@
#include "Qt/config.h"
#include "Qt/keyscan.h"
#include "Qt/fceuWrapper.h"
#include "Qt/CheatsConf.h"
#include "Qt/HexEditor.h"
#include "Qt/GameGenie.h"
@ -278,6 +279,7 @@ void GameGenieDialog_t::addCheatClicked(void)
FCEU_WRAPPER_LOCK();
FCEUI_AddCheat( name.c_str(), a, v, c, 1 );
updateCheatDialog();
FCEU_WRAPPER_UNLOCK();
}

View File

@ -399,7 +399,7 @@ static int writeMem( int mode, unsigned int addr, int value )
{
if (debuggerWindowIsOpen())
{
updateAllDebuggerWindows();
updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL);
}
}
@ -1853,7 +1853,7 @@ void HexEditorDialog_t::openDebugSymbolEditWindow( int addr )
if ( ret == QDialog::Accepted )
{
updateAllDebuggerWindows();
updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL);
}
}
//----------------------------------------------------------------------------
@ -2777,7 +2777,13 @@ void QHexEdit::keyPressEvent(QKeyEvent *event)
event->accept();
}
else
{ // Use the input text to modify the values in the editor area.
if (event->text().isEmpty())
{
return;
}
int key;
if ( cursorPosX >= 32 )
{ // Edit Area is ASCII
@ -2830,6 +2836,7 @@ void QHexEdit::keyPressEvent(QKeyEvent *event)
}
else
{ // Edit Area is Hex
key = int(event->text()[0].toUpper().toLatin1());
if ( ::isxdigit( key ) )
@ -4222,7 +4229,6 @@ int hexEditorOpenFromDebugger( int mode, int addr )
{
win->activateWindow();
win->raise();
win->setFocus();
}
win->editor->setMode( mode );

View File

@ -108,7 +108,6 @@ int openNameTableViewWindow( QWidget *parent )
{
nameTableViewWindow->activateWindow();
nameTableViewWindow->raise();
nameTableViewWindow->setFocus();
return -1;
}
initNameTableViewer();

View File

@ -27,22 +27,25 @@
#include <QCloseEvent>
#include <QGridLayout>
#include <QSettings>
#include <QMessageBox>
#include "Qt/throttle.h"
#include "Qt/fceuWrapper.h"
#include "Qt/StateRecorderConf.h"
#include "../../fceu.h"
#include "../../state.h"
#include "../../driver.h"
#include "../../emufile.h"
//----------------------------------------------------------------------------
StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *mainLayout;
QVBoxLayout *mainLayout, *vbox1;
QHBoxLayout *hbox, *hbox1;
QGroupBox *frame, *frame1;
QGridLayout *grid, *memStatsGrid;
QGridLayout *grid, *memStatsGrid, *sysStatusGrid;
QSettings settings;
int opt;
@ -52,14 +55,19 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
grid = new QGridLayout();
recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load"));
snapFrames = new QSpinBox();
snapMinutes = new QSpinBox();
snapSeconds = new QSpinBox();
historyDuration = new QSpinBox();
snapFrameSelBtn = new QRadioButton( tr("By Frames") );
snapTimeSelBtn = new QRadioButton( tr("By Time") );
recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() );
connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) );
snapFrames->setMinimum(1);
snapFrames->setMaximum(10000);
snapSeconds->setMinimum(0);
snapSeconds->setMaximum(60);
snapMinutes->setMinimum(0);
@ -67,6 +75,10 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
historyDuration->setMinimum(1);
historyDuration->setMaximum(180);
opt = 10;
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &opt);
snapFrames->setValue(opt);
opt = 15;
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt );
historyDuration->setValue(opt);
@ -79,6 +91,14 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt);
snapSeconds->setValue(opt);
opt = 0;
g_config->getOption("SDL.StateRecorderTimingMode", &opt);
snapFrameSelBtn->setChecked(opt == 0);
snapTimeSelBtn->setChecked(opt == 1);
snapUseTime = opt ? true : false;
connect( snapFrames , SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( snapMinutes, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( historyDuration, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
@ -113,16 +133,38 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
g_config->getOption("SDL.StateRecorderCompressionLevel", &opt);
cmprLvlCbox->setCurrentIndex(opt);
connect( cmprLvlCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(compressionLevelChanged(int)) );
hbox->addWidget(cmprLvlCbox);
frame->setLayout(hbox);
grid->addWidget( frame, 1, 1 );
frame1 = new QGroupBox(tr("Time Between Snapshots:"));
frame1 = new QGroupBox(tr("Snapshot Timing Setting:"));
hbox1 = new QHBoxLayout();
frame1->setLayout(hbox1);
grid->addWidget( frame1, 2, 0, 1, 2 );
g_config->getOption("SDL.StateRecorderTimingMode", &opt);
hbox1->addWidget( snapFrameSelBtn );
hbox1->addWidget( snapTimeSelBtn );
snapFramesGroup = new QGroupBox(tr("Frames Between Snapshots:"));
hbox1 = new QHBoxLayout();
snapFramesGroup->setLayout(hbox1);
snapFramesGroup->setEnabled(snapFrameSelBtn->isChecked());
grid->addWidget( snapFramesGroup, 3, 0, 1, 2 );
hbox1->addWidget( snapFrames);
hbox1->addWidget( new QLabel( tr("Range (1 - 10000)") ) );
snapTimeGroup = new QGroupBox(tr("Time Between Snapshots:"));
hbox1 = new QHBoxLayout();
snapTimeGroup->setLayout(hbox1);
snapTimeGroup->setEnabled(snapTimeSelBtn->isChecked());
grid->addWidget( snapTimeGroup, 4, 0, 1, 2 );
frame = new QGroupBox();
hbox = new QHBoxLayout();
@ -141,16 +183,53 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
frame->setLayout(hbox);
frame1 = new QGroupBox(tr("Pause on State Load:"));
vbox1 = new QVBoxLayout();
frame1->setLayout(vbox1);
g_config->getOption("SDL.StateRecorderPauseOnLoad", &opt);
pauseOnLoadCbox = new QComboBox();
pauseOnLoadCbox->addItem( tr("No"), StateRecorderConfigData::NO_PAUSE );
pauseOnLoadCbox->addItem( tr("Temporary"), StateRecorderConfigData::TEMPORARY_PAUSE );
pauseOnLoadCbox->addItem( tr("Full"), StateRecorderConfigData::FULL_PAUSE );
pauseOnLoadCbox->setCurrentIndex( opt );
connect( pauseOnLoadCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(pauseOnLoadChanged(int)) );
g_config->getOption("SDL.StateRecorderPauseDuration", &opt);
pauseDuration = new QSpinBox();
pauseDuration->setMinimum(0);
pauseDuration->setMaximum(60);
pauseDuration->setValue(opt);
vbox1->addWidget(pauseOnLoadCbox);
frame = new QGroupBox( tr("Duration:") );
hbox = new QHBoxLayout();
vbox1->addWidget(frame);
hbox->addWidget( pauseDuration);
hbox->addWidget( new QLabel( tr("Seconds") ) );
frame->setLayout(hbox);
grid->addWidget(frame1, 4, 3, 2, 1);
frame = new QGroupBox( tr("Memory Usage:") );
memStatsGrid = new QGridLayout();
numSnapsLbl = new QLineEdit();
snapMemSizeLbl = new QLineEdit();
totalMemUsageLbl = new QLineEdit();
saveTimeLbl = new QLineEdit();
numSnapsLbl->setReadOnly(true);
snapMemSizeLbl->setReadOnly(true);
totalMemUsageLbl->setReadOnly(true);
saveTimeLbl->setReadOnly(true);
grid->addWidget(frame, 1, 3, 2, 2);
frame->setLayout(memStatsGrid);
@ -163,8 +242,71 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 );
memStatsGrid->addWidget( totalMemUsageLbl, 2, 1 );
frame = new QGroupBox( tr("CPU Usage:") );
hbox = new QHBoxLayout();
frame->setLayout(hbox);
hbox->addWidget(new QLabel(tr("Snapshot\nSave Time:")), 2);
hbox->addWidget(saveTimeLbl, 2);
grid->addWidget(frame, 3, 3, 1, 1);
mainLayout->addLayout(grid);
frame1 = new QGroupBox(tr("Recorder Status"));
sysStatusGrid = new QGridLayout();
frame1->setLayout(sysStatusGrid);
frame = new QGroupBox();
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 0, 0, 1, 2);
frame->setLayout(hbox);
recStatusLbl = new QLineEdit();
recStatusLbl->setReadOnly(true);
startStopButton = new QPushButton( tr("Start") );
hbox->addWidget( new QLabel(tr("State:")), 1 );
hbox->addWidget( recStatusLbl, 2 );
hbox->addWidget( startStopButton, 1 );
updateStartStopBuffon();
updateRecorderStatusLabel();
connect(startStopButton, SIGNAL(clicked(void)), this, SLOT(startStopClicked(void)));
connect(snapFrameSelBtn, SIGNAL(clicked(void)), this, SLOT(snapFrameModeClicked(void)));
connect(snapTimeSelBtn , SIGNAL(clicked(void)), this, SLOT(snapTimeModeClicked(void)));
frame = new QGroupBox();
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 0, 2, 1, 1);
frame->setLayout(hbox);
recBufSizeLbl = new QLineEdit();
recBufSizeLbl->setReadOnly(true);
hbox->addWidget( new QLabel(tr("Buffer Size:")), 1 );
hbox->addWidget( recBufSizeLbl, 1 );
frame = new QGroupBox( tr("Buffer Use:") );
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 1, 0, 1, 3);
frame->setLayout(hbox);
bufUsage = new QProgressBar();
bufUsage->setToolTip( tr("% use of history record buffer.") );
bufUsage->setOrientation( Qt::Horizontal );
bufUsage->setMinimum( 0 );
bufUsage->setMaximum( 100 );
bufUsage->setValue( 0 );
hbox->addWidget(bufUsage);
updateBufferSizeStatus();
mainLayout->addWidget(frame1);
hbox = new QHBoxLayout();
mainLayout->addLayout(hbox);
@ -186,6 +328,14 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray());
recalcMemoryUsage();
pauseOnLoadChanged( pauseOnLoadCbox->currentIndex() );
updateTimer = new QTimer(this);
connect(updateTimer, &QTimer::timeout, this, &StateRecorderDialog_t::updatePeriodic);
updateTimer->start(1000); // 1hz
}
//----------------------------------------------------------------------------
StateRecorderDialog_t::~StateRecorderDialog_t(void)
@ -197,44 +347,206 @@ void StateRecorderDialog_t::closeEvent(QCloseEvent *event)
{
QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry());
if (dataSavedCheck())
{
done(0);
deleteLater();
event->accept();
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::closeWindow(void)
{
QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry());
if (dataSavedCheck())
{
done(0);
deleteLater();
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::packConfig( StateRecorderConfigData &config )
{
config.timingMode = snapTimeSelBtn->isChecked() ?
StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES;
config.framesBetweenSnaps = snapFrames->value();
config.historyDurationMinutes = static_cast<float>( historyDuration->value() );
config.timeBetweenSnapsMinutes = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
config.compressionLevel = cmprLvlCbox->currentData().toInt();
config.loadPauseTimeSeconds = pauseDuration->value();
config.pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>( pauseOnLoadCbox->currentData().toInt() );
}
//----------------------------------------------------------------------------
bool StateRecorderDialog_t::dataSavedCheck(void)
{
bool okToClose = true;
const StateRecorderConfigData &curConfig = FCEU_StateRecorderGetConfigData();
StateRecorderConfigData selConfig;
packConfig( selConfig );
if ( selConfig.compare( curConfig ) == false )
{
QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"),
tr("Setting selections have not yet been saved.\nDo you wish to save/apply the new settings?"),
QMessageBox::No | QMessageBox::Yes, this);
msgBox.setDefaultButton( QMessageBox::Yes );
int ret = msgBox.exec();
if ( ret == QMessageBox::Yes )
{
applyChanges();
}
}
return okToClose;
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::applyChanges(void)
{
StateRecorderConfigData config;
config.historyDurationMinutes = static_cast<float>( historyDuration->value() );
config.timeBetweenSnapsMinutes = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
config.compressionLevel = cmprLvlCbox->currentData().toInt();
packConfig( config );
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() );
FCEU_StateRecorderSetConfigData( config );
if (FCEU_StateRecorderRunning())
{
// TODO restart with new settings
QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"),
tr("New settings will not take effect until state recorder is restarted. Do you wish to restart?"),
QMessageBox::No | QMessageBox::Yes, this);
msgBox.setDefaultButton( QMessageBox::Yes );
int ret = msgBox.exec();
if ( ret == QMessageBox::Yes )
{
FCEU_StateRecorderStop();
FCEU_StateRecorderStart();
updateStatusDisplay();
}
}
FCEU_WRAPPER_UNLOCK();
g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() );
g_config->setOption("SDL.StateRecorderTimingMode", snapTimeSelBtn->isChecked() ? 1 : 0);
g_config->setOption("SDL.StateRecorderFramesBetweenSnaps", snapFrames->value() );
g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() );
g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() );
g_config->setOption("SDL.StateRecorderCompressionLevel", config.compressionLevel);
g_config->setOption("SDL.StateRecorderPauseOnLoad", config.pauseOnLoad);
g_config->setOption("SDL.StateRecorderPauseDuration", config.loadPauseTimeSeconds);
g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() );
g_config->save();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::startStopClicked(void)
{
FCEU_WRAPPER_LOCK();
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
FCEU_StateRecorderStop();
}
else
{
FCEU_StateRecorderStart();
}
updateStatusDisplay();
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::snapFrameModeClicked(void)
{
snapUseTime = false;
snapTimeGroup->setEnabled(false);
snapFramesGroup->setEnabled(true);
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::snapTimeModeClicked(void)
{
snapUseTime = true;
snapFramesGroup->setEnabled(false);
snapTimeGroup->setEnabled(true);
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateStartStopBuffon(void)
{
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
startStopButton->setText( tr("Stop") );
startStopButton->setIcon( style()->standardIcon( QStyle::SP_MediaStop ) );
}
else
{
startStopButton->setText( tr("Start") );
startStopButton->setIcon( QIcon(":icons/media-record.png") );
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateBufferSizeStatus(void)
{
char stmp[64];
int numSnapsSaved = FCEU_StateRecorderGetNumSnapsSaved();
int maxSnaps = FCEU_StateRecorderGetMaxSnaps();
snprintf( stmp, sizeof(stmp), "%i", maxSnaps );
recBufSizeLbl->setText( tr(stmp) );
if (maxSnaps > 0)
{
bufUsage->setMaximum( maxSnaps );
}
bufUsage->setValue( numSnapsSaved );
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateRecorderStatusLabel(void)
{
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
recStatusLbl->setText( tr("Recording") );
}
else
{
recStatusLbl->setText( tr("Off") );
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateStatusDisplay(void)
{
updateStartStopBuffon();
updateRecorderStatusLabel();
updateBufferSizeStatus();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updatePeriodic(void)
{
FCEU_WRAPPER_LOCK();
updateStatusDisplay();
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::enableChanged(int val)
{
bool ena = val ? true : false;
@ -252,13 +564,39 @@ void StateRecorderDialog_t::spinBoxValueChanged(int newValue)
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::compressionLevelChanged(int newValue)
{
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::pauseOnLoadChanged(int index)
{
StateRecorderConfigData::PauseType pauseOnLoad;
pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>( pauseOnLoadCbox->currentData().toInt() );
pauseDuration->setEnabled( pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE );
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::recalcMemoryUsage(void)
{
char stmp[64];
float fsnapMin = 1.0;
float fhistMin = static_cast<float>( historyDuration->value() );
float fsnapMin = static_cast<float>( snapMinutes->value() ) +
if (snapUseTime)
{
fsnapMin = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
}
else
{
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double hz = ( ((double)fps) / 16777216.0 );
fsnapMin = static_cast<double>(snapFrames->value()) / (hz * 60.0);
}
float fnumSnaps = fhistMin / fsnapMin;
float fsnapSize = 10.0f * 1024.0f;
@ -272,16 +610,32 @@ void StateRecorderDialog_t::recalcMemoryUsage(void)
numSnapsLbl->setText( tr(stmp) );
saveTimeMs = 0.0;
if (GameInfo)
{
constexpr int numIterations = 10;
double ts_start, ts_end;
FCEU_WRAPPER_LOCK();
EMUFILE_MEMORY em;
int compressionLevel = 0;
int compressionLevel = cmprLvlCbox->currentData().toInt();
ts_start = getHighPrecTimeStamp();
// Perform State Save multiple times to get a good average
// on what the compression delays will be.
for (int i=0; i<numIterations; i++)
{
em.set_len(0);
FCEUSS_SaveMS( &em, compressionLevel );
}
ts_end = getHighPrecTimeStamp();
fsnapSize = static_cast<float>( em.size() ) / 1024.0f;
saveTimeMs = (ts_end - ts_start) * 1000.0 / static_cast<double>(numIterations);
fsnapSize = static_cast<float>( em.size() );
FCEU_WRAPPER_UNLOCK();
}
@ -313,5 +667,8 @@ void StateRecorderDialog_t::recalcMemoryUsage(void)
}
totalMemUsageLbl->setText( tr(stmp) );
sprintf( stmp, "%.02f ms", saveTimeMs);
saveTimeLbl->setText( tr(stmp) );
}
//----------------------------------------------------------------------------

View File

@ -12,10 +12,15 @@
#include <QSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <QRadioButton>
#include <QProgressBar>
#include <QLineEdit>
#include <QLabel>
#include <QFrame>
#include <QGroupBox>
#include <QTimer>
struct StateRecorderConfigData;
class StateRecorderDialog_t : public QDialog
{
@ -30,21 +35,49 @@ protected:
QSpinBox *snapMinutes;
QSpinBox *snapSeconds;
QSpinBox *snapFrames;
QSpinBox *historyDuration;
QSpinBox *pauseDuration;
QCheckBox *recorderEnable;
QLineEdit *numSnapsLbl;
QLineEdit *snapMemSizeLbl;
QLineEdit *totalMemUsageLbl;
QLineEdit *saveTimeLbl;
QPushButton *applyButton;
QPushButton *closeButton;
QComboBox *cmprLvlCbox;
QComboBox *pauseOnLoadCbox;
QGroupBox *snapTimeGroup;
QGroupBox *snapFramesGroup;
QRadioButton *snapFrameSelBtn;
QRadioButton *snapTimeSelBtn;
QLineEdit *recStatusLbl;
QLineEdit *recBufSizeLbl;
QPushButton *startStopButton;
QProgressBar *bufUsage;
QTimer *updateTimer;
double saveTimeMs;
bool snapUseTime;
bool dataSavedCheck(void);
void recalcMemoryUsage(void);
void updateStartStopBuffon(void);
void updateRecorderStatusLabel(void);
void updateBufferSizeStatus(void);
void updateStatusDisplay(void);
void packConfig( StateRecorderConfigData &config );
public slots:
void closeWindow(void);
private slots:
void applyChanges(void);
void updatePeriodic(void);
void startStopClicked(void);
void snapTimeModeClicked(void);
void snapFrameModeClicked(void);
void spinBoxValueChanged(int newValue);
void enableChanged(int);
void compressionLevelChanged(int newValue);
void pauseOnLoadChanged(int index);
};

View File

@ -116,7 +116,6 @@ void tasWindowSetFocus(bool val)
{
tasWin->activateWindow();
tasWin->raise();
tasWin->setFocus();
}
}
// this getter contains formula to decide whether to record or replay movie
@ -2917,7 +2916,6 @@ void TasEditorWindow::openFindNoteWindow(void)
{
findWin->activateWindow();
findWin->raise();
findWin->setFocus();
}
else
{

View File

@ -57,6 +57,7 @@
#include "common/os_utils.h"
#include "Qt/ConsoleDebugger.h"
#include "Qt/ConsoleWindow.h"
#include "Qt/ConsoleUtilities.h"
#include "Qt/TraceLogger.h"
@ -1211,7 +1212,6 @@ void openTraceLoggerWindow(QWidget *parent)
{
traceLogWindow->activateWindow();
traceLogWindow->raise();
traceLogWindow->setFocus();
return;
}
//printf("Open Trace Logger Window\n");
@ -2188,7 +2188,7 @@ void QTraceLogView::openBpEditWindow(int editIdx, watchpointinfo *wp, traceRecor
numWPs++;
}
updateAllDebuggerWindows();
updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL);
}
}
}
@ -2233,7 +2233,7 @@ void QTraceLogView::openDebugSymbolEditWindow(int addr, int bank)
if (ret == QDialog::Accepted)
{
updateAllDebuggerWindows();
updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL);
}
}
//----------------------------------------------------------------------------

View File

@ -756,9 +756,13 @@ InitConfig()
config->addOption("SDL.StateRecorderEnable", false);
config->addOption("SDL.StateRecorderHistoryDurationMin", 15);
config->addOption("SDL.StateRecorderTimingMode", 0);
config->addOption("SDL.StateRecorderFramesBetweenSnaps", 60);
config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0);
config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3);
config->addOption("SDL.StateRecorderCompressionLevel", 0);
config->addOption("SDL.StateRecorderPauseOnLoad", 1);
config->addOption("SDL.StateRecorderPauseDuration", 3);
//TODO implement this
config->addOption("periodicsaves", "SDL.PeriodicSaves", 0);

View File

@ -976,22 +976,36 @@ int fceuWrapperInit( int argc, char *argv[] )
// Initialize the State Recorder
{
bool srEnable = false;
bool srUseTimeMode = false;
int srHistDurMin = 15;
int srFramesBtwSnaps = 60;
int srTimeBtwSnapsMin = 0;
int srTimeBtwSnapsSec = 3;
int srCompressionLevel = 0;
int pauseOnLoadTime = 3;
int pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE;
g_config->getOption("SDL.StateRecorderEnable", &srEnable);
g_config->getOption("SDL.StateRecorderTimingMode", &srUseTimeMode);
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin);
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &srFramesBtwSnaps);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec);
g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel);
g_config->getOption("SDL.StateRecorderPauseOnLoad", &pauseOnLoad);
g_config->getOption("SDL.StateRecorderPauseDuration", &pauseOnLoadTime);
StateRecorderConfigData srConfig;
srConfig.historyDurationMinutes = srHistDurMin;
srConfig.timingMode = srUseTimeMode ?
StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES;
srConfig.timeBetweenSnapsMinutes = static_cast<float>( srTimeBtwSnapsMin ) +
( static_cast<float>( srTimeBtwSnapsSec ) / 60.0f );
srConfig.framesBetweenSnaps = srFramesBtwSnaps;
srConfig.compressionLevel = srCompressionLevel;
srConfig.loadPauseTimeSeconds = pauseOnLoadTime;
srConfig.pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>(pauseOnLoad);
FCEU_StateRecorderSetEnabled( srEnable );
FCEU_StateRecorderSetConfigData( srConfig );
@ -1469,20 +1483,19 @@ int fceuWrapperUpdate( void )
return 0;
}
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
static int minizip_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int idx=0, ret;
unzFile zf;
unz_file_info fi;
char filename[512];
ArchiveScanRecord rec;
zf = unzOpen( fname.c_str() );
zf = unzOpen( filepath );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return rec;
return -1;
}
rec.type = 0;
@ -1512,18 +1525,313 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
unzClose( zf );
return 0;
}
#ifdef _USE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
static int libarchive_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
a = archive_read_new();
if (a == nullptr)
{
return -1;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
r = archive_read_open_filename(a, filepath, 10240);
if (r)
{
archive_read_free(a);
return -1;
}
rec.type = 1;
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
const char *filename = archive_entry_pathname(entry);
FCEUARCHIVEFILEINFO_ITEM item;
item.name.assign( filename );
item.size = archive_entry_size(entry);
item.index = idx; idx++;
rec.files.push_back( item );
}
rec.numFilesInArchive = idx;
archive_read_free(a);
return 0;
}
#endif
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
{
int ret = -1;
ArchiveScanRecord rec;
#ifdef _USE_LIBARCHIVE
ret = libarchive_ScanArchive( fname.c_str(), rec );
#endif
if (ret == -1)
{
minizip_ScanArchive( fname.c_str(), rec );
}
return rec;
}
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
static FCEUFILE* minizip_OpenArchive(ArchiveScanRecord& asr, std::string &fname, std::string *searchFile, int innerIndex )
{
int ret;
FCEUFILE* fp = 0;
int ret, idx=0;
FCEUFILE* fp = nullptr;
void *tmpMem = nullptr;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
bool foundFile = false;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if (searchFile)
{
if ( strcmp( searchFile->c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
idx++;
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
//printf("Loading via minizip\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
#ifdef _USE_LIBARCHIVE
static FCEUFILE* libarchive_OpenArchive( ArchiveScanRecord& asr, std::string& fname, std::string *searchFile, int innerIndex)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
const char *filename = nullptr;
bool foundFile = false;
int fileSize = 0;
FCEUFILE* fp = nullptr;
a = archive_read_new();
if (a == nullptr)
{
archive_read_free(a);
return nullptr;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
r = archive_read_open_filename(a, fname.c_str(), 10240);
if (r)
{
archive_read_free(a);
return nullptr;
}
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
filename = archive_entry_pathname(entry);
fileSize = archive_entry_size(entry);
if (searchFile)
{
if (strcmp( filename, searchFile->c_str() ) == 0)
{
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
idx++;
}
if (foundFile && (fileSize > 0))
{
const void *buff;
size_t size, totalSize = 0;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
//printf("Loading via libarchive\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fileSize);
while (1)
{
r = archive_read_data_block(a, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
{
break;
}
if (r != ARCHIVE_OK)
{
break;
}
//printf("Read: %p Size:%zu Offset:%llu\n", buff, size, (long long int)offset);
ms->fwrite( buff, size );
totalSize += size;
}
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = totalSize;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
}
archive_read_free(a);
return fp;
}
#endif
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
{
FCEUFILE* fp = nullptr;
std::string searchFile;
if ( innerFilename != NULL )
@ -1573,75 +1881,14 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
}
}
zf = unzOpen( fname.c_str() );
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive(asr, fname, &searchFile, -1 );
#endif
if ( zf == NULL )
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, &searchFile, -1 );
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( strcmp( searchFile.c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = searchFile;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
@ -1654,82 +1901,16 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int innerIndex, int* userCancel)
{
int ret, idx=0;
FCEUFILE* fp = 0;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
FCEUFILE* fp = nullptr;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive( asr, fname, nullptr, innerIndex );
#endif
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, nullptr, innerIndex);
}
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( idx == innerIndex )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
idx++;
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}

View File

@ -92,7 +92,6 @@ int openPPUViewWindow( QWidget *parent )
{
ppuViewWindow->activateWindow();
ppuViewWindow->raise();
ppuViewWindow->setFocus();
return -1;
}
initPPUViewer();
@ -110,7 +109,6 @@ int openOAMViewWindow( QWidget *parent )
{
spriteViewWindow->activateWindow();
spriteViewWindow->raise();
spriteViewWindow->setFocus();
return -1;
}
initPPUViewer();
@ -637,8 +635,8 @@ QPoint ppuPatternView_t::convPixToTile( QPoint p )
w = pattern->w;
h = pattern->h;
i = x / (w*8);
j = y / (h*8);
i = w == 0 ? 0 : x / (w*8);
j = h == 0 ? 0 : y / (h*8);
if ( PPUView_sprite16Mode[ patternIndex ] )
{
@ -3338,8 +3336,8 @@ QPoint oamPatternView_t::convPixToTile( QPoint p )
w = oamPattern.w;
h = oamPattern.h;
i = x / (w*8);
j = y / (h*16);
i = w == 0 ? 0 : x / (w*8);
j = h == 0 ? 0 : y / (h*16);
//printf("(x,y) = (%i,%i) w=%i h=%i $%X%X \n", x, y, w, h, jj, ii );

View File

@ -1235,7 +1235,7 @@ void DeleteBreak(int sel)
if(sel<0) return;
if(sel>=numWPs) return;
if (watchpoint[sel].cond)
freeTree(watchpoint[sel].cond);
delete watchpoint[sel].cond;
if (watchpoint[sel].condText)
free(watchpoint[sel].condText);
if (watchpoint[sel].desc)

View File

@ -116,6 +116,7 @@ bool movieSubtitles = true; //Toggle for displaying movie subtitles
bool DebuggerWasUpdated = false; //To prevent the debugger from updating things without being updated.
bool AutoResumePlay = false;
char romNameWhenClosingEmulator[2048] = {0};
static unsigned int pauseTimer = 0;
FCEUGI::FCEUGI()
@ -764,6 +765,22 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
#endif
}
if (EmulationPaused & EMULATIONPAUSED_TIMER)
{
if (pauseTimer > 0)
{
pauseTimer--;
}
else
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
}
if (EmulationPaused & EMULATIONPAUSED_FA)
{
// the user is holding Frame Advance key
@ -787,7 +804,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
RefreshThrottleFPS();
}
#endif
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) )
{
// emulator is paused
memcpy(XBuf, XBackBuf, 256*256);
@ -1263,6 +1280,33 @@ void FCEUI_FrameAdvance(void) {
frameAdvanceRequested = true;
}
void FCEUI_PauseForDuration(int secs)
{
int framesPerSec;
// If already paused, do nothing
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
return;
}
if (PAL || dendy)
{
framesPerSec = 50;
}
else
{
framesPerSec = 60;
}
pauseTimer = framesPerSec * secs;
EmulationPaused |= EMULATIONPAUSED_TIMER;
}
int FCEUI_PauseFramesRemaining(void)
{
return (EmulationPaused & EMULATIONPAUSED_TIMER) ? pauseTimer : 0;
}
static int AutosaveCounter = 0;
void UpdateAutosave(void) {

View File

@ -181,8 +181,9 @@ extern uint8 vsdip;
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
#define EMULATIONPAUSED_PAUSED 1
#define EMULATIONPAUSED_FA 2
#define EMULATIONPAUSED_PAUSED 0x01
#define EMULATIONPAUSED_TIMER 0x02
#define EMULATIONPAUSED_FA 0x04
#define FRAMEADVANCE_DELAY_DEFAULT 10
#define NES_HEADER_SIZE 16

View File

@ -84,9 +84,9 @@ bool backupSavestates = true;
bool compressSavestates = true; //By default FCEUX compresses savestates when a movie is inactive.
// a temp memory stream. We'll be dumping some data here and then compress
EMUFILE_MEMORY memory_savestate;
static EMUFILE_MEMORY memory_savestate;
// temporary buffer for compressed data of a savestate
std::vector<uint8> compressed_buf;
static std::vector<uint8> compressed_buf;
#define SFMDATA_SIZE (128)
static SFORMAT SFMDATA[SFMDATA_SIZE];
@ -1218,6 +1218,10 @@ class StateRecorder
void loadConfig( StateRecorderConfigData &config )
{
if (config.framesBetweenSnaps < 1)
{
config.framesBetweenSnaps = 1;
}
if (config.timeBetweenSnapsMinutes < 0.0)
{
config.timeBetweenSnapsMinutes = 3.0f / 60.0f;
@ -1226,6 +1230,9 @@ class StateRecorder
{
config.historyDurationMinutes = config.timeBetweenSnapsMinutes;
}
if (config.timingMode)
{
const double fhistMin = config.historyDurationMinutes;
const double fsnapMin = config.timeBetweenSnapsMinutes;
const double fnumSnaps = fhistMin / fsnapMin;
@ -1239,15 +1246,30 @@ class StateRecorder
double framesPerSnapf = hz * fsnapMin * 60.0;
framesPerSnap = static_cast<unsigned int>( framesPerSnapf + 0.50 );
}
else
{
const double fhistMin = config.historyDurationMinutes;
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double hz = ( ((double)fps) / 16777216.0 );
const double fsnapMin = static_cast<double>(config.framesBetweenSnaps) / (hz * 60.0);
const double fnumSnaps = fhistMin / fsnapMin;
ringBufSize = static_cast<int>( fnumSnaps + 0.5f );
framesPerSnap = config.framesBetweenSnaps;
}
printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap );
compressionLevel = stateRecorderConfig.compressionLevel;
compressionLevel = config.compressionLevel;
loadPauseTime = config.loadPauseTimeSeconds;
pauseOnLoad = config.pauseOnLoad;
}
void update(void)
{
bool isPaused = FCEUI_EmulationPaused() ? true : false;
bool isPaused = EmulationPaused ? true : false;
unsigned int curFrame = static_cast<unsigned int>(currFrameCounter);
@ -1311,6 +1333,8 @@ class StateRecorder
EMUFILE_MEMORY *em = ringBuf[ snapIdx ];
em->fseek(SEEK_SET, 0);
FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP );
frameCounter = lastLoadFrame = static_cast<unsigned int>(currFrameCounter);
@ -1318,9 +1342,55 @@ class StateRecorder
lastState = snapIdx;
loadIndexReset = true;
if (pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE)
{
if (loadPauseTime > 0)
{ // Temporary pause after loading new state for user to have time to process
FCEUI_PauseForDuration(loadPauseTime);
}
}
else if (pauseOnLoad == StateRecorderConfigData::FULL_PAUSE)
{
FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED );
}
return 0;
}
int loadPrevState(void)
{
int snapIdx = lastState;
if ( lastState == ringHead )
{ // No States to Load
return -1;
}
if ( lastState != ringStart )
{
if ( (lastLoadFrame+30) > frameCounter)
{
snapIdx--;
if (snapIdx < 0)
{
snapIdx += ringBufSize;
}
}
}
return loadStateByIndex( snapIdx );
}
int loadNextState(void)
{
int snapIdx = lastState;
int nextIdx = (lastState + 1) % ringBufSize;
if ( nextIdx != ringHead )
{
snapIdx = nextIdx;
}
return loadStateByIndex( snapIdx );
}
int getHeadIndex(void)
{
return ringHead;
@ -1347,6 +1417,10 @@ class StateRecorder
return ringBuf.size() * ringBuf[0]->size();
}
size_t ringBufferSize(void)
{
return ringBuf.size();
}
static bool enabled;
static int lastState;
private:
@ -1362,6 +1436,8 @@ class StateRecorder
int ringStart;
int ringBufSize;
int compressionLevel;
int loadPauseTime;
StateRecorderConfigData::PauseType pauseOnLoad;
unsigned int frameCounter;
unsigned int framesPerSnap;
unsigned int lastLoadFrame;
@ -1415,13 +1491,37 @@ bool FCEU_StateRecorderRunning(void)
return stateRecorder != nullptr;
}
int FCEU_StateRecorderLoadState(int snapIndex)
int FCEU_StateRecorderGetMaxSnaps(void)
{
int size = 0;
if (stateRecorder != nullptr)
{
stateRecorder->loadStateByIndex(snapIndex);
size = stateRecorder->ringBufferSize();
}
return 0;
return size;
}
int FCEU_StateRecorderGetNumSnapsSaved(void)
{
int n = 0;
if (stateRecorder != nullptr)
{
n = stateRecorder->numSnapsSaved();
}
return n;
}
int FCEU_StateRecorderLoadState(int snapIndex)
{
int ret = -1;
if (stateRecorder != nullptr)
{
ret = stateRecorder->loadStateByIndex(snapIndex);
}
return ret;
}
int FCEU_StateRecorderGetStateIndex(void)
@ -1429,6 +1529,28 @@ int FCEU_StateRecorderGetStateIndex(void)
return StateRecorder::lastState;
}
int FCEU_StateRecorderLoadPrevState(void)
{
int ret = -1;
if (stateRecorder != nullptr)
{
ret = stateRecorder->loadPrevState();
}
return ret;
}
int FCEU_StateRecorderLoadNextState(void)
{
int ret = -1;
if (stateRecorder != nullptr)
{
ret = stateRecorder->loadNextState();
}
return ret;
}
const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void)
{
return stateRecorderConfig;
@ -1436,5 +1558,10 @@ const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void)
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig)
{
stateRecorderConfig = newConfig;
if (stateRecorder != nullptr)
{
stateRecorder->loadConfig( stateRecorderConfig );
}
return 0;
}

View File

@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <string>
enum ENUM_SSLOADPARAMS
@ -83,13 +84,37 @@ struct StateRecorderConfigData
{
float historyDurationMinutes;
float timeBetweenSnapsMinutes;
int framesBetweenSnaps;
int compressionLevel;
int loadPauseTimeSeconds;
enum TimingType
{
FRAMES = 0,
TIME,
} timingMode;
enum PauseType
{
NO_PAUSE = 0,
TEMPORARY_PAUSE,
FULL_PAUSE,
} pauseOnLoad;
StateRecorderConfigData(void)
{
framesBetweenSnaps = 60;
historyDurationMinutes = 15.0f;
timeBetweenSnapsMinutes = 3.0f / 60.0f;
compressionLevel = 0;
loadPauseTimeSeconds = 3;
pauseOnLoad = TEMPORARY_PAUSE;
timingMode = FRAMES;
}
bool compare( const StateRecorderConfigData &other )
{
return memcmp( this, &other, sizeof(StateRecorderConfigData) ) == 0;
}
};
@ -99,7 +124,11 @@ int FCEU_StateRecorderUpdate(void);
bool FCEU_StateRecorderRunning(void);
bool FCEU_StateRecorderIsEnabled(void);
void FCEU_StateRecorderSetEnabled(bool enabled);
int FCEU_StateRecorderGetMaxSnaps(void);
int FCEU_StateRecorderGetNumSnapsSaved(void);
int FCEU_StateRecorderGetStateIndex(void);
int FCEU_StateRecorderLoadState(int snapIndex);
int FCEU_StateRecorderLoadPrevState(void);
int FCEU_StateRecorderLoadNextState(void);
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig);
const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void);

View File

@ -87,6 +87,8 @@ std::string AsSnapshotName =""; //adelikat:this will set the snapshot name whe
void FCEUI_SetSnapshotAsName(std::string name) { AsSnapshotName = name; }
std::string FCEUI_GetSnapshotAsName() { return AsSnapshotName; }
static void FCEU_DrawPauseCountDown(uint8 *XBuf);
void FCEU_KillVirtualVideo(void)
{
if ( XBuf )
@ -254,6 +256,7 @@ void FCEU_PutImage(void)
FCEU_DrawLagCounter(XBuf);
FCEU_DrawNTSCControlBars(XBuf);
FCEU_DrawRecordingStatus(XBuf);
FCEU_DrawPauseCountDown(XBuf);
ShowFPS();
}
@ -771,3 +774,35 @@ void ShowFPS(void)
DrawTextTrans(XBuf + ((256 - ClipSidesOffset) - 40) + (FSettings.FirstSLine + 4) * 256, 256, (uint8*)fpsmsg, 0xA0);
}
bool showPauseCountDown = true;
static void FCEU_DrawPauseCountDown(uint8 *XBuf)
{
if (EmulationPaused & EMULATIONPAUSED_TIMER)
{
int pauseFramesLeft = FCEUI_PauseFramesRemaining();
if (showPauseCountDown && (pauseFramesLeft > 0) )
{
char text[32];
int framesPerSec;
if (PAL || dendy)
{
framesPerSec = 50;
}
else
{
framesPerSec = 60;
}
sprintf(text, "Unpausing in %d...", (pauseFramesLeft / framesPerSec) + 1);
if (text[0])
{
DrawTextTrans(XBuf + ClipSidesOffset + (FSettings.FirstSLine) * 256, 256, (uint8*)text, 0xA0);
}
}
}
}