mirror of https://github.com/snes9xgit/snes9x.git
1285 lines
30 KiB
Plaintext
1285 lines
30 KiB
Plaintext
/*****************************************************************************\
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
This file is licensed under the Snes9x License.
|
|
For further information, consult the LICENSE file in the root directory.
|
|
\*****************************************************************************/
|
|
|
|
/***********************************************************************************
|
|
SNES9X for Mac OS (c) Copyright John Stiles
|
|
|
|
Snes9x for Mac OS X
|
|
|
|
(c) Copyright 2001 - 2011 zones
|
|
(c) Copyright 2002 - 2005 107
|
|
(c) Copyright 2002 PB1400c
|
|
(c) Copyright 2004 Alexander and Sander
|
|
(c) Copyright 2004 - 2005 Steven Seeger
|
|
(c) Copyright 2005 Ryan Vogt
|
|
***********************************************************************************/
|
|
|
|
|
|
#include "snes9x.h"
|
|
#include "memmap.h"
|
|
#include "apu.h"
|
|
#include "snapshot.h"
|
|
#include "cheats.h"
|
|
#include "display.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
|
|
#include "mac-prefix.h"
|
|
#include "mac-cart.h"
|
|
#include "mac-cheatfinder.h"
|
|
#include "mac-controls.h"
|
|
#include "mac-dialog.h"
|
|
#include "mac-file.h"
|
|
#include "mac-joypad.h"
|
|
#include "mac-keyboard.h"
|
|
#include "mac-os.h"
|
|
#include "mac-snes9x.h"
|
|
#include "mac-stringtools.h"
|
|
#include "mac-netplay.h"
|
|
#include "mac-client.h"
|
|
|
|
#ifdef SELF_TEST
|
|
#include <sys/un.h>
|
|
#endif
|
|
|
|
#define KeyIsPressed(km, k) (1 & (((unsigned char *) km) [(k) >> 3] >> ((k) & 7)))
|
|
|
|
enum
|
|
{
|
|
kNPCDialogNone,
|
|
kNPCDialogInit,
|
|
kNPCDialogConnect,
|
|
kNPCDialogConnectFailed,
|
|
kNPCDialogOpenBegin,
|
|
kNPCDialogOpenEnd,
|
|
kNPCDialogPrepare,
|
|
kNPCDialogPrepareFailed,
|
|
kNPCDialogShowList,
|
|
kNPCDialogDone,
|
|
kNPCDialogCancel
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
volatile bool8 padloop;
|
|
volatile bool8 exitsgn;
|
|
volatile uint32 phasecount;
|
|
volatile uint32 phasespan;
|
|
volatile uint8 header;
|
|
bool8 online;
|
|
int socket;
|
|
int numplayers;
|
|
char name[256];
|
|
char serverIP[256];
|
|
|
|
int savedDeviceSetting;
|
|
int savedAutoSaveDelay;
|
|
|
|
bool8 configsaved;
|
|
bool8 dialogcancel;
|
|
bool8 dialogsheet;
|
|
int dialogprocess;
|
|
} clientState;
|
|
|
|
typedef struct
|
|
{
|
|
bool8 ready;
|
|
int player;
|
|
char name[256];
|
|
} clientsInfo;
|
|
|
|
typedef struct
|
|
{
|
|
uint32 crc32;
|
|
int input;
|
|
int length;
|
|
char fname[PATH_MAX + 1];
|
|
} cROMInfo;
|
|
|
|
static char n_csememu[] = "/tmp/s9x_c_emu_semaphore",
|
|
n_csempad[] = "/tmp/s9x_c_pad_semaphore";
|
|
|
|
static clientState npclient;
|
|
static clientsInfo npcinfo[NP_MAX_PLAYERS];
|
|
|
|
static cROMInfo nprominfo;
|
|
|
|
static uint32 npcactvpad[NP_MAX_PLAYERS][64], // [player number]
|
|
npcrecvpad[NP_MAX_PLAYERS][64], // [player number]
|
|
npcsendpad[64],
|
|
npccachpad[64];
|
|
|
|
static WindowRef mRef, sRef;
|
|
static sem_t *csememu, *csempad;
|
|
static pthread_t connectthread, preparethread, gamepadthread;
|
|
|
|
static int NPClientGetMesFromServer (void);
|
|
static void NPClientDetachConnectThread (void);
|
|
static void NPClientDetachPrepareThread (void);
|
|
static void NPClientBeginPlayerListSheet (void);
|
|
static void NPClientEndPlayerListSheet (void);
|
|
static bool8 NPClientConnectToServer (int);
|
|
static bool8 NPClientSendMesToServer (int);
|
|
static bool8 NPClientSendNameToServer (void);
|
|
static bool8 NPClientGetROMInfoFromServer (void);
|
|
static bool8 NPClientBeginOpenROMImage (WindowRef);
|
|
static bool8 NPClientEndOpenROMImage (void);
|
|
static bool8 NPClientROMReadyToServer (void);
|
|
static bool8 NPClientGetSRAMFromServer (void);
|
|
static bool8 NPClientGetPlayerListFromServer (void);
|
|
static bool8 NPClientReplyPhaseSpanTest (void);
|
|
static void * NPClientConnectThread (void *);
|
|
static void * NPClientPrepareThread (void *);
|
|
static void * NPClientNetPlayThread (void *);
|
|
static void NPClientDialogTimerHandler (EventLoopTimerRef, void *);
|
|
static OSStatus NPClientDialogEventHandler (EventHandlerCallRef, EventRef, void *);
|
|
static OSStatus NPClientSheetEventHandler (EventHandlerCallRef, EventRef, void *);
|
|
|
|
|
|
bool8 NPClientDialog (void)
|
|
{
|
|
OSStatus err;
|
|
IBNibRef nibRef;
|
|
|
|
npclient.dialogcancel = true;
|
|
npclient.dialogsheet = false;
|
|
npclient.configsaved = false;
|
|
|
|
err = CreateNibReference(kMacS9XCFString, &nibRef);
|
|
if (err == noErr)
|
|
{
|
|
err = CreateWindowFromNib(nibRef, CFSTR("Connect"), &mRef);
|
|
if (err == noErr)
|
|
{
|
|
err = CreateWindowFromNib(nibRef, CFSTR("PlayerList"), &sRef);
|
|
if (err == noErr)
|
|
{
|
|
EventHandlerRef eref, seref;
|
|
EventLoopTimerRef tref;
|
|
EventHandlerUPP eventUPP, sheetUPP;
|
|
EventLoopTimerUPP timerUPP;
|
|
EventTypeSpec windowEvents[] = { { kEventClassCommand, kEventCommandProcess },
|
|
{ kEventClassCommand, kEventCommandUpdateStatus } };
|
|
CFStringRef ref;
|
|
HIViewRef ctl, root;
|
|
HIViewID cid;
|
|
|
|
npclient.dialogprocess = kNPCDialogInit;
|
|
|
|
eventUPP = NewEventHandlerUPP(NPClientDialogEventHandler);
|
|
err = InstallWindowEventHandler(mRef, eventUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) mRef, &eref);
|
|
|
|
timerUPP = NewEventLoopTimerUPP(NPClientDialogTimerHandler);
|
|
err = InstallEventLoopTimer(GetCurrentEventLoop(), 0.0f, 0.1f, timerUPP, (void *) mRef, &tref);
|
|
|
|
sheetUPP = NewEventHandlerUPP(NPClientSheetEventHandler);
|
|
err = InstallWindowEventHandler(sRef, sheetUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) sRef, &seref);
|
|
|
|
root = HIViewGetRoot(mRef);
|
|
cid.id = 0;
|
|
|
|
cid.signature = 'CHAS';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
HIViewSetVisible(ctl, false);
|
|
|
|
cid.signature = 'SVIP';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
SetEditTextCStr(ctl, npServerIP, false);
|
|
|
|
cid.signature = 'CLNM';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
ref = CFStringCreateWithCString(kCFAllocatorDefault, npName, kCFStringEncodingUTF8);
|
|
if (ref)
|
|
{
|
|
SetEditTextCFString(ctl, ref, false);
|
|
CFRelease(ref);
|
|
}
|
|
else
|
|
SetEditTextCFString(ctl, CFSTR("unknown"), false);
|
|
|
|
MoveWindowPosition(mRef, kWindowClient, false);
|
|
ShowWindow(mRef);
|
|
err = HIViewAdvanceFocus(root, 0);
|
|
err = RunAppModalLoopForWindow(mRef);
|
|
HideWindow(mRef);
|
|
SaveWindowPosition(mRef, kWindowClient);
|
|
|
|
err = RemoveEventHandler(seref);
|
|
DisposeEventHandlerUPP(sheetUPP);
|
|
|
|
err = RemoveEventLoopTimer(tref);
|
|
DisposeEventLoopTimerUPP(timerUPP);
|
|
|
|
err = RemoveEventHandler(eref);
|
|
DisposeEventHandlerUPP(eventUPP);
|
|
|
|
CFRelease(sRef);
|
|
}
|
|
|
|
CFRelease(mRef);
|
|
}
|
|
|
|
DisposeNibReference(nibRef);
|
|
}
|
|
|
|
return (!npclient.dialogcancel);
|
|
}
|
|
|
|
static OSStatus NPClientDialogEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
|
|
{
|
|
OSStatus err, result = eventNotHandledErr;
|
|
|
|
switch (GetEventClass(inEvent))
|
|
{
|
|
case kEventClassCommand:
|
|
switch (GetEventKind(inEvent))
|
|
{
|
|
HICommand tHICommand;
|
|
|
|
case kEventCommandUpdateStatus:
|
|
err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
|
|
if (err == noErr && tHICommand.commandID == 'clos')
|
|
{
|
|
UpdateMenuCommandStatus(false);
|
|
result = noErr;
|
|
}
|
|
|
|
break;
|
|
|
|
case kEventCommandProcess:
|
|
err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
|
|
if (err == noErr)
|
|
{
|
|
switch (tHICommand.commandID)
|
|
{
|
|
case 'OK__':
|
|
CFStringRef ref;
|
|
HIViewRef ctl, root;
|
|
HIViewID cid;
|
|
|
|
root = HIViewGetRoot(mRef);
|
|
cid.id = 0;
|
|
|
|
cid.signature = 'SVIP';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
GetEditTextCStr(ctl, npclient.serverIP);
|
|
DeactivateControl(ctl);
|
|
if (npclient.serverIP[0] == 0)
|
|
strcpy(npclient.serverIP, "127.0.0.1");
|
|
strcpy(npServerIP, npclient.serverIP);
|
|
printf("%s\n", npServerIP);
|
|
|
|
cid.signature = 'CLNM';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
CopyEditTextCFString(ctl, &ref);
|
|
DeactivateControl(ctl);
|
|
if (ref)
|
|
{
|
|
Boolean r;
|
|
|
|
r = CFStringGetCString(ref, npclient.name, 256, kCFStringEncodingUTF8);
|
|
if (!r)
|
|
strcpy(npclient.name, "unknown");
|
|
else
|
|
if (npclient.name[0] == 0)
|
|
strcpy(npclient.name, "Guest");
|
|
|
|
CFRelease(ref);
|
|
}
|
|
else
|
|
strcpy(npclient.name, "unknown");
|
|
strcpy(npName, npclient.name);
|
|
printf("%s\n", npName);
|
|
|
|
cid.signature = 'OK__';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
DeactivateControl(ctl);
|
|
|
|
cid.signature = 'NOT_';
|
|
HIViewFindByID(root, cid, &ctl);
|
|
DeactivateControl(ctl);
|
|
|
|
npclient.dialogcancel = false;
|
|
npclient.dialogprocess = kNPCDialogConnect;
|
|
|
|
result = noErr;
|
|
break;
|
|
|
|
case 'NOT_':
|
|
npclient.dialogcancel = true;
|
|
npclient.dialogprocess = kNPCDialogCancel;
|
|
|
|
result = noErr;
|
|
break;
|
|
|
|
case 'NvDn':
|
|
npclient.dialogcancel = false;
|
|
npclient.dialogprocess = kNPCDialogOpenEnd;
|
|
|
|
result = noErr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
static void NPClientDialogTimerHandler (EventLoopTimerRef inTimer, void *userData)
|
|
{
|
|
WindowRef window = (WindowRef) userData;
|
|
HIViewRef ctl;
|
|
HIViewID cid = { 'CHAS', 0 };
|
|
|
|
HIViewFindByID(HIViewGetRoot(mRef), cid, &ctl);
|
|
|
|
switch (npclient.dialogprocess)
|
|
{
|
|
case kNPCDialogNone:
|
|
break;
|
|
|
|
case kNPCDialogCancel:
|
|
NPNotification(" kNPCDialogCancel", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
npclient.dialogcancel = true;
|
|
QuitAppModalLoopForWindow(mRef);
|
|
break;
|
|
|
|
case kNPCDialogInit:
|
|
NPNotification(" kNPCDialogInit", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
break;
|
|
|
|
case kNPCDialogConnect:
|
|
NPNotification(" kNPCDialogConnect", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
HIViewSetVisible(ctl, true);
|
|
NPClientDetachConnectThread();
|
|
break;
|
|
|
|
case kNPCDialogConnectFailed:
|
|
NPNotification(" kNPCDialogConnectFailed", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
npclient.dialogcancel = true;
|
|
QuitAppModalLoopForWindow(mRef);
|
|
break;
|
|
|
|
case kNPCDialogOpenBegin:
|
|
NPNotification(" kNPCDialogOpenBegin", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
HIViewSetVisible(ctl, false);
|
|
NPClientStoreConfig();
|
|
if (!NPClientBeginOpenROMImage(window))
|
|
{
|
|
NPClientDisconnect();
|
|
NPClientRestoreConfig();
|
|
npclient.dialogprocess = kNPCDialogCancel;
|
|
}
|
|
|
|
break;
|
|
|
|
case kNPCDialogOpenEnd:
|
|
NPNotification(" kNPCDialogOpenEnd", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
if (!NPClientEndOpenROMImage())
|
|
{
|
|
NPClientDisconnect();
|
|
NPClientRestoreConfig();
|
|
npclient.dialogprocess = kNPCDialogCancel;
|
|
}
|
|
else
|
|
npclient.dialogprocess = kNPCDialogPrepare;
|
|
|
|
break;
|
|
|
|
case kNPCDialogPrepare:
|
|
NPNotification(" kNPCDialogPrepare", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
HIViewSetVisible(ctl, true);
|
|
NPClientDetachPrepareThread();
|
|
break;
|
|
|
|
case kNPCDialogPrepareFailed:
|
|
NPNotification(" kNPCDialogPrepareFailed", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
NPClientRestoreConfig();
|
|
npclient.dialogcancel = true;
|
|
QuitAppModalLoopForWindow(mRef);
|
|
break;
|
|
|
|
case kNPCDialogShowList:
|
|
NPNotification(" kNPCDialogShowList", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
HIViewSetVisible(ctl, false);
|
|
npclient.dialogsheet = true;
|
|
NPClientBeginPlayerListSheet();
|
|
break;
|
|
|
|
case kNPCDialogDone:
|
|
NPNotification(" kNPCDialogDone", -1);
|
|
npclient.dialogprocess = kNPCDialogNone;
|
|
NPClientEndPlayerListSheet();
|
|
npclient.dialogsheet = false;
|
|
npclient.dialogcancel = false;
|
|
QuitAppModalLoopForWindow(mRef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void NPClientDetachConnectThread (void)
|
|
{
|
|
pthread_create(&connectthread, NULL, NPClientConnectThread, NULL);
|
|
pthread_detach(connectthread);
|
|
}
|
|
|
|
static void * NPClientConnectThread (void *)
|
|
{
|
|
NPNotification("Client: Entered connection thread.", -1);
|
|
|
|
if ((NPClientConnectToServer(NP_PORT) == false) ||
|
|
(NPClientSendNameToServer() == false) ||
|
|
(NPClientGetROMInfoFromServer() == false))
|
|
{
|
|
NPClientDisconnect();
|
|
npclient.dialogprocess = kNPCDialogConnectFailed;
|
|
return (NULL);
|
|
}
|
|
|
|
npclient.dialogprocess = kNPCDialogOpenBegin;
|
|
NPNotification("Client: Exited connection thread.", -1);
|
|
return (NULL);
|
|
}
|
|
|
|
void NPClientInit (void)
|
|
{
|
|
npclient.padloop = false;
|
|
npclient.exitsgn = false;
|
|
npclient.phasecount = 0;
|
|
npclient.phasespan = 0;
|
|
npclient.header = 0;
|
|
npclient.online = false;
|
|
npclient.socket = -1;
|
|
npclient.numplayers = 0;
|
|
npclient.name[0] = 0;
|
|
npclient.serverIP[0] = 0;
|
|
|
|
nprominfo.crc32 = 0;
|
|
nprominfo.input = 0;
|
|
nprominfo.length = 0;
|
|
nprominfo.fname[0] = 0;
|
|
|
|
for (int i = 0; i < NP_MAX_PLAYERS; i++)
|
|
{
|
|
for (int j = 0; j < 64; j++)
|
|
{
|
|
npcactvpad[i][j] = 0;
|
|
npcrecvpad[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < 64; j++)
|
|
{
|
|
npcsendpad[j] = 0;
|
|
npccachpad[j] = 0;
|
|
}
|
|
|
|
for (int c = 0; c < NP_MAX_PLAYERS; c++)
|
|
{
|
|
npcinfo[c].ready = false;
|
|
npcinfo[c].player = 0;
|
|
npcinfo[c].name[0] = 0;
|
|
}
|
|
}
|
|
|
|
static bool8 NPClientConnectToServer (int port)
|
|
{
|
|
#ifndef SELF_TEST
|
|
struct sockaddr_in address;
|
|
#else
|
|
struct sockaddr_un address;
|
|
#endif
|
|
|
|
NPNotification("Client: Connecting to server...", -1);
|
|
|
|
memset(&address, 0, sizeof(address));
|
|
#ifndef SELF_TEST
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = inet_addr(npclient.serverIP);
|
|
address.sin_port = htons(port);
|
|
#else
|
|
address.sun_family = AF_UNIX;
|
|
strcpy(address.sun_path, SOCK_NAME);
|
|
#endif
|
|
|
|
#ifndef SELF_TEST
|
|
if (address.sin_addr.s_addr == INADDR_NONE)
|
|
{
|
|
NPError("Client: Server IP is invalid.", 5001);
|
|
return (false);
|
|
}
|
|
#endif
|
|
|
|
#ifndef SELF_TEST
|
|
if ((npclient.socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
|
#else
|
|
if ((npclient.socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
#endif
|
|
{
|
|
NPError("Client: Failed to create socket.", 5002);
|
|
return (false);
|
|
}
|
|
|
|
if (connect(npclient.socket, (struct sockaddr *) &address, sizeof(address)) < 0)
|
|
{
|
|
NPError("Client: Failed to connect to server.", 5003);
|
|
return (false);
|
|
}
|
|
|
|
npclient.online = true;
|
|
|
|
NPNotification("Client: Connected to server.", -1);
|
|
return (true);
|
|
}
|
|
|
|
void NPClientDisconnect (void)
|
|
{
|
|
if (npclient.socket != -1)
|
|
{
|
|
NPNotification("Client: Disconnecting from server...", -1);
|
|
|
|
close(npclient.socket);
|
|
npclient.socket = -1;
|
|
|
|
NPNotification("Client: Disconnected from server.", -1);
|
|
}
|
|
|
|
npclient.online = false;
|
|
npclient.name[0] = 0;
|
|
npclient.serverIP[0] = 0;
|
|
}
|
|
|
|
static bool8 NPClientSendMesToServer (int num)
|
|
{
|
|
uint8 mes[2];
|
|
|
|
mes[0] = NP_CLIENT_MAGIC;
|
|
mes[1] = num;
|
|
|
|
if (socket_write(npclient.socket, mes, 2) != 2)
|
|
return (false);
|
|
|
|
return (true);
|
|
}
|
|
|
|
static int NPClientGetMesFromServer (void)
|
|
{
|
|
uint8 mes[2];
|
|
|
|
if (socket_read(npclient.socket, mes, 2) != 2)
|
|
return (-1);
|
|
|
|
if (mes[0] != NP_SERVER_MAGIC)
|
|
return (-1);
|
|
|
|
return ((int) mes[1]);
|
|
}
|
|
|
|
static bool8 NPClientSendNameToServer (void)
|
|
{
|
|
if (!npclient.online)
|
|
return (false);
|
|
|
|
NPNotification("Client: Sending player name to server...", -1);
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerNameRequest)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5101);
|
|
return (false);
|
|
}
|
|
|
|
uint8 mes[4];
|
|
uint32 l;
|
|
|
|
l = strlen(npclient.name);
|
|
WRITE_LONG(mes + 0, l);
|
|
|
|
if (socket_write(npclient.socket, mes, 4) != 4)
|
|
{
|
|
NPError("Client: Failed to send name size to server.", 5102);
|
|
return (false);
|
|
}
|
|
|
|
if (socket_write(npclient.socket, (uint8 *) npclient.name, l) != (int) l)
|
|
{
|
|
NPError("Client: Failed to send name to server.", 5103);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerNameReceived)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5104);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientSendMesToServer(kNPClientNameSent) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5105);
|
|
return (false);
|
|
}
|
|
|
|
NPNotification("Client: Sent player name to server.", -1);
|
|
return (true);
|
|
}
|
|
|
|
static bool8 NPClientGetROMInfoFromServer (void)
|
|
{
|
|
if (!npclient.online)
|
|
return (false);
|
|
|
|
NPNotification("Client: Receiving ROM information from server...", -1);
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerROMInfoWillSend)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5201);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientSendMesToServer(kNPClientROMInfoWaiting) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5202);
|
|
return (false);
|
|
}
|
|
|
|
uint8 mes[16];
|
|
uint32 l;
|
|
|
|
if (socket_read(npclient.socket, mes, 16) != 16)
|
|
{
|
|
NPError("Client: Failed to receive ROM information from server.", 5203);
|
|
return (false);
|
|
}
|
|
|
|
nprominfo.crc32 = READ_LONG(mes + 0);
|
|
nprominfo.input = READ_LONG(mes + 4);
|
|
|
|
l = READ_LONG(mes + 12);
|
|
|
|
if (socket_read(npclient.socket, (uint8 *) nprominfo.fname, l) != (int) l)
|
|
{
|
|
NPError("Client: Failed to receive ROM name from server.", 5204);
|
|
return (false);
|
|
}
|
|
|
|
nprominfo.fname[l] = 0;
|
|
nprominfo.length = l;
|
|
|
|
NPNotification("Client: Received ROM information from server.", -1);
|
|
return (true);
|
|
}
|
|
|
|
void NPClientStoreConfig (void)
|
|
{
|
|
npclient.savedDeviceSetting = deviceSetting;
|
|
npclient.savedAutoSaveDelay = Settings.AutoSaveDelay;
|
|
|
|
npclient.configsaved = true;
|
|
|
|
deviceSetting = nprominfo.input;
|
|
Settings.AutoSaveDelay = 0;
|
|
|
|
ChangeInputDevice();
|
|
}
|
|
|
|
void NPClientRestoreConfig (void)
|
|
{
|
|
if (npclient.configsaved)
|
|
{
|
|
deviceSetting = npclient.savedDeviceSetting;
|
|
Settings.AutoSaveDelay = npclient.savedAutoSaveDelay;
|
|
|
|
npclient.configsaved = false;
|
|
|
|
ChangeInputDevice();
|
|
}
|
|
}
|
|
|
|
static bool8 NPClientBeginOpenROMImage (WindowRef window)
|
|
{
|
|
CFStringRef numRef, romRef, baseRef;
|
|
CFMutableStringRef mesRef;
|
|
SInt32 replaceAt;
|
|
bool8 r;
|
|
|
|
DeinitGameWindow();
|
|
|
|
if (cartOpen)
|
|
{
|
|
SNES9X_SaveSRAM();
|
|
S9xResetSaveTimer(false);
|
|
S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
|
|
}
|
|
|
|
cartOpen = false;
|
|
|
|
ResetCheatFinder();
|
|
|
|
romRef = CFStringCreateWithCString(kCFAllocatorDefault, nprominfo.fname, kCFStringEncodingUTF8);
|
|
numRef = CFCopyLocalizedString(CFSTR("NPROMNamePos"), "1");
|
|
baseRef = CFCopyLocalizedString(CFSTR("NPROMNameMes"), "NPROM");
|
|
mesRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, baseRef);
|
|
replaceAt = CFStringGetIntValue(numRef);
|
|
CFStringReplace(mesRef, CFRangeMake(replaceAt - 1, 1), romRef);
|
|
|
|
// r = NavBeginOpenROMImageSheet(window, mesRef);
|
|
|
|
CFRelease(mesRef);
|
|
CFRelease(baseRef);
|
|
CFRelease(numRef);
|
|
CFRelease(romRef);
|
|
|
|
return (r);
|
|
}
|
|
|
|
static bool8 NPClientEndOpenROMImage (void)
|
|
{
|
|
OSStatus err;
|
|
FSRef cartRef;
|
|
char filename[PATH_MAX + 1];
|
|
bool8 r;
|
|
|
|
r = NavEndOpenROMImageSheet(&cartRef);
|
|
if (!r)
|
|
{
|
|
cartOpen = false;
|
|
return (false);
|
|
}
|
|
|
|
CheckSaveFolder(&cartRef);
|
|
|
|
Settings.ForceLoROM = (romDetect == kLoROMForce );
|
|
Settings.ForceHiROM = (romDetect == kHiROMForce );
|
|
Settings.ForceHeader = (headerDetect == kHeaderForce );
|
|
Settings.ForceNoHeader = (headerDetect == kNoHeaderForce );
|
|
Settings.ForceInterleaved = (interleaveDetect == kInterleaveForce );
|
|
Settings.ForceInterleaved2 = (interleaveDetect == kInterleave2Force );
|
|
Settings.ForceInterleaveGD24 = (interleaveDetect == kInterleaveGD24 );
|
|
Settings.ForceNotInterleaved = (interleaveDetect == kNoInterleaveForce);
|
|
Settings.ForcePAL = (videoDetect == kPALForce );
|
|
Settings.ForceNTSC = (videoDetect == kNTSCForce );
|
|
|
|
GFX.InfoString = NULL;
|
|
GFX.InfoStringTimeout = 0;
|
|
|
|
S9xResetSaveTimer(true);
|
|
|
|
err = FSRefMakePath(&cartRef, (unsigned char *) filename, PATH_MAX);
|
|
|
|
SNES9X_InitSound();
|
|
|
|
if (Memory.LoadROM(filename) /*&& (Memory.ROMCRC32 == nprominfo.crc32)*/)
|
|
{
|
|
ChangeTypeAndCreator(filename, 'CART', '~9X~');
|
|
cartOpen = true;
|
|
return (true);
|
|
}
|
|
else
|
|
{
|
|
cartOpen = false;
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
static void NPClientDetachPrepareThread (void)
|
|
{
|
|
pthread_create(&preparethread, NULL, NPClientPrepareThread, NULL);
|
|
pthread_detach(preparethread);
|
|
}
|
|
|
|
static void * NPClientPrepareThread (void *)
|
|
{
|
|
NPNotification("Client: Entered preparing thread.", -1);
|
|
|
|
if ((NPClientROMReadyToServer() == false) ||
|
|
(NPClientGetSRAMFromServer() == false) ||
|
|
(NPClientGetPlayerListFromServer() == false) ||
|
|
(NPClientReplyPhaseSpanTest() == false))
|
|
{
|
|
NPClientDisconnect();
|
|
npclient.dialogprocess = kNPCDialogPrepareFailed;
|
|
return (NULL);
|
|
}
|
|
|
|
npclient.dialogprocess = kNPCDialogShowList;
|
|
NPNotification("Client: Exited preparing thread.", -1);
|
|
return (NULL);
|
|
}
|
|
|
|
static bool8 NPClientROMReadyToServer (void)
|
|
{
|
|
if (!npclient.online)
|
|
return (false);
|
|
|
|
NPNotification("Client: Sending ROM ready sign to server...", -1);
|
|
|
|
if (NPClientSendMesToServer(kNPClientROMOpened) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5401);
|
|
return (false);
|
|
}
|
|
|
|
NPNotification("Client: Sent ROM ready sign to server.", -1);
|
|
return (true);
|
|
}
|
|
|
|
static bool8 NPClientGetSRAMFromServer (void)
|
|
{
|
|
if (!npclient.online)
|
|
return (false);
|
|
|
|
NPNotification("Client: Receiving SRAM from server...", -1);
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerSRAMWillSend)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5501);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientSendMesToServer(kNPClientSRAMWaiting) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5502);
|
|
return (false);
|
|
}
|
|
|
|
uint8 mes[4];
|
|
uint32 sramsize;
|
|
|
|
if (socket_read(npclient.socket, mes, 4) != 4)
|
|
{
|
|
NPError("Client: Failed to receive SRAM size from server.", 5503);
|
|
return (false);
|
|
}
|
|
|
|
sramsize = READ_LONG(mes + 0);
|
|
|
|
if (sramsize != (uint32) (Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0))
|
|
{
|
|
NPError("Client: SRAM size mismatch.", 5504);
|
|
return (false);
|
|
}
|
|
|
|
if (sramsize && (socket_read(npclient.socket, Memory.SRAM, sramsize) != (int) sramsize))
|
|
{
|
|
NPError("Server: Failed to receive SRAM from server.", 5505);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientSendMesToServer(kNPClientSRAMLoaded) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5506);
|
|
return (false);
|
|
}
|
|
|
|
NPNotification("Client: Received SRAM from server.", -1);
|
|
return (true);
|
|
}
|
|
|
|
static bool8 NPClientGetPlayerListFromServer (void)
|
|
{
|
|
if (!npclient.online)
|
|
return (false);
|
|
|
|
NPNotification("Client: Receiving player list from server...", -1);
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerPlayerWillSend)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5701);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientSendMesToServer(kNPClientPlayerWaiting) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5702);
|
|
return (false);
|
|
}
|
|
|
|
for (int i = 0; i < NP_MAX_PLAYERS; i++)
|
|
{
|
|
uint8 mes[10];
|
|
uint32 l;
|
|
|
|
if (socket_read(npclient.socket, mes, 10) != 10)
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5703);
|
|
return (false);
|
|
}
|
|
|
|
npcinfo[i].ready = READ_BYTE(mes + 1);
|
|
npcinfo[i].player = READ_LONG(mes + 2);
|
|
|
|
l = READ_LONG(mes + 6);
|
|
|
|
if (l && (socket_read(npclient.socket, (uint8 *) npcinfo[i].name, l) != (int) l))
|
|
{
|
|
NPError("Client: Failed to receive messsage from server.", 5704);
|
|
return (false);
|
|
}
|
|
|
|
npcinfo[i].name[l] = 0;
|
|
}
|
|
|
|
npclient.numplayers = 0;
|
|
for (int i = 0; i < NP_MAX_PLAYERS; i++)
|
|
if (npcinfo[i].ready)
|
|
npclient.numplayers++;
|
|
|
|
NPNotification("Client: Received player list from server.", -1);
|
|
NPNotification("Client: Number of players: %d", npclient.numplayers);
|
|
|
|
return (true);
|
|
}
|
|
|
|
static bool8 NPClientReplyPhaseSpanTest (void)
|
|
{
|
|
uint8 mes[21];
|
|
int l = npclient.numplayers * 4 + 1;
|
|
|
|
NPNotification("Client: Replying sending / receiving pad states test...", -1);
|
|
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
if (socket_read(npclient.socket, mes, l) != l)
|
|
return (false);
|
|
|
|
if (socket_write(npclient.socket, mes, 5) != 5)
|
|
return (false);
|
|
}
|
|
|
|
NPNotification("Client: Replied sending / receiving pad states test.", -1);
|
|
|
|
NPNotification("Client: Receiving phase span value from server...", -1);
|
|
|
|
if (socket_read(npclient.socket, mes, 4) != 4)
|
|
return (false);
|
|
|
|
npclient.phasespan = READ_LONG(mes + 0);
|
|
|
|
NPNotification(" phase span: %d (frames)", npclient.phasespan);
|
|
|
|
NPNotification("Client: Received phase span value from server.", -1);
|
|
|
|
return (true);
|
|
}
|
|
|
|
static void NPClientBeginPlayerListSheet (void)
|
|
{
|
|
OSStatus err;
|
|
CFStringRef ref;
|
|
HIViewRef ctl, root;
|
|
HIViewID cid;
|
|
|
|
root = HIViewGetRoot(sRef);
|
|
cid.signature = 'PLNM';
|
|
|
|
for (int i = 0; i < NP_MAX_PLAYERS; i++)
|
|
{
|
|
if (npcinfo[i].ready)
|
|
{
|
|
cid.id = npcinfo[i].player;
|
|
HIViewFindByID(root, cid, &ctl);
|
|
ref = CFStringCreateWithCString(kCFAllocatorDefault, npcinfo[i].name, kCFStringEncodingUTF8);
|
|
if (ref)
|
|
{
|
|
SetStaticTextCFString(ctl, ref, false);
|
|
CFRelease(ref);
|
|
}
|
|
else
|
|
SetStaticTextCFString(ctl, CFSTR("unknown"), false);
|
|
}
|
|
}
|
|
|
|
err = ShowSheetWindow(sRef, mRef);
|
|
}
|
|
|
|
static void NPClientEndPlayerListSheet (void)
|
|
{
|
|
OSStatus err;
|
|
|
|
err = HideSheetWindow(sRef);
|
|
}
|
|
|
|
static OSStatus NPClientSheetEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
|
|
{
|
|
if (!npclient.dialogsheet)
|
|
return (eventNotHandledErr);
|
|
|
|
OSStatus err, result = eventNotHandledErr;
|
|
|
|
switch (GetEventClass(inEvent))
|
|
{
|
|
case kEventClassCommand:
|
|
switch (GetEventKind(inEvent))
|
|
{
|
|
HICommand tHICommand;
|
|
|
|
case kEventCommandUpdateStatus:
|
|
err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
|
|
if (err == noErr && tHICommand.commandID == 'clos')
|
|
{
|
|
UpdateMenuCommandStatus(false);
|
|
result = noErr;
|
|
}
|
|
|
|
break;
|
|
|
|
case kEventCommandProcess:
|
|
err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand);
|
|
if (err == noErr)
|
|
{
|
|
switch (tHICommand.commandID)
|
|
{
|
|
case 'ok ':
|
|
npclient.dialogprocess = kNPCDialogDone;
|
|
result = noErr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
void NPClientDetachNetPlayThread (void)
|
|
{
|
|
NPNotification("Client: Detaching pad thread...", -1);
|
|
|
|
npclient.padloop = true;
|
|
npclient.exitsgn = false;
|
|
|
|
csememu = sem_open(n_csememu, O_CREAT, 0600, 0);
|
|
csempad = sem_open(n_csempad, O_CREAT, 0600, 0);
|
|
|
|
pthread_create(&gamepadthread, NULL, NPClientNetPlayThread, NULL);
|
|
pthread_detach(gamepadthread);
|
|
|
|
NPNotification("Client: Detached pad thread.", -1);
|
|
}
|
|
|
|
void NPClientStopNetPlayThread (void)
|
|
{
|
|
NPNotification("Client: Stopping pad thread...", -1);
|
|
|
|
npclient.padloop = false;
|
|
sem_post(csempad);
|
|
sem_post(csememu);
|
|
|
|
while (!npclient.exitsgn)
|
|
sleep(0);
|
|
|
|
sem_unlink(n_csememu);
|
|
sem_unlink(n_csempad);
|
|
sem_close(csememu);
|
|
sem_close(csempad);
|
|
|
|
NPNotification("Client: Stopped pad thread.", -1);
|
|
}
|
|
|
|
bool8 NPClientNetPlayWaitStart (void)
|
|
{
|
|
NPNotification("Client: Waiting start flag...", -1);
|
|
|
|
if (NPClientSendMesToServer(kNPClientStartWait) == false)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5801);
|
|
return (false);
|
|
}
|
|
|
|
if (NPClientGetMesFromServer() != kNPServerStart)
|
|
{
|
|
NPError("Client: Failed to send messsage to server.", 5802);
|
|
return (false);
|
|
}
|
|
|
|
npclient.phasecount = 0;
|
|
npclient.header = 0;
|
|
|
|
sem_post(csempad);
|
|
|
|
NPNotification("Client: Netplay started.", -1);
|
|
return (true);
|
|
}
|
|
|
|
static void * NPClientNetPlayThread (void *)
|
|
{
|
|
uint8 mes[NP_MAX_PLAYERS * 64 * 4 + 1];
|
|
uint8 count = 0;
|
|
int l;
|
|
|
|
NPNotification("Client: Entered pad thread.", -1);
|
|
|
|
while (npclient.padloop)
|
|
{
|
|
sem_wait(csempad);
|
|
|
|
l = npclient.numplayers * npclient.phasespan * 4 + 1;
|
|
if (socket_read(npclient.socket, mes, l) != l)
|
|
{
|
|
npclient.exitsgn = true;
|
|
sem_post(csememu);
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
if ((mes[0] & 0xF) != count)
|
|
NPNotification("Client: Warning: Failed to synchronize server.", -1);
|
|
|
|
npclient.header = mes[0] & 0xF0;
|
|
|
|
for (int i = 0; i < npclient.numplayers; i++)
|
|
for (uint32 j = 0; j < npclient.phasespan; j++)
|
|
npcrecvpad[i][j] = READ_LONG(mes + (i * npclient.phasespan + j) * 4 + 1);
|
|
|
|
WRITE_BYTE(mes + 0, count);
|
|
|
|
for (uint32 j = 0; j < npclient.phasespan; j++)
|
|
WRITE_LONG(mes + j * 4 + 1, npcsendpad[j]);
|
|
|
|
l = npclient.phasespan * 4 + 1;
|
|
if (socket_write(npclient.socket, mes, l) != l)
|
|
{
|
|
npclient.exitsgn = true;
|
|
sem_post(csememu);
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
count = (count + 1) & 0xF;
|
|
|
|
sem_post(csememu);
|
|
}
|
|
|
|
npclient.exitsgn = true;
|
|
|
|
NPNotification("Client: Exited pad thread.", -1);
|
|
return (NULL);
|
|
}
|
|
|
|
void NPClientProcessInput (void)
|
|
{
|
|
static uint32 pos = 0;
|
|
KeyMap myKeys;
|
|
|
|
if (npclient.exitsgn)
|
|
{
|
|
if (s9xthreadrunning)
|
|
{
|
|
if (!eventQueued)
|
|
{
|
|
PostQueueToSubEventLoop();
|
|
eventQueued = true;
|
|
}
|
|
}
|
|
else
|
|
running = false;
|
|
|
|
return;
|
|
}
|
|
|
|
if (npclient.phasecount == 0)
|
|
{
|
|
sem_wait(csememu);
|
|
|
|
for (int i = 0; i < npclient.numplayers; i++)
|
|
for (uint32 j = 0; j < npclient.phasespan; j++)
|
|
npcactvpad[i][j] = npcrecvpad[i][j];
|
|
|
|
for (uint32 j = 0; j < npclient.phasespan; j++)
|
|
npcsendpad[j] = npccachpad[j];
|
|
|
|
if (npclient.header & 0x80)
|
|
{
|
|
npcsendpad[npclient.phasespan] = 0;
|
|
for (int i = 0; i < npclient.numplayers; i++)
|
|
npcactvpad[i][npclient.phasespan] = 0;
|
|
|
|
npclient.phasespan++;
|
|
if (npclient.phasespan > (uint32) Memory.ROMFramesPerSecond)
|
|
npclient.phasespan = (uint32) Memory.ROMFramesPerSecond;
|
|
|
|
char str[256];
|
|
sprintf(str, "delay: %d", npclient.phasespan);
|
|
S9xMessage(0, 0, str);
|
|
}
|
|
else
|
|
if (npclient.header & 0x40)
|
|
{
|
|
npclient.phasespan--;
|
|
if (npclient.phasespan == 0)
|
|
npclient.phasespan = 1;
|
|
|
|
char str[256];
|
|
sprintf(str, "delay: %d", npclient.phasespan);
|
|
S9xMessage(0, 0, str);
|
|
}
|
|
|
|
npclient.header = 0;
|
|
pos = 0;
|
|
}
|
|
|
|
for (int i = 0; i < npclient.numplayers; i++)
|
|
{
|
|
controlPad[i] = npcactvpad[i][pos];
|
|
ControlPadFlagsToS9xReportButtons(i, controlPad[i]);
|
|
}
|
|
|
|
GetKeys(myKeys);
|
|
|
|
uint32 pad = 0;
|
|
|
|
JoypadScanDirection(0, &pad);
|
|
if (ISpKeyIsPressed(kISp1PR )) pad |= 0x0010;
|
|
if (ISpKeyIsPressed(kISp1PL )) pad |= 0x0020;
|
|
if (ISpKeyIsPressed(kISp1PX )) pad |= 0x0040;
|
|
if (ISpKeyIsPressed(kISp1PA )) pad |= 0x0080;
|
|
if (ISpKeyIsPressed(kISp1PStart )) pad |= 0x1000;
|
|
if (ISpKeyIsPressed(kISp1PSelect)) pad |= 0x2000;
|
|
if (ISpKeyIsPressed(kISp1PY )) pad |= 0x4000;
|
|
if (ISpKeyIsPressed(kISp1PB )) pad |= 0x8000;
|
|
|
|
if (KeyIsPressed(myKeys, keyCode[k1PR] )) pad |= 0x0010;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PL] )) pad |= 0x0020;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PX] )) pad |= 0x0040;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PA] )) pad |= 0x0080;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PRight] )) pad |= 0x0100;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PLeft] )) pad |= 0x0200;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PDown] )) pad |= 0x0400;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PUp] )) pad |= 0x0800;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PStart] )) pad |= 0x1000;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PSelect])) pad |= 0x2000;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PY] )) pad |= 0x4000;
|
|
if (KeyIsPressed(myKeys, keyCode[k1PB] )) pad |= 0x8000;
|
|
|
|
npccachpad[pos] = pad;
|
|
|
|
if (npclient.phasecount == 0)
|
|
{
|
|
npclient.phasecount = npclient.phasespan;
|
|
sem_post(csempad);
|
|
}
|
|
|
|
npclient.phasecount--;
|
|
pos++;
|
|
}
|