1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "mednafen.h"
19 
20 #include <zlib.h>
21 #include <trio/trio.h>
22 
23 #include <map>
24 
25 #include "netplay.h"
26 #include "netplay-driver.h"
27 #include "general.h"
28 #include <mednafen/string/string.h>
29 #include "state.h"
30 #include "movie.h"
31 #include <mednafen/hash/md5.h>
32 #include <mednafen/Time.h>
33 #include <mednafen/net/Net.h>
34 #include "mempatcher.h"
35 
36 #include "MemoryStream.h"
37 
38 #include "driver.h"
39 
40 namespace Mednafen
41 {
42 
43 static std::unique_ptr<Net::Connection> Connection;
44 
45 int MDFNnetplay=0;
46 
47 static std::map<std::string, uint32> PlayersList;
48 static std::string OurNick;
49 
50 static bool Joined = false;
51 static uint32 LocalPlayersMask = 0;
52 static uint32 LocalInputStateSize = 0;
53 static uint32 TotalInputStateSize = 0;
54 static uint8 PortVtoLVMap[16];
55 static uint8 PortLVtoVMap[16];
56 static std::vector<uint8> PreNPPortDataPortData[16];
57 static std::vector<uint8> PostEmulatePortData[16];
58 static bool StateLoaded;	// Set to true/false in Netplay_Update() call paths, used in Netplay_PostProcess()
59 				// to determine where to pull switch data from.
60 
61 static std::unique_ptr<uint8[]> incoming_buffer;	// TotalInputStateSize + 1
62 static std::unique_ptr<uint8[]> outgoing_buffer;	// 1 + LocalInputStateSize + 4
63 
RebuildPortVtoVMap(const uint32 PortDevIdx[])64 static void RebuildPortVtoVMap(const uint32 PortDevIdx[])
65 {
66  const unsigned NumPorts = MDFNGameInfo->PortInfo.size();
67 
68  memset(PortVtoLVMap, 0xFF, sizeof(PortVtoLVMap));
69  memset(PortLVtoVMap, 0xFF, sizeof(PortLVtoVMap));
70 
71  for(unsigned x = 0; x < NumPorts; x++)
72  {
73   if(LocalPlayersMask & (1U << x))
74   {
75    for(unsigned n = 0; n <= x; n++)
76    {
77     auto const* IDII_N = &MDFNGameInfo->PortInfo[n].DeviceInfo[PortDevIdx[n]].IDII;
78     auto const* IDII_X = &MDFNGameInfo->PortInfo[x].DeviceInfo[PortDevIdx[x]].IDII;
79 
80     if(PortLVtoVMap[n] == 0xFF && IDII_N == IDII_X)
81     {
82      PortLVtoVMap[n] = x;
83      PortVtoLVMap[x] = n;
84      break;
85     }
86    }
87   }
88  }
89 }
90 
SetLPM(const uint32 v,const uint32 PortDevIdx[],const uint32 PortLen[])91 static void SetLPM(const uint32 v, const uint32 PortDevIdx[], const uint32 PortLen[])
92 {
93  LocalPlayersMask = v;
94  LocalInputStateSize = 0;
95 
96  for(unsigned x = 0; x < MDFNGameInfo->PortInfo.size(); x++)
97  {
98   if(LocalPlayersMask & (1U << x))
99    LocalInputStateSize += PortLen[x];
100  }
101 
102  outgoing_buffer.reset(nullptr);
103  outgoing_buffer.reset(new uint8[1 + LocalInputStateSize + 4]);
104 
105  RebuildPortVtoVMap(PortDevIdx);
106  //
107  MDFND_NetplaySetHints(true, Connection->CanReceive(), LocalPlayersMask);
108 }
109 
110 
NetError(const char * format,...)111 static void NetError(const char *format, ...)
112 {
113  char *temp = NULL;
114  va_list ap;
115 
116  va_start(ap, format);
117  temp = trio_vaprintf(format, ap);
118  va_end(ap);
119 
120  MDFND_NetplayText(temp, false);
121  MDFNI_NetplayDisconnect();
122  free(temp);
123 }
124 
125 static void NetPrintText(const char* format, ...) MDFN_FORMATSTR(gnu_printf, 1, 2);
NetPrintText(const char * format,...)126 static void NetPrintText(const char* format, ...)
127 {
128  char *temp = NULL;
129  va_list ap;
130 
131  va_start(ap, format);
132  temp = trio_vaprintf(format, ap);
133  va_end(ap);
134 
135  MDFND_NetplayText(temp, false);
136  free(temp);
137 }
138 
SendData(const void * data,uint32 len)139 static void SendData(const void *data, uint32 len)
140 {
141  do
142  {
143   int32 sent = Connection->Send(data, len);
144   assert(sent >= 0);
145 
146   data = (uint8*)data + sent;
147   len -= sent;
148 
149   if(len)
150   {
151    if(MDFND_CheckNeedExit())
152     throw MDFN_Error(0, _("Mednafen exit pending."));
153 
154    Connection->CanSend(50000);
155   }
156  } while(len);
157 }
158 
RecvData(void * data,uint32 len)159 static void RecvData(void *data, uint32 len)
160 {
161  do
162  {
163   int32 received = Connection->Receive(data, len);
164   assert(received >= 0);
165 
166   data = (uint8*)data + received;
167   len -= received;
168 
169   if(len)
170   {
171    if(MDFND_CheckNeedExit())
172     throw MDFN_Error(0, _("Mednafen exit pending."));
173 
174    Connection->CanReceive(50000);
175   }
176  } while(len);
177 
178  MDFND_NetplaySetHints(true, Connection->CanReceive(), LocalPlayersMask);
179 }
180 
181 
182 struct login_data_t
183 {
184  uint8 gameid[16];
185  uint8 password[16];
186 
187  uint8 protocol_version;
188  uint8 total_controllers;
189  uint8 padding0[2];
190 
191  uint8 emu_name_len[4];			// Length of emulator name and version string(up to 64 bytes) - (note that any non-0
192 					// bytes < 0x20 in this string will be replaced by 0x20).
193 
194  uint8 padding1[8];
195 
196  uint8 controller_data_size[16];
197 
198  uint8 padding3[16];
199 
200  uint8 controller_type[16];
201 
202  uint8 local_players;
203 };
204 
205 #if 0
206 // WIP, mostly just ideas now, nothing to justify implementing it.
207 struct p4_login_data_t
208 {
209  uint8 magic[16];		// MEDNAFEN_NETPLAY
210  uint8 protocol_version[4];	// uint32
211 
212  //
213  //
214  //
215  uint8 password[16];		//
216  uint8 nickname[256];		//
217 
218  //
219  //
220  //
221  uint8 game_id[32];
222  uint8 emu_id[256];
223 
224  uint8 total_controllers;	// Total number of controllers(max 32)
225  uint8 controller_type[32];
226  uint8 controller_data_size[32];
227  uint8 local_controllers;	// Number of local controllers.
228 };
229 #endif
230 
NetplayStart(const uint32 PortDeviceCache[16],const uint32 PortDataLenCache[16])231 static void NetplayStart(const uint32 PortDeviceCache[16], const uint32 PortDataLenCache[16])
232 {
233  const char *emu_id = PACKAGE " " MEDNAFEN_VERSION;
234  const uint32 local_players = MDFN_GetSettingUI("netplay.localplayers");
235  const std::string nickname = MDFN_GetSettingS("netplay.nick");
236  const std::string game_key = MDFN_GetSettingS("netplay.gamekey");
237  const std::string connect_password = MDFN_GetSettingS("netplay.password");
238  login_data_t *ld = NULL;
239  std::vector<uint8> sendbuf;
240 
241  NetPrintText(_("*** Sending initialization data to server."));
242 
243  PlayersList.clear();
244  MDFNnetplay = true;
245 
246  sendbuf.resize(4 + sizeof(login_data_t) + nickname.size() + strlen(emu_id));
247 
248  MDFN_en32lsb(&sendbuf[0], sendbuf.size() - 4);
249  ld = (login_data_t*)&sendbuf[4];
250 
251  if(game_key.size())
252  {
253   md5_context md5;
254   uint8 md5out[16];
255 
256   md5.starts();
257   md5.update(MDFNGameInfo->MD5, 16);
258   md5.update((uint8 *)game_key.c_str(), game_key.size());
259   md5.finish(md5out);
260   memcpy(ld->gameid, md5out, 16);
261  }
262  else
263   memcpy(ld->gameid, MDFNGameInfo->MD5, 16);
264 
265  if(connect_password != "")
266  {
267   md5_context md5;
268   uint8 md5out[16];
269 
270   md5.starts();
271   md5.update((uint8*)connect_password.c_str(), connect_password.size());
272   md5.finish(md5out);
273   memcpy(ld->password, md5out, 16);
274  }
275 
276  assert(MDFNGameInfo->PortInfo.size() <= 16);
277 
278  ld->protocol_version = 3;
279 
280  // Set input device number thingies here.
281  ld->total_controllers = MDFNGameInfo->PortInfo.size(); // Total number of ports
282 
283  MDFN_en32lsb(ld->emu_name_len, strlen(emu_id));
284 
285  // Controller data sizes.
286  for(unsigned x = 0; x < MDFNGameInfo->PortInfo.size(); x++)
287   ld->controller_data_size[x] = PortDataLenCache[x];
288 
289  // Controller types
290  for(unsigned x = 0; x < MDFNGameInfo->PortInfo.size(); x++)
291   ld->controller_type[x] = PortDeviceCache[x];	// FIXME: expand controller_type from 8-bit -> 32-bit
292 
293  ld->local_players = local_players;
294 
295  if(nickname != "")
296   memcpy(&sendbuf[4 + sizeof(login_data_t)], nickname.c_str(), nickname.size());
297 
298  memcpy(&sendbuf[4 + sizeof(login_data_t) + nickname.size()], emu_id, strlen(emu_id));
299 
300  SendData(&sendbuf[0], sendbuf.size());
301 
302  TotalInputStateSize = 0;
303  for(unsigned x = 0; x < MDFNGameInfo->PortInfo.size(); x++)
304   TotalInputStateSize += PortDataLenCache[x];
305 
306  // Hack so the server can always encode its command data length properly(a matching "hack" exists in the server).
307  if(TotalInputStateSize < 4)
308   TotalInputStateSize = 4;
309 
310  incoming_buffer.reset(nullptr);
311  incoming_buffer.reset(new uint8[TotalInputStateSize + 1]);
312 
313  SetLPM(0, PortDeviceCache, PortDataLenCache);
314  Joined = false;
315 
316  //
317  //
318  //
319  for(unsigned x = 0; x < MDFNGameInfo->PortInfo.size(); x++)
320  {
321   PreNPPortDataPortData[x].assign(PortDataLenCache[x], 0);
322   PostEmulatePortData[x].assign(PortDataLenCache[x], 0);
323  }
324 
325  MDFN_FlushGameCheats(0);	/* Save our pre-netplay cheats. */
326 
327  if(MDFNMOV_IsPlaying())		/* Recording's ok during netplay, playback is not. */
328   MDFNMOV_Stop();
329 
330  NetPrintText(_("*** Connection established."));
331 
332  if(game_key.size())
333   NetPrintText(_("** Using game key: %s"), game_key.c_str());
334  //printf("%d\n", TotalInputStateSize);
335  //
336  //
337  MDFND_NetplaySetHints(true, Connection->CanReceive(), LocalPlayersMask);
338 }
339 
SendCommand(uint8 cmd,uint32 len,const void * data=NULL)340 static void SendCommand(uint8 cmd, uint32 len, const void* data = NULL)
341 {
342  outgoing_buffer[0] = cmd;
343  memset(&outgoing_buffer[1], 0, LocalInputStateSize);
344  MDFN_en32lsb(&outgoing_buffer[1 + LocalInputStateSize], len);
345  SendData(&outgoing_buffer[0], LocalInputStateSize + 1 + 4);
346 
347  if(data != NULL)
348  {
349   SendData(data, len);
350  }
351 }
352 
NetplaySendCommand(uint8 cmd,uint32 len,const void * data)353 bool NetplaySendCommand(uint8 cmd, uint32 len, const void* data)
354 {
355  try
356  {
357   SendCommand(cmd, len, data);
358  }
359  catch(std::exception &e)
360  {
361   NetError("%s", e.what());
362   return(false);
363  }
364  return(true);
365 }
366 
GenerateMPSString(uint32 mps,bool ctlr_string=false)367 static std::string GenerateMPSString(uint32 mps, bool ctlr_string = false)
368 {
369  std::string ret;
370 
371  if(!mps)
372  {
373   if(!ctlr_string)
374    ret = _("a lurker");
375  }
376  else
377  {
378   ret = ctlr_string ? ((mps == round_up_pow2(mps)) ? _("controller") : _("controllers")) : ((mps == round_up_pow2(mps)) ? _("player") : _("players"));
379 
380   for(unsigned i = 0; i < 16; i++)
381   {
382    if(mps & (1U << i))
383    {
384     char tmp[16];
385     trio_snprintf(tmp, sizeof(tmp), " %u", i + 1);
386     ret += tmp;
387    }
388   }
389  }
390 
391  return ret;
392 }
393 
MDFNI_NetplaySwap(uint8 a,uint8 b)394 static void MDFNI_NetplaySwap(uint8 a, uint8 b)
395 {
396  try
397  {
398   SendCommand(MDFNNPCMD_CTRLR_SWAP, (a << 0) | (b << 8));
399  }
400  catch(std::exception &e)
401  {
402   NetError("%s", e.what());
403  }
404 }
405 
MDFNI_NetplayTake(uint32 mask)406 static void MDFNI_NetplayTake(uint32 mask)
407 {
408  try
409  {
410   SendCommand(MDFNNPCMD_CTRLR_TAKE, mask);
411  }
412  catch(std::exception &e)
413  {
414   NetError("%s", e.what());
415  }
416 }
417 
MDFNI_NetplayDrop(uint32 mask)418 static void MDFNI_NetplayDrop(uint32 mask)
419 {
420  try
421  {
422   SendCommand(MDFNNPCMD_CTRLR_DROP, mask);
423  }
424  catch(std::exception &e)
425  {
426   NetError("%s", e.what());
427  }
428 }
429 
MDFNI_NetplayDupe(uint32 mask)430 static void MDFNI_NetplayDupe(uint32 mask)
431 {
432  try
433  {
434   SendCommand(MDFNNPCMD_CTRLR_DUPE, mask);
435  }
436  catch(std::exception &e)
437  {
438   NetError("%s", e.what());
439  }
440 }
441 
MDFNI_NetplayList(void)442 static void MDFNI_NetplayList(void)
443 {
444  try
445  {
446   for(auto& it : PlayersList)
447   {
448    NetPrintText(_("** <%s> is %s"), it.first.c_str(), GenerateMPSString(it.second).c_str());
449   }
450  }
451  catch(std::exception &e)
452  {
453   NetError("%s", e.what());
454  }
455 }
456 
457 
MDFNI_NetplayPing(void)458 static void MDFNI_NetplayPing(void)
459 {
460  try
461  {
462   uint64 now_time;
463 
464   now_time = Time::MonoMS();
465 
466   // Endianness doesn't matter, since it will be echoed back only to us.
467   SendCommand(MDFNNPCMD_ECHO, sizeof(now_time), &now_time);
468  }
469  catch(std::exception &e)
470  {
471   NetError("%s", e.what());
472  }
473 }
474 #if 0
475 static void MDFNI_NetplayIntegrity(void)
476 {
477  try
478  {
479   SendCommand(MDFNNPCMD_INTEGRITY, 0);
480  }
481  catch(std::exception &e)
482  {
483   NetError("%s", e.what());
484  }
485 }
486 #endif
MDFNI_NetplayText(const char * text)487 static void MDFNI_NetplayText(const char *text)
488 {
489  try
490  {
491   uint32 len;
492 
493   if(!Joined) return;
494 
495   len = strlen(text);
496 
497   SendCommand(MDFNNPCMD_TEXT, len, text);
498  }
499  catch(std::exception &e)
500  {
501   NetError("%s", e.what());
502  }
503 }
504 
MDFNI_NetplayChangeNick(const char * newnick)505 static void MDFNI_NetplayChangeNick(const char* newnick)
506 {
507  try
508  {
509   uint32 len;
510 
511   if(!Joined) return;
512 
513   len = strlen(newnick);
514 
515   SendCommand(MDFNNPCMD_SETNICK, len, newnick);
516  }
517  catch(std::exception &e)
518  {
519   NetError("%s", e.what());
520  }
521 }
522 
MDFNI_NetplayQuit(const char * quit_message)523 static void MDFNI_NetplayQuit(const char *quit_message)
524 {
525  try
526  {
527   SendCommand(MDFNNPCMD_QUIT, strlen(quit_message), quit_message);
528  }
529  catch(std::exception &e)
530  {
531   NetError("%s", e.what());
532  }
533 }
534 
535 
536 //
537 // Integrity checking is experimental, and needs work to function properly(in the emulator cores).
538 //
SendIntegrity(void)539 static int SendIntegrity(void)
540 {
541  MemoryStream sm(65536);
542  md5_context md5;
543  uint8 digest[16];
544 
545  // Do not do a raw/data-only state for speed, due to lack of endian and bool conversion.
546  MDFNSS_SaveSM(&sm, false);
547 
548  md5.starts();
549  md5.update(sm.map(), sm.size());
550  md5.finish(digest);
551 
552  SendCommand(MDFNNPCMD_INTEGRITY_RES, 16, digest);
553 
554  return(1);
555 }
556 
557 
SendState(void)558 static void SendState(void)
559 {
560  std::vector<uint8> cbuf;
561  uLongf clen;
562 
563  {
564   MemoryStream sm(65536);
565 
566   MDFNSS_SaveSM(&sm, false);
567 
568   clen = sm.size() + sm.size() / 1000 + 12;
569   cbuf.resize(4 + clen);
570   MDFN_en32lsb(&cbuf[0], sm.size());
571   compress2((Bytef *)&cbuf[0] + 4, &clen, (Bytef *)sm.map(), sm.size(), 7);
572  }
573 
574  SendCommand(MDFNNPCMD_LOADSTATE, clen + 4, &cbuf[0]);
575 }
576 
RecvState(const uint32 clen)577 static void RecvState(const uint32 clen)
578 {
579  std::vector<uint8> cbuf;
580 
581  if(clen < 4)
582  {
583   throw MDFN_Error(0, _("Compressed save state data is too small: %u"), clen);
584  }
585 
586  if(clen > 8 * 1024 * 1024) // Compressed length sanity check - 8 MiB max.
587  {
588   throw MDFN_Error(0, _("Compressed save state data is too large: %u"), clen);
589  }
590 
591  cbuf.resize(clen);
592 
593  RecvData(&cbuf[0], clen);
594 
595  uLongf len = MDFN_de32lsb(&cbuf[0]);
596  if(len > 12 * 1024 * 1024) // Uncompressed length sanity check - 12 MiB max.
597  {
598   throw MDFN_Error(0, _("Uncompressed save state data is too large: %llu"), (unsigned long long)len);
599  }
600 
601  MemoryStream sm(len, -1);
602 
603  uncompress((Bytef *)sm.map(), &len, (Bytef *)&cbuf[0] + 4, clen - 4);
604 
605  MDFNSS_LoadSM(&sm, false);
606 
607  if(MDFNMOV_IsRecording())
608   MDFNMOV_RecordState();
609 }
610 
NetplaySendState(void)611 void NetplaySendState(void)
612 {
613  try
614  {
615   SendState();
616  }
617  catch(std::exception &e)
618  {
619   NetError("%s", e.what());
620  }
621 }
622 
ProcessCommand(const uint8 cmd,const uint32 raw_len,const uint32 PortDevIdx[],uint8 * const PortData[],const uint32 PortLen[],int NumPorts)623 static void ProcessCommand(const uint8 cmd, const uint32 raw_len, const uint32 PortDevIdx[], uint8* const PortData[], const uint32 PortLen[], int NumPorts)
624 {
625   switch(cmd)
626   {
627    case 0: break; // No command
628 
629    default: MDFN_DoSimpleCommand(cmd);
630 	    break;
631 
632    case MDFNNPCMD_INTEGRITY:
633 			SendIntegrity();
634 			break;
635 
636    case MDFNNPCMD_REQUEST_STATE:
637 			SendState();
638 	  	 	break;
639 
640    case MDFNNPCMD_LOADSTATE:
641 			RecvState(raw_len);
642 			StateLoaded = true;
643 			MDFN_Notify(MDFN_NOTICE_STATUS, _("Remote state loaded."));
644 			break;
645 
646    case MDFNNPCMD_SET_MEDIA:
647 			{
648 			 uint8 buf[4 * 4];
649 
650 			 RecvData(buf, sizeof(buf));
651 
652 			 MDFN_UntrustedSetMedia(MDFN_de32lsb(&buf[0]), MDFN_de32lsb(&buf[4]), MDFN_de32lsb(&buf[8]), MDFN_de32lsb(&buf[12]));
653 			}
654 			break;
655 
656    case MDFNNPCMD_SERVERTEXT:
657 			{
658 			 static const uint32 MaxLength = 2000;
659                          uint8 neobuf[MaxLength + 1];
660                          char *textbuf = NULL;
661                          const uint32 totallen = raw_len;
662 
663                          if(totallen > MaxLength) // Sanity check
664                          {
665                           throw MDFN_Error(0, _("Text length is too long: %u"), totallen);
666                          }
667 
668                          RecvData(neobuf, totallen);
669 
670 			 neobuf[totallen] = 0;
671 			 trio_asprintf(&textbuf, "** %s", neobuf);
672                          MDFND_NetplayText(textbuf, false);
673                          free(textbuf);
674 			}
675 			break;
676 
677    case MDFNNPCMD_ECHO:
678 			{
679                          uint32 totallen = raw_len;
680 			 uint64 then_time;
681 			 uint64 now_time;
682 
683 			 if(totallen != sizeof(then_time))
684 			 {
685                           throw MDFN_Error(0, _("Echo response length is incorrect size: %u"), totallen);
686 			 }
687 
688                          RecvData(&then_time, sizeof(then_time));
689 
690 			 now_time = Time::MonoMS();
691 
692                          char *textbuf = NULL;
693 			 trio_asprintf(&textbuf, _("*** Round-trip time: %llu ms"), (unsigned long long)(now_time - then_time));
694                          MDFND_NetplayText(textbuf, false);
695                          free(textbuf);
696 			}
697 			break;
698 
699    case MDFNNPCMD_TEXT:
700 			{
701 			 static const uint32 MaxLength = 2000;
702 			 char neobuf[MaxLength + 1];
703 			 const uint32 totallen = raw_len;
704                          uint32 nicklen;
705                          bool NetEcho = false;
706                          char *textbuf = NULL;
707 
708 			 if(totallen < 4)
709 			 {
710 			  throw MDFN_Error(0, _("Text command length is too short: %u"), totallen);
711 	  		 }
712 
713 			 if(totallen > MaxLength) // Sanity check
714 			 {
715                           throw MDFN_Error(0, _("Text command length is too long: %u"), totallen);
716 			 }
717 
718 			 RecvData(neobuf, totallen);
719 
720 			 nicklen = MDFN_de32lsb(neobuf);
721 			 if(nicklen > (totallen - 4)) // Sanity check
722 			 {
723 			  throw MDFN_Error(0, _("Received nickname length is too long: %u"), nicklen);
724 			 }
725 
726                          neobuf[totallen] = 0;
727 
728 			 if(nicklen)
729 			 {
730 			  memmove(neobuf, neobuf + 4, nicklen);
731 			  neobuf[nicklen] = 0;
732 
733 			  if(OurNick == neobuf)
734 			  {
735                            trio_asprintf(&textbuf, "> %s", &neobuf[4 + nicklen]);
736 			   NetEcho = true;
737 			  }
738 			  else
739 			   trio_asprintf(&textbuf, "<%s> %s", neobuf, &neobuf[4 + nicklen]);
740 			 }
741 		         else
742 			 {
743 			  trio_asprintf(&textbuf, "* %s", &neobuf[4]);
744 			 }
745                          MDFND_NetplayText(textbuf, NetEcho);
746 			 free(textbuf);
747 			}
748 			break;
749 
750    case MDFNNPCMD_NICKCHANGED:
751 			{
752 			 static const uint32 MaxLength = 2000;
753                          char neobuf[MaxLength + 1];
754                          char* newnick;
755                          char* textbuf = NULL;
756 			 const uint32 len = raw_len;
757 
758                          if(len > MaxLength) // Sanity check
759                          {
760                           throw MDFN_Error(0, _("Nickname change length is too long: %u"), len);
761                          }
762 
763                          RecvData(neobuf, len);
764 
765 			 neobuf[len] = 0;
766 
767 			 newnick = strchr(neobuf, '\n');
768 
769 			 if(newnick)
770 			 {
771 			  bool IsMeow = false;
772 
773 			  *newnick = 0;
774 			  newnick++;
775 
776 			  if(OurNick == neobuf)
777 			  {
778 			   OurNick = newnick;
779 			   textbuf = trio_aprintf(_("* You are now known as <%s>."), newnick);
780 			   IsMeow = true;
781 			  }
782 
783 			  if(!textbuf)
784 			   textbuf = trio_aprintf(_("* <%s> is now known as <%s>"), neobuf, newnick);
785 
786                           MDFND_NetplayText(textbuf, IsMeow);
787 			  free(textbuf);
788 
789 			  // Update players list.
790 			  {
791 			   const std::string ons(neobuf);
792 			   const std::string nns(newnick);
793 
794 			   if(ons != nns)
795 			   {
796 	 	 	    auto ons_it = PlayersList.find(ons);
797 			    auto nns_it = PlayersList.find(nns);
798 
799 			    if(ons_it == PlayersList.end() || nns_it != PlayersList.end())
800 			     MDFND_NetplayText(_("[BUG] Players list state out of sync."), false);
801 			    else
802 			    {
803 			     PlayersList[nns] = ons_it->second;
804 			     PlayersList.erase(ons_it);
805 			    }
806 			   }
807 			  }
808 			 }
809 			}
810 			break;
811 
812     case MDFNNPCMD_CTRL_CHANGE:
813 			{
814 			 const uint32 len = raw_len;
815 
816 			 //
817                          // Joined = true;
818                          SendCommand(MDFNNPCMD_CTRL_CHANGE_ACK, len);
819 			 //
820 			 //
821                          LocalInputStateSize = 0;
822 			 SetLPM(len, PortDevIdx, PortLen);
823 			}
824 			break;
825 
826    case MDFNNPCMD_CTRLR_SWAP_NOTIF:
827 			{
828 			 const uint32 cm = raw_len;
829 			 char textbuf[512];
830 			 const uint8 c0 = cm & 0xFF;
831 			 const uint8 c1 = (cm >> 8) & 0xFF;
832 
833 			 trio_snprintf(textbuf, sizeof(textbuf), _("* All instances of controllers %u and %u have been swapped."), c0 + 1, c1 + 1);
834 			 MDFND_NetplayText(textbuf, false);
835 
836 			 for(auto& it : PlayersList)
837 			 {
838 			  uint32 mps = it.second;
839 			  bool c0b, c1b;
840 
841 			  // Grab bits first before any clearing.
842 		 	  c0b = ((c0 < sizeof(mps) * 8) ? ((mps >> c0) & 1) : false);
843 			  c1b = ((c1 < sizeof(mps) * 8) ? ((mps >> c1) & 1) : false);
844 
845 			  if(c0 < sizeof(mps) * 8)
846 			  {
847 			   mps &= ~((uint32)1 << c0);
848 			   mps |= (uint32)c1b << c0;
849 			  }
850 
851 			  if(c1 < sizeof(mps) * 8)
852 			  {
853 			   mps &= ~((uint32)1 << c1);
854 			   mps |= (uint32)c0b << c1;
855 			  }
856 
857 			  it.second = mps;
858 			 }
859 			}
860 			break;
861 
862    case MDFNNPCMD_CTRLR_TAKE_NOTIF:
863    case MDFNNPCMD_CTRLR_DROP_NOTIF:
864    case MDFNNPCMD_CTRLR_DUPE_NOTIF:
865 			{
866 			 static const uint32 MaxNicknameLength = 1000;
867 			 static const uint32 MaxLength = 12 + MaxNicknameLength;
868 			 const char *fstr = NULL;
869 			 const uint32 len = raw_len;
870 			 uint8 ntf_buf[MaxLength + 1];
871 			 char *textbuf = NULL;
872 
873 			 if(len < 12)
874 			  throw MDFN_Error(0, _("Take/drop/dupe notification is too short: %u"), len);
875 
876 			 if(len > MaxLength)
877 			  throw MDFN_Error(0, _("Take/drop/dupe notification is too long: %u"), len);
878 
879 			 RecvData(ntf_buf, len);
880 			 ntf_buf[len] = 0;
881 
882  	 	 	 switch(cmd)
883 			 {
884 			  case MDFNNPCMD_CTRLR_TAKE_NOTIF:
885 			  	fstr = _("* <%s> took all instances of %s, and is now %s.");
886 				break;
887 
888 			  case MDFNNPCMD_CTRLR_DUPE_NOTIF:
889 			 	fstr = _("* <%s> took copies of %s, and is now %s.");
890 				break;
891 
892 			  case MDFNNPCMD_CTRLR_DROP_NOTIF:
893 				fstr = _("* <%s> dropped %s, and is now %s.");
894 				break;
895 			 }
896                          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());
897 	                 MDFND_NetplayText(textbuf, false);
898 			 free(textbuf);
899 
900 			 // Update players list.
901 			 {
902 			  auto it = PlayersList.find(std::string((const char*)ntf_buf + 12));
903 
904 			  if(it == PlayersList.end())
905 			   MDFND_NetplayText(_("[BUG] Players list state out of sync."), false);
906 			  else
907 			   it->second = MDFN_de32lsb(&ntf_buf[4]);
908 			 }
909 			}
910 			break;
911 
912    case MDFNNPCMD_YOUJOINED:
913    case MDFNNPCMD_YOULEFT:
914    case MDFNNPCMD_PLAYERLEFT:
915    case MDFNNPCMD_PLAYERJOINED:
916 			{
917 			 static const uint32 MaxLength = 2000;
918                          uint8 neobuf[MaxLength + 1];
919                          char *textbuf = NULL;
920 			 uint32 mps;
921                          std::string mps_string;
922 			 const uint32 len = raw_len;
923 
924 			 if(len < 8)
925 			 {
926                           throw MDFN_Error(0, _("Join/Left length is too short: %u"), len);
927 		         }
928 
929                          if(len > MaxLength) // Sanity check
930                          {
931                           throw MDFN_Error(0, _("Join/Left length is too long: %u"), len);
932                          }
933 
934                          RecvData(neobuf, len);
935 			 neobuf[len] = 0; // NULL-terminate the string
936 
937 			 mps = MDFN_de32lsb(&neobuf[0]);
938 
939 			 mps_string = GenerateMPSString(mps);
940 
941 			 if(cmd == MDFNNPCMD_YOULEFT)
942 			 {
943 			  // Uhm, not supported yet!
944 			  SetLPM(0, PortDevIdx, PortLen);
945 			  Joined = false;
946 			 }
947 			 else if(cmd == MDFNNPCMD_YOUJOINED)
948 			 {
949 			  OurNick = (char*)neobuf + 8;
950 
951                           trio_asprintf(&textbuf, _("* You, %s, have connected as: %s"), neobuf + 8, mps_string.c_str());
952 
953 			  SetLPM(mps, PortDevIdx, PortLen);
954 			  Joined = true;
955 
956 			  SendCommand(MDFNNPCMD_SETFPS, MDFNGameInfo->fps);
957 			 }
958 			 else if(cmd == MDFNNPCMD_PLAYERLEFT)
959 			 {
960                                   trio_asprintf(&textbuf, _("* %s(%s) has left"), neobuf + 8, mps_string.c_str());
961 			 }
962 			 else
963 			 {
964                                   trio_asprintf(&textbuf, _("* %s has connected as: %s"), neobuf + 8, mps_string.c_str());
965 			 }
966 
967 	                 MDFND_NetplayText(textbuf, false);
968 			 free(textbuf);
969 
970 			 // Update players list.
971 			 if(cmd == MDFNNPCMD_YOUJOINED || cmd == MDFNNPCMD_PLAYERJOINED)
972 			 {
973 			  PlayersList[std::string((const char*)neobuf + 8)] = mps;
974 			 }
975 			 else
976 			 {
977 			  auto it = PlayersList.find(std::string((const char*)neobuf + 8));
978 
979 			  if(it == PlayersList.end())
980 			   MDFND_NetplayText(_("[BUG] Players list state out of sync."), false);
981 			  else
982 			   PlayersList.erase(it);
983 			 }
984 			}
985 			break;
986   }
987 }
988 
989 #if 0
990        for(auto const& idii : *IDII_N)
991        {
992         if(idii.Type == IDIT_SWITCH)
993         {
994 	 const uint32 cur = BitsExtract(&PortData[n][0], idii.BitOffset, idii.BitSize);
995          const uint32 prev = BitsExtract(&PrevPortData[n][0], idii.BitOffset, idii.BitSize);
996          const uint32 delta = cur - prev;
997 
998 	 printf("%d\n", delta);
999 
1000 	 // We mustn't modify PortData with this filter, only outgoing_buffer(because we can load state in the middle of this function,
1001 	 // which will load new port data that doesn't have this filter applied, and things go boom-boom)!
1002 	 BitsIntract(&outgoing_buffer[wpos], idii.BitOffset, idii.BitSize, delta);
1003         }
1004        }
1005 #endif
1006 
1007 #if 0
1008     if(LocalPlayersMask & (1 << x))
1009     {
1010      for(unsigned n = 0; n <= x; n++)
1011      {
1012       auto const* IDII_N = &MDFNGameInfo->PortInfo[n].DeviceInfo[PortDevIdx[n]].IDII;
1013       auto const* IDII_X = &MDFNGameInfo->PortInfo[x].DeviceInfo[PortDevIdx[x]].IDII;
1014 
1015       if(!Taken[n] && IDII_N == IDII_X)
1016       {
1017        memcpy(outgoing_buffer + wpos, PortData[n], PortLen[n]);
1018        Taken[n] = true;
1019        wpos += PortLen[n];
1020        break;
1021       }
1022      }
1023     }
1024 #endif
1025 
Netplay_Update(const uint32 PortDevIdx[],uint8 * const PortData[],const uint32 PortLen[])1026 void Netplay_Update(const uint32 PortDevIdx[], uint8* const PortData[], const uint32 PortLen[])
1027 {
1028  const unsigned NumPorts = MDFNGameInfo->PortInfo.size();
1029 
1030  StateLoaded = false;
1031 
1032  try
1033  {
1034   if(!Connection)
1035    return;
1036 
1037   if(!MDFNnetplay)
1038   {
1039    try
1040    {
1041     if(!Connection->Established())
1042      return;
1043    }
1044    catch(...)
1045    {
1046     Connection.reset(nullptr);
1047     throw;
1048    }
1049 
1050    NetplayStart(PortDevIdx, PortLen);
1051   }
1052   //
1053   //
1054   //
1055   for(unsigned x = 0; x < NumPorts; x++)
1056   {
1057    if(!PortLen[x])
1058     continue;
1059 
1060    memcpy(PreNPPortDataPortData[x].data(), PortData[x], PortLen[x]);
1061   }
1062 
1063   if(Joined)
1064   {
1065    outgoing_buffer[0] = 0; 	// Not a command
1066 
1067    for(unsigned x = 0, wpos = 1; x < NumPorts; x++)
1068    {
1069     if(!PortLen[x])
1070      continue;
1071 
1072     auto n = PortVtoLVMap[x];
1073     if(n != 0xFF)
1074     {
1075      memcpy(&outgoing_buffer[wpos], PortData[n], PortLen[n]);
1076      wpos += PortLen[n];
1077     }
1078    }
1079    SendData(&outgoing_buffer[0], 1 + LocalInputStateSize);
1080   }
1081   //
1082   //
1083   //
1084   uint8 cmd;
1085   uint32 cmd_raw_len;
1086 
1087   do
1088   {
1089    RecvData(&incoming_buffer[0], TotalInputStateSize + 1);
1090 
1091    cmd = incoming_buffer[TotalInputStateSize];
1092    cmd_raw_len = MDFN_de32lsb(&incoming_buffer[0]);
1093 
1094    if(cmd != 0)
1095     ProcessCommand(cmd, cmd_raw_len, PortDevIdx, PortData, PortLen, NumPorts);
1096   } while(cmd != 0);
1097 
1098   //
1099   // Update local port data buffers with data received.
1100   //
1101   for(unsigned x = 0, rpos = 0; x < NumPorts; x++)
1102   {
1103    memcpy(PortData[x], &incoming_buffer[rpos], PortLen[x]);
1104    rpos += PortLen[x];
1105   }
1106  }
1107  catch(std::exception &e)
1108  {
1109   NetError("%s", e.what());
1110  }
1111 }
1112 
Netplay_PostProcess(const uint32 PortDevIdx[],uint8 * const PortData[],const uint32 PortLen[])1113 void Netplay_PostProcess(const uint32 PortDevIdx[], uint8* const PortData[], const uint32 PortLen[])
1114 {
1115  const unsigned NumPorts = MDFNGameInfo->PortInfo.size();
1116 
1117  //
1118  // Make a backup copy of the current port data, then zero the port data(specifically for rumble and status bits).
1119  //
1120  for(unsigned x = 0; x < NumPorts; x++)
1121  {
1122   if(!PortLen[x])
1123    continue;
1124 
1125   assert(PostEmulatePortData[x].size() == PortLen[x]);
1126   assert(PreNPPortDataPortData[x].size() == PortLen[x]);
1127 
1128   memcpy(PostEmulatePortData[x].data(), PortData[x], PortLen[x]);
1129   memset(PortData[x], 0, PortLen[x]);
1130  }
1131 
1132  //
1133  // Remap rumble and status(along with other data that doesn't matter), and copy switch state bits
1134  // into the current port data from a backup copy saved BEFORE all netplay shenanigans(including load remote save state),
1135  // as a kludgey way of keeping switches from getting into a delay/feedback loop and going bonkers.
1136  //
1137  for(unsigned x = 0; x < NumPorts; x++)
1138  {
1139   if(PortLVtoVMap[x] != 0xFF)
1140    memcpy(PortData[x], PostEmulatePortData[PortLVtoVMap[x]].data(), PortLen[x]);
1141 
1142   for(auto const& idii : MDFNGameInfo->PortInfo[x].DeviceInfo[PortDevIdx[x]].IDII)
1143   {
1144    switch(idii.Type)
1145    {
1146     default:
1147 	break;
1148     case IDIT_SWITCH:
1149 	{
1150 	 uint32 tmp;
1151 
1152 	 tmp = BitsExtract(PreNPPortDataPortData[x].data(), idii.BitOffset, idii.BitSize);
1153 	 BitsIntract(PortData[x], idii.BitOffset, idii.BitSize, tmp);
1154 	}
1155 	break;
1156    }
1157   }
1158  }
1159 }
1160 
1161 //
1162 //
1163 //
1164 //
1165 
1166 struct CommandEntry
1167 {
1168  const char *name;
1169  bool (*func)(const char* arg);
1170  const char *help_args;
1171  const char *help_desc;
1172 };
1173 
1174 static bool CC_server(const char *arg);
1175 static bool CC_quit(const char *arg);
1176 static bool CC_help(const char *arg);
1177 static bool CC_nick(const char *arg);
1178 static bool CC_ping(const char *arg);
1179 //static bool CC_integrity(const char *arg);
1180 static bool CC_gamekey(const char *arg);
1181 static bool CC_swap(const char *arg);
1182 static bool CC_dupe(const char *arg);
1183 static bool CC_drop(const char *arg);
1184 static bool CC_take(const char *arg);
1185 static bool CC_list(const char *arg);
1186 
1187 static CommandEntry ConsoleCommands[]   =
1188 {
1189  { "/server", CC_server,	gettext_noop("[REMOTE_HOST] [PORT]"), "Connects to REMOTE_HOST(IP address or FQDN), on PORT." },
1190 
1191  { "/connect", CC_server,	NULL, NULL },
1192 
1193  { "/gamekey", CC_gamekey,	gettext_noop("[GAMEKEY]"), gettext_noop("Changes the game key to the specified GAMEKEY.") },
1194 
1195  { "/quit", CC_quit,		gettext_noop("[MESSAGE]"), gettext_noop("Disconnects from the netplay server.") },
1196 
1197  { "/help", CC_help,		"", gettext_noop("Help, I'm drowning in a sea of cliche metaphors!") },
1198 
1199  { "/nick", CC_nick,		gettext_noop("NICKNAME"), gettext_noop("Changes your nickname to the specified NICKNAME.") },
1200 
1201  { "/swap", CC_swap,		gettext_noop("A B"), gettext_noop("Swap/Exchange all instances of controllers A and B(numbered from 1).") },
1202 
1203  { "/dupe", CC_dupe,            gettext_noop("[A] [...]"), gettext_noop("Duplicate and take instances of specified controller(s).") },
1204  { "/drop", CC_drop,            gettext_noop("[A] [...]"), gettext_noop("Drop all instances of specified controller(s).") },
1205  { "/take", CC_take,            gettext_noop("[A] [...]"), gettext_noop("Take all instances of specified controller(s).") },
1206 
1207  { "/list", CC_list,		"", "List players in game." },
1208 
1209  { "/ping", CC_ping,		"", "Pings the server." },
1210 
1211  //{ "/integrity", CC_integrity,	"", "Starts netplay integrity check sequence." },
1212 
1213  { NULL, NULL },
1214 };
1215 
MDFNI_NetplayDisconnect(void)1216 void MDFNI_NetplayDisconnect(void)
1217 {
1218  const bool had_connection = Connection != nullptr;
1219  Connection.reset(nullptr);
1220 
1221  if(MDFNnetplay)
1222  {
1223   Joined = false;
1224   MDFNnetplay = 0;
1225   MDFN_FlushGameCheats(1);	/* Don't save netplay cheats. */
1226   MDFN_LoadGameCheats(0);	/* Reload our original cheats. */
1227   OurNick.clear();
1228   PlayersList.clear();
1229   incoming_buffer.reset(nullptr);
1230   outgoing_buffer.reset(nullptr);
1231 
1232   NetPrintText(_("*** Disconnected"));
1233  }
1234  else if(had_connection)
1235  {
1236   NetPrintText(_("*** In-progress connection attempt aborted"));
1237  }
1238 
1239  MDFND_NetplaySetHints(false, false, 0);
1240 }
1241 
1242 
MDFNI_NetplayConnect(void)1243 void MDFNI_NetplayConnect(void)
1244 {
1245  MDFNI_NetplayDisconnect();
1246  //
1247  //
1248  try
1249  {
1250   std::string remote_host = MDFN_GetSettingS("netplay.host");
1251   unsigned int remote_port = MDFN_GetSettingUI("netplay.port");
1252 
1253   NetPrintText(_("*** Connecting to %s port %u..."), remote_host.c_str(), remote_port);
1254   Connection = Net::Connect(remote_host.c_str(), remote_port);
1255  }
1256  catch(std::exception &e)
1257  {
1258   NetError("%s", e.what());
1259  }
1260 }
1261 
CC_server(const char * arg)1262 static bool CC_server(const char *arg)
1263 {
1264  char server[300];
1265  unsigned int port = 0;
1266 
1267  server[0] = 0;
1268 
1269  switch(trio_sscanf(arg, "%299s %u", server, &port))
1270  {
1271   default:
1272   case 0:
1273 	break;
1274 
1275   case 1:
1276 	MDFNI_SetSetting("netplay.host", server);
1277 	break;
1278 
1279   case 2:
1280 	MDFNI_SetSetting("netplay.host", server);
1281 	MDFNI_SetSettingUI("netplay.port", port);
1282 	break;
1283  }
1284 
1285  MDFNI_NetplayConnect();
1286 
1287  return(false);
1288 }
1289 
CC_gamekey(const char * arg)1290 static bool CC_gamekey(const char *arg)
1291 {
1292  MDFNI_SetSetting("netplay.gamekey", arg);
1293 
1294  if(arg[0] == 0)
1295   NetPrintText(_("** Game key cleared."));
1296  else
1297  {
1298   NetPrintText(_("** Game key changed to: %s"), arg);
1299  }
1300 
1301  if(MDFNnetplay)
1302  {
1303   NetPrintText(_("** Caution: Changing the game key will not affect the current netplay session."));
1304  }
1305 
1306  return(true);
1307 }
1308 
CC_quit(const char * arg)1309 static bool CC_quit(const char *arg)
1310 {
1311  if(!MDFNnetplay && !Connection)
1312  {
1313   NetPrintText(_("*** Not connected!"));
1314   return true;
1315  }
1316 
1317  if(MDFNnetplay)
1318   MDFNI_NetplayQuit(arg);
1319 
1320  MDFNI_NetplayDisconnect();
1321 
1322  return false;
1323 }
1324 
CC_list(const char * arg)1325 static bool CC_list(const char *arg)
1326 {
1327  if(MDFNnetplay)
1328   MDFNI_NetplayList();
1329  else
1330  {
1331   NetPrintText(_("*** Not connected!"));
1332   return(true);
1333  }
1334 
1335  return(true);
1336 }
1337 
CC_swap(const char * arg)1338 static bool CC_swap(const char *arg)
1339 {
1340  int a = 0, b = 0;
1341 
1342  if(sscanf(arg, "%u %u", &a, &b) == 2 && a && b)
1343  {
1344   uint32 sc = ((a - 1) & 0xFF) | (((b - 1) & 0xFF) << 8);
1345 
1346   if(MDFNnetplay)
1347    MDFNI_NetplaySwap((sc >> 0) & 0xFF, (sc >> 8) & 0xFF);
1348   else
1349   {
1350    NetPrintText(_("*** Not connected!"));
1351    return(true);
1352   }
1353  }
1354  else
1355  {
1356   NetPrintText(_("*** %s command requires at least %u non-zero integer argument(s)."), "SWAP", 2);
1357   return(true);
1358  }
1359 
1360  return(false);
1361 }
1362 
CC_dupe(const char * arg)1363 static bool CC_dupe(const char *arg)
1364 {
1365  int tmp[32];
1366  int count;
1367 
1368 
1369  memset(tmp, 0, sizeof(tmp));
1370  count = sscanf(arg, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
1371 			&tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1372 			&tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F],
1373                         &tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1374                         &tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F]);
1375 
1376  if(count > 0)
1377  {
1378   uint32 mask = 0;
1379 
1380   for(int i = 0; i < 32; i++)
1381   {
1382    if(tmp[i] > 0)
1383     mask |= 1U << (unsigned)(tmp[i] - 1);
1384   }
1385 
1386   if(MDFNnetplay)
1387    MDFNI_NetplayDupe(mask);
1388   else
1389   {
1390    NetPrintText(_("*** Not connected!"));
1391    return(true);
1392   }
1393  }
1394  else
1395  {
1396   NetPrintText(_("*** %s command requires at least %u non-zero integer argument(s)."), "DUPE", 1);
1397   return(true);
1398  }
1399 
1400  return(false);
1401 }
1402 
CC_drop(const char * arg)1403 static bool CC_drop(const char *arg)
1404 {
1405  int tmp[32];
1406  int count;
1407 
1408 
1409  memset(tmp, 0, sizeof(tmp));
1410  count = sscanf(arg, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
1411                         &tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1412                         &tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F],
1413                         &tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1414                         &tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F]);
1415 
1416  if(count > 0)
1417  {
1418   uint32 mask = 0;
1419 
1420   for(int i = 0; i < 32; i++)
1421   {
1422    if(tmp[i] > 0)
1423     mask |= 1U << (unsigned)(tmp[i] - 1);
1424   }
1425 
1426   if(MDFNnetplay)
1427    MDFNI_NetplayDrop(mask);
1428   else
1429   {
1430    NetPrintText(_("*** Not connected!"));
1431    return(true);
1432   }
1433  }
1434  else
1435  {
1436   NetPrintText(_("*** %s command requires at least %u non-zero integer argument(s)."), "DROP", 1);
1437   return(true);
1438  }
1439 
1440  return(false);
1441 }
1442 
CC_take(const char * arg)1443 static bool CC_take(const char *arg)
1444 {
1445  int tmp[32];
1446  int count;
1447 
1448 
1449  memset(tmp, 0, sizeof(tmp));
1450  count = sscanf(arg, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
1451                         &tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1452                         &tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F],
1453                         &tmp[0x00], &tmp[0x01], &tmp[0x02], &tmp[0x03], &tmp[0x04], &tmp[0x05], &tmp[0x06], &tmp[0x07],
1454                         &tmp[0x08], &tmp[0x09], &tmp[0x0A], &tmp[0x0B], &tmp[0x0C], &tmp[0x0D], &tmp[0x0E], &tmp[0x0F]);
1455 
1456  if(count > 0)
1457  {
1458   uint32 mask = 0;
1459 
1460   for(int i = 0; i < 32; i++)
1461   {
1462    if(tmp[i] > 0)
1463     mask |= 1U << (unsigned)(tmp[i] - 1);
1464   }
1465 
1466   if(MDFNnetplay)
1467    MDFNI_NetplayTake(mask);
1468   else
1469   {
1470    NetPrintText(_("*** Not connected!"));
1471    return(true);
1472   }
1473  }
1474  else
1475  {
1476   NetPrintText(_("*** %s command requires at least %u non-zero integer argument(s)."), "TAKE", 1);
1477   return(true);
1478  }
1479 
1480  return(false);
1481 }
1482 
CC_ping(const char * arg)1483 static bool CC_ping(const char *arg)
1484 {
1485  if(MDFNnetplay)
1486   MDFNI_NetplayPing();
1487  else
1488  {
1489   NetPrintText(_("*** Not connected!"));
1490   return(true);
1491  }
1492 
1493  return(false);
1494 }
1495 
1496 #if 0
1497 static bool CC_integrity(const char *arg)
1498 {
1499  if(MDFNnetplay)
1500   MDFNI_NetplayIntegrity();
1501  else
1502  {
1503   NetPrintText(_("*** Not connected!"));
1504   return(true);
1505  }
1506 
1507  return(false);
1508 }
1509 #endif
1510 
CC_help(const char * arg)1511 static bool CC_help(const char *arg)
1512 {
1513  for(unsigned int x = 0; ConsoleCommands[x].name; x++)
1514  {
1515   if(ConsoleCommands[x].help_desc)
1516   {
1517    char help_buf[512];
1518    trio_snprintf(help_buf, 512, "%s %s  -  %s", ConsoleCommands[x].name, _(ConsoleCommands[x].help_args), _(ConsoleCommands[x].help_desc));
1519    MDFND_NetplayText(help_buf, false);
1520   }
1521  }
1522  return(true);
1523 }
1524 
CC_nick(const char * arg)1525 static bool CC_nick(const char *arg)
1526 {
1527  MDFNI_SetSetting("netplay.nick", arg);
1528 
1529  if(MDFNnetplay)
1530   MDFNI_NetplayChangeNick(arg);
1531 
1532  return(true);
1533 }
1534 
MDFNI_NetplayLine(const char * text,bool & inputable,bool & viewable)1535 void MDFNI_NetplayLine(const char *text, bool &inputable, bool &viewable)
1536 {
1537 	 inputable = viewable = false;
1538 
1539          for(unsigned int x = 0; ConsoleCommands[x].name; x++)
1540 	 {
1541           if(!MDFN_strazicmp(ConsoleCommands[x].name, (char*)text, strlen(ConsoleCommands[x].name)) && text[strlen(ConsoleCommands[x].name)] <= 0x20)
1542           {
1543 	   std::string trim_text(&text[strlen(ConsoleCommands[x].name)]);
1544 
1545 	   MDFN_trim(&trim_text);
1546 
1547            inputable = viewable = ConsoleCommands[x].func(trim_text.c_str());
1548 
1549            return;
1550           }
1551 	 }
1552 
1553          if(text[0] != 0)	// Is non-empty line?
1554 	 {
1555 	  MDFNI_NetplayText(text);
1556 	  viewable = true;
1557          }
1558 }
1559 
1560 }
1561