diff --git a/Core/AutomaticRomTest.cpp b/Core/AutomaticRomTest.cpp index d6002e5a..9deb8c27 100644 --- a/Core/AutomaticRomTest.cpp +++ b/Core/AutomaticRomTest.cpp @@ -6,6 +6,7 @@ #include "PPU.h" #include "VideoDecoder.h" #include "StandardController.h" +#include "NotificationManager.h" AutomaticRomTest::AutomaticRomTest() { @@ -104,6 +105,7 @@ int32_t AutomaticRomTest::Run(string filename) { EmulationSettings::SetMasterVolume(0); _console.reset(new Console()); + _console->GetNotificationManager()->RegisterNotificationListener(shared_from_this()); if(_console->Initialize(filename)) { _console->GetControlManager()->RegisterInputProvider(this); diff --git a/Core/AutomaticRomTest.h b/Core/AutomaticRomTest.h index 763d4a7e..3784d399 100644 --- a/Core/AutomaticRomTest.h +++ b/Core/AutomaticRomTest.h @@ -6,7 +6,7 @@ class Console; -class AutomaticRomTest : public INotificationListener, public IInputProvider +class AutomaticRomTest : public INotificationListener, public IInputProvider, public std::enable_shared_from_this { private: shared_ptr _console; diff --git a/Core/CheatManager.cpp b/Core/CheatManager.cpp index 0999f612..5a6d6a92 100644 --- a/Core/CheatManager.cpp +++ b/Core/CheatManager.cpp @@ -3,6 +3,7 @@ #include "CheatManager.h" #include "Console.h" #include "MessageManager.h" +#include "NotificationManager.h" CheatManager::CheatManager(shared_ptr console) { @@ -97,7 +98,7 @@ void CheatManager::AddCode(CodeInfo &code) } else { _absoluteCheatCodes.push_back(code); } - MessageManager::SendNotification(ConsoleNotificationType::CheatAdded); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatAdded); } void CheatManager::AddGameGenieCode(string code) @@ -138,7 +139,7 @@ void CheatManager::ClearCodes() _absoluteCheatCodes.clear(); if(cheatRemoved) { - MessageManager::SendNotification(ConsoleNotificationType::CheatRemoved); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatRemoved); } } diff --git a/Core/Console.cpp b/Core/Console.cpp index db5a1b76..e85316cf 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -43,6 +43,7 @@ #include "VideoDecoder.h" #include "VideoRenderer.h" #include "DebugHud.h" +#include "NotificationManager.h" Console::Console() { @@ -57,6 +58,7 @@ Console::~Console() void Console::Init() { + _notificationManager.reset(new NotificationManager()); _saveStateManager.reset(new SaveStateManager(shared_from_this())); _videoRenderer.reset(new VideoRenderer(shared_from_this())); _videoDecoder.reset(new VideoDecoder(shared_from_this())); @@ -235,7 +237,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) } //Send notification only if a game was already running and we successfully loaded the new one - MessageManager::SendNotification(ConsoleNotificationType::GameStopped, (void*)1); + _notificationManager->SendNotification(ConsoleNotificationType::GameStopped, (void*)1); } if(isDifferentGame) { @@ -321,7 +323,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) ResetComponents(false); _rewindManager.reset(new RewindManager(shared_from_this())); - MessageManager::RegisterNotificationListener(_rewindManager); + _notificationManager->RegisterNotificationListener(_rewindManager); _videoDecoder->StartThread(); @@ -377,6 +379,11 @@ shared_ptr Console::GetSoundMixer() return _soundMixer; } +shared_ptr Console::GetNotificationManager() +{ + return _notificationManager; +} + BaseMapper* Console::GetMapper() { return _mapper.get(); @@ -473,7 +480,8 @@ void Console::ResetComponents(bool softReset) KeyManager::UpdateDevices(); - MessageManager::SendNotification(softReset ? ConsoleNotificationType::GameReset : ConsoleNotificationType::GameLoaded); + //This notification MUST be sent before the UpdateInputState() below to allow MovieRecorder to grab the first frame's worth of inputs + _notificationManager->SendNotification(softReset ? ConsoleNotificationType::GameReset : ConsoleNotificationType::GameLoaded); if(softReset) { shared_ptr debugger = _debugger; @@ -603,7 +611,7 @@ void Console::Run() bool paused = EmulationSettings::IsPaused(); if(paused && !_stop) { - MessageManager::SendNotification(ConsoleNotificationType::GamePaused); + _notificationManager->SendNotification(ConsoleNotificationType::GamePaused); //Prevent audio from looping endlessly while game is paused _soundMixer->StopAudio(); @@ -625,7 +633,7 @@ void Console::Run() PlatformUtilities::DisableScreensaver(); _runLock.Acquire(); - MessageManager::SendNotification(ConsoleNotificationType::GameResumed); + _notificationManager->SendNotification(ConsoleNotificationType::GameResumed); lastFrameTimer.Reset(); } @@ -668,7 +676,7 @@ void Console::Run() _running = false; - MessageManager::SendNotification(ConsoleNotificationType::BeforeEmulationStop); + _notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop); if(!crashed) { _saveStateManager->SaveRecentGame(GetMapperInfo().RomName, _romFilepath, _patchFilename); @@ -703,8 +711,8 @@ void Console::Run() _emulationThreadId = std::thread::id(); - MessageManager::SendNotification(ConsoleNotificationType::GameStopped); - MessageManager::SendNotification(ConsoleNotificationType::EmulationStopped); + _notificationManager->SendNotification(ConsoleNotificationType::GameStopped); + _notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped); } bool Console::IsRunning() @@ -747,7 +755,7 @@ void Console::UpdateNesModel(bool sendNotification) _apu->SetNesModel(model); if(configChanged && sendNotification) { - MessageManager::SendNotification(ConsoleNotificationType::ConfigChanged); + _notificationManager->SendNotification(ConsoleNotificationType::ConfigChanged); } } @@ -813,7 +821,7 @@ void Console::LoadState(istream &loadStream, uint32_t stateVersion) debugger->ResetCounters(); } - MessageManager::SendNotification(ConsoleNotificationType::StateLoaded); + _notificationManager->SendNotification(ConsoleNotificationType::StateLoaded); } } diff --git a/Core/Console.h b/Core/Console.h index 5db99d66..e513786e 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -25,6 +25,7 @@ class VideoDecoder; class VideoRenderer; class DebugHud; class SoundMixer; +class NotificationManager; struct HdPackData; enum class NesModel; @@ -59,6 +60,7 @@ private: shared_ptr _cheatManager; shared_ptr _debugHud; shared_ptr _soundMixer; + shared_ptr _notificationManager; shared_ptr _hdPackBuilder; shared_ptr _hdData; @@ -96,6 +98,7 @@ public: shared_ptr GetVideoRenderer(); shared_ptr GetDebugHud(); shared_ptr GetSoundMixer(); + shared_ptr GetNotificationManager(); void ProcessCpuClock(); CPU* GetCpu(); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 91ed94fa..50dc732f 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -546,6 +546,7 @@ + @@ -959,15 +960,20 @@ + + + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 52735600..c7ebde96 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1453,6 +1453,9 @@ Nes\Mappers\Unif + + Misc + @@ -1722,5 +1725,20 @@ Misc + + Misc + + + Nes + + + HdPacks + + + Nes\RomLoader + + + Nes\RomLoader + \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index ab9c2d2d..c638031d 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -27,6 +27,7 @@ #include "TraceLogger.h" #include "Breakpoint.h" #include "CodeDataLogger.h" +#include "NotificationManager.h" #ifndef UINT32_MAX #define UINT32_MAX ((uint32_t)-1) @@ -445,7 +446,7 @@ void Debugger::ProcessPpuCycle() int32_t currentCycle = (_ppu->GetCurrentCycle() << 9) + _ppu->GetCurrentScanline(); for(auto updateCycle : _ppuViewerUpdateCycle) { if(updateCycle.second == currentCycle) { - MessageManager::SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame, (void*)(uint64_t)updateCycle.first); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame, (void*)(uint64_t)updateCycle.first); } } @@ -691,7 +692,7 @@ bool Debugger::SleepUntilResume(BreakSource source) if(_preventResume == 0) { _console->GetSoundMixer()->StopAudio(); - MessageManager::SendNotification(ConsoleNotificationType::CodeBreak, (void*)(uint64_t)source); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, (void*)(uint64_t)source); ProcessEvent(EventType::CodeBreak); _stepOverAddr = -1; if(CheckFlag(DebuggerFlags::PpuPartialDraw)) { @@ -1340,7 +1341,7 @@ void Debugger::ProcessEvent(EventType type) _memoryDumper->GatherChrPaletteInfo(); } else if(type == EventType::StartFrame) { //Update the event viewer - MessageManager::SendNotification(ConsoleNotificationType::EventViewerDisplayFrame); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerDisplayFrame); //Clear the current frame/event log if(CheckFlag(DebuggerFlags::PpuPartialDraw)) { diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 805636b0..7a3eb1f1 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -1039,7 +1039,8 @@ public: UpdateEffectiveOverclockRate(); - MessageManager::SendNotification(ConsoleNotificationType::ConfigChanged); + //TODOCONSOLE + //MessageManager::SendNotification(ConsoleNotificationType::ConfigChanged); MessageManager::DisplayMessage("ClockRate", std::to_string((uint32_t)EmulationSettings::GetOverclockRate()) + "%"); } diff --git a/Core/FdsLoader.cpp b/Core/FdsLoader.cpp new file mode 100644 index 00000000..705494b9 --- /dev/null +++ b/Core/FdsLoader.cpp @@ -0,0 +1,160 @@ +#include "stdafx.h" +#include "FdsLoader.h" +#include +#include "../Utilities/FolderUtilities.h" +#include "../Utilities/CRC32.h" +#include "../Utilities/sha1.h" +#include "RomData.h" +#include "MessageManager.h" +#include "MapperFactory.h" +#include "GameDatabase.h" +#include "EmulationSettings.h" + +void FdsLoader::AddGaps(vector& diskSide, uint8_t * readBuffer) +{ + //Start image with 28300 bits of gap + diskSide.insert(diskSide.end(), 28300 / 8, 0); + + for(size_t j = 0; j < FdsDiskSideCapacity;) { + uint8_t blockType = readBuffer[j]; + uint32_t blockLength = 1; + switch(blockType) { + case 1: blockLength = 56; break; //Disk header + case 2: blockLength = 2; break; //File count + case 3: blockLength = 16; break; //File header + case 4: blockLength = 1 + readBuffer[j - 3] + readBuffer[j - 2] * 0x100; break; + default: return; //End parsing when we encounter an invalid block type (This is what Nestopia apppears to do) + } + + if(blockType == 0) { + diskSide.push_back(blockType); + } else { + diskSide.push_back(0x80); + diskSide.insert(diskSide.end(), &readBuffer[j], &readBuffer[j] + blockLength); + + //Fake CRC value + diskSide.push_back(0x4D); + diskSide.push_back(0x62); + + //Insert 976 bits of gap after a block + diskSide.insert(diskSide.end(), 976 / 8, 0); + } + + j += blockLength; + } +} + +vector FdsLoader::LoadBios() +{ + //For FDS, the PRG ROM is the FDS BIOS (8k) + vector biosData; + + ifstream biosFile(FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "FdsBios.bin"), ios::in | ios::binary); + if(biosFile) { + return vector(std::istreambuf_iterator(biosFile), {}); + } else { + biosFile.open(FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "disksys.rom"), ios::in | ios::binary); + if(biosFile) { + return vector(std::istreambuf_iterator(biosFile), {}); + } + } + return {}; +} + +vector FdsLoader::RebuildFdsFile(vector> diskData, bool needHeader) +{ + vector output; + output.reserve(diskData.size() * FdsDiskSideCapacity + 16); + + if(needHeader) { + uint8_t header[16] = { 'F', 'D', 'S', '\x1a', (uint8_t)diskData.size(), '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; + output.insert(output.end(), header, header + sizeof(header)); + } + + for(vector &diskSide : diskData) { + bool inGap = true; + size_t i = 0, len = diskSide.size(); + size_t gapNeeded = FdsDiskSideCapacity; + uint32_t fileSize = 0; + while(i < len) { + if(inGap) { + if(diskSide[i] == 0x80) { + inGap = false; + } + i++; + } else { + uint32_t blockLength = 1; + switch(diskSide[i]) { + case 1: blockLength = 56; break; //Disk header + case 2: blockLength = 2; break; //File count + case 3: blockLength = 16; fileSize = diskSide[i + 13] + diskSide[i + 14] * 0x100; break; //File header + case 4: blockLength = 1 + fileSize; break; + } + output.insert(output.end(), &diskSide[i], &diskSide[i] + blockLength); + gapNeeded -= blockLength; + i += blockLength; + i += 2; //Skip CRC after block + + inGap = true; + } + } + output.insert(output.end(), gapNeeded, 0); + } + + return output; +} + +void FdsLoader::LoadDiskData(vector& romFile, vector>& diskData, vector>& diskHeaders) +{ + uint8_t numberOfSides = 0; + size_t fileOffset = 0; + bool hasHeader = memcmp(romFile.data(), "FDS\x1a", 4) == 0; + if(hasHeader) { + numberOfSides = romFile[4]; + fileOffset = 16; + } else { + numberOfSides = (uint8_t)(romFile.size() / 65500); + } + + for(uint32_t i = 0; i < numberOfSides; i++) { + diskData.push_back(vector()); + vector &fdsDiskImage = diskData.back(); + + diskHeaders.push_back(vector(romFile.data() + fileOffset + 1, romFile.data() + fileOffset + 57)); + + AddGaps(fdsDiskImage, &romFile[fileOffset]); + fileOffset += FdsDiskSideCapacity; + + //Ensure the image is 65500 bytes + if(fdsDiskImage.size() < FdsDiskSideCapacity) { + fdsDiskImage.resize(FdsDiskSideCapacity); + } + } +} + +RomData FdsLoader::LoadRom(vector& romFile, string filename) +{ + RomData romData; + + romData.Sha1 = SHA1::GetHash(romFile); + romData.Crc32 = CRC32::GetCRC(romFile.data(), romFile.size()); + romData.PrgCrc32 = CRC32::GetCRC(romFile.data(), romFile.size()); + + romData.Format = RomFormat::Fds; + romData.MapperID = MapperFactory::FdsMapperID; + romData.Mirroring = MirroringType::Vertical; + romData.PrgRom = LoadBios(); + romData.System = GameSystem::FDS; + + if(romData.PrgRom.size() != 0x2000) { + romData.Error = true; + romData.BiosMissing = true; + } + + //Setup default controllers + if(!_checkOnly && EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) { + GameDatabase::InitializeInputDevices("", GameSystem::FDS); + } + + return romData; +} diff --git a/Core/FdsLoader.h b/Core/FdsLoader.h index d9872b00..9e44e284 100644 --- a/Core/FdsLoader.h +++ b/Core/FdsLoader.h @@ -1,173 +1,22 @@ #pragma once #include "stdafx.h" -#include -#include "../Utilities/FolderUtilities.h" -#include "../Utilities/CRC32.h" -#include "../Utilities/sha1.h" -#include "RomData.h" -#include "MessageManager.h" -#include "MapperFactory.h" -#include "GameDatabase.h" #include "BaseLoader.h" +struct RomData; + class FdsLoader : public BaseLoader { private: - const size_t FdsDiskSideCapacity = 65500; + static constexpr size_t FdsDiskSideCapacity = 65500; private: - void AddGaps(vector& diskSide, uint8_t* readBuffer) - { - //Start image with 28300 bits of gap - diskSide.insert(diskSide.end(), 28300 / 8, 0); - - for(size_t j = 0; j < FdsDiskSideCapacity;) { - uint8_t blockType = readBuffer[j]; - uint32_t blockLength = 1; - switch(blockType) { - case 1: blockLength = 56; break; //Disk header - case 2: blockLength = 2; break; //File count - case 3: blockLength = 16; break; //File header - case 4: blockLength = 1 + readBuffer[j - 3] + readBuffer[j - 2] * 0x100; break; - default: return; //End parsing when we encounter an invalid block type (This is what Nestopia apppears to do) - } - - if(blockType == 0) { - diskSide.push_back(blockType); - } else { - diskSide.push_back(0x80); - diskSide.insert(diskSide.end(), &readBuffer[j], &readBuffer[j] + blockLength); - - //Fake CRC value - diskSide.push_back(0x4D); - diskSide.push_back(0x62); - - //Insert 976 bits of gap after a block - diskSide.insert(diskSide.end(), 976 / 8, 0); - } - - j += blockLength; - } - } - - vector LoadBios() - { - //For FDS, the PRG ROM is the FDS BIOS (8k) - vector biosData; - - ifstream biosFile(FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "FdsBios.bin"), ios::in | ios::binary); - if(biosFile) { - return vector(std::istreambuf_iterator(biosFile), {}); - } else { - biosFile.open(FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "disksys.rom"), ios::in | ios::binary); - if(biosFile) { - return vector(std::istreambuf_iterator(biosFile), {}); - } else { - if(!_checkOnly) { - MessageManager::SendNotification(ConsoleNotificationType::FdsBiosNotFound); - } - } - } - return {}; - } + void AddGaps(vector& diskSide, uint8_t* readBuffer); + vector LoadBios(); public: using BaseLoader::BaseLoader; - vector RebuildFdsFile(vector> diskData, bool needHeader) - { - vector output; - output.reserve(diskData.size() * FdsDiskSideCapacity + 16); - - if(needHeader) { - uint8_t header[16] = { 'F', 'D', 'S', '\x1a', (uint8_t)diskData.size(), '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; - output.insert(output.end(), header, header + sizeof(header)); - } - - for(vector &diskSide : diskData) { - bool inGap = true; - size_t i = 0, len = diskSide.size(); - size_t gapNeeded = FdsDiskSideCapacity; - uint32_t fileSize = 0; - while(i < len) { - if(inGap) { - if(diskSide[i] == 0x80) { - inGap = false; - } - i++; - } else { - uint32_t blockLength = 1; - switch(diskSide[i]) { - case 1: blockLength = 56; break; //Disk header - case 2: blockLength = 2; break; //File count - case 3: blockLength = 16; fileSize = diskSide[i + 13] + diskSide[i + 14] * 0x100; break; //File header - case 4: blockLength = 1 + fileSize; break; - } - output.insert(output.end(), &diskSide[i], &diskSide[i] + blockLength); - gapNeeded -= blockLength; - i += blockLength; - i += 2; //Skip CRC after block - - inGap = true; - } - } - output.insert(output.end(), gapNeeded, 0); - } - - return output; - } - - void LoadDiskData(vector& romFile, vector> &diskData, vector> &diskHeaders) - { - uint8_t numberOfSides = 0; - size_t fileOffset = 0; - bool hasHeader = memcmp(romFile.data(), "FDS\x1a", 4) == 0; - if(hasHeader) { - numberOfSides = romFile[4]; - fileOffset = 16; - } else { - numberOfSides = (uint8_t)(romFile.size() / 65500); - } - - for(uint32_t i = 0; i < numberOfSides; i++) { - diskData.push_back(vector()); - vector &fdsDiskImage = diskData.back(); - - diskHeaders.push_back(vector(romFile.data() + fileOffset + 1, romFile.data() + fileOffset + 57)); - - AddGaps(fdsDiskImage, &romFile[fileOffset]); - fileOffset += FdsDiskSideCapacity; - - //Ensure the image is 65500 bytes - if(fdsDiskImage.size() < FdsDiskSideCapacity) { - fdsDiskImage.resize(FdsDiskSideCapacity); - } - } - } - - RomData LoadRom(vector &romFile, string filename) - { - RomData romData; - - romData.Sha1 = SHA1::GetHash(romFile); - romData.Crc32 = CRC32::GetCRC(romFile.data(), romFile.size()); - romData.PrgCrc32 = CRC32::GetCRC(romFile.data(), romFile.size()); - - romData.Format = RomFormat::Fds; - romData.MapperID = MapperFactory::FdsMapperID; - romData.Mirroring = MirroringType::Vertical; - romData.PrgRom = LoadBios(); - romData.System = GameSystem::FDS; - - if(romData.PrgRom.size() != 0x2000) { - romData.Error = true; - } - - //Setup default controllers - if(!_checkOnly && EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) { - GameDatabase::InitializeInputDevices("", GameSystem::FDS); - } - - return romData; - } + vector RebuildFdsFile(vector> diskData, bool needHeader); + void LoadDiskData(vector& romFile, vector> &diskData, vector> &diskHeaders); + RomData LoadRom(vector &romFile, string filename); }; \ No newline at end of file diff --git a/Core/GameClient.cpp b/Core/GameClient.cpp index 25fb7b17..759e907e 100644 --- a/Core/GameClient.cpp +++ b/Core/GameClient.cpp @@ -5,6 +5,7 @@ using std::thread; #include "MessageManager.h" #include "GameClient.h" #include "Console.h" +#include "NotificationManager.h" #include "../Utilities/Socket.h" #include "ClientConnectionData.h" #include "GameClientConnection.h" @@ -34,7 +35,7 @@ bool GameClient::Connected() void GameClient::Connect(shared_ptr console, ClientConnectionData &connectionData) { _instance.reset(new GameClient(console)); - MessageManager::RegisterNotificationListener(_instance); + console->GetNotificationManager()->RegisterNotificationListener(_instance); shared_ptr instance = _instance; if(instance) { @@ -60,7 +61,7 @@ void GameClient::PrivateConnect(ClientConnectionData &connectionData) shared_ptr socket(new Socket()); if(socket->Connect(connectionData.Host.c_str(), connectionData.Port)) { _connection.reset(new GameClientConnection(_console, socket, connectionData)); - MessageManager::RegisterNotificationListener(_connection); + _console->GetNotificationManager()->RegisterNotificationListener(_connection); _connected = true; } else { MessageManager::DisplayMessage("NetPlay", "CouldNotConnect"); diff --git a/Core/GameClientConnection.cpp b/Core/GameClientConnection.cpp index 8526844b..4bb2a747 100644 --- a/Core/GameClientConnection.cpp +++ b/Core/GameClientConnection.cpp @@ -17,6 +17,7 @@ #include "PlayerListMessage.h" #include "ForceDisconnectMessage.h" #include "ServerInformationMessage.h" +#include "NotificationManager.h" GameClientConnection::GameClientConnection(shared_ptr console, shared_ptr socket, ClientConnectionData &connectionData) : GameConnection(console, socket) { @@ -41,7 +42,7 @@ void GameClientConnection::Shutdown() DisableControllers(); _console->GetControlManager()->UnregisterInputProvider(this); - MessageManager::SendNotification(ConsoleNotificationType::DisconnectedFromServer); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::DisconnectedFromServer); MessageManager::DisplayMessage("NetPlay", "ConnectionLost"); EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed); } diff --git a/Core/GameServer.cpp b/Core/GameServer.cpp index c7327962..08709ae3 100644 --- a/Core/GameServer.cpp +++ b/Core/GameServer.cpp @@ -8,6 +8,7 @@ using std::thread; #include "ControlManager.h" #include "../Utilities/Socket.h" #include "PlayerListMessage.h" +#include "NotificationManager.h" unique_ptr GameServer::Instance; @@ -40,7 +41,7 @@ void GameServer::AcceptConnections() shared_ptr socket = _listener->Accept(); if(!socket->ConnectionError()) { auto connection = shared_ptr(new GameServerConnection(_console, socket, _password)); - MessageManager::RegisterNotificationListener(connection); + _console->GetNotificationManager()->RegisterNotificationListener(connection); _openConnections.push_back(connection); } else { break; diff --git a/Core/HdPpu.cpp b/Core/HdPpu.cpp new file mode 100644 index 00000000..e858d488 --- /dev/null +++ b/Core/HdPpu.cpp @@ -0,0 +1,168 @@ +#include "stdafx.h" +#include "HdPpu.h" +#include "CPU.h" +#include "Console.h" +#include "HdNesPack.h" +#include "VideoDecoder.h" +#include "RewindManager.h" +#include "HdPackConditions.h" +#include "NotificationManager.h" +#include "BaseMapper.h" +#include "MemoryManager.h" + +void HdPpu::DrawPixel() +{ + uint16_t bufferOffset = (_scanline << 8) + _cycle - 1; + uint16_t &pixel = _currentOutputBuffer[bufferOffset]; + _lastSprite = nullptr; + + if(IsRenderingEnabled() || ((_state.VideoRamAddr & 0x3F00) != 0x3F00)) { + bool isChrRam = !_console->GetMapper()->HasChrRom(); + BaseMapper *mapper = _console->GetMapper(); + + uint32_t color = GetPixelColor(); + pixel = (_paletteRAM[color & 0x03 ? color : 0] & _paletteRamMask) | _intensifyColorBits; + + TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); + uint32_t backgroundColor = 0; + if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { + backgroundColor = (((_state.LowBitShift << _state.XScroll) & 0x8000) >> 15) | (((_state.HighBitShift << _state.XScroll) & 0x8000) >> 14); + } + + HdPpuPixelInfo &tileInfo = _info->ScreenTiles[bufferOffset]; + + tileInfo.Grayscale = _paletteRamMask == 0x30; + tileInfo.EmphasisBits = _intensifyColorBits >> 6; + tileInfo.Tile.PpuBackgroundColor = ReadPaletteRAM(0); + tileInfo.Tile.BgColorIndex = backgroundColor; + if(backgroundColor == 0) { + tileInfo.Tile.BgColor = tileInfo.Tile.PpuBackgroundColor; + } else { + tileInfo.Tile.BgColor = ReadPaletteRAM(lastTile->PaletteOffset + backgroundColor); + } + + tileInfo.XScroll = _state.XScroll; + tileInfo.TmpVideoRamAddr = _state.TmpVideoRamAddr; + + if(_lastSprite && _flags.SpritesEnabled) { + int j = 0; + for(uint8_t i = 0; i < _spriteCount; i++) { + int32_t shift = (int32_t)_cycle - _spriteTiles[i].SpriteX - 1; + SpriteInfo& sprite = _spriteTiles[i]; + if(shift >= 0 && shift < 8) { + tileInfo.Sprite[j].TileIndex = sprite.AbsoluteTileAddr / 16; + if(isChrRam) { + mapper->CopyChrRamTile(sprite.AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Sprite[j].TileData); + } + if(_version >= 100) { + tileInfo.Sprite[j].PaletteColors = 0xFF000000 | _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); + } else { + tileInfo.Sprite[j].PaletteColors = _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); + } + if(sprite.OffsetY >= 8) { + tileInfo.Sprite[j].OffsetY = sprite.OffsetY - 8; + } else { + tileInfo.Sprite[j].OffsetY = sprite.OffsetY; + } + + tileInfo.Sprite[j].OffsetX = shift; + tileInfo.Sprite[j].HorizontalMirroring = sprite.HorizontalMirror; + tileInfo.Sprite[j].VerticalMirroring = sprite.VerticalMirror; + tileInfo.Sprite[j].BackgroundPriority = sprite.BackgroundPriority; + + int32_t shift = (int32_t)_cycle - sprite.SpriteX - 1; + if(sprite.HorizontalMirror) { + tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte >> shift) & 0x01) | ((sprite.HighByte >> shift) & 0x01) << 1; + } else { + tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte << shift) & 0x80) >> 7 | ((sprite.HighByte << shift) & 0x80) >> 6; + } + + if(tileInfo.Sprite[j].SpriteColorIndex == 0) { + tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(0); + } else { + tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(sprite.PaletteOffset + tileInfo.Sprite[j].SpriteColorIndex); + } + + tileInfo.Sprite[j].PpuBackgroundColor = tileInfo.Tile.PpuBackgroundColor; + tileInfo.Sprite[j].BgColorIndex = tileInfo.Tile.BgColorIndex; + + j++; + if(j >= 4) { + break; + } + } + } + tileInfo.SpriteCount = j; + } else { + tileInfo.SpriteCount = 0; + } + + if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { + tileInfo.Tile.TileIndex = lastTile->AbsoluteTileAddr / 16; + if(isChrRam) { + mapper->CopyChrRamTile(lastTile->AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Tile.TileData); + } + if(_version >= 100) { + tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16) | (_paletteRAM[0] << 24); + } else { + tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16); + } + tileInfo.Tile.OffsetY = lastTile->OffsetY; + tileInfo.Tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07; + } else { + tileInfo.Tile.TileIndex = HdPpuTileInfo::NoTile; + } + } else { + //"If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color." + pixel = ReadPaletteRAM(_state.VideoRamAddr) | _intensifyColorBits; + _info->ScreenTiles[bufferOffset].Tile.TileIndex = HdPpuTileInfo::NoTile; + _info->ScreenTiles[bufferOffset].SpriteCount = 0; + } +} + +HdPpu::HdPpu(shared_ptr console, HdPackData * hdData) : PPU(console) +{ + _hdData = hdData; + _version = _hdData->Version; + + _screenInfo[0] = new HdScreenInfo(); + _screenInfo[1] = new HdScreenInfo(); + _info = _screenInfo[0]; +} + +HdPpu::~HdPpu() +{ + delete _screenInfo[0]; + delete _screenInfo[1]; +} + +void HdPpu::SendFrame() +{ + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); + + _info->FrameNumber = _frameCount; + _info->WatchedAddressValues.clear(); + for(uint32_t address : _hdData->WatchedMemoryAddresses) { + if(address & HdPackBaseMemoryCondition::PpuMemoryMarker) { + if((address & 0x3FFF) >= 0x3F00) { + _info->WatchedAddressValues[address] = ReadPaletteRAM(address); + } else { + _info->WatchedAddressValues[address] = _console->GetMapper()->DebugReadVRAM(address & 0x3FFF, true); + } + } else { + _info->WatchedAddressValues[address] = _console->GetMemoryManager()->DebugRead(address); + } + } + +#ifdef LIBRETRO + _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); +#else + if(_console->GetRewindManager()->IsRewinding()) { + _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); + } else { + _console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer, _info); + } + _currentOutputBuffer = (_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]; + _info = (_info == _screenInfo[0]) ? _screenInfo[1] : _screenInfo[0]; +#endif +} diff --git a/Core/HdPpu.h b/Core/HdPpu.h index cb4faf18..02efefae 100644 --- a/Core/HdPpu.h +++ b/Core/HdPpu.h @@ -1,13 +1,11 @@ #pragma once #include "stdafx.h" #include "PPU.h" -#include "CPU.h" -#include "HdNesPack.h" -#include "VideoDecoder.h" -#include "RewindManager.h" -#include "HdPackConditions.h" +struct HdScreenInfo; +struct HdPackData; class ControlManager; +class Console; class HdPpu : public PPU { @@ -18,161 +16,11 @@ private: HdPackData *_hdData = nullptr; protected: - void DrawPixel() - { - uint16_t bufferOffset = (_scanline << 8) + _cycle - 1; - uint16_t &pixel = _currentOutputBuffer[bufferOffset]; - _lastSprite = nullptr; - - if(IsRenderingEnabled() || ((_state.VideoRamAddr & 0x3F00) != 0x3F00)) { - bool isChrRam = !_console->GetMapper()->HasChrRom(); - BaseMapper *mapper = _console->GetMapper(); - - uint32_t color = GetPixelColor(); - pixel = (_paletteRAM[color & 0x03 ? color : 0] & _paletteRamMask) | _intensifyColorBits; - - TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); - uint32_t backgroundColor = 0; - if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { - backgroundColor = (((_state.LowBitShift << _state.XScroll) & 0x8000) >> 15) | (((_state.HighBitShift << _state.XScroll) & 0x8000) >> 14); - } - - HdPpuPixelInfo &tileInfo = _info->ScreenTiles[bufferOffset]; - - tileInfo.Grayscale = _paletteRamMask == 0x30; - tileInfo.EmphasisBits = _intensifyColorBits >> 6; - tileInfo.Tile.PpuBackgroundColor = ReadPaletteRAM(0); - tileInfo.Tile.BgColorIndex = backgroundColor; - if(backgroundColor == 0) { - tileInfo.Tile.BgColor = tileInfo.Tile.PpuBackgroundColor; - } else { - tileInfo.Tile.BgColor = ReadPaletteRAM(lastTile->PaletteOffset + backgroundColor); - } - - tileInfo.XScroll = _state.XScroll; - tileInfo.TmpVideoRamAddr = _state.TmpVideoRamAddr; - - if(_lastSprite && _flags.SpritesEnabled) { - int j = 0; - for(uint8_t i = 0; i < _spriteCount; i++) { - int32_t shift = (int32_t)_cycle - _spriteTiles[i].SpriteX - 1; - SpriteInfo& sprite = _spriteTiles[i]; - if(shift >= 0 && shift < 8) { - tileInfo.Sprite[j].TileIndex = sprite.AbsoluteTileAddr / 16; - if(isChrRam) { - mapper->CopyChrRamTile(sprite.AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Sprite[j].TileData); - } - if(_version >= 100) { - tileInfo.Sprite[j].PaletteColors = 0xFF000000 | _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); - } else { - tileInfo.Sprite[j].PaletteColors = _paletteRAM[sprite.PaletteOffset + 3] | (_paletteRAM[sprite.PaletteOffset + 2] << 8) | (_paletteRAM[sprite.PaletteOffset + 1] << 16); - } - if(sprite.OffsetY >= 8) { - tileInfo.Sprite[j].OffsetY = sprite.OffsetY - 8; - } else { - tileInfo.Sprite[j].OffsetY = sprite.OffsetY; - } - - tileInfo.Sprite[j].OffsetX = shift; - tileInfo.Sprite[j].HorizontalMirroring = sprite.HorizontalMirror; - tileInfo.Sprite[j].VerticalMirroring = sprite.VerticalMirror; - tileInfo.Sprite[j].BackgroundPriority = sprite.BackgroundPriority; - - int32_t shift = (int32_t)_cycle - sprite.SpriteX - 1; - if(sprite.HorizontalMirror) { - tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte >> shift) & 0x01) | ((sprite.HighByte >> shift) & 0x01) << 1; - } else { - tileInfo.Sprite[j].SpriteColorIndex = ((sprite.LowByte << shift) & 0x80) >> 7 | ((sprite.HighByte << shift) & 0x80) >> 6; - } - - if(tileInfo.Sprite[j].SpriteColorIndex == 0) { - tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(0); - } else { - tileInfo.Sprite[j].SpriteColor = ReadPaletteRAM(sprite.PaletteOffset + tileInfo.Sprite[j].SpriteColorIndex); - } - - tileInfo.Sprite[j].PpuBackgroundColor = tileInfo.Tile.PpuBackgroundColor; - tileInfo.Sprite[j].BgColorIndex = tileInfo.Tile.BgColorIndex; - - j++; - if(j >= 4) { - break; - } - } - } - tileInfo.SpriteCount = j; - } else { - tileInfo.SpriteCount = 0; - } - - if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { - tileInfo.Tile.TileIndex = lastTile->AbsoluteTileAddr / 16; - if(isChrRam) { - mapper->CopyChrRamTile(lastTile->AbsoluteTileAddr & 0xFFFFFFF0, tileInfo.Tile.TileData); - } - if(_version >= 100) { - tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16) | (_paletteRAM[0] << 24); - } else { - tileInfo.Tile.PaletteColors = _paletteRAM[lastTile->PaletteOffset + 3] | (_paletteRAM[lastTile->PaletteOffset + 2] << 8) | (_paletteRAM[lastTile->PaletteOffset + 1] << 16); - } - tileInfo.Tile.OffsetY = lastTile->OffsetY; - tileInfo.Tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07; - } else { - tileInfo.Tile.TileIndex = HdPpuTileInfo::NoTile; - } - } else { - //"If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color." - pixel = ReadPaletteRAM(_state.VideoRamAddr) | _intensifyColorBits; - _info->ScreenTiles[bufferOffset].Tile.TileIndex = HdPpuTileInfo::NoTile; - _info->ScreenTiles[bufferOffset].SpriteCount = 0; - } - } + void DrawPixel() override; public: - HdPpu(shared_ptr console, HdPackData* hdData) : PPU(console) - { - _hdData = hdData; - _version = _hdData->Version; - - _screenInfo[0] = new HdScreenInfo(); - _screenInfo[1] = new HdScreenInfo(); - _info = _screenInfo[0]; - } + HdPpu(shared_ptr console, HdPackData* hdData); + ~HdPpu(); - ~HdPpu() - { - delete _screenInfo[0]; - delete _screenInfo[1]; - } - - void SendFrame() - { - MessageManager::SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); - - _info->FrameNumber = _frameCount; - _info->WatchedAddressValues.clear(); - for(uint32_t address : _hdData->WatchedMemoryAddresses) { - if(address & HdPackBaseMemoryCondition::PpuMemoryMarker) { - if((address & 0x3FFF) >= 0x3F00) { - _info->WatchedAddressValues[address] = ReadPaletteRAM(address); - } else { - _info->WatchedAddressValues[address] = _console->GetMapper()->DebugReadVRAM(address & 0x3FFF, true); - } - } else { - _info->WatchedAddressValues[address] = _console->GetMemoryManager()->DebugRead(address); - } - } - -#ifdef LIBRETRO - _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); -#else - if(_console->GetRewindManager()->IsRewinding()) { - _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer, _info); - } else { - _console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer, _info); - } - _currentOutputBuffer = (_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0]; - _info = (_info == _screenInfo[0]) ? _screenInfo[1] : _screenInfo[0]; -#endif - } + void SendFrame() override; }; \ No newline at end of file diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index e1123e70..7df2dcc7 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -1,8 +1,10 @@ #include "stdafx.h" -#include "MessageManager.h" +#include "NotificationManager.h" #include "MapperFactory.h" #include "RomLoader.h" #include "UnifBoards.h" +#include "BaseMapper.h" +#include "RomData.h" #include "A65AS.h" #include "Ac08.h" @@ -624,9 +626,11 @@ shared_ptr MapperFactory::InitializeFromFile(shared_ptr con if(mapper) { mapper->SetConsole(console); mapper->Initialize(romData); - MessageManager::RegisterNotificationListener(mapper); + console->GetNotificationManager()->RegisterNotificationListener(mapper); return mapper; } + } else if(loader.GetRomData().BiosMissing) { + console->GetNotificationManager()->SendNotification(ConsoleNotificationType::FdsBiosNotFound); } return nullptr; } diff --git a/Core/MapperFactory.h b/Core/MapperFactory.h index f52deaa6..3b3cc21d 100644 --- a/Core/MapperFactory.h +++ b/Core/MapperFactory.h @@ -1,8 +1,10 @@ #pragma once #include "stdafx.h" -#include "BaseMapper.h" -#include "RomData.h" + class MemoryManager; +class Console; +class BaseMapper; +struct RomData; class MapperFactory { diff --git a/Core/MesenMovie.cpp b/Core/MesenMovie.cpp index 3417ed06..f0e9c1ce 100644 --- a/Core/MesenMovie.cpp +++ b/Core/MesenMovie.cpp @@ -12,6 +12,7 @@ #include "MovieRecorder.h" #include "BatteryManager.h" #include "VirtualFile.h" +#include "NotificationManager.h" MesenMovie::MesenMovie(shared_ptr console) { @@ -63,6 +64,13 @@ vector MesenMovie::LoadBattery(string extension) return batteryData; } +void MesenMovie::ProcessNotification(ConsoleNotificationType type, void * parameter) +{ + if(type == ConsoleNotificationType::GameLoaded) { + _console->GetControlManager()->RegisterInputProvider(this); + } +} + bool MesenMovie::Play(VirtualFile &file) { _movieFile = file; @@ -98,7 +106,7 @@ bool MesenMovie::Play(VirtualFile &file) _console->Pause(); BatteryManager::SetBatteryProvider(shared_from_this()); - _console->GetControlManager()->RegisterInputProvider(this); + _console->GetNotificationManager()->RegisterNotificationListener(shared_from_this()); ApplySettings(); //Disable auto-configure input option (otherwise the movie file's input types are ignored) diff --git a/Core/MesenMovie.h b/Core/MesenMovie.h index 59c20ced..d8adeba2 100644 --- a/Core/MesenMovie.h +++ b/Core/MesenMovie.h @@ -4,12 +4,13 @@ #include "MovieManager.h" #include "VirtualFile.h" #include "BatteryManager.h" +#include "INotificationListener.h" class ZipReader; class Console; struct CodeInfo; -class MesenMovie : public IMovie, public IBatteryProvider, public std::enable_shared_from_this +class MesenMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this { private: shared_ptr _console; @@ -45,4 +46,7 @@ public: // Inherited via IBatteryProvider virtual vector LoadBattery(string extension) override; + + // Inherited via INotificationListener + virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override; }; \ No newline at end of file diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp index 7c27a19b..35992eb2 100644 --- a/Core/MessageManager.cpp +++ b/Core/MessageManager.cpp @@ -607,11 +607,7 @@ std::unordered_map MessageManager::_caResources = { std::list MessageManager::_log; SimpleLock MessageManager::_logLock; -SimpleLock MessageManager::_notificationLock; -SimpleLock MessageManager::_addListenerLock; IMessageManager* MessageManager::_messageManager = nullptr; -vector> MessageManager::_listenersToAdd; -vector> MessageManager::_notificationListeners; void MessageManager::RegisterMessageManager(IMessageManager* messageManager) { @@ -697,50 +693,3 @@ string MessageManager::GetLog() } return ss.str(); } - -void MessageManager::RegisterNotificationListener(shared_ptr notificationListener) -{ - auto lock = _notificationLock.AcquireSafe(); - - for(weak_ptr listener : _notificationListeners) { - if(listener.lock() == notificationListener) { - //This listener is already registered, do nothing - return; - } - } - - _notificationListeners.push_back(notificationListener); -} - -void MessageManager::CleanupNotificationListeners() -{ - auto lock = _notificationLock.AcquireSafe(); - - //Remove expired listeners - _notificationListeners.erase( - std::remove_if( - _notificationListeners.begin(), - _notificationListeners.end(), - [](weak_ptr ptr) { return ptr.expired(); } - ), - _notificationListeners.end() - ); -} - -void MessageManager::SendNotification(ConsoleNotificationType type, void* parameter) -{ - vector> listeners; - { - auto lock = _notificationLock.AcquireSafe(); - CleanupNotificationListeners(); - listeners = _notificationListeners; - } - - //Iterate on a copy without using a lock - for(weak_ptr notificationListener : listeners) { - shared_ptr listener = notificationListener.lock(); - if(listener) { - listener->ProcessNotification(type, parameter); - } - } -} diff --git a/Core/MessageManager.h b/Core/MessageManager.h index 697ef2b0..3378bb96 100644 --- a/Core/MessageManager.h +++ b/Core/MessageManager.h @@ -11,8 +11,6 @@ class MessageManager { private: static IMessageManager* _messageManager; - static vector> _listenersToAdd; - static vector> _notificationListeners; static std::unordered_map _enResources; static std::unordered_map _frResources; static std::unordered_map _jaResources; @@ -23,12 +21,8 @@ private: static std::unordered_map _caResources; static SimpleLock _logLock; - static SimpleLock _notificationLock; - static SimpleLock _addListenerLock; static std::list _log; - - static void CleanupNotificationListeners(); - + public: static string Localize(string key); @@ -37,7 +31,4 @@ public: static void Log(string message = ""); static string GetLog(); - - static void RegisterNotificationListener(shared_ptr notificationListener); - static void SendNotification(ConsoleNotificationType type, void* parameter = nullptr); }; diff --git a/Core/MovieManager.h b/Core/MovieManager.h index dbf14e0a..876bac75 100644 --- a/Core/MovieManager.h +++ b/Core/MovieManager.h @@ -15,7 +15,10 @@ protected: void EndMovie() { MessageManager::DisplayMessage("Movies", "MovieEnded"); - MessageManager::SendNotification(ConsoleNotificationType::MovieEnded); + + //TODOCONSOLE + //MessageManager::SendNotification(ConsoleNotificationType::MovieEnded); + if(EmulationSettings::CheckFlag(EmulationFlags::PauseOnMovieEnd)) { EmulationSettings::SetFlags(EmulationFlags::Paused); } diff --git a/Core/MovieRecorder.cpp b/Core/MovieRecorder.cpp index 2458795a..266f4fdd 100644 --- a/Core/MovieRecorder.cpp +++ b/Core/MovieRecorder.cpp @@ -9,6 +9,7 @@ #include "CheatManager.h" #include "VirtualFile.h" #include "SaveStateManager.h" +#include "NotificationManager.h" MovieRecorder::MovieRecorder(shared_ptr console) { @@ -43,8 +44,10 @@ bool MovieRecorder::Record(RecordMovieOptions options) if(options.RecordFrom == RecordMovieFrom::StartWithSaveData) { BatteryManager::SetBatteryRecorder(shared_from_this()); } - _console->GetControlManager()->RegisterInputRecorder(this); + + _console->GetNotificationManager()->RegisterNotificationListener(shared_from_this()); if(options.RecordFrom == RecordMovieFrom::CurrentState) { + _console->GetControlManager()->RegisterInputRecorder(this); _console->GetSaveStateManager()->SaveState(_saveStateData); _hasSaveState = true; } else { @@ -217,3 +220,10 @@ vector MovieRecorder::LoadBattery(string extension) { return vector(); } + +void MovieRecorder::ProcessNotification(ConsoleNotificationType type, void *parameter) +{ + if(type == ConsoleNotificationType::GameLoaded) { + _console->GetControlManager()->RegisterInputRecorder(this); + } +} diff --git a/Core/MovieRecorder.h b/Core/MovieRecorder.h index 97ab4c89..80bc71ad 100644 --- a/Core/MovieRecorder.h +++ b/Core/MovieRecorder.h @@ -4,12 +4,13 @@ #include "IInputRecorder.h" #include "BatteryManager.h" #include "Types.h" +#include "INotificationListener.h" class ZipWriter; class Console; struct CodeInfo; -class MovieRecorder : public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this +class MovieRecorder : public INotificationListener, public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this { private: static const uint32_t MovieFormatVersion = 1; @@ -44,6 +45,9 @@ public: // Inherited via IBatteryProvider virtual vector LoadBattery(string extension) override; + + // Inherited via INotificationListener + virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override; }; namespace MovieKeys diff --git a/Core/NotificationManager.cpp b/Core/NotificationManager.cpp new file mode 100644 index 00000000..d63c9717 --- /dev/null +++ b/Core/NotificationManager.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include "NotificationManager.h" + +void NotificationManager::RegisterNotificationListener(shared_ptr notificationListener) +{ + auto lock = _lock.AcquireSafe(); + + for(weak_ptr listener : _listeners) { + if(listener.lock() == notificationListener) { + //This listener is already registered, do nothing + return; + } + } + + _listeners.push_back(notificationListener); +} + +void NotificationManager::CleanupNotificationListeners() +{ + auto lock = _lock.AcquireSafe(); + + //Remove expired listeners + _listeners.erase( + std::remove_if( + _listeners.begin(), + _listeners.end(), + [](weak_ptr ptr) { return ptr.expired(); } + ), + _listeners.end() + ); +} + +void NotificationManager::SendNotification(ConsoleNotificationType type, void* parameter) +{ + vector> listeners; + { + auto lock = _lock.AcquireSafe(); + CleanupNotificationListeners(); + listeners = _listeners; + } + + //Iterate on a copy without using a lock + for(weak_ptr notificationListener : listeners) { + shared_ptr listener = notificationListener.lock(); + if(listener) { + listener->ProcessNotification(type, parameter); + } + } +} diff --git a/Core/NotificationManager.h b/Core/NotificationManager.h new file mode 100644 index 00000000..e38a39dc --- /dev/null +++ b/Core/NotificationManager.h @@ -0,0 +1,18 @@ +#pragma once +#include "stdafx.h" +#include "INotificationListener.h" +#include "../Utilities/SimpleLock.h" + +class NotificationManager +{ +private: + SimpleLock _lock; + vector> _listenersToAdd; + vector> _listeners; + + void CleanupNotificationListeners(); + +public: + void RegisterNotificationListener(shared_ptr notificationListener); + void SendNotification(ConsoleNotificationType type, void* parameter = nullptr); +}; diff --git a/Core/NsfLoader.cpp b/Core/NsfLoader.cpp new file mode 100644 index 00000000..a6ccf1b5 --- /dev/null +++ b/Core/NsfLoader.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "NsfLoader.h" +#include "RomData.h" +#include "MapperFactory.h" + +void NsfLoader::Read(uint8_t *& data, uint8_t & dest) +{ + dest = data[0]; + data++; +} + +void NsfLoader::Read(uint8_t *& data, uint16_t & dest) +{ + dest = data[0] | (data[1] << 8); + data += 2; +} + +void NsfLoader::Read(uint8_t *& data, char * dest, size_t len) +{ + memcpy(dest, data, len); + data += len; +} + +void NsfLoader::InitializeFromHeader(RomData & romData) +{ + NsfHeader &header = romData.NsfInfo; + + romData.Format = RomFormat::Nsf; + romData.MapperID = MapperFactory::NsfMapperID; + + if(header.LoadAddress < 0x6000 || header.TotalSongs == 0) { + romData.Error = true; + } + + if(header.Flags == 0x01) { + romData.System = GameSystem::NesPal; + } + + if(header.PlaySpeedNtsc == 0) { + header.PlaySpeedNtsc = 16639; + } + + if(header.PlaySpeedPal == 0) { + header.PlaySpeedPal = 19997; + } + + if(header.StartingSong > header.TotalSongs || header.StartingSong == 0) { + header.StartingSong = 1; + } + + //Log window output + Log("[NSF] Region: " + string(header.Flags == 0x00 ? "NTSC" : (header.Flags == 0x01 ? "PAL" : "NTSC & PAL"))); + if(header.PlaySpeedNtsc > 0) { + Log("[NSF] Play speed (NTSC): " + std::to_string(1000000.0 / (double)header.PlaySpeedNtsc) + " Hz"); + } + if(header.PlaySpeedPal > 0) { + Log("[NSF] Play speed (PAL): " + std::to_string(1000000.0 / (double)header.PlaySpeedPal) + " Hz"); + } + Log("[NSF] Title: " + string(header.SongName)); + Log("[NSF] Artist: " + string(header.ArtistName)); + Log("[NSF] Copyright: " + string(header.CopyrightHolder)); + Log("[NSF] Ripper: " + string(header.RipperName)); + + stringstream ss; + ss << "[NSF] Load Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.LoadAddress; + Log(ss.str()); + ss = stringstream(); + ss << "[NSF] Init Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.InitAddress; + Log(ss.str()); + ss = stringstream(); + ss << "[NSF] Play Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.PlayAddress; + Log(ss.str()); + + vector chips; + if(header.SoundChips & 0x01) { + chips.push_back("VRC6"); + } + if(header.SoundChips & 0x02) { + chips.push_back("VRC7"); + } + if(header.SoundChips & 0x04) { + chips.push_back("FDS"); + } + if(header.SoundChips & 0x08) { + chips.push_back("MMC5"); + } + if(header.SoundChips & 0x10) { + chips.push_back("Namco 163"); + } + if(header.SoundChips & 0x20) { + chips.push_back("Sunsoft 5B"); + } + if(chips.empty()) { + chips.push_back(""); + } + + ss = stringstream(); + for(size_t i = 0; i < chips.size(); i++) { + if(i > 0) { + ss << ", "; + } + ss << chips[i]; + } + + Log("[NSF] Sound Chips: " + ss.str()); + Log("[NSF] ROM size: " + std::to_string(romData.PrgRom.size() / 1024) + " KB"); +} + +void NsfLoader::InitHeader(NsfHeader & header) +{ + memset(&header, 0, sizeof(NsfHeader)); + for(int i = 0; i < 256; i++) { + //Used by NSFE + header.TrackLength[i] = -1; + header.TrackFade[i] = -1; + } +} + +RomData NsfLoader::LoadRom(vector& romFile) +{ + RomData romData; + NsfHeader &header = romData.NsfInfo; + + InitHeader(header); + + uint8_t* data = romFile.data(); + Read(data, header.Header, 5); + Read(data, header.Version); + Read(data, header.TotalSongs); + Read(data, header.StartingSong); + Read(data, header.LoadAddress); + Read(data, header.InitAddress); + Read(data, header.PlayAddress); + Read(data, header.SongName, 32); + Read(data, header.ArtistName, 32); + Read(data, header.CopyrightHolder, 32); + Read(data, header.PlaySpeedNtsc); + Read(data, (char*)header.BankSetup, 8); + Read(data, header.PlaySpeedPal); + Read(data, header.Flags); + Read(data, header.SoundChips); + Read(data, (char*)header.Padding, 4); + + //Ensure strings are null terminated + header.SongName[31] = 0; + header.ArtistName[31] = 0; + header.CopyrightHolder[31] = 0; + + if(header.PlaySpeedNtsc == 16666 || header.PlaySpeedNtsc == 16667) { + header.PlaySpeedNtsc = 16639; + } + if(header.PlaySpeedPal == 20000) { + header.PlaySpeedPal = 19997; + } + + //Pad start of file to make the first block start at a multiple of 4k + romData.PrgRom.insert(romData.PrgRom.end(), header.LoadAddress % 4096, 0); + + romData.PrgRom.insert(romData.PrgRom.end(), data, data + romFile.size() - 0x80); + + //Pad out the last block to be a multiple of 4k + if(romData.PrgRom.size() % 4096 != 0) { + romData.PrgRom.insert(romData.PrgRom.end(), 4096 - (romData.PrgRom.size() % 4096), 0); + } + + InitializeFromHeader(romData); + + return romData; +} diff --git a/Core/NsfLoader.h b/Core/NsfLoader.h index 1478460e..3fed453f 100644 --- a/Core/NsfLoader.h +++ b/Core/NsfLoader.h @@ -1,176 +1,23 @@ #pragma once #include "stdafx.h" -#include "RomData.h" #include "BaseLoader.h" +struct NsfHeader; +struct RomData; + class NsfLoader : public BaseLoader { private: - void Read(uint8_t* &data, uint8_t& dest) - { - dest = data[0]; - data++; - } + void Read(uint8_t* &data, uint8_t& dest); + void Read(uint8_t* &data, uint16_t& dest); + void Read(uint8_t* &data, char* dest, size_t len); - void Read(uint8_t* &data, uint16_t& dest) - { - dest = data[0] | (data[1] << 8); - data += 2; - } - - void Read(uint8_t* &data, char* dest, size_t len) - { - memcpy(dest, data, len); - data += len; - } protected: - void InitializeFromHeader(RomData& romData) - { - NsfHeader &header = romData.NsfInfo; - - romData.Format = RomFormat::Nsf; - romData.MapperID = MapperFactory::NsfMapperID; - - if(header.LoadAddress < 0x6000 || header.TotalSongs == 0) { - romData.Error = true; - } - - if(header.Flags == 0x01) { - romData.System = GameSystem::NesPal; - } - - if(header.PlaySpeedNtsc == 0) { - header.PlaySpeedNtsc = 16639; - } - - if(header.PlaySpeedPal == 0) { - header.PlaySpeedPal = 19997; - } - - if(header.StartingSong > header.TotalSongs || header.StartingSong == 0) { - header.StartingSong = 1; - } - - //Log window output - Log("[NSF] Region: " + string(header.Flags == 0x00 ? "NTSC" : (header.Flags == 0x01 ? "PAL" : "NTSC & PAL"))); - if(header.PlaySpeedNtsc > 0) { - Log("[NSF] Play speed (NTSC): " + std::to_string(1000000.0 / (double)header.PlaySpeedNtsc) + " Hz"); - } - if(header.PlaySpeedPal > 0) { - Log("[NSF] Play speed (PAL): " + std::to_string(1000000.0 / (double)header.PlaySpeedPal) + " Hz"); - } - Log("[NSF] Title: " + string(header.SongName)); - Log("[NSF] Artist: " + string(header.ArtistName)); - Log("[NSF] Copyright: " + string(header.CopyrightHolder)); - Log("[NSF] Ripper: " + string(header.RipperName)); - - stringstream ss; - ss << "[NSF] Load Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.LoadAddress; - Log(ss.str()); - ss = stringstream(); - ss << "[NSF] Init Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.InitAddress; - Log(ss.str()); - ss = stringstream(); - ss << "[NSF] Play Address: 0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << header.PlayAddress; - Log(ss.str()); - - vector chips; - if(header.SoundChips & 0x01) { - chips.push_back("VRC6"); - } - if(header.SoundChips & 0x02) { - chips.push_back("VRC7"); - } - if(header.SoundChips & 0x04) { - chips.push_back("FDS"); - } - if(header.SoundChips & 0x08) { - chips.push_back("MMC5"); - } - if(header.SoundChips & 0x10) { - chips.push_back("Namco 163"); - } - if(header.SoundChips & 0x20) { - chips.push_back("Sunsoft 5B"); - } - if(chips.empty()) { - chips.push_back(""); - } - - ss = stringstream(); - for(size_t i = 0; i < chips.size(); i++) { - if(i > 0) { - ss << ", "; - } - ss << chips[i]; - } - - Log("[NSF] Sound Chips: " + ss.str()); - Log("[NSF] ROM size: " + std::to_string(romData.PrgRom.size() / 1024) + " KB"); - } - - void InitHeader(NsfHeader& header) - { - memset(&header, 0, sizeof(NsfHeader)); - for(int i = 0; i < 256; i++) { - //Used by NSFE - header.TrackLength[i] = -1; - header.TrackFade[i] = -1; - } - } + void InitializeFromHeader(RomData& romData); + void InitHeader(NsfHeader& header); public: using BaseLoader::BaseLoader; - RomData LoadRom(vector& romFile) - { - RomData romData; - NsfHeader &header = romData.NsfInfo; - - InitHeader(header); - - uint8_t* data = romFile.data(); - Read(data, header.Header, 5); - Read(data, header.Version); - Read(data, header.TotalSongs); - Read(data, header.StartingSong); - Read(data, header.LoadAddress); - Read(data, header.InitAddress); - Read(data, header.PlayAddress); - Read(data, header.SongName, 32); - Read(data, header.ArtistName, 32); - Read(data, header.CopyrightHolder, 32); - Read(data, header.PlaySpeedNtsc); - Read(data, (char*)header.BankSetup, 8); - Read(data, header.PlaySpeedPal); - Read(data, header.Flags); - Read(data, header.SoundChips); - Read(data, (char*)header.Padding, 4); - - //Ensure strings are null terminated - header.SongName[31] = 0; - header.ArtistName[31] = 0; - header.CopyrightHolder[31] = 0; - - if(header.PlaySpeedNtsc == 16666 || header.PlaySpeedNtsc == 16667) { - header.PlaySpeedNtsc = 16639; - } - if(header.PlaySpeedPal == 20000) { - header.PlaySpeedPal = 19997; - } - - //Pad start of file to make the first block start at a multiple of 4k - romData.PrgRom.insert(romData.PrgRom.end(), header.LoadAddress % 4096, 0); - - romData.PrgRom.insert(romData.PrgRom.end(), data, data + romFile.size() - 0x80); - - //Pad out the last block to be a multiple of 4k - if(romData.PrgRom.size() % 4096 != 0) { - romData.PrgRom.insert(romData.PrgRom.end(), 4096 - (romData.PrgRom.size() % 4096), 0); - } - - InitializeFromHeader(romData); - - return romData; - } + RomData LoadRom(vector& romFile); }; \ No newline at end of file diff --git a/Core/NsfPpu.cpp b/Core/NsfPpu.cpp new file mode 100644 index 00000000..2ce352eb --- /dev/null +++ b/Core/NsfPpu.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "NsfPpu.h" +#include "Console.h" +#include "NotificationManager.h" + +void NsfPpu::DrawPixel() +{ +} + +void NsfPpu::SendFrame() +{ + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone); +} diff --git a/Core/NsfPpu.h b/Core/NsfPpu.h index 0b67078f..a9d9ba9c 100644 --- a/Core/NsfPpu.h +++ b/Core/NsfPpu.h @@ -5,14 +5,8 @@ class NsfPpu : public PPU { protected: - void DrawPixel() - { - } - - void SendFrame() - { - MessageManager::SendNotification(ConsoleNotificationType::PpuFrameDone); - } + void DrawPixel() override; + void SendFrame() override; public: using PPU::PPU; diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 40abbc8e..91ec440d 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -10,6 +10,7 @@ #include "ControlManager.h" #include "MemoryManager.h" #include "Console.h" +#include "NotificationManager.h" PPU::PPU(shared_ptr console) { @@ -1051,7 +1052,7 @@ void PPU::SendFrame() { UpdateGrayscaleAndIntensifyBits(); - MessageManager::SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); #ifdef LIBRETRO _console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer); diff --git a/Core/RomData.h b/Core/RomData.h index 0bd02b3c..d060c24c 100644 --- a/Core/RomData.h +++ b/Core/RomData.h @@ -285,6 +285,7 @@ struct RomData uint32_t PrgChrCrc32 = 0; bool Error = false; + bool BiosMissing = false; NESHeader NesHeader; NsfHeader NsfInfo; diff --git a/Core/ShortcutKeyHandler.cpp b/Core/ShortcutKeyHandler.cpp index 3f5be7c5..974332eb 100644 --- a/Core/ShortcutKeyHandler.cpp +++ b/Core/ShortcutKeyHandler.cpp @@ -12,6 +12,7 @@ #include "MovieManager.h" #include "ControlManager.h" #include "Console.h" +#include "NotificationManager.h" ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr console) { @@ -117,7 +118,7 @@ void ShortcutKeyHandler::CheckMappedKeys() for(uint64_t i = (uint64_t)EmulatorShortcut::SwitchDiskSide; i < (uint64_t)EmulatorShortcut::ShortcutCount; i++) { if(DetectKeyPress((EmulatorShortcut)i)) { void* param = (void*)i; - MessageManager::SendNotification(ConsoleNotificationType::ExecuteShortcut, param); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, param); } } @@ -169,11 +170,11 @@ void ShortcutKeyHandler::CheckMappedKeys() } if(DetectKeyPress(EmulatorShortcut::ToggleCheats) && !isNetplayClient && !isMovieActive) { - MessageManager::SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleCheats); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleCheats); } if(DetectKeyPress(EmulatorShortcut::ToggleAudio)) { - MessageManager::SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleAudio); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleAudio); } if(DetectKeyPress(EmulatorShortcut::RunSingleFrame)) { diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index 207ea4a8..ad141e25 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -16,6 +16,7 @@ #include "HdNesPack.h" #include "RotateFilter.h" #include "DebugHud.h" +#include "NotificationManager.h" VideoDecoder::VideoDecoder(shared_ptr console) { @@ -121,7 +122,7 @@ void VideoDecoder::DecodeFrame(bool synchronous) ScreenSize screenSize; GetScreenSize(screenSize, true); if(_previousScale != EmulationSettings::GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) { - MessageManager::SendNotification(ConsoleNotificationType::ResolutionChanged); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ResolutionChanged); } _previousScale = EmulationSettings::GetVideoScale(); _previousScreenSize = screenSize; diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index f7373deb..4a392836 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "../Core/MessageManager.h" +#include "../Core/NotificationManager.h" #include "../Core/Console.h" #include "../Core/GameServer.h" #include "../Core/GameClient.h" @@ -360,7 +361,7 @@ namespace InteropEmu { auto lock = _externalNotificationListenerLock.AcquireSafe(); auto listener = shared_ptr(new InteropNotificationListener(callback)); _externalNotificationListeners.push_back(listener); - MessageManager::RegisterNotificationListener(listener); + _console->GetNotificationManager()->RegisterNotificationListener(listener); return listener.get(); } @@ -413,35 +414,34 @@ namespace InteropEmu { DllExport int32_t __stdcall RunRecordedTest(char* filename) { _recordedRomTest.reset(new RecordedRomTest(_console)); - MessageManager::RegisterNotificationListener(_recordedRomTest); + _console->GetNotificationManager()->RegisterNotificationListener(_recordedRomTest); return _recordedRomTest->Run(filename); } DllExport int32_t __stdcall RunAutomaticTest(char* filename) { shared_ptr romTest(new AutomaticRomTest()); - MessageManager::RegisterNotificationListener(romTest); return romTest->Run(filename); } DllExport void __stdcall RomTestRecord(char* filename, bool reset) { _recordedRomTest.reset(new RecordedRomTest(_console)); - MessageManager::RegisterNotificationListener(_recordedRomTest); + _console->GetNotificationManager()->RegisterNotificationListener(_recordedRomTest); _recordedRomTest->Record(filename, reset); } DllExport void __stdcall RomTestRecordFromMovie(char* testFilename, char* movieFilename) { _recordedRomTest.reset(new RecordedRomTest(_console)); - MessageManager::RegisterNotificationListener(_recordedRomTest); + _console->GetNotificationManager()->RegisterNotificationListener(_recordedRomTest); _recordedRomTest->RecordFromMovie(testFilename, string(movieFilename)); } DllExport void __stdcall RomTestRecordFromTest(char* newTestFilename, char* existingTestFilename) { _recordedRomTest.reset(new RecordedRomTest(_console)); - MessageManager::RegisterNotificationListener(_recordedRomTest); + _console->GetNotificationManager()->RegisterNotificationListener(_recordedRomTest); _recordedRomTest->RecordFromTest(newTestFilename, existingTestFilename); }