Netplay (not finished, but works), save state fixes
This commit is contained in:
parent
021efccbed
commit
f4802421d0
30 changed files with 979 additions and 79 deletions
|
@ -78,9 +78,8 @@ bool APU::Exec(uint32_t executedCycles)
|
||||||
if(availableSampleCount >= APU::SamplesPerFrame) {
|
if(availableSampleCount >= APU::SamplesPerFrame) {
|
||||||
size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame);
|
size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame);
|
||||||
APU::AudioDevice->PlayBuffer(_outputBuffer, sampleCount * BitsPerSample / 8);
|
APU::AudioDevice->PlayBuffer(_outputBuffer, sampleCount * BitsPerSample / 8);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +91,8 @@ void APU::StreamState(bool saving)
|
||||||
_apu.save_snapshot(&snapshot);
|
_apu.save_snapshot(&snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<uint32_t>(_currentClock);
|
||||||
|
|
||||||
StreamArray<uint8_t>(snapshot.w40xx, 0x14);
|
StreamArray<uint8_t>(snapshot.w40xx, 0x14);
|
||||||
Stream<uint8_t>(snapshot.w4015);
|
Stream<uint8_t>(snapshot.w4015);
|
||||||
Stream<uint8_t>(snapshot.w4017);
|
Stream<uint8_t>(snapshot.w4017);
|
||||||
|
|
127
Core/Console.cpp
127
Core/Console.cpp
|
@ -4,11 +4,11 @@
|
||||||
#include "../Utilities/Timer.h"
|
#include "../Utilities/Timer.h"
|
||||||
|
|
||||||
Console* Console::Instance = nullptr;
|
Console* Console::Instance = nullptr;
|
||||||
|
IMessageManager* Console::MessageManager = nullptr;
|
||||||
uint32_t Console::Flags = 0;
|
uint32_t Console::Flags = 0;
|
||||||
uint32_t Console::CurrentFPS = 0;
|
uint32_t Console::CurrentFPS = 0;
|
||||||
atomic_flag Console::PauseFlag;
|
SimpleLock Console::PauseLock;
|
||||||
atomic_flag Console::RunningFlag;
|
SimpleLock Console::RunningLock;
|
||||||
|
|
||||||
|
|
||||||
Console::Console(wstring filename)
|
Console::Console(wstring filename)
|
||||||
{
|
{
|
||||||
|
@ -29,16 +29,16 @@ Console::Console(wstring filename)
|
||||||
|
|
||||||
ResetComponents(false);
|
ResetComponents(false);
|
||||||
|
|
||||||
Console::PauseFlag.clear();
|
Console::PauseLock.Release();
|
||||||
Console::RunningFlag.clear();
|
Console::RunningLock.Release();
|
||||||
Console::Instance = this;
|
Console::Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::~Console()
|
Console::~Console()
|
||||||
{
|
{
|
||||||
Movie::Stop();
|
Movie::Stop();
|
||||||
Console::PauseFlag.clear();
|
Console::PauseLock.Release();
|
||||||
Console::RunningFlag.clear();
|
Console::RunningLock.Release();
|
||||||
if(Console::Instance == this) {
|
if(Console::Instance == this) {
|
||||||
Console::Instance = nullptr;
|
Console::Instance = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -66,16 +66,16 @@ void Console::Stop()
|
||||||
|
|
||||||
void Console::Pause()
|
void Console::Pause()
|
||||||
{
|
{
|
||||||
while(Console::PauseFlag.test_and_set());
|
Console::PauseLock.Acquire();
|
||||||
|
|
||||||
//Spin wait until emu pauses
|
//Spin wait until emu pauses
|
||||||
while(Console::RunningFlag.test_and_set());
|
Console::RunningLock.Acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::Resume()
|
void Console::Resume()
|
||||||
{
|
{
|
||||||
Console::RunningFlag.clear();
|
Console::RunningLock.Release();
|
||||||
Console::PauseFlag.clear();
|
Console::PauseLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::SetFlags(int flags)
|
void Console::SetFlags(int flags)
|
||||||
|
@ -93,6 +93,18 @@ bool Console::CheckFlag(int flag)
|
||||||
return (Console::Flags & flag) == flag;
|
return (Console::Flags & flag) == flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Console::RegisterMessageManager(IMessageManager* messageManager)
|
||||||
|
{
|
||||||
|
Console::MessageManager = messageManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::DisplayMessage(wstring message)
|
||||||
|
{
|
||||||
|
if(Console::MessageManager) {
|
||||||
|
Console::MessageManager->DisplayMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Console::GetFPS()
|
uint32_t Console::GetFPS()
|
||||||
{
|
{
|
||||||
return Console::CurrentFPS;
|
return Console::CurrentFPS;
|
||||||
|
@ -105,35 +117,18 @@ void Console::Run()
|
||||||
uint32_t lastFrameCount = 0;
|
uint32_t lastFrameCount = 0;
|
||||||
double elapsedTime = 0;
|
double elapsedTime = 0;
|
||||||
double targetTime = 16.6666666666666666;
|
double targetTime = 16.6666666666666666;
|
||||||
while(Console::RunningFlag.test_and_set());
|
|
||||||
|
Console::RunningLock.Acquire();
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
uint32_t executedCycles = _cpu->Exec();
|
uint32_t executedCycles = _cpu->Exec();
|
||||||
_ppu->Exec();
|
_ppu->Exec();
|
||||||
|
|
||||||
bool frameDone = _apu->Exec(executedCycles);
|
bool frameDone = _apu->Exec(executedCycles);
|
||||||
|
|
||||||
if(frameDone) {
|
if(frameDone) {
|
||||||
_cpu->EndFrame();
|
_cpu->EndFrame();
|
||||||
_ppu->EndFrame();
|
_ppu->EndFrame();
|
||||||
|
|
||||||
if(Console::PauseFlag.test_and_set()) {
|
|
||||||
//Need to temporarely pause the emu (to save/load a state, etc.)
|
|
||||||
Console::RunningFlag.clear();
|
|
||||||
|
|
||||||
//Spin wait until we are allowed to start again
|
|
||||||
while(Console::PauseFlag.test_and_set());
|
|
||||||
Console::PauseFlag.clear();
|
|
||||||
|
|
||||||
while(Console::RunningFlag.test_and_set());
|
|
||||||
} else {
|
|
||||||
Console::PauseFlag.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_stop) {
|
|
||||||
_stop = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) {
|
if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) {
|
||||||
elapsedTime = clockTimer.GetElapsedMS();
|
elapsedTime = clockTimer.GetElapsedMS();
|
||||||
while(targetTime > elapsedTime) {
|
while(targetTime > elapsedTime) {
|
||||||
|
@ -142,18 +137,34 @@ void Console::Run()
|
||||||
}
|
}
|
||||||
elapsedTime = clockTimer.GetElapsedMS();
|
elapsedTime = clockTimer.GetElapsedMS();
|
||||||
}
|
}
|
||||||
clockTimer.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fpsTimer.GetElapsedMS() > 1000) {
|
if(!Console::PauseLock.IsFree()) {
|
||||||
uint32_t frameCount = _ppu->GetFrameCount();
|
//Need to temporarely pause the emu (to save/load a state, etc.)
|
||||||
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)));
|
Console::RunningLock.Release();
|
||||||
lastFrameCount = frameCount;
|
|
||||||
fpsTimer.Reset();
|
//Spin wait until we are allowed to start again
|
||||||
|
Console::PauseLock.WaitForRelease();
|
||||||
|
|
||||||
|
Console::RunningLock.Acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
clockTimer.Reset();
|
||||||
|
|
||||||
|
if(_stop) {
|
||||||
|
_stop = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fpsTimer.GetElapsedMS() > 1000) {
|
||||||
|
uint32_t frameCount = _ppu->GetFrameCount();
|
||||||
|
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)));
|
||||||
|
lastFrameCount = frameCount;
|
||||||
|
fpsTimer.Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Console::RunningFlag.clear();
|
Console::RunningLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::SaveState(wstring filename)
|
void Console::SaveState(wstring filename)
|
||||||
|
@ -165,6 +176,8 @@ void Console::SaveState(wstring filename)
|
||||||
Console::SaveState(file);
|
Console::SaveState(file);
|
||||||
Console::Resume();
|
Console::Resume();
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
Console::DisplayMessage(L"State saved.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,27 +190,45 @@ bool Console::LoadState(wstring filename)
|
||||||
Console::LoadState(file);
|
Console::LoadState(file);
|
||||||
Console::Resume();
|
Console::Resume();
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
Console::DisplayMessage(L"State loaded.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console::DisplayMessage(L"Slot is empty.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::SaveState(ostream &saveStream)
|
void Console::SaveState(ostream &saveStream)
|
||||||
{
|
{
|
||||||
Instance->_cpu->SaveSnapshot(&saveStream);
|
if(Instance) {
|
||||||
Instance->_ppu->SaveSnapshot(&saveStream);
|
Instance->_cpu->SaveSnapshot(&saveStream);
|
||||||
Instance->_memoryManager->SaveSnapshot(&saveStream);
|
Instance->_ppu->SaveSnapshot(&saveStream);
|
||||||
Instance->_mapper->SaveSnapshot(&saveStream);
|
Instance->_memoryManager->SaveSnapshot(&saveStream);
|
||||||
Instance->_apu->SaveSnapshot(&saveStream);
|
Instance->_mapper->SaveSnapshot(&saveStream);
|
||||||
|
Instance->_apu->SaveSnapshot(&saveStream);
|
||||||
|
Instance->_controlManager->SaveSnapshot(&saveStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::LoadState(istream &loadStream)
|
void Console::LoadState(istream &loadStream)
|
||||||
{
|
{
|
||||||
Instance->_cpu->LoadSnapshot(&loadStream);
|
if(Instance) {
|
||||||
Instance->_ppu->LoadSnapshot(&loadStream);
|
Instance->_cpu->LoadSnapshot(&loadStream);
|
||||||
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
Instance->_ppu->LoadSnapshot(&loadStream);
|
||||||
Instance->_mapper->LoadSnapshot(&loadStream);
|
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
||||||
Instance->_apu->LoadSnapshot(&loadStream);
|
Instance->_mapper->LoadSnapshot(&loadStream);
|
||||||
|
Instance->_apu->LoadSnapshot(&loadStream);
|
||||||
|
Instance->_controlManager->LoadSnapshot(&loadStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
||||||
|
{
|
||||||
|
stringstream stream;
|
||||||
|
stream.write((char*)buffer, bufferSize);
|
||||||
|
stream.seekg(0, ios::beg);
|
||||||
|
LoadState(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Console::RunTest(uint8_t *expectedResult)
|
bool Console::RunTest(uint8_t *expectedResult)
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "BaseMapper.h"
|
#include "BaseMapper.h"
|
||||||
#include "MemoryManager.h"
|
#include "MemoryManager.h"
|
||||||
#include "ControlManager.h"
|
#include "ControlManager.h"
|
||||||
|
#include "../Utilities/SimpleLock.h"
|
||||||
|
#include "IMessageManager.h"
|
||||||
|
|
||||||
enum EmulationFlags
|
enum EmulationFlags
|
||||||
{
|
{
|
||||||
|
@ -19,8 +21,9 @@ class Console
|
||||||
static Console* Instance;
|
static Console* Instance;
|
||||||
static uint32_t Flags;
|
static uint32_t Flags;
|
||||||
static uint32_t CurrentFPS;
|
static uint32_t CurrentFPS;
|
||||||
static atomic_flag PauseFlag;
|
static SimpleLock PauseLock;
|
||||||
static atomic_flag RunningFlag;
|
static SimpleLock RunningLock;
|
||||||
|
static IMessageManager* MessageManager;
|
||||||
|
|
||||||
unique_ptr<CPU> _cpu;
|
unique_ptr<CPU> _cpu;
|
||||||
unique_ptr<PPU> _ppu;
|
unique_ptr<PPU> _ppu;
|
||||||
|
@ -56,9 +59,13 @@ class Console
|
||||||
static void SaveState(ostream &saveStream);
|
static void SaveState(ostream &saveStream);
|
||||||
static bool LoadState(wstring filename);
|
static bool LoadState(wstring filename);
|
||||||
static void LoadState(istream &loadStream);
|
static void LoadState(istream &loadStream);
|
||||||
|
static void LoadState(uint8_t *buffer, uint32_t bufferSize);
|
||||||
|
|
||||||
static bool CheckFlag(int flag);
|
static bool CheckFlag(int flag);
|
||||||
static void SetFlags(int flags);
|
static void SetFlags(int flags);
|
||||||
static void ClearFlags(int flags);
|
static void ClearFlags(int flags);
|
||||||
static uint32_t GetFPS();
|
static uint32_t GetFPS();
|
||||||
|
|
||||||
|
static void RegisterMessageManager(IMessageManager* messageManager);
|
||||||
|
static void DisplayMessage(wstring message);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,17 +2,60 @@
|
||||||
#include "ControlManager.h"
|
#include "ControlManager.h"
|
||||||
|
|
||||||
IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
|
IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
|
||||||
|
IControlDevice* ControlManager::OriginalControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
|
||||||
|
IGameBroadcaster* ControlManager::GameBroadcaster = nullptr;
|
||||||
|
|
||||||
ControlManager::ControlManager()
|
ControlManager::ControlManager()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlManager::RegisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||||
|
{
|
||||||
|
ControlManager::GameBroadcaster = gameBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlManager::UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||||
|
{
|
||||||
|
if(ControlManager::GameBroadcaster == gameBroadcaster) {
|
||||||
|
ControlManager::GameBroadcaster = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlManager::BackupControlDevices()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 4; i++) {
|
||||||
|
OriginalControlDevices[i] = ControlDevices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlManager::RestoreControlDevices()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 4; i++) {
|
||||||
|
ControlDevices[i] = OriginalControlDevices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IControlDevice* ControlManager::GetControlDevice(uint8_t port)
|
||||||
|
{
|
||||||
|
return ControlManager::ControlDevices[port];
|
||||||
|
}
|
||||||
|
|
||||||
void ControlManager::RegisterControlDevice(IControlDevice* controlDevice, uint8_t port)
|
void ControlManager::RegisterControlDevice(IControlDevice* controlDevice, uint8_t port)
|
||||||
{
|
{
|
||||||
ControlManager::ControlDevices[port] = controlDevice;
|
ControlManager::ControlDevices[port] = controlDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlManager::UnregisterControlDevice(IControlDevice* controlDevice)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 4; i++) {
|
||||||
|
if(ControlManager::ControlDevices[i] == controlDevice) {
|
||||||
|
ControlManager::ControlDevices[i] = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ControlManager::RefreshAllPorts()
|
void ControlManager::RefreshAllPorts()
|
||||||
{
|
{
|
||||||
RefreshStateBuffer(0);
|
RefreshStateBuffer(0);
|
||||||
|
@ -34,11 +77,7 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
|
||||||
state = Movie::Instance->GetState(port);
|
state = Movie::Instance->GetState(port);
|
||||||
} else {
|
} else {
|
||||||
if(controlDevice) {
|
if(controlDevice) {
|
||||||
ButtonState buttonState = controlDevice->GetButtonState();
|
state = controlDevice->GetButtonState().ToByte();
|
||||||
|
|
||||||
//"Button status for each controller is returned as an 8-bit report in the following order: A, B, Select, Start, Up, Down, Left, Right."
|
|
||||||
state = (uint8_t)buttonState.A | ((uint8_t)buttonState.B << 1) | ((uint8_t)buttonState.Select << 2) | ((uint8_t)buttonState.Start << 3) |
|
|
||||||
((uint8_t)buttonState.Up << 4) | ((uint8_t)buttonState.Down << 5) | ((uint8_t)buttonState.Left << 6) | ((uint8_t)buttonState.Right << 7);
|
|
||||||
} else {
|
} else {
|
||||||
state = 0x00;
|
state = 0x00;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +85,9 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
|
||||||
|
|
||||||
//Used when recording movies
|
//Used when recording movies
|
||||||
Movie::Instance->RecordState(port, state);
|
Movie::Instance->RecordState(port, state);
|
||||||
|
if(ControlManager::GameBroadcaster) {
|
||||||
|
ControlManager::GameBroadcaster->BroadcastInput(state, port);
|
||||||
|
}
|
||||||
|
|
||||||
_stateBuffer[port] = state;
|
_stateBuffer[port] = state;
|
||||||
}
|
}
|
||||||
|
@ -97,3 +139,9 @@ void ControlManager::WriteRAM(uint16_t addr, uint8_t value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlManager::StreamState(bool saving)
|
||||||
|
{
|
||||||
|
StreamArray<uint8_t>(_stateBuffer, 4);
|
||||||
|
Stream<bool>(_refreshState);
|
||||||
|
}
|
|
@ -4,11 +4,16 @@
|
||||||
#include "IControlDevice.h"
|
#include "IControlDevice.h"
|
||||||
#include "IMemoryHandler.h"
|
#include "IMemoryHandler.h"
|
||||||
#include "Movie.h"
|
#include "Movie.h"
|
||||||
|
#include "IGameBroadcaster.h"
|
||||||
|
#include "Snapshotable.h"
|
||||||
|
|
||||||
class ControlManager : public IMemoryHandler
|
class ControlManager : public Snapshotable, public IMemoryHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static IControlDevice* ControlDevices[4];
|
static IControlDevice* ControlDevices[4];
|
||||||
|
static IControlDevice* OriginalControlDevices[4];
|
||||||
|
static IGameBroadcaster* GameBroadcaster;
|
||||||
|
|
||||||
bool _refreshState = false;
|
bool _refreshState = false;
|
||||||
uint8_t _stateBuffer[4];
|
uint8_t _stateBuffer[4];
|
||||||
|
|
||||||
|
@ -16,10 +21,20 @@ class ControlManager : public IMemoryHandler
|
||||||
void RefreshStateBuffer(uint8_t port);
|
void RefreshStateBuffer(uint8_t port);
|
||||||
uint8_t GetPortValue(uint8_t port);
|
uint8_t GetPortValue(uint8_t port);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void StreamState(bool saving);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ControlManager();
|
ControlManager();
|
||||||
|
|
||||||
|
static void RegisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||||
|
static void UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||||
|
|
||||||
|
static void BackupControlDevices();
|
||||||
|
static void RestoreControlDevices();
|
||||||
|
static IControlDevice* GetControlDevice(uint8_t port);
|
||||||
static void RegisterControlDevice(IControlDevice* controlDevice, uint8_t port);
|
static void RegisterControlDevice(IControlDevice* controlDevice, uint8_t port);
|
||||||
|
static void UnregisterControlDevice(IControlDevice* controlDevice);
|
||||||
|
|
||||||
void GetMemoryRanges(MemoryRanges &ranges)
|
void GetMemoryRanges(MemoryRanges &ranges)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IncludePath>$(IncludePath)</IncludePath>
|
||||||
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
@ -93,12 +95,20 @@
|
||||||
<ClInclude Include="CNROM.h" />
|
<ClInclude Include="CNROM.h" />
|
||||||
<ClInclude Include="ColorDreams.h" />
|
<ClInclude Include="ColorDreams.h" />
|
||||||
<ClInclude Include="ControlManager.h" />
|
<ClInclude Include="ControlManager.h" />
|
||||||
|
<ClInclude Include="GameClient.h" />
|
||||||
|
<ClInclude Include="GameClientConnection.h" />
|
||||||
|
<ClInclude Include="GameConnection.h" />
|
||||||
|
<ClInclude Include="GameServer.h" />
|
||||||
|
<ClInclude Include="GameServerConnection.h" />
|
||||||
<ClInclude Include="IAudioDevice.h" />
|
<ClInclude Include="IAudioDevice.h" />
|
||||||
<ClInclude Include="IControlDevice.h" />
|
<ClInclude Include="IControlDevice.h" />
|
||||||
|
<ClInclude Include="IGameBroadcaster.h" />
|
||||||
<ClInclude Include="IMemoryHandler.h" />
|
<ClInclude Include="IMemoryHandler.h" />
|
||||||
<ClInclude Include="Console.h" />
|
<ClInclude Include="Console.h" />
|
||||||
|
<ClInclude Include="IMessageManager.h" />
|
||||||
<ClInclude Include="MMC2.h" />
|
<ClInclude Include="MMC2.h" />
|
||||||
<ClInclude Include="Movie.h" />
|
<ClInclude Include="Movie.h" />
|
||||||
|
<ClInclude Include="NetMessage.h" />
|
||||||
<ClInclude Include="Snapshotable.h" />
|
<ClInclude Include="Snapshotable.h" />
|
||||||
<ClInclude Include="IVideoDevice.h" />
|
<ClInclude Include="IVideoDevice.h" />
|
||||||
<ClInclude Include="MapperFactory.h" />
|
<ClInclude Include="MapperFactory.h" />
|
||||||
|
@ -123,6 +133,7 @@
|
||||||
<ClInclude Include="stdafx.h" />
|
<ClInclude Include="stdafx.h" />
|
||||||
<ClInclude Include="targetver.h" />
|
<ClInclude Include="targetver.h" />
|
||||||
<ClInclude Include="UNROM.h" />
|
<ClInclude Include="UNROM.h" />
|
||||||
|
<ClInclude Include="VirtualController.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="APU.cpp" />
|
<ClCompile Include="APU.cpp" />
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
<Filter Include="Header Files\Mappers">
|
<Filter Include="Header Files\Mappers">
|
||||||
<UniqueIdentifier>{67b52e86-0ff2-4dbe-b9ed-7c92aace61d5}</UniqueIdentifier>
|
<UniqueIdentifier>{67b52e86-0ff2-4dbe-b9ed-7c92aace61d5}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Header Files\NetPlay">
|
||||||
|
<UniqueIdentifier>{43f375c6-d7c7-4bbf-8520-ad53e4e1b4a3}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="stdafx.h">
|
<ClInclude Include="stdafx.h">
|
||||||
|
@ -132,6 +135,33 @@
|
||||||
<ClInclude Include="Movie.h">
|
<ClInclude Include="Movie.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="GameConnection.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="GameClient.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="GameServer.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="VirtualController.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="NetMessage.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IGameBroadcaster.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="GameClientConnection.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="GameServerConnection.h">
|
||||||
|
<Filter>Header Files\NetPlay</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IMessageManager.h">
|
||||||
|
<Filter>Header Files\Interfaces</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="CPU.cpp">
|
<ClCompile Include="CPU.cpp">
|
||||||
|
|
62
Core/GameClient.h
Normal file
62
Core/GameClient.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GameClientConnection.h"
|
||||||
|
|
||||||
|
class GameClient
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
shared_ptr<Socket> _socket;
|
||||||
|
unique_ptr<GameClientConnection> _connection;
|
||||||
|
bool _connected = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~GameClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Connected()
|
||||||
|
{
|
||||||
|
return _connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connect(const char *host, u_short port)
|
||||||
|
{
|
||||||
|
_socket.reset(new Socket());
|
||||||
|
if(_socket->Connect(host, port)) {
|
||||||
|
_connection.reset(new GameClientConnection(_socket));
|
||||||
|
_connected = true;
|
||||||
|
} else {
|
||||||
|
Console::DisplayMessage(L"Could not connect to server.");
|
||||||
|
_connected = false;
|
||||||
|
_socket.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disconnect()
|
||||||
|
{
|
||||||
|
if(_connected) {
|
||||||
|
_connected = false;
|
||||||
|
_socket.reset();
|
||||||
|
_connection.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec()
|
||||||
|
{
|
||||||
|
if(_connected) {
|
||||||
|
if(!_connection->ConnectionError()) {
|
||||||
|
_connection->ProcessMessages();
|
||||||
|
_connection->SendInput();
|
||||||
|
} else {
|
||||||
|
_connected = false;
|
||||||
|
_socket.reset();
|
||||||
|
_connection.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
91
Core/GameServer.h
Normal file
91
Core/GameServer.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GameServerConnection.h"
|
||||||
|
|
||||||
|
class GameServer : public IGameBroadcaster
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
shared_ptr<Socket> _listener;
|
||||||
|
list<shared_ptr<GameServerConnection>> _openConnections;
|
||||||
|
bool _initialized = false;
|
||||||
|
|
||||||
|
void AcceptConnections()
|
||||||
|
{
|
||||||
|
while(true) {
|
||||||
|
shared_ptr<Socket> socket = _listener->Accept(NULL, NULL);
|
||||||
|
if(!socket->ConnectionError()) {
|
||||||
|
_openConnections.push_back(shared_ptr<GameServerConnection>(new GameServerConnection(socket, 1, this)));
|
||||||
|
std::cout << "Client connected." << std::endl;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_listener->Listen(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateConnections()
|
||||||
|
{
|
||||||
|
vector<shared_ptr<GameServerConnection>> connectionsToRemove;
|
||||||
|
for(shared_ptr<GameServerConnection> connection : _openConnections) {
|
||||||
|
if(connection->ConnectionError()) {
|
||||||
|
connectionsToRemove.push_back(connection);
|
||||||
|
} else {
|
||||||
|
connection->ProcessMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(shared_ptr<GameServerConnection> gameConnection : connectionsToRemove) {
|
||||||
|
_openConnections.remove(gameConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameServer()
|
||||||
|
{
|
||||||
|
ControlManager::RegisterBroadcaster(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~GameServer()
|
||||||
|
{
|
||||||
|
ControlManager::UnregisterBroadcaster(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
Console::DisplayMessage(L"Server started.");
|
||||||
|
_listener.reset(new Socket());
|
||||||
|
_listener->Bind(8888);
|
||||||
|
_listener->Listen(10);
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec()
|
||||||
|
{
|
||||||
|
if(_initialized) {
|
||||||
|
AcceptConnections();
|
||||||
|
UpdateConnections();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
_initialized = false;
|
||||||
|
_listener.reset();
|
||||||
|
Console::DisplayMessage(L"Server stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Started()
|
||||||
|
{
|
||||||
|
return _initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BroadcastInput(uint8_t inputData, uint8_t port)
|
||||||
|
{
|
||||||
|
for(shared_ptr<GameServerConnection> connection : _openConnections) {
|
||||||
|
if(!connection->ConnectionError()) {
|
||||||
|
//Send movie stream
|
||||||
|
connection->SendMovieData(inputData, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
95
Core/GameServerConnection.h
Normal file
95
Core/GameServerConnection.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GameConnection.h"
|
||||||
|
#include "IControlDevice.h"
|
||||||
|
#include "IGameBroadcaster.h"
|
||||||
|
|
||||||
|
class GameServerConnection : public GameConnection, public IControlDevice
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _controllerPort;
|
||||||
|
list<uint8_t> _inputData;
|
||||||
|
IGameBroadcaster* _gameBroadcaster;
|
||||||
|
bool _handshakeCompleted = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendGameState()
|
||||||
|
{
|
||||||
|
Console::Pause();
|
||||||
|
stringstream state;
|
||||||
|
Console::SaveState(state);
|
||||||
|
_handshakeCompleted = true;
|
||||||
|
ControlManager::RegisterControlDevice(this, _controllerPort);
|
||||||
|
Console::Resume();
|
||||||
|
|
||||||
|
uint32_t size = (uint32_t)state.tellp();
|
||||||
|
|
||||||
|
char* buffer = new char[size];
|
||||||
|
state.read(buffer, size);
|
||||||
|
SendNetMessage(SaveStateMessage(buffer, size));
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ProcessMessage(NetMessage* message)
|
||||||
|
{
|
||||||
|
switch(message->Type) {
|
||||||
|
case MessageType::HandShake:
|
||||||
|
//Send the game's current state to the client and register the controller
|
||||||
|
if(((HandShakeMessage*)message)->IsValid()) {
|
||||||
|
//SendPlayerNumber();
|
||||||
|
SendGameState();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::InputData:
|
||||||
|
uint8_t state = ((InputDataMessage*)message)->InputState;
|
||||||
|
if(_inputData.size() == 0 || state != _inputData.back()) {
|
||||||
|
_inputData.push_back(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameServerConnection(shared_ptr<Socket> socket, int controllerPort, IGameBroadcaster* gameBroadcaster) : GameConnection(socket)
|
||||||
|
{
|
||||||
|
//Server-side connection
|
||||||
|
_gameBroadcaster = gameBroadcaster;
|
||||||
|
|
||||||
|
_controllerPort = controllerPort;
|
||||||
|
|
||||||
|
Console::DisplayMessage(L"Player " + std::to_wstring(_controllerPort) + L" connected.");
|
||||||
|
|
||||||
|
ControlManager::BackupControlDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
~GameServerConnection()
|
||||||
|
{
|
||||||
|
Console::DisplayMessage(L"Player " + std::to_wstring(_controllerPort) + L" disconnected.");
|
||||||
|
|
||||||
|
ControlManager::RestoreControlDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendMovieData(uint8_t state, uint8_t port)
|
||||||
|
{
|
||||||
|
if(_handshakeCompleted) {
|
||||||
|
SendNetMessage(MovieDataMessage(state, port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonState GetButtonState()
|
||||||
|
{
|
||||||
|
ButtonState state;
|
||||||
|
uint32_t inputBufferSize = _inputData.size();
|
||||||
|
uint8_t stateData = 0;
|
||||||
|
if(inputBufferSize > 0) {
|
||||||
|
stateData = _inputData.front();
|
||||||
|
if(inputBufferSize > 1) {
|
||||||
|
//Always keep the last one the client sent, it will be used until a new one is received
|
||||||
|
_inputData.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.FromByte(stateData);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -14,6 +14,25 @@ struct ButtonState
|
||||||
|
|
||||||
bool Select = false;
|
bool Select = false;
|
||||||
bool Start = false;
|
bool Start = false;
|
||||||
|
|
||||||
|
uint8_t ToByte()
|
||||||
|
{
|
||||||
|
//"Button status for each controller is returned as an 8-bit report in the following order: A, B, Select, Start, Up, Down, Left, Right."
|
||||||
|
return (uint8_t)A | ((uint8_t)B << 1) | ((uint8_t)Select << 2) | ((uint8_t)Start << 3) |
|
||||||
|
((uint8_t)Up << 4) | ((uint8_t)Down << 5) | ((uint8_t)Left << 6) | ((uint8_t)Right << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FromByte(uint8_t stateData)
|
||||||
|
{
|
||||||
|
A = (stateData & 0x01) == 0x01;
|
||||||
|
B = (stateData & 0x02) == 0x02;
|
||||||
|
Select = (stateData & 0x04) == 0x04;
|
||||||
|
Start = (stateData & 0x08) == 0x08;
|
||||||
|
Up = (stateData & 0x10) == 0x10;
|
||||||
|
Down = (stateData & 0x20) == 0x20;
|
||||||
|
Left = (stateData & 0x40) == 0x40;
|
||||||
|
Right = (stateData & 0x80) == 0x80;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class IControlDevice
|
class IControlDevice
|
||||||
|
|
9
Core/IGameBroadcaster.h
Normal file
9
Core/IGameBroadcaster.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
class IGameBroadcaster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void BroadcastInput(uint8_t inputData, uint8_t port) = 0;
|
||||||
|
};
|
9
Core/IMessageManager.h
Normal file
9
Core/IMessageManager.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
class IMessageManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void DisplayMessage(wstring message) = 0;
|
||||||
|
};
|
|
@ -43,6 +43,7 @@ uint8_t Movie::GetState(uint8_t port)
|
||||||
|
|
||||||
if(_readPosition[port] >= _data.DataSize[port]) {
|
if(_readPosition[port] >= _data.DataSize[port]) {
|
||||||
//End of movie file
|
//End of movie file
|
||||||
|
Console::DisplayMessage(L"Movie ended.");
|
||||||
_playing = false;
|
_playing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +83,8 @@ void Movie::StartRecording(wstring filename, bool reset)
|
||||||
_recording = true;
|
_recording = true;
|
||||||
|
|
||||||
Console::Resume();
|
Console::Resume();
|
||||||
|
|
||||||
|
Console::DisplayMessage(L"Recording...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +116,7 @@ void Movie::PlayMovie(wstring filename)
|
||||||
}
|
}
|
||||||
_playing = true;
|
_playing = true;
|
||||||
Console::Resume();
|
Console::Resume();
|
||||||
|
Console::DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
150
Core/NetMessage.h
Normal file
150
Core/NetMessage.h
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "Console.h"
|
||||||
|
|
||||||
|
enum class MessageType : uint8_t
|
||||||
|
{
|
||||||
|
HandShake = 0,
|
||||||
|
SaveState = 1,
|
||||||
|
InputData = 2,
|
||||||
|
MovieData = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageType Type;
|
||||||
|
|
||||||
|
NetMessage(MessageType type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Send(Socket &socket) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HandShakeMessage : public NetMessage
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const int CurrentVersion = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t ProtocolVersion;
|
||||||
|
|
||||||
|
HandShakeMessage(char *readBuffer) : NetMessage(MessageType::HandShake)
|
||||||
|
{
|
||||||
|
ProtocolVersion = *(uint32_t*)readBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandShakeMessage() : NetMessage(MessageType::HandShake)
|
||||||
|
{
|
||||||
|
ProtocolVersion = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid()
|
||||||
|
{
|
||||||
|
return ProtocolVersion == CurrentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Send(Socket &socket)
|
||||||
|
{
|
||||||
|
uint32_t messageLength = sizeof(Type) + sizeof(ProtocolVersion);
|
||||||
|
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||||
|
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||||
|
socket.BufferedSend((char*)&ProtocolVersion, sizeof(ProtocolVersion));
|
||||||
|
socket.SendBuffer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputDataMessage : public NetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint8_t InputState;
|
||||||
|
|
||||||
|
InputDataMessage(char *readBuffer) : NetMessage(MessageType::InputData)
|
||||||
|
{
|
||||||
|
InputState = readBuffer[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDataMessage(uint8_t inputState) : NetMessage(MessageType::InputData)
|
||||||
|
{
|
||||||
|
InputState = inputState;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Send(Socket &socket)
|
||||||
|
{
|
||||||
|
uint32_t messageLength = sizeof(Type) + sizeof(InputState);
|
||||||
|
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||||
|
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||||
|
socket.BufferedSend((char*)&InputState, sizeof(InputState));
|
||||||
|
socket.SendBuffer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaveStateMessage : public NetMessage
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
char* _stateData;
|
||||||
|
uint32_t _dataSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SaveStateMessage(char *readBuffer, uint32_t bufferLength) : NetMessage(MessageType::SaveState)
|
||||||
|
{
|
||||||
|
_stateData = new char[bufferLength];
|
||||||
|
_dataSize = bufferLength;
|
||||||
|
memcpy(_stateData, readBuffer, bufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SaveStateMessage()
|
||||||
|
{
|
||||||
|
delete[] _stateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Send(Socket &socket)
|
||||||
|
{
|
||||||
|
uint32_t messageLength = _dataSize + sizeof(Type);
|
||||||
|
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||||
|
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||||
|
socket.BufferedSend(_stateData, _dataSize);
|
||||||
|
socket.SendBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadState()
|
||||||
|
{
|
||||||
|
Console::LoadState((uint8_t*)_stateData, _dataSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovieDataMessage : public NetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint8_t PortNumber;
|
||||||
|
uint8_t InputState;
|
||||||
|
|
||||||
|
MovieDataMessage(char *readBuffer) : NetMessage(MessageType::MovieData)
|
||||||
|
{
|
||||||
|
PortNumber = readBuffer[0];
|
||||||
|
InputState = readBuffer[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieDataMessage(uint8_t state, uint8_t port) : NetMessage(MessageType::MovieData)
|
||||||
|
{
|
||||||
|
PortNumber = port;
|
||||||
|
InputState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Send(Socket &socket)
|
||||||
|
{
|
||||||
|
uint32_t messageLength = sizeof(Type) + sizeof(PortNumber) + sizeof(InputState);
|
||||||
|
uint8_t type = (uint8_t)Type;
|
||||||
|
uint8_t portNumber = PortNumber;
|
||||||
|
uint8_t inputState = InputState;
|
||||||
|
|
||||||
|
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||||
|
socket.BufferedSend((char*)&type, sizeof(type));
|
||||||
|
socket.BufferedSend((char*)&portNumber, sizeof(portNumber));
|
||||||
|
socket.BufferedSend((char*)&inputState, sizeof(inputState));
|
||||||
|
socket.SendBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -19,6 +19,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
|
@ -32,4 +33,6 @@ using std::ofstream;
|
||||||
using std::wstring;
|
using std::wstring;
|
||||||
using std::exception;
|
using std::exception;
|
||||||
using std::atomic_flag;
|
using std::atomic_flag;
|
||||||
|
using std::atomic;
|
||||||
|
using std::list;
|
||||||
using std::max;
|
using std::max;
|
BIN
GUI/GUI.rc
BIN
GUI/GUI.rc
Binary file not shown.
|
@ -20,6 +20,8 @@ namespace NES {
|
||||||
_renderer.reset(new Renderer(_hWnd));
|
_renderer.reset(new Renderer(_hWnd));
|
||||||
_soundManager.reset(new SoundManager(_hWnd));
|
_soundManager.reset(new SoundManager(_hWnd));
|
||||||
|
|
||||||
|
Console::RegisterMessageManager(_renderer.get());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +60,9 @@ namespace NES {
|
||||||
|
|
||||||
int MainWindow::Run()
|
int MainWindow::Run()
|
||||||
{
|
{
|
||||||
#if _DEBUG
|
//#if _DEBUG
|
||||||
CreateConsole();
|
CreateConsole();
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
|
@ -83,14 +85,21 @@ namespace NES {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_renderer->Render();
|
_renderer->Render();
|
||||||
|
_gameServer.Exec();
|
||||||
|
_gameClient.Exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetMenuEnabled(ID_NETPLAY_STARTSERVER, !_gameServer.Started() && !_gameClient.Connected());
|
||||||
|
SetMenuEnabled(ID_NETPLAY_STOPSERVER, _gameServer.Started() && !_gameClient.Connected());
|
||||||
|
|
||||||
|
SetMenuEnabled(ID_NETPLAY_CONNECT, !_gameServer.Started() && !_gameClient.Connected());
|
||||||
|
SetMenuEnabled(ID_NETPLAY_DISCONNECT, !_gameServer.Started() && _gameClient.Connected());
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
|
||||||
|
|
||||||
if(_playingMovie) {
|
if(_playingMovie) {
|
||||||
if(!Movie::Playing()) {
|
if(!Movie::Playing()) {
|
||||||
_playingMovie = false;
|
_playingMovie = false;
|
||||||
_renderer->DisplayMessage(L"Movie ended.", 3000);
|
|
||||||
|
|
||||||
//Pause game
|
//Pause game
|
||||||
Stop(false);
|
Stop(false);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +164,35 @@ namespace NES {
|
||||||
return (INT_PTR)FALSE;
|
return (INT_PTR)FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INT_PTR CALLBACK MainWindow::ConnectWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(lParam);
|
||||||
|
wchar_t hostName[1000];
|
||||||
|
wstring lastHost;
|
||||||
|
|
||||||
|
switch(message) {
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
|
||||||
|
lastHost = ConfigManager::GetValue<wstring>(Config::LastNetPlayHost);
|
||||||
|
SetDlgItemText(hDlg, IDC_HOSTNAME, lastHost.size() > 0 ? lastHost.c_str() : L"localhost");
|
||||||
|
return (INT_PTR)TRUE;
|
||||||
|
|
||||||
|
case WM_COMMAND:
|
||||||
|
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||||
|
if(LOWORD(wParam) == IDOK) {
|
||||||
|
GetDlgItemText(hDlg, IDC_HOSTNAME, (LPWSTR)hostName, 1000);
|
||||||
|
ConfigManager::SetValue(Config::LastNetPlayHost, wstring(hostName));
|
||||||
|
MainWindow::GetInstance()->_gameClient.Connect(utf8util::UTF8FromUTF16(hostName).c_str(), 8888);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndDialog(hDlg, LOWORD(wParam));
|
||||||
|
return (INT_PTR)TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (INT_PTR)FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
INT_PTR CALLBACK MainWindow::ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
INT_PTR CALLBACK MainWindow::ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(lParam);
|
UNREFERENCED_PARAMETER(lParam);
|
||||||
|
@ -460,7 +498,7 @@ namespace NES {
|
||||||
void MainWindow::SelectSaveSlot(int slot)
|
void MainWindow::SelectSaveSlot(int slot)
|
||||||
{
|
{
|
||||||
_currentSaveSlot = slot % 5;
|
_currentSaveSlot = slot % 5;
|
||||||
_renderer->DisplayMessage(L"Savestate slot: " + std::to_wstring(_currentSaveSlot + 1), 3000);
|
_renderer->DisplayMessage(L"Savestate slot: " + std::to_wstring(_currentSaveSlot + 1));
|
||||||
|
|
||||||
SetMenuCheck(ID_SAVESTATESLOT_1, _currentSaveSlot == 0);
|
SetMenuCheck(ID_SAVESTATESLOT_1, _currentSaveSlot == 0);
|
||||||
SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1);
|
SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1);
|
||||||
|
@ -520,15 +558,10 @@ namespace NES {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ID_FILE_QUICKLOAD:
|
case ID_FILE_QUICKLOAD:
|
||||||
if(mainWindow->_console->LoadState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1))) {
|
mainWindow->_console->LoadState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
||||||
mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000);
|
|
||||||
} else {
|
|
||||||
mainWindow->_renderer->DisplayMessage(L"Slot is empty.", 3000);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ID_FILE_QUICKSAVE:
|
case ID_FILE_QUICKSAVE:
|
||||||
mainWindow->_console->SaveState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
mainWindow->_console->SaveState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
||||||
mainWindow->_renderer->DisplayMessage(L"State saved.", 3000);
|
|
||||||
break;
|
break;
|
||||||
case ID_CHANGESLOT:
|
case ID_CHANGESLOT:
|
||||||
mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1);
|
mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1);
|
||||||
|
@ -563,7 +596,6 @@ namespace NES {
|
||||||
case ID_MOVIES_PLAY:
|
case ID_MOVIES_PLAY:
|
||||||
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), false);
|
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), false);
|
||||||
if(!filename.empty()) {
|
if(!filename.empty()) {
|
||||||
mainWindow->_renderer->DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true), 3000);
|
|
||||||
mainWindow->_playingMovie = true;
|
mainWindow->_playingMovie = true;
|
||||||
Movie::Play(filename);
|
Movie::Play(filename);
|
||||||
}
|
}
|
||||||
|
@ -573,7 +605,6 @@ namespace NES {
|
||||||
case ID_RECORDFROM_NOW:
|
case ID_RECORDFROM_NOW:
|
||||||
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), true, L"nmo");
|
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), true, L"nmo");
|
||||||
if(!filename.empty()) {
|
if(!filename.empty()) {
|
||||||
mainWindow->_renderer->DisplayMessage(L"Recording...", 3000);
|
|
||||||
Movie::Record(filename, wmId == ID_RECORDFROM_START);
|
Movie::Record(filename, wmId == ID_RECORDFROM_START);
|
||||||
}
|
}
|
||||||
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, true);
|
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, true);
|
||||||
|
@ -585,6 +616,20 @@ namespace NES {
|
||||||
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, false);
|
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ID_NETPLAY_STARTSERVER:
|
||||||
|
mainWindow->_gameServer.Start();
|
||||||
|
break;
|
||||||
|
case ID_NETPLAY_STOPSERVER:
|
||||||
|
mainWindow->_gameServer.Stop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ID_NETPLAY_CONNECT:
|
||||||
|
DialogBox(nullptr, MAKEINTRESOURCE(IDD_CONNECT), hWnd, ConnectWndProc);
|
||||||
|
break;
|
||||||
|
case ID_NETPLAY_DISCONNECT:
|
||||||
|
mainWindow->_gameClient.Disconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
case ID_TESTS_RUNTESTS:
|
case ID_TESTS_RUNTESTS:
|
||||||
mainWindow->RunTests();
|
mainWindow->RunTests();
|
||||||
break;
|
break;
|
||||||
|
@ -641,6 +686,7 @@ namespace NES {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
|
mainWindow->_gameClient.Disconnect();
|
||||||
mainWindow->Stop(true);
|
mainWindow->Stop(true);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "SoundManager.h"
|
#include "SoundManager.h"
|
||||||
#include "../Core/Console.h"
|
#include "../Core/Console.h"
|
||||||
#include "../Utilities/ConfigManager.h"
|
#include "../Utilities/ConfigManager.h"
|
||||||
|
#include "../Core/GameServer.h"
|
||||||
|
#include "../Core/GameClient.h"
|
||||||
|
|
||||||
namespace NES {
|
namespace NES {
|
||||||
class MainWindow
|
class MainWindow
|
||||||
|
@ -22,6 +24,9 @@ namespace NES {
|
||||||
wstring _currentROM;
|
wstring _currentROM;
|
||||||
wstring _currentROMName;
|
wstring _currentROMName;
|
||||||
|
|
||||||
|
GameClient _gameClient;
|
||||||
|
GameServer _gameServer;
|
||||||
|
|
||||||
int _currentSaveSlot = 0;
|
int _currentSaveSlot = 0;
|
||||||
|
|
||||||
bool _playingMovie = false;
|
bool _playingMovie = false;
|
||||||
|
@ -31,6 +36,7 @@ namespace NES {
|
||||||
HRESULT InitWindow();
|
HRESULT InitWindow();
|
||||||
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
|
static INT_PTR CALLBACK ConnectWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
static INT_PTR CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
static INT_PTR CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
|
|
@ -276,10 +276,10 @@ namespace NES
|
||||||
return shaderResourceView;
|
return shaderResourceView;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::DisplayMessage(wstring text, uint32_t duration)
|
void Renderer::DisplayMessage(wstring text)
|
||||||
{
|
{
|
||||||
_displayMessage = text;
|
_displayMessage = text;
|
||||||
_displayTimestamp = timeGetTime() + duration;
|
_displayTimestamp = timeGetTime() + 3000; //3 secs
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::DrawNESScreen()
|
void Renderer::DrawNESScreen()
|
||||||
|
@ -335,7 +335,7 @@ namespace NES
|
||||||
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 142, (float)_hdScreenHeight / 2 - 77), Colors::Black, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 142, (float)_hdScreenHeight / 2 - 77), Colors::Black, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
||||||
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 145, (float)_hdScreenHeight / 2 - 80), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 145, (float)_hdScreenHeight / 2 - 80), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
HRESULT Renderer::CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
|
HRESULT Renderer::CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
|
||||||
{
|
{
|
||||||
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
|
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||||
|
@ -353,7 +353,7 @@ namespace NES
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void Renderer::Render()
|
void Renderer::Render()
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "DirectXTK\SpriteBatch.h"
|
#include "DirectXTK\SpriteBatch.h"
|
||||||
#include "DirectXTK\SpriteFont.h"
|
#include "DirectXTK\SpriteFont.h"
|
||||||
#include "../Core/IVideoDevice.h"
|
#include "../Core/IVideoDevice.h"
|
||||||
|
#include "../Core/IMessageManager.h"
|
||||||
|
|
||||||
using namespace DirectX;
|
using namespace DirectX;
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ namespace NES {
|
||||||
ShowPauseScreen = 2,
|
ShowPauseScreen = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Renderer : IVideoDevice
|
class Renderer : public IVideoDevice, public IMessageManager
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
HWND _hWnd = nullptr;
|
HWND _hWnd = nullptr;
|
||||||
|
@ -39,7 +40,7 @@ namespace NES {
|
||||||
byte* _overlayBuffer = nullptr;
|
byte* _overlayBuffer = nullptr;
|
||||||
|
|
||||||
std::unique_ptr<SpriteBatch> _spriteBatch;
|
std::unique_ptr<SpriteBatch> _spriteBatch;
|
||||||
ID3D11PixelShader* _pixelShader = nullptr;
|
//ID3D11PixelShader* _pixelShader = nullptr;
|
||||||
|
|
||||||
uint32_t _screenWidth;
|
uint32_t _screenWidth;
|
||||||
uint32_t _screenHeight;
|
uint32_t _screenHeight;
|
||||||
|
@ -63,7 +64,7 @@ namespace NES {
|
||||||
void DrawNESScreen();
|
void DrawNESScreen();
|
||||||
void DrawPauseScreen();
|
void DrawPauseScreen();
|
||||||
|
|
||||||
HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
|
//HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Renderer(HWND hWnd);
|
Renderer(HWND hWnd);
|
||||||
|
@ -71,7 +72,7 @@ namespace NES {
|
||||||
|
|
||||||
void Render();
|
void Render();
|
||||||
|
|
||||||
void Renderer::DisplayMessage(wstring text, uint32_t duration);
|
void DisplayMessage(wstring text);
|
||||||
|
|
||||||
void SetFlags(uint32_t flags)
|
void SetFlags(uint32_t flags)
|
||||||
{
|
{
|
||||||
|
|
BIN
GUI/resource.h
BIN
GUI/resource.h
Binary file not shown.
|
@ -47,6 +47,11 @@
|
||||||
#include <Fcntl.h>
|
#include <Fcntl.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using std::thread;
|
using std::thread;
|
||||||
|
using std::list;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
// TODO: reference additional headers your program requires here
|
// TODO: reference additional headers your program requires here
|
||||||
|
|
|
@ -11,6 +11,7 @@ string ConfigManager::ConfigTagNames[] = {
|
||||||
"MRU3",
|
"MRU3",
|
||||||
"MRU4",
|
"MRU4",
|
||||||
"LastGameFolder",
|
"LastGameFolder",
|
||||||
|
"LastNetPlayHost",
|
||||||
"Player1_ButtonA",
|
"Player1_ButtonA",
|
||||||
"Player1_ButtonB",
|
"Player1_ButtonB",
|
||||||
"Player1_Select",
|
"Player1_Select",
|
||||||
|
|
|
@ -14,6 +14,7 @@ enum class Config
|
||||||
MRU3,
|
MRU3,
|
||||||
MRU4,
|
MRU4,
|
||||||
LastGameFolder,
|
LastGameFolder,
|
||||||
|
LastNetPlayHost,
|
||||||
Player1_ButtonA,
|
Player1_ButtonA,
|
||||||
Player1_ButtonB,
|
Player1_ButtonB,
|
||||||
Player1_Select,
|
Player1_Select,
|
||||||
|
|
35
Utilities/SimpleLock.h
Normal file
35
Utilities/SimpleLock.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
class SimpleLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
atomic_flag _lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Acquire()
|
||||||
|
{
|
||||||
|
while(_lock.test_and_set());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFree()
|
||||||
|
{
|
||||||
|
if(!_lock.test_and_set()) {
|
||||||
|
_lock.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForRelease()
|
||||||
|
{
|
||||||
|
//Wait until we are able to grab a lock, and then release it again
|
||||||
|
Acquire();
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release()
|
||||||
|
{
|
||||||
|
_lock.clear();
|
||||||
|
}
|
||||||
|
};
|
206
Utilities/Socket.h
Normal file
206
Utilities/Socket.h
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma comment(lib,"ws2_32.lib") //Winsock Library
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
class Socket
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SOCKET _socket = INVALID_SOCKET;
|
||||||
|
bool _connectionError = false;
|
||||||
|
bool _cleanupWSA = false;
|
||||||
|
char* _sendBuffer;
|
||||||
|
int _bufferPosition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Socket()
|
||||||
|
{
|
||||||
|
WSADATA wsaDat;
|
||||||
|
if(WSAStartup(MAKEWORD(2, 2), &wsaDat) != 0) {
|
||||||
|
std::cout << "WSAStartup failed." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
_cleanupWSA = true;
|
||||||
|
|
||||||
|
_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if(_socket == INVALID_SOCKET) {
|
||||||
|
std::cout << "Socket creation failed." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
SetSocketOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendBuffer = new char[200000];
|
||||||
|
_bufferPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket(SOCKET socket)
|
||||||
|
{
|
||||||
|
_socket = socket;
|
||||||
|
|
||||||
|
if(socket == INVALID_SOCKET) {
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
SetSocketOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendBuffer = new char[200000];
|
||||||
|
_bufferPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Socket()
|
||||||
|
{
|
||||||
|
if(_socket != INVALID_SOCKET) {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
if(_cleanupWSA) {
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] _sendBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSocketOptions()
|
||||||
|
{
|
||||||
|
//Non-blocking mode
|
||||||
|
u_long iMode = 1;
|
||||||
|
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||||
|
|
||||||
|
//Set send/recv buffers to 256k
|
||||||
|
int bufferSize = 0x40000;
|
||||||
|
setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(int));
|
||||||
|
setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(int));
|
||||||
|
|
||||||
|
//Disable nagle's algorithm to improve latency
|
||||||
|
u_long value = 1;
|
||||||
|
setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConnectionErrorFlag()
|
||||||
|
{
|
||||||
|
_connectionError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close()
|
||||||
|
{
|
||||||
|
std::cout << "Client disconnected!\r\n";
|
||||||
|
shutdown(_socket, SD_SEND);
|
||||||
|
closesocket(_socket);
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionError()
|
||||||
|
{
|
||||||
|
return _connectionError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bind(u_short port)
|
||||||
|
{
|
||||||
|
SOCKADDR_IN serverInf;
|
||||||
|
serverInf.sin_family = AF_INET;
|
||||||
|
serverInf.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
serverInf.sin_port = htons(port);
|
||||||
|
|
||||||
|
if(bind(_socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR) {
|
||||||
|
std::cout << "Unable to bind socket." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Connect(const char* hostname, u_short port)
|
||||||
|
{
|
||||||
|
// Resolve IP address for hostname
|
||||||
|
struct hostent *host;
|
||||||
|
if((host = gethostbyname(hostname)) == NULL) {
|
||||||
|
std::cout << "Failed to resolve hostname." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
// Setup our socket address structure
|
||||||
|
SOCKADDR_IN SockAddr;
|
||||||
|
SockAddr.sin_port = htons(port);
|
||||||
|
SockAddr.sin_family = AF_INET;
|
||||||
|
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
|
||||||
|
|
||||||
|
u_long iMode = 0;
|
||||||
|
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||||
|
|
||||||
|
// Attempt to connect to server
|
||||||
|
if(connect(_socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR) {
|
||||||
|
std::cout << "Failed to establish connection with server." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
iMode = 1;
|
||||||
|
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listen(int backlog)
|
||||||
|
{
|
||||||
|
if(listen(_socket, backlog) == SOCKET_ERROR) {
|
||||||
|
std::cout << "listen failed." << std::endl;
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<Socket> Accept(sockaddr* addr, int *addrlen)
|
||||||
|
{
|
||||||
|
SOCKET socket = accept(_socket, addr, addrlen);
|
||||||
|
return shared_ptr<Socket>(new Socket(socket));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Send(char *buf, int len, int flags)
|
||||||
|
{
|
||||||
|
int nError;
|
||||||
|
int returnVal;
|
||||||
|
do {
|
||||||
|
//Loop until everything has been sent (shouldn't loop at all in the vast majority of cases)
|
||||||
|
returnVal = send(_socket, buf, len, flags);
|
||||||
|
|
||||||
|
nError = WSAGetLastError();
|
||||||
|
if(nError != 0) {
|
||||||
|
if(nError != WSAEWOULDBLOCK) {
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
} else {
|
||||||
|
if(returnVal > 0) {
|
||||||
|
//Sent partial data, adjust pointer & length
|
||||||
|
buf += returnVal;
|
||||||
|
len -= returnVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(nError == WSAEWOULDBLOCK);
|
||||||
|
|
||||||
|
return returnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferedSend(char *buf, int len)
|
||||||
|
{
|
||||||
|
memcpy(_sendBuffer+_bufferPosition, buf, len);
|
||||||
|
_bufferPosition += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendBuffer()
|
||||||
|
{
|
||||||
|
Send(_sendBuffer, _bufferPosition, 0);
|
||||||
|
_bufferPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Recv(char *buf, int len, int flags)
|
||||||
|
{
|
||||||
|
static ofstream received("received.log", ios::out | ios::binary);
|
||||||
|
int returnVal = recv(_socket, buf, len, flags);
|
||||||
|
|
||||||
|
int nError = WSAGetLastError();
|
||||||
|
if(nError != WSAEWOULDBLOCK && nError != 0) {
|
||||||
|
SetConnectionErrorFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnVal;
|
||||||
|
}
|
||||||
|
};
|
|
@ -73,6 +73,9 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ConfigManager.h" />
|
<ClInclude Include="ConfigManager.h" />
|
||||||
<ClInclude Include="FolderUtilities.h" />
|
<ClInclude Include="FolderUtilities.h" />
|
||||||
|
<ClInclude Include="NATPortMapper.h" />
|
||||||
|
<ClInclude Include="SimpleLock.h" />
|
||||||
|
<ClInclude Include="Socket.h" />
|
||||||
<ClInclude Include="stdafx.h" />
|
<ClInclude Include="stdafx.h" />
|
||||||
<ClInclude Include="targetver.h" />
|
<ClInclude Include="targetver.h" />
|
||||||
<ClInclude Include="Timer.h" />
|
<ClInclude Include="Timer.h" />
|
||||||
|
|
|
@ -42,6 +42,15 @@
|
||||||
<ClInclude Include="FolderUtilities.h">
|
<ClInclude Include="FolderUtilities.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Socket.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="SimpleLock.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="NATPortMapper.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
using std::atomic_flag;
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using std::ifstream;
|
using std::ifstream;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
Loading…
Add table
Reference in a new issue