// Donuts are a tasty treat and delicious with powdered sugar. void MDFNMOV_AddJoy(void *donutdata, uint32 donutlen) { gzFile fp; if(!current) return; /* Not playback nor recording. */ if(current < 0) /* Playback */ { int t; fp = slots[-1 - current]; while((t = gzgetc(fp)) >= 0 && t) { if(t == MDFNNPCMD_LOADSTATE) { uint32 len; StateMem sm; len = gzgetc(fp); len |= gzgetc(fp) << 8; len |= gzgetc(fp) << 16; len |= gzgetc(fp) << 24; if(len >= 5 * 1024 * 1024) // A sanity limit of 5MiB { StopPlayback(); return; } memset(&sm, 0, sizeof(StateMem)); sm.len = len; sm.data = (uint8 *)malloc(len); if(gzread(fp, sm.data, len) != len) { StopPlayback(); return; } if(!MDFNSS_LoadSM(&sm, 0, 0)) { StopPlayback(); return; } } else MDFN_DoSimpleCommand(t); } if(t < 0) { StopPlayback(); return; } if(gzread(fp, donutdata, donutlen) != donutlen) { StopPlayback(); return; } } else /* Recording */ { if(MDFN_StateEvilIsRunning()) { smem_putc(&RewindBuffer, 0); smem_write(&RewindBuffer, donutdata, donutlen); } else { fp = slots[current - 1]; gzputc(fp, 0); gzwrite(fp, donutdata, donutlen); } } }
static void ProcessCommand(const uint8 cmd, const uint32 raw_len, const char **PortDNames, void *PortData[], uint32 PortLen[], int NumPorts) { switch(cmd) { case 0: break; // No command default: MDFN_DoSimpleCommand(cmd); break; case MDFNNPCMD_INTEGRITY: SendIntegrity(); break; case MDFNNPCMD_REQUEST_STATE: SendState(); break; case MDFNNPCMD_LOADSTATE: RecvState(raw_len); MDFN_DispMessage(_("Remote state loaded.")); break; case MDFNNPCMD_SERVERTEXT: { static const uint32 MaxLength = 2000; uint8 neobuf[MaxLength + 1]; char *textbuf = NULL; const uint32 totallen = raw_len; if(totallen > MaxLength) // Sanity check { throw MDFN_Error(0, _("Text length is too long: %u"), totallen); } MDFND_RecvData(neobuf, totallen); neobuf[totallen] = 0; trio_asprintf(&textbuf, "** %s", neobuf); MDFND_NetplayText((UTF8*)textbuf, FALSE); free(textbuf); } break; case MDFNNPCMD_ECHO: { uint32 totallen = raw_len; uint64 then_time; uint64 now_time; if(totallen != sizeof(then_time)) { throw MDFN_Error(0, _("Echo response length is incorrect size: %u"), totallen); } MDFND_RecvData(&then_time, sizeof(then_time)); now_time = MDFND_GetTime(); char *textbuf = NULL; trio_asprintf(&textbuf, _("*** Round-trip time: %llu ms"), (unsigned long long)(now_time - then_time)); MDFND_NetplayText((UTF8*)textbuf, FALSE); free(textbuf); } break; case MDFNNPCMD_TEXT: { static const uint32 MaxLength = 2000; uint8 neobuf[MaxLength + 1]; const uint32 totallen = raw_len; uint32 nicklen; bool NetEcho = false; char *textbuf = NULL; if(totallen < 4) { throw MDFN_Error(0, _("Text command length is too short: %u"), totallen); } if(totallen > MaxLength) // Sanity check { throw MDFN_Error(0, _("Text command length is too long: %u"), totallen); } MDFND_RecvData(neobuf, totallen); nicklen = MDFN_de32lsb(neobuf); if(nicklen > (totallen - 4)) // Sanity check { throw MDFN_Error(0, _("Received nickname length is too long: %u"), nicklen); } neobuf[totallen] = 0; if(nicklen) { uint8 nickbuf[nicklen + 1]; memcpy(nickbuf, neobuf + 4, nicklen); nickbuf[nicklen] = 0; if(OurNick && !strcasecmp(OurNick, (char *)nickbuf)) { trio_asprintf(&textbuf, "> %s", &neobuf[4 + nicklen]); NetEcho = true; } else trio_asprintf(&textbuf, "<%s> %s", nickbuf, &neobuf[4 + nicklen]); } else { trio_asprintf(&textbuf, "* %s", &neobuf[4]); } MDFND_NetplayText((UTF8*)textbuf, NetEcho); free(textbuf); } break; case MDFNNPCMD_NICKCHANGED: { static const uint32 MaxLength = 2000; uint8 neobuf[MaxLength + 1]; uint8 *newnick; char *textbuf = NULL; const uint32 len = raw_len; if(len > MaxLength) // Sanity check { throw MDFN_Error(0, _("Nickname change length is too long: %u"), len); } MDFND_RecvData(neobuf, len); neobuf[len] = 0; newnick = (uint8*)strchr((char*)neobuf, '\n'); if(newnick) { bool IsMeow = FALSE; *newnick = 0; newnick++; if(OurNick) { if(!strcasecmp((char*)neobuf, (char*)OurNick)) { free(OurNick); OurNick = strdup((char*)newnick); textbuf = trio_aprintf(_("* You are now known as <%s>."), newnick); IsMeow = TRUE; } } if(!textbuf) textbuf = trio_aprintf(_("* <%s> is now known as <%s>"), neobuf, newnick); MDFND_NetplayText((UTF8*)textbuf, IsMeow); free(textbuf); } } break; case MDFNNPCMD_CTRL_CHANGE: { const uint32 len = raw_len; // // Joined = true; SendCommand(MDFNNPCMD_CTRL_CHANGE_ACK, len); // // LocalInputStateSize = 0; LocalPlayersMask = len; for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++) { if(LocalPlayersMask & (1 << x)) LocalInputStateSize += PortLen[x]; } } break; case MDFNNPCMD_CTRLR_SWAP_NOTIF: { const uint32 cm = raw_len; char textbuf[512]; trio_snprintf(textbuf, sizeof(textbuf), _("* All instances of controllers %u and %u have been swapped."), ((cm & 0xFF) + 1), ((cm >> 8) & 0xFF) + 1); MDFND_NetplayText((UTF8*)textbuf, false); } break; case MDFNNPCMD_CTRLR_TAKE_NOTIF: case MDFNNPCMD_CTRLR_DROP_NOTIF: case MDFNNPCMD_CTRLR_DUPE_NOTIF: { static const uint32 MaxNicknameLength = 1000; static const uint32 MaxLength = 12 + MaxNicknameLength; const char *fstr = NULL; const uint32 len = raw_len; uint8 ntf_buf[MaxLength + 1]; char *textbuf = NULL; if(len < 12) throw MDFN_Error(0, _("Take/drop/dupe notification is too short: %u"), len); if(len > MaxLength) throw MDFN_Error(0, _("Take/drop/dupe notification is too long: %u"), len); MDFND_RecvData(ntf_buf, len); ntf_buf[len] = 0; switch(cmd) { case MDFNNPCMD_CTRLR_TAKE_NOTIF: fstr = _("* <%s> took all instances of %s, and is now %s."); break; case MDFNNPCMD_CTRLR_DUPE_NOTIF: fstr = _("* <%s> took copies of %s, and is now %s."); break; case MDFNNPCMD_CTRLR_DROP_NOTIF: fstr = _("* <%s> dropped %s, and is now %s."); break; } trio_asprintf(&textbuf, fstr, ntf_buf + 12, GenerateMPSString(MDFN_de32lsb(&ntf_buf[0]), true).c_str(), GenerateMPSString(MDFN_de32lsb(&ntf_buf[4]), false).c_str()); MDFND_NetplayText((UTF8*)textbuf, false); free(textbuf); } break; case MDFNNPCMD_YOUJOINED: case MDFNNPCMD_YOULEFT: case MDFNNPCMD_PLAYERLEFT: case MDFNNPCMD_PLAYERJOINED: { static const uint32 MaxLength = 2000; uint8 neobuf[MaxLength + 1]; char *textbuf = NULL; uint32 mps; std::string mps_string; const uint32 len = raw_len; if(len < 8) { throw MDFN_Error(0, _("Join/Left length is too short: %u"), len); } if(len > MaxLength) // Sanity check { throw MDFN_Error(0, _("Join/Left length is too long: %u"), len); } MDFND_RecvData(neobuf, len); neobuf[len] = 0; // NULL-terminate the string mps = MDFN_de32lsb(&neobuf[0]); mps_string = GenerateMPSString(mps); if(cmd == MDFNNPCMD_YOULEFT) { // Uhm, not supported yet! LocalPlayersMask = 0; LocalInputStateSize = 0; Joined = FALSE; } else if(cmd == MDFNNPCMD_YOUJOINED) { if(OurNick) // This shouldn't happen, really... { free(OurNick); OurNick = NULL; } OurNick = strdup((char*)neobuf + 8); trio_asprintf(&textbuf, _("* You, %s, have connected as: %s"), neobuf + 8, mps_string.c_str()); LocalPlayersMask = mps; LocalInputStateSize = 0; for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++) { if(LocalPlayersMask & (1U << x)) LocalInputStateSize += PortLen[x]; } Joined = TRUE; SendCommand(MDFNNPCMD_SETFPS, MDFNGameInfo->fps); } else if(cmd == MDFNNPCMD_PLAYERLEFT) { trio_asprintf(&textbuf, _("* %s(%s) has left"), neobuf + 8, mps_string.c_str()); } else { trio_asprintf(&textbuf, _("* %s has connected as: %s"), neobuf + 8, mps_string.c_str()); } MDFND_NetplayText((UTF8*)textbuf, FALSE); free(textbuf); } break; } }