/* 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; }