Initial commit.
This commit is contained in:
commit
ccb7edff55
7
Doxyfile
Normal file
7
Doxyfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
GENERATE_HTML=yes
|
||||||
|
EXTRACT_ALL=yes
|
||||||
|
RECURSIVE=yes
|
||||||
|
INPUT=../include
|
||||||
|
DISABLE_INDEX = YES
|
||||||
|
GENERATE_TREEVIEW = YES
|
||||||
|
PROJECT_NAME=msgba
|
30
include/msgba/client_connection_data.h
Normal file
30
include/msgba/client_connection_data.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef MS_CLIENT_CONNECTION_DATA
|
||||||
|
#define MS_CLIENT_CONNECTION_DATA
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct msCoreController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct representing everything needed while a connection is established.
|
||||||
|
*/
|
||||||
|
struct msClientConnectionData {
|
||||||
|
size_t numberOfThread;
|
||||||
|
int clientFd;
|
||||||
|
struct msCoreController *coreController;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new msClientConnectionData.
|
||||||
|
*
|
||||||
|
* @param numberOfThread The unique identifier for this connection.
|
||||||
|
* @param clientFd The client fd from which receive/send messages.
|
||||||
|
*/
|
||||||
|
struct msClientConnectionData *
|
||||||
|
msClientConnectionDataNew(size_t numberOfThread, int clientFd);
|
||||||
|
|
||||||
|
void
|
||||||
|
msClientConnectionDataDestroy(struct msClientConnectionData **data);
|
||||||
|
#endif
|
31
include/msgba/core_controller.h
Normal file
31
include/msgba/core_controller.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef MS_CORE_CONTROLLER
|
||||||
|
#define MS_CORE_CONTROLLER
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
|
||||||
|
#include <msgba/client_connection_data.h>
|
||||||
|
|
||||||
|
struct msMultiplayerController;
|
||||||
|
|
||||||
|
struct msCoreController {
|
||||||
|
struct msMultiplayerController *multiplayer;
|
||||||
|
struct mCoreThread threadContext;
|
||||||
|
color_t *outputBuffer;
|
||||||
|
unsigned int stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct msCoreController *
|
||||||
|
msCoreControllerLoadGame (const unsigned char *rom, size_t rom_len,
|
||||||
|
const unsigned char *state, size_t state_len,
|
||||||
|
struct msClientConnectionData *const data);
|
||||||
|
|
||||||
|
void
|
||||||
|
msCoreControllerDestroy(struct msCoreController **controller_ptr);
|
||||||
|
|
||||||
|
struct msCoreController *
|
||||||
|
msCoreControllerNew (struct mCore *core, struct msClientConnectionData *const data);
|
||||||
|
void
|
||||||
|
msCoreControllerThreadStart (struct msCoreController *const core_controller);
|
||||||
|
#endif
|
8
include/msgba/global.h
Normal file
8
include/msgba/global.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef MS_GLOBAL
|
||||||
|
#define MS_GLOBAL
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern char *ms_last_error;
|
||||||
|
#endif
|
30
include/msgba/multiplayer_controller.h
Normal file
30
include/msgba/multiplayer_controller.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef MS_MULTIPLAYER_CONTROLLER
|
||||||
|
#define MS_MULTIPLAYER_CONTROLLER
|
||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
struct msMultiplayerController {
|
||||||
|
struct msPlayer *players;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
union {
|
||||||
|
struct mLockstep m_lockstep;
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
struct GBSIOLockstep m_gbLockstep;
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
struct GBASIOLockstep m_gbaLockstep;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#endif
|
46
include/msgba/packet.h
Normal file
46
include/msgba/packet.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <msgba/client_connection_data.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The possible values for a packet id.
|
||||||
|
*/
|
||||||
|
extern enum {
|
||||||
|
PACKET_GET_HELLO, //! Packet id for get hello.
|
||||||
|
PACKET_SEND_FRAME, //! Packet id for send frame.
|
||||||
|
PACKETS_NUMBER //! The number of recognized packets.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct representing a generic packet.
|
||||||
|
*/
|
||||||
|
struct msPacket {
|
||||||
|
//! The id of the packet.
|
||||||
|
size_t id;
|
||||||
|
//! The size of the data contained in the packet.
|
||||||
|
size_t size;
|
||||||
|
//! The data as a byte array. (Not null terminated.)
|
||||||
|
char *raw_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the code to handle a concrete packet comming from a client.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
msPacketHandle(struct msPacket *packet, int client_fd, struct msClientConnectionData *const data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When done with a packet it must be destroyed using this method.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
msPacketDestroy(struct msPacket **packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to retrieve a packet from the client, returns NULL in case of error.
|
||||||
|
* Blocks the current thread.
|
||||||
|
*/
|
||||||
|
struct msPacket *
|
||||||
|
msPacketRead(int client_fd);
|
30
include/msgba/packet/hello.h
Normal file
30
include/msgba/packet/hello.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef MS_PACKET_HELLO
|
||||||
|
#define MS_PACKET_HELLO
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <msgba/client_connection_data.h>
|
||||||
|
|
||||||
|
struct msPacket;
|
||||||
|
|
||||||
|
struct msPacketHello {
|
||||||
|
size_t size_rom;
|
||||||
|
unsigned char *rom;
|
||||||
|
size_t size_savestate;
|
||||||
|
unsigned char *savestate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
msPacketHelloDestroy(struct msPacketHello **hello);
|
||||||
|
bool
|
||||||
|
msPacketHelloHandle(const struct msPacket *packet, struct msPacketHello *hello,
|
||||||
|
struct msClientConnectionData *const data);
|
||||||
|
bool
|
||||||
|
msPacketHelloGet(const struct msPacket *packet, int client_fd,
|
||||||
|
struct msClientConnectionData *const data);
|
||||||
|
#endif
|
26
include/msgba/player.h
Normal file
26
include/msgba/player.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef MS_PLAYER
|
||||||
|
#define MS_PLAYER
|
||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
union Node {
|
||||||
|
struct GBSIOLockstepNode* gb;
|
||||||
|
struct GBASIOLockstepNode* gba;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct msPlayer {
|
||||||
|
struct msCoreController* controller;
|
||||||
|
union Node node;
|
||||||
|
int awake;
|
||||||
|
int32_t cyclesPosted;
|
||||||
|
unsigned waitMask;
|
||||||
|
};
|
||||||
|
#endif
|
35
meson.build
Normal file
35
meson.build
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
project('tech.owlcode.msgba', 'c')
|
||||||
|
|
||||||
|
inc = include_directories('include')
|
||||||
|
|
||||||
|
sources = [
|
||||||
|
'src/main.c',
|
||||||
|
'src/player.c',
|
||||||
|
'src/core_controller.c',
|
||||||
|
'src/multiplayer_controller.c',
|
||||||
|
'src/packet.c',
|
||||||
|
'src/packet/hello.c',
|
||||||
|
'src/client_connection_data.c',
|
||||||
|
]
|
||||||
|
|
||||||
|
inc = [
|
||||||
|
'include'
|
||||||
|
]
|
||||||
|
|
||||||
|
link_arguments = [
|
||||||
|
'-lmgba'
|
||||||
|
]
|
||||||
|
|
||||||
|
executable('msgba',
|
||||||
|
sources,
|
||||||
|
include_directories : inc,
|
||||||
|
install : true,
|
||||||
|
link_args : link_arguments,
|
||||||
|
)
|
||||||
|
doxygen = find_program('doxygen', required : false)
|
||||||
|
if doxygen.found()
|
||||||
|
message('Doxygen found')
|
||||||
|
run_target('docs', command : [doxygen, meson.source_root() + '/Doxyfile'])
|
||||||
|
else
|
||||||
|
warning('Documentation disabled without doxygen')
|
||||||
|
endif
|
19
src/client_connection_data.c
Normal file
19
src/client_connection_data.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <msgba/core_controller.h>
|
||||||
|
#include <msgba/client_connection_data.h>
|
||||||
|
struct msClientConnectionData *
|
||||||
|
msClientConnectionDataNew(size_t numberOfThread, int clientFd) {
|
||||||
|
struct msClientConnectionData *data = malloc (sizeof *data);
|
||||||
|
data->numberOfThread = numberOfThread;
|
||||||
|
data->clientFd = clientFd;
|
||||||
|
data->coreController = NULL;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msClientConnectionDataDestroy(struct msClientConnectionData **data) {
|
||||||
|
if ((*data)->coreController) {
|
||||||
|
msCoreControllerDestroy(&(*data)->coreController);
|
||||||
|
}
|
||||||
|
free(*data);
|
||||||
|
*data = NULL;
|
||||||
|
}
|
93
src/core_controller.c
Normal file
93
src/core_controller.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
#include <msgba/global.h>
|
||||||
|
#include <msgba/core_controller.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct msCoreController *
|
||||||
|
msCoreControllerLoadGame (const unsigned char *rom, size_t rom_len,
|
||||||
|
const unsigned char *state, size_t state_len,
|
||||||
|
struct msClientConnectionData *const data) {
|
||||||
|
struct VFile* file_rom = VFileMemChunk (NULL, rom_len);
|
||||||
|
struct VFile* file_state = VFileMemChunk (NULL, state_len);
|
||||||
|
file_rom->seek (file_rom, 0, SEEK_SET);
|
||||||
|
file_state->seek (file_state, 0, SEEK_SET);
|
||||||
|
file_rom->write (file_rom, rom, rom_len);
|
||||||
|
file_state->write (file_state, state, state_len);
|
||||||
|
struct mCore *core = mCoreFindVF (file_rom);
|
||||||
|
struct msCoreController *controller = NULL;
|
||||||
|
if (!core) {
|
||||||
|
ms_last_error = "This rom does not appear to be a GBA or GB/C one";
|
||||||
|
file_rom->close(file_rom);
|
||||||
|
file_state->close(file_rom);
|
||||||
|
goto loadGameReturn;
|
||||||
|
}
|
||||||
|
core->init(core);
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
mCoreInitConfig(core, NULL);
|
||||||
|
mCoreConfigSetIntValue(&core->config, "logLevel", mLOG_FATAL & mLOG_ERROR & mLOG_WARN);
|
||||||
|
|
||||||
|
core->desiredVideoDimensions(core, &width, &height);
|
||||||
|
color_t *outputBuffer = malloc((sizeof *outputBuffer) * width * height);
|
||||||
|
printf("controller->outputBuffer width: %u\n", width);
|
||||||
|
core->setVideoBuffer(core, outputBuffer, width);
|
||||||
|
core->loadROM (core, file_rom);
|
||||||
|
mCoreLoadStateNamed (core, file_state, SAVESTATE_SAVEDATA & SAVESTATE_RTC);
|
||||||
|
|
||||||
|
controller = msCoreControllerNew (core, data);
|
||||||
|
controller->outputBuffer = outputBuffer;
|
||||||
|
controller->stride = width;
|
||||||
|
loadGameReturn:
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msCoreControllerSetFrameCallback(struct msCoreController *const self, void(*callback)(struct mCoreThread *)) {
|
||||||
|
self->threadContext.frameCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msCoreControllerThreadStart (struct msCoreController *const core_controller) {
|
||||||
|
struct mCoreThread *thread = &core_controller->threadContext;
|
||||||
|
mCoreThreadStart(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msCoreControllerDestroy(struct msCoreController **controller_ptr) {
|
||||||
|
if (controller_ptr && *controller_ptr) {
|
||||||
|
struct msCoreController *controller = *controller_ptr;
|
||||||
|
|
||||||
|
if (mCoreThreadHasStarted(&controller->threadContext)
|
||||||
|
&& !mCoreThreadHasExited(&controller->threadContext)) {
|
||||||
|
mCoreThreadEnd(&controller->threadContext);
|
||||||
|
}
|
||||||
|
if (controller->threadContext.core) {
|
||||||
|
struct mCore *core = controller->threadContext.core;
|
||||||
|
core->deinit(core);
|
||||||
|
}
|
||||||
|
free(controller->outputBuffer);
|
||||||
|
free(controller);
|
||||||
|
*controller_ptr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct msCoreController *
|
||||||
|
msCoreControllerNew (struct mCore *core, struct msClientConnectionData *const data) {
|
||||||
|
struct msCoreController *controller = malloc (sizeof *controller);
|
||||||
|
controller->multiplayer = NULL;
|
||||||
|
controller->threadContext.core = core;
|
||||||
|
controller->threadContext.userData = data;
|
||||||
|
return controller;
|
||||||
|
}
|
105
src/main.c
Normal file
105
src/main.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
#include <msgba/core_controller.h>
|
||||||
|
#include <msgba/client_connection_data.h>
|
||||||
|
#include <msgba/global.h>
|
||||||
|
#include <msgba/packet.h>
|
||||||
|
|
||||||
|
char *ms_last_error = "";
|
||||||
|
|
||||||
|
#define MAX_NUMBER_OF_THREADS 100
|
||||||
|
pthread_t pthread_client_connections[MAX_NUMBER_OF_THREADS];
|
||||||
|
pthread_cond_t cond_client_number_decreased;
|
||||||
|
pthread_mutex_t mutex_number_connections;
|
||||||
|
size_t connected_clients = 0;
|
||||||
|
size_t number_of_threads = 0;
|
||||||
|
|
||||||
|
void *handleClientConnection(void *user_data) {
|
||||||
|
struct msClientConnectionData *data = (struct msClientConnectionData *) user_data;
|
||||||
|
int client_fd = data->clientFd;
|
||||||
|
size_t number_of_thread = data->numberOfThread;
|
||||||
|
struct msPacket *packet = NULL;
|
||||||
|
printf("Connection from client with id %lu and fd %d\n", number_of_thread, client_fd);
|
||||||
|
while ((packet = msPacketRead(client_fd)) != NULL) {
|
||||||
|
bool success = msPacketHandle(packet, client_fd, data);
|
||||||
|
msPacketDestroy(&packet);
|
||||||
|
if (!success) {
|
||||||
|
fprintf(stderr, "Failed handling packet for client_fd %d, closing connection\n", client_fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msClientConnectionDataDestroy(&data);
|
||||||
|
if (close (client_fd) == 1) {
|
||||||
|
fprintf(stderr, "Error closing socket %d\n", client_fd);
|
||||||
|
}
|
||||||
|
printf("Closing connection with id %lu and fd %d\n", number_of_thread, client_fd);
|
||||||
|
pthread_mutex_lock(&mutex_number_connections);
|
||||||
|
connected_clients--;
|
||||||
|
pthread_mutex_unlock(&mutex_number_connections);
|
||||||
|
pthread_cond_signal(&cond_client_number_decreased);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SOCKET_ADDRESS "msgba.sock"
|
||||||
|
#define BACKLOG 5
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
struct sockaddr_un address;
|
||||||
|
memset (&address, 0, sizeof address);
|
||||||
|
|
||||||
|
address.sun_family = AF_UNIX;
|
||||||
|
unlink(SOCKET_ADDRESS);
|
||||||
|
size_t max_size_sun_path = sizeof address.sun_path - 1;
|
||||||
|
if (strlen(SOCKET_ADDRESS) > max_size_sun_path) {
|
||||||
|
fprintf(stderr, "Too big socket address");
|
||||||
|
exit (2);
|
||||||
|
}
|
||||||
|
strncpy(address.sun_path, SOCKET_ADDRESS, max_size_sun_path);
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
fprintf (stderr, "Unable to create socket\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
if (bind (fd, (struct sockaddr *)&address,
|
||||||
|
strlen (SOCKET_ADDRESS) + sizeof (address.sun_family)) == -1) {
|
||||||
|
fprintf(stderr, "Unable to bind socket\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
if (listen (fd, BACKLOG) == -1) {
|
||||||
|
fprintf(stderr, "Unable to listen in socket\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
pthread_cond_init(&cond_client_number_decreased, NULL);
|
||||||
|
pthread_mutex_init(&mutex_number_connections, NULL);
|
||||||
|
while (1) {
|
||||||
|
int client_fd = accept(fd, NULL, NULL);
|
||||||
|
pthread_attr_t attributes;
|
||||||
|
pthread_attr_init(&attributes);
|
||||||
|
pthread_attr_setdetachstate(&attributes, 1);
|
||||||
|
pthread_mutex_lock(&mutex_number_connections);
|
||||||
|
while (connected_clients == MAX_NUMBER_OF_THREADS) {
|
||||||
|
pthread_cond_wait(&cond_client_number_decreased, &mutex_number_connections);
|
||||||
|
}
|
||||||
|
struct msClientConnectionData *data = msClientConnectionDataNew(number_of_threads, client_fd);
|
||||||
|
|
||||||
|
pthread_create(&pthread_client_connections[connected_clients++], &attributes,
|
||||||
|
&handleClientConnection, (void *)data);
|
||||||
|
pthread_mutex_unlock(&mutex_number_connections);
|
||||||
|
}
|
||||||
|
}
|
23
src/multiplayer_controller.c
Normal file
23
src/multiplayer_controller.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
#include <msgba/multiplayer_controller.h>
|
||||||
|
|
||||||
|
struct msMultiplayerController *
|
||||||
|
msMultiplayerControllerNew (void) {
|
||||||
|
struct msMultiplayerController *multiplayer = malloc (sizeof *multiplayer);
|
||||||
|
pthread_mutex_init(&(multiplayer->lock), NULL);
|
||||||
|
multiplayer->players = NULL;
|
||||||
|
return multiplayer;
|
||||||
|
}
|
||||||
|
|
89
src/packet.c
Normal file
89
src/packet.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include <msgba/packet.h>
|
||||||
|
#include <msgba/packet/hello.h>
|
||||||
|
|
||||||
|
#define PRINT_DEBUG(NAME, CLIENT_FD) \
|
||||||
|
printf ("Received packet %s from client fd %d\n", #NAME, CLIENT_FD);
|
||||||
|
bool
|
||||||
|
msPacketHandle(struct msPacket *packet, int client_fd,
|
||||||
|
struct msClientConnectionData *const data) {
|
||||||
|
bool result = false;
|
||||||
|
switch (packet->id) {
|
||||||
|
case PACKET_GET_HELLO:
|
||||||
|
PRINT_DEBUG(PACKET_HELLO, client_fd);
|
||||||
|
result = msPacketHelloGet(packet, client_fd, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msPacketSend(const struct msPacket *const packet, int fd) {
|
||||||
|
write(fd, (const void *)packet->id, sizeof packet->id);
|
||||||
|
write(fd, (const void *)packet->size, sizeof packet->size);
|
||||||
|
write(fd, (const void *)packet->raw_data, sizeof *packet->raw_data * packet->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct msPacket *
|
||||||
|
msPacketNew(const size_t id, const size_t size, char *raw_data) {
|
||||||
|
struct msPacket *packet = malloc(sizeof *packet);
|
||||||
|
packet->id = id;
|
||||||
|
packet->size = size;
|
||||||
|
packet->raw_data = raw_data;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
msPacketDestroy(struct msPacket **packet) {
|
||||||
|
if ((*packet)->raw_data) {
|
||||||
|
free((*packet)->raw_data);
|
||||||
|
}
|
||||||
|
free(*packet);
|
||||||
|
*packet=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct msPacket *
|
||||||
|
msPacketRead(int client_fd) {
|
||||||
|
struct msPacket *packet = NULL;
|
||||||
|
size_t id = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
ssize_t result;
|
||||||
|
char *raw_data = NULL;
|
||||||
|
|
||||||
|
result = read(client_fd, &id, sizeof id);
|
||||||
|
if (result < sizeof id) {
|
||||||
|
printf("Unable to read id\n");
|
||||||
|
goto return_read_packet;
|
||||||
|
}
|
||||||
|
result = read(client_fd, &size, sizeof size);
|
||||||
|
if (result < sizeof size) {
|
||||||
|
printf("Unable to read packet\n");
|
||||||
|
goto return_read_packet;
|
||||||
|
}
|
||||||
|
size = be64toh(size);
|
||||||
|
|
||||||
|
raw_data = malloc(size);
|
||||||
|
|
||||||
|
size_t to_read_size = size;
|
||||||
|
while (to_read_size > 0) {
|
||||||
|
result = read(client_fd, &raw_data[size - to_read_size], to_read_size);
|
||||||
|
if (result == -1) {
|
||||||
|
printf("Unable to read raw_data\n");
|
||||||
|
goto return_read_packet;
|
||||||
|
}
|
||||||
|
to_read_size -= result;
|
||||||
|
}
|
||||||
|
if (result < size) {
|
||||||
|
}
|
||||||
|
packet = calloc (1, sizeof *packet);
|
||||||
|
packet->id = id;
|
||||||
|
packet->size = size;
|
||||||
|
packet->raw_data = raw_data;
|
||||||
|
return_read_packet:
|
||||||
|
if (!packet && raw_data) {
|
||||||
|
free(raw_data);
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
103
src/packet/hello.c
Normal file
103
src/packet/hello.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include <msgba/packet.h>
|
||||||
|
#include <msgba/packet/hello.h>
|
||||||
|
void
|
||||||
|
msPacketHelloDestroy(struct msPacketHello **hello) {
|
||||||
|
if ((*hello)->rom) {
|
||||||
|
free((*hello)->rom);
|
||||||
|
(*hello)->rom = NULL;
|
||||||
|
}
|
||||||
|
if ((*hello)->savestate) {
|
||||||
|
free((*hello)->savestate);
|
||||||
|
(*hello)->savestate = NULL;
|
||||||
|
}
|
||||||
|
free(*hello);
|
||||||
|
*hello = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
msPacketHelloHandle(const struct msPacket *packet, struct msPacketHello *hello,
|
||||||
|
struct msClientConnectionData *const data) {
|
||||||
|
bool result = false;
|
||||||
|
if (data->coreController) {
|
||||||
|
fprintf(stderr, "Attempt to send a second hello, aborting connection.\n");
|
||||||
|
goto return_ms_packet_hello_handle;
|
||||||
|
}
|
||||||
|
printf("Loading game and save for client_fd %d\n", data->clientFd);
|
||||||
|
data->coreController = msCoreControllerLoadGame(hello->rom, hello->size_rom, hello->savestate, hello->size_savestate);
|
||||||
|
msCoreControllerThreadStart(data->coreController);
|
||||||
|
result = true;
|
||||||
|
return_ms_packet_hello_handle:
|
||||||
|
msPacketHelloDestroy(&hello);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
msPacketHelloGet(const struct msPacket *packet, int client_fd,
|
||||||
|
struct msClientConnectionData *const data) {
|
||||||
|
bool result = false;
|
||||||
|
struct msPacketHello *hello = NULL;
|
||||||
|
unsigned char *rom = NULL;
|
||||||
|
unsigned char *savestate = NULL;
|
||||||
|
FILE *fp = fmemopen(packet->raw_data, packet->size, "r");
|
||||||
|
if (!fp) {
|
||||||
|
printf("Unable to fmemopen\n");
|
||||||
|
goto return_get_packet_hello;
|
||||||
|
}
|
||||||
|
size_t size_rom = 0;
|
||||||
|
size_t size_savestate = 0;
|
||||||
|
size_t reads[4] = { 0,0,0,0 };
|
||||||
|
size_t total_read = 0;
|
||||||
|
size_t to_read_size;
|
||||||
|
|
||||||
|
#define FREAD(target, size, count, fp, n, goto_to) \
|
||||||
|
to_read_size = size; \
|
||||||
|
reads[n] = fread(target, to_read_size, count, fp); \
|
||||||
|
reads[n] *= size; \
|
||||||
|
if (reads[n] == -1) { \
|
||||||
|
printf("Unable to read %s\n", #target); \
|
||||||
|
goto goto_to; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FREAD(&size_rom, sizeof size_rom, 1, fp, 0, return_get_packet_hello);
|
||||||
|
size_rom = be64toh(size_rom);
|
||||||
|
rom = malloc(sizeof *rom * size_rom);
|
||||||
|
|
||||||
|
FREAD(rom, sizeof *rom, size_rom, fp, 1, return_get_packet_hello);
|
||||||
|
|
||||||
|
FREAD(&size_savestate, sizeof size_savestate, 1, fp, 2, return_get_packet_hello);
|
||||||
|
size_savestate = be64toh(size_savestate);
|
||||||
|
savestate = malloc(sizeof *savestate * size_savestate);
|
||||||
|
|
||||||
|
FREAD(savestate, sizeof *savestate, size_savestate, fp, 3, return_get_packet_hello);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
for (int i = 0; i<4; i++) {
|
||||||
|
total_read += reads[i];
|
||||||
|
}
|
||||||
|
if (total_read != packet->size) {
|
||||||
|
fprintf (stderr, "Total read (%lu) != packet->size (%lu)\n", total_read, packet->size);
|
||||||
|
goto return_get_packet_hello;
|
||||||
|
}
|
||||||
|
hello = calloc(1, sizeof *hello);
|
||||||
|
hello->size_rom = size_rom;
|
||||||
|
hello->rom = rom;
|
||||||
|
hello->size_savestate = size_savestate;
|
||||||
|
hello->savestate = savestate;
|
||||||
|
result = true;
|
||||||
|
return_get_packet_hello:
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "Unable to read the packet hello\n");
|
||||||
|
if (rom) {
|
||||||
|
free (rom);
|
||||||
|
rom = NULL;
|
||||||
|
}
|
||||||
|
if (savestate) {
|
||||||
|
free (savestate);
|
||||||
|
savestate = NULL;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return msPacketHelloHandle(packet, hello, data);
|
||||||
|
}
|
||||||
|
|
30
src/player.c
Normal file
30
src/player.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <msgba/player.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
struct msPlayer *
|
||||||
|
msPlayerNewGBA (struct msCoreController *controller,
|
||||||
|
struct GBASIOLockstepNode *node) {
|
||||||
|
struct msPlayer *player = malloc (sizeof *player);
|
||||||
|
player->controller = controller;
|
||||||
|
player->node.gba = node;
|
||||||
|
player->awake = 1;
|
||||||
|
player->cyclesPosted = 0;
|
||||||
|
player->waitMask = 0;
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
struct msPlayer *
|
||||||
|
msPlayerNewGB (struct msCoreController *controller,
|
||||||
|
struct GBSIOLockstepNode *node) {
|
||||||
|
struct msPlayer *player = malloc (sizeof *player);
|
||||||
|
player->controller = controller;
|
||||||
|
player->node.gb = node;
|
||||||
|
player->awake = 1;
|
||||||
|
player->cyclesPosted = 0;
|
||||||
|
player->waitMask = 0;
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
40
src/try_mem.c
Normal file
40
src/try_mem.c
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <mgba/flags.h>
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/interface.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct mCoreThread *threadContext = malloc(sizeof *threadContext);
|
||||||
|
char *file = "/home/sergio/ruby.gba";
|
||||||
|
struct mCore *core = mCoreFind(file);
|
||||||
|
core->init(core);
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
core->desiredVideoDimensions(core, &width, &height);
|
||||||
|
color_t *outputBuffer = malloc((sizeof *outputBuffer) * width * height);
|
||||||
|
core->setVideoBuffer(core, outputBuffer, 240);
|
||||||
|
mCoreInitConfig(core, NULL);
|
||||||
|
if (!core) {
|
||||||
|
fprintf(stderr, "Unable to load core");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
mCoreLoadFile(core, file);
|
||||||
|
threadContext->core = core;
|
||||||
|
threadContext->userData = NULL;
|
||||||
|
mCoreThreadStart(threadContext);
|
||||||
|
mCoreThreadJoin(threadContext);
|
||||||
|
free(threadContext);
|
||||||
|
}
|
73
tester.pl
Normal file
73
tester.pl
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.34.1;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use IO::Socket::UNIX;
|
||||||
|
|
||||||
|
use Path::Tiny qw/path/;
|
||||||
|
|
||||||
|
my $HOME = $ENV{HOME};
|
||||||
|
my $PACKET_HELLO = 0;
|
||||||
|
|
||||||
|
my $fh = IO::Socket::UNIX->new(
|
||||||
|
Type => SOCK_STREAM(),
|
||||||
|
Peer => 'msgba.sock',
|
||||||
|
) || die "Can't open socket: $IO::Socket::errstr";
|
||||||
|
|
||||||
|
my $rom = path($HOME)->child('ruby.gba')->slurp;
|
||||||
|
my $sav = path($HOME)->child('ruby.ss1')->slurp;
|
||||||
|
|
||||||
|
my $packet_hello = "";
|
||||||
|
open my $fh_packet_hello, '>', \$packet_hello;
|
||||||
|
write_packet_hello($fh_packet_hello, \$rom, \$sav);
|
||||||
|
close $fh_packet_hello;
|
||||||
|
my $packet = "";
|
||||||
|
open my $fh_packet, '>', \$packet;
|
||||||
|
write_packet($fh_packet, $PACKET_HELLO, \$packet_hello);
|
||||||
|
close $fh_packet;
|
||||||
|
print $fh $packet;
|
||||||
|
sleep 1 while 1;
|
||||||
|
|
||||||
|
sub write_packet {
|
||||||
|
my $fh = shift;
|
||||||
|
my $id = shift;
|
||||||
|
my $raw_data = ret_scalar(shift);
|
||||||
|
print $fh pack('Q>', $id);
|
||||||
|
print $fh pack('Q>', length $raw_data);
|
||||||
|
print $fh $raw_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub write_packet_hello {
|
||||||
|
my $fh = shift or die "No file handle";
|
||||||
|
my $rom = ret_scalar(shift);
|
||||||
|
my $save = ret_scalar(shift);
|
||||||
|
my $rom_length = length $rom;
|
||||||
|
my $save_length = length $save;
|
||||||
|
say "Packet size = @{[16 + $rom_length + $save_length]}";
|
||||||
|
|
||||||
|
|
||||||
|
print $fh pack 'Q>', $rom_length;
|
||||||
|
print $fh $rom;
|
||||||
|
print $fh pack 'Q>', $save_length;
|
||||||
|
print $fh $save;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ret_scalar {
|
||||||
|
my $arg = shift;
|
||||||
|
if (check_scalar_ref($arg)) {
|
||||||
|
return ${$arg};
|
||||||
|
}
|
||||||
|
die "No scalar ref";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_scalar_ref {
|
||||||
|
my $arg = shift or die "Undefined scalar ref";
|
||||||
|
|
||||||
|
if ((ref $arg) eq 'SCALAR') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user