mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-23 09:09:45 +00:00
Input: Added support for ~20 new peripherals (Incomplete, more fixes/changes to come)
Movies: Rewrote format to support all IO devices and console types Netplay: Now supports all IO devices and console types
This commit is contained in:
parent
3f83097e64
commit
850102bbdc
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
.vs/*
|
||||
|
||||
# Build results
|
||||
|
||||
@ -165,4 +166,4 @@ $RECYCLE.BIN/
|
||||
*.VC.opendb
|
||||
*.VC.db
|
||||
*.VC.db-wal
|
||||
*.VC.db-shm
|
||||
*.VC.db-shm
|
@ -1,101 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "ArkanoidController.h"
|
||||
#include "ControlManager.h"
|
||||
#include "PPU.h"
|
||||
#include "GameServerConnection.h"
|
||||
#include "IKeyManager.h"
|
||||
|
||||
void ArkanoidController::StreamState(bool saving)
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_stateBuffer, _buttonPressed, _xPosition);
|
||||
}
|
||||
|
||||
uint8_t ArkanoidController::GetPortOutput()
|
||||
{
|
||||
return GetControlState();
|
||||
}
|
||||
|
||||
bool ArkanoidController::IsButtonPressed()
|
||||
{
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::InBackground) || EmulationSettings::CheckFlag(EmulationFlags::AllowBackgroundInput)) {
|
||||
if(ControlManager::IsMouseButtonPressed(MouseButton::LeftButton)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ArkanoidController::GetNetPlayState()
|
||||
{
|
||||
//Used by netplay
|
||||
uint32_t state = ControlManager::GetMousePosition().X;
|
||||
|
||||
if(IsButtonPressed()) {
|
||||
state |= 0x40000000;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
uint8_t ArkanoidController::ProcessNetPlayState(uint32_t netplayState)
|
||||
{
|
||||
_xPosition = netplayState & 0xFF;
|
||||
_buttonPressed = ((netplayState >> 30) & 0x01) == 0x01;
|
||||
|
||||
return RefreshState();
|
||||
}
|
||||
|
||||
void ArkanoidController::RefreshStateBuffer()
|
||||
{
|
||||
const uint8_t validRange = 0xF4 - 0x54;
|
||||
if(!GameServerConnection::GetNetPlayDevice(_port)) {
|
||||
_xPosition = ControlManager::GetMousePosition().X;
|
||||
}
|
||||
|
||||
_xPosition -= 48;
|
||||
if(_xPosition < 0) {
|
||||
_xPosition = 0;
|
||||
} else if(_xPosition >= 160) {
|
||||
_xPosition = 159;
|
||||
}
|
||||
|
||||
_stateBuffer = 0x54 + (uint32_t)(((double)_xPosition / 159) * validRange);
|
||||
}
|
||||
|
||||
uint8_t ArkanoidController::RefreshState()
|
||||
{
|
||||
if(!GameServerConnection::GetNetPlayDevice(_port)) {
|
||||
_buttonPressed = IsButtonPressed();
|
||||
}
|
||||
|
||||
uint8_t output = ((~_stateBuffer) >> 3) & 0x10;
|
||||
_stateBuffer <<= 1;
|
||||
|
||||
if(_buttonPressed) {
|
||||
output |= 0x08;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t ArkanoidController::GetExpansionPortOutput(uint8_t port)
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(port == 0) {
|
||||
//Fire button is on port 1
|
||||
if(!GameServerConnection::GetNetPlayDevice(_port)) {
|
||||
_buttonPressed = IsButtonPressed();
|
||||
}
|
||||
|
||||
if(_buttonPressed) {
|
||||
output |= 0x02;
|
||||
}
|
||||
} else if(port == 1) {
|
||||
//Serial data is on port 2
|
||||
uint8_t arkanoidBits = GetPortOutput();
|
||||
output |= (arkanoidBits >> 3) & 0x02;
|
||||
}
|
||||
return output;
|
||||
}
|
@ -1,28 +1,87 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "ControlManager.h"
|
||||
#include "PPU.h"
|
||||
#include "IKeyManager.h"
|
||||
#include "KeyManager.h"
|
||||
|
||||
class ArkanoidController : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint32_t _currentValue = (0xF4 - 0x54) / 2;
|
||||
uint32_t _stateBuffer = 0;
|
||||
bool _buttonPressed = false;
|
||||
int32_t _xPosition = 0;
|
||||
|
||||
bool IsButtonPressed();
|
||||
enum Buttons { Fire };
|
||||
|
||||
protected:
|
||||
uint8_t RefreshState() override;
|
||||
virtual void StreamState(bool saving) override;
|
||||
bool HasCoordinates() override { return true; }
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "F";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
if(EmulationSettings::InputEnabled()) {
|
||||
SetPressedState(Buttons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
SetMovement(KeyManager::GetMouseMovement());
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_stateBuffer, _currentValue);
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
MouseMovement mov = GetMovement();
|
||||
|
||||
_currentValue += mov.dx;
|
||||
if(_currentValue < 0x54) {
|
||||
_currentValue = 0x54;
|
||||
} else if(_currentValue > 0xF4) {
|
||||
_currentValue = 0xF4;
|
||||
}
|
||||
|
||||
_stateBuffer = _currentValue;
|
||||
}
|
||||
|
||||
public:
|
||||
using BaseControlDevice::BaseControlDevice;
|
||||
ArkanoidController(uint8_t port) : BaseControlDevice(port)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t GetPortOutput() override;
|
||||
void RefreshStateBuffer() override;
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(IsExpansionDevice()) {
|
||||
if(addr == 0x4016) {
|
||||
//Fire button is on port 1
|
||||
if(IsPressed(ArkanoidController::Buttons::Fire)) {
|
||||
output |= 0x02;
|
||||
}
|
||||
} else if(addr == 0x4017) {
|
||||
//Serial data is on port 2
|
||||
output |= ((~_stateBuffer) >> 6) & 0x02;
|
||||
_stateBuffer <<= 1;
|
||||
}
|
||||
} else if(IsCurrentPort(addr)) {
|
||||
output = ((~_stateBuffer) >> 3) & 0x10;
|
||||
_stateBuffer <<= 1;
|
||||
|
||||
virtual uint32_t GetNetPlayState() override;
|
||||
uint8_t ProcessNetPlayState(uint32_t netplayState) override;
|
||||
if(IsPressed(ArkanoidController::Buttons::Fire)) {
|
||||
output |= 0x08;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetExpansionPortOutput(uint8_t port);
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
65
Core/AsciiTurboFile.h
Normal file
65
Core/AsciiTurboFile.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "Console.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "IBattery.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
class AsciiTurboFile : public BaseControlDevice, public IBattery
|
||||
{
|
||||
private:
|
||||
static const int FileSize = 0x2000;
|
||||
static const int BitCount = FileSize * 8;
|
||||
uint8_t _lastWrite = 0;
|
||||
uint16_t _position = 0;
|
||||
uint8_t _data[AsciiTurboFile::FileSize];
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
ArrayInfo<uint8_t> data{ _data, AsciiTurboFile::FileSize };
|
||||
Stream(_position, _lastWrite, data);
|
||||
}
|
||||
|
||||
public:
|
||||
AsciiTurboFile() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
BatteryManager::LoadBattery(".tf", _data, AsciiTurboFile::FileSize);
|
||||
}
|
||||
|
||||
~AsciiTurboFile()
|
||||
{
|
||||
SaveBattery();
|
||||
}
|
||||
|
||||
void SaveBattery()
|
||||
{
|
||||
BatteryManager::SaveBattery(".tf", _data, AsciiTurboFile::FileSize);
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
return ((_data[_position / 8] >> (_position % 8)) & 0x01) << 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(!(value & 0x02)) {
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
if(!(value & 0x04) && (_lastWrite & 0x04)) {
|
||||
//Clock, perform write, increase position
|
||||
_data[_position / 8] &= ~(1 << (_position % 8));
|
||||
_data[_position / 8] |= (value & 0x01) << (_position % 8);
|
||||
_position = (_position + 1) & (AsciiTurboFile::BitCount - 1);
|
||||
}
|
||||
|
||||
_lastWrite = value;
|
||||
}
|
||||
};
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "BaseMapper.h"
|
||||
#include "CPU.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "DatachBarcodeReader.h"
|
||||
|
||||
class BandaiFcg : public BaseMapper
|
||||
{
|
||||
@ -11,6 +13,7 @@ private:
|
||||
uint8_t _prgPage;
|
||||
uint8_t _prgBankSelect;
|
||||
uint8_t _chrRegs[8];
|
||||
shared_ptr<DatachBarcodeReader> _barcodeReader;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
@ -18,6 +21,7 @@ protected:
|
||||
uint16_t RegisterStartAddress() override { return 0x6000; }
|
||||
uint16_t RegisterEndAddress() override { return 0xFFFF; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
ConsoleFeatures GetAvailableFeatures() override { return _mapperID == 157 ? ConsoleFeatures::BarcodeReader : ConsoleFeatures::None; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
@ -27,14 +31,22 @@ protected:
|
||||
_irqReload = 0;
|
||||
_prgPage = 0;
|
||||
_prgBankSelect = 0;
|
||||
|
||||
if(_mapperID == 157) {
|
||||
//"Mapper 157 is used for Datach Joint ROM System boards"
|
||||
_barcodeReader.reset(new DatachBarcodeReader());
|
||||
_mapperControlDevice = _barcodeReader;
|
||||
}
|
||||
|
||||
//Only allow reads from 0x6000 to 0xFFFF
|
||||
//Only allow reads from 0x6000 to 0x7FFF
|
||||
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
|
||||
|
||||
if(_mapperID != 16 || GetPRGPageCount() >= 0x20) {
|
||||
//"For iNES Mapper 153 (with SRAM), the writeable ports must only be mirrored across $8000-$FFFF."
|
||||
//"Mappers 157 and 159 do not need to support the FCG-1 and -2 and so should only mirror the ports across $8000-$FFFF."
|
||||
RemoveRegisterRange(0x6000, 0x7FFF, MemoryOperation::Any);
|
||||
|
||||
//TODO: Check if this is needed
|
||||
//RemoveRegisterRange(0x6000, 0x7FFF, MemoryOperation::Any);
|
||||
}
|
||||
|
||||
//Last bank
|
||||
@ -61,11 +73,11 @@ protected:
|
||||
_irqCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
//Pretend EEPROM data is always 0
|
||||
return 0;
|
||||
return _barcodeReader->GetOutput() | MemoryManager::GetOpenBus(0xE7);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
|
76
Core/BandaiHyperShot.h
Normal file
76
Core/BandaiHyperShot.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "StandardController.h"
|
||||
#include "Zapper.h"
|
||||
#include "IKeyManager.h"
|
||||
#include "KeyManager.h"
|
||||
|
||||
class BandaiHyperShot : public StandardController
|
||||
{
|
||||
private:
|
||||
uint32_t _stateBuffer = 0;
|
||||
|
||||
protected:
|
||||
enum ZapperButtons { Fire = 9 };
|
||||
|
||||
bool HasCoordinates() override { return true; }
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return StandardController::GetKeyNames() + "F";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
StandardController::InternalSetStateFromInput();
|
||||
|
||||
if(EmulationSettings::InputEnabled()) {
|
||||
SetPressedState(ZapperButtons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
|
||||
MousePosition pos = KeyManager::GetMousePosition();
|
||||
if(KeyManager::IsMouseButtonPressed(MouseButton::RightButton)) {
|
||||
pos.X = -1;
|
||||
pos.Y = -1;
|
||||
}
|
||||
SetCoordinates(pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLightFound()
|
||||
{
|
||||
return Zapper::StaticIsLightFound(GetCoordinates());
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_stateBuffer);
|
||||
}
|
||||
|
||||
public:
|
||||
BandaiHyperShot(KeyMappingSet keyMappings) : StandardController(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
_stateBuffer = (uint32_t)ToByte();
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4016) {
|
||||
uint8_t output = (_stateBuffer & 0x01) << 1;
|
||||
_stateBuffer >>= 1;
|
||||
StrobeProcessRead();
|
||||
return output;
|
||||
} else {
|
||||
return (IsLightFound() ? 0 : 0x08) | (IsPressed(BandaiHyperShot::ZapperButtons::Fire) ? 0x10 : 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
@ -3,13 +3,15 @@
|
||||
#include "BaseMapper.h"
|
||||
#include "ControlManager.h"
|
||||
#include "StandardController.h"
|
||||
#include "BandaiMicrophone.h"
|
||||
|
||||
class BandaiKaraoke : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
virtual bool AllowRegisterRead() override { return true; }
|
||||
uint16_t GetPRGPageSize() override { return 0x4000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
bool AllowRegisterRead() override { return true; }
|
||||
bool HasBusConflicts() override { return true; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
@ -19,12 +21,13 @@ protected:
|
||||
SelectPRGPage(0, 0);
|
||||
SelectPRGPage(1, 0x07);
|
||||
SelectCHRPage(0, 0);
|
||||
|
||||
_mapperControlDevice.reset(new BandaiMicrophone(EmulationSettings::GetControllerKeys(0)));
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr) override
|
||||
{
|
||||
//Microphone not implemented - always return A/B buttons as not pressed
|
||||
return 0x03;
|
||||
return _mapperControlDevice->ReadRAM(addr) | MemoryManager::GetOpenBus(0xF8);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
|
45
Core/BandaiMicrophone.h
Normal file
45
Core/BandaiMicrophone.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class BandaiMicrophone : public BaseControlDevice
|
||||
{
|
||||
protected:
|
||||
enum Buttons { A, B, Microphone };
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "ABM";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
//TODO: Add proper key mappings
|
||||
SetPressedState(Buttons::A, keyMapping.A);
|
||||
SetPressedState(Buttons::B, keyMapping.B);
|
||||
SetPressedState(Buttons::Microphone, keyMapping.Microphone);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BandaiMicrophone(KeyMappingSet keyMappings) : BaseControlDevice(BaseControlDevice::MapperInputPort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr >= 0x6000 && addr <= 0x7FFF) {
|
||||
return
|
||||
(IsPressed(Buttons::A) ? 0 : 0x01) |
|
||||
(IsPressed(Buttons::B) ? 0 : 0x02) |
|
||||
(IsPressed(Buttons::Microphone) ? 0x04 : 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
}
|
||||
};
|
102
Core/BarcodeBattlerReader.h
Normal file
102
Core/BarcodeBattlerReader.h
Normal file
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "IBarcodeReader.h"
|
||||
#include "CPU.h"
|
||||
|
||||
class BarcodeBattlerReader : public BaseControlDevice, public IBarcodeReader
|
||||
{
|
||||
private:
|
||||
static const int StreamSize = 200;
|
||||
uint64_t _newBarcode = 0;
|
||||
uint32_t _newBarcodeDigitCount = 0;
|
||||
|
||||
uint8_t _barcodeStream[BarcodeBattlerReader::StreamSize];
|
||||
int32_t _insertCycle = 0;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
|
||||
ArrayInfo<uint8_t> bitStream{ _barcodeStream, BarcodeBattlerReader::StreamSize };
|
||||
Stream(_newBarcode, _newBarcodeDigitCount, _insertCycle, bitStream);
|
||||
}
|
||||
|
||||
bool IsRawString() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitBarcodeStream()
|
||||
{
|
||||
string barcodeText(_state.State.begin(), _state.State.end());
|
||||
|
||||
//Signature at the end, needed for code to be recognized
|
||||
barcodeText += "EPOCH\xD\xA";
|
||||
//Pad to 20 characters with spaces
|
||||
barcodeText.insert(0, 20 - barcodeText.size(), ' ');
|
||||
|
||||
int pos = 0;
|
||||
vector<uint8_t> bits;
|
||||
for(int i = 0; i < 20; i++) {
|
||||
_barcodeStream[pos++] = 1;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
_barcodeStream[pos++] = ~((barcodeText[i] >> j) & 0x01);
|
||||
}
|
||||
_barcodeStream[pos++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BarcodeBattlerReader() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
ClearState();
|
||||
|
||||
if(_newBarcodeDigitCount > 0) {
|
||||
string barcodeText = std::to_string(_newBarcode);
|
||||
//Pad 8 or 13 character barcode with 0s at start
|
||||
barcodeText.insert(0, _newBarcodeDigitCount - barcodeText.size(), '0');
|
||||
SetTextState(barcodeText);
|
||||
|
||||
_newBarcode = 0;
|
||||
_newBarcodeDigitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterSetState() override
|
||||
{
|
||||
if(GetRawState().State.size() > 0) {
|
||||
InitBarcodeStream();
|
||||
_insertCycle = CPU::GetCycleCount();
|
||||
}
|
||||
}
|
||||
|
||||
void InputBarcode(uint64_t barcode, uint32_t digitCount) override
|
||||
{
|
||||
_newBarcode = barcode;
|
||||
_newBarcodeDigitCount = digitCount;
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
int32_t elapsedCycles = CPU::GetCycleCount() - _insertCycle;
|
||||
constexpr uint32_t cyclesPerBit = CPU::ClockRateNtsc / 1200;
|
||||
|
||||
uint32_t streamPosition = elapsedCycles / cyclesPerBit;
|
||||
if(streamPosition < BarcodeBattlerReader::StreamSize) {
|
||||
return _barcodeStream[streamPosition] << 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
}
|
||||
};
|
@ -1,91 +1,261 @@
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "ControlManager.h"
|
||||
#include "MovieManager.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "GameClient.h"
|
||||
#include "GameServerConnection.h"
|
||||
#include "AutomaticRomTest.h"
|
||||
#include "RewindManager.h"
|
||||
#include "Debugger.h"
|
||||
#include "KeyManager.h"
|
||||
#include "../Utilities/StringUtilities.h"
|
||||
|
||||
BaseControlDevice::BaseControlDevice(uint8_t port)
|
||||
BaseControlDevice::BaseControlDevice(uint8_t port, KeyMappingSet keyMappingSet)
|
||||
{
|
||||
_port = port;
|
||||
_famiconDevice = EmulationSettings::GetConsoleType() == ConsoleType::Famicom;
|
||||
if(EmulationSettings::GetControllerType(port) == ControllerType::StandardController) {
|
||||
AddKeyMappings(EmulationSettings::GetControllerKeys(port));
|
||||
}
|
||||
_strobe = false;
|
||||
_keyMappings = keyMappingSet.GetKeyMappingArray();
|
||||
}
|
||||
|
||||
BaseControlDevice::~BaseControlDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void BaseControlDevice::StreamState(bool saving)
|
||||
{
|
||||
Stream(_currentState);
|
||||
}
|
||||
|
||||
uint8_t BaseControlDevice::GetPort()
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
void BaseControlDevice::AddKeyMappings(KeyMappingSet keyMappings)
|
||||
void BaseControlDevice::SetStateFromInput()
|
||||
{
|
||||
if(keyMappings.Mapping1.HasKeySet()) {
|
||||
_keyMappings.push_back(keyMappings.Mapping1);
|
||||
}
|
||||
if(keyMappings.Mapping2.HasKeySet()) {
|
||||
_keyMappings.push_back(keyMappings.Mapping2);
|
||||
}
|
||||
if(keyMappings.Mapping3.HasKeySet()) {
|
||||
_keyMappings.push_back(keyMappings.Mapping3);
|
||||
}
|
||||
if(keyMappings.Mapping4.HasKeySet()) {
|
||||
_keyMappings.push_back(keyMappings.Mapping4);
|
||||
}
|
||||
_turboSpeed = keyMappings.TurboSpeed;
|
||||
ClearState();
|
||||
InternalSetStateFromInput();
|
||||
}
|
||||
|
||||
void BaseControlDevice::RefreshStateBuffer()
|
||||
void BaseControlDevice::InternalSetStateFromInput()
|
||||
{
|
||||
//Do nothing by default - used by standard controllers and some others
|
||||
}
|
||||
|
||||
uint8_t BaseControlDevice::ProcessNetPlayState(uint32_t netplayState)
|
||||
void BaseControlDevice::StreamState(bool saving)
|
||||
{
|
||||
return netplayState;
|
||||
ArrayInfo<uint8_t> state{ _state.State.data(), (uint32_t)_state.State.size() };
|
||||
Stream(_strobe, state);
|
||||
}
|
||||
|
||||
uint8_t BaseControlDevice::GetControlState()
|
||||
bool BaseControlDevice::IsCurrentPort(uint16_t addr)
|
||||
{
|
||||
GameServerConnection* netPlayDevice = GameServerConnection::GetNetPlayDevice(_port);
|
||||
if(RewindManager::IsRewinding()) {
|
||||
_currentState = RewindManager::GetInput(_port);
|
||||
} else if(MovieManager::Playing()) {
|
||||
_currentState = MovieManager::GetState(_port);
|
||||
} else if(GameClient::Connected()) {
|
||||
_currentState = GameClient::GetControllerState(_port);
|
||||
} else if(AutomaticRomTest::Running()) {
|
||||
_currentState = AutomaticRomTest::GetControllerState(_port);
|
||||
} else if(netPlayDevice) {
|
||||
_currentState = ProcessNetPlayState(netPlayDevice->GetState());
|
||||
} else if(Debugger::HasInputOverride(_port)) {
|
||||
_currentState = ProcessNetPlayState(Debugger::GetInputOverride(_port));
|
||||
return _port == (addr - 0x4016);
|
||||
}
|
||||
|
||||
bool BaseControlDevice::IsExpansionDevice()
|
||||
{
|
||||
return _port == BaseControlDevice::ExpDevicePort;
|
||||
}
|
||||
|
||||
void BaseControlDevice::StrobeProcessRead()
|
||||
{
|
||||
if(_strobe) {
|
||||
RefreshStateBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::StrobeProcessWrite(uint8_t value)
|
||||
{
|
||||
bool prevStrobe = _strobe;
|
||||
_strobe = (value & 0x01) == 0x01;
|
||||
|
||||
if(prevStrobe && !_strobe) {
|
||||
RefreshStateBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::ClearState()
|
||||
{
|
||||
_state = ControlDeviceState();
|
||||
}
|
||||
|
||||
ControlDeviceState BaseControlDevice::GetRawState()
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetRawState(ControlDeviceState state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetTextState(string textState)
|
||||
{
|
||||
ClearState();
|
||||
|
||||
if(IsRawString()) {
|
||||
_state.State.insert(_state.State.end(), textState.begin(), textState.end());
|
||||
} else {
|
||||
_currentState = RefreshState();
|
||||
if(HasCoordinates()) {
|
||||
vector<string> data = StringUtilities::Split(textState, ' ');
|
||||
if(data.size() >= 3) {
|
||||
MousePosition pos;
|
||||
try {
|
||||
pos.X = (int16_t)std::stol(data[0]);
|
||||
pos.Y = (int16_t)std::stol(data[1]);
|
||||
} catch(std::exception ex) {
|
||||
pos.X = -1;
|
||||
pos.Y = -1;
|
||||
}
|
||||
SetCoordinates(pos);
|
||||
textState = data[2];
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for(char c : textState) {
|
||||
if(c != '.') {
|
||||
SetBit(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(MovieManager::Recording()) {
|
||||
MovieManager::RecordState(_port, _currentState);
|
||||
string BaseControlDevice::GetTextState()
|
||||
{
|
||||
if(IsRawString()) {
|
||||
return string((char*)_state.State.data(), _state.State.size());
|
||||
} else {
|
||||
string keyNames = GetKeyNames();
|
||||
string output = "";
|
||||
|
||||
if(HasCoordinates()) {
|
||||
MousePosition pos = GetCoordinates();
|
||||
output += std::to_string(pos.X) + " " + std::to_string(pos.Y) + " ";
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < keyNames.size(); i++) {
|
||||
output += IsPressed((uint8_t)i) ? keyNames[i] : '.';
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
//For NetPlay
|
||||
ControlManager::BroadcastInput(_port, _currentState);
|
||||
void BaseControlDevice::EnsureCapacity(int32_t minBitCount)
|
||||
{
|
||||
uint32_t minByteCount = minBitCount / 8 + 1 + (HasCoordinates() ? 32 : 0);
|
||||
int32_t gap = minByteCount - (int32_t)_state.State.size();
|
||||
|
||||
RewindManager::RecordInput(_port, _currentState);
|
||||
if(gap > 0) {
|
||||
_state.State.insert(_state.State.end(), gap, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return _currentState;
|
||||
}
|
||||
bool BaseControlDevice::HasCoordinates()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseControlDevice::IsRawString()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t BaseControlDevice::GetByteIndex(uint8_t bit)
|
||||
{
|
||||
return bit / 8 + (HasCoordinates() ? 4 : 0);
|
||||
}
|
||||
|
||||
bool BaseControlDevice::IsPressed(uint8_t bit)
|
||||
{
|
||||
EnsureCapacity(bit);
|
||||
uint8_t bitMask = 1 << (bit % 8);
|
||||
return (_state.State[GetByteIndex(bit)] & bitMask) != 0;
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetBitValue(uint8_t bit, bool set)
|
||||
{
|
||||
if(set) {
|
||||
SetBit(bit);
|
||||
} else {
|
||||
ClearBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetBit(uint8_t bit)
|
||||
{
|
||||
EnsureCapacity(bit);
|
||||
uint8_t bitMask = 1 << (bit % 8);
|
||||
_state.State[GetByteIndex(bit)] |= bitMask;
|
||||
}
|
||||
|
||||
void BaseControlDevice::ClearBit(uint8_t bit)
|
||||
{
|
||||
EnsureCapacity(bit);
|
||||
uint8_t bitMask = 1 << (bit % 8);
|
||||
_state.State[GetByteIndex(bit)] &= ~bitMask;
|
||||
}
|
||||
|
||||
void BaseControlDevice::InvertBit(uint8_t bit)
|
||||
{
|
||||
if(IsPressed(bit)) {
|
||||
ClearBit(bit);
|
||||
} else {
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetPressedState(uint8_t bit, uint32_t keyCode)
|
||||
{
|
||||
if(EmulationSettings::InputEnabled() && KeyManager::IsKeyPressed(keyCode)) {
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetPressedState(uint8_t bit, bool enabled)
|
||||
{
|
||||
if(enabled) {
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetCoordinates(MousePosition pos)
|
||||
{
|
||||
EnsureCapacity(-1);
|
||||
|
||||
_state.State[0] = pos.X & 0xFF;
|
||||
_state.State[1] = (pos.X >> 8) & 0xFF;
|
||||
_state.State[2] = pos.Y & 0xFF;
|
||||
_state.State[3] = (pos.Y >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
MousePosition BaseControlDevice::GetCoordinates()
|
||||
{
|
||||
EnsureCapacity(-1);
|
||||
|
||||
MousePosition pos;
|
||||
pos.X = _state.State[0] | (_state.State[1] << 8);
|
||||
pos.Y = _state.State[2] | (_state.State[3] << 8);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetMovement(MouseMovement mov)
|
||||
{
|
||||
MouseMovement prev = GetMovement();
|
||||
mov.dx += prev.dx;
|
||||
mov.dy += prev.dy;
|
||||
SetCoordinates({ mov.dx, mov.dy });
|
||||
}
|
||||
|
||||
MouseMovement BaseControlDevice::GetMovement()
|
||||
{
|
||||
MousePosition pos = GetCoordinates();
|
||||
SetCoordinates({ 0, 0 });
|
||||
return { pos.X, pos.Y };
|
||||
}
|
||||
|
||||
void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2)
|
||||
{
|
||||
bool pressed1 = state1->IsPressed(button1);
|
||||
bool pressed2 = state2->IsPressed(button2);
|
||||
|
||||
state1->ClearBit(button1);
|
||||
state2->ClearBit(button2);
|
||||
|
||||
if(pressed1) {
|
||||
state2->SetBit(button2);
|
||||
}
|
||||
if(pressed2) {
|
||||
state1->SetBit(button1);
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
struct ButtonState
|
||||
{
|
||||
bool Up = false;
|
||||
bool Down = false;
|
||||
bool Left = false;
|
||||
bool Right = false;
|
||||
|
||||
bool A = false;
|
||||
bool B = false;
|
||||
|
||||
bool Select = 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;
|
||||
}
|
||||
};
|
||||
#include "ControlManager.h"
|
||||
#include "ControlDeviceState.h"
|
||||
|
||||
class BaseControlDevice : public Snapshotable
|
||||
{
|
||||
protected:
|
||||
uint8_t _port;
|
||||
uint8_t _currentState;
|
||||
ControlDeviceState _state;
|
||||
vector<KeyMapping> _keyMappings;
|
||||
uint32_t _turboSpeed = 0;
|
||||
bool _famiconDevice = false;
|
||||
bool _strobe;
|
||||
uint8_t _port;
|
||||
|
||||
uint8_t GetPort();
|
||||
void AddKeyMappings(KeyMappingSet keyMappings);
|
||||
virtual void RefreshStateBuffer() { }
|
||||
virtual void StreamState(bool saving);
|
||||
|
||||
void EnsureCapacity(int32_t minBitCount);
|
||||
uint32_t GetByteIndex(uint8_t bit);
|
||||
virtual bool HasCoordinates();
|
||||
virtual bool IsRawString();
|
||||
|
||||
//Defined in controller-specific code and called when we need to read a device's state
|
||||
virtual uint8_t RefreshState() = 0;
|
||||
virtual uint8_t ProcessNetPlayState(uint32_t netplayState);
|
||||
bool IsCurrentPort(uint16_t addr);
|
||||
bool IsExpansionDevice();
|
||||
void StrobeProcessRead();
|
||||
void StrobeProcessWrite(uint8_t value);
|
||||
|
||||
virtual void StreamState(bool saving) override;
|
||||
virtual string GetKeyNames() { return ""; }
|
||||
|
||||
void SetPressedState(uint8_t bit, uint32_t keyCode);
|
||||
void SetPressedState(uint8_t bit, bool enabled);
|
||||
|
||||
void SetCoordinates(MousePosition pos);
|
||||
|
||||
void SetMovement(MouseMovement mov);
|
||||
MouseMovement GetMovement();
|
||||
|
||||
virtual void InternalSetStateFromInput();
|
||||
|
||||
public:
|
||||
//Used by controller-specific code to get the current state (buttons, position, etc)
|
||||
uint8_t GetControlState();
|
||||
static const uint8_t ExpDevicePort = 4;
|
||||
static const uint8_t ConsoleInputPort = 5;
|
||||
static const uint8_t MapperInputPort = 6;
|
||||
static const uint8_t PortCount = MapperInputPort + 1;
|
||||
|
||||
BaseControlDevice(uint8_t port);
|
||||
BaseControlDevice(uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet());
|
||||
virtual ~BaseControlDevice();
|
||||
|
||||
//Called when reading $4016/7
|
||||
virtual uint8_t GetPortOutput() = 0;
|
||||
uint8_t GetPort();
|
||||
|
||||
virtual uint32_t GetNetPlayState() = 0;
|
||||
bool IsPressed(uint8_t bit);
|
||||
MousePosition GetCoordinates();
|
||||
|
||||
//Used by standard controllers when $4017.1 is set
|
||||
virtual void RefreshStateBuffer();
|
||||
};
|
||||
void ClearState();
|
||||
void SetBit(uint8_t bit);
|
||||
void ClearBit(uint8_t bit);
|
||||
void InvertBit(uint8_t bit);
|
||||
void SetBitValue(uint8_t bit, bool set);
|
||||
|
||||
void SetTextState(string state);
|
||||
string GetTextState();
|
||||
|
||||
void SetStateFromInput();
|
||||
virtual void OnAfterSetState() { }
|
||||
|
||||
void SetRawState(ControlDeviceState state);
|
||||
ControlDeviceState GetRawState();
|
||||
|
||||
template<typename T>
|
||||
shared_ptr<T> GetState()
|
||||
{
|
||||
return std::dynamic_pointer_cast<T>(_state);
|
||||
}
|
||||
|
||||
virtual uint8_t ReadRAM(uint16_t addr) = 0;
|
||||
virtual void WriteRAM(uint16_t addr, uint8_t value) = 0;
|
||||
|
||||
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2);
|
||||
};
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "CheatManager.h"
|
||||
#include "Debugger.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
void BaseMapper::WriteRegister(uint16_t addr, uint8_t value) { }
|
||||
uint8_t BaseMapper::ReadRegister(uint16_t addr) { return 0; }
|
||||
@ -338,40 +339,22 @@ bool BaseMapper::HasBattery()
|
||||
void BaseMapper::LoadBattery()
|
||||
{
|
||||
if(HasBattery()) {
|
||||
ifstream batteryFile(_batteryFilename, ios::in | ios::binary);
|
||||
if(batteryFile) {
|
||||
batteryFile.read((char*)_saveRam, _saveRamSize);
|
||||
batteryFile.close();
|
||||
}
|
||||
BatteryManager::LoadBattery(".sav", _saveRam, _saveRamSize);
|
||||
}
|
||||
|
||||
if(_hasChrBattery) {
|
||||
ifstream batteryFile(_batteryFilename + ".chr", ios::in | ios::binary);
|
||||
if(batteryFile) {
|
||||
batteryFile.read((char*)_chrRam, _chrRamSize);
|
||||
batteryFile.close();
|
||||
}
|
||||
BatteryManager::LoadBattery(".sav.chr", _chrRam, _chrRamSize);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMapper::SaveBattery()
|
||||
{
|
||||
if(HasBattery()) {
|
||||
ofstream batteryFile(_batteryFilename, ios::out | ios::binary);
|
||||
|
||||
if(batteryFile) {
|
||||
batteryFile.write((char*)_saveRam, _saveRamSize);
|
||||
|
||||
batteryFile.close();
|
||||
}
|
||||
BatteryManager::SaveBattery(".sav", _saveRam, _saveRamSize);
|
||||
}
|
||||
|
||||
if(_hasChrBattery) {
|
||||
ofstream batteryFile(_batteryFilename + ".chr", ios::out | ios::binary);
|
||||
if(batteryFile) {
|
||||
batteryFile.write((char*)_chrRam, _chrRamSize);
|
||||
batteryFile.close();
|
||||
}
|
||||
BatteryManager::SaveBattery(".sav.chr", _chrRam, _chrRamSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,17 +432,19 @@ void BaseMapper::StreamState(bool saving)
|
||||
ArrayInfo<uint8_t> nametableIndexes = { _nametableIndexes, 4 };
|
||||
Stream(_mirroringType, chrRam, workRam, saveRam, prgPageNumbers, chrPageNumbers, nametableIndexes);
|
||||
|
||||
bool hasExtraNametable[2] = { _cartNametableRam[0] != nullptr, _cartNametableRam[1] != nullptr };
|
||||
Stream(hasExtraNametable[0], hasExtraNametable[1]);
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(hasExtraNametable[i]) {
|
||||
if(!_cartNametableRam[i]) {
|
||||
_cartNametableRam[i] = new uint8_t[0x400];
|
||||
}
|
||||
if(GetStateVersion() >= 7) {
|
||||
bool hasExtraNametable[2] = { _cartNametableRam[0] != nullptr, _cartNametableRam[1] != nullptr };
|
||||
Stream(hasExtraNametable[0], hasExtraNametable[1]);
|
||||
|
||||
ArrayInfo<uint8_t> ram = { _cartNametableRam[i], 0x400 };
|
||||
Stream(ram);
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(hasExtraNametable[i]) {
|
||||
if(!_cartNametableRam[i]) {
|
||||
_cartNametableRam[i] = new uint8_t[0x400];
|
||||
}
|
||||
|
||||
ArrayInfo<uint8_t> ram = { _cartNametableRam[i], 0x400 };
|
||||
Stream(ram);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,7 +481,7 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
_hasBattery = (romData.HasBattery || ForceBattery());
|
||||
|
||||
if(romData.SaveRamSize == -1 || ForceSaveRamSize()) {
|
||||
_saveRamSize = GetSaveRamSize(); //Needed because we need to call SaveBattery() in the destructor (and calling virtual functions in the destructor doesn't work correctly)
|
||||
_saveRamSize = GetSaveRamSize();
|
||||
} else {
|
||||
_saveRamSize = romData.SaveRamSize;
|
||||
}
|
||||
@ -580,7 +565,7 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
} else if(GetChrRamSize()) {
|
||||
InitializeChrRam();
|
||||
}
|
||||
|
||||
|
||||
//Load battery data if present
|
||||
LoadBattery();
|
||||
|
||||
@ -714,6 +699,16 @@ void BaseMapper::SetMirroringType(MirroringType type)
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleFeatures BaseMapper::GetAvailableFeatures()
|
||||
{
|
||||
return ConsoleFeatures::None;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> BaseMapper::GetMapperControlDevice()
|
||||
{
|
||||
return _mapperControlDevice;
|
||||
}
|
||||
|
||||
GameSystem BaseMapper::GetGameSystem()
|
||||
{
|
||||
return _gameSystem;
|
||||
|
@ -9,8 +9,11 @@
|
||||
#include "DebuggerTypes.h"
|
||||
#include "Debugger.h"
|
||||
#include "Types.h"
|
||||
#include "IBattery.h"
|
||||
|
||||
class BaseMapper : public IMemoryHandler, public Snapshotable, public INotificationListener
|
||||
class BaseControlDevice;
|
||||
|
||||
class BaseMapper : public IMemoryHandler, public Snapshotable, public INotificationListener, public IBattery
|
||||
{
|
||||
private:
|
||||
MirroringType _mirroringType;
|
||||
@ -51,6 +54,8 @@ private:
|
||||
vector<uint8_t> _originalChrRom;
|
||||
|
||||
protected:
|
||||
shared_ptr<BaseControlDevice> _mapperControlDevice;
|
||||
|
||||
NESHeader _nesHeader;
|
||||
GameInfo _databaseInfo;
|
||||
|
||||
@ -151,6 +156,8 @@ public:
|
||||
virtual ~BaseMapper();
|
||||
virtual void Reset(bool softReset);
|
||||
|
||||
virtual ConsoleFeatures GetAvailableFeatures();
|
||||
|
||||
virtual void SetNesModel(NesModel model) { }
|
||||
virtual void ProcessCpuClock() { }
|
||||
virtual void NotifyVRAMAddressChange(uint16_t addr);
|
||||
@ -158,10 +165,12 @@ public:
|
||||
virtual void GetMemoryRanges(MemoryRanges &ranges) override;
|
||||
|
||||
void ApplyCheats();
|
||||
virtual void SaveBattery();
|
||||
|
||||
virtual void SaveBattery() override;
|
||||
|
||||
virtual void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB);
|
||||
|
||||
shared_ptr<BaseControlDevice> GetMapperControlDevice();
|
||||
GameSystem GetGameSystem();
|
||||
HashInfo GetHashInfo();
|
||||
string GetRomName();
|
||||
|
69
Core/BatteryManager.cpp
Normal file
69
Core/BatteryManager.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "stdafx.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "VirtualFile.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
|
||||
string BatteryManager::_romName;
|
||||
std::weak_ptr<IBatteryRecorder> BatteryManager::_recorder;
|
||||
std::weak_ptr<IBatteryProvider> BatteryManager::_provider;
|
||||
|
||||
void BatteryManager::Initialize(string romName)
|
||||
{
|
||||
_romName = romName;
|
||||
}
|
||||
|
||||
string BatteryManager::GetBasePath()
|
||||
{
|
||||
return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), _romName);
|
||||
}
|
||||
|
||||
void BatteryManager::SetBatteryProvider(shared_ptr<IBatteryProvider> provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
void BatteryManager::SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder)
|
||||
{
|
||||
_recorder = recorder;
|
||||
}
|
||||
|
||||
void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t length)
|
||||
{
|
||||
ofstream out(GetBasePath() + extension, ios::binary);
|
||||
if(out) {
|
||||
out.write((char*)data, length);
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> BatteryManager::LoadBattery(string extension)
|
||||
{
|
||||
shared_ptr<IBatteryProvider> provider = _provider.lock();
|
||||
|
||||
vector<uint8_t> batteryData;
|
||||
if(provider) {
|
||||
//Used by movie player to provider initial state of ram at startup
|
||||
batteryData = provider->LoadBattery(extension);
|
||||
} else {
|
||||
VirtualFile file = GetBasePath() + extension;
|
||||
if(file.IsValid()) {
|
||||
file.ReadFile(batteryData);
|
||||
}
|
||||
}
|
||||
|
||||
if(!batteryData.empty()) {
|
||||
shared_ptr<IBatteryRecorder> recorder = _recorder.lock();
|
||||
if(recorder) {
|
||||
//Used by movies to record initial state of battery-backed ram at power on
|
||||
recorder->OnLoadBattery(extension, batteryData);
|
||||
}
|
||||
}
|
||||
|
||||
return batteryData;
|
||||
}
|
||||
|
||||
void BatteryManager::LoadBattery(string extension, uint8_t* data, uint32_t length)
|
||||
{
|
||||
vector<uint8_t> batteryData = LoadBattery(extension);
|
||||
memset(data, 0, length);
|
||||
memcpy(data, batteryData.data(), std::min((uint32_t)batteryData.size(), length));
|
||||
}
|
37
Core/BatteryManager.h
Normal file
37
Core/BatteryManager.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class IBatteryProvider
|
||||
{
|
||||
public:
|
||||
virtual vector<uint8_t> LoadBattery(string extension) = 0;
|
||||
};
|
||||
|
||||
class IBatteryRecorder
|
||||
{
|
||||
public:
|
||||
virtual void OnLoadBattery(string extension, vector<uint8_t> batteryData) = 0;
|
||||
};
|
||||
|
||||
class BatteryManager
|
||||
{
|
||||
private:
|
||||
static string _romName;
|
||||
static string GetBasePath();
|
||||
|
||||
static std::weak_ptr<IBatteryProvider> _provider;
|
||||
static std::weak_ptr<IBatteryRecorder> _recorder;
|
||||
|
||||
BatteryManager() = delete;
|
||||
|
||||
public:
|
||||
static void Initialize(string romName);
|
||||
|
||||
static void SetBatteryProvider(shared_ptr<IBatteryProvider> provider);
|
||||
static void SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder);
|
||||
|
||||
static void SaveBattery(string extension, uint8_t* data, uint32_t length);
|
||||
|
||||
static vector<uint8_t> LoadBattery(string extension);
|
||||
static void LoadBattery(string extension, uint8_t* data, uint32_t length);
|
||||
};
|
118
Core/BattleBox.h
Normal file
118
Core/BattleBox.h
Normal file
@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "Console.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "IBattery.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
class BattleBox : public BaseControlDevice, public IBattery
|
||||
{
|
||||
private:
|
||||
static const int FileSize = 0x200;
|
||||
uint8_t _lastWrite = 0;
|
||||
uint8_t _address = 0;
|
||||
uint8_t _chipSelect = 0;
|
||||
uint16_t _data[BattleBox::FileSize/2];
|
||||
uint8_t _output = 0;
|
||||
bool _writeEnabled = false;
|
||||
|
||||
uint8_t _inputBitPosition = 0;
|
||||
uint16_t _inputData = 0;
|
||||
bool _isWrite = false;
|
||||
bool _isRead = false;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
ArrayInfo<uint8_t> data{ (uint8_t*)_data, BattleBox::FileSize };
|
||||
Stream(_lastWrite, _address, _chipSelect, _output, _writeEnabled, _inputBitPosition, _isWrite, _isRead, _inputData, data);
|
||||
}
|
||||
|
||||
public:
|
||||
BattleBox() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
BatteryManager::LoadBattery(".bb", (uint8_t*)_data, BattleBox::FileSize);
|
||||
}
|
||||
|
||||
~BattleBox()
|
||||
{
|
||||
SaveBattery();
|
||||
}
|
||||
|
||||
void SaveBattery()
|
||||
{
|
||||
BatteryManager::SaveBattery(".bb", (uint8_t*)_data, BattleBox::FileSize);
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
if(_lastWrite & 0x01) {
|
||||
_chipSelect ^= 0x01;
|
||||
_inputData = 0;
|
||||
_inputBitPosition = 0;
|
||||
}
|
||||
_output ^= 0x01;
|
||||
|
||||
uint8_t readBit = 0;
|
||||
if(_isRead) {
|
||||
readBit = ((_data[(_chipSelect ? 0x80 : 0) | _address] >> _inputBitPosition) & 0x01) << 3;
|
||||
}
|
||||
uint8_t writeBit = (_output << 4);
|
||||
return readBit | writeBit;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
if(value & 0x01 && !(_lastWrite & 0x01)) {
|
||||
//Clock
|
||||
_inputData &= ~(1 << _inputBitPosition);
|
||||
_inputData |= (_output << _inputBitPosition);
|
||||
_inputBitPosition++;
|
||||
|
||||
if(_inputBitPosition > 15) {
|
||||
if(_isWrite) {
|
||||
_data[(_chipSelect ? 0x80 : 0) | _address] = _inputData;
|
||||
_isWrite = false;
|
||||
} else {
|
||||
_isRead = false;
|
||||
|
||||
//done reading addr/command or write data
|
||||
uint8_t address = (_inputData & 0x7F);
|
||||
|
||||
uint8_t cmd = ((_inputData & 0x7F00) >> 8) ^ 0x7F;
|
||||
switch(cmd) {
|
||||
case 0x01:
|
||||
//read
|
||||
_address = address;
|
||||
_isRead = true;
|
||||
break;
|
||||
case 0x06:
|
||||
//program
|
||||
if(_writeEnabled) {
|
||||
_address = address;
|
||||
_isWrite = true;
|
||||
}
|
||||
break;
|
||||
case 0x0C:
|
||||
//chip erase
|
||||
if(_writeEnabled) {
|
||||
memset(_data, 0, BattleBox::FileSize);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0D: break; //busy monitor
|
||||
case 0x09: _writeEnabled = true; break; //erase/write enable
|
||||
case 0x0B: _writeEnabled = false; break; //erase/write disable
|
||||
}
|
||||
}
|
||||
_inputBitPosition = 0;
|
||||
}
|
||||
}
|
||||
_lastWrite = value;
|
||||
}
|
||||
};
|
@ -1,4 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "SystemActionManager.h"
|
||||
#include "FdsSystemActionManager.h"
|
||||
#include "VsSystemActionManager.h"
|
||||
#include "BizhawkMovie.h"
|
||||
#include "VsControlManager.h"
|
||||
#include "FDS.h"
|
||||
@ -7,84 +10,101 @@
|
||||
BizhawkMovie::BizhawkMovie()
|
||||
{
|
||||
_originalPowerOnState = EmulationSettings::GetRamPowerOnState();
|
||||
MessageManager::RegisterNotificationListener(this);
|
||||
}
|
||||
|
||||
BizhawkMovie::~BizhawkMovie()
|
||||
{
|
||||
MessageManager::UnregisterNotificationListener(this);
|
||||
EmulationSettings::SetRamPowerOnState(_originalPowerOnState);
|
||||
Stop();
|
||||
}
|
||||
|
||||
void BizhawkMovie::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
||||
void BizhawkMovie::Stop()
|
||||
{
|
||||
if(type == ConsoleNotificationType::PpuFrameDone) {
|
||||
int32_t frameNumber = PPU::GetFrameCount();
|
||||
if(_isPlaying) {
|
||||
EndMovie();
|
||||
EmulationSettings::SetRamPowerOnState(_originalPowerOnState);
|
||||
_isPlaying = false;
|
||||
}
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
}
|
||||
|
||||
bool BizhawkMovie::SetInput(BaseControlDevice *device)
|
||||
{
|
||||
SystemActionManager* actionManager = dynamic_cast<SystemActionManager*>(device);
|
||||
int32_t frameNumber = PPU::GetFrameCount();
|
||||
if(actionManager) {
|
||||
if(frameNumber < (int32_t)_systemActionByFrame.size()) {
|
||||
uint32_t systemAction = _systemActionByFrame[frameNumber];
|
||||
if(systemAction & 0x01) {
|
||||
//Power, not implemented yet
|
||||
actionManager->SetBit(SystemActionManager::Buttons::PowerButton);
|
||||
}
|
||||
if(systemAction & 0x02) {
|
||||
//Reset, not implemented yet
|
||||
actionManager->SetBit(SystemActionManager::Buttons::ResetButton);
|
||||
}
|
||||
|
||||
if(FDS::GetSideCount()) {
|
||||
|
||||
VsSystemActionManager* vsActionManager = dynamic_cast<VsSystemActionManager*>(device);
|
||||
if(vsActionManager) {
|
||||
if(systemAction & 0x04) {
|
||||
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin1);
|
||||
}
|
||||
if(systemAction & 0x08) {
|
||||
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin2);
|
||||
}
|
||||
if(systemAction & 0x10) {
|
||||
actionManager->SetBit(VsSystemActionManager::VsButtons::ServiceButton);
|
||||
}
|
||||
}
|
||||
|
||||
FdsSystemActionManager* fdsActionManager = dynamic_cast<FdsSystemActionManager*>(device);
|
||||
if(fdsActionManager) {
|
||||
//FDS timings between NesHawk & Mesen are currently significantly different
|
||||
//So FDS games will always go out of sync
|
||||
if(systemAction & 0x04) {
|
||||
FDS::EjectDisk();
|
||||
} else if(systemAction >= 8) {
|
||||
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::EjectDiskButton);
|
||||
}
|
||||
|
||||
if(systemAction >= 8) {
|
||||
systemAction >>= 3;
|
||||
uint32_t diskNumber = 0;
|
||||
while(!(systemAction & 0x01)) {
|
||||
systemAction >>= 1;
|
||||
diskNumber++;
|
||||
}
|
||||
FDS::InsertDisk(diskNumber);
|
||||
}
|
||||
} else if(VsControlManager::GetInstance()) {
|
||||
if(VsControlManager::GetInstance()) {
|
||||
if(systemAction & 0x04) {
|
||||
VsControlManager::GetInstance()->InsertCoin(0);
|
||||
}
|
||||
if(systemAction & 0x08) {
|
||||
VsControlManager::GetInstance()->InsertCoin(1);
|
||||
}
|
||||
VsControlManager::GetInstance()->SetServiceButtonState(systemAction & 0x10 ? true : false);
|
||||
|
||||
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::InsertDisk1 + diskNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BizhawkMovie::GetState(uint8_t port)
|
||||
{
|
||||
int32_t frameNumber = PPU::GetFrameCount() - (PPU::GetCurrentScanline() >= 240 ? 0 : 1);
|
||||
if(frameNumber < (int32_t)_dataByFrame[0].size()) {
|
||||
return _dataByFrame[port][frameNumber];
|
||||
} else {
|
||||
EndMovie();
|
||||
EmulationSettings::SetRamPowerOnState(_originalPowerOnState);
|
||||
_isPlaying = false;
|
||||
return 0;
|
||||
int port = device->GetPort();
|
||||
StandardController* controller = dynamic_cast<StandardController*>(device);
|
||||
if(controller) {
|
||||
if(frameNumber < (int32_t)_dataByFrame[port].size()) {
|
||||
controller->SetTextState(_dataByFrame[port][frameNumber]);
|
||||
} else {
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BizhawkMovie::InitializeGameData(ZipReader & reader)
|
||||
bool BizhawkMovie::InitializeGameData(ZipReader &reader)
|
||||
{
|
||||
std::stringstream ss = reader.GetStream("Header.txt");
|
||||
stringstream fileData;
|
||||
if(!reader.GetStream("Header.txt", fileData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
while(!ss.eof()) {
|
||||
while(!fileData.eof()) {
|
||||
string line;
|
||||
std::getline(ss, line);
|
||||
std::getline(fileData, line);
|
||||
if(line.compare(0, 4, "SHA1", 4) == 0) {
|
||||
if(line.size() >= 45) {
|
||||
HashInfo hashInfo;
|
||||
hashInfo.Sha1Hash = line.substr(5, 40);
|
||||
if(Console::LoadROM("", hashInfo)) {
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if(line.compare(0, 3, "MD5", 3) == 0) {
|
||||
@ -93,31 +113,37 @@ bool BizhawkMovie::InitializeGameData(ZipReader & reader)
|
||||
hashInfo.PrgChrMd5Hash = line.substr(4, 32);
|
||||
std::transform(hashInfo.PrgChrMd5Hash.begin(), hashInfo.PrgChrMd5Hash.end(), hashInfo.PrgChrMd5Hash.begin(), ::toupper);
|
||||
if(Console::LoadROM("", hashInfo)) {
|
||||
result = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BizhawkMovie::InitializeInputData(ZipReader & reader)
|
||||
bool BizhawkMovie::InitializeInputData(ZipReader &reader)
|
||||
{
|
||||
const uint8_t orValues[8] = { 0x10, 0x20, 0x40, 0x80, 0x08, 0x04, 0x02, 0x01 };
|
||||
std::stringstream ss = reader.GetStream("Input Log.txt");
|
||||
|
||||
int systemActionCount = 2;
|
||||
if(FDS::GetSideCount() > 0) {
|
||||
//Eject disk + Insert Disk #XX
|
||||
systemActionCount += FDS::GetSideCount() + 1;
|
||||
} else if(VsControlManager::GetInstance()) {
|
||||
//Insert coin 1, 2 + service button
|
||||
systemActionCount += 3;
|
||||
stringstream inputData;
|
||||
if(!reader.GetStream("Input Log.txt", inputData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(!ss.eof()) {
|
||||
int systemActionCount = 2;
|
||||
shared_ptr<FdsSystemActionManager> fdsActionManager = Console::GetInstance()->GetSystemActionManager<FdsSystemActionManager>();
|
||||
if(fdsActionManager) {
|
||||
//Eject disk + Insert Disk #XX
|
||||
systemActionCount += fdsActionManager->GetSideCount() + 1;
|
||||
} else {
|
||||
shared_ptr<VsSystemActionManager> vsActionManager = Console::GetInstance()->GetSystemActionManager<VsSystemActionManager>();
|
||||
if(vsActionManager) {
|
||||
//Insert coin 1, 2 + service button
|
||||
systemActionCount += 3;
|
||||
}
|
||||
}
|
||||
|
||||
while(!inputData.eof()) {
|
||||
string line;
|
||||
std::getline(ss, line);
|
||||
std::getline(inputData, line);
|
||||
|
||||
if(line.size() > 0 && line[0] == '|') {
|
||||
line.erase(std::remove(line.begin(), line.end(), '|'), line.end());
|
||||
@ -132,20 +158,16 @@ bool BizhawkMovie::InitializeInputData(ZipReader & reader)
|
||||
}
|
||||
_systemActionByFrame.push_back(systemAction);
|
||||
|
||||
//Only supports regular controllers (up to 4 of them)
|
||||
for(int i = 0; i < 8*4; i++) {
|
||||
uint8_t port = i / 8;
|
||||
|
||||
if(port <= 3) {
|
||||
uint8_t portValue = 0;
|
||||
for(int j = 0; j < 8 && i + j + systemActionCount < (int)line.size(); j++) {
|
||||
if(line[i+j+systemActionCount] != '.') {
|
||||
portValue |= orValues[j];
|
||||
}
|
||||
}
|
||||
i += 7;
|
||||
_dataByFrame[port].push_back(portValue);
|
||||
}
|
||||
line = line.substr(systemActionCount);
|
||||
int port = 0;
|
||||
while(line.size() >= 8) {
|
||||
_dataByFrame[port].push_back(line.substr(0, 8));
|
||||
line = line.substr(8);
|
||||
port++;
|
||||
}
|
||||
while(port < 4) {
|
||||
_dataByFrame[port].push_back("........");
|
||||
port++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,39 +175,28 @@ bool BizhawkMovie::InitializeInputData(ZipReader & reader)
|
||||
return _dataByFrame[0].size() > 0;
|
||||
}
|
||||
|
||||
bool BizhawkMovie::Play(stringstream & filestream, bool autoLoadRom)
|
||||
bool BizhawkMovie::Play(VirtualFile &file)
|
||||
{
|
||||
Console::Pause();
|
||||
ZipReader reader;
|
||||
reader.LoadArchive(filestream);
|
||||
if(InitializeGameData(reader)) {
|
||||
if(InitializeInputData(reader)) {
|
||||
//NesHawk initializes memory to 1s
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllOnes);
|
||||
Console::Reset(false);
|
||||
_isPlaying = true;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
file.ReadFile(ss);
|
||||
|
||||
reader.LoadArchive(ss);
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
if(InitializeInputData(reader) && InitializeGameData(reader)) {
|
||||
//NesHawk initializes memory to 1s
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllOnes);
|
||||
_isPlaying = true;
|
||||
} else {
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
}
|
||||
Console::Resume();
|
||||
return _isPlaying;
|
||||
}
|
||||
|
||||
bool BizhawkMovie::IsRecording()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BizhawkMovie::IsPlaying()
|
||||
{
|
||||
return _isPlaying;
|
||||
}
|
||||
|
||||
void BizhawkMovie::RecordState(uint8_t port, uint8_t value)
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
void BizhawkMovie::Record(string filename, bool reset)
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
@ -3,15 +3,18 @@
|
||||
#include "MovieManager.h"
|
||||
#include "../Utilities/ZipReader.h"
|
||||
|
||||
class BizhawkMovie : public IMovie, public INotificationListener
|
||||
class VirtualFile;
|
||||
|
||||
class BizhawkMovie : public IMovie
|
||||
{
|
||||
private:
|
||||
bool InitializeGameData(ZipReader &reader);
|
||||
bool InitializeInputData(ZipReader &reader);
|
||||
void Stop();
|
||||
|
||||
protected:
|
||||
vector<uint32_t> _systemActionByFrame;
|
||||
vector<uint8_t> _dataByFrame[4];
|
||||
vector<string> _dataByFrame[4];
|
||||
bool _isPlaying = false;
|
||||
RamPowerOnState _originalPowerOnState;
|
||||
|
||||
@ -19,15 +22,7 @@ public:
|
||||
BizhawkMovie();
|
||||
virtual ~BizhawkMovie();
|
||||
|
||||
void RecordState(uint8_t port, uint8_t value) override;
|
||||
void Record(string filename, bool reset) override;
|
||||
|
||||
uint8_t GetState(uint8_t port) override;
|
||||
|
||||
virtual bool Play(stringstream &filestream, bool autoLoadRom) override;
|
||||
|
||||
bool IsRecording() override;
|
||||
bool SetInput(BaseControlDevice *device) override;
|
||||
bool Play(VirtualFile &file) override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
};
|
238
Core/Console.cpp
238
Core/Console.cpp
@ -29,13 +29,20 @@
|
||||
#include "SaveStateManager.h"
|
||||
#include "HdPackBuilder.h"
|
||||
#include "HdAudioDevice.h"
|
||||
#include "FDS.h"
|
||||
#include "SystemActionManager.h"
|
||||
#include "FdsSystemActionManager.h"
|
||||
#include "VsSystemActionManager.h"
|
||||
#include "IBarcodeReader.h"
|
||||
#include "IBattery.h"
|
||||
#include "KeyManager.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
shared_ptr<Console> Console::Instance(new Console());
|
||||
|
||||
Console::Console()
|
||||
{
|
||||
_resetRequested = false;
|
||||
_lagCounter = 0;
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
@ -51,17 +58,26 @@ shared_ptr<Console> Console::GetInstance()
|
||||
|
||||
void Console::Release()
|
||||
{
|
||||
Console::Instance.reset(new Console());
|
||||
Console::Instance.reset();
|
||||
}
|
||||
|
||||
void Console::SaveBatteries()
|
||||
{
|
||||
_mapper->SaveBattery();
|
||||
|
||||
shared_ptr<IBattery> device = std::dynamic_pointer_cast<IBattery>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
|
||||
if(device) {
|
||||
device->SaveBattery();
|
||||
}
|
||||
}
|
||||
|
||||
bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
{
|
||||
SoundMixer::StopAudio();
|
||||
StopRecordingHdPack();
|
||||
|
||||
if(!_romFilepath.empty() && _mapper) {
|
||||
//Ensure we save any battery file before loading a new game
|
||||
_mapper->SaveBattery();
|
||||
SaveBatteries();
|
||||
|
||||
//Save current game state before loading another one
|
||||
SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename);
|
||||
@ -71,7 +87,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
LoadHdPack(romFile, patchFile);
|
||||
if(patchFile.IsValid()) {
|
||||
if(romFile.ApplyPatch(patchFile)) {
|
||||
MessageManager::DisplayMessage("Patch", "ApplyingPatch", FolderUtilities::GetFilename(patchFile.GetFilePath(), true));
|
||||
MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName());
|
||||
} else {
|
||||
//Patch failed
|
||||
}
|
||||
@ -79,6 +95,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
vector<uint8_t> fileData;
|
||||
romFile.ReadFile(fileData);
|
||||
|
||||
BatteryManager::Initialize(FolderUtilities::GetFilename(romFile.GetFileName(), false));
|
||||
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(romFile.GetFileName(), fileData);
|
||||
if(mapper) {
|
||||
if(_mapper) {
|
||||
@ -86,8 +103,15 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
MessageManager::SendNotification(ConsoleNotificationType::GameStopped);
|
||||
}
|
||||
|
||||
_romFilepath = romFile;
|
||||
_patchFilename = patchFile;
|
||||
if(_romFilepath != (string)romFile || _patchFilename != (string)patchFile) {
|
||||
_romFilepath = romFile;
|
||||
_patchFilename = patchFile;
|
||||
|
||||
//Changed game, stop all recordings
|
||||
MovieManager::Stop();
|
||||
SoundMixer::StopRecording();
|
||||
StopRecordingHdPack();
|
||||
}
|
||||
|
||||
_autoSaveManager.reset(new AutoSaveManager());
|
||||
VideoDecoder::GetInstance()->StopThread();
|
||||
@ -95,20 +119,29 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
_mapper = mapper;
|
||||
_memoryManager.reset(new MemoryManager(_mapper));
|
||||
_cpu.reset(new CPU(_memoryManager.get()));
|
||||
|
||||
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
||||
_ppu.reset(new HdPpu(_mapper.get(), _hdData->Version));
|
||||
} else if(NsfMapper::GetInstance()) {
|
||||
//Disable most of the PPU for NSFs
|
||||
_ppu.reset(new NsfPpu(_mapper.get()));
|
||||
} else {
|
||||
_ppu.reset(new PPU(_mapper.get()));
|
||||
}
|
||||
|
||||
_apu.reset(new APU(_memoryManager.get()));
|
||||
|
||||
_controlManager.reset(_mapper->GetGameSystem() == GameSystem::VsUniSystem ? new VsControlManager() : new ControlManager());
|
||||
switch(_mapper->GetGameSystem()) {
|
||||
case GameSystem::FDS: _systemActionManager.reset(new FdsSystemActionManager(Console::GetInstance(), _mapper)); break;
|
||||
case GameSystem::VsUniSystem: _systemActionManager.reset(new VsSystemActionManager(Console::GetInstance())); break;
|
||||
default: _systemActionManager.reset(new SystemActionManager(Console::GetInstance())); break;
|
||||
}
|
||||
|
||||
if(_mapper->GetGameSystem() == GameSystem::VsUniSystem) {
|
||||
_controlManager.reset(new VsControlManager(_systemActionManager, _mapper->GetMapperControlDevice()));
|
||||
} else {
|
||||
_controlManager.reset(new ControlManager(_systemActionManager, _mapper->GetMapperControlDevice()));
|
||||
}
|
||||
_controlManager->UpdateControlDevices();
|
||||
|
||||
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
||||
_ppu.reset(new HdPpu(_mapper.get(), _controlManager.get(), _hdData->Version));
|
||||
} else if(NsfMapper::GetInstance()) {
|
||||
//Disable most of the PPU for NSFs
|
||||
_ppu.reset(new NsfPpu(_mapper.get(), _controlManager.get()));
|
||||
} else {
|
||||
_ppu.reset(new PPU(_mapper.get(), _controlManager.get()));
|
||||
}
|
||||
|
||||
_memoryManager->RegisterIODevice(_ppu.get());
|
||||
_memoryManager->RegisterIODevice(_apu.get());
|
||||
@ -125,7 +158,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
_initialized = true;
|
||||
|
||||
if(_debugger) {
|
||||
auto lock = _debuggerLock.AcquireSafe();
|
||||
StopDebugger();
|
||||
GetDebugger();
|
||||
}
|
||||
@ -133,6 +165,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
ResetComponents(false);
|
||||
|
||||
_rewindManager.reset(new RewindManager());
|
||||
_controlManager->UpdateInputState();
|
||||
|
||||
VideoDecoder::GetInstance()->StartThread();
|
||||
|
||||
@ -148,6 +181,9 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
|
||||
}
|
||||
}
|
||||
|
||||
//Reset battery source to current game if new game failed to load
|
||||
BatteryManager::Initialize(FolderUtilities::GetFilename(GetRomName(), false));
|
||||
|
||||
MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName());
|
||||
return false;
|
||||
}
|
||||
@ -163,15 +199,33 @@ bool Console::LoadROM(VirtualFile romFile, VirtualFile patchFile)
|
||||
bool Console::LoadROM(string romName, HashInfo hashInfo)
|
||||
{
|
||||
string currentRomFilepath = Console::GetRomPath().GetFilePath();
|
||||
string currentFolder = FolderUtilities::GetFolderName(currentRomFilepath);
|
||||
if(!currentRomFilepath.empty()) {
|
||||
HashInfo gameHashInfo = Instance->_mapper->GetHashInfo();
|
||||
if(gameHashInfo.Crc32Hash == hashInfo.Crc32Hash || gameHashInfo.Sha1Hash.compare(hashInfo.Sha1Hash) == 0 || gameHashInfo.PrgChrMd5Hash.compare(hashInfo.PrgChrMd5Hash) == 0) {
|
||||
//Current game matches, no need to do anything
|
||||
//Current game matches, power cycle game and return
|
||||
Instance->PowerCycle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
string match = FindMatchingRom(romName, hashInfo);
|
||||
if(!match.empty()) {
|
||||
return Console::LoadROM(match);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string Console::FindMatchingRom(string romName, HashInfo hashInfo)
|
||||
{
|
||||
VirtualFile currentRom = Console::GetRomPath();
|
||||
if(currentRom.IsValid() && !Console::GetPatchFile().IsValid()) {
|
||||
HashInfo gameHashInfo = Instance->_mapper->GetHashInfo();
|
||||
if(gameHashInfo.Crc32Hash == hashInfo.Crc32Hash || gameHashInfo.Sha1Hash.compare(hashInfo.Sha1Hash) == 0 || gameHashInfo.PrgChrMd5Hash.compare(hashInfo.PrgChrMd5Hash) == 0) {
|
||||
//Current game matches
|
||||
return currentRom;
|
||||
}
|
||||
}
|
||||
|
||||
string lcRomname = romName;
|
||||
std::transform(lcRomname.begin(), lcRomname.end(), lcRomname.begin(), ::tolower);
|
||||
std::unordered_set<string> validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } };
|
||||
@ -180,19 +234,19 @@ bool Console::LoadROM(string romName, HashInfo hashInfo)
|
||||
vector<string> files = FolderUtilities::GetFilesInFolder(folder, validExtensions, true);
|
||||
romFiles.insert(romFiles.end(), files.begin(), files.end());
|
||||
}
|
||||
|
||||
|
||||
string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, true);
|
||||
if(!match.empty()) {
|
||||
return Console::LoadROM(match);
|
||||
return match;
|
||||
}
|
||||
|
||||
//Perform slow CRC32 search for ROM
|
||||
match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, false);
|
||||
if(!match.empty()) {
|
||||
return Console::LoadROM(match);
|
||||
return match;
|
||||
}
|
||||
|
||||
return false;
|
||||
return "";
|
||||
}
|
||||
|
||||
VirtualFile Console::GetRomPath()
|
||||
@ -209,6 +263,11 @@ string Console::GetRomName()
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFile Console::GetPatchFile()
|
||||
{
|
||||
return Instance ? Instance->_patchFilename : VirtualFile();
|
||||
}
|
||||
|
||||
RomFormat Console::GetRomFormat()
|
||||
{
|
||||
if(Instance->_mapper) {
|
||||
@ -240,45 +299,36 @@ NesModel Console::GetModel()
|
||||
return Instance->_model;
|
||||
}
|
||||
|
||||
shared_ptr<SystemActionManager> Console::GetSystemActionManager()
|
||||
{
|
||||
return _systemActionManager;
|
||||
}
|
||||
|
||||
void Console::PowerCycle()
|
||||
{
|
||||
if(Instance->_initialized && !Instance->_romFilepath.empty()) {
|
||||
LoadROM(Instance->_romFilepath, Instance->_patchFilename);
|
||||
if(_initialized && !_romFilepath.empty()) {
|
||||
LoadROM(_romFilepath, _patchFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Reset(bool softReset)
|
||||
{
|
||||
if(Instance->_initialized) {
|
||||
if(softReset && EmulationSettings::CheckFlag(EmulationFlags::DisablePpuReset)) {
|
||||
//Allow mid-frame resets to allow the PPU to get out-of-sync
|
||||
RequestReset();
|
||||
} else {
|
||||
MovieManager::Stop();
|
||||
SoundMixer::StopRecording();
|
||||
|
||||
Console::Pause();
|
||||
if(Instance->_initialized) {
|
||||
if(softReset) {
|
||||
Instance->ResetComponents(softReset);
|
||||
} else {
|
||||
//Full reset of all objects to ensure the emulator always starts in the exact same state
|
||||
LoadROM(Instance->_romFilepath, Instance->_patchFilename);
|
||||
}
|
||||
if(softReset) {
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::DisablePpuReset)) {
|
||||
//Allow mid-frame resets to allow the PPU to get out-of-sync
|
||||
RequestReset();
|
||||
} else {
|
||||
Instance->_systemActionManager->Reset();
|
||||
}
|
||||
Console::Resume();
|
||||
} else {
|
||||
Instance->_systemActionManager->PowerCycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Console::ResetComponents(bool softReset)
|
||||
{
|
||||
MovieManager::Stop();
|
||||
if(!softReset) {
|
||||
SoundMixer::StopRecording();
|
||||
_hdPackBuilder.reset();
|
||||
}
|
||||
|
||||
_memoryManager->Reset(softReset);
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::DisablePpuReset) || !softReset) {
|
||||
_ppu->Reset();
|
||||
@ -287,9 +337,7 @@ void Console::ResetComponents(bool softReset)
|
||||
_cpu->Reset(softReset, _model);
|
||||
_controlManager->Reset(softReset);
|
||||
|
||||
_lagCounter = 0;
|
||||
|
||||
SoundMixer::StopAudio(true);
|
||||
KeyManager::UpdateDevices();
|
||||
|
||||
MessageManager::SendNotification(softReset ? ConsoleNotificationType::GameReset : ConsoleNotificationType::GameLoaded);
|
||||
|
||||
@ -379,16 +427,10 @@ void Console::Run()
|
||||
|
||||
uint32_t currentFrameNumber = PPU::GetFrameCount();
|
||||
if(currentFrameNumber != lastFrameNumber) {
|
||||
if(_controlManager->GetLagFlag()) {
|
||||
_lagCounter++;
|
||||
}
|
||||
|
||||
_rewindManager->ProcessEndOfFrame();
|
||||
EmulationSettings::DisableOverclocking(_disableOcNextFrame || NsfMapper::GetInstance());
|
||||
_disableOcNextFrame = false;
|
||||
|
||||
lastFrameNumber = PPU::GetFrameCount();
|
||||
|
||||
//Sleep until we're ready to start the next frame
|
||||
clockTimer.WaitUntil(targetTime);
|
||||
|
||||
@ -427,6 +469,8 @@ void Console::Run()
|
||||
_runLock.Acquire();
|
||||
MessageManager::SendNotification(ConsoleNotificationType::GameResumed);
|
||||
}
|
||||
|
||||
_systemActionManager->ProcessSystemActions();
|
||||
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
@ -443,6 +487,8 @@ void Console::Run()
|
||||
if(targetTime < 0) {
|
||||
targetTime = 0;
|
||||
}
|
||||
|
||||
lastFrameNumber = PPU::GetFrameCount();
|
||||
|
||||
if(_stop) {
|
||||
_stop = false;
|
||||
@ -472,7 +518,7 @@ void Console::Run()
|
||||
|
||||
if(!_romFilepath.empty() && _mapper) {
|
||||
//Ensure we save any battery file before unloading anything
|
||||
_mapper->SaveBattery();
|
||||
SaveBatteries();
|
||||
}
|
||||
|
||||
_romFilepath = "";
|
||||
@ -575,21 +621,21 @@ void Console::SaveState(ostream &saveStream)
|
||||
}
|
||||
}
|
||||
|
||||
void Console::LoadState(istream &loadStream)
|
||||
void Console::LoadState(istream &loadStream, uint32_t stateVersion)
|
||||
{
|
||||
if(Instance->_initialized) {
|
||||
//Stop any movie that might have been playing/recording if a state is loaded
|
||||
//(Note: Loading a state is disabled in the UI while a movie is playing/recording)
|
||||
MovieManager::Stop();
|
||||
|
||||
Instance->_cpu->LoadSnapshot(&loadStream);
|
||||
Instance->_ppu->LoadSnapshot(&loadStream);
|
||||
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
||||
Instance->_apu->LoadSnapshot(&loadStream);
|
||||
Instance->_controlManager->LoadSnapshot(&loadStream);
|
||||
Instance->_mapper->LoadSnapshot(&loadStream);
|
||||
Instance->_cpu->LoadSnapshot(&loadStream, stateVersion);
|
||||
Instance->_ppu->LoadSnapshot(&loadStream, stateVersion);
|
||||
Instance->_memoryManager->LoadSnapshot(&loadStream, stateVersion);
|
||||
Instance->_apu->LoadSnapshot(&loadStream, stateVersion);
|
||||
Instance->_controlManager->LoadSnapshot(&loadStream, stateVersion);
|
||||
Instance->_mapper->LoadSnapshot(&loadStream, stateVersion);
|
||||
if(Instance->_hdAudioDevice) {
|
||||
Instance->_hdAudioDevice->LoadSnapshot(&loadStream);
|
||||
Instance->_hdAudioDevice->LoadSnapshot(&loadStream, stateVersion);
|
||||
} else {
|
||||
Snapshotable::SkipBlock(&loadStream);
|
||||
}
|
||||
@ -616,16 +662,16 @@ void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
||||
|
||||
std::shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
|
||||
{
|
||||
auto lock = _debuggerLock.AcquireSafe();
|
||||
if(!_debugger && autoStart) {
|
||||
_debugger.reset(new Debugger(Console::Instance, _cpu, _ppu, _apu, _memoryManager, _mapper));
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(!debugger && autoStart) {
|
||||
debugger.reset(new Debugger(Console::Instance, _cpu, _ppu, _apu, _memoryManager, _mapper));
|
||||
_debugger = debugger;
|
||||
}
|
||||
return _debugger;
|
||||
return debugger;
|
||||
}
|
||||
|
||||
void Console::StopDebugger()
|
||||
{
|
||||
auto lock = _debuggerLock.AcquireSafe();
|
||||
_debugger.reset();
|
||||
}
|
||||
|
||||
@ -634,19 +680,21 @@ void Console::RequestReset()
|
||||
Instance->_resetRequested = true;
|
||||
}
|
||||
|
||||
uint32_t Console::GetLagCounter()
|
||||
{
|
||||
return Instance->_lagCounter;
|
||||
}
|
||||
|
||||
std::thread::id Console::GetEmulationThreadId()
|
||||
{
|
||||
return Instance->_emulationThreadId;
|
||||
}
|
||||
|
||||
uint32_t Console::GetLagCounter()
|
||||
{
|
||||
return Instance->_controlManager->GetLagCounter();
|
||||
}
|
||||
|
||||
void Console::ResetLagCounter()
|
||||
{
|
||||
Instance->_lagCounter = 0;
|
||||
Console::Pause();
|
||||
Instance->_controlManager->ResetLagCounter();
|
||||
Console::Reset();
|
||||
}
|
||||
|
||||
bool Console::IsDebuggerAttached()
|
||||
@ -697,7 +745,7 @@ void Console::StartRecordingHdPack(string saveFolder, ScaleFilterType filterType
|
||||
|
||||
Instance->_memoryManager->UnregisterIODevice(Instance->_ppu.get());
|
||||
Instance->_ppu.reset();
|
||||
Instance->_ppu.reset(new HdBuilderPpu(Instance->_mapper.get(), Instance->_hdPackBuilder.get(), chrRamBankSize));
|
||||
Instance->_ppu.reset(new HdBuilderPpu(Instance->_mapper.get(), Instance->_controlManager.get(), Instance->_hdPackBuilder.get(), chrRamBankSize));
|
||||
Instance->_memoryManager->RegisterIODevice(Instance->_ppu.get());
|
||||
|
||||
Instance->LoadState(saveState);
|
||||
@ -713,7 +761,7 @@ void Console::StopRecordingHdPack()
|
||||
|
||||
Instance->_memoryManager->UnregisterIODevice(Instance->_ppu.get());
|
||||
Instance->_ppu.reset();
|
||||
Instance->_ppu.reset(new PPU(Instance->_mapper.get()));
|
||||
Instance->_ppu.reset(new PPU(Instance->_mapper.get(), Instance->_controlManager.get()));
|
||||
Instance->_memoryManager->RegisterIODevice(Instance->_ppu.get());
|
||||
|
||||
Instance->_hdPackBuilder.reset();
|
||||
@ -721,4 +769,34 @@ void Console::StopRecordingHdPack()
|
||||
Instance->LoadState(saveState);
|
||||
Console::Resume();
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleFeatures Console::GetAvailableFeatures()
|
||||
{
|
||||
ConsoleFeatures features = ConsoleFeatures::None;
|
||||
if(_mapper) {
|
||||
features = (ConsoleFeatures)((int)features | (int)_mapper->GetAvailableFeatures());
|
||||
|
||||
if(dynamic_cast<VsControlManager*>(_controlManager.get())) {
|
||||
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::VsSystem);
|
||||
}
|
||||
|
||||
if(std::dynamic_pointer_cast<IBarcodeReader>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort))) {
|
||||
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::BarcodeReader);
|
||||
}
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
void Console::InputBarcode(uint64_t barcode, uint32_t digitCount)
|
||||
{
|
||||
shared_ptr<IBarcodeReader> barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(_mapper->GetMapperControlDevice());
|
||||
if(barcodeReader) {
|
||||
barcodeReader->InputBarcode(barcode, digitCount);
|
||||
}
|
||||
|
||||
barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
|
||||
if(barcodeReader) {
|
||||
barcodeReader->InputBarcode(barcode, digitCount);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
#include "VirtualFile.h"
|
||||
#include "RomData.h"
|
||||
#include "SaveStateManager.h"
|
||||
|
||||
class Debugger;
|
||||
class BaseMapper;
|
||||
@ -17,9 +18,11 @@ class ControlManager;
|
||||
class AutoSaveManager;
|
||||
class HdPackBuilder;
|
||||
class HdAudioDevice;
|
||||
class SystemActionManager;
|
||||
struct HdPackData;
|
||||
enum class NesModel;
|
||||
enum class ScaleFilterType;
|
||||
enum class ConsoleFeatures;
|
||||
|
||||
class Console
|
||||
{
|
||||
@ -34,11 +37,12 @@ class Console
|
||||
shared_ptr<PPU> _ppu;
|
||||
shared_ptr<APU> _apu;
|
||||
shared_ptr<Debugger> _debugger;
|
||||
SimpleLock _debuggerLock;
|
||||
shared_ptr<BaseMapper> _mapper;
|
||||
unique_ptr<ControlManager> _controlManager;
|
||||
shared_ptr<MemoryManager> _memoryManager;
|
||||
|
||||
shared_ptr<SystemActionManager> _systemActionManager;
|
||||
|
||||
unique_ptr<AutoSaveManager> _autoSaveManager;
|
||||
|
||||
shared_ptr<HdPackBuilder> _hdPackBuilder;
|
||||
@ -55,29 +59,41 @@ class Console
|
||||
bool _disableOcNextFrame = false;
|
||||
|
||||
atomic<bool> _resetRequested;
|
||||
atomic<uint32_t> _lagCounter;
|
||||
|
||||
bool _initialized = false;
|
||||
std::thread::id _emulationThreadId;
|
||||
|
||||
void LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile);
|
||||
|
||||
void ResetComponents(bool softReset);
|
||||
bool Initialize(VirtualFile &romFile, VirtualFile &patchFile);
|
||||
void UpdateNesModel(bool sendNotification);
|
||||
double GetFrameDelay();
|
||||
|
||||
void SaveBatteries();
|
||||
|
||||
public:
|
||||
Console();
|
||||
~Console();
|
||||
void Run();
|
||||
void Stop();
|
||||
|
||||
shared_ptr<SystemActionManager> GetSystemActionManager();
|
||||
|
||||
template<typename T>
|
||||
shared_ptr<T> GetSystemActionManager()
|
||||
{
|
||||
return std::dynamic_pointer_cast<T>(_systemActionManager);
|
||||
}
|
||||
|
||||
ConsoleFeatures GetAvailableFeatures();
|
||||
void InputBarcode(uint64_t barcode, uint32_t digitCount);
|
||||
|
||||
static std::thread::id GetEmulationThreadId();
|
||||
|
||||
static void RequestReset();
|
||||
static void Reset(bool softReset = true);
|
||||
static void PowerCycle();
|
||||
void PowerCycle();
|
||||
void ResetComponents(bool softReset);
|
||||
|
||||
//Used to pause the emu loop to perform thread-safe operations
|
||||
static void Pause();
|
||||
@ -85,17 +101,19 @@ class Console
|
||||
//Used to resume the emu loop after calling Pause()
|
||||
static void Resume();
|
||||
|
||||
std::shared_ptr<Debugger> GetDebugger(bool autoStart = true);
|
||||
shared_ptr<Debugger> GetDebugger(bool autoStart = true);
|
||||
void StopDebugger();
|
||||
|
||||
static void SaveState(ostream &saveStream);
|
||||
static void LoadState(istream &loadStream);
|
||||
static void LoadState(istream &loadStream, uint32_t stateVersion = SaveStateManager::FileFormatVersion);
|
||||
static void LoadState(uint8_t *buffer, uint32_t bufferSize);
|
||||
|
||||
static bool LoadROM(VirtualFile romFile, VirtualFile patchFile = {});
|
||||
static bool LoadROM(string romName, HashInfo hashInfo);
|
||||
static string FindMatchingRom(string romName, HashInfo hashInfo);
|
||||
static VirtualFile GetRomPath();
|
||||
static string GetRomName();
|
||||
static VirtualFile GetPatchFile();
|
||||
static bool IsChrRam();
|
||||
static RomFormat GetRomFormat();
|
||||
static HashInfo GetHashInfo();
|
||||
|
13
Core/ControlDeviceState.h
Normal file
13
Core/ControlDeviceState.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <cstring>
|
||||
|
||||
struct ControlDeviceState
|
||||
{
|
||||
vector<uint8_t> State;
|
||||
|
||||
bool operator!=(ControlDeviceState &other)
|
||||
{
|
||||
return State.size() != other.State.size() || memcmp(State.data(), other.State.data(), State.size()) != 0;
|
||||
}
|
||||
};
|
@ -1,190 +1,217 @@
|
||||
#include "stdafx.h"
|
||||
#include "ControlManager.h"
|
||||
#include "StandardController.h"
|
||||
#include "Zapper.h"
|
||||
#include "ArkanoidController.h"
|
||||
#include "OekaKidsTablet.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "Console.h"
|
||||
#include "GameServerConnection.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "PPU.h"
|
||||
#include "IKeyManager.h"
|
||||
#include "IInputProvider.h"
|
||||
#include "IInputRecorder.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "StandardController.h"
|
||||
#include "Zapper.h"
|
||||
#include "ArkanoidController.h"
|
||||
#include "OekaKidsTablet.h"
|
||||
#include "FourScore.h"
|
||||
#include "SnesController.h"
|
||||
#include "SnesMouse.h"
|
||||
#include "PowerPad.h"
|
||||
#include "FamilyMatTrainer.h"
|
||||
#include "KonamiHyperShot.h"
|
||||
#include "FamilyBasicKeyboard.h"
|
||||
#include "FamilyBasicDataRecorder.h"
|
||||
#include "PartyTap.h"
|
||||
#include "PachinkoController.h"
|
||||
#include "ExcitingBoxingController.h"
|
||||
#include "SuborKeyboard.h"
|
||||
#include "SuborMouse.h"
|
||||
#include "JissenMahjongController.h"
|
||||
#include "BarcodeBattlerReader.h"
|
||||
#include "HoriTrack.h"
|
||||
#include "BandaiHyperShot.h"
|
||||
#include "VsZapper.h"
|
||||
#include "AsciiTurboFile.h"
|
||||
#include "BattleBox.h"
|
||||
|
||||
unique_ptr<IKeyManager> ControlManager::_keyManager = nullptr;
|
||||
shared_ptr<BaseControlDevice> ControlManager::_controlDevices[2] = { nullptr, nullptr };
|
||||
IGameBroadcaster* ControlManager::_gameBroadcaster = nullptr;
|
||||
MousePosition ControlManager::_mousePosition = { -1, -1 };
|
||||
ControlManager* ControlManager::_instance = nullptr;
|
||||
vector<IInputRecorder*> ControlManager::_inputRecorders;
|
||||
vector<IInputProvider*> ControlManager::_inputProviders;
|
||||
SimpleLock ControlManager::_deviceLock;
|
||||
|
||||
ControlManager::ControlManager()
|
||||
ControlManager::ControlManager(shared_ptr<BaseControlDevice> systemActionManager, shared_ptr<BaseControlDevice> mapperControlDevice)
|
||||
{
|
||||
|
||||
_systemActionManager = systemActionManager;
|
||||
_mapperControlDevice = mapperControlDevice;
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
ControlManager::~ControlManager()
|
||||
{
|
||||
}
|
||||
|
||||
void ControlManager::RegisterKeyManager(IKeyManager* keyManager)
|
||||
{
|
||||
_keyManager.reset(keyManager);
|
||||
}
|
||||
|
||||
void ControlManager::RefreshKeyState()
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->RefreshState();
|
||||
if(_instance == this) {
|
||||
_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlManager::IsKeyPressed(uint32_t keyCode)
|
||||
void ControlManager::RegisterInputProvider(IInputProvider* provider)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->IsKeyPressed(keyCode);
|
||||
}
|
||||
return false;
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
_inputProviders.push_back(provider);
|
||||
}
|
||||
|
||||
bool ControlManager::IsMouseButtonPressed(MouseButton button)
|
||||
void ControlManager::UnregisterInputProvider(IInputProvider* provider)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->IsMouseButtonPressed(button);
|
||||
}
|
||||
return false;
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
vector<IInputProvider*> &vec = _inputProviders;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
|
||||
}
|
||||
|
||||
vector<uint32_t> ControlManager::GetPressedKeys()
|
||||
void ControlManager::RegisterInputRecorder(IInputRecorder* provider)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetPressedKeys();
|
||||
}
|
||||
return vector<uint32_t>();
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
_inputRecorders.push_back(provider);
|
||||
}
|
||||
|
||||
string ControlManager::GetKeyName(uint32_t keyCode)
|
||||
void ControlManager::UnregisterInputRecorder(IInputRecorder* provider)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetKeyName(keyCode);
|
||||
}
|
||||
return "";
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
vector<IInputRecorder*> &vec = _inputRecorders;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
|
||||
}
|
||||
|
||||
uint32_t ControlManager::GetKeyCode(string keyName)
|
||||
vector<ControlDeviceState> ControlManager::GetPortStates()
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetKeyCode(keyName);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
void ControlManager::RegisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||
{
|
||||
ControlManager::_gameBroadcaster = gameBroadcaster;
|
||||
}
|
||||
|
||||
void ControlManager::UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||
{
|
||||
if(ControlManager::_gameBroadcaster == gameBroadcaster) {
|
||||
ControlManager::_gameBroadcaster = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::BroadcastInput(uint8_t port, uint8_t state)
|
||||
{
|
||||
if(ControlManager::_gameBroadcaster) {
|
||||
//Used when acting as a game server
|
||||
ControlManager::_gameBroadcaster->BroadcastInput(state, port);
|
||||
vector<ControlDeviceState> states;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
shared_ptr<BaseControlDevice> device = GetControlDevice(i);
|
||||
if(device) {
|
||||
states.push_back(device->GetRawState());
|
||||
} else {
|
||||
states.push_back(ControlDeviceState());
|
||||
}
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port)
|
||||
{
|
||||
return ControlManager::_controlDevices[port];
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
auto result = std::find_if(_instance->_controlDevices.begin(), _instance->_controlDevices.end(), [port](const shared_ptr<BaseControlDevice> control) { return control->GetPort() == port; });
|
||||
if(result != _instance->_controlDevices.end()) {
|
||||
return *result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ControlManager::RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice, uint8_t port)
|
||||
void ControlManager::RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice)
|
||||
{
|
||||
ControlManager::_controlDevices[port] = controlDevice;
|
||||
_controlDevices.push_back(controlDevice);
|
||||
}
|
||||
|
||||
void ControlManager::UnregisterControlDevice(uint8_t port)
|
||||
ControllerType ControlManager::GetControllerType(uint8_t port)
|
||||
{
|
||||
ControlManager::_controlDevices[port].reset();
|
||||
return EmulationSettings::GetControllerType(port);
|
||||
}
|
||||
|
||||
void ControlManager::RefreshAllPorts()
|
||||
shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerType type, uint8_t port)
|
||||
{
|
||||
if(_keyManager) {
|
||||
_keyManager->RefreshState();
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
|
||||
switch(type) {
|
||||
case ControllerType::StandardController: device.reset(new StandardController(port, EmulationSettings::GetControllerKeys(port))); break;
|
||||
case ControllerType::Zapper: device.reset(new Zapper(port)); break;
|
||||
case ControllerType::ArkanoidController: device.reset(new ArkanoidController(port)); break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(port, EmulationSettings::GetControllerKeys(port))); break;
|
||||
case ControllerType::PowerPad: device.reset(new PowerPad(port, EmulationSettings::GetControllerKeys(port))); break;
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(port)); break;
|
||||
case ControllerType::SuborMouse: device.reset(new SuborMouse(port)); break;
|
||||
case ControllerType::VsZapper: device.reset(new VsZapper(port)); break;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(ControlManager::_controlDevices[i]) {
|
||||
ControlManager::_controlDevices[i]->RefreshStateBuffer();
|
||||
}
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> ControlManager::GetZapper(uint8_t port)
|
||||
shared_ptr<BaseControlDevice> ControlManager::CreateExpansionDevice(ExpansionPortDevice type)
|
||||
{
|
||||
return shared_ptr<BaseControlDevice>(new Zapper(port));
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
|
||||
switch(type) {
|
||||
case ExpansionPortDevice::Zapper: device.reset(new Zapper(BaseControlDevice::ExpDevicePort)); break;
|
||||
case ExpansionPortDevice::ArkanoidController: device.reset(new ArkanoidController(BaseControlDevice::ExpDevicePort)); break;
|
||||
case ExpansionPortDevice::OekaKidsTablet: device.reset(new OekaKidsTablet()); break;
|
||||
case ExpansionPortDevice::FamilyTrainerMat: device.reset(new FamilyMatTrainer(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::KonamiHyperShot: device.reset(new KonamiHyperShot(EmulationSettings::GetControllerKeys(0), EmulationSettings::GetControllerKeys(1))); break;
|
||||
case ExpansionPortDevice::FamilyBasicKeyboard: device.reset(new FamilyBasicKeyboard(EmulationSettings::GetControllerKeys(0))); break; //TODO: tape reader
|
||||
case ExpansionPortDevice::PartyTap: device.reset(new PartyTap(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::Pachinko: device.reset(new PachinkoController(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::ExcitingBoxing: device.reset(new ExcitingBoxingController(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::JissenMahjong: device.reset(new JissenMahjongController(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::SuborKeyboard: device.reset(new SuborKeyboard(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::BarcodeBattler: device.reset(new BarcodeBattlerReader()); break;
|
||||
case ExpansionPortDevice::HoriTrack: device.reset(new HoriTrack(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::BandaiHyperShot: device.reset(new BandaiHyperShot(EmulationSettings::GetControllerKeys(0))); break;
|
||||
case ExpansionPortDevice::AsciiTurboFile: device.reset(new AsciiTurboFile()); break;
|
||||
case ExpansionPortDevice::BattleBox: device.reset(new BattleBox()); break;
|
||||
|
||||
case ExpansionPortDevice::FourPlayerAdapter:
|
||||
default: break;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void ControlManager::UpdateControlDevices()
|
||||
{
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
//Reset update flag
|
||||
EmulationSettings::NeedControllerUpdate();
|
||||
|
||||
ControlManager::_controlDevices.clear();
|
||||
|
||||
ControlManager::RegisterControlDevice(_systemActionManager);
|
||||
|
||||
bool fourScore = EmulationSettings::CheckFlag(EmulationFlags::HasFourScore);
|
||||
ConsoleType consoleType = EmulationSettings::GetConsoleType();
|
||||
ExpansionPortDevice expansionDevice = EmulationSettings::GetExpansionDevice();
|
||||
|
||||
if(EmulationSettings::GetConsoleType() != ConsoleType::Famicom) {
|
||||
|
||||
if(consoleType != ConsoleType::Famicom) {
|
||||
expansionDevice = ExpansionPortDevice::None;
|
||||
} else if(expansionDevice != ExpansionPortDevice::FourPlayerAdapter) {
|
||||
fourScore = false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
bool forceController =
|
||||
i == 1 && EmulationSettings::GetControllerType(1) != ControllerType::StandardController &&
|
||||
(expansionDevice == ExpansionPortDevice::ArkanoidController || expansionDevice == ExpansionPortDevice::Zapper || expansionDevice == ExpansionPortDevice::OekaKidsTablet);
|
||||
|
||||
bool controllerRequired = forceController || (EmulationSettings::GetConsoleType() == ConsoleType::Famicom && !EmulationSettings::CheckFlag(EmulationFlags::UseNes101Hvc101Behavior));
|
||||
|
||||
if(fourScore || controllerRequired) {
|
||||
//Need to set standard controller in all slots if four score (to allow emulation to work correctly)
|
||||
device.reset(new StandardController(i, forceController));
|
||||
} else {
|
||||
switch(EmulationSettings::GetControllerType(i)) {
|
||||
case ControllerType::StandardController: device.reset(new StandardController(i)); break;
|
||||
case ControllerType::Zapper: device = GetZapper(i); break;
|
||||
case ControllerType::ArkanoidController: device.reset(new ArkanoidController(i)); break;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < (fourScore ? 4 : 2); i++) {
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i);
|
||||
if(device) {
|
||||
ControlManager::RegisterControlDevice(device, i);
|
||||
|
||||
if(fourScore) {
|
||||
if(EmulationSettings::GetControllerType(i + 2) == ControllerType::StandardController) {
|
||||
std::dynamic_pointer_cast<StandardController>(device)->AddAdditionalController(shared_ptr<StandardController>(new StandardController(i + 2)));
|
||||
}
|
||||
} else if(i == 1 || expansionDevice == ExpansionPortDevice::ArkanoidController) {
|
||||
switch(expansionDevice) {
|
||||
case ExpansionPortDevice::Zapper: std::dynamic_pointer_cast<StandardController>(device)->AddAdditionalController(shared_ptr<Zapper>(new Zapper(2))); break;
|
||||
case ExpansionPortDevice::ArkanoidController: std::dynamic_pointer_cast<StandardController>(device)->AddAdditionalController(shared_ptr<ArkanoidController>(new ArkanoidController(2))); break;
|
||||
case ExpansionPortDevice::OekaKidsTablet: std::dynamic_pointer_cast<StandardController>(device)->AddAdditionalController(shared_ptr<OekaKidsTablet>(new OekaKidsTablet(2))); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//Remove current device if it's no longer in use
|
||||
ControlManager::UnregisterControlDevice(i);
|
||||
ControlManager::RegisterControlDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
if(fourScore && consoleType == ConsoleType::Nes) {
|
||||
//FourScore is only used to provide the signature for reads past the first 16 reads
|
||||
ControlManager::RegisterControlDevice(shared_ptr<FourScore>(new FourScore()));
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> expDevice = CreateExpansionDevice(expansionDevice);
|
||||
if(expDevice) {
|
||||
ControlManager::RegisterControlDevice(expDevice);
|
||||
}
|
||||
|
||||
if(_mapperControlDevice) {
|
||||
ControlManager::RegisterControlDevice(_mapperControlDevice);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ControlManager::GetOpenBusMask(uint8_t port)
|
||||
{
|
||||
//"In the NES and Famicom, the top three (or five) bits are not driven, and so retain the bits of the previous byte on the bus.
|
||||
//Usually this is the most significant byte of the address of the controller port - 0x40.
|
||||
//Paperboy relies on this behavior and requires that reads from the controller ports return exactly $40 or $41 as appropriate."
|
||||
|
||||
switch(EmulationSettings::GetConsoleType()) {
|
||||
default:
|
||||
case ConsoleType::Nes:
|
||||
@ -203,82 +230,87 @@ uint8_t ControlManager::GetOpenBusMask(uint8_t port)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ControlManager::GetPortValue(uint8_t port)
|
||||
void ControlManager::UpdateInputState()
|
||||
{
|
||||
if(_refreshState) {
|
||||
//Reload until strobe bit is set to off
|
||||
RefreshAllPorts();
|
||||
if(_isLagging) {
|
||||
_lagCounter++;
|
||||
} else {
|
||||
_isLagging = true;
|
||||
}
|
||||
shared_ptr<BaseControlDevice> device = GetControlDevice(port);
|
||||
|
||||
//"In the NES and Famicom, the top three (or five) bits are not driven, and so retain the bits of the previous byte on the bus.
|
||||
//Usually this is the most significant byte of the address of the controller port - 0x40.
|
||||
//Paperboy relies on this behavior and requires that reads from the controller ports return exactly $40 or $41 as appropriate."
|
||||
uint8_t value = MemoryManager::GetOpenBus(GetOpenBusMask(port));
|
||||
if(device) {
|
||||
value |= device->GetPortOutput();
|
||||
|
||||
if(port == 0 && EmulationSettings::GetConsoleType() == ConsoleType::Famicom) {
|
||||
//Connect $4016.2 to the 2nd controller's microphone on Famicoms
|
||||
shared_ptr<StandardController> controller = std::dynamic_pointer_cast<StandardController>(GetControlDevice(1));
|
||||
if(controller && controller->IsMicrophoneActive()) {
|
||||
value |= 0x04;
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
string log = "";
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
device->ClearState();
|
||||
|
||||
bool inputSet = false;
|
||||
for(size_t i = 0; i < _inputProviders.size(); i++) {
|
||||
IInputProvider* provider = _inputProviders[i];
|
||||
if(provider->SetInput(device.get())) {
|
||||
inputSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!inputSet) {
|
||||
device->SetStateFromInput();
|
||||
}
|
||||
|
||||
device->OnAfterSetState();
|
||||
|
||||
shared_ptr<Debugger> debugger = Console::GetInstance()->GetDebugger(false);
|
||||
if(debugger) {
|
||||
debugger->ProcessEvent(EventType::InputPolled);
|
||||
}
|
||||
|
||||
log += "|" + device->GetTextState();
|
||||
|
||||
for(IInputRecorder* recorder : _inputRecorders) {
|
||||
recorder->RecordInput(device.get());
|
||||
}
|
||||
}
|
||||
|
||||
for(IInputRecorder* recorder : _inputRecorders) {
|
||||
recorder->EndFrame();
|
||||
}
|
||||
|
||||
return value;
|
||||
MessageManager::Log(log);
|
||||
}
|
||||
|
||||
uint32_t ControlManager::GetLagCounter()
|
||||
{
|
||||
return _lagCounter;
|
||||
}
|
||||
|
||||
void ControlManager::ResetLagCounter()
|
||||
{
|
||||
_lagCounter = 0;
|
||||
}
|
||||
|
||||
uint8_t ControlManager::ReadRAM(uint16_t addr)
|
||||
{
|
||||
//Used for lag counter
|
||||
//Any frame where the input is read does not count as lag
|
||||
//Used for lag counter - any frame where the input is read does not count as lag
|
||||
_isLagging = false;
|
||||
|
||||
switch(addr) {
|
||||
case 0x4016: return GetPortValue(0);
|
||||
case 0x4017: return GetPortValue(1);
|
||||
uint8_t value = MemoryManager::GetOpenBus(GetOpenBusMask(addr - 0x4016));
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
value |= device->ReadRAM(addr);
|
||||
}
|
||||
return value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
shared_ptr<T> ControlManager::GetExpansionDevice()
|
||||
{
|
||||
shared_ptr<StandardController> controller;
|
||||
controller = std::dynamic_pointer_cast<StandardController>(GetControlDevice(1));
|
||||
if(controller) {
|
||||
shared_ptr<T> expansionDevice;
|
||||
expansionDevice = std::dynamic_pointer_cast<T>(controller->GetAdditionalController());
|
||||
return expansionDevice;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ControlManager::WriteRAM(uint16_t addr, uint8_t value)
|
||||
{
|
||||
//$4016 writes
|
||||
bool previousState = _refreshState;
|
||||
_refreshState = (value & 0x01) == 0x01;
|
||||
|
||||
auto tablet = GetExpansionDevice<OekaKidsTablet>();
|
||||
if(tablet) {
|
||||
tablet->WriteRam(value);
|
||||
} else {
|
||||
if(previousState && !_refreshState) {
|
||||
//Refresh controller once strobe bit is disabled
|
||||
RefreshAllPorts();
|
||||
}
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
device->WriteRAM(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::Reset(bool softReset)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
_keyManager->UpdateDevices();
|
||||
}
|
||||
ResetLagCounter();
|
||||
}
|
||||
|
||||
void ControlManager::StreamState(bool saving)
|
||||
@ -304,8 +336,11 @@ void ControlManager::StreamState(bool saving)
|
||||
}
|
||||
}
|
||||
|
||||
int32_t unusedMousePositionX = 0;
|
||||
int32_t unusedMousePositionY = 0;
|
||||
|
||||
ArrayInfo<ControllerType> types = { controllerTypes, 4 };
|
||||
Stream(_refreshState, _mousePosition.X, _mousePosition.Y, nesModel, expansionDevice, consoleType, types, hasFourScore, useNes101Hvc101Behavior, zapperDetectionRadius);
|
||||
Stream(_refreshState, unusedMousePositionX, unusedMousePositionY, nesModel, expansionDevice, consoleType, types, hasFourScore, useNes101Hvc101Behavior, zapperDetectionRadius, _lagCounter);
|
||||
|
||||
if(!saving) {
|
||||
EmulationSettings::SetNesModel(nesModel);
|
||||
@ -322,31 +357,10 @@ void ControlManager::StreamState(bool saving)
|
||||
UpdateControlDevices();
|
||||
}
|
||||
|
||||
SnapshotInfo device0{ GetControlDevice(0).get() };
|
||||
SnapshotInfo device1{ GetControlDevice(1).get() };
|
||||
Stream(device0, device1);
|
||||
}
|
||||
|
||||
void ControlManager::SetMousePosition(double x, double y)
|
||||
{
|
||||
if(x < 0 || y < 0) {
|
||||
_mousePosition.X = -1;
|
||||
_mousePosition.Y = -1;
|
||||
} else {
|
||||
OverscanDimensions overscan = EmulationSettings::GetOverscanDimensions();
|
||||
_mousePosition.X = (int32_t)(x * (PPU::ScreenWidth - overscan.Left - overscan.Right) + overscan.Left);
|
||||
_mousePosition.Y = (int32_t)(y * (PPU::ScreenHeight - overscan.Top - overscan.Bottom) + overscan.Top);
|
||||
if(GetStateVersion() >= 7) {
|
||||
for(uint8_t i = 0; i < _controlDevices.size(); i++) {
|
||||
SnapshotInfo device{ _controlDevices[i].get() };
|
||||
Stream(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MousePosition ControlManager::GetMousePosition()
|
||||
{
|
||||
return _mousePosition;
|
||||
}
|
||||
|
||||
bool ControlManager::GetLagFlag()
|
||||
{
|
||||
bool flag = _isLagging;
|
||||
_isLagging = true;
|
||||
return flag;
|
||||
}
|
@ -7,75 +7,68 @@
|
||||
|
||||
class BaseControlDevice;
|
||||
class Zapper;
|
||||
class IGameBroadcaster;
|
||||
class IKeyManager;
|
||||
enum class MouseButton;
|
||||
|
||||
struct MousePosition
|
||||
{
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
};
|
||||
class SystemActionManager;
|
||||
class IInputRecorder;
|
||||
class IInputProvider;
|
||||
struct ControlDeviceState;
|
||||
enum class ControllerType;
|
||||
enum class ExpansionPortDevice;
|
||||
|
||||
class ControlManager : public Snapshotable, public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
static unique_ptr<IKeyManager> _keyManager;
|
||||
static shared_ptr<BaseControlDevice> _controlDevices[2];
|
||||
static IGameBroadcaster* _gameBroadcaster;
|
||||
static MousePosition _mousePosition;
|
||||
private:
|
||||
static ControlManager* _instance;
|
||||
static vector<IInputRecorder*> _inputRecorders;
|
||||
static vector<IInputProvider*> _inputProviders;
|
||||
static SimpleLock _deviceLock;
|
||||
|
||||
bool _isLagging = false;
|
||||
bool _refreshState = false;
|
||||
vector<shared_ptr<BaseControlDevice>> _controlDevices;
|
||||
|
||||
uint8_t GetOpenBusMask(uint8_t port);
|
||||
shared_ptr<BaseControlDevice> _systemActionManager;
|
||||
shared_ptr<BaseControlDevice> _mapperControlDevice;
|
||||
|
||||
template<typename T> shared_ptr<T> GetExpansionDevice();
|
||||
uint32_t _lagCounter = 0;
|
||||
bool _isLagging = false;
|
||||
bool _refreshState = false;
|
||||
|
||||
virtual shared_ptr<BaseControlDevice> GetZapper(uint8_t port);
|
||||
uint8_t GetOpenBusMask(uint8_t port);
|
||||
void RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice);
|
||||
|
||||
static void RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice, uint8_t port);
|
||||
void UnregisterControlDevice(uint8_t port);
|
||||
protected:
|
||||
virtual void StreamState(bool saving) override;
|
||||
virtual ControllerType GetControllerType(uint8_t port);
|
||||
|
||||
protected:
|
||||
uint8_t GetPortValue(uint8_t port);
|
||||
virtual void RefreshAllPorts();
|
||||
public:
|
||||
|
||||
virtual void StreamState(bool saving) override;
|
||||
ControlManager(shared_ptr<BaseControlDevice> systemActionManager, shared_ptr<BaseControlDevice> mapperControlDevice);
|
||||
virtual ~ControlManager();
|
||||
|
||||
public:
|
||||
ControlManager();
|
||||
virtual ~ControlManager();
|
||||
void UpdateControlDevices();
|
||||
void UpdateInputState();
|
||||
|
||||
void UpdateControlDevices();
|
||||
bool GetLagFlag();
|
||||
uint32_t GetLagCounter();
|
||||
void ResetLagCounter();
|
||||
|
||||
virtual void Reset(bool softReset);
|
||||
|
||||
static void RegisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||
static void UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||
virtual void Reset(bool softReset);
|
||||
|
||||
static void RegisterKeyManager(IKeyManager* keyManager);
|
||||
static void RefreshKeyState();
|
||||
static bool IsKeyPressed(uint32_t keyCode);
|
||||
static bool IsMouseButtonPressed(MouseButton button);
|
||||
static vector<uint32_t> GetPressedKeys();
|
||||
static string GetKeyName(uint32_t keyCode);
|
||||
static uint32_t GetKeyCode(string keyName);
|
||||
|
||||
static shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port);
|
||||
static void RegisterInputProvider(IInputProvider* provider);
|
||||
static void UnregisterInputProvider(IInputProvider* provider);
|
||||
|
||||
static void SetMousePosition(double x, double y);
|
||||
static MousePosition GetMousePosition();
|
||||
static void RegisterInputRecorder(IInputRecorder* recorder);
|
||||
static void UnregisterInputRecorder(IInputRecorder* recorder);
|
||||
|
||||
static void BroadcastInput(uint8_t port, uint8_t state);
|
||||
static vector<ControlDeviceState> GetPortStates();
|
||||
|
||||
virtual void GetMemoryRanges(MemoryRanges &ranges) override
|
||||
{
|
||||
ranges.AddHandler(MemoryOperation::Read, 0x4016, 0x4017);
|
||||
ranges.AddHandler(MemoryOperation::Write, 0x4016);
|
||||
}
|
||||
|
||||
virtual uint8_t ReadRAM(uint16_t addr) override;
|
||||
virtual void WriteRAM(uint16_t addr, uint8_t value) override;
|
||||
};
|
||||
static shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port);
|
||||
static shared_ptr<BaseControlDevice> CreateControllerDevice(ControllerType type, uint8_t port);
|
||||
static shared_ptr<BaseControlDevice> CreateExpansionDevice(ExpansionPortDevice type);
|
||||
|
||||
virtual void GetMemoryRanges(MemoryRanges &ranges) override
|
||||
{
|
||||
ranges.AddHandler(MemoryOperation::Read, 0x4016, 0x4017);
|
||||
ranges.AddHandler(MemoryOperation::Write, 0x4016);
|
||||
}
|
||||
|
||||
virtual uint8_t ReadRAM(uint16_t addr) override;
|
||||
virtual void WriteRAM(uint16_t addr, uint8_t value) override;
|
||||
};
|
||||
|
@ -411,25 +411,53 @@
|
||||
<ClInclude Include="ArkanoidController.h" />
|
||||
<ClInclude Include="Assembler.h" />
|
||||
<ClInclude Include="AutomaticRomTest.h" />
|
||||
<ClInclude Include="BandaiHyperShot.h" />
|
||||
<ClInclude Include="BandaiMicrophone.h" />
|
||||
<ClInclude Include="BarcodeBattlerReader.h" />
|
||||
<ClInclude Include="BaseRenderer.h" />
|
||||
<ClInclude Include="BatteryManager.h" />
|
||||
<ClInclude Include="BattleBox.h" />
|
||||
<ClInclude Include="ControlDeviceState.h" />
|
||||
<ClInclude Include="FdsSystemActionManager.h" />
|
||||
<ClInclude Include="IBarcodeReader.h" />
|
||||
<ClInclude Include="IBattery.h" />
|
||||
<ClInclude Include="IInputProvider.h" />
|
||||
<ClInclude Include="IInputRecorder.h" />
|
||||
<ClInclude Include="KeyManager.h" />
|
||||
<ClInclude Include="MovieRecorder.h" />
|
||||
<ClInclude Include="AsciiTurboFile.h" />
|
||||
<ClInclude Include="SystemActionManager.h" />
|
||||
<ClInclude Include="DatachBarcodeReader.h" />
|
||||
<ClInclude Include="DebugHud.h" />
|
||||
<ClInclude Include="DrawCommand.h" />
|
||||
<ClInclude Include="DrawLineCommand.h" />
|
||||
<ClInclude Include="DrawPixelCommand.h" />
|
||||
<ClInclude Include="DrawStringCommand.h" />
|
||||
<ClInclude Include="ExcitingBoxingController.h" />
|
||||
<ClInclude Include="FamilyBasicDataRecorder.h" />
|
||||
<ClInclude Include="FamilyBasicKeyboard.h" />
|
||||
<ClInclude Include="FamilyMatTrainer.h" />
|
||||
<ClInclude Include="FceuxMovie.h" />
|
||||
<ClInclude Include="FourScore.h" />
|
||||
<ClInclude Include="HdAudioDevice.h" />
|
||||
<ClInclude Include="HdBuilderPpu.h" />
|
||||
<ClInclude Include="HdData.h" />
|
||||
<ClInclude Include="HdPackBuilder.h" />
|
||||
<ClInclude Include="HdPackLoader.h" />
|
||||
<ClInclude Include="HoriTrack.h" />
|
||||
<ClInclude Include="JissenMahjongController.h" />
|
||||
<ClInclude Include="KonamiHyperShot.h" />
|
||||
<ClInclude Include="RotateFilter.h" />
|
||||
<ClInclude Include="LuaApi.h" />
|
||||
<ClInclude Include="LuaCallHelper.h" />
|
||||
<ClInclude Include="LuaScriptingContext.h" />
|
||||
<ClInclude Include="Mapper174.h" />
|
||||
<ClInclude Include="Mapper39.h" />
|
||||
<ClInclude Include="OggMixer.h" />
|
||||
<ClInclude Include="OggReader.h" />
|
||||
<ClInclude Include="PachinkoController.h" />
|
||||
<ClInclude Include="PartyTap.h" />
|
||||
<ClInclude Include="PowerPad.h" />
|
||||
<ClInclude Include="Rambo1_158.h" />
|
||||
<ClInclude Include="RecordedRomTest.h" />
|
||||
<ClInclude Include="AutoSaveManager.h" />
|
||||
@ -473,6 +501,10 @@
|
||||
<ClInclude Include="ScriptHost.h" />
|
||||
<ClInclude Include="ScriptingContext.h" />
|
||||
<ClInclude Include="SealieComputing.h" />
|
||||
<ClInclude Include="SnesController.h" />
|
||||
<ClInclude Include="SnesMouse.h" />
|
||||
<ClInclude Include="SuborKeyboard.h" />
|
||||
<ClInclude Include="SuborMouse.h" />
|
||||
<ClInclude Include="UnlD1038.h" />
|
||||
<ClInclude Include="DaouInfosys.h" />
|
||||
<ClInclude Include="DebugBreakHelper.h" />
|
||||
@ -675,7 +707,6 @@
|
||||
<ClInclude Include="HdPpu.h" />
|
||||
<ClInclude Include="IAudioDevice.h" />
|
||||
<ClInclude Include="BaseControlDevice.h" />
|
||||
<ClInclude Include="IGameBroadcaster.h" />
|
||||
<ClInclude Include="IKeyManager.h" />
|
||||
<ClInclude Include="IMemoryHandler.h" />
|
||||
<ClInclude Include="Console.h" />
|
||||
@ -776,6 +807,7 @@
|
||||
<ClInclude Include="VsControlManager.h" />
|
||||
<ClInclude Include="VsSystem.h" />
|
||||
<ClInclude Include="ScaleFilter.h" />
|
||||
<ClInclude Include="VsSystemActionManager.h" />
|
||||
<ClInclude Include="VsZapper.h" />
|
||||
<ClInclude Include="Waixing162.h" />
|
||||
<ClInclude Include="Waixing164.h" />
|
||||
@ -788,10 +820,10 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="APU.cpp" />
|
||||
<ClCompile Include="ApuLengthCounter.cpp" />
|
||||
<ClCompile Include="ArkanoidController.cpp" />
|
||||
<ClCompile Include="Assembler.cpp" />
|
||||
<ClCompile Include="AutomaticRomTest.cpp" />
|
||||
<ClCompile Include="BaseRenderer.cpp" />
|
||||
<ClCompile Include="BatteryManager.cpp" />
|
||||
<ClCompile Include="DebugHud.cpp" />
|
||||
<ClCompile Include="DrawRectangleCommand.h" />
|
||||
<ClCompile Include="FceuxMovie.cpp" />
|
||||
@ -799,9 +831,11 @@
|
||||
<ClCompile Include="HdNesPack.cpp" />
|
||||
<ClCompile Include="HdPackBuilder.cpp" />
|
||||
<ClCompile Include="HdPackLoader.cpp" />
|
||||
<ClCompile Include="KeyManager.cpp" />
|
||||
<ClCompile Include="LuaApi.cpp" />
|
||||
<ClCompile Include="LuaCallHelper.cpp" />
|
||||
<ClCompile Include="LuaScriptingContext.cpp" />
|
||||
<ClCompile Include="MovieRecorder.cpp" />
|
||||
<ClCompile Include="OggMixer.cpp" />
|
||||
<ClCompile Include="OggReader.cpp" />
|
||||
<ClCompile Include="RecordedRomTest.cpp" />
|
||||
@ -840,7 +874,6 @@
|
||||
<ClCompile Include="MesenMovie.cpp" />
|
||||
<ClCompile Include="NsfMapper.cpp" />
|
||||
<ClCompile Include="NtscFilter.cpp" />
|
||||
<ClCompile Include="OekaKidsTablet.cpp" />
|
||||
<ClCompile Include="Profiler.cpp" />
|
||||
<ClCompile Include="ReverbFilter.cpp" />
|
||||
<ClCompile Include="RewindData.cpp" />
|
||||
@ -852,7 +885,6 @@
|
||||
<ClCompile Include="ShortcutKeyHandler.cpp" />
|
||||
<ClCompile Include="Snapshotable.cpp" />
|
||||
<ClCompile Include="SoundMixer.cpp" />
|
||||
<ClCompile Include="StandardController.cpp" />
|
||||
<ClCompile Include="MapperFactory.cpp" />
|
||||
<ClCompile Include="MemoryManager.cpp" />
|
||||
<ClCompile Include="MessageManager.cpp" />
|
||||
@ -881,9 +913,7 @@
|
||||
<ClCompile Include="VirtualFile.cpp" />
|
||||
<ClCompile Include="VsControlManager.cpp" />
|
||||
<ClCompile Include="ScaleFilter.cpp" />
|
||||
<ClCompile Include="VsZapper.cpp" />
|
||||
<ClCompile Include="WaveRecorder.cpp" />
|
||||
<ClCompile Include="Zapper.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -52,9 +52,6 @@
|
||||
<Filter Include="Nes\Mappers\FDS">
|
||||
<UniqueIdentifier>{4a7af167-f6cb-4173-b7ca-04ed7c5858b1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Controllers">
|
||||
<UniqueIdentifier>{acd315c2-48ad-4243-a997-bb9a970c24bd}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\APU\Filters">
|
||||
<UniqueIdentifier>{783f3638-4293-480f-b525-2485c4209ff5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@ -101,6 +98,21 @@
|
||||
<Filter Include="Debugger\Scripting\Lua">
|
||||
<UniqueIdentifier>{e1e8a5d2-aa9a-40e1-94eb-00adec1cdef3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Input">
|
||||
<UniqueIdentifier>{acd315c2-48ad-4243-a997-bb9a970c24bd}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Input\System">
|
||||
<UniqueIdentifier>{b7a48fc3-5ebb-4aa7-93ae-f70d58bdbfd8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Input\Storage">
|
||||
<UniqueIdentifier>{fdd6ac29-77ce-4db5-8d06-2aa27f95a41c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Input\BarcodeReaders">
|
||||
<UniqueIdentifier>{38798114-2511-4cce-97a7-c6adac514423}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Nes\Input\Controllers">
|
||||
<UniqueIdentifier>{7b42e684-9487-4031-a769-a05934998777}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="IAudioDevice.h">
|
||||
@ -262,9 +274,6 @@
|
||||
<ClInclude Include="GameServerConnection.h">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IGameBroadcaster.h">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Console.h">
|
||||
<Filter>Nes</Filter>
|
||||
</ClInclude>
|
||||
@ -505,14 +514,8 @@
|
||||
<ClInclude Include="VideoRenderer.h">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StandardController.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Zapper.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BaseControlDevice.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
<Filter>Nes\Input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PlayerListMessage.h">
|
||||
<Filter>NetPlay\Messages</Filter>
|
||||
@ -523,9 +526,6 @@
|
||||
<ClInclude Include="ForceDisconnectMessage.h">
|
||||
<Filter>NetPlay\Messages</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ArkanoidController.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StereoPanningFilter.h">
|
||||
<Filter>Nes\APU\Filters</Filter>
|
||||
</ClInclude>
|
||||
@ -676,9 +676,6 @@
|
||||
<ClInclude Include="Mapper227.h">
|
||||
<Filter>Nes\Mappers\Unnamed</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VsZapper.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NsfLoader.h">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClInclude>
|
||||
@ -844,9 +841,6 @@
|
||||
<ClInclude Include="Racermate.h">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OekaKidsTablet.h">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MMC3_114.h">
|
||||
<Filter>Nes\Mappers\MMC</Filter>
|
||||
</ClInclude>
|
||||
@ -1243,9 +1237,123 @@
|
||||
<ClInclude Include="VirtualFile.h">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Mapper39.h">
|
||||
<Filter>Nes\Mappers\Unnamed</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MovieRecorder.h">
|
||||
<Filter>Movies</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IBarcodeReader.h">
|
||||
<Filter>Nes\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IBattery.h">
|
||||
<Filter>Nes\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyManager.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IInputProvider.h">
|
||||
<Filter>Nes\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IInputRecorder.h">
|
||||
<Filter>Nes\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlDeviceState.h">
|
||||
<Filter>Nes\Input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BatteryManager.h">
|
||||
<Filter>Nes</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RotateFilter.h">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SystemActionManager.h">
|
||||
<Filter>Nes\Input\System</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DatachBarcodeReader.h">
|
||||
<Filter>Nes\Input\BarcodeReaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BarcodeBattlerReader.h">
|
||||
<Filter>Nes\Input\BarcodeReaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AsciiTurboFile.h">
|
||||
<Filter>Nes\Input\Storage</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BattleBox.h">
|
||||
<Filter>Nes\Input\Storage</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FdsSystemActionManager.h">
|
||||
<Filter>Nes\Input\System</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VsSystemActionManager.h">
|
||||
<Filter>Nes\Input\System</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ArkanoidController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BandaiMicrophone.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BandaiHyperShot.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ExcitingBoxingController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FamilyBasicDataRecorder.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FamilyBasicKeyboard.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FamilyMatTrainer.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FourScore.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HoriTrack.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="JissenMahjongController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KonamiHyperShot.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OekaKidsTablet.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PachinkoController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PartyTap.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PowerPad.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SnesController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SnesMouse.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StandardController.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SuborKeyboard.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SuborMouse.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VsZapper.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Zapper.h">
|
||||
<Filter>Nes\Input\Controllers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
@ -1350,17 +1458,8 @@
|
||||
<ClCompile Include="VideoRenderer.cpp">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StandardController.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Zapper.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BaseControlDevice.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArkanoidController.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
<Filter>Nes\Input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StereoPanningFilter.cpp">
|
||||
<Filter>Nes\APU\Filters</Filter>
|
||||
@ -1395,18 +1494,12 @@
|
||||
<ClCompile Include="GameDatabase.cpp">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VsZapper.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NsfMapper.cpp">
|
||||
<Filter>Nes\Mappers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoHud.cpp">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OekaKidsTablet.cpp">
|
||||
<Filter>Nes\Controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AutoSaveManager.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
@ -1482,9 +1575,6 @@
|
||||
<ClCompile Include="OggMixer.cpp">
|
||||
<Filter>HdPacks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OggReader.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugHud.cpp">
|
||||
<Filter>Debugger\Scripting\DebugHud</Filter>
|
||||
</ClCompile>
|
||||
@ -1509,9 +1599,21 @@
|
||||
<ClCompile Include="HdAudioDevice.cpp">
|
||||
<Filter>HdPacks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OggReader.cpp">
|
||||
<Filter>HdPacks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VirtualFile.cpp">
|
||||
<Filter>Nes\RomLoader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MovieRecorder.cpp">
|
||||
<Filter>Movies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyManager.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BatteryManager.cpp">
|
||||
<Filter>Nes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RotateFilter.cpp">
|
||||
<Filter>VideoDecoder</Filter>
|
||||
</ClCompile>
|
||||
|
200
Core/DatachBarcodeReader.h
Normal file
200
Core/DatachBarcodeReader.h
Normal file
@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "IBarcodeReader.h"
|
||||
#include "CPU.h"
|
||||
|
||||
class DatachBarcodeReader : public BaseControlDevice, public IBarcodeReader
|
||||
{
|
||||
private:
|
||||
vector<uint8_t> _data;
|
||||
int32_t _insertCycle = 0;
|
||||
uint64_t _newBarcode = 0;
|
||||
uint32_t _newBarcodeDigitCount = 0;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
|
||||
uint32_t dataSize = (uint32_t)_data.size();
|
||||
Stream(dataSize);
|
||||
if(!saving) {
|
||||
_data.resize(dataSize);
|
||||
}
|
||||
|
||||
ArrayInfo<uint8_t> data{ _data.data(), dataSize };
|
||||
Stream(_insertCycle, _newBarcode, _newBarcodeDigitCount, data);
|
||||
}
|
||||
|
||||
bool IsRawString() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
DatachBarcodeReader() : BaseControlDevice(BaseControlDevice::MapperInputPort)
|
||||
{
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
if(_newBarcodeDigitCount > 0) {
|
||||
string barcodeText = std::to_string(_newBarcode);
|
||||
//Pad 8 or 13 character barcode with 0s at start
|
||||
barcodeText.insert(0, _newBarcodeDigitCount - barcodeText.size(), '0');
|
||||
SetTextState(barcodeText);
|
||||
_newBarcode = 0;
|
||||
_newBarcodeDigitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterSetState() override
|
||||
{
|
||||
if(GetRawState().State.size() > 0) {
|
||||
InitBarcodeData();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetOutput()
|
||||
{
|
||||
int32_t elapsedCycles = CPU::GetCycleCount() - _insertCycle;
|
||||
int32_t bitNumber = elapsedCycles / 1000;
|
||||
if(bitNumber < (int32_t)_data.size()) {
|
||||
return _data[bitNumber];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InputBarcode(uint64_t barcode, uint32_t digitCount)
|
||||
{
|
||||
_newBarcode = barcode;
|
||||
_newBarcodeDigitCount = digitCount;
|
||||
}
|
||||
|
||||
void InitBarcodeData()
|
||||
{
|
||||
_insertCycle = CPU::GetCycleCount();
|
||||
|
||||
static const uint8_t prefixParityType[10][6] = {
|
||||
{ 8,8,8,8,8,8 },{ 8,8,0,8,0,0 },
|
||||
{ 8,8,0,0,8,0 },{ 8,8,0,0,0,8 },
|
||||
{ 8,0,8,8,0,0 },{ 8,0,0,8,8,0 },
|
||||
{ 8,0,0,0,8,8 },{ 8,0,8,0,8,0 },
|
||||
{ 8,0,8,0,0,8 },{ 8,0,0,8,0,8 }
|
||||
};
|
||||
|
||||
static const uint8_t dataLeftOdd[10][7] = {
|
||||
{ 8,8,8,0,0,8,0 },{ 8,8,0,0,8,8,0 },
|
||||
{ 8,8,0,8,8,0,0 },{ 8,0,0,0,0,8,0 },
|
||||
{ 8,0,8,8,8,0,0 },{ 8,0,0,8,8,8,0 },
|
||||
{ 8,0,8,0,0,0,0 },{ 8,0,0,0,8,0,0 },
|
||||
{ 8,0,0,8,0,0,0 },{ 8,8,8,0,8,0,0 }
|
||||
};
|
||||
|
||||
static const uint8_t dataLeftEven[10][7] = {
|
||||
{ 8,0,8,8,0,0,0 },{ 8,0,0,8,8,0,0 },
|
||||
{ 8,8,0,0,8,0,0 },{ 8,0,8,8,8,8,0 },
|
||||
{ 8,8,0,0,0,8,0 },{ 8,0,0,0,8,8,0 },
|
||||
{ 8,8,8,8,0,8,0 },{ 8,8,0,8,8,8,0 },
|
||||
{ 8,8,8,0,8,8,0 },{ 8,8,0,8,0,0,0 }
|
||||
};
|
||||
|
||||
static const uint8_t dataRight[10][7] = {
|
||||
{ 0,0,0,8,8,0,8 },{ 0,0,8,8,0,0,8 },
|
||||
{ 0,0,8,0,0,8,8 },{ 0,8,8,8,8,0,8 },
|
||||
{ 0,8,0,0,0,8,8 },{ 0,8,8,0,0,0,8 },
|
||||
{ 0,8,0,8,8,8,8 },{ 0,8,8,8,0,8,8 },
|
||||
{ 0,8,8,0,8,8,8 },{ 0,0,0,8,0,8,8 }
|
||||
};
|
||||
|
||||
string barcode = GetTextState();
|
||||
vector<uint8_t> code;
|
||||
for(uint8_t i = 0; i < barcode.size(); i++) {
|
||||
code.push_back(barcode[i] - '0');
|
||||
}
|
||||
|
||||
_data.clear();
|
||||
|
||||
for(uint32_t i = 0; i < 33; i++) {
|
||||
_data.push_back(8);
|
||||
}
|
||||
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
|
||||
uint32_t sum = 0;
|
||||
|
||||
if(barcode.size() == 13) {
|
||||
for(uint32_t i = 0; i < 6; i++) {
|
||||
bool odd = prefixParityType[code[0]][i] != 0;
|
||||
for(uint32_t j = 0; j < 7; j++) {
|
||||
_data.push_back(odd ? dataLeftOdd[code[i + 1]][j] : dataLeftEven[code[i + 1]][j]);
|
||||
}
|
||||
}
|
||||
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
|
||||
for(uint32_t i = 7; i < 12; i++) {
|
||||
for(uint32_t j = 0; j < 7; j++) {
|
||||
_data.push_back(dataRight[code[i]][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < 12; i++) {
|
||||
sum += (i & 1) ? (code[i] * 3) : (code[i] * 1);
|
||||
}
|
||||
} else {
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
for(uint32_t j = 0; j < 7; j++) {
|
||||
_data.push_back(dataLeftOdd[code[i]][j]);
|
||||
}
|
||||
}
|
||||
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
|
||||
for(uint32_t i = 4; i < 7; i++) {
|
||||
for(uint32_t j = 0; j < 7; j++) {
|
||||
_data.push_back(dataRight[code[i]][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < 7; i++) {
|
||||
sum += (i & 1) ? (code[i] * 1) : (code[i] * 3);
|
||||
}
|
||||
}
|
||||
|
||||
sum = (10 - (sum % 10)) % 10;
|
||||
|
||||
for(uint32_t i = 0; i < 7; i++) {
|
||||
_data.push_back(dataRight[sum][i]);
|
||||
}
|
||||
|
||||
_data.push_back(0);
|
||||
_data.push_back(8);
|
||||
_data.push_back(0);
|
||||
|
||||
for(uint32_t i = 0; i < 32; i++) {
|
||||
_data.push_back(8);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value)
|
||||
{
|
||||
}
|
||||
};
|
@ -24,6 +24,7 @@
|
||||
#include "DebugBreakHelper.h"
|
||||
#include "ScriptHost.h"
|
||||
#include "DebugHud.h"
|
||||
#include "StandardController.h"
|
||||
|
||||
Debugger* Debugger::Instance = nullptr;
|
||||
const int Debugger::BreakpointTypeCount;
|
||||
@ -1025,22 +1026,6 @@ int32_t Debugger::FindSubEntryPoint(uint16_t relativeAddress)
|
||||
return address > relativeAddress ? relativeAddress : (address + 1);
|
||||
}
|
||||
|
||||
bool Debugger::HasInputOverride(uint8_t port)
|
||||
{
|
||||
if(Debugger::Instance) {
|
||||
return Debugger::Instance->_inputOverride[port] != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t Debugger::GetInputOverride(uint8_t port)
|
||||
{
|
||||
if(Debugger::Instance) {
|
||||
return Debugger::Instance->_inputOverride[port];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Debugger::SetInputOverride(uint8_t port, uint32_t state)
|
||||
{
|
||||
_inputOverride[port] = state;
|
||||
@ -1143,4 +1128,22 @@ void Debugger::ProcessEvent(EventType type)
|
||||
script->ProcessEvent(type);
|
||||
}
|
||||
}
|
||||
|
||||
if(type == EventType::InputPolled) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(_inputOverride[i] != 0) {
|
||||
shared_ptr<StandardController> controller = std::dynamic_pointer_cast<StandardController>(ControlManager::GetControlDevice(i));
|
||||
if(controller) {
|
||||
controller->SetBitValue(StandardController::Buttons::A, (_inputOverride[i] & 0x01) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::B, (_inputOverride[i] & 0x02) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Select, (_inputOverride[i] & 0x04) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Start, (_inputOverride[i] & 0x08) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Up, (_inputOverride[i] & 0x10) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Down, (_inputOverride[i] & 0x20) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Left, (_inputOverride[i] & 0x40) != 0);
|
||||
controller->SetBitValue(StandardController::Buttons::Right, (_inputOverride[i] & 0x80) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,4 +87,6 @@ enum class EventType
|
||||
CodeBreak = 5,
|
||||
StateLoaded = 6,
|
||||
StateSaved = 7,
|
||||
InputPolled = 8,
|
||||
EventTypeSize
|
||||
};
|
@ -8,7 +8,7 @@ private:
|
||||
uint8_t _reg;
|
||||
|
||||
protected:
|
||||
uint16_t GetPRGPageSize() override { return 0x2000; }
|
||||
uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
uint32_t GetWorkRamSize() override { return 0x8000; }
|
||||
uint32_t GetWorkRamPageSize() override { return 0x2000; }
|
||||
@ -31,7 +31,7 @@ protected:
|
||||
|
||||
void UpdatePrg()
|
||||
{
|
||||
SelectPrgPage4x(0, (_reg & 0x1F) << 2);
|
||||
SelectPRGPage(0, _reg & 0x1F);
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (_reg >> 6) & 0x03, PrgMemoryType::WorkRam);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "Console.h"
|
||||
#include "VsControlManager.h"
|
||||
#include "RewindManager.h"
|
||||
|
||||
//Version 0.9.3
|
||||
@ -62,6 +61,7 @@ std::unordered_map<uint32_t, KeyCombination> EmulationSettings::_emulatorKeys[2]
|
||||
std::unordered_map<uint32_t, vector<KeyCombination>> EmulationSettings::_shortcutSupersets[2];
|
||||
|
||||
RamPowerOnState EmulationSettings::_ramPowerOnState = RamPowerOnState::AllZeros;
|
||||
uint32_t EmulationSettings::_dipSwitches = 0;
|
||||
|
||||
InputDisplaySettings EmulationSettings::_inputDisplaySettings = { 0, InputDisplayPosition::TopLeft, false };
|
||||
|
||||
@ -69,7 +69,7 @@ OverscanDimensions EmulationSettings::_overscan;
|
||||
VideoFilterType EmulationSettings::_videoFilterType = VideoFilterType::None;
|
||||
VideoResizeFilter EmulationSettings::_resizeFilter = VideoResizeFilter::NearestNeighbor;
|
||||
double EmulationSettings::_videoScale = 1;
|
||||
VideoAspectRatio EmulationSettings::_aspectRatio = VideoAspectRatio::Auto;
|
||||
VideoAspectRatio EmulationSettings::_aspectRatio = VideoAspectRatio::NoStretching;
|
||||
double EmulationSettings::_customAspectRatio = 1.0;
|
||||
PictureSettings EmulationSettings::_pictureSettings;
|
||||
NtscFilterSettings EmulationSettings::_ntscFilterSettings;
|
||||
@ -83,6 +83,8 @@ ControllerType EmulationSettings::_controllerTypes[4] = { ControllerType::None,
|
||||
KeyMappingSet EmulationSettings::_controllerKeys[4] = { KeyMappingSet(), KeyMappingSet(), KeyMappingSet(), KeyMappingSet() };
|
||||
bool EmulationSettings::_needControllerUpdate = false;
|
||||
uint32_t EmulationSettings::_zapperDetectionRadius = 0;
|
||||
double EmulationSettings::_mouseSensitivity = 1.0;
|
||||
int32_t EmulationSettings::_inputPollScanline = 240;
|
||||
|
||||
uint32_t EmulationSettings::_defaultPpuPalette[64] = { /* 2C02 */ 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 };
|
||||
|
||||
@ -143,3 +145,62 @@ double EmulationSettings::GetAspectRatio()
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const vector<string> NesModelNames = {
|
||||
"Auto",
|
||||
"NTSC",
|
||||
"PAL",
|
||||
"Dendy"
|
||||
};
|
||||
|
||||
const vector<string> ConsoleTypeNames = {
|
||||
"Nes",
|
||||
"Famicom",
|
||||
};
|
||||
|
||||
const vector<string> ControllerTypeNames = {
|
||||
"None",
|
||||
"StandardController",
|
||||
"Zapper",
|
||||
"ArkanoidController",
|
||||
"SnesController",
|
||||
"PowerPad",
|
||||
"SnesMouse",
|
||||
"SuborMouse",
|
||||
"VsZapper"
|
||||
};
|
||||
|
||||
const vector<string> ExpansionPortDeviceNames = {
|
||||
"None",
|
||||
"Zapper",
|
||||
"FourPlayerAdapter",
|
||||
"ArkanoidController",
|
||||
"OekaKidsTablet",
|
||||
"FamilyTrainerMat",
|
||||
"KonamiHyperShot",
|
||||
"FamilyBasicKeyboard",
|
||||
"PartyTap",
|
||||
"Pachinko",
|
||||
"ExcitingBoxing",
|
||||
"JissenMahjong",
|
||||
"SuborKeyboard",
|
||||
"BarcodeBattler",
|
||||
"HoriTrack",
|
||||
"BandaiHyperShot",
|
||||
"AsciiTurboFile",
|
||||
"BattleBox",
|
||||
};
|
||||
|
||||
const vector<string> PpuModelNames = {
|
||||
"Ppu2C02",
|
||||
"Ppu2C03",
|
||||
"Ppu2C04A",
|
||||
"Ppu2C04B",
|
||||
"Ppu2C04C",
|
||||
"Ppu2C04D",
|
||||
"Ppu2C05A",
|
||||
"Ppu2C05B",
|
||||
"Ppu2C05C",
|
||||
"Ppu2C05D",
|
||||
"Ppu2C05E"
|
||||
};
|
@ -103,21 +103,6 @@ enum class EqualizerFilterType
|
||||
Chebyshev2 = 3
|
||||
};
|
||||
|
||||
enum class NesModel
|
||||
{
|
||||
Auto = 0,
|
||||
NTSC = 1,
|
||||
PAL = 2,
|
||||
Dendy = 3,
|
||||
};
|
||||
|
||||
enum class RamPowerOnState
|
||||
{
|
||||
AllZeros = 0,
|
||||
AllOnes = 1,
|
||||
Random = 2
|
||||
};
|
||||
|
||||
enum class ScaleFilterType
|
||||
{
|
||||
xBRZ = 0,
|
||||
@ -223,20 +208,44 @@ struct NtscFilterSettings
|
||||
double QFilterLength = 0;
|
||||
};
|
||||
|
||||
enum class RamPowerOnState
|
||||
{
|
||||
AllZeros = 0,
|
||||
AllOnes = 1,
|
||||
Random = 2
|
||||
};
|
||||
|
||||
extern const vector<string> NesModelNames;
|
||||
enum class NesModel
|
||||
{
|
||||
Auto = 0,
|
||||
NTSC = 1,
|
||||
PAL = 2,
|
||||
Dendy = 3,
|
||||
};
|
||||
|
||||
extern const vector<string> ConsoleTypeNames;
|
||||
enum class ConsoleType
|
||||
{
|
||||
Nes = 0,
|
||||
Famicom = 1
|
||||
};
|
||||
|
||||
extern const vector<string> ControllerTypeNames;
|
||||
enum class ControllerType
|
||||
{
|
||||
None = 0,
|
||||
StandardController = 1,
|
||||
Zapper = 2,
|
||||
ArkanoidController = 3,
|
||||
SnesController = 4,
|
||||
PowerPad = 5,
|
||||
SnesMouse = 6,
|
||||
SuborMouse = 7,
|
||||
VsZapper = 8
|
||||
};
|
||||
|
||||
extern const vector<string> ExpansionPortDeviceNames;
|
||||
enum class ExpansionPortDevice
|
||||
{
|
||||
None = 0,
|
||||
@ -244,6 +253,35 @@ enum class ExpansionPortDevice
|
||||
FourPlayerAdapter = 2,
|
||||
ArkanoidController = 3,
|
||||
OekaKidsTablet = 4,
|
||||
FamilyTrainerMat = 5,
|
||||
KonamiHyperShot = 6,
|
||||
FamilyBasicKeyboard = 7,
|
||||
PartyTap = 8,
|
||||
Pachinko = 9,
|
||||
ExcitingBoxing = 10,
|
||||
JissenMahjong = 11,
|
||||
SuborKeyboard = 12,
|
||||
BarcodeBattler = 13,
|
||||
HoriTrack = 14,
|
||||
BandaiHyperShot = 15,
|
||||
AsciiTurboFile = 16,
|
||||
BattleBox = 17,
|
||||
};
|
||||
|
||||
extern const vector<string> PpuModelNames;
|
||||
enum class PpuModel
|
||||
{
|
||||
Ppu2C02 = 0,
|
||||
Ppu2C03 = 1,
|
||||
Ppu2C04A = 2,
|
||||
Ppu2C04B = 3,
|
||||
Ppu2C04C = 4,
|
||||
Ppu2C04D = 5,
|
||||
Ppu2C05A = 6,
|
||||
Ppu2C05B = 7,
|
||||
Ppu2C05C = 8,
|
||||
Ppu2C05D = 9,
|
||||
Ppu2C05E = 10
|
||||
};
|
||||
|
||||
struct KeyMapping
|
||||
@ -261,10 +299,20 @@ struct KeyMapping
|
||||
uint32_t TurboStart = 0;
|
||||
uint32_t TurboSelect = 0;
|
||||
uint32_t Microphone = 0;
|
||||
uint32_t LButton = 0;
|
||||
uint32_t RButton = 0;
|
||||
|
||||
uint32_t PowerPadButtons[12] = {};
|
||||
uint32_t FamilyBasicKeyboardButtons[72] = {};
|
||||
uint32_t PartyTapButtons[6] = {};
|
||||
uint32_t PachinkoButtons[2] = {};
|
||||
uint32_t ExcitingBoxingButtons[8] = {};
|
||||
uint32_t JissenMahjongButtons[21] = {};
|
||||
uint32_t SuborKeyboardButtons[99] = {};
|
||||
|
||||
bool HasKeySet()
|
||||
{
|
||||
return A || B || Up || Down || Left || Right || Start || Select || TurboA || TurboB || TurboStart || TurboSelect || Microphone;
|
||||
return true || A || B || Up || Down || Left || Right || Start || Select || TurboA || TurboB || TurboStart || TurboSelect || Microphone || LButton || RButton;
|
||||
}
|
||||
};
|
||||
|
||||
@ -274,7 +322,25 @@ struct KeyMappingSet
|
||||
KeyMapping Mapping2;
|
||||
KeyMapping Mapping3;
|
||||
KeyMapping Mapping4;
|
||||
uint32_t TurboSpeed;
|
||||
uint32_t TurboSpeed = 0;
|
||||
|
||||
vector<KeyMapping> GetKeyMappingArray()
|
||||
{
|
||||
vector<KeyMapping> keyMappings;
|
||||
if(Mapping1.HasKeySet()) {
|
||||
keyMappings.push_back(Mapping1);
|
||||
}
|
||||
if(Mapping2.HasKeySet()) {
|
||||
keyMappings.push_back(Mapping2);
|
||||
}
|
||||
if(Mapping3.HasKeySet()) {
|
||||
keyMappings.push_back(Mapping3);
|
||||
}
|
||||
if(Mapping4.HasKeySet()) {
|
||||
keyMappings.push_back(Mapping4);
|
||||
}
|
||||
return keyMappings;
|
||||
}
|
||||
};
|
||||
|
||||
enum class EmulatorShortcut
|
||||
@ -305,6 +371,8 @@ enum class EmulatorShortcut
|
||||
|
||||
InsertCoin1,
|
||||
InsertCoin2,
|
||||
|
||||
InputBarcode,
|
||||
|
||||
TakeScreenshot,
|
||||
|
||||
@ -421,21 +489,6 @@ enum class StereoFilter
|
||||
Panning = 2,
|
||||
};
|
||||
|
||||
enum class PpuModel
|
||||
{
|
||||
Ppu2C02 = 0,
|
||||
Ppu2C03 = 1,
|
||||
Ppu2C04A = 2,
|
||||
Ppu2C04B = 3,
|
||||
Ppu2C04C = 4,
|
||||
Ppu2C04D = 5,
|
||||
Ppu2C05A = 6,
|
||||
Ppu2C05B = 7,
|
||||
Ppu2C05C = 8,
|
||||
Ppu2C05D = 9,
|
||||
Ppu2C05E = 10
|
||||
};
|
||||
|
||||
enum class InputDisplayPosition
|
||||
{
|
||||
TopLeft = 0,
|
||||
@ -521,6 +574,8 @@ private:
|
||||
static KeyMappingSet _controllerKeys[4];
|
||||
static bool _needControllerUpdate;
|
||||
static uint32_t _zapperDetectionRadius;
|
||||
static double _mouseSensitivity;
|
||||
static int32_t _inputPollScanline;
|
||||
|
||||
static int32_t _nsfAutoDetectSilenceDelay;
|
||||
static int32_t _nsfMoveToNextTrackTime;
|
||||
@ -535,6 +590,7 @@ private:
|
||||
static std::unordered_map<uint32_t, vector<KeyCombination>> _shortcutSupersets[2];
|
||||
|
||||
static RamPowerOnState _ramPowerOnState;
|
||||
static uint32_t _dipSwitches;
|
||||
|
||||
static SimpleLock _shortcutLock;
|
||||
static SimpleLock _equalizerLock;
|
||||
@ -610,6 +666,11 @@ public:
|
||||
return (CheckFlag(EmulationFlags::Paused) || (CheckFlag(EmulationFlags::InBackground) && CheckFlag(EmulationFlags::PauseWhenInBackground) && !GameClient::Connected())) && !CheckFlag(EmulationFlags::DebuggerWindowEnabled);
|
||||
}
|
||||
|
||||
static bool InputEnabled()
|
||||
{
|
||||
return !EmulationSettings::CheckFlag(EmulationFlags::InBackground) || EmulationSettings::CheckFlag(EmulationFlags::AllowBackgroundInput);
|
||||
}
|
||||
|
||||
static void SetNesModel(NesModel model)
|
||||
{
|
||||
_model = model;
|
||||
@ -1172,6 +1233,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void SetMouseSensitivity(double sensitivity)
|
||||
{
|
||||
_mouseSensitivity = sensitivity;
|
||||
}
|
||||
|
||||
static double GetMouseSensitivity()
|
||||
{
|
||||
return _mouseSensitivity;
|
||||
}
|
||||
|
||||
static void SetZapperDetectionRadius(uint32_t detectionRadius)
|
||||
{
|
||||
_zapperDetectionRadius = detectionRadius;
|
||||
@ -1182,6 +1253,16 @@ public:
|
||||
return _zapperDetectionRadius;
|
||||
}
|
||||
|
||||
static void SetInputPollScanline(int32_t scanline)
|
||||
{
|
||||
_inputPollScanline = scanline;
|
||||
}
|
||||
|
||||
static int32_t GetInputPollScanline()
|
||||
{
|
||||
return _inputPollScanline;
|
||||
}
|
||||
|
||||
static bool HasZapper()
|
||||
{
|
||||
return _controllerTypes[0] == ControllerType::Zapper || _controllerTypes[1] == ControllerType::Zapper || (_consoleType == ConsoleType::Famicom && _expansionDevice == ExpansionPortDevice::Zapper);
|
||||
@ -1245,4 +1326,14 @@ public:
|
||||
showMessage = _autoSaveNotify;
|
||||
return _autoSaveDelay;
|
||||
}
|
||||
|
||||
static void SetDipSwitches(uint32_t dipSwitches)
|
||||
{
|
||||
_dipSwitches = dipSwitches;
|
||||
}
|
||||
|
||||
static uint32_t GetDipSwitches()
|
||||
{
|
||||
return _dipSwitches;
|
||||
}
|
||||
};
|
||||
|
61
Core/ExcitingBoxingController.h
Normal file
61
Core/ExcitingBoxingController.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class ExcitingBoxingController : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint8_t _selectedSensors = 0;
|
||||
enum Buttons { LeftHook = 0, MoveRight, MoveLeft, RightHook, LeftJab, HitBody, RightJab, Straight };
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_selectedSensors);
|
||||
}
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "HRLhJBjS";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
SetPressedState(i, keyMapping.ExcitingBoxingButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ExcitingBoxingController(KeyMappingSet keyMappings) : BaseControlDevice(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
if(_selectedSensors == 0) {
|
||||
return
|
||||
(IsPressed(ExcitingBoxingController::Buttons::LeftHook) ? 0 : 0x02) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::MoveRight) ? 0 : 0x04) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::MoveLeft) ? 0 : 0x08) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::RightHook) ? 0 : 0x10);
|
||||
} else {
|
||||
return
|
||||
(IsPressed(ExcitingBoxingController::Buttons::LeftJab) ? 0 : 0x02) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::HitBody) ? 0 : 0x04) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::RightJab) ? 0 : 0x08) |
|
||||
(IsPressed(ExcitingBoxingController::Buttons::Straight) ? 0 : 0x10);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_selectedSensors = (value & 0x02) >> 1;
|
||||
}
|
||||
};
|
167
Core/FDS.cpp
167
Core/FDS.cpp
@ -1,16 +1,14 @@
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/IpsPatcher.h"
|
||||
#include "Console.h"
|
||||
#include "FDS.h"
|
||||
#include "CPU.h"
|
||||
#include "FdsAudio.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
FDS* FDS::Instance = nullptr;
|
||||
bool FDS::_disableAutoInsertDisk = false;
|
||||
#include "BatteryManager.h"
|
||||
|
||||
void FDS::InitMapper()
|
||||
{
|
||||
_newDiskNumber = (IsAutoInsertDiskEnabled() || EmulationSettings::CheckFlag(EmulationFlags::FdsAutoLoadDisk)) ? 0 : FDS::NoDiskInserted;
|
||||
|
||||
//FDS BIOS
|
||||
SetCpuMemoryMapping(0xE000, 0xFFFF, 0, PrgMemoryType::PrgRom, MemoryAccessType::Read);
|
||||
|
||||
@ -27,6 +25,38 @@ void FDS::InitMapper(RomData &romData)
|
||||
_fdsDiskSides = romData.FdsDiskData;
|
||||
_fdsDiskHeaders = romData.FdsDiskHeaders;
|
||||
_fdsRawData = romData.RawData;
|
||||
|
||||
//Apply save data (saved as an IPS file), if found
|
||||
vector<uint8_t> ipsData = BatteryManager::LoadBattery(".ips");
|
||||
LoadDiskData(ipsData);
|
||||
}
|
||||
|
||||
void FDS::LoadDiskData(vector<uint8_t> ipsData)
|
||||
{
|
||||
_fdsDiskSides.clear();
|
||||
_fdsDiskHeaders.clear();
|
||||
|
||||
FdsLoader loader;
|
||||
vector<uint8_t> patchedData;
|
||||
if(ipsData.size() > 0 && IpsPatcher::PatchBuffer(ipsData, _fdsRawData, patchedData)) {
|
||||
loader.LoadDiskData(patchedData, _fdsDiskSides, _fdsDiskHeaders);
|
||||
} else {
|
||||
loader.LoadDiskData(_fdsRawData, _fdsDiskSides, _fdsDiskHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> FDS::CreateIpsPatch()
|
||||
{
|
||||
FdsLoader loader;
|
||||
bool needHeader = (memcmp(_fdsRawData.data(), "FDS\x1a", 4) == 0);
|
||||
vector<uint8_t> newData = loader.RebuildFdsFile(_fdsDiskSides, needHeader);
|
||||
return IpsPatcher::CreatePatch(_fdsRawData, newData);
|
||||
}
|
||||
|
||||
void FDS::SaveBattery()
|
||||
{
|
||||
vector<uint8_t> ipsData = CreateIpsPatch();
|
||||
BatteryManager::SaveBattery(".ips", ipsData.data(), (uint32_t)ipsData.size());
|
||||
}
|
||||
|
||||
void FDS::Reset(bool softReset)
|
||||
@ -54,9 +84,6 @@ void FDS::WriteFdsDisk(uint8_t value)
|
||||
{
|
||||
assert(_diskNumber < _fdsDiskSides.size());
|
||||
assert(_diskPosition < _fdsDiskSides[_diskNumber].size());
|
||||
if(_fdsDiskSides[_diskNumber][_diskPosition - 2] != value) {
|
||||
_isDirty = true;
|
||||
}
|
||||
_fdsDiskSides[_diskNumber][_diskPosition - 2] = value;
|
||||
}
|
||||
|
||||
@ -75,11 +102,6 @@ void FDS::ClockIrq()
|
||||
}
|
||||
}
|
||||
|
||||
bool FDS::IsAutoInsertDiskEnabled()
|
||||
{
|
||||
return !_disableAutoInsertDisk && EmulationSettings::CheckFlag(EmulationFlags::FdsAutoInsertDisk);
|
||||
}
|
||||
|
||||
uint8_t FDS::ReadRAM(uint16_t addr)
|
||||
{
|
||||
if(addr == 0xE18C && !_gameStarted && (CPU::DebugReadByte(0x100) & 0xC0) != 0) {
|
||||
@ -124,7 +146,6 @@ uint8_t FDS::ReadRAM(uint16_t addr)
|
||||
|
||||
if(matchIndex >= 0) {
|
||||
//Found a single match, insert it
|
||||
_newDiskNumber = matchIndex;
|
||||
_diskNumber = matchIndex;
|
||||
if(matchIndex > 0) {
|
||||
//Make sure we disable fast forward
|
||||
@ -177,14 +198,6 @@ void FDS::ProcessCpuClock()
|
||||
ClockIrq();
|
||||
_audio->Clock();
|
||||
|
||||
if(_newDiskInsertDelay > 0) {
|
||||
//Insert new disk after delay expires, to allow games to notice the disk was ejected
|
||||
_newDiskInsertDelay--;
|
||||
_diskNumber = FDS::NoDiskInserted;
|
||||
} else {
|
||||
_diskNumber = _newDiskNumber;
|
||||
}
|
||||
|
||||
if(_diskNumber == FDS::NoDiskInserted || !_motorOn) {
|
||||
//Disk has been ejected
|
||||
_endOfHead = true;
|
||||
@ -366,11 +379,6 @@ void FDS::WriteRegister(uint16_t addr, uint8_t value)
|
||||
}
|
||||
}
|
||||
|
||||
bool FDS::IsDiskInserted()
|
||||
{
|
||||
return _diskNumber != FDS::NoDiskInserted;
|
||||
}
|
||||
|
||||
uint8_t FDS::ReadRegister(uint16_t addr)
|
||||
{
|
||||
uint8_t value = MemoryManager::GetOpenBus();
|
||||
@ -411,7 +419,6 @@ uint8_t FDS::ReadRegister(uint16_t addr)
|
||||
//Eject the current disk and insert a new one in 300k cycles (~10 frames)
|
||||
_autoDiskSwitchCounter = 300000;
|
||||
_diskNumber = NoDiskInserted;
|
||||
_newDiskNumber = NoDiskInserted;
|
||||
}
|
||||
return value;
|
||||
|
||||
@ -429,14 +436,35 @@ void FDS::StreamState(bool saving)
|
||||
BaseMapper::StreamState(saving);
|
||||
|
||||
bool unusedNeedIrq = false;
|
||||
uint32_t unusedNewDiskNumber = 0;
|
||||
uint32_t unusedNewDiskInsertDelay = 0;
|
||||
bool unusedIsDirty = false;
|
||||
|
||||
SnapshotInfo audio{ _audio.get() };
|
||||
|
||||
Stream(_irqReloadValue, _irqCounter, _irqEnabled, _irqRepeatEnabled, _diskRegEnabled, _soundRegEnabled, _writeDataReg, _motorOn, _resetTransfer,
|
||||
_readMode, _crcControl, _diskReady, _diskIrqEnabled, _extConWriteReg, _badCrc, _endOfHead, _readWriteEnabled, _readDataReg, _diskWriteProtected,
|
||||
_diskNumber, _newDiskNumber, _newDiskInsertDelay, _diskPosition, _delay, _previousCrcControlFlag, _gapEnded, _scanningDisk, unusedNeedIrq,
|
||||
_transferComplete, _isDirty, audio);
|
||||
_diskNumber, unusedNewDiskNumber, unusedNewDiskInsertDelay, _diskPosition, _delay, _previousCrcControlFlag, _gapEnded, _scanningDisk, unusedNeedIrq,
|
||||
_transferComplete, unusedIsDirty, audio);
|
||||
|
||||
if(saving) {
|
||||
vector<uint8_t> ipsData = CreateIpsPatch();
|
||||
uint32_t size = (uint32_t)ipsData.size();
|
||||
Stream(size);
|
||||
|
||||
ArrayInfo<uint8_t> data{ ipsData.data(), (uint32_t)ipsData.size() };
|
||||
Stream(data);
|
||||
} else {
|
||||
uint32_t size = 0;
|
||||
Stream(size);
|
||||
if(size > 0) {
|
||||
vector<uint8_t> ipsData(size, 0);
|
||||
ArrayInfo<uint8_t> data{ ipsData.data(), (uint32_t)ipsData.size() };
|
||||
Stream(data);
|
||||
|
||||
LoadDiskData(ipsData);
|
||||
}
|
||||
|
||||
if(!saving) {
|
||||
//Make sure we disable fast forwarding when loading a state
|
||||
//Otherwise it's possible to get stuck in fast forward mode
|
||||
_gameStarted = true;
|
||||
@ -445,8 +473,6 @@ void FDS::StreamState(bool saving)
|
||||
|
||||
FDS::FDS()
|
||||
{
|
||||
FDS::Instance = this;
|
||||
|
||||
_audio.reset(new FdsAudio());
|
||||
}
|
||||
|
||||
@ -454,68 +480,41 @@ FDS::~FDS()
|
||||
{
|
||||
//Restore emulation speed to normal when closing
|
||||
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
|
||||
|
||||
if(FDS::Instance == this) {
|
||||
FDS::Instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FDS::SaveBattery()
|
||||
ConsoleFeatures FDS::GetAvailableFeatures()
|
||||
{
|
||||
if(_isDirty) {
|
||||
FdsLoader loader;
|
||||
loader.SaveIpsFile(_romFilepath, _fdsRawData, _fdsDiskSides);
|
||||
}
|
||||
return ConsoleFeatures::Fds;
|
||||
}
|
||||
|
||||
uint32_t FDS::GetSideCount()
|
||||
{
|
||||
if(FDS::Instance) {
|
||||
return (uint32_t)FDS::Instance->_fdsDiskSides.size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FDS::InsertDisk(uint32_t diskNumber)
|
||||
{
|
||||
if(FDS::Instance) {
|
||||
Console::Pause();
|
||||
FDS::Instance->_newDiskNumber = diskNumber;
|
||||
FDS::Instance->_newDiskInsertDelay = FDS::DiskInsertDelay;
|
||||
Console::Resume();
|
||||
|
||||
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void FDS::InsertNextDisk()
|
||||
{
|
||||
InsertDisk(((FDS::Instance->_diskNumber & 0xFE) + 2) % GetSideCount());
|
||||
}
|
||||
|
||||
void FDS::SwitchDiskSide()
|
||||
{
|
||||
if(FDS::Instance) {
|
||||
Console::Pause();
|
||||
if(FDS::Instance->_newDiskInsertDelay == 0 && FDS::Instance->_diskNumber != NoDiskInserted) {
|
||||
FDS::Instance->_newDiskNumber = (FDS::Instance->_diskNumber & 0x01) ? (FDS::Instance->_diskNumber & 0xFE) : (FDS::Instance->_diskNumber | 0x01);
|
||||
FDS::Instance->_newDiskInsertDelay = FDS::DiskInsertDelay;
|
||||
}
|
||||
Console::Resume();
|
||||
|
||||
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
|
||||
}
|
||||
return (uint32_t)_fdsDiskSides.size();
|
||||
}
|
||||
|
||||
void FDS::EjectDisk()
|
||||
{
|
||||
if(FDS::Instance) {
|
||||
Console::Pause();
|
||||
FDS::Instance->_newDiskNumber = NoDiskInserted;
|
||||
FDS::Instance->_newDiskInsertDelay = 0;
|
||||
Console::Resume();
|
||||
_diskNumber = FDS::NoDiskInserted;
|
||||
}
|
||||
|
||||
MessageManager::SendNotification(ConsoleNotificationType::FdsDiskChanged);
|
||||
void FDS::InsertDisk(uint32_t diskNumber)
|
||||
{
|
||||
if(_diskNumber == FDS::NoDiskInserted) {
|
||||
_diskNumber = diskNumber;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FDS::GetCurrentDisk()
|
||||
{
|
||||
return _diskNumber;
|
||||
}
|
||||
|
||||
bool FDS::IsDiskInserted()
|
||||
{
|
||||
return _diskNumber != FDS::NoDiskInserted;
|
||||
}
|
||||
|
||||
bool FDS::IsAutoInsertDiskEnabled()
|
||||
{
|
||||
return !_disableAutoInsertDisk && EmulationSettings::CheckFlag(EmulationFlags::FdsAutoInsertDisk);
|
||||
}
|
28
Core/FDS.h
28
Core/FDS.h
@ -13,10 +13,7 @@ class FDS : public BaseMapper
|
||||
{
|
||||
private:
|
||||
static const uint32_t NoDiskInserted = 0xFF;
|
||||
static const uint32_t DiskInsertDelay = 3600000; //approx 2 sec delay
|
||||
static bool _disableAutoInsertDisk;
|
||||
|
||||
static FDS* Instance;
|
||||
bool _disableAutoInsertDisk;
|
||||
|
||||
unique_ptr<FdsAudio> _audio;
|
||||
|
||||
@ -53,9 +50,7 @@ private:
|
||||
bool _diskWriteProtected = false;
|
||||
|
||||
//Internal values
|
||||
uint32_t _diskNumber = 0;
|
||||
uint32_t _newDiskNumber = 0;
|
||||
uint32_t _newDiskInsertDelay = 0;
|
||||
uint32_t _diskNumber = FDS::NoDiskInserted;
|
||||
uint32_t _diskPosition = 0;
|
||||
uint32_t _delay = 0;
|
||||
uint16_t _crcAccumulator;
|
||||
@ -63,7 +58,6 @@ private:
|
||||
bool _gapEnded = true;
|
||||
bool _scanningDisk = false;
|
||||
bool _transferComplete = false;
|
||||
bool _isDirty = false;
|
||||
|
||||
vector<uint8_t> _fdsRawData;
|
||||
vector<vector<uint8_t>> _fdsDiskSides;
|
||||
@ -83,6 +77,8 @@ protected:
|
||||
|
||||
void InitMapper() override;
|
||||
void InitMapper(RomData &romData) override;
|
||||
void LoadDiskData(vector<uint8_t> ipsData = vector<uint8_t>());
|
||||
vector<uint8_t> CreateIpsPatch();
|
||||
void Reset(bool softReset) override;
|
||||
|
||||
uint32_t GetFdsDiskSideSize(uint8_t side);
|
||||
@ -94,8 +90,6 @@ protected:
|
||||
void ProcessCpuClock() override;
|
||||
void UpdateCrc(uint8_t value);
|
||||
|
||||
bool IsDiskInserted();
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override;
|
||||
uint8_t ReadRegister(uint16_t addr) override;
|
||||
|
||||
@ -108,12 +102,14 @@ public:
|
||||
~FDS();
|
||||
|
||||
void SaveBattery() override;
|
||||
ConsoleFeatures GetAvailableFeatures() override;
|
||||
|
||||
static uint32_t GetSideCount();
|
||||
uint32_t GetSideCount();
|
||||
|
||||
static void InsertDisk(uint32_t diskNumber);
|
||||
static void InsertNextDisk();
|
||||
static void SwitchDiskSide();
|
||||
static void EjectDisk();
|
||||
static bool IsAutoInsertDiskEnabled();
|
||||
void EjectDisk();
|
||||
void InsertDisk(uint32_t diskNumber);
|
||||
uint32_t GetCurrentDisk();
|
||||
bool IsDiskInserted();
|
||||
|
||||
bool IsAutoInsertDiskEnabled();
|
||||
};
|
74
Core/FamilyBasicDataRecorder.h
Normal file
74
Core/FamilyBasicDataRecorder.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <deque>
|
||||
#include "BaseControlDevice.h"
|
||||
#include "CPU.h"
|
||||
|
||||
//TODO: Integration with UI
|
||||
class FamilyBasicDataRecorder : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
const uint32_t SamplingRate = 88;
|
||||
vector<uint8_t> _saveData;
|
||||
bool _enabled = false;
|
||||
int32_t _lastCycle = -1;
|
||||
int32_t _readStartCycle = -1;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_enabled);
|
||||
}
|
||||
|
||||
public:
|
||||
FamilyBasicDataRecorder() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4016) {
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::ShowFrameCounter)) {
|
||||
if(_readStartCycle == -1) {
|
||||
_readStartCycle = CPU::GetCycleCount();
|
||||
}
|
||||
|
||||
int readPos = (CPU::GetCycleCount() - _readStartCycle) / 88;
|
||||
|
||||
if((int32_t)_saveData.size() > readPos) {
|
||||
uint8_t value = (_saveData[readPos] & 0x01) << 1;
|
||||
return _enabled ? value : 0;
|
||||
}
|
||||
} else {
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::ShowFPS)) {
|
||||
_lastCycle = -1;
|
||||
_readStartCycle = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_enabled = (value & 0x04) != 0;
|
||||
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::ShowFPS)) {
|
||||
if(_lastCycle == -1) {
|
||||
_saveData.clear();
|
||||
_lastCycle = CPU::GetCycleCount() - 88;
|
||||
}
|
||||
while(_lastCycle < CPU::GetCycleCount()) {
|
||||
_saveData.push_back(value & 0x01);
|
||||
_lastCycle += 88;
|
||||
}
|
||||
} else {
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::ShowFrameCounter)) {
|
||||
_lastCycle = -1;
|
||||
_readStartCycle = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
117
Core/FamilyBasicKeyboard.h
Normal file
117
Core/FamilyBasicKeyboard.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class FamilyBasicKeyboard : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint8_t _row = 0;
|
||||
uint8_t _column = 0;
|
||||
bool _enabled = false;
|
||||
|
||||
const uint32_t _keyMatrix[72] = {
|
||||
F8, Return, RightBracket, LeftBracket,
|
||||
Kana, RightShift, Yen, Stop,
|
||||
F7, AtSign, Colon, SemiColon,
|
||||
Underscore, Slash, Minus, Caret,
|
||||
F6, O, L, K,
|
||||
Dot, Comma, P, Num0,
|
||||
F5, I, U, J,
|
||||
M, N, Num9, Num8,
|
||||
F4, Y, G, H,
|
||||
B, V, Num7, Num6,
|
||||
F3, T, R, D,
|
||||
F, C, Num5, Num4,
|
||||
F2, W, S, A,
|
||||
X, Z, E, Num3,
|
||||
F1, Esc, Q, Ctrl,
|
||||
LeftShift, Grph, Num1, Num2,
|
||||
ClrHome, Up, Right, Left,
|
||||
Down, Space, Del, Ins
|
||||
};
|
||||
|
||||
enum Buttons
|
||||
{
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
|
||||
Return, Space, Del, Ins, Esc,
|
||||
Ctrl, RightShift, LeftShift,
|
||||
RightBracket, LeftBracket,
|
||||
Up, Down, Left, Right,
|
||||
Dot, Comma, Colon, SemiColon, Underscore, Slash, Minus, Caret,
|
||||
F1, F2, F3, F4, F5, F6, F7, F8,
|
||||
Yen, Stop, AtSign, Grph, ClrHome, Kana
|
||||
};
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456798rsdiecSs[]udlrd,:;_/-^12345678ysagck";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
for(int i = 0; i < 72; i++) {
|
||||
SetPressedState(i, keyMapping.FamilyBasicKeyboardButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetActiveKeys(uint8_t row, uint8_t column)
|
||||
{
|
||||
if(row == 9) {
|
||||
//10th row has no keys
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t result = 0;
|
||||
uint32_t baseIndex = row * 8 + (column ? 4 : 0);
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(IsPressed(_keyMatrix[baseIndex + i])) {
|
||||
result |= 0x10;
|
||||
}
|
||||
result >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_row, _column, _enabled);
|
||||
}
|
||||
|
||||
public:
|
||||
FamilyBasicKeyboard(KeyMappingSet keyMappings) : BaseControlDevice(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
if(_enabled) {
|
||||
return ((~GetActiveKeys(_row, _column)) << 1) & 0x1E;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
uint8_t prevColumn = _column;
|
||||
_column = (value & 0x02) >> 1;
|
||||
if(!_column && prevColumn) {
|
||||
//"Incrementing the row from the (keyless) 10th row will cause it to wrap back to the first row."
|
||||
_row = (_row + 1) % 10;
|
||||
}
|
||||
|
||||
if(value & 0x01) {
|
||||
_row = 0;
|
||||
}
|
||||
|
||||
_enabled = (value & 0x04) != 0;
|
||||
}
|
||||
};
|
45
Core/FamilyMatTrainer.h
Normal file
45
Core/FamilyMatTrainer.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "PowerPad.h"
|
||||
|
||||
class FamilyMatTrainer : public PowerPad
|
||||
{
|
||||
private:
|
||||
uint8_t _ignoreRows = 0;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
PowerPad::StreamState(saving);
|
||||
Stream(_ignoreRows);
|
||||
}
|
||||
|
||||
public:
|
||||
FamilyMatTrainer(KeyMappingSet keyMappings) : PowerPad(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4017) {
|
||||
uint8_t pressedKeys[4] = {};
|
||||
for(int j = 0; j < 3; j++) {
|
||||
if((_ignoreRows >> (2 - j)) & 0x01) {
|
||||
//Ignore this row
|
||||
continue;
|
||||
}
|
||||
for(int i = 0; i < 4; i++) {
|
||||
pressedKeys[i] |= IsPressed(j * 4 + i) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
output = ~((pressedKeys[0] << 4) | (pressedKeys[1] << 3) | (pressedKeys[2] << 2) | (pressedKeys[3] << 1)) & 0x1E;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_ignoreRows = value & 0x07;
|
||||
}
|
||||
};
|
@ -1,6 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include <algorithm>
|
||||
#include "../Utilities/StringUtilities.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "ControlManager.h"
|
||||
#include "FceuxMovie.h"
|
||||
#include "Console.h"
|
||||
|
||||
@ -26,10 +28,13 @@ vector<uint8_t> FceuxMovie::Base64Decode(string in)
|
||||
|
||||
bool FceuxMovie::InitializeData(stringstream &filestream)
|
||||
{
|
||||
const uint8_t orValues[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
|
||||
uint32_t systemActionCount = 0;
|
||||
|
||||
bool result = false;
|
||||
|
||||
_dataByFrame[0].push_back("");
|
||||
_dataByFrame[1].push_back("");
|
||||
_dataByFrame[2].push_back("");
|
||||
_dataByFrame[3].push_back("");
|
||||
|
||||
while(!filestream.eof()) {
|
||||
string line;
|
||||
std::getline(filestream, line);
|
||||
@ -43,31 +48,27 @@ bool FceuxMovie::InitializeData(stringstream &filestream)
|
||||
return false;
|
||||
}
|
||||
} else if(line.size() > 0 && line[0] == '|') {
|
||||
line.erase(std::remove(line.begin(), line.end(), '|'), line.end());
|
||||
line = line.substr(1, line.size() - 2);
|
||||
vector<string> lineData = StringUtilities::Split(line.substr(1), '|');
|
||||
|
||||
if(lineData.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Read power/reset/FDS/VS/etc. commands
|
||||
/*uint32_t systemAction = 0;
|
||||
for(int i = 0; i < systemActionCount; i++) {
|
||||
if(line[i] != '.') {
|
||||
systemAction |= (1 << i);
|
||||
}
|
||||
uint32_t systemAction = 0;
|
||||
try {
|
||||
systemAction = (uint32_t)std::atol(lineData[0].c_str());
|
||||
} catch(std::exception ex) {
|
||||
}
|
||||
_systemActionByFrame.push_back(systemAction);*/
|
||||
_systemActionByFrame.push_back(systemAction);
|
||||
|
||||
//Only supports regular controllers (up to 4 of them)
|
||||
for(int i = 0; i < 8 * 4; i++) {
|
||||
uint8_t port = i / 8;
|
||||
|
||||
if(port <= 3) {
|
||||
uint8_t portValue = 0;
|
||||
for(int j = 0; j < 8 && i + j + systemActionCount < line.size(); j++) {
|
||||
if(line[i + j + systemActionCount] != '.') {
|
||||
portValue |= orValues[j];
|
||||
}
|
||||
}
|
||||
i += 7;
|
||||
_dataByFrame[port].push_back(portValue);
|
||||
for(size_t i = 1; i < lineData.size() && i < 5; i++) {
|
||||
if(lineData[i].size() >= 8) {
|
||||
string data = lineData[i].substr(3, 1) + lineData[i].substr(2, 1) + lineData[i].substr(1, 1) + lineData[i].substr(0, 1);
|
||||
_dataByFrame[i - 1].push_back(data + lineData[i].substr(4, 4));
|
||||
} else {
|
||||
_dataByFrame[i - 1].push_back("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,14 +76,19 @@ bool FceuxMovie::InitializeData(stringstream &filestream)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FceuxMovie::Play(stringstream & filestream, bool autoLoadRom)
|
||||
bool FceuxMovie::Play(VirtualFile &file)
|
||||
{
|
||||
Console::Pause();
|
||||
if(InitializeData(filestream)) {
|
||||
|
||||
std::stringstream ss;
|
||||
file.ReadFile(ss);
|
||||
if(InitializeData(ss)) {
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllZeros);
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
Console::Reset(false);
|
||||
_isPlaying = true;
|
||||
}
|
||||
|
||||
Console::Resume();
|
||||
return _isPlaying;
|
||||
}
|
@ -11,5 +11,5 @@ private:
|
||||
bool InitializeData(stringstream &filestream);
|
||||
|
||||
public:
|
||||
bool Play(stringstream &filestream, bool autoLoadRom) override;
|
||||
bool Play(VirtualFile &file) override;
|
||||
};
|
@ -2,7 +2,6 @@
|
||||
#include "stdafx.h"
|
||||
#include <algorithm>
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Utilities/IpsPatcher.h"
|
||||
#include "../Utilities/CRC32.h"
|
||||
#include "../Utilities/sha1.h"
|
||||
#include "RomData.h"
|
||||
@ -50,6 +49,21 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> LoadBios()
|
||||
{
|
||||
//For FDS, the PRG ROM is the FDS BIOS (8k)
|
||||
vector<uint8_t> biosData;
|
||||
|
||||
ifstream biosFile("FdsBios.bin", ios::in | ios::binary);
|
||||
if(biosFile) {
|
||||
return vector<uint8_t>(std::istreambuf_iterator<char>(biosFile), {});
|
||||
} else {
|
||||
MessageManager::SendNotification(ConsoleNotificationType::FdsBiosNotFound);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
public:
|
||||
vector<uint8_t> RebuildFdsFile(vector<vector<uint8_t>> diskData, bool needHeader)
|
||||
{
|
||||
vector<uint8_t> output;
|
||||
@ -93,7 +107,7 @@ private:
|
||||
return output;
|
||||
}
|
||||
|
||||
void LoadDiskData(vector<uint8_t>& romFile, RomData &romData)
|
||||
void LoadDiskData(vector<uint8_t>& romFile, vector<vector<uint8_t>> &diskData, vector<vector<uint8_t>> &diskHeaders)
|
||||
{
|
||||
uint8_t numberOfSides = 0;
|
||||
size_t fileOffset = 0;
|
||||
@ -106,12 +120,12 @@ private:
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < numberOfSides; i++) {
|
||||
romData.FdsDiskData.push_back(vector<uint8_t>());
|
||||
vector<uint8_t> &fdsDiskImage = romData.FdsDiskData.back();
|
||||
diskData.push_back(vector<uint8_t>());
|
||||
vector<uint8_t> &fdsDiskImage = diskData.back();
|
||||
|
||||
romData.FdsDiskHeaders.push_back(vector<uint8_t>(romFile.data() + fileOffset + 1, romFile.data() + fileOffset + 57));
|
||||
diskHeaders.push_back(vector<uint8_t>(romFile.data() + fileOffset + 1, romFile.data() + fileOffset + 57));
|
||||
|
||||
AddGaps(fdsDiskImage, &romFile[fileOffset]);
|
||||
AddGaps(fdsDiskImage, &romFile[fileOffset]);
|
||||
fileOffset += FdsDiskSideCapacity;
|
||||
|
||||
//Ensure the image is 65500 bytes
|
||||
@ -121,48 +135,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
vector<uint8_t> LoadBios()
|
||||
RomData LoadRom(vector<uint8_t> &romFile, string filename)
|
||||
{
|
||||
//For FDS, the PRG ROM is the FDS BIOS (8k)
|
||||
vector<uint8_t> biosData;
|
||||
|
||||
ifstream biosFile("FdsBios.bin", ios::in | ios::binary);
|
||||
if(biosFile) {
|
||||
return vector<uint8_t>(std::istreambuf_iterator<char>(biosFile), {});
|
||||
} else {
|
||||
MessageManager::SendNotification(ConsoleNotificationType::FdsBiosNotFound);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
public:
|
||||
void SaveIpsFile(string filename, vector<uint8_t> &originalDiskData, vector<vector<uint8_t>> ¤tDiskData)
|
||||
{
|
||||
bool needHeader = (memcmp(originalDiskData.data(), "FDS\x1a", 4) == 0);
|
||||
vector<uint8_t> newData = RebuildFdsFile(currentDiskData, needHeader);
|
||||
vector<uint8_t> ipsData = IpsPatcher::CreatePatch(originalDiskData, newData);
|
||||
|
||||
string fdsSaveFilepath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(filename, false) + ".ips");
|
||||
ofstream outputIps(fdsSaveFilepath, ios::binary);
|
||||
if(outputIps) {
|
||||
outputIps.write((char*)ipsData.data(), ipsData.size());
|
||||
outputIps.close();
|
||||
}
|
||||
}
|
||||
|
||||
RomData LoadRom(vector<uint8_t> romFile, string filename)
|
||||
{
|
||||
//Note: "romFile" is intentionally passed by copy - modifying the original array will alter the "RawData" property which
|
||||
//is used when saving the IPS file for FDS save data. If the RawData is modified by this function, then the IPS file will
|
||||
//will only contain new changes, and all previous save data will be lost/corrupted.
|
||||
|
||||
//Apply save data (saved as an IPS file), if found
|
||||
string fdsSaveFilepath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(filename, false) + ".ips");
|
||||
vector<uint8_t> patchedData;
|
||||
if(IpsPatcher::PatchBuffer(fdsSaveFilepath, romFile, patchedData)) {
|
||||
romFile = patchedData;
|
||||
}
|
||||
|
||||
RomData romData;
|
||||
|
||||
romData.Sha1 = SHA1::GetHash(romFile);
|
||||
@ -177,8 +151,6 @@ public:
|
||||
|
||||
if(romData.PrgRom.size() != 0x2000) {
|
||||
romData.Error = true;
|
||||
} else {
|
||||
LoadDiskData(romFile, romData);
|
||||
}
|
||||
|
||||
//Setup default controllers
|
||||
|
134
Core/FdsSystemActionManager.h
Normal file
134
Core/FdsSystemActionManager.h
Normal file
@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "SystemActionManager.h"
|
||||
#include "FDS.h"
|
||||
|
||||
class FdsSystemActionManager : public SystemActionManager
|
||||
{
|
||||
private:
|
||||
const uint8_t ReinsertDiskFrameDelay = 120;
|
||||
|
||||
std::weak_ptr<FDS> _mapper;
|
||||
|
||||
bool _needEjectDisk = false;
|
||||
uint8_t _insertDiskNumber = 0;
|
||||
uint8_t _insertDiskDelay = 0;
|
||||
uint32_t _sideCount;
|
||||
|
||||
protected:
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return string("RPE0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").substr(0, _sideCount + 3);
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
SystemActionManager::StreamState(saving);
|
||||
Stream(_needEjectDisk, _insertDiskNumber, _insertDiskDelay);
|
||||
}
|
||||
|
||||
public:
|
||||
enum FdsButtons { EjectDiskButton = 2, InsertDisk1 };
|
||||
|
||||
FdsSystemActionManager(shared_ptr<Console> console, shared_ptr<BaseMapper> mapper) : SystemActionManager(console)
|
||||
{
|
||||
_mapper = std::dynamic_pointer_cast<FDS>(mapper);
|
||||
_sideCount = std::dynamic_pointer_cast<FDS>(mapper)->GetSideCount();
|
||||
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::FdsAutoLoadDisk)) {
|
||||
InsertDisk(0);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterSetState() override
|
||||
{
|
||||
SystemActionManager::OnAfterSetState();
|
||||
if(_needEjectDisk) {
|
||||
SetBit(FdsSystemActionManager::FdsButtons::EjectDiskButton);
|
||||
_needEjectDisk = false;
|
||||
}
|
||||
if(_insertDiskDelay > 0) {
|
||||
_insertDiskDelay--;
|
||||
if(_insertDiskDelay == 0) {
|
||||
SetBit(FdsSystemActionManager::FdsButtons::InsertDisk1 + _insertDiskNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessSystemActions() override
|
||||
{
|
||||
SystemActionManager::ProcessSystemActions();
|
||||
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
if(IsPressed(FdsSystemActionManager::FdsButtons::EjectDiskButton)) {
|
||||
mapper->EjectDisk();
|
||||
}
|
||||
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if(IsPressed(FdsSystemActionManager::FdsButtons::InsertDisk1 + i)) {
|
||||
mapper->InsertDisk(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EjectDisk()
|
||||
{
|
||||
_needEjectDisk = true;
|
||||
}
|
||||
|
||||
void InsertDisk(uint8_t diskNumber)
|
||||
{
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
if(mapper->IsDiskInserted()) {
|
||||
//Eject disk on next frame, then insert new disk 2 seconds later
|
||||
_needEjectDisk = true;
|
||||
_insertDiskNumber = diskNumber;
|
||||
_insertDiskDelay = FdsSystemActionManager::ReinsertDiskFrameDelay;
|
||||
} else {
|
||||
//Insert disk on next frame
|
||||
_insertDiskNumber = diskNumber;
|
||||
_insertDiskDelay = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchDiskSide()
|
||||
{
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
InsertDisk(mapper->GetCurrentDisk() ^ 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
void InsertNextDisk()
|
||||
{
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
InsertDisk(((mapper->GetCurrentDisk() & 0xFE) + 2) % mapper->GetSideCount());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetSideCount()
|
||||
{
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
return mapper->GetSideCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsAutoInsertDiskEnabled()
|
||||
{
|
||||
shared_ptr<FDS> mapper = _mapper.lock();
|
||||
if(mapper) {
|
||||
return mapper->IsAutoInsertDiskEnabled();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
48
Core/FourScore.h
Normal file
48
Core/FourScore.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class FourScore : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint32_t _signature4016 = 0;
|
||||
uint32_t _signature4017 = 0;
|
||||
|
||||
protected:
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_signature4016, _signature4017);
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
//Signature for port 0 = 0x10, reversed bit order => 0x08
|
||||
//Signature for port 1 = 0x20, reversed bit order => 0x04
|
||||
_signature4016 = (0x08 << 16);
|
||||
_signature4017 = (0x04 << 16);
|
||||
}
|
||||
|
||||
public:
|
||||
FourScore() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4016) {
|
||||
output = _signature4016 & 0x01;
|
||||
_signature4016 >>= 1;
|
||||
} else if(addr == 0x4017) {
|
||||
output = _signature4017 & 0x01;
|
||||
_signature4017 >>= 1;
|
||||
}
|
||||
StrobeProcessRead();
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
@ -93,12 +93,6 @@ void GameClient::ProcessNotification(ConsoleNotificationType type, void* paramet
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GameClient::GetControllerState(uint8_t port)
|
||||
{
|
||||
shared_ptr<GameClientConnection> connection = GetConnection();
|
||||
return connection ? connection->GetControllerState(port) : 0;
|
||||
}
|
||||
|
||||
void GameClient::SelectController(uint8_t port)
|
||||
{
|
||||
shared_ptr<GameClientConnection> connection = GetConnection();
|
||||
|
@ -34,7 +34,5 @@ public:
|
||||
static uint8_t GetControllerPort();
|
||||
static uint8_t GetAvailableControllers();
|
||||
|
||||
static uint8_t GetControllerState(uint8_t port);
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
};
|
@ -10,6 +10,9 @@
|
||||
#include "ControlManager.h"
|
||||
#include "ClientConnectionData.h"
|
||||
#include "StandardController.h"
|
||||
#include "Zapper.h"
|
||||
#include "ArkanoidController.h"
|
||||
#include "BandaiHyperShot.h"
|
||||
#include "SelectControllerMessage.h"
|
||||
#include "PlayerListMessage.h"
|
||||
#include "ForceDisconnectMessage.h"
|
||||
@ -22,6 +25,7 @@ GameClientConnection::GameClientConnection(shared_ptr<Socket> socket, shared_ptr
|
||||
|
||||
MessageManager::RegisterNotificationListener(this);
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectedToServer");
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
SendHandshake();
|
||||
}
|
||||
|
||||
@ -36,10 +40,11 @@ void GameClientConnection::Shutdown()
|
||||
_shutdown = true;
|
||||
DisableControllers();
|
||||
|
||||
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
MessageManager::UnregisterNotificationListener(this);
|
||||
MessageManager::SendNotification(ConsoleNotificationType::DisconnectedFromServer);
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectionLost");
|
||||
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +63,7 @@ void GameClientConnection::SendControllerSelection(uint8_t port)
|
||||
void GameClientConnection::ClearInputData()
|
||||
{
|
||||
LockHandler lock = _writeLock.AcquireSafe();
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
_inputSize[i] = 0;
|
||||
_inputData[i].clear();
|
||||
}
|
||||
@ -76,14 +81,7 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
|
||||
ClearInputData();
|
||||
((SaveStateMessage*)message)->LoadState();
|
||||
_enableControllers = true;
|
||||
switch(EmulationSettings::GetControllerType(_controllerPort)) {
|
||||
case ControllerType::StandardController: _controlDevice.reset(new StandardController(0)); break;
|
||||
|
||||
case ControllerType::Zapper:
|
||||
case ControllerType::ArkanoidController:
|
||||
_controlDevice = ControlManager::GetControlDevice(_controllerPort);
|
||||
break;
|
||||
}
|
||||
InitControlDevice();
|
||||
Console::Resume();
|
||||
}
|
||||
break;
|
||||
@ -131,7 +129,7 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
|
||||
}
|
||||
}
|
||||
|
||||
void GameClientConnection::PushControllerState(uint8_t port, uint8_t state)
|
||||
void GameClientConnection::PushControllerState(uint8_t port, ControlDeviceState state)
|
||||
{
|
||||
LockHandler lock = _writeLock.AcquireSafe();
|
||||
_inputData[port].push_back(state);
|
||||
@ -147,14 +145,17 @@ void GameClientConnection::DisableControllers()
|
||||
//Used to prevent deadlocks when client is trying to fill its buffer while the host changes the current game/settings/etc. (i.e situations where we need to call Console::Pause())
|
||||
ClearInputData();
|
||||
_enableControllers = false;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
_waitForInput[i].Signal();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GameClientConnection::GetControllerState(uint8_t port)
|
||||
bool GameClientConnection::SetInput(BaseControlDevice *device)
|
||||
{
|
||||
device->SetRawState(ControlDeviceState());
|
||||
|
||||
if(_enableControllers) {
|
||||
uint8_t port = device->GetPort();
|
||||
while(_inputSize[port] == 0) {
|
||||
_waitForInput[port].Wait();
|
||||
|
||||
@ -164,12 +165,12 @@ uint8_t GameClientConnection::GetControllerState(uint8_t port)
|
||||
}
|
||||
|
||||
if(_shutdown || !_enableControllers) {
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LockHandler lock = _writeLock.AcquireSafe();
|
||||
uint8_t state = _inputData[port].front();
|
||||
ControlDeviceState state = _inputData[port].front();
|
||||
_inputData[port].pop_front();
|
||||
_inputSize[port]--;
|
||||
|
||||
@ -180,18 +181,27 @@ uint8_t GameClientConnection::GetControllerState(uint8_t port)
|
||||
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
|
||||
EmulationSettings::SetEmulationSpeed(100);
|
||||
}
|
||||
return state;
|
||||
|
||||
device->SetRawState(state);
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GameClientConnection::InitControlDevice()
|
||||
{
|
||||
if(_controllerPort == BaseControlDevice::ExpDevicePort) {
|
||||
_newControlDevice = ControlManager::CreateExpansionDevice(EmulationSettings::GetExpansionDevice());
|
||||
} else {
|
||||
//Pretend we are using port 0 (to use player 1's keybindings during netplay)
|
||||
_newControlDevice = ControlManager::CreateControllerDevice(EmulationSettings::GetControllerType(_controllerPort), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GameClientConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
||||
{
|
||||
if(type == ConsoleNotificationType::ConfigChanged) {
|
||||
switch(EmulationSettings::GetControllerType(_controllerPort)) {
|
||||
case ControllerType::StandardController: _newControlDevice.reset(new StandardController(0)); break;
|
||||
case ControllerType::Zapper: _newControlDevice = ControlManager::GetControlDevice(_controllerPort); break;
|
||||
}
|
||||
InitControlDevice();
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,9 +213,10 @@ void GameClientConnection::SendInput()
|
||||
_newControlDevice.reset();
|
||||
}
|
||||
|
||||
uint32_t inputState = 0;
|
||||
ControlDeviceState inputState;
|
||||
if(_controlDevice) {
|
||||
inputState = _controlDevice->GetNetPlayState();
|
||||
_controlDevice->SetStateFromInput();
|
||||
inputState = _controlDevice->GetRawState();
|
||||
}
|
||||
|
||||
if(_lastInputSent != inputState) {
|
||||
@ -223,9 +234,9 @@ void GameClientConnection::SelectController(uint8_t port)
|
||||
|
||||
uint8_t GameClientConnection::GetAvailableControllers()
|
||||
{
|
||||
uint8_t availablePorts = 0x0F;
|
||||
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
|
||||
for(PlayerInfo &playerInfo : _playerList) {
|
||||
if(playerInfo.ControllerPort < 4) {
|
||||
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
|
||||
availablePorts &= ~(1 << playerInfo.ControllerPort);
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,18 @@
|
||||
#include "GameConnection.h"
|
||||
#include "../Utilities/AutoResetEvent.h"
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
#include "StandardController.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "IInputProvider.h"
|
||||
#include "ControlDeviceState.h"
|
||||
|
||||
class ClientConnectionData;
|
||||
|
||||
class GameClientConnection : public GameConnection, public INotificationListener
|
||||
class GameClientConnection : public GameConnection, public INotificationListener, public IInputProvider
|
||||
{
|
||||
private:
|
||||
std::deque<uint8_t> _inputData[4];
|
||||
atomic<uint32_t> _inputSize[4];
|
||||
AutoResetEvent _waitForInput[4];
|
||||
std::deque<ControlDeviceState> _inputData[BaseControlDevice::PortCount];
|
||||
atomic<uint32_t> _inputSize[BaseControlDevice::PortCount];
|
||||
AutoResetEvent _waitForInput[BaseControlDevice::PortCount];
|
||||
SimpleLock _writeLock;
|
||||
atomic<bool> _shutdown;
|
||||
atomic<bool> _enableControllers;
|
||||
@ -23,7 +25,7 @@ private:
|
||||
|
||||
shared_ptr<BaseControlDevice> _controlDevice;
|
||||
shared_ptr<BaseControlDevice> _newControlDevice;
|
||||
uint32_t _lastInputSent = 0x00;
|
||||
ControlDeviceState _lastInputSent;
|
||||
bool _gameLoaded = false;
|
||||
uint8_t _controllerPort = GameConnection::SpectatorPort;
|
||||
|
||||
@ -31,7 +33,7 @@ private:
|
||||
void SendHandshake();
|
||||
void SendControllerSelection(uint8_t port);
|
||||
void ClearInputData();
|
||||
void PushControllerState(uint8_t port, uint8_t state);
|
||||
void PushControllerState(uint8_t port, ControlDeviceState state);
|
||||
void DisableControllers();
|
||||
|
||||
protected:
|
||||
@ -45,7 +47,8 @@ public:
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
|
||||
uint8_t GetControllerState(uint8_t port);
|
||||
bool SetInput(BaseControlDevice *device);
|
||||
void InitControlDevice();
|
||||
void SendInput();
|
||||
|
||||
void SelectController(uint8_t port);
|
||||
|
@ -28,6 +28,9 @@ void GameDatabase::InitDatabase()
|
||||
while(db.good()) {
|
||||
string lineContent;
|
||||
std::getline(db, lineContent);
|
||||
if(lineContent[lineContent.size() - 1] == '\r') {
|
||||
lineContent = lineContent.substr(0, lineContent.size() - 1);
|
||||
}
|
||||
if(lineContent.empty() || lineContent[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
@ -105,7 +108,7 @@ void GameDatabase::InitializeInputDevices(string inputType, GameSystem system)
|
||||
ExpansionPortDevice expDevice = ExpansionPortDevice::None;
|
||||
EmulationSettings::ClearFlags(EmulationFlags::HasFourScore);
|
||||
|
||||
bool isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS);
|
||||
bool isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
||||
|
||||
if(inputType.compare("Zapper") == 0) {
|
||||
MessageManager::Log("[DB] Input: Zapper connected");
|
||||
@ -134,6 +137,45 @@ void GameDatabase::InitializeInputDevices(string inputType, GameSystem system)
|
||||
MessageManager::Log("[DB] Input: Oeka Kids Tablet connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::OekaKidsTablet;
|
||||
} else if(inputType.compare("KonamiHypershot") == 0) {
|
||||
MessageManager::Log("[DB] Input: Konami Hyper Shot connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::KonamiHyperShot;
|
||||
} else if(inputType.compare("FamilyKeyboard") == 0) {
|
||||
MessageManager::Log("[DB] Input: Family Basic Keyboard connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::FamilyBasicKeyboard;
|
||||
} else if(inputType.compare("PartyTap") == 0) {
|
||||
MessageManager::Log("[DB] Input: Party Tap connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::PartyTap;
|
||||
} else if(inputType.compare("Pachinko") == 0) {
|
||||
MessageManager::Log("[DB] Input: Pachinko controller connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::Pachinko;
|
||||
} else if(inputType.compare("ExcitingBoxing") == 0) {
|
||||
MessageManager::Log("[DB] Input: Exciting Boxing controller connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::ExcitingBoxing;
|
||||
} else if(inputType.compare("SuborKeyboard") == 0) {
|
||||
MessageManager::Log("[DB] Input: Subor mouse connected");
|
||||
MessageManager::Log("[DB] Input: Subor keyboard connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::SuborKeyboard;
|
||||
//TODO: FIX
|
||||
//controllers[2] = ControllerType::SuborMouse;
|
||||
} else if(inputType.compare("Mahjong") == 0) {
|
||||
MessageManager::Log("[DB] Input: Jissen Mahjong controller connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::JissenMahjong;
|
||||
} else if(inputType.compare("BarCodeWorld") == 0) {
|
||||
MessageManager::Log("[DB] Input: Barcode Battler barcode reader connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::BarcodeBattler;
|
||||
} else if(inputType.compare("BandaiHypershot") == 0) {
|
||||
MessageManager::Log("[DB] Input: Bandai Hyper Shot gun connected");
|
||||
system = GameSystem::Famicom;
|
||||
expDevice = ExpansionPortDevice::BandaiHyperShot;
|
||||
} else {
|
||||
MessageManager::Log("[DB] Input: 2 standard controllers connected");
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ GameServer::GameServer(uint16_t listenPort, string hostPlayerName)
|
||||
_port = listenPort;
|
||||
_hostPlayerName = hostPlayerName;
|
||||
_hostControllerPort = 0;
|
||||
ControlManager::RegisterBroadcaster(this);
|
||||
ControlManager::RegisterInputRecorder(this);
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
}
|
||||
|
||||
GameServer::~GameServer()
|
||||
@ -27,7 +28,8 @@ GameServer::~GameServer()
|
||||
|
||||
Stop();
|
||||
|
||||
ControlManager::UnregisterBroadcaster(this);
|
||||
ControlManager::UnregisterInputRecorder(this);
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
}
|
||||
|
||||
void GameServer::AcceptConnections()
|
||||
@ -68,6 +70,31 @@ list<shared_ptr<GameServerConnection>> GameServer::GetConnectionList()
|
||||
}
|
||||
}
|
||||
|
||||
bool GameServer::SetInput(BaseControlDevice *device)
|
||||
{
|
||||
uint8_t port = device->GetPort();
|
||||
|
||||
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
|
||||
if(connection) {
|
||||
//Device is controlled by a client
|
||||
device->SetRawState(connection->GetState());
|
||||
return true;
|
||||
}
|
||||
|
||||
//Host is controlling this device
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameServer::RecordInput(BaseControlDevice *device)
|
||||
{
|
||||
for(shared_ptr<GameServerConnection> connection : _openConnections) {
|
||||
if(!connection->ConnectionError()) {
|
||||
//Send movie stream
|
||||
connection->SendMovieData(device->GetPort(), device->GetRawState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::Exec()
|
||||
{
|
||||
_listener.reset(new Socket());
|
||||
@ -114,16 +141,6 @@ bool GameServer::Started()
|
||||
}
|
||||
}
|
||||
|
||||
void GameServer::BroadcastInput(uint8_t inputData, uint8_t port)
|
||||
{
|
||||
for(shared_ptr<GameServerConnection> connection : _openConnections) {
|
||||
if(!connection->ConnectionError()) {
|
||||
//Send movie stream
|
||||
connection->SendMovieData(inputData, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string GameServer::GetHostPlayerName()
|
||||
{
|
||||
if(GameServer::Started()) {
|
||||
@ -155,9 +172,9 @@ void GameServer::SetHostControllerPort(uint8_t port)
|
||||
|
||||
uint8_t GameServer::GetAvailableControllers()
|
||||
{
|
||||
uint8_t availablePorts = 0x0F;
|
||||
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
|
||||
for(PlayerInfo &playerInfo : GetPlayerList()) {
|
||||
if(playerInfo.ControllerPort < 4) {
|
||||
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
|
||||
availablePorts &= ~(1 << playerInfo.ControllerPort);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,12 @@
|
||||
#include <thread>
|
||||
#include "GameServerConnection.h"
|
||||
#include "INotificationListener.h"
|
||||
#include "IInputProvider.h"
|
||||
#include "IInputRecorder.h"
|
||||
|
||||
using std::thread;
|
||||
|
||||
class GameServer : public IGameBroadcaster
|
||||
class GameServer : public IInputRecorder, public IInputProvider
|
||||
{
|
||||
private:
|
||||
static unique_ptr<GameServer> Instance;
|
||||
@ -44,5 +46,6 @@ public:
|
||||
|
||||
static list<shared_ptr<GameServerConnection>> GetConnectionList();
|
||||
|
||||
virtual void BroadcastInput(uint8_t inputData, uint8_t port);
|
||||
bool SetInput(BaseControlDevice *device) override;
|
||||
void RecordInput(BaseControlDevice *device) override;
|
||||
};
|
@ -15,8 +15,9 @@
|
||||
#include "PlayerListMessage.h"
|
||||
#include "GameServer.h"
|
||||
#include "ForceDisconnectMessage.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
GameServerConnection* GameServerConnection::_netPlayDevices[4] = { nullptr,nullptr,nullptr,nullptr };
|
||||
GameServerConnection* GameServerConnection::_netPlayDevices[BaseControlDevice::PortCount] = { };
|
||||
|
||||
GameServerConnection::GameServerConnection(shared_ptr<Socket> socket) : GameConnection(socket, nullptr)
|
||||
{
|
||||
@ -45,7 +46,7 @@ void GameServerConnection::SendGameInformation()
|
||||
Console::Resume();
|
||||
}
|
||||
|
||||
void GameServerConnection::SendMovieData(uint8_t state, uint8_t port)
|
||||
void GameServerConnection::SendMovieData(uint8_t port, ControlDeviceState state)
|
||||
{
|
||||
if(_handshakeCompleted) {
|
||||
MovieDataMessage message(state, port);
|
||||
@ -60,17 +61,18 @@ void GameServerConnection::SendForceDisconnectMessage(string disconnectMessage)
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
void GameServerConnection::PushState(uint32_t state)
|
||||
void GameServerConnection::PushState(ControlDeviceState state)
|
||||
{
|
||||
if(_inputData.size() == 0 || state != _inputData.back()) {
|
||||
_inputData.clear();
|
||||
_inputData.push_back(state);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GameServerConnection::GetState()
|
||||
ControlDeviceState GameServerConnection::GetState()
|
||||
{
|
||||
size_t inputBufferSize = _inputData.size();
|
||||
uint32_t stateData = 0;
|
||||
ControlDeviceState stateData;
|
||||
if(inputBufferSize > 0) {
|
||||
stateData = _inputData.front();
|
||||
if(inputBufferSize > 1) {
|
||||
@ -162,7 +164,6 @@ void GameServerConnection::ProcessNotification(ConsoleNotificationType type, voi
|
||||
case ConsoleNotificationType::GameReset:
|
||||
case ConsoleNotificationType::StateLoaded:
|
||||
case ConsoleNotificationType::CheatAdded:
|
||||
case ConsoleNotificationType::FdsDiskChanged:
|
||||
case ConsoleNotificationType::ConfigChanged:
|
||||
SendGameInformation();
|
||||
break;
|
||||
@ -179,7 +180,7 @@ void GameServerConnection::RegisterNetPlayDevice(GameServerConnection* device, u
|
||||
void GameServerConnection::UnregisterNetPlayDevice(GameServerConnection* device)
|
||||
{
|
||||
if(device != nullptr) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
if(GameServerConnection::_netPlayDevices[i] == device) {
|
||||
GameServerConnection::_netPlayDevices[i] = nullptr;
|
||||
break;
|
||||
@ -196,7 +197,7 @@ GameServerConnection* GameServerConnection::GetNetPlayDevice(uint8_t port)
|
||||
uint8_t GameServerConnection::GetFirstFreeControllerPort()
|
||||
{
|
||||
uint8_t hostPost = GameServer::GetHostControllerPort();
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
if(hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr) {
|
||||
return i;
|
||||
}
|
||||
|
@ -3,20 +3,22 @@
|
||||
#include <deque>
|
||||
#include "GameConnection.h"
|
||||
#include "StandardController.h"
|
||||
#include "IGameBroadcaster.h"
|
||||
#include "INotificationListener.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "ControlDeviceState.h"
|
||||
|
||||
class HandShakeMessage;
|
||||
|
||||
class GameServerConnection : public GameConnection, public INotificationListener
|
||||
{
|
||||
private:
|
||||
static GameServerConnection* _netPlayDevices[4];
|
||||
static GameServerConnection* _netPlayDevices[BaseControlDevice::PortCount];
|
||||
|
||||
list<uint32_t> _inputData;
|
||||
list<ControlDeviceState> _inputData;
|
||||
int _controllerPort;
|
||||
bool _handshakeCompleted = false;
|
||||
void PushState(uint32_t state);
|
||||
|
||||
void PushState(ControlDeviceState state);
|
||||
void SendGameInformation();
|
||||
void SelectControllerPort(uint8_t port);
|
||||
|
||||
@ -35,8 +37,8 @@ public:
|
||||
GameServerConnection(shared_ptr<Socket> socket);
|
||||
~GameServerConnection();
|
||||
|
||||
uint32_t GetState();
|
||||
void SendMovieData(uint8_t state, uint8_t port);
|
||||
ControlDeviceState GetState();
|
||||
void SendMovieData(uint8_t port, ControlDeviceState state);
|
||||
|
||||
string GetPlayerName();
|
||||
uint8_t GetControllerPort();
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "RewindManager.h"
|
||||
#include "HdPackBuilder.h"
|
||||
|
||||
class ControlManager;
|
||||
|
||||
class HdBuilderPpu : public PPU
|
||||
{
|
||||
private:
|
||||
@ -108,7 +110,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
HdBuilderPpu(BaseMapper* mapper, HdPackBuilder* hdPackBuilder, uint32_t chrRamBankSize) : PPU(mapper)
|
||||
HdBuilderPpu(BaseMapper* mapper, ControlManager* controlManager, HdPackBuilder* hdPackBuilder, uint32_t chrRamBankSize) : PPU(mapper, controlManager)
|
||||
{
|
||||
_hdPackBuilder = hdPackBuilder;
|
||||
_chrRamBankSize = chrRamBankSize;
|
||||
|
@ -244,7 +244,7 @@ void HdPackLoader::ProcessPatchTag(vector<string> &tokens)
|
||||
return;
|
||||
}
|
||||
|
||||
std::transform(tokens[1].begin(), tokens[1].end(), tokens[1].begin(), ::tolower);
|
||||
std::transform(tokens[1].begin(), tokens[1].end(), tokens[1].begin(), ::toupper);
|
||||
if(_loadFromZip) {
|
||||
_data->PatchesByHash[tokens[1]] = VirtualFile(_hdPackFolder, tokens[0]);
|
||||
} else {
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "VideoDecoder.h"
|
||||
#include "RewindManager.h"
|
||||
|
||||
class ControlManager;
|
||||
|
||||
class HdPpu : public PPU
|
||||
{
|
||||
private:
|
||||
@ -126,7 +128,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
HdPpu(BaseMapper* mapper, uint32_t version) : PPU(mapper)
|
||||
HdPpu(BaseMapper* mapper, ControlManager* controlManager, uint32_t version) : PPU(mapper, controlManager)
|
||||
{
|
||||
_screenTileBuffers[0] = new HdPpuPixelInfo[256 * 240];
|
||||
_screenTileBuffers[1] = new HdPpuPixelInfo[256 * 240];
|
||||
|
51
Core/HoriTrack.h
Normal file
51
Core/HoriTrack.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "StandardController.h"
|
||||
#include "KeyManager.h"
|
||||
|
||||
class HoriTrack : public StandardController
|
||||
{
|
||||
protected:
|
||||
bool HasCoordinates() override { return true; }
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
StandardController::InternalSetStateFromInput();
|
||||
SetPressedState(StandardController::Buttons::A, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
SetPressedState(StandardController::Buttons::B, KeyManager::IsMouseButtonPressed(MouseButton::RightButton));
|
||||
SetMovement(KeyManager::GetMouseMovement());
|
||||
}
|
||||
|
||||
public:
|
||||
HoriTrack(KeyMappingSet keyMappings) : StandardController(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr)
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4016) {
|
||||
output = (_stateBuffer & 0x01) << 1;
|
||||
_stateBuffer >>= 1;
|
||||
StrobeProcessRead();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
MouseMovement mov = GetMovement();
|
||||
|
||||
mov.dx = std::max(-8, std::min((int)mov.dx, 7));
|
||||
mov.dy = std::max(-8, std::min((int)mov.dy, 7));
|
||||
|
||||
mov.dx = ((mov.dx & 0x08) >> 3) | ((mov.dx & 0x04) >> 1) | ((mov.dx & 0x02) << 1) | ((mov.dx & 0x01) << 3);
|
||||
mov.dy = ((mov.dy & 0x08) >> 3) | ((mov.dy & 0x04) >> 1) | ((mov.dy & 0x02) << 1) | ((mov.dy & 0x01) << 3);
|
||||
|
||||
uint8_t byte1 = (~mov.dy & 0x0F) | ((~mov.dx & 0x0F) << 4);
|
||||
uint8_t byte2 = 0x09;
|
||||
|
||||
StandardController::RefreshStateBuffer();
|
||||
_stateBuffer = (_stateBuffer & 0xFF) | (byte1 << 8) | (byte2 << 16);
|
||||
}
|
||||
};
|
8
Core/IBarcodeReader.h
Normal file
8
Core/IBarcodeReader.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class IBarcodeReader
|
||||
{
|
||||
public:
|
||||
virtual void InputBarcode(uint64_t barcode, uint32_t digitCount) = 0;
|
||||
};
|
7
Core/IBattery.h
Normal file
7
Core/IBattery.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class IBattery
|
||||
{
|
||||
public:
|
||||
virtual void SaveBattery() = 0;
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class IGameBroadcaster
|
||||
{
|
||||
public:
|
||||
virtual void BroadcastInput(uint8_t inputData, uint8_t port) = 0;
|
||||
};
|
9
Core/IInputProvider.h
Normal file
9
Core/IInputProvider.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class BaseControlDevice;
|
||||
|
||||
class IInputProvider
|
||||
{
|
||||
public:
|
||||
virtual bool SetInput(BaseControlDevice* device) = 0;
|
||||
};
|
10
Core/IInputRecorder.h
Normal file
10
Core/IInputRecorder.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
class BaseControlDevice;
|
||||
|
||||
class IInputRecorder
|
||||
{
|
||||
public:
|
||||
virtual void RecordInput(BaseControlDevice *device) = 0;
|
||||
virtual void EndFrame() { }
|
||||
};
|
@ -15,13 +15,12 @@ enum class ConsoleNotificationType
|
||||
PpuFrameDone = 9,
|
||||
MovieEnded = 10,
|
||||
ResolutionChanged = 11,
|
||||
FdsDiskChanged = 12,
|
||||
FdsBiosNotFound = 13,
|
||||
ConfigChanged = 14,
|
||||
DisconnectedFromServer = 15,
|
||||
PpuViewerDisplayFrame = 16,
|
||||
ExecuteShortcut = 17,
|
||||
EmulationStopped = 18,
|
||||
FdsBiosNotFound = 12,
|
||||
ConfigChanged = 13,
|
||||
DisconnectedFromServer = 14,
|
||||
PpuViewerDisplayFrame = 15,
|
||||
ExecuteShortcut = 16,
|
||||
EmulationStopped = 17,
|
||||
};
|
||||
|
||||
class INotificationListener
|
||||
|
@ -1,27 +1,28 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "NetMessage.h"
|
||||
#include "ControlDeviceState.h"
|
||||
|
||||
class InputDataMessage : public NetMessage
|
||||
{
|
||||
private:
|
||||
uint32_t _inputState;
|
||||
ControlDeviceState _inputState;
|
||||
|
||||
protected:
|
||||
virtual void ProtectedStreamState()
|
||||
{
|
||||
Stream<uint32_t>(_inputState);
|
||||
StreamArray(_inputState.State);
|
||||
}
|
||||
|
||||
public:
|
||||
InputDataMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
|
||||
|
||||
InputDataMessage(uint32_t inputState) : NetMessage(MessageType::InputData)
|
||||
InputDataMessage(ControlDeviceState inputState) : NetMessage(MessageType::InputData)
|
||||
{
|
||||
_inputState = inputState;
|
||||
}
|
||||
|
||||
uint32_t GetInputState()
|
||||
ControlDeviceState GetInputState()
|
||||
{
|
||||
return _inputState;
|
||||
}
|
||||
|
98
Core/JissenMahjongController.h
Normal file
98
Core/JissenMahjongController.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class JissenMahjongController : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint8_t _row = 0;
|
||||
uint32_t _stateBuffer = 0;
|
||||
|
||||
protected:
|
||||
enum Buttons { A = 0, B, C, D, E, F, G, H, I, J, K, L, M, N, Select, Start, Kan, Pon, Chii, Riichi, Ron };
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "ABCDEFGHIJKLMNSTkpcir";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
for(int i = 0; i < 21; i++) {
|
||||
SetPressedState(i, keyMapping.JissenMahjongButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_row, _stateBuffer);
|
||||
}
|
||||
|
||||
public:
|
||||
JissenMahjongController(KeyMappingSet keyMappings) : BaseControlDevice(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
uint8_t value = (_stateBuffer & 0x01) << 1;
|
||||
_stateBuffer >>= 1;
|
||||
StrobeProcessRead();
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
switch(_row) {
|
||||
default:
|
||||
case 0:
|
||||
_stateBuffer = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_stateBuffer =
|
||||
(IsPressed(JissenMahjongController::Buttons::N) ? 0x04 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::M) ? 0x08 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::L) ? 0x10 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::K) ? 0x20 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::J) ? 0x40 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::I) ? 0x80 : 0);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_stateBuffer =
|
||||
(IsPressed(JissenMahjongController::Buttons::H) ? 0x01 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::G) ? 0x02 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::F) ? 0x04 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::E) ? 0x08 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::D) ? 0x10 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::C) ? 0x20 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::B) ? 0x40 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::A) ? 0x80 : 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_stateBuffer =
|
||||
(IsPressed(JissenMahjongController::Buttons::Ron) ? 0x02 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Riichi) ? 0x04 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Chii) ? 0x08 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Pon) ? 0x10 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Kan) ? 0x20 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Start) ? 0x40 : 0) |
|
||||
(IsPressed(JissenMahjongController::Buttons::Select) ? 0x80 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_row = (value & 0x6) >> 1;
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
108
Core/KeyManager.cpp
Normal file
108
Core/KeyManager.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "stdafx.h"
|
||||
#include "KeyManager.h"
|
||||
#include "IKeyManager.h"
|
||||
#include "Types.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "PPU.h"
|
||||
|
||||
unique_ptr<IKeyManager> KeyManager::_keyManager;
|
||||
MousePosition KeyManager::_mousePosition;
|
||||
atomic<int16_t> KeyManager::_xMouseMovement;
|
||||
atomic<int16_t> KeyManager::_yMouseMovement;
|
||||
|
||||
void KeyManager::RegisterKeyManager(IKeyManager* keyManager)
|
||||
{
|
||||
_keyManager.reset(keyManager);
|
||||
}
|
||||
|
||||
void KeyManager::RefreshKeyState()
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->RefreshState();
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyManager::IsKeyPressed(uint32_t keyCode)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->IsKeyPressed(keyCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyManager::IsMouseButtonPressed(MouseButton button)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->IsMouseButtonPressed(button);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<uint32_t> KeyManager::GetPressedKeys()
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetPressedKeys();
|
||||
}
|
||||
return vector<uint32_t>();
|
||||
}
|
||||
|
||||
string KeyManager::GetKeyName(uint32_t keyCode)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetKeyName(keyCode);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
uint32_t KeyManager::GetKeyCode(string keyName)
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
return _keyManager->GetKeyCode(keyName);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KeyManager::UpdateDevices()
|
||||
{
|
||||
if(_keyManager != nullptr) {
|
||||
_keyManager->UpdateDevices();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyManager::SetMouseMovement(int16_t x, int16_t y)
|
||||
{
|
||||
_xMouseMovement += x;
|
||||
_yMouseMovement += y;
|
||||
}
|
||||
|
||||
MouseMovement KeyManager::GetMouseMovement()
|
||||
{
|
||||
double factor = EmulationSettings::GetVideoScale() * EmulationSettings::GetMouseSensitivity();
|
||||
MouseMovement mov;
|
||||
int16_t x = _xMouseMovement;
|
||||
int16_t y = _yMouseMovement;
|
||||
|
||||
mov.dx = (int16_t)(_xMouseMovement / factor);
|
||||
mov.dy = (int16_t)(_yMouseMovement / factor);
|
||||
_xMouseMovement -= (int16_t)(mov.dx * factor);
|
||||
_yMouseMovement -= (int16_t)(mov.dy * factor);
|
||||
|
||||
return mov;
|
||||
}
|
||||
|
||||
void KeyManager::SetMousePosition(double x, double y)
|
||||
{
|
||||
if(x < 0 || y < 0) {
|
||||
_mousePosition.X = -1;
|
||||
_mousePosition.Y = -1;
|
||||
} else {
|
||||
OverscanDimensions overscan = EmulationSettings::GetOverscanDimensions();
|
||||
_mousePosition.X = (int32_t)(x * (PPU::ScreenWidth - overscan.Left - overscan.Right) + overscan.Left);
|
||||
_mousePosition.Y = (int32_t)(y * (PPU::ScreenHeight - overscan.Top - overscan.Bottom) + overscan.Top);
|
||||
}
|
||||
}
|
||||
|
||||
MousePosition KeyManager::GetMousePosition()
|
||||
{
|
||||
return _mousePosition;
|
||||
}
|
32
Core/KeyManager.h
Normal file
32
Core/KeyManager.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Types.h"
|
||||
|
||||
class IKeyManager;
|
||||
enum class MouseButton;
|
||||
|
||||
class KeyManager
|
||||
{
|
||||
private:
|
||||
static unique_ptr<IKeyManager> _keyManager;
|
||||
static MousePosition _mousePosition;
|
||||
static atomic<int16_t> _xMouseMovement;
|
||||
static atomic<int16_t> _yMouseMovement;
|
||||
|
||||
public:
|
||||
static void RegisterKeyManager(IKeyManager* keyManager);
|
||||
static void RefreshKeyState();
|
||||
static bool IsKeyPressed(uint32_t keyCode);
|
||||
static bool IsMouseButtonPressed(MouseButton button);
|
||||
static vector<uint32_t> GetPressedKeys();
|
||||
static string GetKeyName(uint32_t keyCode);
|
||||
static uint32_t GetKeyCode(string keyName);
|
||||
|
||||
static void UpdateDevices();
|
||||
|
||||
static void SetMouseMovement(int16_t x, int16_t y);
|
||||
static MouseMovement GetMouseMovement();
|
||||
|
||||
static void SetMousePosition(double x, double y);
|
||||
static MousePosition GetMousePosition();
|
||||
};
|
84
Core/KonamiHyperShot.h
Normal file
84
Core/KonamiHyperShot.h
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class KonamiHyperShot : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
bool _enableP1 = true;
|
||||
bool _enableP2 = true;
|
||||
uint32_t _p1TurboSpeed;
|
||||
uint32_t _p2TurboSpeed;
|
||||
vector<KeyMapping> _p2KeyMappings;
|
||||
|
||||
protected:
|
||||
enum Buttons { Player1Run = 0, Player1Jump, Player2Run, Player2Jump };
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "RJrj";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
SetPressedState(Buttons::Player1Jump, keyMapping.A);
|
||||
SetPressedState(Buttons::Player1Run, keyMapping.B);
|
||||
|
||||
uint8_t turboFreq = 1 << (4 - _p1TurboSpeed);
|
||||
bool turboOn = (uint8_t)(PPU::GetFrameCount() % turboFreq) < turboFreq / 2;
|
||||
if(turboOn) {
|
||||
SetPressedState(Buttons::Player1Jump, keyMapping.TurboA);
|
||||
SetPressedState(Buttons::Player1Run, keyMapping.TurboB);
|
||||
}
|
||||
}
|
||||
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
SetPressedState(Buttons::Player1Jump, keyMapping.A);
|
||||
SetPressedState(Buttons::Player1Run, keyMapping.B);
|
||||
|
||||
uint8_t turboFreq = 1 << (4 - _p2TurboSpeed);
|
||||
bool turboOn = (uint8_t)(PPU::GetFrameCount() % turboFreq) < turboFreq / 2;
|
||||
if(turboOn) {
|
||||
SetPressedState(Buttons::Player2Jump, keyMapping.TurboA);
|
||||
SetPressedState(Buttons::Player2Run, keyMapping.TurboB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_enableP1, _enableP2);
|
||||
}
|
||||
|
||||
public:
|
||||
KonamiHyperShot(KeyMappingSet p1, KeyMappingSet p2) : BaseControlDevice(BaseControlDevice::ExpDevicePort, p1)
|
||||
{
|
||||
_p1TurboSpeed = p1.TurboSpeed;
|
||||
_p2TurboSpeed = p2.TurboSpeed;
|
||||
_p2KeyMappings = p2.GetKeyMappingArray();
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4017) {
|
||||
if(_enableP1) {
|
||||
output |= IsPressed(KonamiHyperShot::Buttons::Player1Jump) ? 0x02 : 0;
|
||||
output |= IsPressed(KonamiHyperShot::Buttons::Player1Run) ? 0x04 : 0;
|
||||
}
|
||||
if(_enableP2) {
|
||||
output |= IsPressed(KonamiHyperShot::Buttons::Player2Jump) ? 0x08 : 0;
|
||||
output |= IsPressed(KonamiHyperShot::Buttons::Player2Run) ? 0x10 : 0;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
_enableP2 = (value & 0x02) == 0;
|
||||
_enableP1 = (value & 0x04) == 0;
|
||||
}
|
||||
};
|
@ -18,6 +18,7 @@
|
||||
#include "StandardController.h"
|
||||
#include "PPU.h"
|
||||
#include "CheatManager.h"
|
||||
#include "KeyManager.h"
|
||||
|
||||
#define lua_pushintvalue(name, value) lua_pushliteral(lua, #name); lua_pushinteger(lua, (int)value); lua_settable(lua, -3);
|
||||
#define lua_pushboolvalue(name, value) lua_pushliteral(lua, #name); lua_pushboolean(lua, (int)value); lua_settable(lua, -3);
|
||||
@ -135,6 +136,7 @@ int LuaApi::GetLibrary(lua_State *lua)
|
||||
lua_pushintvalue(codeBreak, EventType::CodeBreak);
|
||||
lua_pushintvalue(stateLoaded, EventType::StateLoaded);
|
||||
lua_pushintvalue(stateSaved, EventType::StateSaved);
|
||||
lua_pushintvalue(inputPolled, EventType::InputPolled);
|
||||
lua_settable(lua, -3);
|
||||
|
||||
lua_pushliteral(lua, "executeCountType");
|
||||
@ -255,7 +257,7 @@ int LuaApi::RegisterEventCallback(lua_State *lua)
|
||||
EventType type = (EventType)l.ReadInteger();
|
||||
int reference = l.GetReference();
|
||||
checkparams();
|
||||
errorCond(type < EventType::Reset || type > EventType::StateSaved, "the specified type is invalid");
|
||||
errorCond(type < EventType::Reset || type >= EventType::EventTypeSize, "the specified type is invalid");
|
||||
errorCond(reference == LUA_NOREF, "the specified function could not be found");
|
||||
_context->RegisterEventCallback(type, reference);
|
||||
l.Return(reference);
|
||||
@ -268,7 +270,7 @@ int LuaApi::UnregisterEventCallback(lua_State *lua)
|
||||
EventType type = (EventType)l.ReadInteger();
|
||||
int reference = l.ReadInteger();
|
||||
checkparams();
|
||||
errorCond(type < EventType::Reset || type > EventType::StateSaved, "the specified type is invalid");
|
||||
errorCond(type < EventType::Reset || type >= EventType::EventTypeSize, "the specified type is invalid");
|
||||
errorCond(reference == LUA_NOREF, "function reference is invalid");
|
||||
_context->UnregisterEventCallback(type, reference);
|
||||
return l.ReturnCount();
|
||||
@ -280,7 +282,7 @@ int LuaApi::DrawString(lua_State *lua)
|
||||
l.ForceParamCount(6);
|
||||
int frameCount = l.ReadInteger(1);
|
||||
int backColor = l.ReadInteger(0);
|
||||
int color = l.ReadInteger(0xFFFFFFFF);
|
||||
int color = l.ReadInteger(0xFFFFFF);
|
||||
string text = l.ReadString();
|
||||
int y = l.ReadInteger();
|
||||
int x = l.ReadInteger();
|
||||
@ -294,8 +296,9 @@ int LuaApi::DrawString(lua_State *lua)
|
||||
int LuaApi::DrawLine(lua_State *lua)
|
||||
{
|
||||
LuaCallHelper l(lua);
|
||||
l.ForceParamCount(6);
|
||||
int frameCount = l.ReadInteger(1);
|
||||
int color = l.ReadInteger(0xFFFFFFFF);
|
||||
int color = l.ReadInteger(0xFFFFFF);
|
||||
int y2 = l.ReadInteger();
|
||||
int x2 = l.ReadInteger();
|
||||
int y = l.ReadInteger();
|
||||
@ -310,6 +313,7 @@ int LuaApi::DrawLine(lua_State *lua)
|
||||
int LuaApi::DrawPixel(lua_State *lua)
|
||||
{
|
||||
LuaCallHelper l(lua);
|
||||
l.ForceParamCount(4);
|
||||
int frameCount = l.ReadInteger(1);
|
||||
int color = l.ReadInteger();
|
||||
int y = l.ReadInteger();
|
||||
@ -324,9 +328,10 @@ int LuaApi::DrawPixel(lua_State *lua)
|
||||
int LuaApi::DrawRectangle(lua_State *lua)
|
||||
{
|
||||
LuaCallHelper l(lua);
|
||||
l.ForceParamCount(7);
|
||||
int frameCount = l.ReadInteger(1);
|
||||
bool fill = l.ReadBool(false);
|
||||
int color = l.ReadInteger(0xFFFFFFFF);
|
||||
int color = l.ReadInteger(0xFFFFFF);
|
||||
int height = l.ReadInteger();
|
||||
int width = l.ReadInteger();
|
||||
int y = l.ReadInteger();
|
||||
@ -364,14 +369,14 @@ int LuaApi::GetPixel(lua_State *lua)
|
||||
int LuaApi::GetMouseState(lua_State *lua)
|
||||
{
|
||||
LuaCallHelper l(lua);
|
||||
MousePosition pos = ControlManager::GetMousePosition();
|
||||
MousePosition pos = KeyManager::GetMousePosition();
|
||||
checkparams();
|
||||
lua_newtable(lua);
|
||||
lua_pushintvalue(x, pos.X);
|
||||
lua_pushintvalue(y, pos.Y);
|
||||
lua_pushboolvalue(left, ControlManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
lua_pushboolvalue(middle, ControlManager::IsMouseButtonPressed(MouseButton::MiddleButton));
|
||||
lua_pushboolvalue(right, ControlManager::IsMouseButtonPressed(MouseButton::RightButton));
|
||||
lua_pushboolvalue(left, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
lua_pushboolvalue(middle, KeyManager::IsMouseButtonPressed(MouseButton::MiddleButton));
|
||||
lua_pushboolvalue(right, KeyManager::IsMouseButtonPressed(MouseButton::RightButton));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -521,9 +526,9 @@ int LuaApi::IsKeyPressed(lua_State *lua)
|
||||
LuaCallHelper l(lua);
|
||||
string keyName = l.ReadString();
|
||||
checkparams();
|
||||
uint32_t keyCode = ControlManager::GetKeyCode(keyName);
|
||||
uint32_t keyCode = KeyManager::GetKeyCode(keyName);
|
||||
errorCond(keyCode == 0, "Invalid key name");
|
||||
l.Return(ControlManager::IsKeyPressed(keyCode));
|
||||
l.Return(KeyManager::IsKeyPressed(keyCode));
|
||||
return l.ReturnCount();
|
||||
}
|
||||
|
||||
@ -537,22 +542,25 @@ int LuaApi::GetInput(lua_State *lua)
|
||||
shared_ptr<StandardController> controller = std::dynamic_pointer_cast<StandardController>(ControlManager::GetControlDevice(port));
|
||||
errorCond(controller == nullptr, "Input port must be connected to a standard controller");
|
||||
|
||||
ButtonState state = controller->GetButtonState();
|
||||
lua_newtable(lua);
|
||||
lua_pushboolvalue(a, state.A);
|
||||
lua_pushboolvalue(b, state.B);
|
||||
lua_pushboolvalue(start, state.Start);
|
||||
lua_pushboolvalue(select, state.Select);
|
||||
lua_pushboolvalue(up, state.Up);
|
||||
lua_pushboolvalue(down, state.Down);
|
||||
lua_pushboolvalue(left, state.Left);
|
||||
lua_pushboolvalue(right, state.Right);
|
||||
lua_pushboolvalue(a, controller->IsPressed(StandardController::Buttons::A));
|
||||
lua_pushboolvalue(b, controller->IsPressed(StandardController::Buttons::B));
|
||||
lua_pushboolvalue(start, controller->IsPressed(StandardController::Buttons::Start));
|
||||
lua_pushboolvalue(select, controller->IsPressed(StandardController::Buttons::Select));
|
||||
lua_pushboolvalue(up, controller->IsPressed(StandardController::Buttons::Up));
|
||||
lua_pushboolvalue(down, controller->IsPressed(StandardController::Buttons::Down));
|
||||
lua_pushboolvalue(left, controller->IsPressed(StandardController::Buttons::Left));
|
||||
lua_pushboolvalue(right, controller->IsPressed(StandardController::Buttons::Right));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaApi::SetInput(lua_State *lua)
|
||||
{
|
||||
lua_settop(lua, 2);
|
||||
LuaCallHelper l(lua);
|
||||
lua_settop(lua, 3);
|
||||
|
||||
bool allowUserInput = l.ReadBool();
|
||||
|
||||
luaL_checktype(lua, 2, LUA_TTABLE);
|
||||
lua_getfield(lua, 2, "a");
|
||||
lua_getfield(lua, 2, "b");
|
||||
@ -563,22 +571,32 @@ int LuaApi::SetInput(lua_State *lua)
|
||||
lua_getfield(lua, 2, "left");
|
||||
lua_getfield(lua, 2, "right");
|
||||
|
||||
LuaCallHelper l(lua);
|
||||
ButtonState buttonState;
|
||||
buttonState.Right = l.ReadBool();
|
||||
buttonState.Left = l.ReadBool();
|
||||
buttonState.Down = l.ReadBool();
|
||||
buttonState.Up = l.ReadBool();
|
||||
buttonState.Select = l.ReadBool();
|
||||
buttonState.Start = l.ReadBool();
|
||||
buttonState.B = l.ReadBool();
|
||||
buttonState.A = l.ReadBool();
|
||||
Nullable<bool> right = l.ReadOptionalBool();
|
||||
Nullable<bool> left = l.ReadOptionalBool();
|
||||
Nullable<bool> down = l.ReadOptionalBool();
|
||||
Nullable<bool> up = l.ReadOptionalBool();
|
||||
Nullable<bool> select = l.ReadOptionalBool();
|
||||
Nullable<bool> start = l.ReadOptionalBool();
|
||||
Nullable<bool> b = l.ReadOptionalBool();
|
||||
Nullable<bool> a = l.ReadOptionalBool();
|
||||
|
||||
lua_pop(lua, 1);
|
||||
int port = l.ReadInteger();
|
||||
|
||||
_debugger->SetInputOverride(port, buttonState.ToByte());
|
||||
errorCond(port < 0 || port > 3, "Invalid port number - must be between 0 to 3");
|
||||
|
||||
shared_ptr<StandardController> controller = std::dynamic_pointer_cast<StandardController>(ControlManager::GetControlDevice(port));
|
||||
errorCond(controller == nullptr, "Input port must be connected to a standard controller");
|
||||
|
||||
if(right.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Right, right.Value);
|
||||
if(left.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Left, left.Value);
|
||||
if(down.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Down, down.Value);
|
||||
if(up.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Up, up.Value);
|
||||
if(select.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Select, select.Value);
|
||||
if(start.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::Start, start.Value);
|
||||
if(b.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::B, b.Value);
|
||||
if(a.HasValue || !allowUserInput) controller->SetBitValue(StandardController::Buttons::A, a.Value);
|
||||
|
||||
return l.ReturnCount();
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,36 @@ bool LuaCallHelper::ReadBool(bool defaultValue)
|
||||
return value;
|
||||
}
|
||||
|
||||
Nullable<bool> LuaCallHelper::ReadOptionalBool()
|
||||
{
|
||||
_paramCount++;
|
||||
Nullable<bool> result;
|
||||
if(lua_isboolean(_lua, -1)) {
|
||||
result.HasValue = true;
|
||||
result.Value = lua_toboolean(_lua, -1) != 0;
|
||||
} else if(lua_isnumber(_lua, -1)) {
|
||||
result.HasValue = true;
|
||||
result.Value = lua_tonumber(_lua, -1) != 0;
|
||||
}
|
||||
lua_pop(_lua, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
Nullable<uint32_t> LuaCallHelper::ReadOptionalInteger()
|
||||
{
|
||||
_paramCount++;
|
||||
Nullable<uint32_t> result;
|
||||
if(lua_isinteger(_lua, -1)) {
|
||||
result.HasValue = true;
|
||||
result.Value = (uint32_t)lua_tointeger(_lua, -1);
|
||||
} else if(lua_isnumber(_lua, -1)) {
|
||||
result.HasValue = true;
|
||||
result.Value = (uint32_t)lua_tonumber(_lua, -1);
|
||||
}
|
||||
lua_pop(_lua, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t LuaCallHelper::ReadInteger(uint32_t defaultValue)
|
||||
{
|
||||
_paramCount++;
|
||||
|
@ -2,6 +2,13 @@
|
||||
#include "stdafx.h"
|
||||
#include "../Lua/lua.hpp"
|
||||
|
||||
template<typename T>
|
||||
struct Nullable
|
||||
{
|
||||
bool HasValue = false;
|
||||
T Value = {};
|
||||
};
|
||||
|
||||
class LuaCallHelper
|
||||
{
|
||||
private:
|
||||
@ -22,6 +29,9 @@ public:
|
||||
string ReadString();
|
||||
int GetReference();
|
||||
|
||||
Nullable<bool> ReadOptionalBool();
|
||||
Nullable<uint32_t> ReadOptionalInteger();
|
||||
|
||||
void Return(bool value);
|
||||
void Return(int value);
|
||||
void Return(uint32_t value);
|
||||
|
27
Core/Mapper39.h
Normal file
27
Core/Mapper39.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseMapper.h"
|
||||
|
||||
//Used by Study and Game 32-in-1 (Ch)
|
||||
class Mapper39 : public BaseMapper
|
||||
{
|
||||
protected:
|
||||
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
|
||||
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
|
||||
|
||||
void InitMapper() override
|
||||
{
|
||||
SelectPRGPage(0, 0);
|
||||
SelectCHRPage(0, 0);
|
||||
}
|
||||
|
||||
void Reset(bool softReset) override
|
||||
{
|
||||
SelectPRGPage(0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
SelectPRGPage(0, value);
|
||||
}
|
||||
};
|
@ -22,8 +22,7 @@ protected:
|
||||
SelectPRGPage(0, prgBank);
|
||||
SelectPRGPage(1, prgBank);
|
||||
} else {
|
||||
SelectPRGPage(0, prgBank & 0xFE);
|
||||
SelectPRGPage(1, (prgBank & 0xFE) + 1);
|
||||
SelectPrgPage2x(0, prgBank & 0x06);
|
||||
}
|
||||
SelectCHRPage(0, (addr >> 3) & 0x07);
|
||||
|
||||
|
@ -76,6 +76,7 @@
|
||||
#include "MagicKidGooGoo.h"
|
||||
#include "Mapper15.h"
|
||||
#include "Mapper35.h"
|
||||
#include "Mapper39.h"
|
||||
#include "Mapper40.h"
|
||||
#include "Mapper42.h"
|
||||
#include "Mapper43.h"
|
||||
@ -319,6 +320,7 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
|
||||
case 36: return new Txc22000();
|
||||
case 37: return new MMC3_37();
|
||||
case 38: return new UnlPci556();
|
||||
case 39: return new Mapper39();
|
||||
case 40: return new Mapper40();
|
||||
case 41: return new Caltron41();
|
||||
case 42: return new Mapper42();
|
||||
|
@ -1,371 +1,303 @@
|
||||
#include "stdafx.h"
|
||||
#include "MessageManager.h"
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/ZipReader.h"
|
||||
#include "../Utilities/StringUtilities.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "MesenMovie.h"
|
||||
#include "MessageManager.h"
|
||||
#include "ControlManager.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "Console.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "RomLoader.h"
|
||||
#include "CheatManager.h"
|
||||
#include "SaveStateManager.h"
|
||||
#include "CheatManager.h"
|
||||
#include "MovieRecorder.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "VirtualFile.h"
|
||||
|
||||
MesenMovie::MesenMovie()
|
||||
{
|
||||
}
|
||||
|
||||
MesenMovie::~MesenMovie()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void MesenMovie::Stop()
|
||||
{
|
||||
if(_playing) {
|
||||
EndMovie();
|
||||
_playing = false;
|
||||
}
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
}
|
||||
|
||||
bool MesenMovie::SetInput(BaseControlDevice *device)
|
||||
{
|
||||
if(_inputData.size() > _readIndex && _inputData[_readIndex].size() > _deviceIndex) {
|
||||
device->SetTextState(_inputData[_readIndex][_deviceIndex]);
|
||||
|
||||
_deviceIndex++;
|
||||
if(_deviceIndex >= _inputData[_readIndex].size()) {
|
||||
//Move to the next frame's data
|
||||
_deviceIndex = 0;
|
||||
_readIndex++;
|
||||
}
|
||||
} else {
|
||||
Stop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MesenMovie::IsPlaying()
|
||||
{
|
||||
return _playing;
|
||||
}
|
||||
|
||||
bool MesenMovie::IsRecording()
|
||||
vector<uint8_t> MesenMovie::LoadBattery(string extension)
|
||||
{
|
||||
return _recording;
|
||||
vector<uint8_t> batteryData;
|
||||
_reader->ExtractFile("Battery" + extension, batteryData);
|
||||
return batteryData;
|
||||
}
|
||||
|
||||
void MesenMovie::PushState(uint8_t port)
|
||||
bool MesenMovie::Play(VirtualFile &file)
|
||||
{
|
||||
if(_counter[port] > 0) {
|
||||
uint16_t data = _lastState[port] << 8 | _counter[port];
|
||||
_data.PortData[port].push_back(data);
|
||||
_movieFile = file;
|
||||
|
||||
_lastState[port] = 0;
|
||||
_counter[port] = 0;
|
||||
std::stringstream ss;
|
||||
file.ReadFile(ss);
|
||||
|
||||
_reader.reset(new ZipReader());
|
||||
_reader->LoadArchive(ss);
|
||||
|
||||
stringstream settingsData, inputData;
|
||||
if(!_reader->GetStream("GameSettings.txt", settingsData)) {
|
||||
MessageManager::Log("[Movie] File not found: GameSettings.txt");
|
||||
return false;
|
||||
}
|
||||
if(!_reader->GetStream("Input.txt", inputData)) {
|
||||
MessageManager::Log("[Movie] File not found: Input.txt");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MesenMovie::RecordState(uint8_t port, uint8_t state)
|
||||
{
|
||||
if(_recording) {
|
||||
if(_lastState[port] != state || _counter[port] == 0) {
|
||||
if(_counter[port] != 0) {
|
||||
PushState(port);
|
||||
}
|
||||
_lastState[port] = state;
|
||||
_counter[port] = 1;
|
||||
} else {
|
||||
_counter[port]++;
|
||||
|
||||
if(_counter[port] == 255) {
|
||||
PushState(port);
|
||||
}
|
||||
while(inputData) {
|
||||
string line;
|
||||
std::getline(inputData, line);
|
||||
if(line.substr(0, 1) == "|") {
|
||||
_inputData.push_back(StringUtilities::Split(line.substr(1), '|'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MesenMovie::GetState(uint8_t port)
|
||||
{
|
||||
uint16_t data = --_data.PortData[port][_readPosition[port]];
|
||||
if((data & 0xFF) == 0) {
|
||||
_readPosition[port]++;
|
||||
}
|
||||
|
||||
if(_readPosition[port] >= _data.DataSize[port]) {
|
||||
//End of movie file
|
||||
EndMovie();
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
return (data >> 8);
|
||||
}
|
||||
|
||||
void MesenMovie::Reset()
|
||||
{
|
||||
_startState.clear();
|
||||
_startState.seekg(0, ios::beg);
|
||||
_startState.seekp(0, ios::beg);
|
||||
|
||||
memset(_readPosition, 0, 4 * sizeof(uint32_t));
|
||||
memset(_counter, 0, 4);
|
||||
memset(_lastState, 0, 4);
|
||||
_data = MovieData();
|
||||
|
||||
_recording = false;
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
void MesenMovie::Record(string filename, bool reset)
|
||||
{
|
||||
_filename = filename;
|
||||
_file.open(filename, ios::out | ios::binary);
|
||||
|
||||
if(_file) {
|
||||
Console::Pause();
|
||||
|
||||
Reset();
|
||||
|
||||
if(reset) {
|
||||
//Movies need a fixed power up state to be identical on each replay, force all 0s for RAM, no matter the setting
|
||||
RamPowerOnState originalState = EmulationSettings::GetRamPowerOnState();
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllZeros);
|
||||
Console::Reset(false);
|
||||
EmulationSettings::SetRamPowerOnState(originalState);
|
||||
} else {
|
||||
Console::SaveState(_startState);
|
||||
}
|
||||
|
||||
_recording = true;
|
||||
|
||||
Console::Resume();
|
||||
|
||||
MessageManager::DisplayMessage("Movies", "MovieRecordingTo", FolderUtilities::GetFilename(filename, true));
|
||||
}
|
||||
}
|
||||
|
||||
void MesenMovie::Stop()
|
||||
{
|
||||
if(_recording) {
|
||||
_recording = false;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
PushState(i);
|
||||
}
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
bool MesenMovie::Play(stringstream &filestream, bool autoLoadRom)
|
||||
{
|
||||
Stop();
|
||||
|
||||
Reset();
|
||||
_readIndex = 0;
|
||||
_deviceIndex = 0;
|
||||
|
||||
ParseSettings(settingsData);
|
||||
|
||||
Console::Pause();
|
||||
if(Load(filestream, autoLoadRom)) {
|
||||
if(_startState.tellp() > 0) {
|
||||
//Restore state if one was present in the movie
|
||||
Console::LoadState(_startState);
|
||||
}
|
||||
|
||||
BatteryManager::SetBatteryProvider(shared_from_this());
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
ApplySettings();
|
||||
|
||||
CheatManager::SetCheats(_cheatList);
|
||||
_playing = true;
|
||||
if(!LoadGame()) {
|
||||
Console::Resume();
|
||||
return false;
|
||||
}
|
||||
|
||||
stringstream saveStateData;
|
||||
if(_reader->GetStream("SaveState.mst", saveStateData)) {
|
||||
if(!SaveStateManager::LoadState(saveStateData, true)) {
|
||||
Console::Resume();
|
||||
return false;
|
||||
} else {
|
||||
//Reset to first line of the input log
|
||||
//TODO: Change this to allow rewinding during movie playback
|
||||
_readIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_playing = true;
|
||||
|
||||
Console::Resume();
|
||||
return _playing;
|
||||
}
|
||||
|
||||
struct MovieHeader
|
||||
{
|
||||
char Header[3] = { 'M', 'M', 'O' };
|
||||
uint32_t MesenVersion;
|
||||
uint32_t MovieFormatVersion;
|
||||
uint32_t SaveStateFormatVersion;
|
||||
uint32_t RomCrc32;
|
||||
uint32_t Region;
|
||||
uint32_t ConsoleType;
|
||||
uint8_t ControllerTypes[4];
|
||||
uint32_t ExpansionDevice;
|
||||
uint32_t OverclockRate = 100;
|
||||
bool OverclockAdjustApu = true;
|
||||
uint32_t ExtraScanlinesBeforeNmi = 0;
|
||||
uint32_t ExtraScanlinesAfterNmi = 0;
|
||||
bool DisablePpu2004Reads = false;
|
||||
bool DisablePaletteRead = false;
|
||||
bool DisableOamAddrBug = false;
|
||||
bool UseNes101Hvc101Behavior = false;
|
||||
|
||||
uint32_t CheatCount;
|
||||
uint32_t FilenameLength;
|
||||
};
|
||||
|
||||
bool MesenMovie::Save()
|
||||
{
|
||||
string romFilename = Console::GetRomName();
|
||||
|
||||
MovieHeader header = {};
|
||||
header.MesenVersion = EmulationSettings::GetMesenVersion();
|
||||
header.MovieFormatVersion = MesenMovie::MovieFormatVersion;
|
||||
header.SaveStateFormatVersion = SaveStateManager::FileFormatVersion;
|
||||
header.RomCrc32 = Console::GetHashInfo().Crc32Hash;
|
||||
header.Region = (uint32_t)Console::GetModel();
|
||||
header.ConsoleType = (uint32_t)EmulationSettings::GetConsoleType();
|
||||
header.ExpansionDevice = (uint32_t)EmulationSettings::GetExpansionDevice();
|
||||
header.OverclockRate = (uint32_t)EmulationSettings::GetOverclockRate();
|
||||
header.OverclockAdjustApu = EmulationSettings::GetOverclockAdjustApu();
|
||||
header.DisablePpu2004Reads = EmulationSettings::CheckFlag(EmulationFlags::DisablePpu2004Reads);
|
||||
header.DisablePaletteRead = EmulationSettings::CheckFlag(EmulationFlags::DisablePaletteRead);
|
||||
header.DisableOamAddrBug = EmulationSettings::CheckFlag(EmulationFlags::DisableOamAddrBug);
|
||||
header.UseNes101Hvc101Behavior = EmulationSettings::CheckFlag(EmulationFlags::UseNes101Hvc101Behavior);
|
||||
for(int port = 0; port < 4; port++) {
|
||||
header.ControllerTypes[port] = (uint32_t)EmulationSettings::GetControllerType(port);
|
||||
}
|
||||
header.FilenameLength = (uint32_t)romFilename.size();
|
||||
|
||||
vector<CodeInfo> cheatList = CheatManager::GetCheats();
|
||||
header.CheatCount = (uint32_t)cheatList.size();
|
||||
|
||||
_file.write((char*)header.Header, sizeof(header.Header));
|
||||
_file.write((char*)&header.MesenVersion, sizeof(header.MesenVersion));
|
||||
_file.write((char*)&header.MovieFormatVersion, sizeof(header.MovieFormatVersion));
|
||||
_file.write((char*)&header.SaveStateFormatVersion, sizeof(header.SaveStateFormatVersion));
|
||||
_file.write((char*)&header.RomCrc32, sizeof(header.RomCrc32));
|
||||
_file.write((char*)&header.Region, sizeof(header.Region));
|
||||
_file.write((char*)&header.ConsoleType, sizeof(header.ConsoleType));
|
||||
_file.write((char*)&header.ControllerTypes, sizeof(header.ControllerTypes));
|
||||
_file.write((char*)&header.ExpansionDevice, sizeof(header.ExpansionDevice));
|
||||
_file.write((char*)&header.OverclockRate, sizeof(header.OverclockRate));
|
||||
_file.write((char*)&header.OverclockAdjustApu, sizeof(header.OverclockAdjustApu));
|
||||
_file.write((char*)&header.ExtraScanlinesBeforeNmi, sizeof(header.ExtraScanlinesBeforeNmi));
|
||||
_file.write((char*)&header.ExtraScanlinesAfterNmi, sizeof(header.ExtraScanlinesAfterNmi));
|
||||
_file.write((char*)&header.DisablePpu2004Reads, sizeof(header.DisablePpu2004Reads));
|
||||
_file.write((char*)&header.DisablePaletteRead, sizeof(header.DisablePaletteRead));
|
||||
_file.write((char*)&header.DisableOamAddrBug, sizeof(header.DisableOamAddrBug));
|
||||
_file.write((char*)&header.UseNes101Hvc101Behavior, sizeof(header.UseNes101Hvc101Behavior));
|
||||
_file.write((char*)&header.CheatCount, sizeof(header.CheatCount));
|
||||
_file.write((char*)&header.FilenameLength, sizeof(header.FilenameLength));
|
||||
|
||||
_file.write((char*)romFilename.c_str(), header.FilenameLength);
|
||||
|
||||
for(CodeInfo cheatCode : cheatList) {
|
||||
_file.write((char*)&cheatCode.Address, sizeof(cheatCode.Address));
|
||||
_file.write((char*)&cheatCode.Value, sizeof(cheatCode.Value));
|
||||
_file.write((char*)&cheatCode.CompareValue, sizeof(cheatCode.CompareValue));
|
||||
_file.write((char*)&cheatCode.IsRelativeAddress, sizeof(cheatCode.IsRelativeAddress));
|
||||
}
|
||||
|
||||
_data.SaveStateSize = (uint32_t)_startState.tellp();
|
||||
_file.write((char*)&_data.SaveStateSize, sizeof(uint32_t));
|
||||
|
||||
if(_data.SaveStateSize > 0) {
|
||||
_startState.seekg(0, ios::beg);
|
||||
uint8_t *stateBuffer = new uint8_t[_data.SaveStateSize];
|
||||
_startState.read((char*)stateBuffer, _data.SaveStateSize);
|
||||
_file.write((char*)stateBuffer, _data.SaveStateSize);
|
||||
delete[] stateBuffer;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
_data.DataSize[i] = (uint32_t)_data.PortData[i].size();
|
||||
_file.write((char*)&_data.DataSize[i], sizeof(uint32_t));
|
||||
if(_data.DataSize[i] > 0) {
|
||||
_file.write((char*)&_data.PortData[i][0], _data.DataSize[i] * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
_file.close();
|
||||
|
||||
MessageManager::DisplayMessage("Movies", "MovieSaved", FolderUtilities::GetFilename(_filename, true));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MesenMovie::Load(std::stringstream &file, bool autoLoadRom)
|
||||
template<typename T>
|
||||
T FromString(string name, const vector<string> &enumNames, T defaultValue)
|
||||
{
|
||||
MovieHeader header = {};
|
||||
file.read((char*)header.Header, sizeof(header.Header));
|
||||
|
||||
if(memcmp(header.Header, "MMO", 3) != 0) {
|
||||
//Invalid movie file
|
||||
MessageManager::DisplayMessage("Movies", "MovieInvalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
file.read((char*)&header.MesenVersion, sizeof(header.MesenVersion));
|
||||
|
||||
if(header.MesenVersion > EmulationSettings::GetMesenVersion()) {
|
||||
MessageManager::DisplayMessage("Movies", "MovieNewerVersion");
|
||||
return false;
|
||||
}
|
||||
|
||||
file.read((char*)&header.MovieFormatVersion, sizeof(header.MovieFormatVersion));
|
||||
if(header.MovieFormatVersion < 2 || header.MovieFormatVersion > MesenMovie::MovieFormatVersion) {
|
||||
//Currently compatible with version 2 & 3
|
||||
MessageManager::DisplayMessage("Movies", "MovieIncompatibleVersion");
|
||||
return false;
|
||||
}
|
||||
|
||||
file.read((char*)&header.SaveStateFormatVersion, sizeof(header.SaveStateFormatVersion));
|
||||
file.read((char*)&header.RomCrc32, sizeof(header.RomCrc32));
|
||||
file.read((char*)&header.Region, sizeof(header.Region));
|
||||
file.read((char*)&header.ConsoleType, sizeof(header.ConsoleType));
|
||||
file.read((char*)&header.ControllerTypes, sizeof(header.ControllerTypes));
|
||||
file.read((char*)&header.ExpansionDevice, sizeof(header.ExpansionDevice));
|
||||
if(header.MovieFormatVersion >= 3) {
|
||||
//New fields in version 3
|
||||
file.read((char*)&header.OverclockRate, sizeof(header.OverclockRate));
|
||||
file.read((char*)&header.OverclockAdjustApu, sizeof(header.OverclockAdjustApu));
|
||||
}
|
||||
if(header.MovieFormatVersion >= 4) {
|
||||
file.read((char*)&header.ExtraScanlinesBeforeNmi, sizeof(header.ExtraScanlinesBeforeNmi));
|
||||
file.read((char*)&header.ExtraScanlinesAfterNmi, sizeof(header.ExtraScanlinesAfterNmi));
|
||||
}
|
||||
if(header.MovieFormatVersion >= 5) {
|
||||
file.read((char*)&header.DisablePpu2004Reads, sizeof(header.DisablePpu2004Reads));
|
||||
file.read((char*)&header.DisablePaletteRead, sizeof(header.DisablePaletteRead));
|
||||
file.read((char*)&header.DisableOamAddrBug, sizeof(header.DisableOamAddrBug));
|
||||
file.read((char*)&header.UseNes101Hvc101Behavior, sizeof(header.UseNes101Hvc101Behavior));
|
||||
}
|
||||
|
||||
EmulationSettings::SetOverclockRate(header.OverclockRate, header.OverclockAdjustApu);
|
||||
EmulationSettings::SetPpuNmiConfig(header.ExtraScanlinesBeforeNmi, header.ExtraScanlinesAfterNmi);
|
||||
EmulationSettings::SetFlagState(EmulationFlags::UseNes101Hvc101Behavior, header.UseNes101Hvc101Behavior);
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisablePpu2004Reads, header.DisablePpu2004Reads);
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisablePaletteRead, header.DisablePaletteRead);
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisableOamAddrBug, header.DisableOamAddrBug);
|
||||
|
||||
file.read((char*)&header.CheatCount, sizeof(header.CheatCount));
|
||||
file.read((char*)&header.FilenameLength, sizeof(header.FilenameLength));
|
||||
|
||||
EmulationSettings::SetConsoleType((ConsoleType)header.ConsoleType);
|
||||
EmulationSettings::SetExpansionDevice((ExpansionPortDevice)header.ExpansionDevice);
|
||||
for(int port = 0; port < 4; port++) {
|
||||
EmulationSettings::SetControllerType(port, (ControllerType)header.ControllerTypes[port]);
|
||||
}
|
||||
|
||||
char* romFilename = new char[header.FilenameLength + 1];
|
||||
memset(romFilename, 0, header.FilenameLength + 1);
|
||||
file.read((char*)romFilename, header.FilenameLength);
|
||||
|
||||
_cheatList.clear();
|
||||
CodeInfo cheatCode;
|
||||
for(uint32_t i = 0; i < header.CheatCount; i++) {
|
||||
file.read((char*)&cheatCode.Address, sizeof(cheatCode.Address));
|
||||
file.read((char*)&cheatCode.Value, sizeof(cheatCode.Value));
|
||||
file.read((char*)&cheatCode.CompareValue, sizeof(cheatCode.CompareValue));
|
||||
file.read((char*)&cheatCode.IsRelativeAddress, sizeof(cheatCode.IsRelativeAddress));
|
||||
_cheatList.push_back(cheatCode);
|
||||
}
|
||||
|
||||
file.read((char*)&_data.SaveStateSize, sizeof(uint32_t));
|
||||
|
||||
if(_data.SaveStateSize > 0) {
|
||||
if(header.SaveStateFormatVersion != SaveStateManager::FileFormatVersion) {
|
||||
MessageManager::DisplayMessage("Movies", "MovieIncompatibleVersion");
|
||||
return false;
|
||||
for(size_t i = 0; i < enumNames.size(); i++) {
|
||||
if(name == enumNames[i]) {
|
||||
return (T)i;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool loadedGame = true;
|
||||
if(autoLoadRom) {
|
||||
string currentRom = Console::GetRomName();
|
||||
if(currentRom.empty() || header.RomCrc32 != Console::GetHashInfo().Crc32Hash) {
|
||||
//Loaded game isn't the same as the game used for the movie, attempt to load the correct game
|
||||
HashInfo hashInfo;
|
||||
hashInfo.Crc32Hash = header.RomCrc32;
|
||||
loadedGame = Console::LoadROM(romFilename, hashInfo);
|
||||
void MesenMovie::ParseSettings(stringstream &data)
|
||||
{
|
||||
while(!data.eof()) {
|
||||
string line;
|
||||
std::getline(data, line);
|
||||
|
||||
if(!line.empty()) {
|
||||
size_t index = line.find_first_of(' ');
|
||||
if(index >= 0) {
|
||||
string name = line.substr(0, index);
|
||||
string value = line.substr(index + 1);
|
||||
|
||||
if(name == "Cheat") {
|
||||
_cheats.push_back(value);
|
||||
} else {
|
||||
_settings[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MesenMovie::LoadGame()
|
||||
{
|
||||
string mesenVersion = LoadString(_settings, MovieKeys::MesenVersion);
|
||||
string gameFile = LoadString(_settings, MovieKeys::GameFile);
|
||||
string sha1Hash = LoadString(_settings, MovieKeys::Sha1);
|
||||
//string patchFile = LoadString(_settings, MovieKeys::PatchFile);
|
||||
//string patchFileSha1 = LoadString(_settings, MovieKeys::PatchFileSha1);
|
||||
//string patchedRomSha1 = LoadString(_settings, MovieKeys::PatchedRomSha1);
|
||||
|
||||
HashInfo hashInfo;
|
||||
hashInfo.Sha1Hash = sha1Hash;
|
||||
|
||||
VirtualFile romFile = Console::FindMatchingRom(gameFile, hashInfo);
|
||||
bool gameLoaded = false;
|
||||
if(romFile.IsValid()) {
|
||||
VirtualFile patchFile(_movieFile.GetFilePath(), "PatchData.dat");
|
||||
if(patchFile.IsValid()) {
|
||||
gameLoaded = Console::LoadROM(romFile, patchFile);
|
||||
} else {
|
||||
Console::Reset(false);
|
||||
gameLoaded = Console::LoadROM(romFile);
|
||||
}
|
||||
}
|
||||
|
||||
if(loadedGame) {
|
||||
if(_data.SaveStateSize > 0) {
|
||||
uint8_t *stateBuffer = new uint8_t[_data.SaveStateSize];
|
||||
file.read((char*)stateBuffer, _data.SaveStateSize);
|
||||
_startState.write((char*)stateBuffer, _data.SaveStateSize);
|
||||
delete[] stateBuffer;
|
||||
}
|
||||
return gameLoaded;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
file.read((char*)&_data.DataSize[i], sizeof(uint32_t));
|
||||
void MesenMovie::ApplySettings()
|
||||
{
|
||||
NesModel region = FromString(LoadString(_settings, MovieKeys::Region), NesModelNames, NesModel::NTSC);
|
||||
ConsoleType consoleType = FromString(LoadString(_settings, MovieKeys::ConsoleType), ConsoleTypeNames, ConsoleType::Nes);
|
||||
ControllerType controller1 = FromString(LoadString(_settings, MovieKeys::Controller1), ControllerTypeNames, ControllerType::None);
|
||||
ControllerType controller2 = FromString(LoadString(_settings, MovieKeys::Controller2), ControllerTypeNames, ControllerType::None);
|
||||
ControllerType controller3 = FromString(LoadString(_settings, MovieKeys::Controller3), ControllerTypeNames, ControllerType::None);
|
||||
ControllerType controller4 = FromString(LoadString(_settings, MovieKeys::Controller4), ControllerTypeNames, ControllerType::None);
|
||||
ExpansionPortDevice expansionDevice = FromString<ExpansionPortDevice>(LoadString(_settings, MovieKeys::ExpansionDevice), ExpansionPortDeviceNames, ExpansionPortDevice::None);
|
||||
|
||||
uint16_t* readBuffer = new uint16_t[_data.DataSize[i]];
|
||||
file.read((char*)readBuffer, _data.DataSize[i] * sizeof(uint16_t));
|
||||
_data.PortData[i] = vector<uint16_t>(readBuffer, readBuffer + _data.DataSize[i]);
|
||||
delete[] readBuffer;
|
||||
uint32_t ramPowerOnState = LoadInt(_settings, MovieKeys::RamPowerOnState);
|
||||
if(ramPowerOnState == 0xFF) {
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllOnes);
|
||||
} else {
|
||||
EmulationSettings::SetRamPowerOnState(RamPowerOnState::AllZeros);
|
||||
}
|
||||
|
||||
EmulationSettings::SetZapperDetectionRadius(LoadInt(_settings, MovieKeys::ZapperDetectionRadius));
|
||||
|
||||
uint32_t cpuClockRate = LoadInt(_settings, MovieKeys::CpuClockRate);
|
||||
if(cpuClockRate != 100) {
|
||||
bool adjustApu = LoadBool(_settings, MovieKeys::OverclockAdjustApu);
|
||||
EmulationSettings::SetOverclockRate(cpuClockRate, adjustApu);
|
||||
} else {
|
||||
EmulationSettings::SetOverclockRate(100, true);
|
||||
}
|
||||
|
||||
EmulationSettings::SetPpuNmiConfig(
|
||||
LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi),
|
||||
LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi)
|
||||
);
|
||||
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisablePpu2004Reads, LoadBool(_settings, MovieKeys::DisablePpu2004Reads));
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisablePaletteRead, LoadBool(_settings, MovieKeys::DisablePaletteRead));
|
||||
EmulationSettings::SetFlagState(EmulationFlags::DisableOamAddrBug, LoadBool(_settings, MovieKeys::DisableOamAddrBug));
|
||||
EmulationSettings::SetFlagState(EmulationFlags::UseNes101Hvc101Behavior, LoadBool(_settings, MovieKeys::UseNes101Hvc101Behavior));
|
||||
EmulationSettings::SetFlagState(EmulationFlags::EnableOamDecay, LoadBool(_settings, MovieKeys::EnableOamDecay));
|
||||
|
||||
//VS System flags
|
||||
EmulationSettings::SetPpuModel(FromString(LoadString(_settings, MovieKeys::PpuModel), PpuModelNames, PpuModel::Ppu2C02));
|
||||
EmulationSettings::SetDipSwitches(HexUtilities::FromHex(LoadString(_settings, MovieKeys::DipSwitches)));
|
||||
|
||||
LoadCheats();
|
||||
}
|
||||
|
||||
uint32_t MesenMovie::LoadInt(std::unordered_map<string, string> &settings, string name)
|
||||
{
|
||||
auto result = settings.find(name);
|
||||
if(result != settings.end()) {
|
||||
try {
|
||||
return (uint32_t)std::stoul(result->second);
|
||||
} catch(std::exception ex) {
|
||||
MessageManager::Log("[Movies] Invalid value for tag: " + name);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
MessageManager::DisplayMessage("Movies", "MovieMissingRom", romFilename);
|
||||
return 0;
|
||||
}
|
||||
delete[] romFilename;
|
||||
}
|
||||
|
||||
return loadedGame;
|
||||
}
|
||||
bool MesenMovie::LoadBool(std::unordered_map<string, string> &settings, string name)
|
||||
{
|
||||
auto result = settings.find(name);
|
||||
if(result != settings.end()) {
|
||||
if(result->second == "true") {
|
||||
return true;
|
||||
} else if(result->second == "false") {
|
||||
return false;
|
||||
} else {
|
||||
MessageManager::Log("[Movies] Invalid value for tag: " + name);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string MesenMovie::LoadString(std::unordered_map<string, string> &settings, string name)
|
||||
{
|
||||
auto result = settings.find(name);
|
||||
if(result != settings.end()) {
|
||||
return result->second;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void MesenMovie::LoadCheats()
|
||||
{
|
||||
vector<CodeInfo> cheats;
|
||||
for(string cheatData : _cheats) {
|
||||
CodeInfo code;
|
||||
if(LoadCheat(cheatData, code)) {
|
||||
cheats.push_back(code);
|
||||
}
|
||||
}
|
||||
CheatManager::SetCheats(cheats);
|
||||
}
|
||||
|
||||
bool MesenMovie::LoadCheat(string cheatData, CodeInfo &code)
|
||||
{
|
||||
vector<string> data = StringUtilities::Split(cheatData, ' ');
|
||||
|
||||
if(data.size() >= 3) {
|
||||
uint32_t address = HexUtilities::FromHex(data[0]);
|
||||
uint8_t value = HexUtilities::FromHex(data[1]);
|
||||
bool relativeAddress = data[2] == "true";
|
||||
int32_t compareValue = data.size() > 3 ? HexUtilities::FromHex(data[3]) : -1;
|
||||
|
||||
code.Address = address;
|
||||
code.Value = value;
|
||||
code.IsRelativeAddress = relativeAddress;
|
||||
code.CompareValue = compareValue;
|
||||
return true;
|
||||
} else {
|
||||
MessageManager::Log("[Movie] Invalid cheat definition: " + cheatData);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3,46 +3,45 @@
|
||||
#include "stdafx.h"
|
||||
#include "CheatManager.h"
|
||||
#include "MovieManager.h"
|
||||
#include "ControlManager.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "VirtualFile.h"
|
||||
|
||||
struct MovieData
|
||||
{
|
||||
uint32_t SaveStateSize = 0;
|
||||
uint32_t DataSize[4];
|
||||
vector<uint16_t> PortData[4];
|
||||
};
|
||||
class ZipReader;
|
||||
|
||||
class MesenMovie : public IMovie
|
||||
class MesenMovie : public IMovie, public IBatteryProvider, public std::enable_shared_from_this<MesenMovie>
|
||||
{
|
||||
private:
|
||||
const uint32_t MovieFormatVersion = 5;
|
||||
bool _recording = false;
|
||||
VirtualFile _movieFile;
|
||||
shared_ptr<ZipReader> _reader;
|
||||
bool _playing = false;
|
||||
uint8_t _counter[4];
|
||||
uint8_t _lastState[4];
|
||||
uint32_t _readPosition[4];
|
||||
ofstream _file;
|
||||
size_t _readIndex = 0;
|
||||
size_t _deviceIndex = 0;
|
||||
vector<vector<string>> _inputData;
|
||||
vector<string> _cheats;
|
||||
std::unordered_map<string, string> _settings;
|
||||
string _filename;
|
||||
stringstream _startState;
|
||||
MovieData _data;
|
||||
vector<CodeInfo> _cheatList;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
bool Save();
|
||||
void ParseSettings(stringstream &data);
|
||||
void ApplySettings();
|
||||
bool LoadGame();
|
||||
void Stop();
|
||||
bool Load(std::stringstream &file, bool autoLoadRom);
|
||||
|
||||
protected:
|
||||
void PushState(uint8_t port);
|
||||
void Record(string filename, bool reset);
|
||||
bool Play(stringstream &filestream, bool autoLoadRom);
|
||||
|
||||
bool IsPlaying();
|
||||
bool IsRecording();
|
||||
uint32_t LoadInt(std::unordered_map<string, string> &settings, string name);
|
||||
bool LoadBool(std::unordered_map<string, string> &settings, string name);
|
||||
string LoadString(std::unordered_map<string, string> &settings, string name);
|
||||
void LoadCheats();
|
||||
bool LoadCheat(string cheatData, CodeInfo &code);
|
||||
|
||||
public:
|
||||
MesenMovie();
|
||||
~MesenMovie();
|
||||
|
||||
void RecordState(uint8_t port, uint8_t state);
|
||||
uint8_t GetState(uint8_t port);
|
||||
bool Play(VirtualFile &file) override;
|
||||
bool SetInput(BaseControlDevice* device) override;
|
||||
bool IsPlaying();
|
||||
|
||||
// Inherited via IBatteryProvider
|
||||
virtual vector<uint8_t> LoadBattery(string extension) override;
|
||||
};
|
@ -1,24 +1,25 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "NetMessage.h"
|
||||
#include "ControlDeviceState.h"
|
||||
|
||||
class MovieDataMessage : public NetMessage
|
||||
{
|
||||
private:
|
||||
uint8_t _portNumber;
|
||||
uint8_t _inputState;
|
||||
ControlDeviceState _inputState;
|
||||
|
||||
protected:
|
||||
virtual void ProtectedStreamState()
|
||||
{
|
||||
Stream<uint8_t>(_portNumber);
|
||||
Stream<uint8_t>(_inputState);
|
||||
StreamArray(_inputState.State);
|
||||
}
|
||||
|
||||
public:
|
||||
MovieDataMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
|
||||
|
||||
MovieDataMessage(uint8_t state, uint8_t port) : NetMessage(MessageType::MovieData)
|
||||
MovieDataMessage(ControlDeviceState state, uint8_t port) : NetMessage(MessageType::MovieData)
|
||||
{
|
||||
_portNumber = port;
|
||||
_inputState = state;
|
||||
@ -29,7 +30,7 @@ public:
|
||||
return _portNumber;
|
||||
}
|
||||
|
||||
uint8_t GetInputState()
|
||||
ControlDeviceState GetInputState()
|
||||
{
|
||||
return _inputState;
|
||||
}
|
||||
|
@ -4,95 +4,68 @@
|
||||
#include "MesenMovie.h"
|
||||
#include "BizhawkMovie.h"
|
||||
#include "FceuxMovie.h"
|
||||
#include "MovieRecorder.h"
|
||||
#include "VirtualFile.h"
|
||||
|
||||
shared_ptr<IMovie> MovieManager::_instance;
|
||||
shared_ptr<IMovie> MovieManager::_player;
|
||||
shared_ptr<MovieRecorder> MovieManager::_recorder;
|
||||
|
||||
void MovieManager::Record(string filename, bool reset)
|
||||
{
|
||||
shared_ptr<IMovie> movie(new MesenMovie());
|
||||
movie->Record(filename, reset);
|
||||
_instance = movie;
|
||||
}
|
||||
|
||||
void MovieManager::Play(string filename)
|
||||
{
|
||||
ifstream file(filename, ios::in | ios::binary);
|
||||
if(file.good()) {
|
||||
std::stringstream ss;
|
||||
ss << file.rdbuf();
|
||||
file.close();
|
||||
if(MovieManager::Play(ss, true)) {
|
||||
MessageManager::DisplayMessage("Movies", "MoviePlaying", FolderUtilities::GetFilename(filename, true));
|
||||
}
|
||||
shared_ptr<MovieRecorder> recorder(new MovieRecorder());
|
||||
if(recorder->Record(filename, reset)) {
|
||||
_recorder = recorder;
|
||||
}
|
||||
}
|
||||
|
||||
bool MovieManager::Play(std::stringstream &filestream, bool autoLoadRom)
|
||||
void MovieManager::Play(VirtualFile file)
|
||||
{
|
||||
char header[3] = { };
|
||||
filestream.read(header, 3);
|
||||
filestream.seekg(0, ios::beg);
|
||||
vector<uint8_t> fileData;
|
||||
if(file.IsValid() && file.ReadFile(fileData)) {
|
||||
shared_ptr<IMovie> player;
|
||||
if(memcmp(fileData.data(), "MMO", 3) == 0) {
|
||||
//Old movie format, no longer supported
|
||||
MessageManager::DisplayMessage("Movies", "MovieIncompatibleVersion");
|
||||
} else if(memcmp(fileData.data(), "PK", 2) == 0) {
|
||||
//Mesen or Bizhawk movie
|
||||
ZipReader reader;
|
||||
reader.LoadArchive(fileData);
|
||||
|
||||
if(memcmp(header, "MMO", 3) == 0) {
|
||||
shared_ptr<IMovie> movie(new MesenMovie());
|
||||
if(movie->Play(filestream, autoLoadRom)) {
|
||||
_instance = movie;
|
||||
return true;
|
||||
vector<string> files = reader.GetFileList();
|
||||
if(std::find(files.begin(), files.end(), "GameSettings.txt") != files.end()) {
|
||||
player.reset(new MesenMovie());
|
||||
} else {
|
||||
player.reset(new BizhawkMovie());
|
||||
}
|
||||
} else if(memcmp(fileData.data(), "ver", 3) == 0) {
|
||||
player.reset(new FceuxMovie());
|
||||
}
|
||||
} else if(memcmp(header, "PK", 2) == 0) {
|
||||
shared_ptr<IMovie> movie(new BizhawkMovie());
|
||||
if(movie->Play(filestream, autoLoadRom)) {
|
||||
_instance = movie;
|
||||
return true;
|
||||
}
|
||||
} else if(memcmp(header, "ver", 3) == 0) {
|
||||
shared_ptr<IMovie> movie(new FceuxMovie());
|
||||
if(movie->Play(filestream, autoLoadRom)) {
|
||||
_instance = movie;
|
||||
return true;
|
||||
|
||||
if(player && player->Play(file)) {
|
||||
_player = player;
|
||||
|
||||
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MovieManager::Stop()
|
||||
{
|
||||
if(_instance && _instance->IsPlaying()) {
|
||||
MessageManager::DisplayMessage("Movies", "MovieEnded");
|
||||
_player.reset();
|
||||
|
||||
if(_recorder) {
|
||||
_recorder->Stop();
|
||||
_recorder.reset();
|
||||
}
|
||||
_instance.reset();
|
||||
}
|
||||
|
||||
bool MovieManager::Playing()
|
||||
{
|
||||
if(_instance) {
|
||||
return _instance->IsPlaying();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
shared_ptr<IMovie> player = _player;
|
||||
return player && player->IsPlaying();
|
||||
}
|
||||
|
||||
bool MovieManager::Recording()
|
||||
{
|
||||
if(_instance) {
|
||||
return _instance->IsRecording();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return _recorder != nullptr;
|
||||
}
|
||||
|
||||
void MovieManager::RecordState(uint8_t port, uint8_t value)
|
||||
{
|
||||
if(_instance) {
|
||||
_instance->RecordState(port, value);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MovieManager::GetState(uint8_t port)
|
||||
{
|
||||
if(_instance) {
|
||||
return _instance->GetState(port);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -2,8 +2,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "MessageManager.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "IInputProvider.h"
|
||||
|
||||
class IMovie
|
||||
class MovieRecorder;
|
||||
class VirtualFile;
|
||||
|
||||
class IMovie : public IInputProvider
|
||||
{
|
||||
protected:
|
||||
void EndMovie()
|
||||
@ -16,29 +20,20 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void RecordState(uint8_t port, uint8_t value) = 0;
|
||||
virtual uint8_t GetState(uint8_t port) = 0;
|
||||
|
||||
virtual void Record(string filename, bool reset) = 0;
|
||||
virtual bool Play(stringstream &filestream, bool autoLoadRom) = 0;
|
||||
|
||||
virtual bool IsRecording() = 0;
|
||||
virtual bool Play(VirtualFile &file) = 0;
|
||||
virtual bool IsPlaying() = 0;
|
||||
};
|
||||
|
||||
class MovieManager
|
||||
{
|
||||
private:
|
||||
static shared_ptr<IMovie> _instance;
|
||||
static shared_ptr<IMovie> _player;
|
||||
static shared_ptr<MovieRecorder> _recorder;
|
||||
|
||||
public:
|
||||
static void Record(string filename, bool reset);
|
||||
static void Play(string filename);
|
||||
static bool Play(std::stringstream &filestream, bool autoLoadRom);
|
||||
static void Play(VirtualFile file);
|
||||
static void Stop();
|
||||
static bool Playing();
|
||||
static bool Recording();
|
||||
|
||||
static void RecordState(uint8_t port, uint8_t value);
|
||||
static uint8_t GetState(uint8_t port);
|
||||
};
|
200
Core/MovieRecorder.cpp
Normal file
200
Core/MovieRecorder.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Utilities/ZipWriter.h"
|
||||
#include "MovieRecorder.h"
|
||||
#include "ControlManager.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "Console.h"
|
||||
#include "CheatManager.h"
|
||||
#include "VirtualFile.h"
|
||||
#include "SaveStateManager.h"
|
||||
|
||||
MovieRecorder::MovieRecorder()
|
||||
{
|
||||
}
|
||||
|
||||
MovieRecorder::~MovieRecorder()
|
||||
{
|
||||
}
|
||||
|
||||
bool MovieRecorder::Record(string filename, bool reset)
|
||||
{
|
||||
_filename = filename;
|
||||
_writer.reset(new ZipWriter());
|
||||
_inputData = stringstream();
|
||||
_saveStateData = stringstream();
|
||||
_hasSaveState = false;
|
||||
|
||||
if(!_writer->Initialize(_filename)) {
|
||||
_writer.reset();
|
||||
return false;
|
||||
} else {
|
||||
Console::Pause();
|
||||
|
||||
//TODO: Prevent game from loading battery from disk when not recording battery files
|
||||
BatteryManager::SetBatteryProvider(shared_from_this());
|
||||
|
||||
//Save existing battery files
|
||||
BatteryManager::SetBatteryRecorder(shared_from_this());
|
||||
ControlManager::RegisterInputRecorder(this);
|
||||
if(reset) {
|
||||
Console::GetInstance()->PowerCycle();
|
||||
} else {
|
||||
SaveStateManager::SaveState(_saveStateData);
|
||||
_hasSaveState = true;
|
||||
}
|
||||
BatteryManager::SetBatteryRecorder(nullptr);
|
||||
Console::Resume();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void MovieRecorder::GetGameSettings(stringstream &out)
|
||||
{
|
||||
NesModel model = Console::GetModel();
|
||||
|
||||
WriteString(out, MovieKeys::MesenVersion, EmulationSettings::GetMesenVersionString());
|
||||
WriteInt(out, MovieKeys::MovieFormatVersion, MovieRecorder::MovieFormatVersion);
|
||||
|
||||
VirtualFile romFile = Console::GetRomPath();
|
||||
WriteString(out, MovieKeys::GameFile, romFile.GetFileName());
|
||||
WriteString(out, MovieKeys::Sha1, romFile.GetSha1Hash());
|
||||
|
||||
VirtualFile patchFile = Console::GetPatchFile();
|
||||
if(patchFile.IsValid()) {
|
||||
WriteString(out, MovieKeys::PatchFile, patchFile.GetFileName());
|
||||
WriteString(out, MovieKeys::PatchFileSha1, patchFile.GetSha1Hash());
|
||||
WriteString(out, MovieKeys::PatchedRomSha1, Console::GetHashInfo().Sha1Hash);
|
||||
}
|
||||
|
||||
switch(model) {
|
||||
case NesModel::NTSC: WriteString(out, MovieKeys::Region, "NTSC"); break;
|
||||
case NesModel::PAL: WriteString(out, MovieKeys::Region, "PAL"); break;
|
||||
case NesModel::Dendy: WriteString(out, MovieKeys::Region, "Dendy"); break;
|
||||
}
|
||||
|
||||
switch(EmulationSettings::GetConsoleType()) {
|
||||
case ConsoleType::Nes: WriteString(out, MovieKeys::ConsoleType, "NES"); break;
|
||||
case ConsoleType::Famicom: WriteString(out, MovieKeys::ConsoleType, "Famicom"); break;
|
||||
}
|
||||
|
||||
WriteString(out, MovieKeys::Controller1, ControllerTypeNames[(int)EmulationSettings::GetControllerType(0)]);
|
||||
WriteString(out, MovieKeys::Controller2, ControllerTypeNames[(int)EmulationSettings::GetControllerType(1)]);
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::HasFourScore)) {
|
||||
WriteString(out, MovieKeys::Controller3, ControllerTypeNames[(int)EmulationSettings::GetControllerType(2)]);
|
||||
WriteString(out, MovieKeys::Controller4, ControllerTypeNames[(int)EmulationSettings::GetControllerType(3)]);
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetConsoleType() == ConsoleType::Famicom) {
|
||||
WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)EmulationSettings::GetExpansionDevice()]);
|
||||
}
|
||||
|
||||
WriteInt(out, MovieKeys::CpuClockRate, EmulationSettings::GetOverclockRateSetting());
|
||||
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, EmulationSettings::GetPpuExtraScanlinesBeforeNmi());
|
||||
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, EmulationSettings::GetPpuExtraScanlinesAfterNmi());
|
||||
|
||||
if(EmulationSettings::GetOverclockRateSetting() != 100) {
|
||||
WriteBool(out, MovieKeys::OverclockAdjustApu, EmulationSettings::GetOverclockAdjustApu());
|
||||
}
|
||||
WriteBool(out, MovieKeys::DisablePpu2004Reads, EmulationSettings::CheckFlag(EmulationFlags::DisablePpu2004Reads));
|
||||
WriteBool(out, MovieKeys::DisablePaletteRead, EmulationSettings::CheckFlag(EmulationFlags::DisablePaletteRead));
|
||||
WriteBool(out, MovieKeys::DisableOamAddrBug, EmulationSettings::CheckFlag(EmulationFlags::DisableOamAddrBug));
|
||||
WriteBool(out, MovieKeys::UseNes101Hvc101Behavior, EmulationSettings::CheckFlag(EmulationFlags::UseNes101Hvc101Behavior));
|
||||
WriteBool(out, MovieKeys::EnableOamDecay, EmulationSettings::CheckFlag(EmulationFlags::EnableOamDecay));
|
||||
WriteBool(out, MovieKeys::DisablePpuReset, EmulationSettings::CheckFlag(EmulationFlags::DisablePpuReset));
|
||||
|
||||
WriteInt(out, MovieKeys::ZapperDetectionRadius, EmulationSettings::GetZapperDetectionRadius());
|
||||
|
||||
switch(EmulationSettings::GetRamPowerOnState()) {
|
||||
case RamPowerOnState::AllZeros: WriteInt(out, MovieKeys::RamPowerOnState, 0x00); break;
|
||||
case RamPowerOnState::AllOnes: WriteInt(out, MovieKeys::RamPowerOnState, 0xFF); break;
|
||||
case RamPowerOnState::Random: WriteInt(out, MovieKeys::RamPowerOnState, -1); break; //TODO: Shouldn't be used for movies
|
||||
}
|
||||
|
||||
//VS System flags
|
||||
if(Console::GetInstance()->GetAvailableFeatures() == ConsoleFeatures::VsSystem) {
|
||||
WriteString(out, MovieKeys::DipSwitches, HexUtilities::ToHex(EmulationSettings::GetDipSwitches()));
|
||||
WriteString(out, MovieKeys::PpuModel, PpuModelNames[(int)EmulationSettings::GetPpuModel()]);
|
||||
}
|
||||
|
||||
for(CodeInfo &code : CheatManager::GetCheats()) {
|
||||
WriteCheat(out, code);
|
||||
}
|
||||
}
|
||||
|
||||
void MovieRecorder::WriteCheat(stringstream &out, CodeInfo &code)
|
||||
{
|
||||
out << "Cheat " <<
|
||||
HexUtilities::ToHex(code.Address) << " " <<
|
||||
HexUtilities::ToHex(code.Value) << " " <<
|
||||
(code.IsRelativeAddress ? "true" : "false") << " " <<
|
||||
(code.CompareValue < 0 ? HexUtilities::ToHex((uint8_t)code.CompareValue) : "") << "\n";
|
||||
}
|
||||
|
||||
void MovieRecorder::WriteString(stringstream &out, string name, string value)
|
||||
{
|
||||
out << name << " " << value << "\n";
|
||||
}
|
||||
|
||||
void MovieRecorder::WriteInt(stringstream &out, string name, uint32_t value)
|
||||
{
|
||||
out << name << " " << std::to_string(value) << "\n";
|
||||
}
|
||||
|
||||
void MovieRecorder::WriteBool(stringstream &out, string name, bool enabled)
|
||||
{
|
||||
out << name << " " << (enabled ? "true" : "false") << "\n";
|
||||
}
|
||||
|
||||
bool MovieRecorder::Stop()
|
||||
{
|
||||
if(_writer) {
|
||||
ControlManager::UnregisterInputRecorder(this);
|
||||
|
||||
_writer->AddFile(_inputData, "Input.txt");
|
||||
|
||||
stringstream out;
|
||||
GetGameSettings(out);
|
||||
_writer->AddFile(out, "GameSettings.txt");
|
||||
|
||||
VirtualFile patchFile = Console::GetPatchFile();
|
||||
vector<uint8_t> patchData;
|
||||
if(patchFile.IsValid() && patchFile.ReadFile(patchData)) {
|
||||
_writer->AddFile(patchData, "PatchData.dat");
|
||||
}
|
||||
|
||||
if(_hasSaveState) {
|
||||
_writer->AddFile(_saveStateData, "SaveState.mst");
|
||||
}
|
||||
|
||||
for(auto kvp : _batteryData) {
|
||||
_writer->AddFile(kvp.second, "Battery" + kvp.first);
|
||||
}
|
||||
|
||||
return _writer->Save();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MovieRecorder::RecordInput(BaseControlDevice *device)
|
||||
{
|
||||
_inputData << ("|" + device->GetTextState());
|
||||
}
|
||||
|
||||
void MovieRecorder::EndFrame()
|
||||
{
|
||||
_inputData << "\n";
|
||||
}
|
||||
|
||||
void MovieRecorder::OnLoadBattery(string extension, vector<uint8_t> batteryData)
|
||||
{
|
||||
_batteryData[extension] = batteryData;
|
||||
}
|
||||
|
||||
vector<uint8_t> MovieRecorder::LoadBattery(string extension)
|
||||
{
|
||||
return vector<uint8_t>();
|
||||
}
|
74
Core/MovieRecorder.h
Normal file
74
Core/MovieRecorder.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <unordered_map>
|
||||
#include "IInputRecorder.h"
|
||||
#include "CheatManager.h"
|
||||
#include "BatteryManager.h"
|
||||
|
||||
class ZipWriter;
|
||||
|
||||
class MovieRecorder : public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this<MovieRecorder>
|
||||
{
|
||||
private:
|
||||
static const uint32_t MovieFormatVersion = 1;
|
||||
string _filename;
|
||||
unique_ptr<ZipWriter> _writer;
|
||||
std::unordered_map<string, vector<uint8_t>> _batteryData;
|
||||
stringstream _inputData;
|
||||
bool _hasSaveState;
|
||||
stringstream _saveStateData;
|
||||
|
||||
void GetGameSettings(stringstream &out);
|
||||
void WriteCheat(stringstream &out, CodeInfo &code);
|
||||
void WriteString(stringstream &out, string name, string value);
|
||||
void WriteInt(stringstream &out, string name, uint32_t value);
|
||||
void WriteBool(stringstream &out, string name, bool enabled);
|
||||
|
||||
public:
|
||||
MovieRecorder();
|
||||
~MovieRecorder();
|
||||
|
||||
bool Record(string filename, bool reset);
|
||||
bool Stop();
|
||||
|
||||
void RecordInput(BaseControlDevice *device) override;
|
||||
void EndFrame() override;
|
||||
|
||||
// Inherited via IBatteryRecorder
|
||||
virtual void OnLoadBattery(string extension, vector<uint8_t> batteryData) override;
|
||||
|
||||
// Inherited via IBatteryProvider
|
||||
virtual vector<uint8_t> LoadBattery(string extension) override;
|
||||
};
|
||||
|
||||
namespace MovieKeys
|
||||
{
|
||||
constexpr const char* MesenVersion = "MesenVersion";
|
||||
constexpr const char* MovieFormatVersion = "MovieFormatVersion";
|
||||
constexpr const char* GameFile = "GameFile";
|
||||
constexpr const char* Sha1 = "SHA1";
|
||||
constexpr const char* PatchFile = "PatchFile";
|
||||
constexpr const char* PatchFileSha1 = "PatchFileSHA1";
|
||||
constexpr const char* PatchedRomSha1 = "PatchedRomSHA1";
|
||||
constexpr const char* Region = "Region";
|
||||
constexpr const char* ConsoleType = "ConsoleType";
|
||||
constexpr const char* Controller1 = "Controller1";
|
||||
constexpr const char* Controller2 = "Controller2";
|
||||
constexpr const char* Controller3 = "Controller3";
|
||||
constexpr const char* Controller4 = "Controller4";
|
||||
constexpr const char* ExpansionDevice = "ExpansionDevice";
|
||||
constexpr const char* CpuClockRate = "CpuClockRate";
|
||||
constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi";
|
||||
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
|
||||
constexpr const char* OverclockAdjustApu = "OverclockAdjustApu";
|
||||
constexpr const char* DisablePpu2004Reads = "DisablePpu2004Reads";
|
||||
constexpr const char* DisablePaletteRead = "DisablePaletteRead";
|
||||
constexpr const char* DisableOamAddrBug = "DisableOamAddrBug";
|
||||
constexpr const char* UseNes101Hvc101Behavior = "UseNes101Hvc101Behavior";
|
||||
constexpr const char* EnableOamDecay = "EnableOamDecay";
|
||||
constexpr const char* DisablePpuReset = "DisablePpuReset";
|
||||
constexpr const char* ZapperDetectionRadius = "ZapperDetectionRadius";
|
||||
constexpr const char* RamPowerOnState = "RamPowerOnState";
|
||||
constexpr const char* PpuModel = "PpuModel";
|
||||
constexpr const char* DipSwitches = "DipSwitches";
|
||||
};
|
@ -58,6 +58,26 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void StreamArray(vector<uint8_t> &data)
|
||||
{
|
||||
uint32_t length = (uint32_t)data.size();
|
||||
Stream<uint32_t>(length);
|
||||
if(_sending) {
|
||||
uint8_t* bytes = (uint8_t*)data.data();
|
||||
for(uint32_t i = 0, len = length; i < len; i++) {
|
||||
_buffer.push_back(bytes[i]);
|
||||
_position++;
|
||||
}
|
||||
} else {
|
||||
data.resize(length, 0);
|
||||
uint8_t* bytes = (uint8_t*)data.data();
|
||||
for(uint32_t i = 0, len = length; i < len; i++) {
|
||||
bytes[i] = _buffer[_position];
|
||||
_position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState()
|
||||
{
|
||||
Stream<MessageType>(_type);
|
||||
|
@ -448,3 +448,8 @@ NsfHeader NsfMapper::GetNsfHeader()
|
||||
{
|
||||
return _nsfHeader;
|
||||
}
|
||||
|
||||
ConsoleFeatures NsfMapper::GetAvailableFeatures()
|
||||
{
|
||||
return ConsoleFeatures::Nsf;
|
||||
}
|
@ -121,6 +121,7 @@ public:
|
||||
static NsfMapper* GetInstance();
|
||||
|
||||
void SetNesModel(NesModel model) override;
|
||||
ConsoleFeatures GetAvailableFeatures() override;
|
||||
|
||||
void SelectTrack(uint8_t trackNumber);
|
||||
uint8_t GetCurrentTrack();
|
||||
|
@ -15,8 +15,5 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
NsfPpu(BaseMapper* mapper) : PPU(mapper)
|
||||
{
|
||||
|
||||
}
|
||||
using PPU::PPU;
|
||||
};
|
@ -1,86 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "OekaKidsTablet.h"
|
||||
#include "ControlManager.h"
|
||||
#include "GameServerConnection.h"
|
||||
#include "IKeyManager.h"
|
||||
#include "IKeyManager.h"
|
||||
|
||||
void OekaKidsTablet::StreamState(bool saving)
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_xPosition, _yPosition, _touch, _click);
|
||||
}
|
||||
|
||||
uint32_t OekaKidsTablet::GetNetPlayState()
|
||||
{
|
||||
//Used by netplay
|
||||
uint32_t state = _xPosition | (_yPosition << 8);
|
||||
|
||||
if(_touch) {
|
||||
state |= 0x40000000;
|
||||
}
|
||||
|
||||
if(_click) {
|
||||
state |= 0x80000000;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
uint8_t OekaKidsTablet::ProcessNetPlayState(uint32_t netplayState)
|
||||
{
|
||||
_xPosition = netplayState & 0xFF;
|
||||
_yPosition = (netplayState >> 8) & 0xFF;
|
||||
|
||||
_touch = ((netplayState >> 30) & 0x01) == 0x01;
|
||||
_click = ((netplayState >> 31) & 0x01) == 0x01;
|
||||
|
||||
return RefreshState();
|
||||
}
|
||||
|
||||
uint8_t OekaKidsTablet::RefreshState()
|
||||
{
|
||||
if(_strobe) {
|
||||
if(_shift) {
|
||||
return (_stateBuffer & 0x40000) ? 0x00 : 0x08;
|
||||
} else {
|
||||
return 0x04;
|
||||
}
|
||||
} else {
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t OekaKidsTablet::GetPortOutput()
|
||||
{
|
||||
return GetControlState();
|
||||
}
|
||||
|
||||
void OekaKidsTablet::WriteRam(uint8_t value)
|
||||
{
|
||||
_strobe = (value & 0x01) == 0x01;
|
||||
bool shift = ((value >> 1) & 0x01) == 0x01;
|
||||
|
||||
if(_strobe) {
|
||||
if(!_shift && shift) {
|
||||
_stateBuffer <<= 1;
|
||||
}
|
||||
_shift = shift;
|
||||
} else {
|
||||
if(!GameServerConnection::GetNetPlayDevice(_port)) {
|
||||
MousePosition position = ControlManager::GetMousePosition();
|
||||
_xPosition = (int32_t)((double)std::max(0, position.X + 8) / 256.0 * 240);
|
||||
_yPosition = (int32_t)((double)std::max(0, position.Y - 14) / 240.0 * 256);
|
||||
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::InBackground) || EmulationSettings::CheckFlag(EmulationFlags::AllowBackgroundInput)) {
|
||||
_touch = position.Y >= 48 || ControlManager::IsMouseButtonPressed(MouseButton::LeftButton);
|
||||
_click = ControlManager::IsMouseButtonPressed(MouseButton::LeftButton);
|
||||
} else {
|
||||
_touch = false;
|
||||
_click = false;
|
||||
}
|
||||
}
|
||||
|
||||
_stateBuffer = ((_xPosition & 0xFF) << 10) | ((_yPosition & 0xFF) << 2) | (_touch ? 0x02 : 0x00) | (_click ? 0x01 : 0x00);
|
||||
}
|
||||
}
|
@ -9,21 +9,70 @@ private:
|
||||
bool _shift = false;
|
||||
uint32_t _stateBuffer = 0;
|
||||
|
||||
bool _click = false;
|
||||
bool _touch = false;
|
||||
int32_t _xPosition = 0;
|
||||
int32_t _yPosition = 0;
|
||||
|
||||
protected:
|
||||
virtual uint8_t RefreshState() override;
|
||||
uint8_t ProcessNetPlayState(uint32_t netplayState) override;
|
||||
void StreamState(bool saving) override;
|
||||
enum Buttons { Click, Touch };
|
||||
bool HasCoordinates() override { return true; }
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "CT";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
if(EmulationSettings::InputEnabled()) {
|
||||
MousePosition pos = KeyManager::GetMousePosition();
|
||||
SetPressedState(Buttons::Click, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
SetPressedState(Buttons::Touch, pos.Y >= 48 || KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
SetCoordinates(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_strobe, _shift, _stateBuffer);
|
||||
}
|
||||
|
||||
public:
|
||||
using BaseControlDevice::BaseControlDevice;
|
||||
OekaKidsTablet() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
|
||||
{
|
||||
}
|
||||
|
||||
virtual uint8_t GetPortOutput() override;
|
||||
virtual uint32_t GetNetPlayState() override;
|
||||
uint8_t ReadRAM(uint16_t addr)
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
if(_strobe) {
|
||||
if(_shift) {
|
||||
return (_stateBuffer & 0x40000) ? 0x00 : 0x08;
|
||||
} else {
|
||||
return 0x04;
|
||||
}
|
||||
} else {
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRam(uint8_t value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value)
|
||||
{
|
||||
_strobe = (value & 0x01) == 0x01;
|
||||
bool shift = ((value >> 1) & 0x01) == 0x01;
|
||||
|
||||
if(_strobe) {
|
||||
if(!_shift && shift) {
|
||||
_stateBuffer <<= 1;
|
||||
}
|
||||
_shift = shift;
|
||||
} else {
|
||||
MousePosition pos = GetCoordinates();
|
||||
|
||||
uint8_t xPosition = (uint8_t)((double)std::max(0, pos.X + 8) / 256.0 * 240);
|
||||
uint8_t yPosition = (uint8_t)((double)std::max(0, pos.Y - 14) / 240.0 * 256);
|
||||
|
||||
_stateBuffer = (xPosition << 10) | (yPosition << 2) | (IsPressed(OekaKidsTablet::Buttons::Touch) ? 0x02 : 0x00) | (IsPressed(OekaKidsTablet::Buttons::Click) ? 0x01 : 0x00);
|
||||
}
|
||||
}
|
||||
};
|
@ -7,16 +7,18 @@
|
||||
#include "Debugger.h"
|
||||
#include "BaseMapper.h"
|
||||
#include "RewindManager.h"
|
||||
#include "ControlManager.h"
|
||||
|
||||
PPU* PPU::Instance = nullptr;
|
||||
|
||||
PPU::PPU(BaseMapper *mapper)
|
||||
PPU::PPU(BaseMapper *mapper, ControlManager *controlManager)
|
||||
{
|
||||
PPU::Instance = this;
|
||||
|
||||
EmulationSettings::SetPpuModel(PpuModel::Ppu2C02);
|
||||
|
||||
_mapper = mapper;
|
||||
_controlManager = controlManager;
|
||||
_outputBuffers[0] = new uint16_t[256 * 240];
|
||||
_outputBuffers[1] = new uint16_t[256 * 240];
|
||||
|
||||
@ -1075,6 +1077,10 @@ void PPU::Exec()
|
||||
Debugger::ProcessPpuCycle();
|
||||
|
||||
UpdateApuStatus();
|
||||
|
||||
if(_scanline == EmulationSettings::GetInputPollScanline()) {
|
||||
_controlManager->UpdateInputState();
|
||||
}
|
||||
|
||||
//Cycle = 0
|
||||
if(_scanline == -1) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
enum class NesModel;
|
||||
|
||||
class BaseMapper;
|
||||
class ControlManager;
|
||||
|
||||
enum PPURegisters
|
||||
{
|
||||
@ -100,6 +101,8 @@ class PPU : public IMemoryHandler, public Snapshotable
|
||||
|
||||
int32_t _oamDecayCycles[0x40];
|
||||
bool _enableOamDecay;
|
||||
|
||||
ControlManager *_controlManager;
|
||||
|
||||
void UpdateStatusFlag();
|
||||
|
||||
@ -158,7 +161,7 @@ class PPU : public IMemoryHandler, public Snapshotable
|
||||
static const uint32_t PixelCount = 256*240;
|
||||
static const uint32_t OutputBufferSize = 256*240*2;
|
||||
|
||||
PPU(BaseMapper *mapper);
|
||||
PPU(BaseMapper *mapper, ControlManager *controlManager);
|
||||
virtual ~PPU();
|
||||
|
||||
void Reset();
|
||||
|
60
Core/PachinkoController.h
Normal file
60
Core/PachinkoController.h
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "StandardController.h"
|
||||
|
||||
class PachinkoController : public StandardController
|
||||
{
|
||||
private:
|
||||
uint8_t _analogData = 0;
|
||||
|
||||
protected:
|
||||
enum PachinkoButtons { Press = 8, Release = 9 };
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
StandardController::InternalSetStateFromInput();
|
||||
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
SetPressedState(PachinkoButtons::Press, keyMapping.PachinkoButtons[0]);
|
||||
SetPressedState(PachinkoButtons::Release, keyMapping.PachinkoButtons[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
PachinkoController(KeyMappingSet keyMappings) : StandardController(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr)
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4016) {
|
||||
output = (_stateBuffer & 0x01) << 1;
|
||||
_stateBuffer >>= 1;
|
||||
StrobeProcessRead();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
if(_analogData < 63 && IsPressed(PachinkoController::PachinkoButtons::Press)) {
|
||||
_analogData++;
|
||||
} else if(_analogData > 0 && IsPressed(PachinkoController::PachinkoButtons::Release)) {
|
||||
_analogData--;
|
||||
}
|
||||
|
||||
uint8_t analogData =
|
||||
((_analogData & 0x01) << 7) |
|
||||
((_analogData & 0x02) << 5) |
|
||||
((_analogData & 0x04) << 3) |
|
||||
((_analogData & 0x08) << 1) |
|
||||
((_analogData & 0x10) >> 1) |
|
||||
((_analogData & 0x20) >> 3) |
|
||||
((_analogData & 0x40) >> 5) |
|
||||
((_analogData & 0x80) >> 7);
|
||||
|
||||
StandardController::RefreshStateBuffer();
|
||||
_stateBuffer = (_stateBuffer & 0xFF) | (~analogData << 8);
|
||||
}
|
||||
};
|
73
Core/PartyTap.h
Normal file
73
Core/PartyTap.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class PartyTap : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint8_t _stateBuffer = 0;
|
||||
uint8_t _readCount = 0;
|
||||
bool _enabled = false;
|
||||
|
||||
protected:
|
||||
enum Buttons { B1 = 0, B2, B3, B4, B5, B6 };
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "123456";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
for(int i = 0; i < 6; i++) {
|
||||
SetPressedState(i, keyMapping.PartyTapButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_stateBuffer, _readCount, _enabled);
|
||||
}
|
||||
|
||||
public:
|
||||
PartyTap(KeyMappingSet keyMappings) : BaseControlDevice(BaseControlDevice::ExpDevicePort, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
if(addr == 0x4017) {
|
||||
if(_readCount < 2) {
|
||||
uint8_t value = (_stateBuffer & 0x7) << 2;
|
||||
_stateBuffer >>= 3;
|
||||
StrobeProcessRead();
|
||||
_readCount++;
|
||||
return value;
|
||||
} else {
|
||||
//"After 1st/2nd reads, a detection value can be read : $4017 & $1C == $14"
|
||||
return 0x14;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
_readCount = 0;
|
||||
_stateBuffer =
|
||||
IsPressed(PartyTap::Buttons::B1) ? 1 : 0 |
|
||||
IsPressed(PartyTap::Buttons::B2) ? 2 : 0 |
|
||||
IsPressed(PartyTap::Buttons::B3) ? 4 : 0 |
|
||||
IsPressed(PartyTap::Buttons::B4) ? 8 : 0 |
|
||||
IsPressed(PartyTap::Buttons::B5) ? 16 : 0 |
|
||||
IsPressed(PartyTap::Buttons::B6) ? 32 : 0;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
71
Core/PowerPad.h
Normal file
71
Core/PowerPad.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class PowerPad : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
uint8_t _stateBufferL;
|
||||
uint8_t _stateBufferH;
|
||||
|
||||
protected:
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "123456789ABC";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
for(int i = 0; i < 12; i++) {
|
||||
SetPressedState(i, keyMapping.PowerPadButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
uint8_t pressedKeys[12] = {};
|
||||
for(int i = 0; i < 12; i++) {
|
||||
pressedKeys[i] |= IsPressed(i) ? 1 : 0;
|
||||
}
|
||||
|
||||
//"Serial data from buttons 2, 1, 5, 9, 6, 10, 11, 7"
|
||||
_stateBufferL = pressedKeys[1] | (pressedKeys[0] << 1) | (pressedKeys[4] << 2) | (pressedKeys[8] << 3) | (pressedKeys[5] << 4) | (pressedKeys[9] << 5) | (pressedKeys[10] << 6) | (pressedKeys[6] << 7);
|
||||
|
||||
//"Serial data from buttons 4, 3, 12, 8 (following 4 bits read as H=1)"
|
||||
_stateBufferH = pressedKeys[3] | (pressedKeys[2] << 1) | (pressedKeys[11] << 2) | (pressedKeys[7] << 3) | 0xF0;
|
||||
}
|
||||
|
||||
void StreamState(bool saving) override
|
||||
{
|
||||
BaseControlDevice::StreamState(saving);
|
||||
Stream(_stateBufferL, _stateBufferH);
|
||||
}
|
||||
|
||||
public:
|
||||
PowerPad(uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(port, keyMappings)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
if(IsCurrentPort(addr)) {
|
||||
output = ((_stateBufferH & 0x01) << 4) | ((_stateBufferL & 0x01) << 3);
|
||||
_stateBufferL >>= 1;
|
||||
_stateBufferH >>= 1;
|
||||
|
||||
_stateBufferL |= 0x80;
|
||||
_stateBufferH |= 0x80;
|
||||
|
||||
StrobeProcessRead();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRAM(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
#include "Debugger.h"
|
||||
#include "MovieManager.h"
|
||||
#include "PPU.h"
|
||||
#include "VirtualFile.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Utilities/md5.h"
|
||||
#include "../Utilities/ZipWriter.h"
|
||||
@ -129,8 +130,12 @@ void RecordedRomTest::Record(string filename, bool reset)
|
||||
}
|
||||
}
|
||||
|
||||
void RecordedRomTest::RecordFromMovie(string testFilename, stringstream &movieStream, bool autoLoadRom)
|
||||
void RecordedRomTest::RecordFromMovie(string testFilename, VirtualFile movieFile)
|
||||
{
|
||||
if(!movieFile.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filename = testFilename;
|
||||
|
||||
string mrtFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(testFilename), FolderUtilities::GetFilename(testFilename, false) + ".mrt");
|
||||
@ -143,33 +148,21 @@ void RecordedRomTest::RecordFromMovie(string testFilename, stringstream &movieSt
|
||||
_recording = true;
|
||||
|
||||
//Start playing movie
|
||||
MovieManager::Play(movieStream, autoLoadRom);
|
||||
movieStream.seekg(0, ios::beg);
|
||||
_movieStream << movieStream.rdbuf();
|
||||
|
||||
MovieManager::Play(movieFile);
|
||||
movieFile.ReadFile(_movieData);
|
||||
_recordingFromMovie = true;
|
||||
|
||||
Console::Resume();
|
||||
}
|
||||
}
|
||||
|
||||
void RecordedRomTest::RecordFromMovie(string testFilename, string movieFilename)
|
||||
{
|
||||
stringstream ss;
|
||||
ifstream file(movieFilename, ios::in | ios::binary);
|
||||
if(file) {
|
||||
ss << file.rdbuf();
|
||||
file.close();
|
||||
RecordFromMovie(testFilename, ss, true);
|
||||
}
|
||||
}
|
||||
|
||||
void RecordedRomTest::RecordFromTest(string newTestFilename, string existingTestFilename)
|
||||
{
|
||||
ZipReader zipReader;
|
||||
zipReader.LoadArchive(existingTestFilename);
|
||||
std::stringstream testMovie = zipReader.GetStream("TestMovie.mmo");
|
||||
std::stringstream testRom = zipReader.GetStream("TestRom.nes");
|
||||
stringstream testMovie, testRom;
|
||||
zipReader.GetStream("TestMovie.mmo", testMovie);
|
||||
zipReader.GetStream("TestRom.nes", testRom);
|
||||
VirtualFile romFile(testRom, newTestFilename);
|
||||
|
||||
if(testMovie && testRom) {
|
||||
@ -178,7 +171,7 @@ void RecordedRomTest::RecordFromTest(string newTestFilename, string existingTest
|
||||
testRom.seekg(0, ios::beg);
|
||||
_romStream << testRom.rdbuf();
|
||||
|
||||
RecordFromMovie(newTestFilename, testMovie, false);
|
||||
RecordFromMovie(newTestFilename, VirtualFile(existingTestFilename, "TestMovie.mmo"));
|
||||
Console::Resume();
|
||||
}
|
||||
}
|
||||
@ -198,13 +191,16 @@ int32_t RecordedRomTest::Run(string filename)
|
||||
EmulationSettings::SetNesModel(NesModel::NTSC);
|
||||
}
|
||||
|
||||
VirtualFile testMovie(filename, "TestMovie.mmo");
|
||||
VirtualFile testRom(filename, "TestRom.nes");
|
||||
|
||||
ZipReader zipReader;
|
||||
zipReader.LoadArchive(filename);
|
||||
std::stringstream testData = zipReader.GetStream("TestData.mrt");
|
||||
std::stringstream testMovie = zipReader.GetStream("TestMovie.mmo");
|
||||
std::stringstream testRom = zipReader.GetStream("TestRom.nes");
|
||||
|
||||
stringstream testData;
|
||||
zipReader.GetStream("TestData.mrt", testData);
|
||||
|
||||
if(testData && testMovie && testRom) {
|
||||
if(testData && testMovie.IsValid() && testRom.IsValid()) {
|
||||
char header[3];
|
||||
testData.read((char*)&header, 3);
|
||||
if(memcmp((char*)&header, "MRT", 3) != 0) {
|
||||
@ -235,26 +231,10 @@ int32_t RecordedRomTest::Run(string filename)
|
||||
_currentCount = _repetitionCount.front();
|
||||
_repetitionCount.pop_front();
|
||||
|
||||
shared_ptr<ArchiveReader> reader = ArchiveReader::GetReader(testRom);
|
||||
if(reader) {
|
||||
//Some older test files contain a zip file instead of a rom file, grab the first rom we can find in the zip
|
||||
vector<string> files = reader->GetFileList({ ".nes" });
|
||||
vector<uint8_t> fileData;
|
||||
if(files.size() > 0 && reader->ExtractFile(files[0], fileData)) {
|
||||
testRom = std::stringstream();
|
||||
testRom.write((char*)fileData.data(), fileData.size());
|
||||
testRom.seekg(0, ios::beg);
|
||||
} else {
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFile testRomFile(testRom, filename);
|
||||
|
||||
//Start playing movie
|
||||
if(Console::LoadROM(testRomFile)) {
|
||||
if(Console::LoadROM(testRom)) {
|
||||
_runningTest = true;
|
||||
MovieManager::Play(testMovie, false);
|
||||
MovieManager::Play(testMovie);
|
||||
|
||||
Console::Resume();
|
||||
EmulationSettings::ClearFlags(EmulationFlags::Paused);
|
||||
@ -305,14 +285,15 @@ void RecordedRomTest::Save()
|
||||
|
||||
_file.close();
|
||||
|
||||
ZipWriter writer(_filename);
|
||||
ZipWriter writer;
|
||||
writer.Initialize(_filename);
|
||||
|
||||
string mrtFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(_filename), FolderUtilities::GetFilename(_filename, false) + ".mrt");
|
||||
writer.AddFile(mrtFilename, "TestData.mrt");
|
||||
std::remove(mrtFilename.c_str());
|
||||
|
||||
if(_recordingFromMovie) {
|
||||
writer.AddFile(_movieStream, "TestMovie.mmo");
|
||||
writer.AddFile(_movieData, "TestMovie.mmo");
|
||||
writer.AddFile(_romStream, "TestRom.nes");
|
||||
} else {
|
||||
string mmoFilename = FolderUtilities::CombinePath(FolderUtilities::GetFolderName(_filename), FolderUtilities::GetFilename(_filename, false) + ".mmo");
|
||||
@ -321,7 +302,7 @@ void RecordedRomTest::Save()
|
||||
|
||||
writer.AddFile(Console::GetRomPath().GetFilePath(), "TestRom.nes");
|
||||
}
|
||||
|
||||
writer.Save();
|
||||
|
||||
MessageManager::DisplayMessage("Test", "TestFileSavedTo", FolderUtilities::GetFilename(_filename, true));
|
||||
}
|
@ -5,6 +5,8 @@
|
||||
#include "INotificationListener.h"
|
||||
#include "../Utilities/AutoResetEvent.h"
|
||||
|
||||
class VirtualFile;
|
||||
|
||||
class RecordedRomTest : public INotificationListener
|
||||
{
|
||||
private:
|
||||
@ -18,8 +20,8 @@ private:
|
||||
std::deque<uint8_t> _repetitionCount;
|
||||
uint8_t _currentCount;
|
||||
|
||||
//Used when make a test out of an existing movie/test
|
||||
stringstream _movieStream;
|
||||
//Used when making a test out of an existing movie/test
|
||||
vector<uint8_t> _movieData;
|
||||
stringstream _romStream;
|
||||
|
||||
string _filename;
|
||||
@ -32,7 +34,6 @@ private:
|
||||
void ValidateFrame(uint16_t* ppuFrameBuffer);
|
||||
void SaveFrame(uint16_t* ppuFrameBuffer);
|
||||
void Save();
|
||||
void RecordFromMovie(string testFilename, stringstream &movieStream, bool autoLoadRom);
|
||||
|
||||
public:
|
||||
RecordedRomTest();
|
||||
@ -40,7 +41,7 @@ public:
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
void Record(string filename, bool reset);
|
||||
void RecordFromMovie(string testFilename, string movieFilename);
|
||||
void RecordFromMovie(string testFilename, VirtualFile movieFile);
|
||||
void RecordFromTest(string newTestFilename, string existingTestFilename);
|
||||
int32_t Run(string filename);
|
||||
void Stop();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <deque>
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
class RewindData
|
||||
{
|
||||
@ -11,7 +12,7 @@ private:
|
||||
void CompressState(string stateData, vector<uint8_t> &compressedState);
|
||||
|
||||
public:
|
||||
std::deque<uint8_t> InputLogs[4];
|
||||
std::deque<ControlDeviceState> InputLogs[BaseControlDevice::PortCount];
|
||||
int32_t FrameCount = 0;
|
||||
|
||||
void LoadState();
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "Console.h"
|
||||
#include "VideoRenderer.h"
|
||||
#include "SoundMixer.h"
|
||||
#include "BaseControlDevice.h"
|
||||
|
||||
RewindManager* RewindManager::_instance = nullptr;
|
||||
|
||||
@ -15,6 +16,8 @@ RewindManager::RewindManager()
|
||||
AddHistoryBlock();
|
||||
|
||||
MessageManager::RegisterNotificationListener(this);
|
||||
ControlManager::RegisterInputProvider(this);
|
||||
ControlManager::RegisterInputRecorder(this);
|
||||
}
|
||||
|
||||
RewindManager::~RewindManager()
|
||||
@ -23,6 +26,8 @@ RewindManager::~RewindManager()
|
||||
_instance = nullptr;
|
||||
}
|
||||
MessageManager::UnregisterNotificationListener(this);
|
||||
ControlManager::UnregisterInputProvider(this);
|
||||
ControlManager::UnregisterInputRecorder(this);
|
||||
}
|
||||
|
||||
void RewindManager::ClearBuffer()
|
||||
@ -262,21 +267,23 @@ bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount, ui
|
||||
}
|
||||
}
|
||||
|
||||
void RewindManager::RecordInput(uint8_t port, uint8_t input)
|
||||
void RewindManager::RecordInput(BaseControlDevice *device)
|
||||
{
|
||||
if(EmulationSettings::GetRewindBufferSize() > 0 && _instance && _instance->_rewindState == RewindState::Stopped) {
|
||||
_instance->_currentHistory.InputLogs[port].push_back(input);
|
||||
_instance->_currentHistory.InputLogs[device->GetPort()].push_back(device->GetRawState());
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t RewindManager::GetInput(uint8_t port)
|
||||
bool RewindManager::SetInput(BaseControlDevice *device)
|
||||
{
|
||||
if(!_instance->_currentHistory.InputLogs[port].empty()) {
|
||||
uint8_t value = _instance->_currentHistory.InputLogs[port].front();
|
||||
uint8_t port = device->GetPort();
|
||||
if(!_instance->_currentHistory.InputLogs[port].empty() && RewindManager::IsRewinding()) {
|
||||
ControlDeviceState state = _instance->_currentHistory.InputLogs[port].front();
|
||||
_instance->_currentHistory.InputLogs[port].pop_front();
|
||||
return value;
|
||||
device->SetRawState(state);
|
||||
return true;
|
||||
} else {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <deque>
|
||||
#include "INotificationListener.h"
|
||||
#include "RewindData.h"
|
||||
#include "IInputProvider.h"
|
||||
#include "IInputRecorder.h"
|
||||
|
||||
enum class RewindState
|
||||
{
|
||||
@ -13,7 +15,7 @@ enum class RewindState
|
||||
Debugging = 4
|
||||
};
|
||||
|
||||
class RewindManager : public INotificationListener
|
||||
class RewindManager : public INotificationListener, public IInputProvider, public IInputRecorder
|
||||
{
|
||||
private:
|
||||
static const uint32_t BufferSize = 30; //Number of frames between each save state
|
||||
@ -48,9 +50,9 @@ public:
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
void ProcessEndOfFrame();
|
||||
|
||||
static void RecordInput(uint8_t port, uint8_t input);
|
||||
static uint8_t GetInput(uint8_t port);
|
||||
|
||||
void RecordInput(BaseControlDevice *device) override;
|
||||
bool SetInput(BaseControlDevice *device) override;
|
||||
|
||||
static void StartRewinding(bool forDebugger = false);
|
||||
static void StopRewinding(bool forDebugger = false);
|
||||
|
@ -26,7 +26,7 @@ bool RomLoader::LoadFile(VirtualFile romFile)
|
||||
|
||||
bool RomLoader::LoadFile(string filename, vector<uint8_t> &fileData)
|
||||
{
|
||||
if(fileData.size() < 10) {
|
||||
if(fileData.size() < 15) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ string RomLoader::FindMatchingRomInFile(string filePath, HashInfo hashInfo)
|
||||
vector<uint8_t> fileData;
|
||||
if(loader.LoadFile(filePath)) {
|
||||
if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) {
|
||||
return filePath+"\n"+file;
|
||||
return VirtualFile(filePath, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,12 +123,15 @@ string RomLoader::FindMatchingRomInFile(string filePath, HashInfo hashInfo)
|
||||
string RomLoader::FindMatchingRom(vector<string> romFiles, string romFilename, HashInfo hashInfo, bool useFastSearch)
|
||||
{
|
||||
if(useFastSearch) {
|
||||
for(string romFile : romFiles) {
|
||||
string lcRomFile = romFilename;
|
||||
std::transform(lcRomFile.begin(), lcRomFile.end(), lcRomFile.begin(), ::tolower);
|
||||
|
||||
for(string currentFile : romFiles) {
|
||||
//Quick search by filename
|
||||
string lcRomFile = romFile;
|
||||
std::transform(lcRomFile.begin(), lcRomFile.end(), lcRomFile.begin(), ::tolower);
|
||||
if(FolderUtilities::GetFilename(lcRomFile, false).compare(FolderUtilities::GetFilename(romFilename, false)) == 0) {
|
||||
string match = RomLoader::FindMatchingRomInFile(romFile, hashInfo);
|
||||
string lcCurrentFile = currentFile;
|
||||
std::transform(lcCurrentFile.begin(), lcCurrentFile.end(), lcCurrentFile.begin(), ::tolower);
|
||||
if(FolderUtilities::GetFilename(lcRomFile, false).compare(FolderUtilities::GetFilename(lcCurrentFile, false)) == 0) {
|
||||
string match = RomLoader::FindMatchingRomInFile(currentFile, hashInfo);
|
||||
if(!match.empty()) {
|
||||
return match;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
|
||||
}
|
||||
}
|
||||
|
||||
Console::LoadState(stream);
|
||||
Console::LoadState(stream, fileFormatVersion);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -192,7 +192,8 @@ void SaveStateManager::SaveRecentGame(string romName, string romPath, string pat
|
||||
{
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::ConsoleMode) && !EmulationSettings::CheckFlag(EmulationFlags::DisableGameSelectionScreen) && Console::GetRomFormat() != RomFormat::Nsf) {
|
||||
string filename = FolderUtilities::GetFilename(Console::GetRomName(), false) + ".rgd";
|
||||
ZipWriter writer(FolderUtilities::CombinePath(FolderUtilities::GetRecentGamesFolder(), filename));
|
||||
ZipWriter writer;
|
||||
writer.Initialize(FolderUtilities::CombinePath(FolderUtilities::GetRecentGamesFolder(), filename));
|
||||
|
||||
std::stringstream pngStream;
|
||||
VideoDecoder::GetInstance()->TakeScreenshot(pngStream);
|
||||
@ -207,6 +208,7 @@ void SaveStateManager::SaveRecentGame(string romName, string romPath, string pat
|
||||
romInfoStream << romPath << std::endl;
|
||||
romInfoStream << patchPath << std::endl;
|
||||
writer.AddFile(romInfoStream, "RomInfo.txt");
|
||||
writer.Save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,8 +217,9 @@ void SaveStateManager::LoadRecentGame(string filename, bool resetGame)
|
||||
ZipReader reader;
|
||||
reader.LoadArchive(filename);
|
||||
|
||||
std::stringstream romInfoStream = reader.GetStream("RomInfo.txt");
|
||||
std::stringstream stateStream = reader.GetStream("Savestate.mst");
|
||||
stringstream romInfoStream, stateStream;
|
||||
reader.GetStream("RomInfo.txt", romInfoStream);
|
||||
reader.GetStream("Savestate.mst", stateStream);
|
||||
|
||||
string romName, romPath, patchPath;
|
||||
std::getline(romInfoStream, romName);
|
||||
|
@ -11,7 +11,7 @@ private:
|
||||
static string GetStateFilepath(int stateIndex);
|
||||
|
||||
public:
|
||||
static const uint32_t FileFormatVersion = 6;
|
||||
static const uint32_t FileFormatVersion = 7;
|
||||
|
||||
static uint64_t GetStateInfo(int stateIndex);
|
||||
|
||||
|
@ -15,7 +15,8 @@ int ScriptHost::GetScriptId()
|
||||
|
||||
const char* ScriptHost::GetLog()
|
||||
{
|
||||
return _context ? _context->GetLog() : "";
|
||||
shared_ptr<ScriptingContext> context = _context;
|
||||
return context ? context->GetLog() : "";
|
||||
}
|
||||
|
||||
bool ScriptHost::LoadScript(string scriptName, string scriptContent, Debugger* debugger)
|
||||
|
@ -36,7 +36,7 @@ protected:
|
||||
string _scriptName;
|
||||
|
||||
vector<int> _callbacks[5][0x10000];
|
||||
vector<int> _eventCallbacks[8];
|
||||
vector<int> _eventCallbacks[(int)EventType::EventTypeSize];
|
||||
|
||||
virtual void InternalCallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type) = 0;
|
||||
virtual int InternalCallEventCallback(EventType type) = 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user