snes9x/macosx/mac-client.mm

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++;
}