/*
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see .
*/
#include "maplelink.h"
#include "cfg/option.h"
#include "hw/maple/maple_if.h"
std::mutex& MapleLink::mutex()
{
static std::mutex *m = new std::mutex();
return *m;
}
MapleLink::LinkTable& MapleLink::links()
{
static LinkTable *l = new LinkTable();
return *l;
}
namespace
{
struct GameState
{
GameState()
{
EventManager::listen(Event::Start, [](Event, void*) {
started = true;
});
EventManager::listen(Event::Terminate, [](Event, void*) {
started = false;
});
}
static bool started;
static GameState instance;
};
bool GameState::started;
GameState GameState::instance;
}
std::size_t MapleLink::activeLinkCount(int bus) const
{
std::size_t count = 0;
if (bus >= 0 && bus < (int)links().size())
{
std::lock_guard _(mutex());
for (const std::list& list : links()[bus]) {
if (!list.empty() && list.front().get() == this)
++count;
}
}
return count;
}
bool MapleLink::isGameStarted() const {
return GameState::started;
}
void MapleLink::registerLink(int bus, int port)
{
if (bus >= 0 && bus < (int)links().size()
&& port >= 0 && port < (int)links()[0].size())
{
if (this->bus != bus) {
this->bus = bus;
this->ports = 0;
}
this->ports |= 1 << port;
std::lock_guard _(mutex());
links()[bus][port].push_front(shared_from_this());
}
}
void MapleLink::unregisterLink(int bus, int port)
{
if (bus >= 0 && bus < (int)links().size()
&& port >= 0 && port < (int)links()[0].size())
{
if (this->bus != bus) {
this->bus = -1;
this->ports = 0;
}
else {
this->ports &= ~(1 << port);
}
std::lock_guard _(mutex());
links()[bus][port].remove_if([this](const Ptr& item) { return item.get() == this; });
}
}
bool MapleLink::StorageEnabled()
{
std::lock_guard _(mutex());
for (const auto& ports : links())
{
for (const std::list& list : ports)
if (!list.empty() && list.front()->storageEnabled())
return true;
}
return false;
}
BaseMapleLink::BaseMapleLink(bool storageSupported)
: storageSupported(storageSupported)
{
EventManager::listen(Event::LoadState, eventHandler, this);
EventManager::listen(Event::Start, eventHandler, this);
EventManager::listen(Event::Terminate, eventHandler, this);
if (isGameStarted())
vmuStorage = storageSupported && config::UsePhysicalVmuMemory;
}
BaseMapleLink::~BaseMapleLink()
{
EventManager::unlisten(Event::LoadState, eventHandler, this);
EventManager::unlisten(Event::Start, eventHandler, this);
EventManager::unlisten(Event::Terminate, eventHandler, this);
}
void BaseMapleLink::gameStarted() {
vmuStorage = storageSupported && config::UsePhysicalVmuMemory && isConnected();
}
void BaseMapleLink::eventHandler(Event event, void *p)
{
BaseMapleLink *self = (BaseMapleLink *)p;
switch (event)
{
case Event::Start:
self->gameStarted();
break;
case Event::Terminate:
self->gameTermination();
break;
case Event::LoadState:
if (self->vmuStorage) {
WARN_LOG(INPUT, "State loaded but VMU has storage enabled");
self->disableStorage();
}
break;
default:
break;
}
}
void BaseMapleLink::disableStorage()
{
if (!vmuStorage)
return;
vmuStorage = false;
if (isGameStarted())
{
emu.run([bus=this->bus, ports=this->ports]() {
if (bus != -1 && (ports & 1))
maple_ReconnectDevice(bus, 0);
if (bus != -1 && (ports & 2))
maple_ReconnectDevice(bus, 1);
});
}
}
bool BaseMapleLink::storageEnabled()
{
if (!isConnected())
return false;
if (!isGameStarted())
return storageSupported && config::UsePhysicalVmuMemory;
else
return vmuStorage;
}
bool BaseMapleLink::handleGetLastError(const MapleMsg&)
{
// Just acknowledge by default
return true;
}