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

This commit is contained in:
Akeraiotitasoft 2023-06-06 11:18:32 -07:00
commit 26b2438365
73 changed files with 4631 additions and 883 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@
/output/auxlib.lua
/output/fceux.pdb
/output/7z.dll
/output/7z_64.dll
/output/fceux.exe
/output/fceux.exp
/output/fceux.lib

3
README
View File

@ -7,7 +7,7 @@ Updated By mjbudd77
https://fceux.com
Last Modified: March 24, 2022
Last Modified: April 30, 2023
Table of Contents
-----------------
@ -32,6 +32,7 @@ Table of Contents
* libx265 (optional) - H.265 video encoder for avi recording (recommended)
* ffmpeg libraries (optional) - for avi recording (recommended)
* - ffmpeg libraries used: libavcodec libavformat libavutil libswresample libswscale
* libarchive (optional) - for 7zip archive support (test with version 3.4.0)
* minizip
* zlib
* openGL

View File

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

View File

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

View File

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

View File

@ -22,6 +22,12 @@ if ( ${QHELP} )
add_definitions( -D_USE_QHELP )
endif()
if ( ${FCEU_PROFILER_ENABLE} )
message( STATUS "FCEU Profiler Enabled")
add_definitions( -D__FCEU_PROFILER_ENABLE__ )
endif()
if ( ${QT6} )
find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets ${QtHelpModule})
add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} )
@ -41,12 +47,15 @@ if(WIN32)
add_definitions( -DMSVC -D_CRT_SECURE_NO_WARNINGS )
add_definitions( -D__SDL__ -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS )
add_definitions( -DFCEUDEF_DEBUGGER )
add_definitions( -D_USE_LIBARCHIVE )
add_definitions( /wd4267 /wd4244 )
#add_definitions( /wd4018 ) # Integer comparison sign mismatch warnings
include_directories( ${SDL_INSTALL_PREFIX}/SDL2/include )
include_directories( ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/include )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/drivers/win/zlib )
set( OPENGL_LDFLAGS OpenGL::GL )
set( SDL2_LDFLAGS ${SDL_INSTALL_PREFIX}/SDL2/lib/x64/SDL2.lib )
set( LIBARCHIVE_LDFLAGS ${LIBARCHIVE_INSTALL_PREFIX}/libarchive/lib/archive.lib )
set( SYS_LIBS wsock32 ws2_32 vfw32 Htmlhelp )
set(APP_ICON_RESOURCES_WINDOWS ${CMAKE_SOURCE_DIR}/icons/fceux.rc )
@ -93,6 +102,12 @@ else(WIN32)
#endif()
add_definitions( -D__QT_DRIVER__ -DQT_DEPRECATED_WARNINGS )
if ( ${GPROF_ENABLE} )
add_definitions( -pg )
set( GPROF_LDFLAGS -pg )
message( STATUS "GNU Profiling Enabled" )
endif()
if ( ${ASAN_ENABLE} )
add_definitions( -fsanitize=address -fsanitize=bounds-strict )
add_definitions( -fsanitize=undefined -fno-sanitize=vptr )
@ -110,6 +125,13 @@ else(WIN32)
add_definitions( -D_SYSTEM_MINIZIP ${MINIZIP_CFLAGS} )
endif()
pkg_check_modules( LIBARCHIVE libarchive)
if ( ${LIBARCHIVE_FOUND} )
message( STATUS "Using System Libarchive Library ${LIBARCHIVE_VERSION}" )
add_definitions( -D_USE_LIBARCHIVE ${LIBARCHIVE_CFLAGS} )
endif()
pkg_check_modules( X264 x264)
if ( ${X264_FOUND} )
@ -293,6 +315,7 @@ set(SRC_CORE
${CMAKE_CURRENT_SOURCE_DIR}/nsf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/oldmovie.cpp
${CMAKE_CURRENT_SOURCE_DIR}/palette.cpp
${CMAKE_CURRENT_SOURCE_DIR}/profiler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ppu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/state.cpp
@ -504,6 +527,7 @@ set(SRC_CORE
${CMAKE_CURRENT_SOURCE_DIR}/utils/md5.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/memory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/mutex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utils/timeStamp.cpp
)
@ -526,6 +550,8 @@ set(SRC_DRIVERS_SDL
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerGL.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerSDL.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerQWidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleViewerInterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/InputConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/GamePadConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/FamilyKeyboard.cpp
@ -551,6 +577,7 @@ set(SRC_DRIVERS_SDL
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleUtilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleVideoConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/StateRecorderConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/iNesHeaderEditor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/SplashScreen.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/TraceLogger.cpp
@ -635,14 +662,15 @@ add_executable( ${APP_NAME} ${SOURCES} ../resources.qrc
${CMAKE_CURRENT_BINARY_DIR}/fceux_git_info.cpp)
endif()
target_link_libraries( ${APP_NAME} ${ASAN_LDFLAGS}
target_link_libraries( ${APP_NAME}
${ASAN_LDFLAGS} ${GPROF_LDFLAGS}
${${Qt}Widgets_LIBRARIES}
${${Qt}Help_LIBRARIES}
${${Qt}OpenGL_LIBRARIES}
${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS}
${SDL2_LDFLAGS}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES}
${MINIZIP_LDFLAGS} ${ZLIB_LIBRARIES} ${LIBARCHIVE_LDFLAGS}
${LUA_LDFLAGS} ${X264_LDFLAGS} ${X265_LDFLAGS} ${LIBAV_LDFLAGS}
${SYS_LIBS}
)

View File

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

View File

@ -16,11 +16,11 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Very complicated homebrew multicart mapper with.
* The code is so obscured and weird because it's ported from Verilog CPLD source code:
*
* Very complicated homebrew multicart mapper with.
* The code is so obscured and weird because it's ported from Verilog CPLD source code:
* https://github.com/ClusterM/coolgirl-famicom-multicart/blob/master/CoolGirl_mappers.vh
*
*
* Range: $5000-$5FFF
*
* Mask: $5007
@ -103,14 +103,12 @@ const uint32 FLASH_SECTOR_SIZE = 128 * 1024;
const int ROM_CHIP = 0x00;
const int WRAM_CHIP = 0x10;
const int FLASH_CHIP = 0x11;
const int CHR_RAM_CHIP = 0x12;
const int CFI_CHIP = 0x13;
static int CHR_SIZE = 0;
static uint32 WRAM_SIZE = 0;
static uint8 *WRAM = NULL;
static uint32 CHR_RAM_SIZE = 0;
static uint8 *CHR_RAM;
static uint8 *SAVE_FLASH = NULL;
static uint8* WRAM = NULL;
static uint8* SAVE_FLASH = NULL;
static uint8* CFI;
static uint8 sram_enabled = 0;
@ -388,75 +386,75 @@ static void COOLGIRL_Sync_CHR(void) {
int chr_shift_left = 0;
// enable or disable writes to CHR RAM, setup CHR mask
SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_RAM_SIZE - 1)) + 1, can_write_chr);
SetupCartCHRMapping(0, UNIFchrrama, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_SIZE - 1)) + 1, can_write_chr);
switch (chr_mode & 7)
{
default:
case 0:
setchr8r(0x12, chr_bank_a >> 3 >> chr_shift_right << chr_shift_left);
setchr8(chr_bank_a >> 3 >> chr_shift_right << chr_shift_left);
break;
case 1:
setchr4r(0x12, 0x0000, mapper_163_latch >> chr_shift_right << chr_shift_left);
setchr4r(0x12, 0x1000, mapper_163_latch >> chr_shift_right << chr_shift_left);
setchr4(0x0000, mapper_163_latch >> chr_shift_right << chr_shift_left);
setchr4(0x1000, mapper_163_latch >> chr_shift_right << chr_shift_left);
break;
case 2:
setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
TKSMIR[0] = TKSMIR[1] = chr_bank_a;
setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
TKSMIR[2] = TKSMIR[3] = chr_bank_c;
setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift_right << chr_shift_left);
setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left);
TKSMIR[4] = chr_bank_e;
setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift_right << chr_shift_left);
setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left);
TKSMIR[5] = chr_bank_f;
setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift_right << chr_shift_left);
setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left);
TKSMIR[6] = chr_bank_g;
setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left);
setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left);
TKSMIR[7] = chr_bank_h;
break;
case 3:
setchr1r(0x12, 0x0000, chr_bank_e >> chr_shift_right << chr_shift_left);
setchr1(0x0000, chr_bank_e >> chr_shift_right << chr_shift_left);
TKSMIR[0] = chr_bank_e;
setchr1r(0x12, 0x0400, chr_bank_f >> chr_shift_right << chr_shift_left);
setchr1(0x0400, chr_bank_f >> chr_shift_right << chr_shift_left);
TKSMIR[1] = chr_bank_f;
setchr1r(0x12, 0x0800, chr_bank_g >> chr_shift_right << chr_shift_left);
setchr1(0x0800, chr_bank_g >> chr_shift_right << chr_shift_left);
TKSMIR[2] = chr_bank_g;
setchr1r(0x12, 0x0C00, chr_bank_h >> chr_shift_right << chr_shift_left);
setchr1(0x0C00, chr_bank_h >> chr_shift_right << chr_shift_left);
TKSMIR[3] = chr_bank_h;
setchr2r(0x12, 0x1000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x1000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
TKSMIR[4] = TKSMIR[5] = chr_bank_a;
setchr2r(0x12, 0x1800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x1800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
TKSMIR[6] = TKSMIR[7] = chr_bank_c;
break;
case 4:
setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left);
setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left);
break;
case 5:
if (!ppu_latch0)
setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left);
else
setchr4r(0x12, 0x0000, chr_bank_b >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x0000, chr_bank_b >> 2 >> chr_shift_right << chr_shift_left);
if (!ppu_latch1)
setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left);
else
setchr4r(0x12, 0x1000, chr_bank_f >> 2 >> chr_shift_right << chr_shift_left);
setchr4(0x1000, chr_bank_f >> 2 >> chr_shift_right << chr_shift_left);
break;
case 6:
setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
setchr2r(0x12, 0x1000, chr_bank_e >> 1 >> chr_shift_right << chr_shift_left);
setchr2r(0x12, 0x1800, chr_bank_g >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x1000, chr_bank_e >> 1 >> chr_shift_right << chr_shift_left);
setchr2(0x1800, chr_bank_g >> 1 >> chr_shift_right << chr_shift_left);
break;
case 7:
setchr1r(0x12, 0x0000, chr_bank_a >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x0400, chr_bank_b >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x0800, chr_bank_c >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x0C00, chr_bank_d >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift_right << chr_shift_left);
setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left);
setchr1(0x0000, chr_bank_a >> chr_shift_right << chr_shift_left);
setchr1(0x0400, chr_bank_b >> chr_shift_right << chr_shift_left);
setchr1(0x0800, chr_bank_c >> chr_shift_right << chr_shift_left);
setchr1(0x0C00, chr_bank_d >> chr_shift_right << chr_shift_left);
setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left);
setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left);
setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left);
setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left);
break;
}
}
@ -468,10 +466,10 @@ static void COOLGIRL_Sync_Mirroring(void) {
setmirror((mirroring < 2) ? (mirroring ^ 1) : mirroring);
}
else { // four screen mode
vnapage[0] = CHR_RAM + 0x3F000;
vnapage[1] = CHR_RAM + 0x3F400;
vnapage[2] = CHR_RAM + 0x3F800;
vnapage[3] = CHR_RAM + 0x3FC00;
vnapage[0] = UNIFchrrama + 0x3F000;
vnapage[1] = UNIFchrrama + 0x3F400;
vnapage[2] = UNIFchrrama + 0x3F800;
vnapage[3] = UNIFchrrama + 0x3FC00;
}
}
@ -509,9 +507,8 @@ static DECLFW(COOLGIRL_Flash_Write) {
uint32 sector_address = sector * FLASH_SECTOR_SIZE;
for (uint32 i = sector_address; i < sector_address + FLASH_SECTOR_SIZE; i++)
SAVE_FLASH[i % SAVE_FLASH_SIZE] = 0xFF;
FCEU_printf("Flash sector #%d is erased: 0x%08x - 0x%08x.\n", sector, sector_address, sector_address + FLASH_SECTOR_SIZE - 1);
flash_state = 0;
}
}
// write byte
if ((flash_state == 4) &&
@ -557,7 +554,6 @@ static DECLFW(COOLGIRL_WRITE) {
if (A >= 0x5000 && A < 0x6000 && !lockout)
{
//FCEU_printf("Write: %02x => %04x\n", V, A);
switch (A & 7)
{
case 0:
@ -573,7 +569,6 @@ static DECLFW(COOLGIRL_WRITE) {
// {chr_mask[18], prg_mask[20:14]} = cpu_data_in[7:0];
SET_BITS(chr_mask, "18", V, "7");
SET_BITS(prg_mask, "20:14", V, "6:0");
//FCEU_printf("REG_prg_mask: %02x\n", REG_prg_mask);
break;
case 3:
// {prg_mode[2:0], chr_bank_a[7:3]} = cpu_data_in[7:0];
@ -886,7 +881,7 @@ static DECLFW(COOLGIRL_WRITE) {
if (mapper == 0b000100)
{
// prg_bank_a[5:1] = cpu_data_in[4:0];
SET_BITS(chr_bank_a, "5:1", V, "4:0");
SET_BITS(prg_bank_a, "5:1", V, "4:0");
// mirroring = { 1'b0, ~cpu_data_in[7]};
mirroring = get_bits(V, "7") ^ 1;
}
@ -1176,7 +1171,6 @@ static DECLFW(COOLGIRL_WRITE) {
// Mapper #1 - MMC1
/*
r0 - load register
flag0 - 16KB of SRAM (SOROM)
*/
if (mapper == 0b010000)
@ -1230,7 +1224,7 @@ static DECLFW(COOLGIRL_WRITE) {
SET_BITS(chr_bank_e, "6:2", mmc1_load_register, "5:1");
break;
case 0b11: // 2'b11
// prg_bank_a[4:1] = r0[4:1];
// prg_bank_a[4:1] = mmc1_load_register[4:1];
SET_BITS(prg_bank_a, "4:1", mmc1_load_register, "4:1");
// sram_enabled = ~mmc1_load_register[5];
sram_enabled = get_bits(mmc1_load_register, "5") ^ 1;
@ -1397,9 +1391,6 @@ static DECLFW(COOLGIRL_WRITE) {
}
// Mapper #112
/*
r0[2:0] - internal register
*/
if (mapper == 0b010101)
{
switch (get_bits(A, "14:13"))
@ -1464,6 +1455,7 @@ static DECLFW(COOLGIRL_WRITE) {
case 0b1100: // 4'b1100: if (flags[0]) mirroring = {1'b0, cpu_data_in[6]}; // $E000, mirroring, for mapper #48
if (flags & 1) // 48
mirroring = get_bits(V, "6"); // mirroring = cpu_data_in[6];
break;
case 0b1000: // 4'b1000: irq_scanline_latch = ~cpu_data_in; // $C000, IRQ latch
mmc3_irq_latch = set_bits(mmc3_irq_latch, "7:0", get_bits(V, "7:0") ^ 0b11111111);
break;
@ -1813,7 +1805,7 @@ static DECLFW(COOLGIRL_WRITE) {
SET_BITS(chr_bank_c, "8:1", V, "7:0"); break; // 3'b001: chr_bank_c[8:1] <= cpu_data_in[7:0];
case 0b110:
SET_BITS(chr_bank_e, "8:1", V, "7:0"); break; // 3'b110: chr_bank_e[8:1] <= cpu_data_in[7:0];
case 0b111:
case 0b111:
SET_BITS(chr_bank_g, "8:1", V, "7:0"); break; // 3'b111: chr_bank_g[8:1] <= cpu_data_in[7:0];
}
}
@ -1963,32 +1955,19 @@ static void COOLGIRL_CpuCounter(int a) {
// Mapper #23 - VRC4
if (vrc4_irq_control & 2) // if (ENABLE_MAPPER_021_022_023_025 & ENABLE_VRC4_INTERRUPTS & (vrc4_irq_control[1]))
{
// Cycle mode without prescaler is not used by any games? It's missed in fceux source code.
if (vrc4_irq_control & 4) // if (vrc4_irq_control[2]) // cycle mode
vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler
// if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114)
// || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113
if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113))
{
FCEU_PrintError("Cycle IRQ mode is not supported, please report to Cluster");
vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; // just count IRQ value
if (vrc4_irq_value == 0) // if (carry)
vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0;
vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1;
if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00;
vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1;
if (vrc4_irq_value == 0) // f (carry)
{
X6502_IRQBegin(FCEU_IQEXT); // vrc4_irq_out = 1;
vrc4_irq_value = vrc4_irq_latch; // vrc4_irq_value[7:0] = vrc4_irq_latch[7:0];
}
}
else {
vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler
// if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114)
// || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113
if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113))
{
vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0;
vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1;
if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00;
vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1;
if (vrc4_irq_value == 0) // f (carry)
{
X6502_IRQBegin(FCEU_IQEXT);
vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0];
}
X6502_IRQBegin(FCEU_IQEXT);
vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0];
}
}
}
@ -2238,15 +2217,13 @@ static void COOLGIRL_Power(void) {
}
static void COOLGIRL_Close(void) {
if (CHR_RAM)
FCEU_gfree(CHR_RAM);
if (WRAM)
FCEU_gfree(WRAM);
if (SAVE_FLASH)
FCEU_gfree(SAVE_FLASH);
if (CFI)
FCEU_gfree(CFI);
CHR_RAM = WRAM = SAVE_FLASH = CFI = NULL;
WRAM = SAVE_FLASH = CFI = NULL;
}
static void COOLGIRL_Restore(int version) {
@ -2256,12 +2233,8 @@ static void COOLGIRL_Restore(int version) {
#define ExState(var, varname) AddExState(&var, sizeof(var), 0, varname)
void COOLGIRL_Init(CartInfo *info) {
CHR_RAM_SIZE = info->ines2 ? (info->vram_size + info->battery_vram_size) : (512 * 1024);
CHR_RAM = (uint8*)FCEU_gmalloc(CHR_RAM_SIZE);
memset(CHR_RAM, 0, CHR_RAM_SIZE);
SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, CHR_RAM_SIZE, 0);
AddExState(CHR_RAM, sizeof(CHR_RAM_SIZE), 0, "CHR_");
void COOLGIRL_Init(CartInfo* info) {
CHR_SIZE = info->vram_size ? info->vram_size /* NES 2.0 */ : 256 * 1024 /* UNIF, fixed */;
WRAM_SIZE = info->ines2 ? (info->wram_size + info->battery_wram_size) : (32 * 1024);
if (WRAM_SIZE > 0) {
@ -2271,7 +2244,7 @@ void COOLGIRL_Init(CartInfo *info) {
AddExState(WRAM, 32 * 1024, 0, "SRAM");
if (info->battery)
{
info->addSaveGameBuf( WRAM, 32 * 1024);
info->addSaveGameBuf(WRAM, 32 * 1024);
}
}
@ -2280,7 +2253,7 @@ void COOLGIRL_Init(CartInfo *info) {
SAVE_FLASH = (uint8*)FCEU_gmalloc(SAVE_FLASH_SIZE);
SetupCartPRGMapping(FLASH_CHIP, SAVE_FLASH, SAVE_FLASH_SIZE, 1);
AddExState(SAVE_FLASH, SAVE_FLASH_SIZE, 0, "SAVF");
info->addSaveGameBuf( SAVE_FLASH, SAVE_FLASH_SIZE );
info->addSaveGameBuf(SAVE_FLASH, SAVE_FLASH_SIZE);
}
CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2);

View File

@ -26,6 +26,7 @@ static uint16 addrreg0=0, addrreg1=0;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE=0;
static void (*WSync)(void) = nullptr;
static uint8 submapper;
static DECLFW(LatchWrite) {
// FCEU_printf("bs %04x %02x\n",A,V);
@ -68,6 +69,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad
info->Power = LatchPower;
info->Close = LatchClose;
GameStateRestore = StateRestore;
submapper = info->submapper;
if(info->ines2)
if(info->battery_wram_size + info->wram_size > 0)
wram = 1;
@ -295,7 +297,11 @@ static void M78Sync() {
setprg16(0x8000, (latche & 7));
setprg16(0xc000, ~0);
setchr8(latche >> 4);
setmirror(MI_0 + ((latche >> 3) & 1));
if (submapper == 3) {
setmirror((latche >> 3) & 1);
} else {
setmirror(MI_0 + ((latche >> 3) & 1));
}
}
void Mapper78_Init(CartInfo *info) {

View File

@ -1136,20 +1136,73 @@ void Mapper198_Init(CartInfo *info) {
info->Power = M195Power;
}
// ---------------------------- Mapper 205 ------------------------------
// GN-45 BOARD
/* ---------------------------- Mapper 205 ------------------------------ */
/* UNIF boardname BMC-JC-016-2
https://wiki.nesdev.com/w/index.php/INES_Mapper_205 */
/* 2023-02 : Update reg write logic and add solder pad */
static void M205PW(uint32 A, uint8 V) {
// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü 1F + àïïàðàòíûé ïåðåêëþ÷àòåëü íà øèíå àäðåñà
setprg8(A, (V & 0x0f) | EXPREGS[0]);
uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x0F : 0x1F);
if (PRGsize[1]) { // split-rom variant
setprg8r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank);
} else {
setprg8(A, EXPREGS[0] << 4 | bank);
}
}
static void M205CW(uint32 A, uint8 V) {
// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü FF
uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x7F : 0xFF);
if (CHRsize[1]) { // split-rom variant
setchr1r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank);
} else {
setchr1(A, (EXPREGS[0] << 7) | bank);
}
}
static DECLFW(M205Write) {
EXPREGS[0] = V & 3;
if (V & 1) {
EXPREGS[0] |= EXPREGS[1];
}
CartBW(A, V);
FixMMC3PRG(MMC3_cmd);
FixMMC3CHR(MMC3_cmd);
}
static void M205Reset(void) {
EXPREGS[0] = 0;
EXPREGS[1] ^= 2; /* solder pad */
MMC3RegReset();
}
static void M205Power(void) {
EXPREGS[0] = EXPREGS[1] = 0;
GenMMC3Power();
SetWriteHandler(0x6000, 0x7FFF, M205Write);
}
void Mapper205_Init(CartInfo *info) {
GenMMC3_Init(info, 256, 128, 0, 0);
pwrap = M205PW;
cwrap = M205CW;
info->Power = M205Power;
info->Reset = M205Reset;
AddExState(EXPREGS, 2, 0, "EXPR");
}
/* --------------------------- GN-45 BOARD ------------------------------ */
/* Mapper 361 and 366, previously assigned as Mapper 205 */
static void GN45PW(uint32 A, uint8 V) {
setprg8(A, (V & 0x0f) | EXPREGS[0]);
}
static void GN45CW(uint32 A, uint8 V) {
setchr1(A, (V & 0x7F) | (EXPREGS[0] << 3));
}
static DECLFW(M205Write0) {
static DECLFW(GN45Write0) {
if (EXPREGS[2] == 0) {
EXPREGS[0] = A & 0x30;
EXPREGS[2] = A & 0x80;
@ -1159,7 +1212,7 @@ static DECLFW(M205Write0) {
CartBW(A, V);
}
static DECLFW(M205Write1) {
static DECLFW(GN45Write1) {
if (EXPREGS[2] == 0) {
EXPREGS[0] = V & 0x30;
FixMMC3PRG(MMC3_cmd);
@ -1168,23 +1221,23 @@ static DECLFW(M205Write1) {
CartBW(A, V);
}
static void M205Reset(void) {
static void GN45Reset(void) {
EXPREGS[0] = EXPREGS[2] = 0;
MMC3RegReset();
}
static void M205Power(void) {
static void GN45Power(void) {
GenMMC3Power();
SetWriteHandler(0x6000, 0x6fff, M205Write0);
SetWriteHandler(0x7000, 0x7fff, M205Write1); // OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein
SetWriteHandler(0x6000, 0x6fff, GN45Write0);
SetWriteHandler(0x7000, 0x7fff, GN45Write1); /* OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein */
}
void Mapper205_Init(CartInfo *info) {
void GN45_Init(CartInfo *info) {
GenMMC3_Init(info, 128, 128, 8, 0);
pwrap = M205PW;
cwrap = M205CW;
info->Power = M205Power;
info->Reset = M205Reset;
pwrap = GN45PW;
cwrap = GN45CW;
info->Power = GN45Power;
info->Reset = GN45Reset;
AddExState(EXPREGS, 1, 0, "EXPR");
}

View File

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

View File

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

View File

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

View File

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

View File

@ -756,7 +756,6 @@ int debugSymbolTable_t::loadGameSymbols(void)
{
int nPages, pageSize, romSize = 0x10000;
this->save();
this->clear();
if ( GameInfo != nullptr )

View File

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

View File

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

View File

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

View File

@ -2580,6 +2580,7 @@ int FCEUD_AviGetFormatOpts( std::vector <std::string> &formatList )
AviRecordDiskThread_t::AviRecordDiskThread_t( QObject *parent )
: QThread(parent)
{
setObjectName( QString("AviRecordDiskThread") );
}
//----------------------------------------------------
AviRecordDiskThread_t::~AviRecordDiskThread_t(void)

View File

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

View File

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

View File

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

View File

@ -172,7 +172,7 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent)
loadDisplayViews();
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
dbgWin = this;
@ -373,7 +373,7 @@ void ConsoleDebugger::ld65ImportDebug(void)
debugSymbolTable.ld65LoadDebugFile( filename.toStdString().c_str() );
queueUpdate();
queueUpdate(QAsmView::UPDATE_ALL);
return;
}
@ -879,6 +879,16 @@ QMenuBar *ConsoleDebugger::buildMenuBar(void)
symMenu->addAction(act);
// Symbols -> Save
act = new QAction(tr("&Save"), this);
//act->setShortcut(QKeySequence( tr("F7") ) );
act->setStatusTip(tr("&Save"));
//act->setCheckable(true);
//act->setChecked( break_on_unlogged_data );
connect( act, SIGNAL(triggered(void)), this, SLOT(saveSymbolsCB(void)) );
symMenu->addAction(act);
symMenu->addSeparator();
// Symbols -> Symbolic Debug
@ -1834,28 +1844,26 @@ void ConsoleDebugger::selBmAddrChanged(const QString &txt)
//printf("selBmAddrVal = %04X\n", selBmAddrVal );
}
//----------------------------------------------------------------------------
void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool forceAccept )
DebuggerBreakpointEditor::DebuggerBreakpointEditor(int editIndex, watchpointinfo *wpIn, QWidget *parent)
: QDialog(parent)
{
int ret;
QDialog dialog(this);
editIdx = editIndex;
wp = wpIn;
QHBoxLayout *hbox;
QVBoxLayout *mainLayout, *vbox;
QLabel *lbl;
QLineEdit *addr1, *addr2, *cond, *name;
QCheckBox *forbidChkBox, *rbp, *wbp, *xbp, *ebp;
QGridLayout *grid;
QFrame *frame;
QGroupBox *gbox;
QPushButton *okButton, *cancelButton;
QRadioButton *cpu_radio, *ppu_radio, *oam_radio, *rom_radio;
if ( editIdx >= 0 )
{
dialog.setWindowTitle( tr("Edit Breakpoint") );
setWindowTitle( tr("Edit Breakpoint") );
}
else
{
dialog.setWindowTitle( tr("Add Breakpoint") );
setWindowTitle( tr("Add Breakpoint") );
}
hbox = new QHBoxLayout();
@ -1874,6 +1882,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
hbox->addWidget( lbl );
hbox->addWidget( addr2 );
connect( addr1, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &)));
connect( addr2, SIGNAL(textChanged(const QString &)), this, SLOT(addressTextChanged(const QString &)));
forbidChkBox = new QCheckBox( tr("Forbid") );
hbox->addWidget( forbidChkBox );
@ -1906,6 +1917,11 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
rom_radio = new QRadioButton( tr("ROM") );
cpu_radio->setChecked(true);
connect( cpu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( ppu_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( oam_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
connect( rom_radio, SIGNAL(toggled(bool)), this, SLOT(typeChanged(bool)));
gbox->setLayout( hbox );
hbox->addWidget( cpu_radio );
hbox->addWidget( ppu_radio );
@ -1917,6 +1933,9 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
mainLayout->addLayout( grid );
lbl = new QLabel( tr("Condition") );
cond = new QLineEdit();
condValid = true;
connect( cond, SIGNAL(textChanged(const QString &)), this, SLOT(conditionTextChanged(const QString &)));
grid->addWidget( lbl, 0, 0 );
grid->addWidget( cond, 0, 1 );
@ -1928,15 +1947,17 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
grid->addWidget( name, 1, 1 );
hbox = new QHBoxLayout();
msgLbl = new QLabel();
okButton = new QPushButton( tr("OK") );
cancelButton = new QPushButton( tr("Cancel") );
mainLayout->addLayout( hbox );
hbox->addWidget( cancelButton );
hbox->addWidget( okButton );
hbox->addWidget( msgLbl, 5 );
hbox->addWidget( cancelButton, 1 );
hbox->addWidget( okButton, 1 );
connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) );
connect( okButton, SIGNAL(clicked(void)), this, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), this, SLOT(reject(void)) );
okButton->setIcon( style()->standardIcon( QStyle::SP_DialogOkButton ) );
cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) );
@ -2036,99 +2057,270 @@ void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp, bool fo
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 )
{
dialog->loadBreakpoint();
dialog->deleteLater();
ret = QDialog::Accepted;
}
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;
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 );
}
}
bpListUpdate( false );
}
}
//----------------------------------------------------------------------------
@ -2439,7 +2631,7 @@ static void DeleteBreak(int sel)
if (watchpoint[sel].cond)
{
freeTree(watchpoint[sel].cond);
delete watchpoint[sel].cond;
}
if (watchpoint[sel].condText)
{
@ -2488,7 +2680,7 @@ void debuggerClearAllBreakpoints(void)
{
if (watchpoint[i].cond)
{
freeTree(watchpoint[i].cond);
delete watchpoint[i].cond;
}
if (watchpoint[i].condText)
{
@ -2499,7 +2691,7 @@ void debuggerClearAllBreakpoints(void)
free(watchpoint[i].desc);
}
watchpoint[i].address = 0;
watchpoint[i].address = 0;
watchpoint[i].endaddress = 0;
watchpoint[i].flags = 0;
watchpoint[i].cond = 0;
@ -2753,6 +2945,13 @@ void ConsoleDebugger::reloadSymbolsCB(void)
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void ConsoleDebugger::saveSymbolsCB(void)
{
FCEU_WRAPPER_LOCK();
debugSymbolTable.save();
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void ConsoleDebugger::pcSetPlaceTop(void)
{
asmView->setPC_placement( 0 );
@ -2898,7 +3097,7 @@ void ConsoleDebugger::debugStepBackCB(void)
{
FCEU_WRAPPER_LOCK();
FCEUD_TraceLoggerBackUpInstruction();
updateWindowData();
updateWindowData(QAsmView::UPDATE_ALL);
hexEditorUpdateMemoryValues(true);
hexEditorRequestUpdateAll();
lastBpIdx = BREAK_TYPE_STEP;
@ -3093,8 +3292,7 @@ void ConsoleDebugger::seekPCCB (void)
setRegsFromEntry();
//updateAllDebugWindows();
}
windowUpdateReq = true;
//asmView->scrollToPC();
windowUpdateReq = QAsmView::UPDATE_ALL;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::openChangePcDialog(void)
@ -3148,7 +3346,7 @@ void ConsoleDebugger::openChangePcDialog(void)
{
X.PC = sbox->value();
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
}
}
//----------------------------------------------------------------------------
@ -4142,20 +4340,25 @@ void ConsoleDebugger::updateRegisterView(void)
ppuScrollY->setText( tr(stmp) );
}
//----------------------------------------------------------------------------
void ConsoleDebugger::updateWindowData(void)
void ConsoleDebugger::updateWindowData(enum QAsmView::UpdateType type)
{
asmView->updateAssemblyView();
asmView->scrollToPC();
if (type == QAsmView::UPDATE_ALL)
{
asmView->updateAssemblyView();
asmView->scrollToPC();
updateRegisterView();
} else if (type == QAsmView::UPDATE_NO_SCROLL)
{
asmView->updateAssemblyView();
updateRegisterView();
}
updateRegisterView();
windowUpdateReq = false;
windowUpdateReq = QAsmView::UPDATE_NONE;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::queueUpdate(void)
void ConsoleDebugger::queueUpdate(enum QAsmView::UpdateType type)
{
windowUpdateReq = true;
windowUpdateReq = type;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::updatePeriodic(void)
@ -4173,10 +4376,10 @@ void ConsoleDebugger::updatePeriodic(void)
bpNotifyReq = false;
}
if ( windowUpdateReq )
if ( windowUpdateReq != QAsmView::UPDATE_NONE )
{
FCEU_WRAPPER_LOCK();
updateWindowData();
updateWindowData(windowUpdateReq);
FCEU_WRAPPER_UNLOCK();
}
asmView->update();
@ -4346,7 +4549,7 @@ void ConsoleDebugger::breakPointNotify( int bpNum )
}
}
windowUpdateReq = true;
windowUpdateReq = QAsmView::UPDATE_ALL;
}
//----------------------------------------------------------------------------
void ConsoleDebugger::hbarChanged(int value)
@ -4461,7 +4664,6 @@ void debuggerWindowSetFocus(bool val)
{
dbgWin->activateWindow();
dbgWin->raise();
dbgWin->setFocus();
}
}
//----------------------------------------------------------------------------
@ -4470,11 +4672,11 @@ bool debuggerWaitingAtBreakpoint(void)
return waitingAtBp;
}
//----------------------------------------------------------------------------
void updateAllDebuggerWindows( void )
void updateAllDebuggerWindows( enum QAsmView::UpdateType type )
{
if ( dbgWin )
{
dbgWin->queueUpdate();
dbgWin->queueUpdate(type);
}
}
//----------------------------------------------------------------------------

View File

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

View File

@ -85,8 +85,9 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent)
driverSelect = new QComboBox();
driverSelect->addItem( tr("OpenGL"), 0 );
driverSelect->addItem( tr("SDL"), 1 );
driverSelect->addItem( tr("OpenGL"), ConsoleViewerBase::VIDEO_DRIVER_OPENGL );
driverSelect->addItem( tr("SDL"), ConsoleViewerBase::VIDEO_DRIVER_SDL );
driverSelect->addItem( tr("QPainter"), ConsoleViewerBase::VIDEO_DRIVER_QPAINTER );
hbox1 = new QHBoxLayout();
@ -232,15 +233,10 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent)
if ( consoleWindow )
{
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
autoScaleCbx->setChecked( consoleWindow->viewport_GL->getAutoScaleOpt() );
aspectCbx->setChecked( consoleWindow->viewport_GL->getForceAspectOpt() );
}
else if ( consoleWindow->viewport_SDL )
{
autoScaleCbx->setChecked( consoleWindow->viewport_SDL->getAutoScaleOpt() );
aspectCbx->setChecked( consoleWindow->viewport_SDL->getForceAspectOpt() );
autoScaleCbx->setChecked( consoleWindow->viewport_Interface->getAutoScaleOpt() );
aspectCbx->setChecked( consoleWindow->viewport_Interface->getForceAspectOpt() );
}
}
@ -305,15 +301,10 @@ ConsoleVideoConfDialog_t::ConsoleVideoConfDialog_t(QWidget *parent)
if ( consoleWindow )
{
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
xScaleBox->setValue( consoleWindow->viewport_GL->getScaleX() );
yScaleBox->setValue( consoleWindow->viewport_GL->getScaleY() );
}
else if ( consoleWindow->viewport_SDL )
{
xScaleBox->setValue( consoleWindow->viewport_SDL->getScaleX() );
yScaleBox->setValue( consoleWindow->viewport_SDL->getScaleY() );
xScaleBox->setValue( consoleWindow->viewport_Interface->getScaleX() );
yScaleBox->setValue( consoleWindow->viewport_Interface->getScaleY() );
}
}
@ -594,13 +585,9 @@ void ConsoleVideoConfDialog_t::updateReadouts(void)
w = consoleWindow->size();
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
v = consoleWindow->viewport_GL->size();
}
else if ( consoleWindow->viewport_SDL )
{
v = consoleWindow->viewport_SDL->size();
v = consoleWindow->viewport_Interface->size();
}
sprintf( stmp, "%i x %i ", w.width(), w.height() );
@ -726,13 +713,9 @@ void ConsoleVideoConfDialog_t::openGL_linearFilterChanged( int value )
if ( consoleWindow != NULL )
{
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
consoleWindow->viewport_GL->setLinearFilterEnable( opt );
}
if ( consoleWindow->viewport_SDL )
{
consoleWindow->viewport_SDL->setLinearFilterEnable( opt );
consoleWindow->viewport_Interface->setLinearFilterEnable( opt );
}
}
}
@ -745,13 +728,9 @@ void ConsoleVideoConfDialog_t::autoScaleChanged( int value )
if ( consoleWindow != NULL )
{
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
consoleWindow->viewport_GL->setAutoScaleOpt( opt );
}
if ( consoleWindow->viewport_SDL )
{
consoleWindow->viewport_SDL->setAutoScaleOpt( opt );
consoleWindow->viewport_Interface->setAutoScaleOpt( opt );
}
}
}
@ -832,9 +811,9 @@ void ConsoleVideoConfDialog_t::vsync_changed( int value )
//consoleWindow->viewport_GL->setVsyncEnable( opt );
consoleWindow->loadVideoDriver( 0, true );
}
if ( consoleWindow->viewport_SDL )
else if ( consoleWindow->viewport_Interface )
{
consoleWindow->viewport_SDL->setVsyncEnable( opt );
consoleWindow->viewport_Interface->setVsyncEnable( opt );
}
}
}
@ -1094,15 +1073,10 @@ QSize ConsoleVideoConfDialog_t::calcNewScreenSize(void)
w = consoleWindow->size();
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
v = consoleWindow->viewport_GL->size();
aspectRatio = consoleWindow->viewport_GL->getAspectRatio();
}
else if ( consoleWindow->viewport_SDL )
{
v = consoleWindow->viewport_SDL->size();
aspectRatio = consoleWindow->viewport_SDL->getAspectRatio();
v = consoleWindow->viewport_Interface->size();
aspectRatio = consoleWindow->viewport_Interface->getAspectRatio();
}
dw = w.width() - v.width();
@ -1167,21 +1141,13 @@ void ConsoleVideoConfDialog_t::applyChanges( void )
g_config->setOption("SDL.WinSizeX", s.width() );
g_config->setOption("SDL.WinSizeY", s.height() );
if ( consoleWindow->viewport_GL )
if ( consoleWindow->viewport_Interface )
{
consoleWindow->viewport_GL->setLinearFilterEnable( gl_LF_chkBox->isChecked() );
consoleWindow->viewport_GL->setForceAspectOpt( aspectCbx->isChecked() );
consoleWindow->viewport_GL->setAutoScaleOpt( autoScaleCbx->isChecked() );
consoleWindow->viewport_GL->setScaleXY( xscale, yscale );
consoleWindow->viewport_GL->reset();
}
if ( consoleWindow->viewport_SDL )
{
consoleWindow->viewport_SDL->setLinearFilterEnable( gl_LF_chkBox->isChecked() );
consoleWindow->viewport_SDL->setForceAspectOpt( aspectCbx->isChecked() );
consoleWindow->viewport_SDL->setAutoScaleOpt( autoScaleCbx->isChecked() );
consoleWindow->viewport_SDL->setScaleXY( xscale, yscale );
consoleWindow->viewport_SDL->reset();
consoleWindow->viewport_Interface->setLinearFilterEnable( gl_LF_chkBox->isChecked() );
consoleWindow->viewport_Interface->setForceAspectOpt( aspectCbx->isChecked() );
consoleWindow->viewport_Interface->setAutoScaleOpt( autoScaleCbx->isChecked() );
consoleWindow->viewport_Interface->setScaleXY( xscale, yscale );
consoleWindow->viewport_Interface->reset();
}
if ( !consoleWindow->isFullScreen() && !consoleWindow->isMaximized() )

View File

@ -92,7 +92,7 @@ ConsoleViewGL_t::ConsoleViewGL_t(QWidget *parent)
if ( localBuf )
{
memset( localBuf, 0, localBufSize );
memset32( localBuf, alphaMask, localBufSize );
}
vsyncEnabled = true;
@ -136,6 +136,8 @@ ConsoleViewGL_t::ConsoleViewGL_t(QWidget *parent)
ConsoleViewGL_t::~ConsoleViewGL_t(void)
{
//printf("Destroying GL Viewport\n");
if ( localBuf )
{
free( localBuf ); localBuf = NULL;
@ -502,7 +504,7 @@ void ConsoleViewGL_t::transfer2LocalBuffer(void)
}
else
{
memcpy( localBuf, src, cpSize );
copyPixels32( dest, src, cpSize, alphaMask);
}
}

View File

@ -10,7 +10,9 @@
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions
#include "Qt/ConsoleViewerInterface.h"
class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions, public ConsoleViewerBase
{
Q_OBJECT
@ -20,6 +22,8 @@ class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions
int init(void);
void reset(void);
void queueRedraw(void){ update(); };
int driver(void){ return VIDEO_DRIVER_OPENGL; };
void transfer2LocalBuffer(void);
@ -41,6 +45,13 @@ class ConsoleViewGL_t : public QOpenGLWidget, protected QOpenGLFunctions
void screenChanged(QScreen *scr);
void setBgColor( QColor &c );
void setCursor(const QCursor &c){ QOpenGLWidget::setCursor(c); };
void setCursor( Qt::CursorShape s ){ QOpenGLWidget::setCursor(s); };
QSize size(void){ return QOpenGLWidget::size(); };
QCursor cursor(void){ return QOpenGLWidget::cursor(); };
void setMinimumSize(const QSize &s){ return QOpenGLWidget::setMinimumSize(s); };
void setMaximumSize(const QSize &s){ return QOpenGLWidget::setMaximumSize(s); };
protected:
void initializeGL(void);

View File

@ -0,0 +1,28 @@
// ConsoleViewerInterface.cpp
//
#include "Qt/ConsoleViewerInterface.h"
//----------------------------------------------------------
void ConsoleViewerBase::memset32( void *buf, uint32_t val, size_t size)
{
uint32_t *p = static_cast<uint32_t*>(buf);
size_t n = size / sizeof(uint32_t);
for (size_t i=0; i<n; i++)
{
*p = val; p++;
}
}
//----------------------------------------------------------
void ConsoleViewerBase::copyPixels32( void *dest, void *src, size_t size, uint32_t alphaMask)
{
uint32_t *d = static_cast<uint32_t*>(dest);
uint32_t *s = static_cast<uint32_t*>(src);
size_t n = size / sizeof(uint32_t);
for (size_t i=0; i<n; i++)
{
*d = *s | alphaMask; d++; s++;
}
}
//----------------------------------------------------------

View File

@ -0,0 +1,56 @@
// ConsoleViewerInterface.h
//
#pragma once
#include <stdint.h>
#include <QColor>
#include <QCursor>
#include <QSize>
class ConsoleViewerBase
{
public:
enum VideoDriver
{
VIDEO_DRIVER_OPENGL = 0,
VIDEO_DRIVER_SDL,
VIDEO_DRIVER_QPAINTER
};
virtual int init(void) = 0;
virtual void reset(void) = 0;
virtual void queueRedraw(void) = 0;
virtual int driver(void) = 0;
virtual void transfer2LocalBuffer(void) = 0;
virtual void setVsyncEnable( bool ena ) = 0;
virtual void setLinearFilterEnable( bool ena ) = 0;
virtual bool getForceAspectOpt(void) = 0;
virtual void setForceAspectOpt( bool val ) = 0;
virtual bool getAutoScaleOpt(void) = 0;
virtual void setAutoScaleOpt( bool val ) = 0;
virtual double getScaleX(void) = 0;
virtual double getScaleY(void) = 0;
virtual void setScaleXY( double xs, double ys ) = 0;
virtual void getNormalizedCursorPos( double &x, double &y ) = 0;
virtual bool getMouseButtonState( unsigned int btn ) = 0;
virtual void setAspectXY( double x, double y ) = 0;
virtual void getAspectXY( double &x, double &y ) = 0;
virtual double getAspectRatio(void) = 0;
virtual void setCursor(const QCursor &c) = 0;
virtual void setCursor( Qt::CursorShape s ) = 0;
virtual void setBgColor( QColor &c ) = 0;
virtual QSize size(void) = 0;
virtual QCursor cursor(void) = 0;
virtual void setMinimumSize(const QSize &) = 0;
virtual void setMaximumSize(const QSize &) = 0;
static void memset32( void *buf, uint32_t val, size_t size);
static void copyPixels32( void *dest, void *src, size_t size, uint32_t alphaMask);
static constexpr uint32_t alphaMask = 0xff000000;
protected:
};

View File

@ -0,0 +1,505 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2020 mjbudd77
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// GameViewer.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//#include <unistd.h>
#include "../../profiler.h"
#include "Qt/nes_shm.h"
#include "Qt/throttle.h"
#include "Qt/fceuWrapper.h"
#include "Qt/ConsoleViewerQWidget.h"
#include "Qt/ConsoleUtilities.h"
#include "Qt/ConsoleWindow.h"
extern unsigned int gui_draw_area_width;
extern unsigned int gui_draw_area_height;
ConsoleViewQWidget_t::ConsoleViewQWidget_t(QWidget *parent)
: QWidget( parent )
{
consoleWin_t *win = qobject_cast <consoleWin_t*>(parent);
printf("Initializing QPainter Video Driver\n");
QPalette pal = palette();
pal.setColor(QPalette::Window, Qt::black);
setAutoFillBackground(true);
setPalette(pal);
bgColor = nullptr;
if ( win )
{
bgColor = win->getVideoBgColorPtr();
bgColor->setRgb( 0, 0, 0 );
}
setMinimumWidth( 256 );
setMinimumHeight( 224 );
setFocusPolicy(Qt::StrongFocus);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
view_width = GL_NES_WIDTH;
view_height = GL_NES_HEIGHT;
sx = sy = 0;
rw = view_width;
rh = view_height;
sdlRendW = 0;
sdlRendH = 0;
xscale = 2.0;
yscale = 2.0;
devPixRatio = 1.0f;
aspectRatio = 1.0f;
aspectX = 1.0f;
aspectY = 1.0f;
vsyncEnabled = false;
mouseButtonMask = 0;
localBufSize = (4 * GL_NES_WIDTH) * (4 * GL_NES_HEIGHT) * sizeof(uint32_t);
localBuf = (uint32_t*)malloc( localBufSize );
if ( localBuf )
{
memset32( localBuf, alphaMask, localBufSize );
}
forceAspect = true;
autoScaleEna = true;
linearFilter = false;
if ( g_config )
{
int opt;
g_config->getOption("SDL.OpenGLip", &opt );
linearFilter = (opt) ? true : false;
g_config->getOption ("SDL.AutoScale", &opt);
autoScaleEna = (opt) ? true : false;
g_config->getOption("SDL.XScale", &xscale);
g_config->getOption("SDL.YScale", &yscale);
g_config->getOption ("SDL.ForceAspect", &forceAspect);
if ( bgColor )
{
fceuLoadConfigColor( "SDL.VideoBgColor", bgColor );
}
g_config->getOption ("SDL.VideoVsync", &vsyncEnabled);
}
}
ConsoleViewQWidget_t::~ConsoleViewQWidget_t(void)
{
//printf("Destroying QPainter Viewport\n");
if ( localBuf )
{
free( localBuf ); localBuf = nullptr;
}
cleanup();
}
void ConsoleViewQWidget_t::setBgColor( QColor &c )
{
if ( bgColor )
{
*bgColor = c;
}
}
void ConsoleViewQWidget_t::setVsyncEnable( bool ena )
{
if ( vsyncEnabled != ena )
{
vsyncEnabled = ena;
reset();
}
}
void ConsoleViewQWidget_t::setLinearFilterEnable( bool ena )
{
if ( ena != linearFilter )
{
linearFilter = ena;
reset();
}
}
void ConsoleViewQWidget_t::setScaleXY( double xs, double ys )
{
xscale = xs;
yscale = ys;
if ( forceAspect )
{
if ( xscale < yscale )
{
yscale = xscale;
}
else
{
xscale = yscale;
}
}
}
void ConsoleViewQWidget_t::setAspectXY( double x, double y )
{
aspectX = x;
aspectY = y;
aspectRatio = aspectY / aspectX;
}
void ConsoleViewQWidget_t::getAspectXY( double &x, double &y )
{
x = aspectX;
y = aspectY;
}
double ConsoleViewQWidget_t::getAspectRatio(void)
{
return aspectRatio;
}
void ConsoleViewQWidget_t::transfer2LocalBuffer(void)
{
int i=0, hq = 0, bufIdx;
int numPixels = nes_shm->video.ncol * nes_shm->video.nrow;
unsigned int cpSize = numPixels * 4;
uint8_t *src, *dest;
bufIdx = nes_shm->pixBufIdx-1;
if ( bufIdx < 0 )
{
bufIdx = NES_VIDEO_BUFLEN-1;
}
if ( cpSize > localBufSize )
{
cpSize = localBufSize;
}
src = (uint8_t*)nes_shm->pixbuf[bufIdx];
dest = (uint8_t*)localBuf;
hq = (nes_shm->video.preScaler == 1) || (nes_shm->video.preScaler == 4); // hq2x and hq3x
if ( hq )
{
for (i=0; i<numPixels; i++)
{
dest[3] = 0xFF;
dest[1] = src[1];
dest[2] = src[2];
dest[0] = src[0];
src += 4; dest += 4;
}
}
else
{
//memcpy( localBuf, src, cpSize );
copyPixels32( dest, src, cpSize, alphaMask);
}
}
int ConsoleViewQWidget_t::init(void)
{
return 0;
}
void ConsoleViewQWidget_t::cleanup(void)
{
}
void ConsoleViewQWidget_t::reset(void)
{
cleanup();
if ( init() == 0 )
{
}
else
{
cleanup();
}
}
void ConsoleViewQWidget_t::setCursor(const QCursor &c)
{
QWidget::setCursor(c);
}
void ConsoleViewQWidget_t::setCursor( Qt::CursorShape s )
{
QWidget::setCursor(s);
}
void ConsoleViewQWidget_t::showEvent(QShowEvent *event)
{
//printf("SDL Show: %i x %i \n", width(), height() );
//view_width = width();
//view_height = height();
//gui_draw_area_width = view_width;
//gui_draw_area_height = view_height;
//reset();
}
void ConsoleViewQWidget_t::resizeEvent(QResizeEvent *event)
{
QSize s;
s = event->size();
view_width = s.width();
view_height = s.height();
printf("QWidget Resize: %i x %i \n", view_width, view_height);
gui_draw_area_width = view_width;
gui_draw_area_height = view_height;
reset();
}
void ConsoleViewQWidget_t::mousePressEvent(QMouseEvent * event)
{
//printf("Mouse Button Press: (%i,%i) %x %x\n",
// event->pos().x(), event->pos().y(), event->button(), event->buttons() );
mouseButtonMask = event->buttons();
}
void ConsoleViewQWidget_t::mouseReleaseEvent(QMouseEvent * event)
{
//printf("Mouse Button Release: (%i,%i) %x %x\n",
// event->pos().x(), event->pos().y(), event->button(), event->buttons() );
mouseButtonMask = event->buttons();
}
bool ConsoleViewQWidget_t::getMouseButtonState( unsigned int btn )
{
bool isPressed = false;
if ( mouseButtonMask & btn )
{
isPressed = true;
}
else
{ // Check SDL mouse state just in case SDL is intercepting
// mouse events from window system causing Qt not to see them.
int x, y;
uint32_t b;
b = SDL_GetMouseState( &x, &y);
if ( btn & Qt::LeftButton )
{
if ( b & SDL_BUTTON(SDL_BUTTON_LEFT) )
{
isPressed = true;
}
}
if ( btn & Qt::RightButton )
{
if ( b & SDL_BUTTON(SDL_BUTTON_RIGHT) )
{
isPressed = true;
}
}
if ( btn & Qt::MiddleButton )
{
if ( b & SDL_BUTTON(SDL_BUTTON_MIDDLE) )
{
isPressed = true;
}
}
}
return isPressed;
}
void ConsoleViewQWidget_t::getNormalizedCursorPos( double &x, double &y )
{
QPoint cursor;
cursor = QCursor::pos();
//printf("Global Cursor (%i,%i) \n", cursor.x(), cursor.y() );
cursor = mapFromGlobal( cursor );
//printf("Window Cursor (%i,%i) \n", cursor.x(), cursor.y() );
x = (double)(cursor.x() - sx) / (double)rw;
y = (double)(cursor.y() - sy) / (double)rh;
if ( x < 0.0 )
{
x = 0.0;
}
else if ( x > 1.0 )
{
x = 1.0;
}
if ( y < 0.0 )
{
y = 0.0;
}
else if ( y > 1.0 )
{
y = 1.0;
}
//printf("Normalized Cursor (%f,%f) \n", x, y );
}
void ConsoleViewQWidget_t::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
int nesWidth = GL_NES_WIDTH;
int nesHeight = GL_NES_HEIGHT;
float ixScale = 1.0;
float iyScale = 1.0;
if ( nes_shm != nullptr )
{
nesWidth = nes_shm->video.ncol;
nesHeight = nes_shm->video.nrow;
ixScale = (float)nes_shm->video.xscale;
iyScale = (float)nes_shm->video.yscale;
}
//printf(" %i x %i \n", nesWidth, nesHeight );
float xscaleTmp = (float)view_width / (float)nesWidth;
float yscaleTmp = (float)view_height / (float)nesHeight;
xscaleTmp *= ixScale;
yscaleTmp *= iyScale;
if ( forceAspect )
{
if ( xscaleTmp < yscaleTmp )
{
yscaleTmp = xscaleTmp;
}
else
{
xscaleTmp = yscaleTmp;
}
}
if ( autoScaleEna )
{
xscale = xscaleTmp;
yscale = yscaleTmp;
}
else
{
if ( xscaleTmp > xscale )
{
xscaleTmp = xscale;
}
if ( yscaleTmp > yscale )
{
yscaleTmp = yscale;
}
}
rw=(int)(nesWidth*xscaleTmp/ixScale);
rh=(int)(nesHeight*yscaleTmp/iyScale);
if ( forceAspect )
{
int iw, ih, ax, ay;
ax = (int)(aspectX+0.50);
ay = (int)(aspectY+0.50);
iw = rw * ay;
ih = rh * ax;
if ( iw > ih )
{
rh = (rw * ay) / ax;
}
else
{
rw = (rh * ax) / ay;
}
if ( rw > view_width )
{
rw = view_width;
rh = (rw * ay) / ax;
}
if ( rh > view_height )
{
rh = view_height;
rw = (rh * ax) / ay;
}
}
if ( rw > view_width ) rw = view_width;
if ( rh > view_height) rh = view_height;
sx=(view_width-rw)/2;
sy=(view_height-rh)/2;
if ( bgColor )
{
painter.fillRect( 0, 0, view_width, view_height, *bgColor );
}
else
{
painter.fillRect( 0, 0, view_width, view_height, Qt::black );
}
painter.setRenderHint( QPainter::SmoothPixmapTransform, linearFilter );
int rowPitch = nesWidth * sizeof(uint32_t);
QImage tmpImage( (const uchar*)localBuf, nesWidth, nesHeight, rowPitch, QImage::Format_ARGB32);
//SDL_Rect source = {0, 0, nesWidth, nesHeight };
QRect dest( sx, sy, rw, rh );
painter.drawImage( dest, tmpImage );
videoBufferSwapMark();
nes_shm->render_count++;
}

View File

@ -0,0 +1,92 @@
// ConsoleViewerQWidget.h
//
#pragma once
#include <QWidget>
#include <QColor>
#include <QCursor>
#include <QImage>
#include <QPaintEvent>
#include <QResizeEvent>
#include "Qt/ConsoleViewerInterface.h"
class ConsoleViewQWidget_t : public QWidget, public ConsoleViewerBase
{
Q_OBJECT
public:
ConsoleViewQWidget_t(QWidget *parent = 0);
~ConsoleViewQWidget_t(void);
int init(void);
void reset(void);
void cleanup(void);
void queueRedraw(void){ update(); };
int driver(void){ return VIDEO_DRIVER_QPAINTER; };
void transfer2LocalBuffer(void);
void setVsyncEnable( bool ena );
void setLinearFilterEnable( bool ena );
bool getForceAspectOpt(void){ return forceAspect; };
void setForceAspectOpt( bool val ){ forceAspect = val; return; };
bool getAutoScaleOpt(void){ return autoScaleEna; };
void setAutoScaleOpt( bool val ){ autoScaleEna = val; return; };
double getScaleX(void){ return xscale; };
double getScaleY(void){ return yscale; };
void setScaleXY( double xs, double ys );
void getNormalizedCursorPos( double &x, double &y );
bool getMouseButtonState( unsigned int btn );
void setAspectXY( double x, double y );
void getAspectXY( double &x, double &y );
double getAspectRatio(void);
void setCursor(const QCursor &c);
void setCursor( Qt::CursorShape s );
void setBgColor( QColor &c );
QSize size(void){ return QWidget::size(); };
QCursor cursor(void){ return QWidget::cursor(); };
void setMinimumSize(const QSize &s){ return QWidget::setMinimumSize(s); };
void setMaximumSize(const QSize &s){ return QWidget::setMaximumSize(s); };
protected:
void paintEvent(QPaintEvent *event);
void showEvent(QShowEvent *event);
void resizeEvent(QResizeEvent *event);
void mousePressEvent(QMouseEvent * event);
void mouseReleaseEvent(QMouseEvent * event);
int view_width;
int view_height;
double devPixRatio;
double aspectRatio;
double aspectX;
double aspectY;
double xscale;
double yscale;
int rw;
int rh;
int sx;
int sy;
int sdlRendW;
int sdlRendH;
bool vsyncEnabled;
bool linearFilter;
bool forceAspect;
bool autoScaleEna;
QColor *bgColor;
uint32_t *localBuf;
uint32_t localBufSize;
unsigned int mouseButtonMask;
private slots:
};

View File

@ -89,7 +89,7 @@ ConsoleViewSDL_t::ConsoleViewSDL_t(QWidget *parent)
if ( localBuf )
{
memset( localBuf, 0, localBufSize );
memset32( localBuf, alphaMask, localBufSize );
}
forceAspect = true;
@ -122,6 +122,8 @@ ConsoleViewSDL_t::ConsoleViewSDL_t(QWidget *parent)
ConsoleViewSDL_t::~ConsoleViewSDL_t(void)
{
//printf("Destroying SDL Viewport\n");
if ( localBuf )
{
free( localBuf ); localBuf = NULL;
@ -240,7 +242,7 @@ void ConsoleViewSDL_t::transfer2LocalBuffer(void)
}
else
{
memcpy( localBuf, src, cpSize );
copyPixels32( dest, src, cpSize, alphaMask);
}
}

View File

@ -10,7 +10,9 @@
#include <QResizeEvent>
#include <SDL.h>
class ConsoleViewSDL_t : public QWidget
#include "Qt/ConsoleViewerInterface.h"
class ConsoleViewSDL_t : public QWidget, public ConsoleViewerBase
{
Q_OBJECT
@ -22,6 +24,8 @@ class ConsoleViewSDL_t : public QWidget
void reset(void);
void cleanup(void);
void render(void);
void queueRedraw(void){ render(); };
int driver(void){ return VIDEO_DRIVER_SDL; };
void transfer2LocalBuffer(void);
@ -44,6 +48,12 @@ class ConsoleViewSDL_t : public QWidget
void setCursor(const QCursor &c);
void setCursor( Qt::CursorShape s );
void setBgColor( QColor &c );
QSize size(void){ return QWidget::size(); };
QCursor cursor(void){ return QWidget::cursor(); };
void setMinimumSize(const QSize &s){ return QWidget::setMinimumSize(s); };
void setMaximumSize(const QSize &s){ return QWidget::setMaximumSize(s); };
protected:
//void paintEvent(QPaintEvent *event);

View File

@ -35,6 +35,7 @@
#include <QPixmap>
#include <QWindow>
#include <QScreen>
#include <QSettings>
#include <QHeaderView>
#include <QFileInfo>
#include <QFileDialog>
@ -53,8 +54,11 @@
#include "../../input.h"
#include "../../movie.h"
#include "../../wave.h"
#include "../../state.h"
#include "../../profiler.h"
#include "../../version.h"
#include "common/os_utils.h"
#include "utils/timeStamp.h"
#ifdef _S9XLUA_H
#include "../../fceulua.h"
@ -78,6 +82,7 @@
#include "Qt/MoviePlay.h"
#include "Qt/MovieRecord.h"
#include "Qt/MovieOptions.h"
#include "Qt/StateRecorderConf.h"
#include "Qt/TimingConf.h"
#include "Qt/FrameTimingStats.h"
#include "Qt/LuaControl.h"
@ -110,7 +115,7 @@ consoleWin_t::consoleWin_t(QWidget *parent)
: QMainWindow( parent )
{
int opt, xWinPos = -1, yWinPos = -1, xWinSize = 256, yWinSize = 240;
int use_SDL_video = false;
int videoDriver = 0;
int setFullScreen = false;
//QString libpath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
@ -122,6 +127,13 @@ consoleWin_t::consoleWin_t(QWidget *parent)
printf("Running on Platform: %s\n", QGuiApplication::platformName().toStdString().c_str() );
QThread *thread = QThread::currentThread();
if (thread)
{
thread->setObjectName( QString("MainThread") );
}
QApplication::setStyle( new fceuStyle() );
initHotKeys();
@ -129,8 +141,10 @@ consoleWin_t::consoleWin_t(QWidget *parent)
firstResize = true;
closeRequested = false;
errorMsgValid = false;
viewport_GL = NULL;
viewport_SDL = NULL;
viewport_GL = NULL;
viewport_SDL = NULL;
viewport_QWidget = NULL;
viewport_Interface = NULL;
contextMenuEnable = false;
soundUseGlobalFocus = false;
@ -145,21 +159,9 @@ consoleWin_t::consoleWin_t(QWidget *parent)
g_config->getOption( "SDL.AutoHideMenuFullsreen", &autoHideMenuFullscreen );
g_config->getOption( "SDL.ContextMenuEnable", &contextMenuEnable );
g_config->getOption( "SDL.Sound.UseGlobalFocus", &soundUseGlobalFocus );
g_config->getOption ("SDL.VideoDriver", &use_SDL_video);
g_config->getOption ("SDL.VideoDriver", &videoDriver);
if ( use_SDL_video )
{
viewport_SDL = new ConsoleViewSDL_t(this);
setCentralWidget(viewport_SDL);
}
else
{
viewport_GL = new ConsoleViewGL_t(this);
setCentralWidget(viewport_GL);
}
setViewportAspect();
loadVideoDriver( videoDriver );
setWindowTitle( tr(FCEU_NAME_AND_VERSION) );
setWindowIcon(QIcon(":fceux1.png"));
@ -225,13 +227,9 @@ consoleWin_t::consoleWin_t(QWidget *parent)
// the window is resized appropriately. On the first resize event,
// we will set the minimum viewport size back to 1x values that the
// window can be shrunk by dragging lower corner.
if ( viewport_GL != NULL )
if ( viewport_Interface != NULL )
{
viewport_GL->setMinimumSize( reqSize );
}
else if ( viewport_SDL != NULL )
{
viewport_SDL->setMinimumSize( reqSize );
viewport_Interface->setMinimumSize( reqSize );
}
//this->resize( reqSize );
}
@ -274,17 +272,6 @@ consoleWin_t::~consoleWin_t(void)
if ( !isFullScreen() && !isMaximized() )
{
// Scaling is only saved when applying video settings
//if ( viewport_GL != NULL )
//{
// g_config->setOption( "SDL.XScale", viewport_GL->getScaleX() );
// g_config->setOption( "SDL.YScale", viewport_GL->getScaleY() );
//}
//else if ( viewport_SDL != NULL )
//{
// g_config->setOption( "SDL.XScale", viewport_SDL->getScaleX() );
// g_config->setOption( "SDL.YScale", viewport_SDL->getScaleY() );
//}
g_config->setOption( "SDL.WinPosX" , pos().x() );
g_config->setOption( "SDL.WinPosY" , pos().y() );
g_config->setOption( "SDL.WinSizeX", w.width() );
@ -325,14 +312,8 @@ consoleWin_t::~consoleWin_t(void)
//fceuWrapperClose();
//FCEU_WRAPPER_UNLOCK();
if ( viewport_GL != NULL )
{
delete viewport_GL; viewport_GL = NULL;
}
if ( viewport_SDL != NULL )
{
delete viewport_SDL; viewport_SDL = NULL;
}
unloadVideoDriver();
delete mutex;
// LoadGame() checks for an IP and if it finds one begins a network session
@ -365,26 +346,18 @@ int consoleWin_t::videoInit(void)
{
int ret = 0;
if ( viewport_SDL )
if (viewport_Interface)
{
ret = viewport_SDL->init();
}
else if ( viewport_GL )
{
ret = viewport_GL->init();
ret = viewport_Interface->init();
}
return ret;
}
void consoleWin_t::videoReset(void)
{
if ( viewport_SDL )
if (viewport_Interface)
{
viewport_SDL->reset();
}
else if ( viewport_GL )
{
viewport_GL->reset();
viewport_Interface->reset();
}
return;
}
@ -487,21 +460,13 @@ QSize consoleWin_t::calcRequiredSize(void)
w = size();
if ( viewport_GL )
if ( viewport_Interface )
{
v = viewport_GL->size();
forceAspect = viewport_GL->getForceAspectOpt();
aspectRatio = viewport_GL->getAspectRatio();
xscale = viewport_GL->getScaleX();
yscale = viewport_GL->getScaleY();
}
else if ( viewport_SDL )
{
v = viewport_SDL->size();
forceAspect = viewport_SDL->getForceAspectOpt();
aspectRatio = viewport_SDL->getAspectRatio();
xscale = viewport_SDL->getScaleX();
yscale = viewport_SDL->getScaleY();
v = viewport_Interface->size();
forceAspect = viewport_Interface->getForceAspectOpt();
aspectRatio = viewport_Interface->getAspectRatio();
xscale = viewport_Interface->getScaleX();
yscale = viewport_Interface->getScaleY();
}
dw = 0;
@ -572,13 +537,9 @@ void consoleWin_t::setViewportAspect(void)
break;
}
if ( viewport_GL )
if (viewport_Interface)
{
viewport_GL->setAspectXY( x, y );
}
else if ( viewport_SDL )
{
viewport_SDL->setAspectXY( x, y );
viewport_Interface->setAspectXY( x, y );
}
}
@ -648,25 +609,17 @@ void consoleWin_t::loadCursor(void)
void consoleWin_t::setViewerCursor( QCursor s )
{
if ( viewport_GL )
if (viewport_Interface)
{
viewport_GL->setCursor(s);
}
else if ( viewport_SDL )
{
viewport_SDL->setCursor(s);
viewport_Interface->setCursor(s);
}
}
void consoleWin_t::setViewerCursor( Qt::CursorShape s )
{
if ( viewport_GL )
if (viewport_Interface)
{
viewport_GL->setCursor(s);
}
else if ( viewport_SDL )
{
viewport_SDL->setCursor(s);
viewport_Interface->setCursor(s);
}
}
@ -674,13 +627,9 @@ Qt::CursorShape consoleWin_t::getViewerCursor(void)
{
Qt::CursorShape s = Qt::ArrowCursor;
if ( viewport_GL )
if (viewport_Interface)
{
s = viewport_GL->cursor().shape();
}
else if ( viewport_SDL )
{
s = viewport_SDL->cursor().shape();
s = viewport_Interface->cursor().shape();
}
return s;
}
@ -692,14 +641,11 @@ void consoleWin_t::resizeEvent(QResizeEvent *event)
// We are assuming that window has been exposed and all sizing of menu is finished
// Restore minimum sizes to 1x values after first resize event so that
// window is still able to be shrunk by dragging lower corners.
if ( viewport_GL != NULL )
if (viewport_Interface)
{
viewport_GL->setMinimumSize( QSize( 256, 224 ) );
}
else if ( viewport_SDL != NULL )
{
viewport_SDL->setMinimumSize( QSize( 256, 224 ) );
viewport_Interface->setMinimumSize( QSize( 256, 224 ) );
}
firstResize = false;
}
//printf("%i x %i \n", event->size().width(), event->size().height() );
@ -784,9 +730,21 @@ void consoleWin_t::dropEvent(QDropEvent *event)
QFileInfo fi( filename );
QString suffix = fi.suffix();
bool isStateSaveFile = (suffix.size() == 3) &&
(suffix[0] == 'f') && (suffix[1] == 'c') &&
( (suffix[2] == 's') || suffix[2].isDigit() );
//printf("DragNDrop Suffix: %s\n", suffix.toStdString().c_str() );
if ( suffix.compare("lua", Qt::CaseInsensitive) == 0 )
if (isStateSaveFile)
{
FCEU_WRAPPER_LOCK();
FCEUI_LoadState( filename.toStdString().c_str() );
FCEU_WRAPPER_UNLOCK();
event->accept();
}
else if ( suffix.compare("lua", Qt::CaseInsensitive) == 0 )
{
int luaLoadSuccess;
@ -920,6 +878,9 @@ void consoleWin_t::initHotKeys(void)
connect( Hotkeys[ HK_LOAD_STATE_7 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState7(void)) );
connect( Hotkeys[ HK_LOAD_STATE_8 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState8(void)) );
connect( Hotkeys[ HK_LOAD_STATE_9 ].getShortcut(), SIGNAL(activated()), this, SLOT(loadState9(void)) );
connect( Hotkeys[ HK_LOAD_PREV_STATE ].getShortcut(), SIGNAL(activated()), this, SLOT(loadPrevState(void)) );
connect( Hotkeys[ HK_LOAD_NEXT_STATE ].getShortcut(), SIGNAL(activated()), this, SLOT(loadNextState(void)) );
}
//---------------------------------------------------------------------------
void consoleWin_t::createMainMenu(void)
@ -1214,6 +1175,15 @@ void consoleWin_t::createMainMenu(void)
optMenu->addAction(timingConfig);
// Options -> State Recorder Config
stateRecordConfig = new QAction(tr("&State Recorder Config"), this);
//stateRecordConfig->setShortcut( QKeySequence(tr("Ctrl+C")));
stateRecordConfig->setStatusTip(tr("State Recorder Configure"));
stateRecordConfig->setIcon( QIcon(":icons/media-record.png") );
connect(stateRecordConfig, SIGNAL(triggered()), this, SLOT(openStateRecorderConfWin(void)) );
optMenu->addAction(stateRecordConfig);
// Options -> Movie Options
movieConfig = new QAction(tr("&Movie Options"), this);
//movieConfig->setShortcut( QKeySequence(tr("Ctrl+C")));
@ -1982,85 +1952,164 @@ void consoleWin_t::createMainMenu(void)
#endif
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
int consoleWin_t::unloadVideoDriver(void)
{
viewport_Interface = NULL;
if (viewport_GL != NULL)
{
if ( viewport_GL == centralWidget() )
{
takeCentralWidget();
}
else
{
printf("Error: Central Widget Failed!\n");
}
viewport_GL->deleteLater();
viewport_GL = NULL;
}
if (viewport_SDL != NULL)
{
if ( viewport_SDL == centralWidget() )
{
takeCentralWidget();
}
else
{
printf("Error: Central Widget Failed!\n");
}
viewport_SDL->deleteLater();
viewport_SDL = NULL;
}
if (viewport_QWidget != NULL)
{
if ( viewport_QWidget == centralWidget() )
{
takeCentralWidget();
}
else
{
printf("Error: Central Widget Failed!\n");
}
viewport_QWidget->deleteLater();
viewport_QWidget = NULL;
}
return 0;
}
//---------------------------------------------------------------------------
void consoleWin_t::videoDriverDestroyed(QObject* obj)
{
if (viewport_GL == obj)
{
//printf("GL Video Driver Destroyed\n");
if (viewport_Interface == static_cast<ConsoleViewerBase*>(viewport_GL))
{
viewport_Interface = NULL;
}
viewport_GL = NULL;
}
if (viewport_SDL == obj)
{
//printf("SDL Video Driver Destroyedi\n");
if (viewport_Interface == static_cast<ConsoleViewerBase*>(viewport_SDL))
{
viewport_Interface = NULL;
}
viewport_SDL = NULL;
}
if (viewport_QWidget == obj)
{
//printf("QPainter Video Driver Destroyed\n");
if (viewport_Interface == static_cast<ConsoleViewerBase*>(viewport_QWidget))
{
viewport_Interface = NULL;
}
viewport_QWidget = NULL;
}
printf("Video Driver Destroyed: %p\n", obj);
//printf("viewport_GL: %p\n", viewport_GL);
//printf("viewport_SDL: %p\n", viewport_SDL);
//printf("viewport_Qt: %p\n", viewport_QWidget);
//printf("viewport_Interface: %p\n", viewport_Interface);
}
//---------------------------------------------------------------------------
int consoleWin_t::loadVideoDriver( int driverId, bool force )
{
if ( driverId )
{ // SDL Driver
if ( viewport_SDL != NULL )
if (viewport_Interface)
{
if (viewport_Interface->driver() == driverId)
{ // Already Loaded
if ( force )
if (force)
{
if ( viewport_SDL == centralWidget() )
{
takeCentralWidget();
}
delete viewport_SDL;
viewport_SDL = NULL;
unloadVideoDriver();
}
else
{
return 0;
}
}
if ( viewport_GL != NULL )
{
if ( viewport_GL == centralWidget() )
{
takeCentralWidget();
}
delete viewport_GL;
viewport_GL = NULL;
}
viewport_SDL = new ConsoleViewSDL_t(this);
setCentralWidget(viewport_SDL);
setViewportAspect();
viewport_SDL->init();
}
else
{ // OpenGL Driver
if ( viewport_GL != NULL )
{ // Already Loaded
if ( force )
{
if ( viewport_GL == centralWidget() )
{
takeCentralWidget();
}
delete viewport_GL;
viewport_GL = NULL;
}
else
{
return 0;
}
}
if ( viewport_SDL != NULL )
switch ( driverId )
{
case ConsoleViewerBase::VIDEO_DRIVER_SDL:
{
if ( viewport_SDL == centralWidget() )
{
takeCentralWidget();
}
delete viewport_SDL;
viewport_SDL = new ConsoleViewSDL_t(this);
viewport_SDL = NULL;
viewport_Interface = static_cast<ConsoleViewerBase*>(viewport_SDL);
setCentralWidget(viewport_SDL);
setViewportAspect();
viewport_SDL->init();
connect( viewport_SDL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) );
}
viewport_GL = new ConsoleViewGL_t(this);
break;
case ConsoleViewerBase::VIDEO_DRIVER_OPENGL:
{
viewport_GL = new ConsoleViewGL_t(this);
setCentralWidget(viewport_GL);
viewport_Interface = static_cast<ConsoleViewerBase*>(viewport_GL);
setViewportAspect();
setCentralWidget(viewport_GL);
viewport_GL->init();
setViewportAspect();
viewport_GL->init();
connect( viewport_GL, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) );
}
break;
default:
case ConsoleViewerBase::VIDEO_DRIVER_QPAINTER:
{
viewport_QWidget = new ConsoleViewQWidget_t(this);
viewport_Interface = static_cast<ConsoleViewerBase*>(viewport_QWidget);
setCentralWidget(viewport_QWidget);
setViewportAspect();
viewport_QWidget->init();
connect( viewport_QWidget, SIGNAL(destroyed(QObject*)), this, SLOT(videoDriverDestroyed(QObject*)) );
}
break;
}
// Reload Viewport Cursor Type and Visibility
@ -2256,15 +2305,10 @@ void consoleWin_t::videoBgColorChanged( QColor &c )
{
//printf("Color Changed\n");
if ( viewport_GL )
if ( viewport_Interface )
{
viewport_GL->setBgColor(c);
viewport_GL->update();
}
else if ( viewport_SDL )
{
viewport_SDL->setBgColor(c);
viewport_SDL->render();
viewport_Interface->setBgColor(c);
viewport_Interface->queueRedraw();
}
}
//---------------------------------------------------------------------------
@ -2282,6 +2326,7 @@ int consoleWin_t::showListSelectDialog( const char *title, std::vector <std::st
QPushButton *okButton, *cancelButton;
QTreeWidget *tree;
QTreeWidgetItem *item;
QSettings settings;
dialog.setWindowTitle( tr(title) );
@ -2329,8 +2374,15 @@ int consoleWin_t::showListSelectDialog( const char *title, std::vector <std::st
dialog.setLayout( mainLayout );
// Restore Window Geometry
dialog.restoreGeometry(settings.value("ArchiveViewer/geometry").toByteArray());
// Run Dialog Execution Loop
ret = dialog.exec();
// Save Window Geometry
settings.setValue("ArchiveViewer/geometry", dialog.saveGeometry());
if ( ret == QDialog::Accepted )
{
idx = 0;
@ -2362,7 +2414,7 @@ void consoleWin_t::openROMFile(void)
QDir d;
const QStringList filters(
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP)",
{ "All Useable files (*.nes *.NES *.nsf *.NSF *.fds *.FDS *.unf *.UNF *.unif *.UNIF *.zip *.ZIP, *.7z *.7zip)",
"NES files (*.nes *.NES)",
"NSF files (*.nsf *.NSF)",
"UNF files (*.unf *.UNF *.unif *.UNIF)",
@ -2729,6 +2781,20 @@ void consoleWin_t::loadState7(void){ loadState(7); }
void consoleWin_t::loadState8(void){ loadState(8); }
void consoleWin_t::loadState9(void){ loadState(9); }
void consoleWin_t::loadPrevState(void)
{
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadPrevState();
FCEU_WRAPPER_UNLOCK();
}
void consoleWin_t::loadNextState(void)
{
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderLoadNextState();
FCEU_WRAPPER_UNLOCK();
}
void consoleWin_t::quickSave(void)
{
FCEU_WRAPPER_LOCK();
@ -2858,6 +2924,10 @@ void consoleWin_t::takeScreenShot(void)
{
image = screen->grabWindow( viewport_SDL->winId() );
}
else if ( viewport_QWidget )
{
image = screen->grabWindow( viewport_QWidget->winId() );
}
for (u = 0; u < 99999; ++u)
{
@ -3173,17 +3243,11 @@ void consoleWin_t::winResizeIx(int iscale)
w = size();
if ( viewport_GL )
if ( viewport_Interface )
{
v = viewport_GL->size();
aspectRatio = viewport_GL->getAspectRatio();
forceAspect = viewport_GL->getForceAspectOpt();
}
else if ( viewport_SDL )
{
v = viewport_SDL->size();
aspectRatio = viewport_SDL->getAspectRatio();
forceAspect = viewport_SDL->getForceAspectOpt();
v = viewport_Interface->size();
aspectRatio = viewport_Interface->getAspectRatio();
forceAspect = viewport_Interface->getForceAspectOpt();
}
dw = w.width() - v.width();
@ -3846,6 +3910,15 @@ void consoleWin_t::toggleTurboMode(void)
NoWaiting ^= 1;
}
void consoleWin_t::openStateRecorderConfWin(void)
{
StateRecorderDialog_t *win;
win = new StateRecorderDialog_t(this);
win->show();
}
void consoleWin_t::openMovie(void)
{
MoviePlayDialog_t *win;
@ -4435,19 +4508,15 @@ int consoleWin_t::getPeriodicInterval(void)
void consoleWin_t::transferVideoBuffer(void)
{
FCEU_PROFILE_FUNC(prof, "VideoXfer");
if ( nes_shm->blitUpdated )
{
nes_shm->blitUpdated = 0;
if ( viewport_SDL )
if (viewport_Interface)
{
viewport_SDL->transfer2LocalBuffer();
viewport_SDL->render();
}
else if ( viewport_GL )
{
viewport_GL->transfer2LocalBuffer();
viewport_GL->update();
viewport_Interface->transfer2LocalBuffer();
viewport_Interface->queueRedraw();
}
}
}
@ -4476,6 +4545,7 @@ void consoleWin_t::emuFrameFinish(void)
void consoleWin_t::updatePeriodic(void)
{
FCEU_PROFILE_FUNC(prof, "updatePeriodic");
static bool eventProcessingInProg = false;
if ( eventProcessingInProg )
@ -4551,6 +4621,9 @@ void consoleWin_t::updatePeriodic(void)
updateCounter++;
#ifdef __FCEU_PROFILER_ENABLE__
FCEU_profiler_log_thread_activity();
#endif
return;
}
@ -4561,6 +4634,7 @@ emulatorThread_t::emulatorThread_t( QObject *parent )
pself = 0;
#endif
setObjectName( QString("EmulationThread") );
}
#if defined(__linux__)

View File

@ -30,6 +30,7 @@
#include "Qt/ColorMenu.h"
#include "Qt/ConsoleViewerGL.h"
#include "Qt/ConsoleViewerSDL.h"
#include "Qt/ConsoleViewerQWidget.h"
#include "Qt/GamePadConf.h"
#include "Qt/AviRecord.h"
@ -126,8 +127,10 @@ class consoleWin_t : public QMainWindow
consoleWin_t(QWidget *parent = 0);
~consoleWin_t(void);
ConsoleViewGL_t *viewport_GL;
ConsoleViewSDL_t *viewport_SDL;
ConsoleViewGL_t *viewport_GL;
ConsoleViewSDL_t *viewport_SDL;
ConsoleViewQWidget_t *viewport_QWidget;
ConsoleViewerBase *viewport_Interface;
void setCyclePeriodms( int ms );
@ -155,6 +158,7 @@ class consoleWin_t : public QMainWindow
#endif
int loadVideoDriver( int driverId, bool force = false );
int unloadVideoDriver(void);
double getRefreshRate(void){ return refreshRate; }
@ -211,6 +215,7 @@ class consoleWin_t : public QMainWindow
QAction *hotkeyConfig;
QAction *paletteConfig;
QAction *guiConfig;
QAction *stateRecordConfig;
QAction *timingConfig;
QAction *movieConfig;
QAction *autoResume;
@ -344,6 +349,7 @@ class consoleWin_t : public QMainWindow
void openPaletteConfWin(void);
void openGuiConfWin(void);
void openTimingConfWin(void);
void openStateRecorderConfWin(void);
void openPaletteEditorWin(void);
void openAviRiffViewer(void);
void openTimingStatWin(void);
@ -439,6 +445,8 @@ class consoleWin_t : public QMainWindow
void loadState7(void);
void loadState8(void);
void loadState9(void);
void loadPrevState(void);
void loadNextState(void);
void mainMenuOpen(void);
void mainMenuClose(void);
void warnAmbiguousShortcut( QShortcut*);
@ -459,6 +467,7 @@ class consoleWin_t : public QMainWindow
void toggleUseBgPaletteForVideo(bool);
void videoBgColorChanged( QColor &c );
void loadRomRequestCB( QString s );
void videoDriverDestroyed( QObject *obj );
};

View File

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

View File

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

View File

@ -989,6 +989,8 @@ static int readQPaletteFromFile( const char *path, QPalette *pal )
rTxtMatch = NULL;
r = QPalette::WindowText;
for (k=0; k<30; k++)
{

View File

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

View File

@ -230,10 +230,14 @@ MsgLogViewDialog_t::MsgLogViewDialog_t(QWidget *parent)
updateTimer->start(500); // 2hz
FCEU_WRAPPER_LOCK();
msgLog.loadTextViewer(txtView);
totalLines = msgLog.getTotalLineCount();
FCEU_WRAPPER_UNLOCK();
txtView->moveCursor(QTextCursor::End);
restoreGeometry(settings.value("MsgLogWindow/geometry").toByteArray());

View File

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

View File

@ -0,0 +1,674 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2022 thor2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// StateRecorderConf.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <QCloseEvent>
#include <QGridLayout>
#include <QSettings>
#include <QMessageBox>
#include "Qt/throttle.h"
#include "Qt/fceuWrapper.h"
#include "Qt/StateRecorderConf.h"
#include "../../fceu.h"
#include "../../state.h"
#include "../../driver.h"
#include "../../emufile.h"
//----------------------------------------------------------------------------
StateRecorderDialog_t::StateRecorderDialog_t(QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *mainLayout, *vbox1;
QHBoxLayout *hbox, *hbox1;
QGroupBox *frame, *frame1;
QGridLayout *grid, *memStatsGrid, *sysStatusGrid;
QSettings settings;
int opt;
setWindowTitle("State Recorder Config");
mainLayout = new QVBoxLayout();
grid = new QGridLayout();
recorderEnable = new QCheckBox(tr("Auto Start Recorder at ROM Load"));
snapFrames = new QSpinBox();
snapMinutes = new QSpinBox();
snapSeconds = new QSpinBox();
historyDuration = new QSpinBox();
snapFrameSelBtn = new QRadioButton( tr("By Frames") );
snapTimeSelBtn = new QRadioButton( tr("By Time") );
recorderEnable->setChecked( FCEU_StateRecorderIsEnabled() );
connect( recorderEnable, SIGNAL(stateChanged(int)), this, SLOT(enableChanged(int)) );
snapFrames->setMinimum(1);
snapFrames->setMaximum(10000);
snapSeconds->setMinimum(0);
snapSeconds->setMaximum(60);
snapMinutes->setMinimum(0);
snapMinutes->setMaximum(60);
historyDuration->setMinimum(1);
historyDuration->setMaximum(180);
opt = 10;
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &opt);
snapFrames->setValue(opt);
opt = 15;
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &opt );
historyDuration->setValue(opt);
opt = 0;
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &opt);
snapMinutes->setValue(opt);
opt = 3;
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &opt);
snapSeconds->setValue(opt);
opt = 0;
g_config->getOption("SDL.StateRecorderTimingMode", &opt);
snapFrameSelBtn->setChecked(opt == 0);
snapTimeSelBtn->setChecked(opt == 1);
snapUseTime = opt ? true : false;
connect( snapFrames , SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( snapSeconds, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( snapMinutes, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
connect( historyDuration, SIGNAL(valueChanged(int)), this, SLOT(spinBoxValueChanged(int)) );
frame = new QGroupBox(tr("Retain History For:"));
hbox = new QHBoxLayout();
hbox->addWidget( historyDuration);
hbox->addWidget( new QLabel( tr("Minutes") ) );
frame->setLayout(hbox);
grid->addWidget( recorderEnable, 0, 0 );
grid->addWidget( frame , 1, 0 );
frame = new QGroupBox(tr("Compression Level:"));
hbox = new QHBoxLayout();
cmprLvlCbox = new QComboBox();
cmprLvlCbox->addItem( tr("0 - None"), 0 );
cmprLvlCbox->addItem( tr("1"), 1 );
cmprLvlCbox->addItem( tr("2"), 2 );
cmprLvlCbox->addItem( tr("3"), 3 );
cmprLvlCbox->addItem( tr("4"), 4 );
cmprLvlCbox->addItem( tr("5"), 5 );
cmprLvlCbox->addItem( tr("6"), 6 );
cmprLvlCbox->addItem( tr("7"), 7 );
cmprLvlCbox->addItem( tr("8"), 8 );
cmprLvlCbox->addItem( tr("9 - Max"), 9 );
opt = 0;
g_config->getOption("SDL.StateRecorderCompressionLevel", &opt);
cmprLvlCbox->setCurrentIndex(opt);
connect( cmprLvlCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(compressionLevelChanged(int)) );
hbox->addWidget(cmprLvlCbox);
frame->setLayout(hbox);
grid->addWidget( frame, 1, 1 );
frame1 = new QGroupBox(tr("Snapshot Timing Setting:"));
hbox1 = new QHBoxLayout();
frame1->setLayout(hbox1);
grid->addWidget( frame1, 2, 0, 1, 2 );
g_config->getOption("SDL.StateRecorderTimingMode", &opt);
hbox1->addWidget( snapFrameSelBtn );
hbox1->addWidget( snapTimeSelBtn );
snapFramesGroup = new QGroupBox(tr("Frames Between Snapshots:"));
hbox1 = new QHBoxLayout();
snapFramesGroup->setLayout(hbox1);
snapFramesGroup->setEnabled(snapFrameSelBtn->isChecked());
grid->addWidget( snapFramesGroup, 3, 0, 1, 2 );
hbox1->addWidget( snapFrames);
hbox1->addWidget( new QLabel( tr("Range (1 - 10000)") ) );
snapTimeGroup = new QGroupBox(tr("Time Between Snapshots:"));
hbox1 = new QHBoxLayout();
snapTimeGroup->setLayout(hbox1);
snapTimeGroup->setEnabled(snapTimeSelBtn->isChecked());
grid->addWidget( snapTimeGroup, 4, 0, 1, 2 );
frame = new QGroupBox();
hbox = new QHBoxLayout();
hbox1->addWidget(frame);
hbox->addWidget( snapMinutes);
hbox->addWidget( new QLabel( tr("Minutes") ) );
frame->setLayout(hbox);
frame = new QGroupBox();
hbox = new QHBoxLayout();
hbox1->addWidget(frame);
hbox->addWidget( snapSeconds);
hbox->addWidget( new QLabel( tr("Seconds") ) );
frame->setLayout(hbox);
frame1 = new QGroupBox(tr("Pause on State Load:"));
vbox1 = new QVBoxLayout();
frame1->setLayout(vbox1);
g_config->getOption("SDL.StateRecorderPauseOnLoad", &opt);
pauseOnLoadCbox = new QComboBox();
pauseOnLoadCbox->addItem( tr("No"), StateRecorderConfigData::NO_PAUSE );
pauseOnLoadCbox->addItem( tr("Temporary"), StateRecorderConfigData::TEMPORARY_PAUSE );
pauseOnLoadCbox->addItem( tr("Full"), StateRecorderConfigData::FULL_PAUSE );
pauseOnLoadCbox->setCurrentIndex( opt );
connect( pauseOnLoadCbox, SIGNAL(currentIndexChanged(int)), this, SLOT(pauseOnLoadChanged(int)) );
g_config->getOption("SDL.StateRecorderPauseDuration", &opt);
pauseDuration = new QSpinBox();
pauseDuration->setMinimum(0);
pauseDuration->setMaximum(60);
pauseDuration->setValue(opt);
vbox1->addWidget(pauseOnLoadCbox);
frame = new QGroupBox( tr("Duration:") );
hbox = new QHBoxLayout();
vbox1->addWidget(frame);
hbox->addWidget( pauseDuration);
hbox->addWidget( new QLabel( tr("Seconds") ) );
frame->setLayout(hbox);
grid->addWidget(frame1, 4, 3, 2, 1);
frame = new QGroupBox( tr("Memory Usage:") );
memStatsGrid = new QGridLayout();
numSnapsLbl = new QLineEdit();
snapMemSizeLbl = new QLineEdit();
totalMemUsageLbl = new QLineEdit();
saveTimeLbl = new QLineEdit();
numSnapsLbl->setReadOnly(true);
snapMemSizeLbl->setReadOnly(true);
totalMemUsageLbl->setReadOnly(true);
saveTimeLbl->setReadOnly(true);
grid->addWidget(frame, 1, 3, 2, 2);
frame->setLayout(memStatsGrid);
memStatsGrid->addWidget( new QLabel( tr("Number of\nSnapshots:") ), 0, 0 );
memStatsGrid->addWidget( numSnapsLbl, 0, 1 );
memStatsGrid->addWidget( new QLabel( tr("Snapshot Size:") ), 1, 0 );
memStatsGrid->addWidget( snapMemSizeLbl, 1, 1 );
memStatsGrid->addWidget( new QLabel( tr("Total Size:") ), 2, 0 );
memStatsGrid->addWidget( totalMemUsageLbl, 2, 1 );
frame = new QGroupBox( tr("CPU Usage:") );
hbox = new QHBoxLayout();
frame->setLayout(hbox);
hbox->addWidget(new QLabel(tr("Snapshot\nSave Time:")), 2);
hbox->addWidget(saveTimeLbl, 2);
grid->addWidget(frame, 3, 3, 1, 1);
mainLayout->addLayout(grid);
frame1 = new QGroupBox(tr("Recorder Status"));
sysStatusGrid = new QGridLayout();
frame1->setLayout(sysStatusGrid);
frame = new QGroupBox();
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 0, 0, 1, 2);
frame->setLayout(hbox);
recStatusLbl = new QLineEdit();
recStatusLbl->setReadOnly(true);
startStopButton = new QPushButton( tr("Start") );
hbox->addWidget( new QLabel(tr("State:")), 1 );
hbox->addWidget( recStatusLbl, 2 );
hbox->addWidget( startStopButton, 1 );
updateStartStopBuffon();
updateRecorderStatusLabel();
connect(startStopButton, SIGNAL(clicked(void)), this, SLOT(startStopClicked(void)));
connect(snapFrameSelBtn, SIGNAL(clicked(void)), this, SLOT(snapFrameModeClicked(void)));
connect(snapTimeSelBtn , SIGNAL(clicked(void)), this, SLOT(snapTimeModeClicked(void)));
frame = new QGroupBox();
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 0, 2, 1, 1);
frame->setLayout(hbox);
recBufSizeLbl = new QLineEdit();
recBufSizeLbl->setReadOnly(true);
hbox->addWidget( new QLabel(tr("Buffer Size:")), 1 );
hbox->addWidget( recBufSizeLbl, 1 );
frame = new QGroupBox( tr("Buffer Use:") );
hbox = new QHBoxLayout();
sysStatusGrid->addWidget( frame, 1, 0, 1, 3);
frame->setLayout(hbox);
bufUsage = new QProgressBar();
bufUsage->setToolTip( tr("% use of history record buffer.") );
bufUsage->setOrientation( Qt::Horizontal );
bufUsage->setMinimum( 0 );
bufUsage->setMaximum( 100 );
bufUsage->setValue( 0 );
hbox->addWidget(bufUsage);
updateBufferSizeStatus();
mainLayout->addWidget(frame1);
hbox = new QHBoxLayout();
mainLayout->addLayout(hbox);
closeButton = new QPushButton( tr("Close") );
applyButton = new QPushButton( tr("Apply") );
closeButton->setIcon( style()->standardIcon( QStyle::SP_DialogCloseButton ) );
applyButton->setIcon( style()->standardIcon( QStyle::SP_DialogApplyButton ) );
connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)));
connect(applyButton, SIGNAL(clicked(void)), this, SLOT(applyChanges(void)));
hbox->addWidget(applyButton, 1);
hbox->addStretch(8);
hbox->addWidget(closeButton, 1);
setLayout(mainLayout);
restoreGeometry(settings.value("stateRecorderWindow/geometry").toByteArray());
recalcMemoryUsage();
pauseOnLoadChanged( pauseOnLoadCbox->currentIndex() );
updateTimer = new QTimer(this);
connect(updateTimer, &QTimer::timeout, this, &StateRecorderDialog_t::updatePeriodic);
updateTimer->start(1000); // 1hz
}
//----------------------------------------------------------------------------
StateRecorderDialog_t::~StateRecorderDialog_t(void)
{
//printf("Destroy State Recorder Config Window\n");
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::closeEvent(QCloseEvent *event)
{
QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry());
if (dataSavedCheck())
{
done(0);
deleteLater();
event->accept();
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::closeWindow(void)
{
QSettings settings;
settings.setValue("stateRecorderWindow/geometry", saveGeometry());
if (dataSavedCheck())
{
done(0);
deleteLater();
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::packConfig( StateRecorderConfigData &config )
{
config.timingMode = snapTimeSelBtn->isChecked() ?
StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES;
config.framesBetweenSnaps = snapFrames->value();
config.historyDurationMinutes = static_cast<float>( historyDuration->value() );
config.timeBetweenSnapsMinutes = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
config.compressionLevel = cmprLvlCbox->currentData().toInt();
config.loadPauseTimeSeconds = pauseDuration->value();
config.pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>( pauseOnLoadCbox->currentData().toInt() );
}
//----------------------------------------------------------------------------
bool StateRecorderDialog_t::dataSavedCheck(void)
{
bool okToClose = true;
const StateRecorderConfigData &curConfig = FCEU_StateRecorderGetConfigData();
StateRecorderConfigData selConfig;
packConfig( selConfig );
if ( selConfig.compare( curConfig ) == false )
{
QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"),
tr("Setting selections have not yet been saved.\nDo you wish to save/apply the new settings?"),
QMessageBox::No | QMessageBox::Yes, this);
msgBox.setDefaultButton( QMessageBox::Yes );
int ret = msgBox.exec();
if ( ret == QMessageBox::Yes )
{
applyChanges();
}
}
return okToClose;
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::applyChanges(void)
{
StateRecorderConfigData config;
packConfig( config );
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderSetEnabled( recorderEnable->isChecked() );
FCEU_StateRecorderSetConfigData( config );
if (FCEU_StateRecorderRunning())
{
QMessageBox msgBox(QMessageBox::Question, tr("State Recorder"),
tr("New settings will not take effect until state recorder is restarted. Do you wish to restart?"),
QMessageBox::No | QMessageBox::Yes, this);
msgBox.setDefaultButton( QMessageBox::Yes );
int ret = msgBox.exec();
if ( ret == QMessageBox::Yes )
{
FCEU_StateRecorderStop();
FCEU_StateRecorderStart();
updateStatusDisplay();
}
}
FCEU_WRAPPER_UNLOCK();
g_config->setOption("SDL.StateRecorderHistoryDurationMin", historyDuration->value() );
g_config->setOption("SDL.StateRecorderTimingMode", snapTimeSelBtn->isChecked() ? 1 : 0);
g_config->setOption("SDL.StateRecorderFramesBetweenSnaps", snapFrames->value() );
g_config->setOption("SDL.StateRecorderTimeBetweenSnapsMin", snapMinutes->value() );
g_config->setOption("SDL.StateRecorderTimeBetweenSnapsSec", snapSeconds->value() );
g_config->setOption("SDL.StateRecorderCompressionLevel", config.compressionLevel);
g_config->setOption("SDL.StateRecorderPauseOnLoad", config.pauseOnLoad);
g_config->setOption("SDL.StateRecorderPauseDuration", config.loadPauseTimeSeconds);
g_config->setOption("SDL.StateRecorderEnable", recorderEnable->isChecked() );
g_config->save();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::startStopClicked(void)
{
FCEU_WRAPPER_LOCK();
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
FCEU_StateRecorderStop();
}
else
{
FCEU_StateRecorderStart();
}
updateStatusDisplay();
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::snapFrameModeClicked(void)
{
snapUseTime = false;
snapTimeGroup->setEnabled(false);
snapFramesGroup->setEnabled(true);
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::snapTimeModeClicked(void)
{
snapUseTime = true;
snapFramesGroup->setEnabled(false);
snapTimeGroup->setEnabled(true);
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateStartStopBuffon(void)
{
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
startStopButton->setText( tr("Stop") );
startStopButton->setIcon( style()->standardIcon( QStyle::SP_MediaStop ) );
}
else
{
startStopButton->setText( tr("Start") );
startStopButton->setIcon( QIcon(":icons/media-record.png") );
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateBufferSizeStatus(void)
{
char stmp[64];
int numSnapsSaved = FCEU_StateRecorderGetNumSnapsSaved();
int maxSnaps = FCEU_StateRecorderGetMaxSnaps();
snprintf( stmp, sizeof(stmp), "%i", maxSnaps );
recBufSizeLbl->setText( tr(stmp) );
if (maxSnaps > 0)
{
bufUsage->setMaximum( maxSnaps );
}
bufUsage->setValue( numSnapsSaved );
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateRecorderStatusLabel(void)
{
bool isRunning = FCEU_StateRecorderRunning();
if (isRunning)
{
recStatusLbl->setText( tr("Recording") );
}
else
{
recStatusLbl->setText( tr("Off") );
}
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updateStatusDisplay(void)
{
updateStartStopBuffon();
updateRecorderStatusLabel();
updateBufferSizeStatus();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::updatePeriodic(void)
{
FCEU_WRAPPER_LOCK();
updateStatusDisplay();
FCEU_WRAPPER_UNLOCK();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::enableChanged(int val)
{
bool ena = val ? true : false;
FCEU_WRAPPER_LOCK();
FCEU_StateRecorderSetEnabled( ena );
FCEU_WRAPPER_UNLOCK();
g_config->setOption("SDL.StateRecorderEnable", ena );
g_config->save();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::spinBoxValueChanged(int newValue)
{
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::compressionLevelChanged(int newValue)
{
recalcMemoryUsage();
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::pauseOnLoadChanged(int index)
{
StateRecorderConfigData::PauseType pauseOnLoad;
pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>( pauseOnLoadCbox->currentData().toInt() );
pauseDuration->setEnabled( pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE );
}
//----------------------------------------------------------------------------
void StateRecorderDialog_t::recalcMemoryUsage(void)
{
char stmp[64];
float fsnapMin = 1.0;
float fhistMin = static_cast<float>( historyDuration->value() );
if (snapUseTime)
{
fsnapMin = static_cast<float>( snapMinutes->value() ) +
( static_cast<float>( snapSeconds->value() ) / 60.0f );
}
else
{
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double hz = ( ((double)fps) / 16777216.0 );
fsnapMin = static_cast<double>(snapFrames->value()) / (hz * 60.0);
}
float fnumSnaps = fhistMin / fsnapMin;
float fsnapSize = 10.0f * 1024.0f;
float ftotalSize;
constexpr float oneKiloByte = 1024.0f;
constexpr float oneMegaByte = 1024.0f * 1024.0f;
int inumSnaps = static_cast<int>( fnumSnaps + 0.5f );
sprintf( stmp, "%i", inumSnaps );
numSnapsLbl->setText( tr(stmp) );
saveTimeMs = 0.0;
if (GameInfo)
{
constexpr int numIterations = 10;
double ts_start, ts_end;
FCEU_WRAPPER_LOCK();
EMUFILE_MEMORY em;
int compressionLevel = cmprLvlCbox->currentData().toInt();
ts_start = getHighPrecTimeStamp();
// Perform State Save multiple times to get a good average
// on what the compression delays will be.
for (int i=0; i<numIterations; i++)
{
em.set_len(0);
FCEUSS_SaveMS( &em, compressionLevel );
}
ts_end = getHighPrecTimeStamp();
saveTimeMs = (ts_end - ts_start) * 1000.0 / static_cast<double>(numIterations);
fsnapSize = static_cast<float>( em.size() );
FCEU_WRAPPER_UNLOCK();
}
if (fsnapSize >= oneKiloByte)
{
sprintf( stmp, "%.02f kB", fsnapSize / oneKiloByte );
}
else
{
sprintf( stmp, "%.0f B", fsnapSize );
}
snapMemSizeLbl->setText( tr(stmp) );
ftotalSize = fsnapSize * static_cast<float>(inumSnaps);
if (ftotalSize >= oneMegaByte)
{
sprintf( stmp, "%.02f MB", ftotalSize / oneMegaByte );
}
else if (ftotalSize >= oneKiloByte)
{
sprintf( stmp, "%.02f kB", ftotalSize / oneKiloByte );
}
else
{
sprintf( stmp, "%.0f B", ftotalSize );
}
totalMemUsageLbl->setText( tr(stmp) );
sprintf( stmp, "%.02f ms", saveTimeMs);
saveTimeLbl->setText( tr(stmp) );
}
//----------------------------------------------------------------------------

View File

@ -0,0 +1,83 @@
// StateRecorderConf.h
//
#pragma once
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <QRadioButton>
#include <QProgressBar>
#include <QLineEdit>
#include <QLabel>
#include <QFrame>
#include <QGroupBox>
#include <QTimer>
struct StateRecorderConfigData;
class StateRecorderDialog_t : public QDialog
{
Q_OBJECT
public:
StateRecorderDialog_t(QWidget *parent = 0);
~StateRecorderDialog_t(void);
protected:
void closeEvent(QCloseEvent *event);
QSpinBox *snapMinutes;
QSpinBox *snapSeconds;
QSpinBox *snapFrames;
QSpinBox *historyDuration;
QSpinBox *pauseDuration;
QCheckBox *recorderEnable;
QLineEdit *numSnapsLbl;
QLineEdit *snapMemSizeLbl;
QLineEdit *totalMemUsageLbl;
QLineEdit *saveTimeLbl;
QPushButton *applyButton;
QPushButton *closeButton;
QComboBox *cmprLvlCbox;
QComboBox *pauseOnLoadCbox;
QGroupBox *snapTimeGroup;
QGroupBox *snapFramesGroup;
QRadioButton *snapFrameSelBtn;
QRadioButton *snapTimeSelBtn;
QLineEdit *recStatusLbl;
QLineEdit *recBufSizeLbl;
QPushButton *startStopButton;
QProgressBar *bufUsage;
QTimer *updateTimer;
double saveTimeMs;
bool snapUseTime;
bool dataSavedCheck(void);
void recalcMemoryUsage(void);
void updateStartStopBuffon(void);
void updateRecorderStatusLabel(void);
void updateBufferSizeStatus(void);
void updateStatusDisplay(void);
void packConfig( StateRecorderConfigData &config );
public slots:
void closeWindow(void);
private slots:
void applyChanges(void);
void updatePeriodic(void);
void startStopClicked(void);
void snapTimeModeClicked(void);
void snapFrameModeClicked(void);
void spinBoxValueChanged(int newValue);
void enableChanged(int);
void compressionLevelChanged(int newValue);
void pauseOnLoadChanged(int index);
};

View File

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

View File

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

View File

@ -295,6 +295,12 @@ int getHotKeyConfig( int i, const char **nameOut, const char **keySeqOut, const
case HK_SELECT_STATE_PREV:
name = "SelectStatePrev"; keySeq = ""; title = "Select Previous State Slot"; group = "State";
break;
case HK_LOAD_PREV_STATE:
name = "LoadPrevState"; keySeq = ""; title = "Load Previous Recorded State"; group = "State";
break;
case HK_LOAD_NEXT_STATE:
name = "LoadNextState"; keySeq = ""; title = "Load Next Recorded State"; group = "State";
break;
case HK_VOLUME_MUTE:
name = "VolumeMute"; keySeq = ""; title = "Sound Volume Mute"; group = "Sound";
break;
@ -748,6 +754,16 @@ InitConfig()
config->addOption("loadstate", "SDL.AutoLoadState", INVALID_STATE);
config->addOption("savestate", "SDL.AutoSaveState", INVALID_STATE);
config->addOption("SDL.StateRecorderEnable", false);
config->addOption("SDL.StateRecorderHistoryDurationMin", 15);
config->addOption("SDL.StateRecorderTimingMode", 0);
config->addOption("SDL.StateRecorderFramesBetweenSnaps", 60);
config->addOption("SDL.StateRecorderTimeBetweenSnapsMin", 0);
config->addOption("SDL.StateRecorderTimeBetweenSnapsSec", 3);
config->addOption("SDL.StateRecorderCompressionLevel", 0);
config->addOption("SDL.StateRecorderPauseOnLoad", 1);
config->addOption("SDL.StateRecorderPauseDuration", 3);
//TODO implement this
config->addOption("periodicsaves", "SDL.PeriodicSaves", 0);

View File

@ -34,6 +34,9 @@ enum HOTKEY {
HK_SELECT_STATE_5, HK_SELECT_STATE_6, HK_SELECT_STATE_7, HK_SELECT_STATE_8, HK_SELECT_STATE_9,
HK_SELECT_STATE_NEXT, HK_SELECT_STATE_PREV,
// State Recorder
HK_LOAD_PREV_STATE, HK_LOAD_NEXT_STATE,
// GUI
HK_FULLSCREEN, HK_MAIN_MENU_HIDE,

View File

@ -52,6 +52,8 @@
#include "../../fceu.h"
#include "../../cheat.h"
#include "../../movie.h"
#include "../../state.h"
#include "../../profiler.h"
#include "../../version.h"
#ifdef _S9XLUA_H
@ -60,6 +62,7 @@
#include "common/os_utils.h"
#include "common/configSys.h"
#include "utils/timeStamp.h"
#include "../../oldmovie.h"
#include "../../types.h"
@ -189,7 +192,23 @@ const char *FCEUD_GetCompilerString(void)
uint64
FCEUD_GetTime(void)
{
return SDL_GetTicks();
uint64 t;
if (FCEU::timeStampModuleInitialized())
{
FCEU::timeStampRecord ts;
ts.readNew();
t = ts.toCounts();
}
else
{
t = (double)SDL_GetTicks();
t = t * 1e-3;
}
return t;
}
/**
@ -199,7 +218,13 @@ uint64
FCEUD_GetTimeFreq(void)
{
// SDL_GetTicks() is in milliseconds
return 1000;
uint64 f = 1000;
if (FCEU::timeStampModuleInitialized())
{
f = FCEU::timeStampRecord::countFreq();
}
return f;
}
/**
@ -972,6 +997,45 @@ int fceuWrapperInit( int argc, char *argv[] )
// load the hotkeys from the config life
setHotKeys();
// Initialize the State Recorder
{
bool srEnable = false;
bool srUseTimeMode = false;
int srHistDurMin = 15;
int srFramesBtwSnaps = 60;
int srTimeBtwSnapsMin = 0;
int srTimeBtwSnapsSec = 3;
int srCompressionLevel = 0;
int pauseOnLoadTime = 3;
int pauseOnLoad = StateRecorderConfigData::TEMPORARY_PAUSE;
g_config->getOption("SDL.StateRecorderEnable", &srEnable);
g_config->getOption("SDL.StateRecorderTimingMode", &srUseTimeMode);
g_config->getOption("SDL.StateRecorderHistoryDurationMin", &srHistDurMin);
g_config->getOption("SDL.StateRecorderFramesBetweenSnaps", &srFramesBtwSnaps);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsMin", &srTimeBtwSnapsMin);
g_config->getOption("SDL.StateRecorderTimeBetweenSnapsSec", &srTimeBtwSnapsSec);
g_config->getOption("SDL.StateRecorderCompressionLevel", &srCompressionLevel);
g_config->getOption("SDL.StateRecorderPauseOnLoad", &pauseOnLoad);
g_config->getOption("SDL.StateRecorderPauseDuration", &pauseOnLoadTime);
StateRecorderConfigData srConfig;
srConfig.historyDurationMinutes = srHistDurMin;
srConfig.timingMode = srUseTimeMode ?
StateRecorderConfigData::TIME : StateRecorderConfigData::FRAMES;
srConfig.timeBetweenSnapsMinutes = static_cast<float>( srTimeBtwSnapsMin ) +
( static_cast<float>( srTimeBtwSnapsSec ) / 60.0f );
srConfig.framesBetweenSnaps = srFramesBtwSnaps;
srConfig.compressionLevel = srCompressionLevel;
srConfig.loadPauseTimeSeconds = pauseOnLoadTime;
srConfig.pauseOnLoad = static_cast<StateRecorderConfigData::PauseType>(pauseOnLoad);
FCEU_StateRecorderSetEnabled( srEnable );
FCEU_StateRecorderSetConfigData( srConfig );
}
// Rom Load
if (romIndex >= 0)
{
QFileInfo fi( argv[romIndex] );
@ -1425,6 +1489,9 @@ int fceuWrapperUpdate( void )
emulatorHasMutex = 0;
#ifdef __FCEU_PROFILER_ENABLE__
FCEU_profiler_log_thread_activity();
#endif
while ( SpeedThrottle() )
{
// Input device processing is in main thread
@ -1438,25 +1505,27 @@ int fceuWrapperUpdate( void )
emulatorHasMutex = 0;
#ifdef __FCEU_PROFILER_ENABLE__
FCEU_profiler_log_thread_activity();
#endif
msleep( 100 );
}
return 0;
}
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
static int minizip_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int idx=0, ret;
unzFile zf;
unz_file_info fi;
char filename[512];
ArchiveScanRecord rec;
zf = unzOpen( fname.c_str() );
zf = unzOpen( filepath );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return rec;
return -1;
}
rec.type = 0;
@ -1486,18 +1555,313 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
unzClose( zf );
return 0;
}
#ifdef _USE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
static int libarchive_ScanArchive( const char *filepath, ArchiveScanRecord &rec)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
a = archive_read_new();
if (a == nullptr)
{
return -1;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return -1;
}
r = archive_read_open_filename(a, filepath, 10240);
if (r)
{
archive_read_free(a);
return -1;
}
rec.type = 1;
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
const char *filename = archive_entry_pathname(entry);
FCEUARCHIVEFILEINFO_ITEM item;
item.name.assign( filename );
item.size = archive_entry_size(entry);
item.index = idx; idx++;
rec.files.push_back( item );
}
rec.numFilesInArchive = idx;
archive_read_free(a);
return 0;
}
#endif
ArchiveScanRecord FCEUD_ScanArchive(std::string fname)
{
int ret = -1;
ArchiveScanRecord rec;
#ifdef _USE_LIBARCHIVE
ret = libarchive_ScanArchive( fname.c_str(), rec );
#endif
if (ret == -1)
{
minizip_ScanArchive( fname.c_str(), rec );
}
return rec;
}
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
static FCEUFILE* minizip_OpenArchive(ArchiveScanRecord& asr, std::string &fname, std::string *searchFile, int innerIndex )
{
int ret;
FCEUFILE* fp = 0;
int ret, idx=0;
FCEUFILE* fp = nullptr;
void *tmpMem = nullptr;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
bool foundFile = false;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if (searchFile)
{
if ( strcmp( searchFile->c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
idx++;
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
//printf("Loading via minizip\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
#ifdef _USE_LIBARCHIVE
static FCEUFILE* libarchive_OpenArchive( ArchiveScanRecord& asr, std::string& fname, std::string *searchFile, int innerIndex)
{
int r, idx=0;
struct archive *a;
struct archive_entry *entry;
const char *filename = nullptr;
bool foundFile = false;
int fileSize = 0;
FCEUFILE* fp = nullptr;
a = archive_read_new();
if (a == nullptr)
{
archive_read_free(a);
return nullptr;
}
// Initialize decoders
r = archive_read_support_filter_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
// Initialize formats
r = archive_read_support_format_all(a);
if (r)
{
archive_read_free(a);
return nullptr;
}
r = archive_read_open_filename(a, fname.c_str(), 10240);
if (r)
{
archive_read_free(a);
return nullptr;
}
while (1)
{
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
{
break;
}
else if (r != ARCHIVE_OK)
{
printf("archive_read_next_header() %s\n", archive_error_string(a));
break;
}
filename = archive_entry_pathname(entry);
fileSize = archive_entry_size(entry);
if (searchFile)
{
if (strcmp( filename, searchFile->c_str() ) == 0)
{
foundFile = true; break;
}
}
else if ((innerIndex != -1) && (idx == innerIndex))
{
foundFile = true; break;
}
idx++;
}
if (foundFile && (fileSize > 0))
{
const void *buff;
size_t size, totalSize = 0;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
//printf("Loading via libarchive\n");
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fileSize);
while (1)
{
r = archive_read_data_block(a, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
{
break;
}
if (r != ARCHIVE_OK)
{
break;
}
//printf("Read: %p Size:%zu Offset:%llu\n", buff, size, (long long int)offset);
ms->fwrite( buff, size );
totalSize += size;
}
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = idx;
fp->mode = FCEUFILE::READ;
fp->size = totalSize;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
}
archive_read_free(a);
return fp;
}
#endif
FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel)
{
FCEUFILE* fp = nullptr;
std::string searchFile;
if ( innerFilename != NULL )
@ -1547,75 +1911,14 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
}
}
zf = unzOpen( fname.c_str() );
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive(asr, fname, &searchFile, -1 );
#endif
if ( zf == NULL )
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, &searchFile, -1 );
}
//printf("Searching for %s in %s \n", searchFile.c_str(), fname.c_str() );
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( strcmp( searchFile.c_str(), filename ) == 0 )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = searchFile;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}
@ -1628,82 +1931,16 @@ FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::str
FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int innerIndex, int* userCancel)
{
int ret, idx=0;
FCEUFILE* fp = 0;
unzFile zf;
unz_file_info fi;
char filename[512];
char foundFile = 0;
void *tmpMem = NULL;
FCEUFILE* fp = nullptr;
zf = unzOpen( fname.c_str() );
if ( zf == NULL )
#ifdef _USE_LIBARCHIVE
fp = libarchive_OpenArchive( asr, fname, nullptr, innerIndex );
#endif
if (fp == nullptr)
{
//printf("Error: Failed to open Zip File: '%s'\n", fname.c_str() );
return fp;
fp = minizip_OpenArchive(asr, fname, nullptr, innerIndex);
}
ret = unzGoToFirstFile( zf );
//printf("unzGoToFirstFile: %i \n", ret );
while ( ret == 0 )
{
unzGetCurrentFileInfo( zf, &fi, filename, sizeof(filename), NULL, 0, NULL, 0 );
//printf("Filename: %u '%s' \n", fi.uncompressed_size, filename );
if ( idx == innerIndex )
{
//printf("Found Filename: %u '%s' \n", fi.uncompressed_size, filename );
foundFile = 1; break;
}
idx++;
ret = unzGoToNextFile( zf );
//printf("unzGoToNextFile: %i \n", ret );
}
if ( !foundFile )
{
unzClose( zf );
return fp;
}
tmpMem = ::malloc( fi.uncompressed_size );
if ( tmpMem == NULL )
{
unzClose( zf );
return fp;
}
EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(fi.uncompressed_size);
unzOpenCurrentFile( zf );
unzReadCurrentFile( zf, tmpMem, fi.uncompressed_size );
unzCloseCurrentFile( zf );
ms->fwrite( tmpMem, fi.uncompressed_size );
free( tmpMem );
//if we extracted the file correctly
fp = new FCEUFILE();
fp->archiveFilename = fname;
fp->filename = filename;
fp->fullFilename = fp->archiveFilename + "|" + fp->filename;
fp->archiveIndex = ret;
fp->mode = FCEUFILE::READ;
fp->size = fi.uncompressed_size;
fp->stream = ms;
fp->archiveCount = (int)asr.numFilesInArchive;
ms->fseek(0,SEEK_SET); //rewind so that the rom analyzer sees a freshly opened file
unzClose( zf );
return fp;
}

View File

@ -1230,28 +1230,15 @@ void GetMouseData(uint32 (&d)[3])
b = 0; // map mouse buttons
if (consoleWindow->viewport_SDL)
if (consoleWindow->viewport_Interface)
{
consoleWindow->viewport_SDL->getNormalizedCursorPos(nx, ny);
consoleWindow->viewport_Interface->getNormalizedCursorPos(nx, ny);
if (consoleWindow->viewport_SDL->getMouseButtonState(Qt::LeftButton))
if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::LeftButton))
{
b |= 0x01;
}
if (consoleWindow->viewport_SDL->getMouseButtonState(Qt::RightButton))
{
b |= 0x02;
}
}
else if (consoleWindow->viewport_GL)
{
consoleWindow->viewport_GL->getNormalizedCursorPos(nx, ny);
if (consoleWindow->viewport_GL->getMouseButtonState(Qt::LeftButton))
{
b |= 0x01;
}
if (consoleWindow->viewport_GL->getMouseButtonState(Qt::RightButton))
if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::RightButton))
{
b |= 0x02;
}

View File

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

View File

@ -22,6 +22,7 @@
#include "Qt/sdl.h"
#include "Qt/throttle.h"
#include "utils/timeStamp.h"
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
#include <time.h>
@ -36,7 +37,8 @@ static const double Fastest = 32; // 32x speed (around 1920 fps on NTSC)
static const double Normal = 1.0; // 1x speed (around 60 fps on NTSC)
static uint32 frameLateCounter = 0;
static double Lasttime=0, Nexttime=0, Latetime=0;
static FCEU::timeStampRecord Lasttime, Nexttime, Latetime;
static FCEU::timeStampRecord DesiredFrameTime, HalfFrameTime, QuarterFrameTime, DoubleFrameTime;
static double desired_frametime = (1.0 / 60.099823);
static double desired_frameRate = (60.099823);
static double baseframeRate = (60.099823);
@ -60,21 +62,22 @@ extern bool turbo;
double getHighPrecTimeStamp(void)
{
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec ts;
double t;
clock_gettime( CLOCK_REALTIME, &ts );
if (FCEU::timeStampModuleInitialized())
{
FCEU::timeStampRecord ts;
t = (double)ts.tv_sec + (double)(ts.tv_nsec * 1.0e-9);
#else
double t;
ts.readNew();
t = (double)SDL_GetTicks();
t = t * 1e-3;
#endif
t = ts.toSeconds();
}
else
{
t = (double)SDL_GetTicks();
t = t * 1e-3;
}
return t;
}
@ -117,9 +120,9 @@ static void setTimer( double hz )
//printf("Timer Set: %li ns\n", ispec.it_value.tv_nsec );
Lasttime = getHighPrecTimeStamp();
Nexttime = Lasttime + desired_frametime;
Latetime = Nexttime + (desired_frametime*0.50);
Lasttime.readNew();
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
#endif
@ -268,10 +271,17 @@ RefreshThrottleFPS(void)
if ( T < 0 ) T = 1;
DesiredFrameTime.fromSeconds( desired_frametime );
HalfFrameTime = DesiredFrameTime / 2;
QuarterFrameTime = DesiredFrameTime / 4;
DoubleFrameTime = DesiredFrameTime * 2;
//printf("FrameTime: %f %f %f %f \n", DesiredFrameTime.toSeconds(),
// HalfFrameTime.toSeconds(), QuarterFrameTime.toSeconds(), DoubleFrameTime.toSeconds() );
//printf("FrameTime: %llu %llu %f %lf \n", fps, fps >> 24, hz, desired_frametime );
Lasttime=0;
Nexttime=0;
Lasttime.zero();
Nexttime.zero();
InFrame=0;
#ifdef __linux__
@ -295,18 +305,17 @@ double getFrameRateAdjustmentRatio(void)
return frmRateAdjRatio;
}
int highPrecSleep( double timeSeconds )
static int highPrecSleep( FCEU::timeStampRecord &ts )
{
int ret = 0;
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec req, rem;
req.tv_sec = (long)timeSeconds;
req.tv_nsec = (long)((timeSeconds - (double)req.tv_sec) * 1e9);
req = ts.toTimeSpec();
ret = nanosleep( &req, &rem );
#else
SDL_Delay( (long)(timeSeconds * 1e3) );
SDL_Delay( ts.toMilliSeconds() );
#endif
return ret;
}
@ -321,39 +330,37 @@ SpeedThrottle(void)
{
return 0; /* Done waiting */
}
double time_left;
double cur_time, idleStart;
double frame_time = desired_frametime;
double halfFrame = 0.500 * frame_time;
double quarterFrame = 0.250 * frame_time;
FCEU::timeStampRecord cur_time, idleStart, time_left;
idleStart = cur_time = getHighPrecTimeStamp();
cur_time.readNew();
idleStart = cur_time;
if (Lasttime < 1.0)
if (Lasttime.isZero())
{
//printf("Lasttime Reset\n");
Lasttime = cur_time;
Latetime = Lasttime + 2.0*frame_time;
Latetime = Lasttime + DoubleFrameTime;
}
if (!InFrame)
{
InFrame = 1;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
if (cur_time >= Nexttime)
{
time_left = 0;
time_left.zero();
}
else
{
time_left = Nexttime - cur_time;
}
if (time_left > 50)
if (time_left.toMilliSeconds() > 50)
{
time_left = 50;
time_left.fromMilliSeconds(50);
/* In order to keep input responsive, don't wait too long at once */
/* 50 ms wait gives us a 20 Hz responsetime which is nice. */
}
@ -379,7 +386,7 @@ SpeedThrottle(void)
}
}
}
else if ( time_left > 0 )
else if ( !time_left.isZero() )
{
highPrecSleep( time_left );
}
@ -392,7 +399,7 @@ SpeedThrottle(void)
}
}
#else
if ( time_left > 0 )
if ( !time_left.isZero() )
{
highPrecSleep( time_left );
}
@ -406,14 +413,15 @@ SpeedThrottle(void)
}
#endif
cur_time = getHighPrecTimeStamp();
cur_time.readNew();
if ( cur_time >= (Nexttime - quarterFrame) )
if ( cur_time >= (Nexttime - QuarterFrameTime) )
{
if ( keepFrameTimeStats )
{
FCEU::timeStampRecord diffTime = (cur_time - Lasttime);
frameDeltaCur = (cur_time - Lasttime);
frameDeltaCur = diffTime.toSeconds();
if ( frameDeltaCur < frameDeltaMin )
{
@ -424,7 +432,9 @@ SpeedThrottle(void)
frameDeltaMax = frameDeltaCur;
}
frameIdleCur = (cur_time - idleStart);
diffTime = (cur_time - idleStart);
frameIdleCur = diffTime.toSeconds();
if ( frameIdleCur < frameIdleMin )
{
@ -438,14 +448,14 @@ SpeedThrottle(void)
//printf("Frame Sleep Time: %f Target Error: %f us\n", time_left * 1e6, (cur_time - Nexttime) * 1e6 );
}
Lasttime = Nexttime;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
if ( cur_time >= Nexttime )
{
Lasttime = cur_time;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
return 0; /* Done waiting */
}

View File

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

View File

@ -179,10 +179,10 @@ public:
va_start(argptr, format);
vsprintf(tempbuf,format,argptr);
fwrite(tempbuf,amt);
fwrite(tempbuf,amt);
delete[] tempbuf;
va_end(argptr);
va_end(argptr);
return amt;
};

View File

@ -36,6 +36,7 @@
#include "unif.h"
#include "cheat.h"
#include "palette.h"
#include "profiler.h"
#include "state.h"
#include "movie.h"
#include "video.h"
@ -116,6 +117,7 @@ bool movieSubtitles = true; //Toggle for displaying movie subtitles
bool DebuggerWasUpdated = false; //To prevent the debugger from updating things without being updated.
bool AutoResumePlay = false;
char romNameWhenClosingEmulator[2048] = {0};
static unsigned int pauseTimer = 0;
FCEUGI::FCEUGI()
@ -208,6 +210,8 @@ static void FCEU_CloseGame(void)
GameInterface(GI_CLOSE);
FCEU_StateRecorderStop();
FCEUI_StopMovie();
ResetExState(0, 0);
@ -592,6 +596,12 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen
}
FCEU_fclose(fp);
if ( FCEU_StateRecorderIsEnabled() )
{
FCEU_StateRecorderStart();
}
return GameInfo;
}
@ -725,6 +735,7 @@ extern unsigned int frameAdvHoldTimer;
///Skip may be passed in, if FRAMESKIP is #defined, to cause this to emulate more than one frame
void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int skip) {
FCEU_PROFILE_FUNC(prof, "Emulate Single Frame");
//skip initiates frame skip if 1, or frame skip and sound skip if 2
FCEU_MAYBE_UNUSED int r;
int ssize;
@ -756,6 +767,22 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
#endif
}
if (EmulationPaused & EMULATIONPAUSED_TIMER)
{
if (pauseTimer > 0)
{
pauseTimer--;
}
else
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
EmulationPaused &= ~EMULATIONPAUSED_TIMER;
}
}
if (EmulationPaused & EMULATIONPAUSED_FA)
{
// the user is holding Frame Advance key
@ -779,7 +806,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
RefreshThrottleFPS();
}
#endif
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) )
{
// emulator is paused
memcpy(XBuf, XBackBuf, 256*256);
@ -793,6 +820,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski
AutoFire();
UpdateAutosave();
FCEU_StateRecorderUpdate();
#ifdef _S9XLUA_H
FCEU_LuaFrameBoundary();
@ -1254,6 +1282,33 @@ void FCEUI_FrameAdvance(void) {
frameAdvanceRequested = true;
}
void FCEUI_PauseForDuration(int secs)
{
int framesPerSec;
// If already paused, do nothing
if (EmulationPaused & EMULATIONPAUSED_PAUSED)
{
return;
}
if (PAL || dendy)
{
framesPerSec = 50;
}
else
{
framesPerSec = 60;
}
pauseTimer = framesPerSec * secs;
EmulationPaused |= EMULATIONPAUSED_TIMER;
}
int FCEUI_PauseFramesRemaining(void)
{
return (EmulationPaused & EMULATIONPAUSED_TIMER) ? pauseTimer : 0;
}
static int AutosaveCounter = 0;
void UpdateAutosave(void) {

View File

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

View File

@ -21,9 +21,6 @@ enum LuaMemHookType
LUAMEMHOOK_WRITE,
LUAMEMHOOK_READ,
LUAMEMHOOK_EXEC,
LUAMEMHOOK_WRITE_SUB,
LUAMEMHOOK_READ_SUB,
LUAMEMHOOK_EXEC_SUB,
LUAMEMHOOK_COUNT
};

View File

@ -689,7 +689,7 @@ BMAPPINGLocal bmap[] = {
{"", 171, Mapper171_Init},
{"", 172, Mapper172_Init},
{"", 173, Mapper173_Init},
// {"", 174, Mapper174_Init},
{"NTDec 5-in-1", 174, Mapper174_Init},
{"", 175, Mapper175_Init},
{"BMCFK23C", 176, BMCFK23C_Init}, // zero 26-may-2012 - well, i have some WXN junk games that use 176 for instance ????. i dont know what game uses this BMCFK23C as mapper 176. we'll have to make a note when we find it.
{"", 177, Mapper177_Init},
@ -720,7 +720,7 @@ BMAPPINGLocal bmap[] = {
{"", 202, Mapper202_Init},
{"", 203, Mapper203_Init},
{"", 204, Mapper204_Init},
{"", 205, Mapper205_Init},
{"JC-016-2", 205, Mapper205_Init},
{"NAMCOT 108 Rev. C", 206, Mapper206_Init}, // Deprecated, Used to be "DEIROM" whatever it means, but actually simple version of MMC3
{"TAITO X1-005 Rev. B", 207, Mapper207_Init},
{"", 208, Mapper208_Init},
@ -788,6 +788,8 @@ BMAPPINGLocal bmap[] = {
{"HP10xx/H20xx Boards", 260, BMCHPxx_Init},
{"810544-CA-1", 261, BMC810544CA1_Init},
{"AA6023/AA6023B", 268, AA6023_Init},
{"OK-411", 361, GN45_Init},
{"GN-45", 366, GN45_Init},
{"COOLGIRL", 342, COOLGIRL_Init },
{"FAM250/81-01-39-C/SCHI-24", 354, Mapper354_Init },

View File

@ -208,6 +208,7 @@ void Mapper170_Init(CartInfo *);
void Mapper171_Init(CartInfo *);
void Mapper172_Init(CartInfo *);
void Mapper173_Init(CartInfo *);
void Mapper174_Init(CartInfo *);
void Mapper175_Init(CartInfo *);
void Mapper177_Init(CartInfo *);
void Mapper178_Init(CartInfo *);
@ -280,6 +281,7 @@ void Mapper406_Init(CartInfo *);
void Mapper474_Init(CartInfo*);
void INX_007T_Init(CartInfo* info);
void GN45_Init(CartInfo *info); /* previously mapper 205 */
typedef struct {
const char *name;

View File

@ -26,7 +26,6 @@
#include "debug.h"
#include "debugsymboltable.h"
#include "sound.h"
#include "drawing.h"
#include "state.h"
#include "movie.h"
#include "driver.h"
@ -319,10 +318,6 @@ static const char* luaMemHookTypeStrings [] =
"MEMHOOK_WRITE",
"MEMHOOK_READ",
"MEMHOOK_EXEC",
"MEMHOOK_WRITE_SUB",
"MEMHOOK_READ_SUB",
"MEMHOOK_EXEC_SUB",
};
//make sure we have the right number of strings
@ -2431,53 +2426,17 @@ static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaul
return 0;
}
LuaMemHookType MatchHookTypeToCPU(lua_State* L, LuaMemHookType hookType)
{
int cpuID = 0;
int cpunameIndex = 0;
if(lua_type(L,2) == LUA_TSTRING)
cpunameIndex = 2;
else if(lua_type(L,3) == LUA_TSTRING)
cpunameIndex = 3;
if(cpunameIndex)
{
const char* cpuName = lua_tostring(L, cpunameIndex);
if(!stricmp(cpuName, "sub"))
cpuID = 1;
lua_remove(L, cpunameIndex);
}
switch(cpuID)
{
case 0:
return hookType;
case 1:
switch(hookType)
{
case LUAMEMHOOK_WRITE: return LUAMEMHOOK_WRITE_SUB;
case LUAMEMHOOK_READ: return LUAMEMHOOK_READ_SUB;
case LUAMEMHOOK_EXEC: return LUAMEMHOOK_EXEC_SUB;
default: return hookType;
}
}
return hookType;
}
static int memory_registerwrite(lua_State *L)
{
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1);
return memory_registerHook(L, LUAMEMHOOK_WRITE, 1);
}
FCEU_MAYBE_UNUSED
static int memory_registerread(lua_State *L)
{
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1);
return memory_registerHook(L, LUAMEMHOOK_READ, 1);
}
static int memory_registerexec(lua_State *L)
{
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_EXEC), 1);
return memory_registerHook(L, LUAMEMHOOK_EXEC, 1);
}
//adelikat: table pulled from GENS. credz nitsuja!
@ -6137,7 +6096,7 @@ static const struct luaL_reg memorylib [] = {
// memory hooks
{"registerwrite", memory_registerwrite},
//{"registerread", memory_registerread}, TODO
{"registerread", memory_registerread},
{"registerexec", memory_registerexec},
// alternate names
{"register", memory_registerwrite},

View File

@ -1734,10 +1734,17 @@ void FCEUPPU_Reset(void) {
void FCEUPPU_Power(void) {
int x;
memset(NTARAM, 0x00, 0x800);
memset(PALRAM, 0x00, 0x20);
memset(UPALRAM, 0x00, 0x03);
memset(SPRAM, 0x00, 0x100);
// initialize PPU memory regions according to settings
FCEU_MemoryRand(NTARAM, 0x800, true);
FCEU_MemoryRand(PALRAM, 0x20, true);
FCEU_MemoryRand(SPRAM, 0x100, true);
// palettes can only store values up to $3F, and PALRAM X4/X8/XC are mirrors of X0 for rendering purposes (UPALRAM is used for $2007 readback)
for (x = 0; x < 0x20; ++x) PALRAM[x] &= 0x3F;
UPALRAM[0] = PALRAM[0x04];
UPALRAM[1] = PALRAM[0x08];
UPALRAM[2] = PALRAM[0x0C];
PALRAM[0x0C] = PALRAM[0x08] = PALRAM[0x04] = PALRAM[0x00];
PALRAM[0x1C] = PALRAM[0x18] = PALRAM[0x14] = PALRAM[0x10];
FCEUPPU_Reset();
for (x = 0x2000; x < 0x4000; x += 8) {

367
src/profiler.cpp Normal file
View File

@ -0,0 +1,367 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// profiler.cpp
//
#ifdef __FCEU_PROFILER_ENABLE__
#include <stdio.h>
#ifdef __QT_DRIVER__
#include <QThread>
#endif
#include "utils/mutex.h"
#include "fceu.h"
#include "profiler.h"
namespace FCEU
{
static thread_local profileExecVector execList;
static thread_local profilerFuncMap threadProfileMap;
FILE *profilerManager::pLog = nullptr;
static profilerManager pMgr;
//-------------------------------------------------------------------------
//---- Function Profile Record
//-------------------------------------------------------------------------
funcProfileRecord::funcProfileRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral)
: fileLineNum(fileLineNumber), fileName(fileNameStringLiteral),
funcName(funcNameStringLiteral), comment(commentStringLiteral)
{
min.fromSeconds(9);
max.zero();
sum.zero();
numCalls = 0;
recursionCount = 0;
threadProfileMap.addRecord( fileNameStringLiteral, fileLineNumber,
funcNameStringLiteral, commentStringLiteral, this);
}
//-------------------------------------------------------------------------
void funcProfileRecord::reset(void)
{
min.fromSeconds(9);
max.zero();
sum.zero();
numCalls = 0;
}
//-------------------------------------------------------------------------
double funcProfileRecord::average(void)
{
double avg = 0.0;
if (numCalls)
{
avg = sum.toSeconds() / static_cast<double>(numCalls);
}
return avg;
}
//-------------------------------------------------------------------------
//---- Profile Scoped Function Class
//-------------------------------------------------------------------------
profileFuncScoped::profileFuncScoped( funcProfileRecord *recordIn )
{
rec = recordIn;
if (rec)
{
threadProfileMap.pushStack(rec);
start.readNew();
rec->numCalls++;
rec->recursionCount++;
}
}
//-------------------------------------------------------------------------
profileFuncScoped::~profileFuncScoped(void)
{
if (rec)
{
timeStampRecord ts, dt;
ts.readNew();
dt = ts - start;
rec->last = dt;
rec->sum += dt;
if (dt < rec->min) rec->min = dt;
if (dt > rec->max) rec->max = dt;
rec->recursionCount--;
execList._vec.push_back(*rec);
threadProfileMap.popStack(rec);
}
}
//-------------------------------------------------------------------------
//---- Profile Execution Vector
//-------------------------------------------------------------------------
profileExecVector::profileExecVector(void)
{
_vec.reserve( 10000 );
char threadName[128];
char fileName[256];
strcpy( threadName, "MainThread");
#ifdef __QT_DRIVER__
QThread *thread = QThread::currentThread();
if (thread)
{
//printf("Thread: %s\n", thread->objectName().toStdString().c_str());
strcpy( threadName, thread->objectName().toStdString().c_str());
}
#endif
sprintf( fileName, "fceux-profile-%s.log", threadName);
logFp = ::fopen(fileName, "w");
if (logFp == nullptr)
{
printf("Error: Failed to create profiler logfile: %s\n", fileName);
}
}
//-------------------------------------------------------------------------
profileExecVector::~profileExecVector(void)
{
if (logFp)
{
::fclose(logFp);
}
}
//-------------------------------------------------------------------------
void profileExecVector::update(void)
{
size_t n = _vec.size();
for (size_t i=0; i<n; i++)
{
funcProfileRecord &rec = _vec[i];
fprintf( logFp, "%s: %u %f %f %f %f\n", rec.funcName, rec.numCalls, rec.last.toSeconds(), rec.average(), rec.min.toSeconds(), rec.max.toSeconds());
}
_vec.clear();
}
//-------------------------------------------------------------------------
//---- Profile Function Record Map
//-------------------------------------------------------------------------
profilerFuncMap::profilerFuncMap(void)
{
//printf("profilerFuncMap Constructor: %p\n", this);
pMgr.addThreadProfiler(this);
_map_it = _map.begin();
}
//-------------------------------------------------------------------------
profilerFuncMap::~profilerFuncMap(void)
{
//printf("profilerFuncMap Destructor: %p\n", this);
pMgr.removeThreadProfiler(this);
//{
// autoScopedLock aLock(_mapMtx);
// for (auto it = _map.begin(); it != _map.end(); it++)
// {
// delete it->second;
// }
// _map.clear();
//}
}
//-------------------------------------------------------------------------
void profilerFuncMap::pushStack(funcProfileRecord *rec)
{
stack.push_back(rec);
}
//-------------------------------------------------------------------------
void profilerFuncMap::popStack(funcProfileRecord *rec)
{
stack.pop_back();
}
//-------------------------------------------------------------------------
int profilerFuncMap::addRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral,
funcProfileRecord *rec )
{
autoScopedLock aLock(_mapMtx);
char lineString[64];
sprintf( lineString, ":%i", fileLineNumber);
std::string fname(fileNameStringLiteral);
fname.append( lineString );
_map[fname] = rec;
return 0;
}
//-------------------------------------------------------------------------
funcProfileRecord *profilerFuncMap::findRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral,
bool create)
{
autoScopedLock aLock(_mapMtx);
char lineString[64];
funcProfileRecord *rec = nullptr;
sprintf( lineString, ":%i", fileLineNumber);
std::string fname(fileNameStringLiteral);
fname.append( lineString );
auto it = _map.find(fname);
if (it != _map.end())
{
rec = it->second;
}
else if (create)
{
fprintf( pMgr.pLog, "Creating Function Profile Record: %s %s\n", fname.c_str(), funcNameStringLiteral);
rec = new funcProfileRecord( fileNameStringLiteral, fileLineNumber,
funcNameStringLiteral, commentStringLiteral);
_map[fname] = rec;
}
return rec;
}
//-------------------------------------------------------------------------
funcProfileRecord *profilerFuncMap::iterateBegin(void)
{
autoScopedLock aLock(_mapMtx);
funcProfileRecord *rec = nullptr;
_map_it = _map.begin();
if (_map_it != _map.end())
{
rec = _map_it->second;
}
return rec;
}
//-------------------------------------------------------------------------
funcProfileRecord *profilerFuncMap::iterateNext(void)
{
autoScopedLock aLock(_mapMtx);
funcProfileRecord *rec = nullptr;
if (_map_it != _map.end())
{
_map_it++;
}
if (_map_it != _map.end())
{
rec = _map_it->second;
}
return rec;
}
//-------------------------------------------------------------------------
//----- profilerManager class
//-------------------------------------------------------------------------
profilerManager* profilerManager::instance = nullptr;
profilerManager* profilerManager::getInstance(void)
{
return instance;
}
//-------------------------------------------------------------------------
profilerManager::profilerManager(void)
{
//printf("profilerManager Constructor\n");
if (pLog == nullptr)
{
pLog = stdout;
}
if (instance == nullptr)
{
instance = this;
}
}
profilerManager::~profilerManager(void)
{
//printf("profilerManager Destructor\n");
{
autoScopedLock aLock(threadListMtx);
threadList.clear();
}
if (pLog && (pLog != stdout))
{
fclose(pLog); pLog = nullptr;
}
if (instance == this)
{
instance = nullptr;
}
}
int profilerManager::addThreadProfiler( profilerFuncMap *m )
{
autoScopedLock aLock(threadListMtx);
threadList.push_back(m);
return 0;
}
int profilerManager::removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy )
{
int result = -1;
autoScopedLock aLock(threadListMtx);
for (auto it = threadList.begin(); it != threadList.end(); it++)
{
if (*it == m )
{
threadList.erase(it);
if (shouldDestroy)
{
delete m;
}
result = 0;
break;
}
}
return result;
}
//-------------------------------------------------------------------------
} // namespace FCEU
//-------------------------------------------------------------------------
int FCEU_profiler_log_thread_activity(void)
{
FCEU::execList.update();
return 0;
}
#endif // __FCEU_PROFILER_ENABLE__

167
src/profiler.h Normal file
View File

@ -0,0 +1,167 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// profiler.h
#pragma once
/*
* This module is intended for debug use only. This allows for high precision timing of function
* execution. This functionality is not included in the build unless __FCEU_PROFILER_ENABLE__
* is defined. To check timing on a particular function, add FCEU_PROFILE_FUNC macro to the top
* of the function body in the following manner.
* FCEU_PROFILE_FUNC(prof, "String Literal comment, whatever I want it to say")
* When __FCEU_PROFILER_ENABLE__ is not defined, the FCEU_PROFILE_FUNC macro evaluates to nothing
* so it won't break the regular build by having it used in code.
*/
#ifdef __FCEU_PROFILER_ENABLE__
#include <stdio.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
#include <time.h>
#endif
#include "utils/mutex.h"
#include "utils/timeStamp.h"
namespace FCEU
{
struct funcProfileRecord
{
const int fileLineNum;
const char *fileName;
const char *funcName;
const char *comment;
timeStampRecord min;
timeStampRecord max;
timeStampRecord sum;
timeStampRecord last;
unsigned int numCalls;
unsigned int recursionCount;
funcProfileRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral);
void reset(void);
double average(void);
};
struct profileFuncScoped
{
funcProfileRecord *rec;
timeStampRecord start;
profileFuncScoped( funcProfileRecord *recordIn );
~profileFuncScoped(void);
};
struct profileExecVector
{
profileExecVector(void);
~profileExecVector(void);
void update(void);
std::vector <funcProfileRecord> _vec;
FILE *logFp;
};
class profilerFuncMap
{
public:
profilerFuncMap();
~profilerFuncMap();
int addRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral,
funcProfileRecord *rec );
funcProfileRecord *findRecord(const char *fileNameStringLiteral,
const int fileLineNumber,
const char *funcNameStringLiteral,
const char *commentStringLiteral,
bool create = false);
funcProfileRecord *iterateBegin(void);
funcProfileRecord *iterateNext(void);
void pushStack(funcProfileRecord *rec);
void popStack(funcProfileRecord *rec);
private:
mutex _mapMtx;
std::map<std::string, funcProfileRecord*> _map;
std::map<std::string, funcProfileRecord*>::iterator _map_it;
std::vector <funcProfileRecord*> stack;
};
class profilerManager
{
public:
profilerManager(void);
~profilerManager(void);
int addThreadProfiler( profilerFuncMap *m );
int removeThreadProfiler( profilerFuncMap *m, bool shouldDestroy = false );
static FILE *pLog;
static profilerManager *getInstance();
private:
mutex threadListMtx;
std::list <profilerFuncMap*> threadList;
static profilerManager *instance;
};
}
#if defined(__PRETTY_FUNCTION__)
#define __FCEU_PROFILE_FUNC_NAME__ __PRETTY_FUNCTION__
#else
#define __FCEU_PROFILE_FUNC_NAME__ __func__
#endif
#define FCEU_PROFILE_FUNC(id, comment) \
static thread_local FCEU::funcProfileRecord id( __FILE__, __LINE__, __FCEU_PROFILE_FUNC_NAME__, comment ); \
FCEU::profileFuncScoped id ## _unique_scope( &id )
int FCEU_profiler_log_thread_activity(void);
#else // __FCEU_PROFILER_ENABLE__ not defined
#define FCEU_PROFILE_FUNC(id, comment)
#endif // __FCEU_PROFILER_ENABLE__

View File

@ -84,9 +84,9 @@ bool backupSavestates = true;
bool compressSavestates = true; //By default FCEUX compresses savestates when a movie is inactive.
// a temp memory stream. We'll be dumping some data here and then compress
EMUFILE_MEMORY memory_savestate;
static EMUFILE_MEMORY memory_savestate;
// temporary buffer for compressed data of a savestate
std::vector<uint8> compressed_buf;
static std::vector<uint8> compressed_buf;
#define SFMDATA_SIZE (128)
static SFORMAT SFMDATA[SFMDATA_SIZE];
@ -1179,3 +1179,389 @@ void RedoLoadState()
redoLS = false; //Flag that RedoLoadState can not be run again
undoLS = true; //Flag that LoadBackup can be run again
}
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
//----------- Save State History ----------------
//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------
static StateRecorderConfigData stateRecorderConfig;
class StateRecorder
{
public:
StateRecorder(void)
{
loadConfig( stateRecorderConfig );
for (int i=0; i<ringBufSize; i++)
{
EMUFILE_MEMORY *em = new EMUFILE_MEMORY( 0x1000 );
ringBuf.push_back(em);
}
ringStart = ringHead = ringTail = 0;
frameCounter = 0;
lastState = ringHead;
loadIndexReset = false;
lastLoadFrame = 0;
}
~StateRecorder(void)
{
for (size_t i=0; i<ringBuf.size(); i++)
{
delete ringBuf[i];
}
ringBuf.clear();
}
void loadConfig( StateRecorderConfigData &config )
{
if (config.framesBetweenSnaps < 1)
{
config.framesBetweenSnaps = 1;
}
if (config.timeBetweenSnapsMinutes < 0.0)
{
config.timeBetweenSnapsMinutes = 3.0f / 60.0f;
}
if (config.timeBetweenSnapsMinutes > config.historyDurationMinutes)
{
config.historyDurationMinutes = config.timeBetweenSnapsMinutes;
}
if (config.timingMode)
{
const double fhistMin = config.historyDurationMinutes;
const double fsnapMin = config.timeBetweenSnapsMinutes;
const double fnumSnaps = fhistMin / fsnapMin;
ringBufSize = static_cast<int>( fnumSnaps + 0.5f );
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double hz = ( ((double)fps) / 16777216.0 );
double framesPerSnapf = hz * fsnapMin * 60.0;
framesPerSnap = static_cast<unsigned int>( framesPerSnapf + 0.50 );
}
else
{
const double fhistMin = config.historyDurationMinutes;
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
double hz = ( ((double)fps) / 16777216.0 );
const double fsnapMin = static_cast<double>(config.framesBetweenSnaps) / (hz * 60.0);
const double fnumSnaps = fhistMin / fsnapMin;
ringBufSize = static_cast<int>( fnumSnaps + 0.5f );
framesPerSnap = config.framesBetweenSnaps;
}
printf("ringBufSize:%i framesPerSnap:%i\n", ringBufSize, framesPerSnap );
compressionLevel = config.compressionLevel;
loadPauseTime = config.loadPauseTimeSeconds;
pauseOnLoad = config.pauseOnLoad;
}
void update(void)
{
bool isPaused = EmulationPaused ? true : false;
unsigned int curFrame = static_cast<unsigned int>(currFrameCounter);
if (!isPaused && loadIndexReset)
{
ringHead = (lastState + 1) % ringBufSize;
frameCounter = curFrame;
loadIndexReset = false;
}
if (!isPaused && (curFrame > frameCounter) )
{
frameCounter = curFrame;
if ( (frameCounter % framesPerSnap) == 0 )
{
EMUFILE_MEMORY *em = ringBuf[ ringHead ];
em->set_len(0);
FCEUSS_SaveMS( em, compressionLevel );
//printf("Frame:%u Save:%i Size:%zu Total:%zukB \n", frameCounter, ringHead, em->size(), dataSize() / 1024 );
lastState = ringHead;
ringHead = (ringHead + 1) % ringBufSize;
if (ringStart == ringHead)
{
ringStart = (ringHead + 1) % ringBufSize;
}
}
}
}
int loadStateRelativeToEnd( int numSnapsFromLatest )
{
if (numSnapsFromLatest < 0)
{
numSnapsFromLatest = 0;
}
numSnapsFromLatest = numSnapsFromLatest % ringBufSize;
int snapIdx = ringHead - numSnapsFromLatest - 1;
loadStateByIndex(snapIdx);
return 0;
}
int loadStateByIndex( int snapIdx )
{
if (snapIdx < 0)
{
snapIdx = snapIdx + ringBufSize;
}
snapIdx = snapIdx % ringBufSize;
EMUFILE_MEMORY *em = ringBuf[ snapIdx ];
em->fseek(SEEK_SET, 0);
FCEUSS_LoadFP( em, SSLOADPARAM_NOBACKUP );
frameCounter = lastLoadFrame = static_cast<unsigned int>(currFrameCounter);
lastState = snapIdx;
loadIndexReset = true;
if (pauseOnLoad == StateRecorderConfigData::TEMPORARY_PAUSE)
{
if (loadPauseTime > 0)
{ // Temporary pause after loading new state for user to have time to process
FCEUI_PauseForDuration(loadPauseTime);
}
}
else if (pauseOnLoad == StateRecorderConfigData::FULL_PAUSE)
{
FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED );
}
return 0;
}
int loadPrevState(void)
{
int snapIdx = lastState;
if ( lastState == ringHead )
{ // No States to Load
return -1;
}
if ( lastState != ringStart )
{
if ( (lastLoadFrame+30) > frameCounter)
{
snapIdx--;
if (snapIdx < 0)
{
snapIdx += ringBufSize;
}
}
}
return loadStateByIndex( snapIdx );
}
int loadNextState(void)
{
int snapIdx = lastState;
int nextIdx = (lastState + 1) % ringBufSize;
if ( nextIdx != ringHead )
{
snapIdx = nextIdx;
}
return loadStateByIndex( snapIdx );
}
int getHeadIndex(void)
{
return ringHead;
}
int getStartIndex(void)
{
return ringStart;
}
int numSnapsSaved(void)
{
int numSnaps = ringHead - ringStart;
if (numSnaps < 0)
{
numSnaps = numSnaps + static_cast<int>( ringBuf.size() );
}
return numSnaps;
}
size_t dataSize(void)
{
return ringBuf.size() * ringBuf[0]->size();
}
size_t ringBufferSize(void)
{
return ringBuf.size();
}
static bool enabled;
static int lastState;
private:
void doSnap(void)
{
}
std::vector <EMUFILE_MEMORY*> ringBuf;
int ringHead;
int ringTail;
int ringStart;
int ringBufSize;
int compressionLevel;
int loadPauseTime;
StateRecorderConfigData::PauseType pauseOnLoad;
unsigned int frameCounter;
unsigned int framesPerSnap;
unsigned int lastLoadFrame;
bool loadIndexReset;
};
static StateRecorder *stateRecorder = nullptr;
bool StateRecorder::enabled = false;
int StateRecorder::lastState = 0;
int FCEU_StateRecorderStart(void)
{
if (stateRecorder == nullptr)
{
stateRecorder = new StateRecorder();
}
return stateRecorder == nullptr;
}
int FCEU_StateRecorderStop(void)
{
if (stateRecorder != nullptr)
{
delete stateRecorder; stateRecorder = nullptr;
}
return stateRecorder != nullptr;
}
int FCEU_StateRecorderUpdate(void)
{
if (stateRecorder != nullptr)
{
stateRecorder->update();
}
return 0;
}
bool FCEU_StateRecorderIsEnabled(void)
{
return StateRecorder::enabled;
}
void FCEU_StateRecorderSetEnabled(bool enabled)
{
StateRecorder::enabled = enabled;
}
bool FCEU_StateRecorderRunning(void)
{
return stateRecorder != nullptr;
}
int FCEU_StateRecorderGetMaxSnaps(void)
{
int size = 0;
if (stateRecorder != nullptr)
{
size = stateRecorder->ringBufferSize();
}
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)
{
return StateRecorder::lastState;
}
int FCEU_StateRecorderLoadPrevState(void)
{
int ret = -1;
if (stateRecorder != nullptr)
{
ret = stateRecorder->loadPrevState();
}
return ret;
}
int FCEU_StateRecorderLoadNextState(void)
{
int ret = -1;
if (stateRecorder != nullptr)
{
ret = stateRecorder->loadNextState();
}
return ret;
}
const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void)
{
return stateRecorderConfig;
}
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig)
{
stateRecorderConfig = newConfig;
if (stateRecorder != nullptr)
{
stateRecorder->loadConfig( stateRecorderConfig );
}
return 0;
}

View File

@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <string>
enum ENUM_SSLOADPARAMS
@ -78,3 +79,56 @@ extern bool backupSavestates; //Whether or not to make backups, true by defaul
bool CheckBackupSaveStateExist(); //Checks if backupsavestate exists
extern bool compressSavestates; //Whether or not to compress non-movie savestates (by default, yes)
struct StateRecorderConfigData
{
float historyDurationMinutes;
float timeBetweenSnapsMinutes;
int framesBetweenSnaps;
int compressionLevel;
int loadPauseTimeSeconds;
enum TimingType
{
FRAMES = 0,
TIME,
} timingMode;
enum PauseType
{
NO_PAUSE = 0,
TEMPORARY_PAUSE,
FULL_PAUSE,
} pauseOnLoad;
StateRecorderConfigData(void)
{
framesBetweenSnaps = 60;
historyDurationMinutes = 15.0f;
timeBetweenSnapsMinutes = 3.0f / 60.0f;
compressionLevel = 0;
loadPauseTimeSeconds = 3;
pauseOnLoad = TEMPORARY_PAUSE;
timingMode = FRAMES;
}
bool compare( const StateRecorderConfigData &other )
{
return memcmp( this, &other, sizeof(StateRecorderConfigData) ) == 0;
}
};
int FCEU_StateRecorderStart(void);
int FCEU_StateRecorderStop(void);
int FCEU_StateRecorderUpdate(void);
bool FCEU_StateRecorderRunning(void);
bool FCEU_StateRecorderIsEnabled(void);
void FCEU_StateRecorderSetEnabled(bool enabled);
int FCEU_StateRecorderGetMaxSnaps(void);
int FCEU_StateRecorderGetNumSnapsSaved(void);
int FCEU_StateRecorderGetStateIndex(void);
int FCEU_StateRecorderLoadState(int snapIndex);
int FCEU_StateRecorderLoadPrevState(void);
int FCEU_StateRecorderLoadNextState(void);
int FCEU_StateRecorderSetConfigData(const StateRecorderConfigData &newConfig);
const StateRecorderConfigData& FCEU_StateRecorderGetConfigData(void);

View File

@ -475,7 +475,8 @@ static BMAPPING bmap[] = {
{ "FNS", FNS_Init, BMCFLAG_16KCHRR },
{ "BS-400R", BS400R_Init, 0 },
{ "BS-4040R", BS4040R_Init, 0 },
{ "COOLGIRL", COOLGIRL_Init, 0 },
{ "COOLGIRL", COOLGIRL_Init, BMCFLAG_256KCHRR },
{ "JC-016-2", Mapper205_Init, 0 },
{ 0, 0, 0 }
};

View File

@ -60,6 +60,15 @@ autoScopedLock::autoScopedLock( mutex *mtx )
}
}
autoScopedLock::autoScopedLock( mutex &mtx )
{
m = &mtx;
if (m)
{
m->lock();
}
}
autoScopedLock::~autoScopedLock(void)
{
if (m)

View File

@ -1,4 +1,5 @@
// mutex.h
#pragma once
#ifdef __QT_DRIVER__
#include <QMutex>
@ -32,6 +33,7 @@ namespace FCEU
{
public:
autoScopedLock( mutex *mtx );
autoScopedLock( mutex &mtx );
~autoScopedLock(void);
private:

117
src/utils/timeStamp.cpp Normal file
View File

@ -0,0 +1,117 @@
// timeStamp.cpp
#include <stdio.h>
#include "timeStamp.h"
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
#include <unistd.h>
#endif
#if defined(WIN32)
#include <windows.h>
#endif
//-------------------------------------------------------------------------
//---- Time Stamp Record
//-------------------------------------------------------------------------
#if defined(WIN32)
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#else
#include <x86intrin.h>
#endif
static uint64_t rdtsc()
{
return __rdtsc();
}
namespace FCEU
{
uint64_t timeStampRecord::_tscFreq = 0;
#if defined(WIN32)
uint64_t timeStampRecord::qpcFreq = 0;
#endif
void timeStampRecord::readNew(void)
{
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
clock_gettime( CLOCK_REALTIME, &ts );
#else
QueryPerformanceCounter((LARGE_INTEGER*)&ts);
#endif
tsc = rdtsc();
}
#if defined(WIN32)
void timeStampRecord::qpcCalibrate(void)
{
if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0)
{
printf("QueryPerformanceFrequency FAILED!\n");
}
}
#endif
class timeStampModule
{
public:
timeStampModule(void)
{
printf("timeStampModuleInit\n");
#if defined(WIN32)
timeStampRecord::qpcCalibrate();
#endif
}
};
static timeStampModule module;
bool timeStampModuleInitialized(void)
{
#if defined(WIN32)
bool initialized = timeStampRecord::countFreq() != 0;
#else
bool initialized = true;
#endif
return initialized;
}
void timeStampRecord::tscCalibrate(int numSamples)
{
timeStampRecord t1, t2, td;
uint64_t td_sum = 0;
double td_avg;
#if defined(WIN32)
if (QueryPerformanceFrequency((LARGE_INTEGER*)&timeStampRecord::qpcFreq) == 0)
{
printf("QueryPerformanceFrequency FAILED!\n");
}
#endif
printf("Running TSC Calibration: %i sec...\n", numSamples);
for (int i=0; i<numSamples; i++)
{
t1.readNew();
#if defined(WIN32)
Sleep(1000);
#else
sleep(1);
#endif
t2.readNew();
td += t2 - t1;
td_sum = td.tsc;
td_avg = static_cast<double>(td_sum);
timeStampRecord::_tscFreq = static_cast<uint64_t>( td_avg / td.toSeconds() );
printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(),
static_cast<unsigned long long>(td.tsc), static_cast<double>(timeStampRecord::_tscFreq) * 1.0e-6 );
}
}
} // namespace FCEU

384
src/utils/timeStamp.h Normal file
View File

@ -0,0 +1,384 @@
// timeStamp.h
#pragma once
#include <stdint.h>
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
#include <time.h>
#endif
namespace FCEU
{
class timeStampRecord
{
public:
static constexpr uint64_t ONE_SEC_TO_MILLI = 1000;
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
static constexpr long int ONE_SEC_TO_NANO = 1000000000;
static constexpr long int MILLI_TO_NANO = 1000000;
timeStampRecord(void)
{
ts.tv_sec = 0;
ts.tv_nsec = 0;
tsc = 0;
}
timeStampRecord& operator = (const timeStampRecord& in)
{
ts = in.ts;
tsc = in.tsc;
return *this;
}
timeStampRecord& operator += (const timeStampRecord& op)
{
ts.tv_sec += op.ts.tv_sec;
ts.tv_nsec += op.ts.tv_nsec;
if (ts.tv_nsec >= ONE_SEC_TO_NANO)
{
ts.tv_nsec -= ONE_SEC_TO_NANO;
ts.tv_sec++;
}
tsc += op.tsc;
return *this;
}
timeStampRecord operator + (const timeStampRecord& op)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec + op.ts.tv_sec;
res.ts.tv_nsec = ts.tv_nsec + op.ts.tv_nsec;
if (res.ts.tv_nsec >= ONE_SEC_TO_NANO)
{
res.ts.tv_nsec -= ONE_SEC_TO_NANO;
res.ts.tv_sec++;
}
res.tsc = tsc + op.tsc;
return res;
}
timeStampRecord operator - (const timeStampRecord& op)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec - op.ts.tv_sec;
res.ts.tv_nsec = ts.tv_nsec - op.ts.tv_nsec;
if (res.ts.tv_nsec < 0)
{
res.ts.tv_nsec += ONE_SEC_TO_NANO;
res.ts.tv_sec--;
}
res.tsc = tsc - op.tsc;
return res;
}
timeStampRecord operator * (const unsigned int multiplier)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec * multiplier;
res.ts.tv_nsec = ts.tv_nsec * multiplier;
if (res.ts.tv_nsec >= ONE_SEC_TO_NANO)
{
res.ts.tv_nsec -= ONE_SEC_TO_NANO;
res.ts.tv_sec++;
}
res.tsc = tsc * multiplier;
return res;
}
timeStampRecord operator / (const unsigned int divisor)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec / divisor;
res.ts.tv_nsec = ts.tv_nsec / divisor;
res.tsc = tsc / divisor;
return res;
}
bool operator > (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec > op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec > op.ts.tv_sec);
}
return res;
}
bool operator >= (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec >= op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec >= op.ts.tv_sec);
}
return res;
}
bool operator < (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec < op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec < op.ts.tv_sec);
}
return res;
}
bool operator <= (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec <= op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec <= op.ts.tv_sec);
}
return res;
}
void zero(void)
{
ts.tv_sec = 0;
ts.tv_nsec = 0;
tsc = 0;
}
bool isZero(void)
{
return (ts.tv_sec == 0) && (ts.tv_nsec == 0);
}
void fromSeconds(unsigned int sec)
{
ts.tv_sec = sec;
ts.tv_nsec = 0;
tsc = 0;
}
void fromSeconds(double sec)
{
double ns;
ts.tv_sec = static_cast<time_t>(sec);
ns = (sec - static_cast<double>(ts.tv_sec)) * 1.0e9;
ts.tv_nsec = static_cast<long>(ns);
tsc = 0;
}
double toSeconds(void)
{
double sec = static_cast<double>(ts.tv_sec) + ( static_cast<double>(ts.tv_nsec) * 1.0e-9 );
return sec;
}
void fromMilliSeconds(uint64_t ms)
{
ts.tv_sec = ms / ONE_SEC_TO_MILLI;
ts.tv_nsec = (ms * MILLI_TO_NANO) - (ts.tv_sec * ONE_SEC_TO_NANO);
}
uint64_t toMilliSeconds(void)
{
uint64_t ms = (ts.tv_sec * ONE_SEC_TO_MILLI) + (ts.tv_nsec / MILLI_TO_NANO );
return ms;
}
uint64_t toCounts(void)
{
return (ts.tv_sec * ONE_SEC_TO_NANO) + ts.tv_nsec;
}
static uint64_t countFreq(void)
{
return ONE_SEC_TO_NANO;
}
struct timespec toTimeSpec(void)
{
return ts;
}
#else // WIN32
timeStampRecord(void)
{
ts = 0;
tsc = 0;
}
timeStampRecord& operator = (const timeStampRecord& in)
{
ts = in.ts;
tsc = in.tsc;
return *this;
}
timeStampRecord& operator += (const timeStampRecord& op)
{
ts += op.ts;
tsc += op.tsc;
return *this;
}
timeStampRecord operator + (const timeStampRecord& op)
{
timeStampRecord res;
res.ts = ts + op.ts;
res.tsc = tsc + op.tsc;
return res;
}
timeStampRecord operator - (const timeStampRecord& op)
{
timeStampRecord res;
res.ts = ts - op.ts;
res.tsc = tsc - op.tsc;
return res;
}
timeStampRecord operator * (const unsigned int multiplier)
{
timeStampRecord res;
res.ts = ts * multiplier;
res.tsc = tsc * multiplier;
return res;
}
timeStampRecord operator / (const unsigned int divisor)
{
timeStampRecord res;
res.ts = ts / divisor;
res.tsc = tsc / divisor;
return res;
}
bool operator > (const timeStampRecord& op)
{
return ts > op.ts;
}
bool operator >= (const timeStampRecord& op)
{
return ts >= op.ts;
}
bool operator < (const timeStampRecord& op)
{
return ts < op.ts;
}
bool operator <= (const timeStampRecord& op)
{
return ts <= op.ts;
}
void zero(void)
{
ts = 0;
tsc = 0;
}
bool isZero(void)
{
return (ts == 0);
}
void fromSeconds(unsigned int sec)
{
ts = sec * qpcFreq;
tsc = 0;
}
void fromSeconds(double sec)
{
ts = static_cast<uint64_t>(sec * static_cast<double>(qpcFreq));
tsc = 0;
}
double toSeconds(void)
{
double sec = static_cast<double>(ts) / static_cast<double>(qpcFreq);
return sec;
}
void fromMilliSeconds(uint64_t ms)
{
ts = (ms * qpcFreq) / ONE_SEC_TO_MILLI;
}
uint64_t toMilliSeconds(void)
{
uint64_t ms = (ts * ONE_SEC_TO_MILLI) / qpcFreq;
return ms;
}
uint64_t toCounts(void)
{
return ts;
}
static uint64_t countFreq(void)
{
return qpcFreq;
}
static void qpcCalibrate(void);
#endif
uint64_t getTSC(void){ return tsc; };
static uint64_t tscFreq(void)
{
return _tscFreq;
}
static bool tscValid(void){ return tscFreq != 0; };
// Call this function to calibrate the estimated TSC frequency
static void tscCalibrate(int numSamples = 0);
void readNew(void);
private:
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec ts;
#else // Win32
uint64_t ts;
static uint64_t qpcFreq;
#endif
uint64_t tsc;
static uint64_t _tscFreq;
};
bool timeStampModuleInitialized(void);
} // namespace FCEU

View File

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

View File

@ -47,7 +47,11 @@ void (*MapIRQHook)(int a);
//normal memory read
static INLINE uint8 RdMem(unsigned int A)
{
return(_DB=ARead[A](A));
_DB=ARead[A](A);
#ifdef _S9XLUA_H
CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ);
#endif
return(_DB);
}
//normal memory write
@ -61,9 +65,13 @@ static INLINE void WrMem(unsigned int A, uint8 V)
static INLINE uint8 RdRAM(unsigned int A)
{
_DB=ARead[A](A);
#ifdef _S9XLUA_H
CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ);
#endif
//bbit edited: this was changed so cheat substituion would work
return(_DB=ARead[A](A));
// return(_DB=RAM[A]);
return(_DB);
}
static INLINE void WrRAM(unsigned int A, uint8 V)
@ -77,7 +85,11 @@ static INLINE void WrRAM(unsigned int A, uint8 V)
uint8 X6502_DMR(uint32 A)
{
ADDCYC(1);
return(X.DB=ARead[A](A));
_DB=ARead[A](A);
#ifdef _S9XLUA_H
CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ);
#endif
return(_DB);
}
void X6502_DMW(uint32 A, uint8 V)

View File

@ -744,6 +744,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
<ClCompile Include="..\src\utils\md5.cpp" />
<ClCompile Include="..\src\utils\memory.cpp" />
<ClCompile Include="..\src\utils\mutex.cpp" />
<ClCompile Include="..\src\utils\timeStamp.cpp" />
<ClCompile Include="..\src\utils\unzip.cpp" />
<ClCompile Include="..\src\utils\xstring.cpp" />
<ClCompile Include="..\src\lua\src\lapi.c">
@ -1008,6 +1009,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
<ClCompile Include="..\src\oldmovie.cpp" />
<ClCompile Include="..\src\palette.cpp" />
<ClCompile Include="..\src\ppu.cpp" />
<ClCompile Include="..\src\profiler.cpp" />
<ClCompile Include="..\src\sound.cpp" />
<ClCompile Include="..\src\state.cpp" />
<ClCompile Include="..\src\unif.cpp" />
@ -1137,6 +1139,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
<ClInclude Include="..\src\oldmovie.h" />
<ClInclude Include="..\src\palette.h" />
<ClInclude Include="..\src\ppu.h" />
<ClInclude Include="..\src\profiler.h" />
<ClInclude Include="..\src\sound.h" />
<ClInclude Include="..\src\state.h" />
<ClInclude Include="..\src\types-des.h" />
@ -1151,6 +1154,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)"</Command>
<ClInclude Include="..\src\utils\md5.h" />
<ClInclude Include="..\src\utils\memory.h" />
<ClInclude Include="..\src\utils\mutex.h" />
<ClInclude Include="..\src\utils\timeStamp.h" />
<ClInclude Include="..\src\utils\unzip.h" />
<ClInclude Include="..\src\utils\valuearray.h" />
<ClInclude Include="..\src\utils\xstring.h" />

View File

@ -81,6 +81,7 @@
brew install qt5
brew install sdl2
brew install minizip
brew install libarchive (optional dependency for 7zip archive support)
brew install ffmpeg (optional dependency but recommended for AVI recording)
brew install x264 (optional dependency but recommended for AVI recording)
</pre>