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 sudo apt-get --assume-yes install libminizip-dev
pkg-config --cflags --libs minizip 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 # GTK+-2 is no longer needed
#sudo apt-get install libgtk2.0-dev #sudo apt-get install libgtk2.0-dev

View File

@ -70,6 +70,12 @@ echo 'Install Dependency minizip'
echo '****************************************' echo '****************************************'
brew install minizip brew install minizip
echo '****************************************'
echo 'Install Optional Dependency libarchive'
echo '****************************************'
brew install libarchive
LIBARCHIVE_PATH=`brew --prefix libarchive`;
echo '****************************************' echo '****************************************'
echo 'Install Optional Dependency x264' echo 'Install Optional Dependency x264'
echo '****************************************' echo '****************************************'
@ -87,7 +93,7 @@ brew install ffmpeg
#brew install zlib # Already installed in appveyor macOS #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; ls -ltr $HOME/Qt;

View File

@ -24,28 +24,33 @@ mkdir bin
set SDL_VERSION=2.24.1 set SDL_VERSION=2.24.1
set FFMPEG_VERSION=5.1.2 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/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://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 REM rmdir /q /s SDL2
powershell -command "Expand-Archive" SDL2-devel-%SDL_VERSION%-VC.zip . 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" ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip
powershell -command "Expand-Archive" libarchive-v%LIBARCHIVE_VERSION%-amd64.zip
rename SDL2-%SDL_VERSION% SDL2 rename SDL2-%SDL_VERSION% SDL2
move ffmpeg-%FFMPEG_VERSION%-full_build-shared\ffmpeg-%FFMPEG_VERSION%-full_build-shared ffmpeg move ffmpeg-%FFMPEG_VERSION%-full_build-shared\ffmpeg-%FFMPEG_VERSION%-full_build-shared ffmpeg
rmdir ffmpeg-%FFMPEG_VERSION%-full_build-shared rmdir ffmpeg-%FFMPEG_VERSION%-full_build-shared
del ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip del ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip
move libarchive-v%LIBARCHIVE_VERSION%-amd64\libarchive libarchive
set SDL_INSTALL_PREFIX=%CD% set SDL_INSTALL_PREFIX=%CD%
set FFMPEG_INSTALL_PREFIX=%CD% set FFMPEG_INSTALL_PREFIX=%CD%
set LIBARCHIVE_INSTALL_PREFIX=%CD%
set PUBLIC_RELEASE=0 set PUBLIC_RELEASE=0
IF DEFINED FCEU_RELEASE_VERSION (set PUBLIC_RELEASE=1) IF DEFINED FCEU_RELEASE_VERSION (set PUBLIC_RELEASE=1)
REM cmake -h REM cmake -h
REM cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% .. 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 REM nmake
msbuild /m fceux.sln /p:Configuration=Release 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 src\Release\fceux.exe bin\qfceux.exe
copy %PROJECT_ROOT%\src\auxlib.lua bin\. copy %PROJECT_ROOT%\src\auxlib.lua bin\.
REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\. 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\lua5.1.dll bin\.
copy %SDL_INSTALL_PREFIX%\SDL2\lib\x64\SDL2.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\. copy %FFMPEG_INSTALL_PREFIX%\ffmpeg\bin\*.dll bin\.
windeployqt --no-compiler-runtime bin\qfceux.exe 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( -DMSVC -D_CRT_SECURE_NO_WARNINGS )
add_definitions( -D__SDL__ -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS ) add_definitions( -D__SDL__ -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS )
add_definitions( -DFCEUDEF_DEBUGGER ) add_definitions( -DFCEUDEF_DEBUGGER )
add_definitions( -D_USE_LIBARCHIVE )
add_definitions( /wd4267 /wd4244 ) add_definitions( /wd4267 /wd4244 )
#add_definitions( /wd4018 ) # Integer comparison sign mismatch warnings #add_definitions( /wd4018 ) # Integer comparison sign mismatch warnings
include_directories( ${SDL_INSTALL_PREFIX}/SDL2/include ) include_directories( ${SDL_INSTALL_PREFIX}/SDL2/include )
include_directories( ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/include )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib )
set( OPENGL_LDFLAGS OpenGL::GL ) set( OPENGL_LDFLAGS OpenGL::GL )
set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib ) 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( SYS_LIBS wsock32 ws2_32 vfw32 Htmlhelp )
set(APP_ICON_RESOURCES_WINDOWS ${CMAKE_SOURCE_DIR}/icons/fceux.rc ) set(APP_ICON_RESOURCES_WINDOWS ${CMAKE_SOURCE_DIR}/icons/fceux.rc )
@ -110,6 +113,13 @@ else(WIN32)
add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} ) add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} )
endif() 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) pkg_check_modules( X264 x264)
if ( ${X264_FOUND} ) if ( ${X264_FOUND} )
@ -643,7 +653,7 @@ target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS}
${${Qt}OpenGLWidgets_LIBRARIES} ${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS} ${OPENGL_LDFLAGS}
${SDL2_LDFLAGS} ${SDL2_LDFLAGS}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${LIBARCHIVE_LDFLAGS}
${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS} ${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS}
${SYS_LIBS} ${SYS_LIBS}
) )

View File

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

View File

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

View File

@ -61,9 +61,28 @@ struct Condition
unsigned int type2; unsigned int type2;
unsigned int value2; 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); Condition* generateCondition(const char* str);
#endif #endif

View File

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

View File

@ -172,7 +172,7 @@ DebuggerState &FCEUI_Debugger();
//#define WRITE_BREAKPOINT 16 //#define WRITE_BREAKPOINT 16
//#define EXECUTE_BREAKPOINT 32 //#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); unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable);
#endif #endif

View File

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

View File

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

View File

@ -30,9 +30,14 @@
#endif #endif
#ifdef _USE_X264 #ifdef _USE_X264
#include <cstdint>
#include "x264.h" #include "x264.h"
#endif #endif
#ifdef _USE_LIBARCHIVE
#include <archive.h>
#endif
#ifdef _USE_LIBAV #ifdef _USE_LIBAV
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@ -201,6 +206,23 @@ AboutWindow::AboutWindow(QWidget *parent)
sprintf( stmp, " Compiled with zlib %s\n", ZLIB_VERSION ); sprintf( stmp, " Compiled with zlib %s\n", ZLIB_VERSION );
credits->insertPlainText( stmp ); credits->insertPlainText( stmp );
#endif #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 #ifdef _S9XLUA_H
sprintf( stmp, " Compiled with %s\n", LUA_RELEASE ); sprintf( stmp, " Compiled with %s\n", LUA_RELEASE );

View File

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

View File

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

View File

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

View File

@ -172,7 +172,7 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent)
loadDisplayViews(); loadDisplayViews();
windowUpdateReq = true; windowUpdateReq = QAsmView::UPDATE_ALL;
dbgWin = this; dbgWin = this;
@ -373,7 +373,7 @@ void ConsoleDebugger::ld65ImportDebug(void)
debugSymbolTable.ld65LoadDebugFile( filename.toStdString().c_str() ); debugSymbolTable.ld65LoadDebugFile( filename.toStdString().c_str() );
queueUpdate(); queueUpdate(QAsmView::UPDATE_ALL);
return; return;
} }
@ -1834,28 +1834,26 @@ void ConsoleDebugger::selBmAddrChanged(const QString &txt)
//printf("selBmAddrVal = %04X\n", selBmAddrVal ); //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; editIdx = editIndex;
QDialog dialog(this); wp = wpIn;
QHBoxLayout *hbox; QHBoxLayout *hbox;
QVBoxLayout *mainLayout, *vbox; QVBoxLayout *mainLayout, *vbox;
QLabel *lbl; QLabel *lbl;
QLineEdit *addr1, *addr2, *cond, *name;
QCheckBox *forbidChkBox, *rbp, *wbp, *xbp, *ebp;
QGridLayout *grid; QGridLayout *grid;
QFrame *frame; QFrame *frame;
QGroupBox *gbox; QGroupBox *gbox;
QPushButton *okButton, *cancelButton;
QRadioButton *cpu_radio, *ppu_radio, *oam_radio, *rom_radio;
if ( editIdx >= 0 ) if ( editIdx >= 0 )
{ {
dialog.setWindowTitle( tr("Edit Breakpoint") ); setWindowTitle( tr("Edit Breakpoint") );
} }
else else
{ {
dialog.setWindowTitle( tr("Add Breakpoint") ); setWindowTitle( tr("Add Breakpoint") );
} }
hbox = new QHBoxLayout(); hbox = new QHBoxLayout();
@ -1874,6 +1872,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
hbox->addWidget( lbl ); hbox->addWidget( lbl );
hbox->addWidget( addr2 ); 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") ); forbidChkBox = new QCheckBox( tr("Forbid") );
hbox->addWidget( forbidChkBox ); hbox->addWidget( forbidChkBox );
@ -1906,6 +1907,11 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
rom_radio = new QRadioButton( tr("ROM") ); rom_radio = new QRadioButton( tr("ROM") );
cpu_radio->setChecked(true); 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 ); gbox->setLayout( hbox );
hbox->addWidget( cpu_radio ); hbox->addWidget( cpu_radio );
hbox->addWidget( ppu_radio ); hbox->addWidget( ppu_radio );
@ -1917,6 +1923,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
mainLayout->addLayout( grid ); mainLayout->addLayout( grid );
lbl = new QLabel( tr("Condition") ); lbl = new QLabel( tr("Condition") );
cond = new QLineEdit(); cond = new QLineEdit();
condValid = true;
connect( cond, SIGNAL(textChanged(const QString &)), this, SLOT(conditionTextChanged(const QString &)));
grid->addWidget( lbl, 0, 0 ); grid->addWidget( lbl, 0, 0 );
grid->addWidget( cond, 0, 1 ); grid->addWidget( cond, 0, 1 );
@ -1928,15 +1937,17 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
grid->addWidget( name, 1, 1 ); grid->addWidget( name, 1, 1 );
hbox = new QHBoxLayout(); hbox = new QHBoxLayout();
msgLbl = new QLabel();
okButton = new QPushButton( tr("OK") ); okButton = new QPushButton( tr("OK") );
cancelButton = new QPushButton( tr("Cancel") ); cancelButton = new QPushButton( tr("Cancel") );
mainLayout->addLayout( hbox ); mainLayout->addLayout( hbox );
hbox->addWidget( cancelButton ); hbox->addWidget( msgLbl, 5 );
hbox->addWidget( okButton ); hbox->addWidget( cancelButton, 1 );
hbox->addWidget( okButton, 1 );
connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); connect( okButton, SIGNAL(clicked(void)), this, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); connect( cancelButton, SIGNAL(clicked(void)), this, SLOT(reject(void)) );
okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) ); okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) );
cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) ); cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) );
@ -2036,99 +2047,270 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
ebp->setChecked(true); ebp->setChecked(true);
} }
dialog.setLayout( mainLayout ); setLayout( mainLayout );
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 )
{
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
{
startAddrValid = false;
}
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() )
{
type |= BT_C;
}
else if ( ppu_radio->isChecked() )
{
type |= BT_P;
}
else if ( oam_radio->isChecked() )
{
type |= BT_S;
}
else if ( rom_radio->isChecked() )
{
type |= BT_R;
}
s = addr1->text().toStdString();
if ( s.size() > 0 )
{
start_addr = offsetStringToInt( type, s.c_str() );
}
s = addr2->text().toStdString();
if ( s.size() > 0 )
{
end_addr = offsetStringToInt( type, s.c_str() );
}
if ( rbp->isChecked() )
{
type |= WP_R;
}
if ( wbp->isChecked() )
{
type |= WP_W;
}
if ( xbp->isChecked() )
{
type |= WP_X;
}
if ( forbidChkBox->isChecked() )
{
type |= WP_F;
}
enable = ebp->isChecked();
if ( (start_addr >= 0) && (numWPs < 64) )
{
unsigned int retval;
std::string nameString, condString;
nameString = name->text().toStdString();
condString = cond->text().toStdString();
retval = NewBreak( nameString.c_str(), start_addr, end_addr, type, condString.c_str(), slot, enable);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
if (editIdx < 0)
{
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 ) if ( forceAccept )
{ {
dialog->loadBreakpoint();
dialog->deleteLater();
ret = QDialog::Accepted; ret = QDialog::Accepted;
} }
else else
{ {
ret = dialog.exec(); ret = dialog->exec();
} }
if ( ret == QDialog::Accepted ) if (ret == QDialog::Accepted)
{ {
int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot; bpListUpdate( false );
std::string s;
slot = (editIdx < 0) ? numWPs : editIdx;
if ( cpu_radio->isChecked() )
{
type |= BT_C;
}
else if ( ppu_radio->isChecked() )
{
type |= BT_P;
}
else if ( oam_radio->isChecked() )
{
type |= BT_S;
}
else if ( rom_radio->isChecked() )
{
type |= BT_R;
}
s = addr1->text().toStdString();
if ( s.size() > 0 )
{
start_addr = offsetStringToInt( type, s.c_str() );
}
s = addr2->text().toStdString();
if ( s.size() > 0 )
{
end_addr = offsetStringToInt( type, s.c_str() );
}
if ( rbp->isChecked() )
{
type |= WP_R;
}
if ( wbp->isChecked() )
{
type |= WP_W;
}
if ( xbp->isChecked() )
{
type |= WP_X;
}
if ( forbidChkBox->isChecked() )
{
type |= WP_F;
}
enable = ebp->isChecked();
if ( (start_addr >= 0) && (numWPs < 64) )
{
unsigned int retval;
std::string nameString, condString;
nameString = name->text().toStdString();
condString = cond->text().toStdString();
retval = NewBreak( nameString.c_str(), start_addr, end_addr, type, condString.c_str(), slot, enable);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
if (editIdx < 0)
{
numWPs++;
}
bpListUpdate( false );
}
}
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -2439,7 +2621,7 @@ static void DeleteBreak(int sel)
if (watchpoint[sel].cond) if (watchpoint[sel].cond)
{ {
freeTree(watchpoint[sel].cond); delete watchpoint[sel].cond;
} }
if (watchpoint[sel].condText) if (watchpoint[sel].condText)
{ {
@ -2488,7 +2670,7 @@ void debuggerClearAllBreakpoints(void)
{ {
if (watchpoint[i].cond) if (watchpoint[i].cond)
{ {
freeTree(watchpoint[i].cond); delete watchpoint[i].cond;
} }
if (watchpoint[i].condText) if (watchpoint[i].condText)
{ {
@ -2499,7 +2681,7 @@ void debuggerClearAllBreakpoints(void)
free(watchpoint[i].desc); free(watchpoint[i].desc);
} }
watchpoint[i].address = 0; watchpoint[i].address = 0;
watchpoint[i].endaddress = 0; watchpoint[i].endaddress = 0;
watchpoint[i].flags = 0; watchpoint[i].flags = 0;
watchpoint[i].cond = 0; watchpoint[i].cond = 0;
@ -2898,7 +3080,7 @@ void ConsoleDebugger::debugStepBackCB(void)
{ {
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
FCEUD_TraceLoggerBackUpInstruction(); FCEUD_TraceLoggerBackUpInstruction();
updateWindowData(); updateWindowData(QAsmView::UPDATE_ALL);
hexEditorUpdateMemoryValues(true); hexEditorUpdateMemoryValues(true);
hexEditorRequestUpdateAll(); hexEditorRequestUpdateAll();
lastBpIdx = BREAK_TYPE_STEP; lastBpIdx = BREAK_TYPE_STEP;
@ -3093,8 +3275,7 @@ void ConsoleDebugger::seekPCCB (void)
setRegsFromEntry(); setRegsFromEntry();
//updateAllDebugWindows(); //updateAllDebugWindows();
} }
windowUpdateReq = true; windowUpdateReq = QAsmView::UPDATE_ALL;
//asmView->scrollToPC();
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ConsoleDebugger::openChangePcDialog(void) void ConsoleDebugger::openChangePcDialog(void)
@ -3148,7 +3329,7 @@ void ConsoleDebugger::openChangePcDialog(void)
{ {
X.PC = sbox->value(); X.PC = sbox->value();
windowUpdateReq = true; windowUpdateReq = QAsmView::UPDATE_ALL;
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -4142,20 +4323,25 @@ void ConsoleDebugger::updateRegisterView(void)
ppuScrollY->setText( tr(stmp) ); ppuScrollY->setText( tr(stmp) );
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ConsoleDebugger::updateWindowData(void) void ConsoleDebugger::updateWindowData(enum QAsmView::UpdateType type)
{ {
asmView->updateAssemblyView(); if (type == QAsmView::UPDATE_ALL)
{
asmView->scrollToPC(); asmView->updateAssemblyView();
asmView->scrollToPC();
updateRegisterView();
} else if (type == QAsmView::UPDATE_NO_SCROLL)
{
asmView->updateAssemblyView();
updateRegisterView();
}
updateRegisterView(); windowUpdateReq = QAsmView::UPDATE_NONE;
windowUpdateReq = false;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ConsoleDebugger::queueUpdate(void) void ConsoleDebugger::queueUpdate(enum QAsmView::UpdateType type)
{ {
windowUpdateReq = true; windowUpdateReq = type;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ConsoleDebugger::updatePeriodic(void) void ConsoleDebugger::updatePeriodic(void)
@ -4173,10 +4359,10 @@ void ConsoleDebugger::updatePeriodic(void)
bpNotifyReq = false; bpNotifyReq = false;
} }
if ( windowUpdateReq ) if ( windowUpdateReq != QAsmView::UPDATE_NONE )
{ {
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
updateWindowData(); updateWindowData(windowUpdateReq);
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
} }
asmView->update(); asmView->update();
@ -4346,7 +4532,7 @@ void ConsoleDebugger::breakPointNotify( int bpNum )
} }
} }
windowUpdateReq = true; windowUpdateReq = QAsmView::UPDATE_ALL;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void ConsoleDebugger::hbarChanged(int value) void ConsoleDebugger::hbarChanged(int value)
@ -4461,7 +4647,6 @@ void debuggerWindowSetFocus(bool val)
{ {
dbgWin->activateWindow(); dbgWin->activateWindow();
dbgWin->raise(); dbgWin->raise();
dbgWin->setFocus();
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -4470,11 +4655,11 @@ bool debuggerWaitingAtBreakpoint(void)
return waitingAtBp; return waitingAtBp;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void updateAllDebuggerWindows( void ) void updateAllDebuggerWindows( enum QAsmView::UpdateType type )
{ {
if ( dbgWin ) if ( dbgWin )
{ {
dbgWin->queueUpdate(); dbgWin->queueUpdate(type);
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -167,6 +167,8 @@ class QAsmView : public QWidget
QFont getFont(void){ return font; }; QFont getFont(void){ return font; };
enum UpdateType { UPDATE_NONE, UPDATE_ALL, UPDATE_NO_SCROLL };
protected: protected:
bool event(QEvent *event) override; bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
@ -420,6 +422,51 @@ class DebugBreakOnDialog : public QDialog
void resetDeltas(void); 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 class ConsoleDebugger : public QDialog
{ {
Q_OBJECT Q_OBJECT
@ -428,7 +475,7 @@ class ConsoleDebugger : public QDialog
ConsoleDebugger(QWidget *parent = 0); ConsoleDebugger(QWidget *parent = 0);
~ConsoleDebugger(void); ~ConsoleDebugger(void);
void updateWindowData(void); void updateWindowData(enum QAsmView::UpdateType type);
void updateRegisterView(void); void updateRegisterView(void);
void updateTabVisibility(void); void updateTabVisibility(void);
void breakPointNotify(int bpNum); void breakPointNotify(int bpNum);
@ -437,7 +484,7 @@ class ConsoleDebugger : public QDialog
void setBookmarkSelectedAddress( int addr ); void setBookmarkSelectedAddress( int addr );
int getBookmarkSelectedAddress(void){ return selBmAddrVal; }; int getBookmarkSelectedAddress(void){ return selBmAddrVal; };
void edit_BM_name( int addr ); void edit_BM_name( int addr );
void queueUpdate(void); void queueUpdate(enum QAsmView::UpdateType type);
QLabel *asmLineSelLbl; QLabel *asmLineSelLbl;
@ -530,7 +577,8 @@ class ConsoleDebugger : public QDialog
ColorMenuItem *pcColorAct; ColorMenuItem *pcColorAct;
int selBmAddrVal; int selBmAddrVal;
bool windowUpdateReq; enum QAsmView::UpdateType windowUpdateReq;
bool startedTraceLogger; bool startedTraceLogger;
private: private:
@ -625,6 +673,6 @@ void saveGameDebugBreakpoints( bool force = false );
void loadGameDebugBreakpoints(void); void loadGameDebugBreakpoints(void);
void debuggerClearAllBreakpoints(void); void debuggerClearAllBreakpoints(void);
void debuggerClearAllBookmarks(void); void debuggerClearAllBookmarks(void);
void updateAllDebuggerWindows(void); void updateAllDebuggerWindows(enum QAsmView::UpdateType type);
extern debuggerBookmarkManager_t dbgBmMgr; extern debuggerBookmarkManager_t dbgBmMgr;

View File

@ -2388,7 +2388,7 @@ void consoleWin_t::openROMFile(void)
QDir d; QDir d;
const QStringList filters( 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)", "NES files (*.nes *.NES)",
"NSF files (*.nsf *.NSF)", "NSF files (*.nsf *.NSF)",
"UNF files (*.unf *.UNF *.unif *.UNIF)", "UNF files (*.unf *.UNF *.unif *.UNIF)",
@ -2758,14 +2758,14 @@ void consoleWin_t::loadState9(void){ loadState(9); }
void consoleWin_t::loadPrevState(void) void consoleWin_t::loadPrevState(void)
{ {
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); FCEU_StateRecorderLoadPrevState();
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
} }
void consoleWin_t::loadNextState(void) void consoleWin_t::loadNextState(void)
{ {
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadState( FCEU_StateRecorderGetStateIndex()-1 ); FCEU_StateRecorderLoadNextState();
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -27,22 +27,25 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QGridLayout> #include <QGridLayout>
#include <QSettings> #include <QSettings>
#include <QMessageBox>
#include "Qt/throttle.h"
#include "Qt/fceuWrapper.h" #include "Qt/fceuWrapper.h"
#include "Qt/StateRecorderConf.h" #include "Qt/StateRecorderConf.h"
#include "../../fceu.h" #include "../../fceu.h"
#include "../../state.h" #include "../../state.h"
#include "../../driver.h"
#include "../../emufile.h" #include "../../emufile.h"
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent) StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
: QDialog(parent) : QDialog(parent)
{ {
QVBoxLayout *mainLayout; QVBoxLayout *mainLayout, *vbox1;
QHBoxLayout *hbox, *hbox1; QHBoxLayout *hbox, *hbox1;
QGroupBox *frame, *frame1; QGroupBox *frame, *frame1;
QGridLayout *grid, *memStatsGrid; QGridLayout *grid, *memStatsGrid, *sysStatusGrid;
QSettings settings; QSettings settings;
int opt; int opt;
@ -52,14 +55,19 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
grid = new QGridLayout(); grid = new QGridLayout();
recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load")); recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load"));
snapFrames = new QSpinBox();
snapMinutes = new QSpinBox(); snapMinutes = new QSpinBox();
snapSeconds = new QSpinBox(); snapSeconds = new QSpinBox();
historyDuration = new QSpinBox(); historyDuration = new QSpinBox();
snapFrameSelBtn = new QRadioButton( tr("By Frames") );
snapTimeSelBtn = new QRadioButton( tr("By Time") );
recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() ); recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() );
connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) ); connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) );
snapFrames->setMinimum(1);
snapFrames->setMaximum(10000);
snapSeconds->setMinimum(0); snapSeconds->setMinimum(0);
snapSeconds->setMaximum(60); snapSeconds->setMaximum(60);
snapMinutes->setMinimum(0); snapMinutes->setMinimum(0);
@ -67,6 +75,10 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
historyDuration->setMinimum(1); historyDuration->setMinimum(1);
historyDuration->setMaximum(180); historyDuration->setMaximum(180);
opt = 10;
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &opt);
snapFrames->setValue(opt);
opt = 15; opt = 15;
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt ); g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt );
historyDuration->setValue(opt); historyDuration->setValue(opt);
@ -79,6 +91,14 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt);
snapSeconds->setValue(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( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( snapMinutes, 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)) ); 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); g_config->getOption("SDL.StateRecorderCompressionLevel", &opt);
cmprLvlCbox->setCurrentIndex(opt); cmprLvlCbox->setCurrentIndex(opt);
connect( cmprLvlCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(compressionLevelChanged(int)) );
hbox->addWidget(cmprLvlCbox); hbox->addWidget(cmprLvlCbox);
frame->setLayout(hbox); frame->setLayout(hbox);
grid->addWidget( frame, 1, 1 ); grid->addWidget( frame, 1, 1 );
frame1 = new QGroupBox(tr("Time Between Snapshots:")); frame1 = new QGroupBox(tr("Snapshot Timing Setting:"));
hbox1 = new QHBoxLayout(); hbox1 = new QHBoxLayout();
frame1->setLayout(hbox1); frame1->setLayout(hbox1);
grid->addWidget( frame1, 2, 0, 1, 2 ); 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(); frame = new QGroupBox();
hbox = new QHBoxLayout(); hbox = new QHBoxLayout();
@ -141,16 +183,53 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
frame->setLayout(hbox); 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:") ); frame = new QGroupBox( tr("Memory Usage:") );
memStatsGrid = new QGridLayout(); memStatsGrid = new QGridLayout();
numSnapsLbl = new QLineEdit(); numSnapsLbl = new QLineEdit();
snapMemSizeLbl = new QLineEdit(); snapMemSizeLbl = new QLineEdit();
totalMemUsageLbl = new QLineEdit(); totalMemUsageLbl = new QLineEdit();
saveTimeLbl = new QLineEdit();
numSnapsLbl->setReadOnly(true); numSnapsLbl->setReadOnly(true);
snapMemSizeLbl->setReadOnly(true); snapMemSizeLbl->setReadOnly(true);
totalMemUsageLbl->setReadOnly(true); totalMemUsageLbl->setReadOnly(true);
saveTimeLbl->setReadOnly(true);
grid->addWidget(frame, 1, 3, 2, 2); grid->addWidget(frame, 1, 3, 2, 2);
frame->setLayout(memStatsGrid); frame->setLayout(memStatsGrid);
@ -163,8 +242,71 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 ); memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 );
memStatsGrid->addWidget( totalMemUsageLbl, 2, 1 ); 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); 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(); hbox = new QHBoxLayout();
mainLayout->addLayout(hbox); mainLayout->addLayout(hbox);
@ -186,6 +328,14 @@ StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray()); restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray());
recalcMemoryUsage(); 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) StateRecorderDialog_t::~StateRecorderDialog_t(void)
@ -197,44 +347,206 @@ void StateRecorderDialog_t::closeEvent(QCloseEvent *event)
{ {
QSettings settings; QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry()); settings.setValue("stateRecorderWindow/geometry", saveGeometry());
done(0);
deleteLater(); if (dataSavedCheck())
event->accept(); {
done(0);
deleteLater();
event->accept();
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void StateRecorderDialog_t::closeWindow(void) void StateRecorderDialog_t::closeWindow(void)
{ {
QSettings settings; QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry()); settings.setValue("stateRecorderWindow/geometry", saveGeometry());
done(0);
deleteLater(); 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) void StateRecorderDialog_t::applyChanges(void)
{ {
StateRecorderConfigData config; StateRecorderConfigData config;
config.historyDurationMinutes = static_cast<float>( historyDuration->value() ); packConfig( config );
config.timeBetweenSnapsMinutes = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
config.compressionLevel = cmprLvlCbox->currentData().toInt();
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() ); FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() );
FCEU_StateRecorderSetConfigData( config ); FCEU_StateRecorderSetConfigData( config );
if (FCEU_StateRecorderRunning()) 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(); FCEU_WRAPPER_UNLOCK();
g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() ); 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.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() );
g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->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->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() );
g_config->save(); 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) void StateRecorderDialog_t::enableChanged(int val)
{ {
bool ena = val ? true : false; bool ena = val ? true : false;
@ -252,13 +564,39 @@ void StateRecorderDialog_t::spinBoxValueChanged(int newValue)
recalcMemoryUsage(); 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) void StateRecorderDialog_t::recalcMemoryUsage(void)
{ {
char stmp[64]; char stmp[64];
float fsnapMin = 1.0;
float fhistMin = static_cast<float>( historyDuration->value() ); 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 ); ( 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 fnumSnaps = fhistMin / fsnapMin;
float fsnapSize = 10.0f * 1024.0f; float fsnapSize = 10.0f * 1024.0f;
@ -272,16 +610,32 @@ void StateRecorderDialog_t::recalcMemoryUsage(void)
numSnapsLbl->setText( tr(stmp) ); numSnapsLbl->setText( tr(stmp) );
saveTimeMs = 0.0;
if (GameInfo) if (GameInfo)
{ {
constexpr int numIterations = 10;
double ts_start, ts_end;
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
EMUFILE_MEMORY em; EMUFILE_MEMORY em;
int compressionLevel = 0; int compressionLevel = cmprLvlCbox->currentData().toInt();
FCEUSS_SaveMS( &em, compressionLevel ); ts_start = getHighPrecTimeStamp();
fsnapSize = static_cast<float>( em.size() ) / 1024.0f; // 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();
saveTimeMs = (ts_end - ts_start) * 1000.0 / static_cast<double>(numIterations);
fsnapSize = static_cast<float>( em.size() );
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
} }
@ -313,5 +667,8 @@ void StateRecorderDialog_t::recalcMemoryUsage(void)
} }
totalMemUsageLbl->setText( tr(stmp) ); totalMemUsageLbl->setText( tr(stmp) );
sprintf( stmp, "%.02f ms", saveTimeMs);
saveTimeLbl->setText( tr(stmp) );
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -12,10 +12,15 @@
#include <QSpinBox> #include <QSpinBox>
#include <QComboBox> #include <QComboBox>
#include <QPushButton> #include <QPushButton>
#include <QRadioButton>
#include <QProgressBar>
#include <QLineEdit> #include <QLineEdit>
#include <QLabel> #include <QLabel>
#include <QFrame> #include <QFrame>
#include <QGroupBox> #include <QGroupBox>
#include <QTimer>
struct StateRecorderConfigData;
class StateRecorderDialog_t : public QDialog class StateRecorderDialog_t : public QDialog
{ {
@ -28,23 +33,51 @@ public:
protected: protected:
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);
QSpinBox *snapMinutes; QSpinBox *snapMinutes;
QSpinBox *snapSeconds; QSpinBox *snapSeconds;
QSpinBox *historyDuration; QSpinBox *snapFrames;
QCheckBox *recorderEnable; QSpinBox *historyDuration;
QLineEdit *numSnapsLbl; QSpinBox *pauseDuration;
QLineEdit *snapMemSizeLbl; QCheckBox *recorderEnable;
QLineEdit *totalMemUsageLbl; QLineEdit *numSnapsLbl;
QPushButton *applyButton; QLineEdit *snapMemSizeLbl;
QPushButton *closeButton; QLineEdit *totalMemUsageLbl;
QComboBox *cmprLvlCbox; 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 recalcMemoryUsage(void);
void updateStartStopBuffon(void);
void updateRecorderStatusLabel(void);
void updateBufferSizeStatus(void);
void updateStatusDisplay(void);
void packConfig( StateRecorderConfigData &config );
public slots: public slots:
void closeWindow(void); void closeWindow(void);
private slots: private slots:
void applyChanges(void); void applyChanges(void);
void updatePeriodic(void);
void startStopClicked(void);
void snapTimeModeClicked(void);
void snapFrameModeClicked(void);
void spinBoxValueChanged(int newValue); void spinBoxValueChanged(int newValue);
void enableChanged(int); void enableChanged(int);
void compressionLevelChanged(int newValue);
void pauseOnLoadChanged(int index);
}; };

View File

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

View File

@ -57,6 +57,7 @@
#include "common/os_utils.h" #include "common/os_utils.h"
#include "Qt/ConsoleDebugger.h"
#include "Qt/ConsoleWindow.h" #include "Qt/ConsoleWindow.h"
#include "Qt/ConsoleUtilities.h" #include "Qt/ConsoleUtilities.h"
#include "Qt/TraceLogger.h" #include "Qt/TraceLogger.h"
@ -1211,7 +1212,6 @@ void openTraceLoggerWindow(QWidget *parent)
{ {
traceLogWindow->activateWindow(); traceLogWindow->activateWindow();
traceLogWindow->raise(); traceLogWindow->raise();
traceLogWindow->setFocus();
return; return;
} }
//printf("Open Trace Logger Window\n"); //printf("Open Trace Logger Window\n");
@ -2188,7 +2188,7 @@ void QTraceLogView::openBpEditWindow(int editIdx, watchpointinfo *wp, traceRecor
numWPs++; numWPs++;
} }
updateAllDebuggerWindows(); updateAllDebuggerWindows(QAsmView::UPDATE_NO_SCROLL);
} }
} }
} }
@ -2233,7 +2233,7 @@ void QTraceLogView::openDebugSymbolEditWindow(int addr, int bank)
if (ret == QDialog::Accepted) 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.StateRecorderEnable", false);
config->addOption("SDL.StateRecorderHistoryDurationMin", 15); config->addOption("SDL.StateRecorderHistoryDurationMin", 15);
config->addOption("SDL.StateRecorderTimingMode", 0);
config->addOption("SDL.StateRecorderFramesBetweenSnaps", 60);
config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0); config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0);
config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3); config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3);
config->addOption("SDL.StateRecorderCompressionLevel", 0); config->addOption("SDL.StateRecorderCompressionLevel", 0);
config->addOption("SDL.StateRecorderPauseOnLoad", 1);
config->addOption("SDL.StateRecorderPauseDuration", 3);
//TODO implement this //TODO implement this
config->addOption("periodicsaves", "SDL.PeriodicSaves", 0); config->addOption("periodicsaves", "SDL.PeriodicSaves", 0);

View File

@ -976,22 +976,36 @@ int fceuWrapperInit( int argc, char *argv[] )
// Initialize the State Recorder // Initialize the State Recorder
{ {
bool srEnable = false; bool srEnable = false;
bool srUseTimeMode = false;
int srHistDurMin = 15; int srHistDurMin = 15;
int srFramesBtwSnaps = 60;
int srTimeBtwSnapsMin = 0; int srTimeBtwSnapsMin = 0;
int srTimeBtwSnapsSec = 3; int srTimeBtwSnapsSec = 3;
int srCompressionLevel = 0; int srCompressionLevel = 0;
int pauseOnLoadTime = 3;
int pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE;
g_config->getOption("SDL.StateRecorderEnable", &srEnable); g_config->getOption("SDL.StateRecorderEnable", &srEnable);
g_config->getOption("SDL.StateRecorderTimingMode", &srUseTimeMode);
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin); g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin);
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &srFramesBtwSnaps);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec); g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec);
g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel); g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel);
g_config->getOption("SDL.StateRecorderPauseOnLoad", &pauseOnLoad);
g_config->getOption("SDL.StateRecorderPauseDuration", &pauseOnLoadTime);
StateRecorderConfigData srConfig; StateRecorderConfigData srConfig;
srConfig.historyDurationMinutes = srHistDurMin; srConfig.historyDurationMinutes = srHistDurMin;
srConfig.timingMode = srUseTimeMode ?
StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES;
srConfig.timeBetweenSnapsMinutes = static_cast<float>( srTimeBtwSnapsMin ) + srConfig.timeBetweenSnapsMinutes = static_cast<float>( srTimeBtwSnapsMin ) +
( static_cast<float>( srTimeBtwSnapsSec ) / 60.0f ); ( static_cast<float>( srTimeBtwSnapsSec ) / 60.0f );
srConfig.framesBetweenSnaps = srFramesBtwSnaps;
srConfig.compressionLevel = srCompressionLevel; srConfig.compressionLevel = srCompressionLevel;
srConfig.loadPauseTimeSeconds = pauseOnLoadTime;
srConfig.pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>(pauseOnLoad);
FCEU_StateRecorderSetEnabled( srEnable ); FCEU_StateRecorderSetEnabled( srEnable );
FCEU_StateRecorderSetConfigData( srConfig ); FCEU_StateRecorderSetConfigData( srConfig );
@ -1469,20 +1483,19 @@ int fceuWrapperUpdate( void )
return 0; return 0;
} }
ArchiveScanRecord FCEUD_ScanArchive(std::string fname) static int minizip_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{ {
int idx=0, ret; int idx=0, ret;
unzFile zf; unzFile zf;
unz_file_info fi; unz_file_info fi;
char filename[512]; char filename[512];
ArchiveScanRecord rec;
zf = unzOpen( filepath );
zf = unzOpen( fname.c_str() );
if ( zf == NULL ) if ( zf == NULL )
{ {
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); //printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return rec; return -1;
} }
rec.type = 0; rec.type = 0;
@ -1512,18 +1525,313 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
unzClose( zf ); 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; 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; int ret, idx=0;
FCEUFILE* fp = 0; FCEUFILE* fp = nullptr;
void *tmpMem = nullptr;
unzFile zf; unzFile zf;
unz_file_info fi; unz_file_info fi;
char filename[512]; char filename[512];
char foundFile = 0; bool foundFile = false;
void *tmpMem = NULL;
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; std::string searchFile;
if ( innerFilename != NULL ) 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() ); fp = minizip_OpenArchive(asr, fname, &searchFile, -1 );
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 ( 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; 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) FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int innerIndex, int* userCancel)
{ {
int ret, idx=0; FCEUFILE* fp = nullptr;
FCEUFILE* fp = 0;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
zf = unzOpen( fname.c_str() ); #ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive( asr, fname, nullptr, innerIndex );
if ( zf == NULL ) #endif
if (fp == nullptr)
{ {
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() ); fp = minizip_OpenArchive(asr, fname, nullptr, innerIndex);
return fp;
} }
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; return fp;
} }

View File

@ -92,7 +92,6 @@ int openPPUViewWindow( QWidget *parent )
{ {
ppuViewWindow->activateWindow(); ppuViewWindow->activateWindow();
ppuViewWindow->raise(); ppuViewWindow->raise();
ppuViewWindow->setFocus();
return -1; return -1;
} }
initPPUViewer(); initPPUViewer();
@ -110,7 +109,6 @@ int openOAMViewWindow( QWidget *parent )
{ {
spriteViewWindow->activateWindow(); spriteViewWindow->activateWindow();
spriteViewWindow->raise(); spriteViewWindow->raise();
spriteViewWindow->setFocus();
return -1; return -1;
} }
initPPUViewer(); initPPUViewer();
@ -637,8 +635,8 @@ QPoint ppuPatternView_t::convPixToTile( QPoint p )
w = pattern->w; w = pattern->w;
h = pattern->h; h = pattern->h;
i = x / (w*8); i = w == 0 ? 0 : x / (w*8);
j = y / (h*8); j = h == 0 ? 0 : y / (h*8);
if ( PPUView_sprite16Mode[ patternIndex ] ) if ( PPUView_sprite16Mode[ patternIndex ] )
{ {
@ -3338,8 +3336,8 @@ QPoint oamPatternView_t::convPixToTile( QPoint p )
w = oamPattern.w; w = oamPattern.w;
h = oamPattern.h; h = oamPattern.h;
i = x / (w*8); i = w == 0 ? 0 : x / (w*8);
j = y / (h*16); 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 ); //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<0) return;
if(sel>=numWPs) return; if(sel>=numWPs) return;
if (watchpoint[sel].cond) if (watchpoint[sel].cond)
freeTree(watchpoint[sel].cond); delete watchpoint[sel].cond;
if (watchpoint[sel].condText) if (watchpoint[sel].condText)
free(watchpoint[sel].condText); free(watchpoint[sel].condText);
if (watchpoint[sel].desc) 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 DebuggerWasUpdated = false; //To prevent the debugger from updating things without being updated.
bool AutoResumePlay = false; bool AutoResumePlay = false;
char romNameWhenClosingEmulator[2048] = {0}; char romNameWhenClosingEmulator[2048] = {0};
static unsigned int pauseTimer = 0;
FCEUGI::FCEUGI() FCEUGI::FCEUGI()
@ -764,6 +765,22 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
#endif #endif
} }
if (EmulationPaused & EMULATIONPAUSED_TIMER)
{
if (pauseTimer > 0)
{
pauseTimer--;
}
else
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
}
if (EmulationPaused & EMULATIONPAUSED_FA) if (EmulationPaused & EMULATIONPAUSED_FA)
{ {
// the user is holding Frame Advance key // the user is holding Frame Advance key
@ -787,7 +804,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
RefreshThrottleFPS(); RefreshThrottleFPS();
} }
#endif #endif
if (EmulationPaused & EMULATIONPAUSED_PAUSED) if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) )
{ {
// emulator is paused // emulator is paused
memcpy(XBuf, XBackBuf, 256*256); memcpy(XBuf, XBackBuf, 256*256);
@ -1263,6 +1280,33 @@ void FCEUI_FrameAdvance(void) {
frameAdvanceRequested = true; 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; static int AutosaveCounter = 0;
void UpdateAutosave(void) { void UpdateAutosave(void) {

View File

@ -181,8 +181,9 @@ extern uint8 vsdip;
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
#define EMULATIONPAUSED_PAUSED 1 #define EMULATIONPAUSED_PAUSED 0x01
#define EMULATIONPAUSED_FA 2 #define EMULATIONPAUSED_TIMER 0x02
#define EMULATIONPAUSED_FA 0x04
#define FRAMEADVANCE_DELAY_DEFAULT 10 #define FRAMEADVANCE_DELAY_DEFAULT 10
#define NES_HEADER_SIZE 16 #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. 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 // 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 // temporary buffer for compressed data of a savestate
std::vector<uint8> compressed_buf; static std::vector<uint8> compressed_buf;
#define SFMDATA_SIZE (128) #define SFMDATA_SIZE (128)
static SFORMAT SFMDATA[SFMDATA_SIZE]; static SFORMAT SFMDATA[SFMDATA_SIZE];
@ -1218,6 +1218,10 @@ class StateRecorder
void loadConfig( StateRecorderConfigData &config ) void loadConfig( StateRecorderConfigData &config )
{ {
if (config.framesBetweenSnaps < 1)
{
config.framesBetweenSnaps = 1;
}
if (config.timeBetweenSnapsMinutes < 0.0) if (config.timeBetweenSnapsMinutes < 0.0)
{ {
config.timeBetweenSnapsMinutes = 3.0f / 60.0f; config.timeBetweenSnapsMinutes = 3.0f / 60.0f;
@ -1226,28 +1230,46 @@ class StateRecorder
{ {
config.historyDurationMinutes = config.timeBetweenSnapsMinutes; config.historyDurationMinutes = config.timeBetweenSnapsMinutes;
} }
const double fhistMin = config.historyDurationMinutes;
const double fsnapMin = config.timeBetweenSnapsMinutes;
const double fnumSnaps = fhistMin / fsnapMin;
ringBufSize = static_cast<int>( fnumSnaps + 0.5f ); if (config.timingMode)
{
const double fhistMin = config.historyDurationMinutes;
const double fsnapMin = config.timeBetweenSnapsMinutes;
const double fnumSnaps = fhistMin / fsnapMin;
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz ringBufSize = static_cast<int>( fnumSnaps + 0.5f );
double hz = ( ((double)fps) / 16777216.0 ); int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double framesPerSnapf = hz * fsnapMin * 60.0; double hz = ( ((double)fps) / 16777216.0 );
framesPerSnap = static_cast<unsigned int>( framesPerSnapf + 0.50 ); 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 ); printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap );
compressionLevel = stateRecorderConfig.compressionLevel; compressionLevel = config.compressionLevel;
loadPauseTime = config.loadPauseTimeSeconds;
pauseOnLoad = config.pauseOnLoad;
} }
void update(void) void update(void)
{ {
bool isPaused = FCEUI_EmulationPaused() ? true : false; bool isPaused = EmulationPaused ? true : false;
unsigned int curFrame = static_cast<unsigned int>(currFrameCounter); unsigned int curFrame = static_cast<unsigned int>(currFrameCounter);
@ -1311,6 +1333,8 @@ class StateRecorder
EMUFILE_MEMORY *em = ringBuf[ snapIdx ]; EMUFILE_MEMORY *em = ringBuf[ snapIdx ];
em->fseek(SEEK_SET, 0);
FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP ); FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP );
frameCounter = lastLoadFrame = static_cast<unsigned int>(currFrameCounter); frameCounter = lastLoadFrame = static_cast<unsigned int>(currFrameCounter);
@ -1318,9 +1342,55 @@ class StateRecorder
lastState = snapIdx; lastState = snapIdx;
loadIndexReset = true; 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; 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) int getHeadIndex(void)
{ {
return ringHead; return ringHead;
@ -1347,6 +1417,10 @@ class StateRecorder
return ringBuf.size() * ringBuf[0]->size(); return ringBuf.size() * ringBuf[0]->size();
} }
size_t ringBufferSize(void)
{
return ringBuf.size();
}
static bool enabled; static bool enabled;
static int lastState; static int lastState;
private: private:
@ -1362,6 +1436,8 @@ class StateRecorder
int ringStart; int ringStart;
int ringBufSize; int ringBufSize;
int compressionLevel; int compressionLevel;
int loadPauseTime;
StateRecorderConfigData::PauseType pauseOnLoad;
unsigned int frameCounter; unsigned int frameCounter;
unsigned int framesPerSnap; unsigned int framesPerSnap;
unsigned int lastLoadFrame; unsigned int lastLoadFrame;
@ -1415,13 +1491,37 @@ bool FCEU_StateRecorderRunning(void)
return stateRecorder != nullptr; return stateRecorder != nullptr;
} }
int FCEU_StateRecorderLoadState(int snapIndex) int FCEU_StateRecorderGetMaxSnaps(void)
{ {
int size = 0;
if (stateRecorder != nullptr) 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) int FCEU_StateRecorderGetStateIndex(void)
@ -1429,6 +1529,28 @@ int FCEU_StateRecorderGetStateIndex(void)
return StateRecorder::lastState; 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) const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void)
{ {
return stateRecorderConfig; return stateRecorderConfig;
@ -1436,5 +1558,10 @@ const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void)
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig) int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig)
{ {
stateRecorderConfig = newConfig; stateRecorderConfig = newConfig;
if (stateRecorder != nullptr)
{
stateRecorder->loadConfig( stateRecorderConfig );
}
return 0; return 0;
} }

View File

@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#pragma once
#include <string> #include <string>
enum ENUM_SSLOADPARAMS enum ENUM_SSLOADPARAMS
@ -83,13 +84,37 @@ struct StateRecorderConfigData
{ {
float historyDurationMinutes; float historyDurationMinutes;
float timeBetweenSnapsMinutes; float timeBetweenSnapsMinutes;
int framesBetweenSnaps;
int compressionLevel; int compressionLevel;
int loadPauseTimeSeconds;
enum TimingType
{
FRAMES = 0,
TIME,
} timingMode;
enum PauseType
{
NO_PAUSE = 0,
TEMPORARY_PAUSE,
FULL_PAUSE,
} pauseOnLoad;
StateRecorderConfigData(void) StateRecorderConfigData(void)
{ {
framesBetweenSnaps = 60;
historyDurationMinutes = 15.0f; historyDurationMinutes = 15.0f;
timeBetweenSnapsMinutes = 3.0f / 60.0f; timeBetweenSnapsMinutes = 3.0f / 60.0f;
compressionLevel = 0; 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_StateRecorderRunning(void);
bool FCEU_StateRecorderIsEnabled(void); bool FCEU_StateRecorderIsEnabled(void);
void FCEU_StateRecorderSetEnabled(bool enabled); void FCEU_StateRecorderSetEnabled(bool enabled);
int FCEU_StateRecorderGetMaxSnaps(void);
int FCEU_StateRecorderGetNumSnapsSaved(void);
int FCEU_StateRecorderGetStateIndex(void); int FCEU_StateRecorderGetStateIndex(void);
int FCEU_StateRecorderLoadState(int snapIndex); int FCEU_StateRecorderLoadState(int snapIndex);
int FCEU_StateRecorderLoadPrevState(void);
int FCEU_StateRecorderLoadNextState(void);
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig); int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig);
const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void); 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; } void FCEUI_SetSnapshotAsName(std::string name) { AsSnapshotName = name; }
std::string FCEUI_GetSnapshotAsName() { return AsSnapshotName; } std::string FCEUI_GetSnapshotAsName() { return AsSnapshotName; }
static void FCEU_DrawPauseCountDown(uint8 *XBuf);
void FCEU_KillVirtualVideo(void) void FCEU_KillVirtualVideo(void)
{ {
if ( XBuf ) if ( XBuf )
@ -254,6 +256,7 @@ void FCEU_PutImage(void)
FCEU_DrawLagCounter(XBuf); FCEU_DrawLagCounter(XBuf);
FCEU_DrawNTSCControlBars(XBuf); FCEU_DrawNTSCControlBars(XBuf);
FCEU_DrawRecordingStatus(XBuf); FCEU_DrawRecordingStatus(XBuf);
FCEU_DrawPauseCountDown(XBuf);
ShowFPS(); ShowFPS();
} }
@ -771,3 +774,35 @@ void ShowFPS(void)
DrawTextTrans(XBuf + ((256 - ClipSidesOffset) - 40) + (FSettings.FirstSLine + 4) * 256, 256, (uint8*)fpsmsg, 0xA0); 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);
}
}
}
}