diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c1f1dbc6..4c841ed8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,8 +39,8 @@ if ( ${QT} EQUAL 6 ) message( STATUS "GUI Frontend: Qt6") set( Qt Qt6 ) find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets) + find_package( Qt6 REQUIRED COMPONENTS Network) find_package( Qt6 COMPONENTS Help QUIET) - find_package( Qt6 COMPONENTS Network) find_package( Qt6 COMPONENTS Qml) find_package( Qt6 COMPONENTS UiTools) add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6Network_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} ) diff --git a/src/cheat.cpp b/src/cheat.cpp index 7ac99376..0f4ae160 100644 --- a/src/cheat.cpp +++ b/src/cheat.cpp @@ -58,6 +58,15 @@ void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p) CheatRPtrs[AB+x]=p-A; } +// Cheat change event callback. Called whenever cheat map is changed or recalculated. +static void (*cheatsChangeEventCB)(void*) = nullptr; +static void* cheatsChangeEventUserData = nullptr; + +void FCEU_SetCheatChangeEventCallback( void (*func)(void*), void* userData ) +{ + cheatsChangeEventCB = func; + cheatsChangeEventUserData = userData; +} CHEATF_SUBFAST SubCheats[256]; uint32 numsubcheats = 0; @@ -132,6 +141,11 @@ void RebuildSubCheats(void) } FrozenAddressCount = numsubcheats; //Update the frozen address list + // Notify the system of a change + if (cheatsChangeEventCB != nullptr) + { + cheatsChangeEventCB( cheatsChangeEventUserData ); + } } void FCEU_PowerCheats() @@ -368,12 +382,15 @@ void FCEU_FlushGameCheats(FILE *override, int nosave) } -int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type) +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status, bool rebuild) { - AddCheatEntry(name, addr, val, compare, 1, type); + AddCheatEntry(name, addr, val, compare, status, type); savecheats = 1; - RebuildSubCheats(); + if (rebuild) + { + RebuildSubCheats(); + } return 1; } diff --git a/src/cheat.h b/src/cheat.h index 200a0055..f90fc50b 100644 --- a/src/cheat.h +++ b/src/cheat.h @@ -33,6 +33,8 @@ extern int disableAutoLSCheats; int FCEU_DisableAllCheats(void); int FCEU_DeleteAllCheats(void); +void FCEU_SetCheatChangeEventCallback( void (*func)(void*) = nullptr, void* userData = nullptr ); + struct CHEATF_SUBFAST { uint16 addr; diff --git a/src/driver.h b/src/driver.h index 207676f0..39ea3bef 100644 --- a/src/driver.h +++ b/src/driver.h @@ -197,7 +197,7 @@ void FCEU_DispMessage( __FCEU_PRINTF_FORMAT const char *format, int disppos, ... int FCEUI_DecodePAR(const char *code, int *a, int *v, int *c, int *type); int FCEUI_DecodeGG(const char *str, int *a, int *v, int *c); -int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type); +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status = 1, bool rebuild = true); int FCEUI_DelCheat(uint32 which); int FCEUI_ToggleCheat(uint32 which); int FCEUI_GlobalToggleCheat(int global_enable); diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 2d8e029e..4073d078 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -55,6 +55,7 @@ #include "../../movie.h" #include "../../wave.h" #include "../../state.h" +#include "../../cheat.h" #include "../../profiler.h" #include "../../version.h" #include "common/os_utils.h" @@ -285,6 +286,19 @@ consoleWin_t::consoleWin_t(QWidget *parent) } }; FCEUSS_SetLoadCallback( stateLoadCallback ); + + // Register Cheat Change Callback + auto cheatChangeCallback = []( void* userData ) + { + FCEU_UNUSED(userData); + + //printf("Cheats Changed Event!\n"); + if (consoleWindow != nullptr) + { + emit consoleWindow->cheatsChanged(); + } + }; + FCEU_SetCheatChangeEventCallback( cheatChangeCallback, this ); } consoleWin_t::~consoleWin_t(void) diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index 0b49aeb7..b59f94ff 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -332,6 +332,7 @@ class consoleWin_t : public QMainWindow void stateLoaded(void); void nesResetOccurred(void); void pauseToggled(bool state); + void cheatsChanged(void); public slots: void openDebugWindow(void); diff --git a/src/drivers/Qt/NetPlay.cpp b/src/drivers/Qt/NetPlay.cpp index 8ad437cf..c8033739 100644 --- a/src/drivers/Qt/NetPlay.cpp +++ b/src/drivers/Qt/NetPlay.cpp @@ -25,6 +25,7 @@ #include "../../fceu.h" #include "../../cart.h" +#include "../../cheat.h" #include "../../state.h" #include "../../movie.h" #include "../../debug.h" @@ -182,6 +183,7 @@ NetPlayServer::NetPlayServer(QObject *parent) connect(consoleWindow, SIGNAL(romUnload(void)), this, SLOT(onRomUnload(void))); connect(consoleWindow, SIGNAL(stateLoaded(void)), this, SLOT(onStateLoad(void))); connect(consoleWindow, SIGNAL(nesResetOccurred(void)), this, SLOT(onNesReset(void))); + connect(consoleWindow, SIGNAL(cheatsChanged(void)), this, SLOT(onCheatsChanged(void))); connect(consoleWindow, SIGNAL(pauseToggled(bool)), this, SLOT(onPauseToggled(bool))); FCEU_WRAPPER_LOCK(); @@ -396,12 +398,43 @@ int NetPlayServer::sendRomLoadReq( NetPlayClient *client ) return 0; } //----------------------------------------------------------------------------- +struct NetPlayServerCheatQuery +{ + int numLoaded = 0; + + netPlayLoadStateResp::CheatData data[netPlayLoadStateResp::MaxCheats]; +}; + +static int serverActiveCheatListCB(const char *name, uint32 a, uint8 v, int c, int s, int type, void *data) +{ + NetPlayServerCheatQuery* query = static_cast(data); + + const int i = query->numLoaded; + + if (i < netPlayLoadStateResp::MaxCheats) + { + auto& cheat = query->data[i]; + cheat.addr = a; + cheat.val = v; + cheat.cmp = c; + cheat.type = type; + cheat.stat = s; + Strlcpy( cheat.name, name, sizeof(cheat.name)); + + query->numLoaded++; + } + + return 1; +} +//----------------------------------------------------------------------------- int NetPlayServer::sendStateSyncReq( NetPlayClient *client ) { EMUFILE_MEMORY em; - int compressionLevel = 1; + int numCtrlFrames = 0, numCheats = 0, compressionLevel = 1; static constexpr size_t maxBytesPerWrite = 32 * 1024; netPlayLoadStateResp resp; + netPlayLoadStateResp::CtrlData ctrlData[netPlayLoadStateResp::MaxCtrlFrames]; + NetPlayServerCheatQuery cheatQuery; if ( GameInfo == nullptr ) { @@ -409,9 +442,9 @@ int NetPlayServer::sendStateSyncReq( NetPlayClient *client ) } FCEUSS_SaveMS( &em, compressionLevel ); - resp.hdr.msgSize += em.size(); resp.stateSize = em.size(); resp.opsCrc32 = opsCrc32; + resp.romCrc32 = romCrc32; NetPlayFrameData lastFrameData; netPlayFrameData.getLast( lastFrameData ); @@ -428,20 +461,34 @@ int NetPlayServer::sendStateSyncReq( NetPlayClient *client ) { if (i < netPlayLoadStateResp::MaxCtrlFrames) { - resp.ctrlData[i].frameNum = inputFrame.frameCounter; - resp.ctrlData[i].ctrlState[0] = inputFrame.ctrl[0]; - resp.ctrlData[i].ctrlState[1] = inputFrame.ctrl[1]; - resp.ctrlData[i].ctrlState[2] = inputFrame.ctrl[2]; - resp.ctrlData[i].ctrlState[3] = inputFrame.ctrl[3]; + ctrlData[i].frameNum = netPlayByteSwap(inputFrame.frameCounter); + ctrlData[i].ctrlState[0] = inputFrame.ctrl[0]; + ctrlData[i].ctrlState[1] = inputFrame.ctrl[1]; + ctrlData[i].ctrlState[2] = inputFrame.ctrl[2]; + ctrlData[i].ctrlState[3] = inputFrame.ctrl[3]; i++; } } - resp.numCtrlFrames = i; + resp.numCtrlFrames = numCtrlFrames = i; } + FCEUI_ListCheats(::serverActiveCheatListCB, (void *)&cheatQuery); + resp.numCheats = numCheats = cheatQuery.numLoaded; + + resp.calcTotalSize(); + printf("Sending ROM Sync Request: %zu\n", em.size()); sendMsg( client, &resp, sizeof(netPlayLoadStateResp), [&resp]{ resp.toNetworkByteOrder(); } ); + + if (numCtrlFrames > 0) + { + sendMsg( client, ctrlData, numCtrlFrames * sizeof(netPlayLoadStateResp::CtrlData) ); + } + if (numCheats > 0) + { + sendMsg( client, &cheatQuery.data, numCheats * sizeof(netPlayLoadStateResp::CheatData) ); + } //sendMsg( client, em.buf(), em.size() ); const unsigned char* bufPtr = em.buf(); @@ -583,6 +630,7 @@ void NetPlayServer::onRomLoad() //----------------------------------------------------------------------------- void NetPlayServer::onRomUnload() { + //printf("ROM UnLoaded!\n"); netPlayMsgHdr unloadMsg(NETPLAY_UNLOAD_ROM_REQ); romCrc32 = 0; @@ -642,6 +690,32 @@ void NetPlayServer::onNesReset() FCEU_WRAPPER_UNLOCK(); } //----------------------------------------------------------------------------- +void NetPlayServer::onCheatsChanged() +{ + //printf("NES Cheats Event!\n"); + if (romCrc32 == 0) + { + return; + } + FCEU_WRAPPER_LOCK(); + + opsCrc32 = 0; + netPlayFrameData.reset(); + + inputClear(); + inputFrameCount = static_cast(currFrameCounter); + + sendPauseAll(); + + // NES Reset has occurred on server, signal clients sync + for (auto& client : clientList ) + { + //sendRomLoadReq( client ); + sendStateSyncReq( client ); + } + FCEU_WRAPPER_UNLOCK(); +} +//----------------------------------------------------------------------------- void NetPlayServer::onPauseToggled( bool isPaused ) { if (isPaused) @@ -1568,6 +1642,11 @@ int NetPlayClient::requestStateLoad(EMUFILE *is) { printf("Read Error\n"); } + + if (currCartInfo != nullptr) + { + resp.romCrc32 = romCrc32; + } printf("Sending Client ROM Sync Request: %u\n", resp.stateSize); resp.toNetworkByteOrder(); @@ -1893,6 +1972,7 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) netPlayLoadStateResp* msg = static_cast(msgBuf); msg->toHostByteOrder(); + const bool romMatch = (msg->romCrc32 = romCrc32); char *stateData = msg->stateDataBuf(); const uint32_t stateDataSize = msg->stateDataSize(); @@ -1901,34 +1981,64 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) EMUFILE_MEMORY em( stateData, stateDataSize ); FCEU_WRAPPER_LOCK(); - serverRequestedStateLoad = true; - FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP ); - serverRequestedStateLoad = false; - opsCrc32 = msg->opsCrc32; - netPlayFrameData.reset(); + bool dataValid = romMatch; - NetPlayFrameData data; - data.frameNum = msg->lastFrame.num; - data.opsCrc32 = msg->lastFrame.opsCrc32; - data.ramCrc32 = msg->lastFrame.ramCrc32; - - netPlayFrameData.push( data ); - - inputClear(); - - const int numInputFrames = msg->numCtrlFrames; - for (int i=0; ictrlData[i].frameNum; - inputFrame.ctrl[0] = msg->ctrlData[i].ctrlState[0]; - inputFrame.ctrl[1] = msg->ctrlData[i].ctrlState[1]; - inputFrame.ctrl[2] = msg->ctrlData[i].ctrlState[2]; - inputFrame.ctrl[3] = msg->ctrlData[i].ctrlState[3]; + opsCrc32 = msg->opsCrc32; + netPlayFrameData.reset(); - pushBackInput( inputFrame ); + NetPlayFrameData data; + data.frameNum = msg->lastFrame.num; + data.opsCrc32 = msg->lastFrame.opsCrc32; + data.ramCrc32 = msg->lastFrame.ramCrc32; + + netPlayFrameData.push( data ); + + inputClear(); + + const int numInputFrames = msg->numCtrlFrames; + for (int i=0; ictrlDataBuf(); + + ctrlData[i].toHostByteOrder(); + inputFrame.frameCounter = ctrlData[i].frameNum; + inputFrame.ctrl[0] = ctrlData[i].ctrlState[0]; + inputFrame.ctrl[1] = ctrlData[i].ctrlState[1]; + inputFrame.ctrl[2] = ctrlData[i].ctrlState[2]; + inputFrame.ctrl[3] = ctrlData[i].ctrlState[3]; + + pushBackInput( inputFrame ); + } + + const int numCheats = msg->numCheats; + + if (numCheats > 0) + { + const int lastCheatIdx = numCheats - 1; + + FCEU_FlushGameCheats(0, 1); + for (int i=0; icheatDataBuf(); + auto& cheatData = cheatBuf[i]; + // Set cheat rebuild flag on last item. + bool lastItem = (i == lastCheatIdx); + + FCEUI_AddCheat( cheatData.name, cheatData.addr, cheatData.val, cheatData.cmp, cheatData.type, cheatData.stat, lastItem ); + } + } + else + { + FCEU_DeleteAllCheats(); + } } FCEU_WRAPPER_UNLOCK(); @@ -3009,10 +3119,11 @@ uint64_t netPlayByteSwap(uint64_t in) //---------------------------------------------------------------------------- uint32_t netPlayCalcRamChkSum() { + constexpr int ramSize = 0x800; uint32_t crc = 0; - uint8_t ram[256]; + uint8_t ram[ramSize]; - for (int i=0; i<256; i++) + for (int i=0; i