BS-X: Improved emulation and support for .bin broadcast files

This commit is contained in:
Sour 2020-02-20 23:57:01 -05:00
parent cf909b56d8
commit b29fabbb57
21 changed files with 679 additions and 28 deletions

View file

@ -5,6 +5,7 @@
#include "MemoryMappings.h"
#include "BaseCartridge.h"
#include "BsxMemoryPack.h"
#include "BsxSatellaview.h"
#include "RamHandler.h"
#include "EmuSettings.h"
@ -18,10 +19,10 @@ BsxCart::BsxCart(Console* console, BsxMemoryPack* memPack)
mm->RegisterHandler(0x00, 0x0F, 0x5000, 0x5FFF, this);
mm->RegisterHandler(0x10, 0x1F, 0x5000, 0x5FFF, _console->GetCartridge()->GetSaveRamHandlers());
//Regular B Bus register handler, keep a reference to it, it'll be overwritten below
_bBusHandler = mm->GetHandler(0x2000);
mm->RegisterHandler(0x00, 0x3F, 0x2000, 0x2FFF, this);
mm->RegisterHandler(0x80, 0xBF, 0x2000, 0x2FFF, this);
//Override regular B-bus handler
_satellaview.reset(new BsxSatellaview(console, mm->GetHandler(0x2000)));
mm->RegisterHandler(0x00, 0x3F, 0x2000, 0x2FFF, _satellaview.get());
mm->RegisterHandler(0x80, 0xBF, 0x2000, 0x2FFF, _satellaview.get());
_psRamSize = 512 * 1024;
_psRam = new uint8_t[_psRamSize];
@ -41,17 +42,6 @@ BsxCart::~BsxCart()
uint8_t BsxCart::Read(uint32_t addr)
{
if((addr & 0x6000) == 0x2000) {
addr &= 0xFFFF;
if(addr >= 0x2188 && addr <= 0x219F) {
//Handle BS-X $2188-219F registers
return 0;
} else {
//Use regular B-bus handler for everything else
return _bBusHandler->Read(addr);
}
}
uint8_t openBus = _memoryManager->GetOpenBus();
if((addr & 0xFFFF) != 0x5000) {
return openBus;
@ -69,16 +59,7 @@ uint8_t BsxCart::Read(uint32_t addr)
void BsxCart::Write(uint32_t addr, uint8_t value)
{
if((addr & 0x6000) == 0x2000) {
addr &= 0xFFFF;
if(addr >= 0x2188 && addr <= 0x219F) {
//Handle BS-X register writes
} else {
//Regular B-bus handler
_bBusHandler->Write(addr, value);
}
return;
} else if((addr & 0xFFFF) != 0x5000) {
if((addr & 0xFFFF) != 0x5000) {
return;
}
@ -196,6 +177,8 @@ void BsxCart::Reset()
_dirty = false;
memcpy(_dirtyRegs, _regs, sizeof(_regs));
_satellaview->Reset();
UpdateMemoryMappings();
}
@ -205,6 +188,7 @@ void BsxCart::Serialize(Serializer& s)
ArrayInfo<uint8_t> regs = { _regs, 0x10 };
ArrayInfo<uint8_t> dirtyRegs = { _dirtyRegs, 0x10 };
s.Stream(psRam, regs, dirtyRegs, _dirty);
s.Stream(_satellaview.get());
if(!s.IsSaving()) {
UpdateMemoryMappings();

View file

@ -5,6 +5,7 @@
class Console;
class MemoryManager;
class BsxMemoryPack;
class BsxSatellaview;
class BsxCart : public BaseCoprocessor
{
@ -12,13 +13,12 @@ private:
Console* _console;
MemoryManager* _memoryManager;
BsxMemoryPack* _memPack;
unique_ptr<BsxSatellaview> _satellaview;
uint8_t* _psRam = nullptr;
uint32_t _psRamSize = 0;
vector<unique_ptr<IMemoryHandler>> _psRamHandlers;
IMemoryHandler* _bBusHandler = nullptr;
uint8_t _regs[0x10] = {};
uint8_t _dirtyRegs[0x10] = {};
bool _dirty = false;

101
Core/BsxSatellaview.cpp Normal file
View file

@ -0,0 +1,101 @@
#include "stdafx.h"
#include "BsxSatellaview.h"
#include "Console.h"
#include "EmuSettings.h"
#include "../Utilities/Serializer.h"
BsxSatellaview::BsxSatellaview(Console* console, IMemoryHandler* bBusHandler)
{
_customDate = console->GetSettings()->GetEmulationConfig().BsxCustomDate;
_bBusHandler = bBusHandler;
Reset();
}
void BsxSatellaview::Reset()
{
_streamReg = 0;
_extOutput = 0xFF;
_stream[0].Reset(_customDate);
_stream[1].Reset(_customDate);
}
uint8_t BsxSatellaview::Read(uint32_t addr)
{
addr &= 0xFFFF;
if(addr >= 0x2188 && addr <= 0x219F) {
//Handle BS-X $2188-219F registers
switch(addr) {
case 0x2188: return _stream[0].GetChannel() & 0xFF;
case 0x2189: return (_stream[0].GetChannel()) >> 8;
case 0x218A: return _stream[0].GetPrefixCount();
case 0x218B: return _stream[0].GetPrefix();
case 0x218C: return _stream[0].GetData();
case 0x218D: return _stream[0].GetStatus((_streamReg & 0x01) != 0);
case 0x218E: return _stream[1].GetChannel() & 0xFF;
case 0x218F: return (_stream[1].GetChannel()) >> 8;
case 0x2190: return _stream[1].GetPrefixCount();
case 0x2191: return _stream[1].GetPrefix();
case 0x2192: return _stream[1].GetData();
case 0x2193: return _stream[1].GetStatus((_streamReg & 0x01) != 0);
case 0x2194: return _streamReg; //LED and Stream register
case 0x2195: return 0; //Unknown
case 0x2196: return 0x10; //Satellaview status
case 0x2197: return _extOutput; //Soundlink / EXT output
case 0x2198: return 0x80; //Serial IO (Serial number)
case 0x2199: return 0x01; //Serial IO (???)
case 0x219A: return 0x10; //Unknown
}
}
//Use regular B-bus handler for everything else
return _bBusHandler->Read(addr);
}
void BsxSatellaview::Write(uint32_t addr, uint8_t value)
{
addr &= 0xFFFF;
if(addr >= 0x2188 && addr <= 0x219F) {
//Handle BS-X register writes
switch(addr) {
case 0x2188: _stream[0].SetChannelLow(value); break;
case 0x2189: _stream[0].SetChannelHigh(value); break;
case 0x218B: _stream[0].SetPrefixLatch(value); break;
case 0x218C: _stream[0].SetDataLatch(value); break;
case 0x218E: _stream[1].SetChannelLow(value); break;
case 0x218F: _stream[1].SetChannelHigh(value); break;
case 0x2191: _stream[1].SetPrefixLatch(value); break;
case 0x2192: _stream[1].SetDataLatch(value); break;
case 0x2194: _streamReg = value; break;
case 0x2197: _extOutput = value; break;
}
} else {
//Regular B-bus handler
_bBusHandler->Write(addr, value);
}
}
uint8_t BsxSatellaview::Peek(uint32_t addr)
{
return 0;
}
void BsxSatellaview::PeekBlock(uint32_t addr, uint8_t* output)
{
memset(output, 0, 0x1000);
}
AddressInfo BsxSatellaview::GetAbsoluteAddress(uint32_t address)
{
return { -1, SnesMemoryType::Register };
}
void BsxSatellaview::Serialize(Serializer& s)
{
s.Stream(_extOutput, _streamReg);
s.Stream(&_stream[0]);
s.Stream(&_stream[1]);
}

31
Core/BsxSatellaview.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "BsxStream.h"
#include "../Utilities/ISerializable.h"
class Console;
class BsxSatellaview : public IMemoryHandler, public ISerializable
{
private:
IMemoryHandler* _bBusHandler;
BsxStream _stream[2];
uint8_t _streamReg;
uint8_t _extOutput;
int64_t _customDate;
public:
BsxSatellaview(Console* console, IMemoryHandler *bBusHandler);
void Reset();
uint8_t Read(uint32_t addr) override;
uint8_t Peek(uint32_t addr) override;
void PeekBlock(uint32_t addr, uint8_t* output) override;
void Write(uint32_t addr, uint8_t value) override;
AddressInfo GetAbsoluteAddress(uint32_t address) override;
void Serialize(Serializer& s) override;
};

280
Core/BsxStream.cpp Normal file
View file

@ -0,0 +1,280 @@
#include "stdafx.h"
#include "BsxStream.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/Serializer.h"
BsxStream::BsxStream()
{
}
void BsxStream::Reset(int64_t customDate)
{
_file.close();
_channel = 0;
_prefix = 0;
_data = 0;
_status = 0;
_prefixLatch = false;
_dataLatch = false;
_firstPacket = false;
_fileOffset = 0;
_fileIndex = 0;
_queueLength = 0;
_prefixQueueLength = 0;
_dataQueueLength = 0;
_activeChannel = 0;
_activeFileIndex = 0;
_customDate = customDate;
time(&_resetTime);
_latchedTime = 0;
_tm = {};
}
uint16_t BsxStream::GetChannel()
{
return _channel;
}
void BsxStream::FillQueues()
{
//TODO: Make this run based on master clock
while(_queueLength > 0) {
_queueLength--;
if(_prefixLatch && _prefixQueueLength < 0x80) {
_prefixQueueLength++;
}
if(_dataLatch && _dataQueueLength < 0x80) {
_dataQueueLength++;
}
}
}
void BsxStream::OpenStreamFile()
{
_file.close();
string filename = "BSX" + HexUtilities::ToHex(_activeChannel) + "-" + std::to_string(_activeFileIndex) + ".bin";
string folder = FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "Satellaview");
_file.open(FolderUtilities::CombinePath(folder, filename), ios::binary);
}
bool BsxStream::LoadStreamFile()
{
_activeChannel = _channel;
_activeFileIndex = _fileIndex;
OpenStreamFile();
if(_file) {
_firstPacket = true;
_file.seekg(0, ios::end);
_queueLength = (uint16_t)std::ceil(_file.tellg() / 22.0);
_file.seekg(0, ios::beg);
_fileIndex++;
FillQueues();
return true;
} else {
if(_fileIndex > 0) {
//Go back to file #0 and try again
_fileIndex = 0;
if(LoadStreamFile()) {
return true;
}
}
//Couldn't load data for the specified channel
_prefix |= 0x0F;
return false;
}
}
uint8_t BsxStream::GetPrefixCount()
{
if(!_prefixLatch || !_dataLatch) {
//Stream is disabled
return 0;
}
if(_prefixQueueLength == 0 && _dataQueueLength == 0) {
//Queue is empty, try to load in new data
_fileOffset = 0;
if(_channel == 0) {
//Time channel
_queueLength = 1;
FillQueues();
_firstPacket = true;
} else {
LoadStreamFile();
}
}
return _prefixQueueLength;
}
uint8_t BsxStream::GetPrefix()
{
if(!_prefixLatch) {
return 0;
}
if(_prefixQueueLength > 0) {
_prefix = 0;
if(_firstPacket) {
_prefix |= 0x10;
_firstPacket = false;
}
_prefixQueueLength--;
if(_queueLength == 0 && _prefixQueueLength == 0) {
//Last packet
_prefix |= 0x80;
}
}
_status |= _prefix;
return _prefix;
}
uint8_t BsxStream::GetData()
{
if(!_dataLatch) {
return 0;
}
if(_dataQueueLength > 0) {
if(_channel == 0) {
//Return Time
_data = GetTime();
} else if(_file) {
//Read byte from stream file
char byte;
_file.get(byte);
_data = (uint8_t)byte;
}
_fileOffset++;
if(_fileOffset % 22 == 0) {
//Finished reading current packet
_dataQueueLength--;
}
}
return _data;
}
uint8_t BsxStream::GetStatus(bool reset)
{
uint8_t status = _status;
if(reset) {
_status = 0;
}
return status;
}
void BsxStream::SetChannelLow(uint8_t value)
{
if((_channel & 0xFF) != 0xFF) {
_fileIndex = 0;
}
_channel = (_channel & 0xFF00) | value;
if(_channel == 0) {
std::cout << "Test";
}
}
void BsxStream::SetChannelHigh(uint8_t value)
{
if((_channel >> 8) != (value & 0x3F)) {
_fileIndex = 0;
}
_channel = (_channel & 0xFF) | ((value & 0x3F) << 8);
if(_channel == 0) {
std::cout << "Test";
}
}
void BsxStream::SetPrefixLatch(uint8_t value)
{
_prefixLatch = (value != 0);
_prefixQueueLength = 0;
}
void BsxStream::SetDataLatch(uint8_t value)
{
_dataLatch = (value != 0);
_dataQueueLength = 0;
}
void BsxStream::InitTimeStruct()
{
if(_customDate >= 0) {
//Use custom date
time_t elapsed = _latchedTime - _resetTime;
_latchedTime = _customDate + elapsed;
}
#ifdef _MSC_VER
localtime_s(&_tm, &_latchedTime);
#else
localtime_r(&_latchedTime, &_tm);
#endif
_tm.tm_wday++;
_tm.tm_mon++;
_tm.tm_year += 1900;
}
uint8_t BsxStream::GetTime()
{
if(_fileOffset == 0) {
time(&_latchedTime);
InitTimeStruct();
}
switch(_fileOffset) {
case 0: return 0x00; //Data Group ID / Repetition
case 1: return 0x00; //Data Group Link / Continuity
case 2: return 0x00; //Data Group Size (24-bit)
case 3: return 0x00;
case 4: return 0x10;
case 5: return 0x01; //Must be 0x01
case 6: return 0x01; //Amount of packets (1)
case 7: return 0x00; //Offset (24-bit)
case 8: return 0x00;
case 9: return 0x00;
case 10: return _tm.tm_sec;
case 11: return _tm.tm_min;
case 12: return _tm.tm_hour;
case 13: return _tm.tm_wday;
case 14: return _tm.tm_mday;
case 15: return _tm.tm_mon;
case 16: return _tm.tm_year >> 0;
case 17: return _tm.tm_year >> 8;
default: return 0x00;
}
}
void BsxStream::Serialize(Serializer& s)
{
s.Stream(
_channel, _prefix, _data, _status, _prefixLatch, _dataLatch, _firstPacket, _fileOffset, _fileIndex,
_queueLength, _prefixQueueLength, _dataQueueLength, _latchedTime, _resetTime, _customDate,
_activeChannel, _activeFileIndex
);
if(!s.IsSaving()) {
InitTimeStruct();
OpenStreamFile();
if(_file) {
//Seek back to the previous location
_file.seekg(_fileOffset, ios::beg);
}
}
}

57
Core/BsxStream.h Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
class BsxStream : public ISerializable
{
private:
ifstream _file;
tm _tm = {};
uint16_t _channel = 0;
uint8_t _prefix = 0;
uint8_t _data = 0;
uint8_t _status = 0;
bool _prefixLatch = false;
bool _dataLatch = false;
bool _firstPacket = false;
uint32_t _fileOffset = 0;
uint8_t _fileIndex = 0;
uint16_t _queueLength = 0;
uint8_t _prefixQueueLength = 0;
uint8_t _dataQueueLength = 0;
uint16_t _activeChannel = 0;
uint8_t _activeFileIndex = 0;
int64_t _customDate = -1;
time_t _resetTime = 0;
time_t _latchedTime = 0;
void FillQueues();
void OpenStreamFile();
bool LoadStreamFile();
void InitTimeStruct();
uint8_t GetTime();
public:
BsxStream();
void Reset(int64_t customDate);
uint16_t GetChannel();
uint8_t GetPrefixCount();
uint8_t GetPrefix();
uint8_t GetData();
uint8_t GetStatus(bool reset);
void SetChannelLow(uint8_t value);
void SetChannelHigh(uint8_t value);
void SetPrefixLatch(uint8_t value);
void SetDataLatch(uint8_t value);
void Serialize(Serializer& s) override;
};

View file

@ -51,6 +51,8 @@
<ClInclude Include="BatteryManager.h" />
<ClInclude Include="BsxCart.h" />
<ClInclude Include="BsxMemoryPack.h" />
<ClInclude Include="BsxSatellaview.h" />
<ClInclude Include="BsxStream.h" />
<ClInclude Include="CheatManager.h" />
<ClInclude Include="ClientConnectionData.h" />
<ClInclude Include="Cpu.Shared.h" />
@ -227,6 +229,8 @@
<ClCompile Include="BreakpointManager.cpp" />
<ClCompile Include="BsxCart.cpp" />
<ClCompile Include="BsxMemoryPack.cpp" />
<ClCompile Include="BsxSatellaview.cpp" />
<ClCompile Include="BsxStream.cpp" />
<ClCompile Include="CallstackManager.cpp" />
<ClCompile Include="CheatManager.cpp" />
<ClCompile Include="CodeDataLogger.cpp" />

View file

@ -509,6 +509,12 @@
<ClInclude Include="BsxMemoryPack.h">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClInclude>
<ClInclude Include="BsxSatellaview.h">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClInclude>
<ClInclude Include="BsxStream.h">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -815,6 +821,12 @@
<ClCompile Include="BsxMemoryPack.cpp">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClCompile>
<ClCompile Include="BsxSatellaview.cpp">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClCompile>
<ClCompile Include="BsxStream.cpp">
<Filter>SNES\Coprocessors\BSX</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -285,6 +285,8 @@ struct EmulationConfig
uint32_t GsuClockSpeed = 100;
RamState RamPowerOnState = RamState::Random;
int64_t BsxCustomDate = -1;
};
struct PreferencesConfig

View file

@ -32,6 +32,8 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/BreakpointManager.cpp \
$(CORE_DIR)/BsxCart.cpp \
$(CORE_DIR)/BsxMemoryPack.cpp \
$(CORE_DIR)/BsxSatellaview.cpp \
$(CORE_DIR)/BsxStream.cpp \
$(CORE_DIR)/CallstackManager.cpp \
$(CORE_DIR)/CheatManager.cpp \
$(CORE_DIR)/CodeDataLogger.cpp \

View file

@ -211,6 +211,7 @@ namespace Mesen.GUI.Config
public static string WaveFolder { get { return GetFolder(DefaultWaveFolder, Config.Preferences.WaveFolder, Config.Preferences.OverrideWaveFolder); } }
public static string CheatFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Cheats"), null, false); } }
public static string SatellaviewFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Satellaview"), null, false); } }
public static string DebuggerFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Debugger"), null, false); } }
public static string FirmwareFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Firmware"), null, false); } }

View file

@ -26,6 +26,8 @@ namespace Mesen.GUI.Config
[MinMax(100, 1000)] public UInt32 GsuClockSpeed = 100;
public RamState RamPowerOnState = RamState.Random;
public long BsxCustomDate = -1;
public void ApplyConfig()
{

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -68,6 +68,13 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.nudGsuClockSpeed = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblGsuClockSpeed = new System.Windows.Forms.Label();
this.tpgBsx = new System.Windows.Forms.TabPage();
this.grpBsxDateTime = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel();
this.dtpBsxCustomDate = new System.Windows.Forms.DateTimePicker();
this.radBsxLocalTime = new System.Windows.Forms.RadioButton();
this.radBsxCustomTime = new System.Windows.Forms.RadioButton();
this.dtpBsxCustomTime = new System.Windows.Forms.DateTimePicker();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@ -83,6 +90,9 @@
this.grpPpuTiming.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.tpgBsx.SuspendLayout();
this.grpBsxDateTime.SuspendLayout();
this.tableLayoutPanel6.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
@ -96,6 +106,7 @@
this.tabMain.Controls.Add(this.tpgGeneral);
this.tabMain.Controls.Add(this.tpgAdvanced);
this.tabMain.Controls.Add(this.tpgOverclocking);
this.tabMain.Controls.Add(this.tpgBsx);
this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
@ -709,6 +720,100 @@
this.lblGsuClockSpeed.TabIndex = 0;
this.lblGsuClockSpeed.Text = "Super FX clock speed (%):";
//
// tpgBsx
//
this.tpgBsx.Controls.Add(this.grpBsxDateTime);
this.tpgBsx.Location = new System.Drawing.Point(4, 22);
this.tpgBsx.Name = "tpgBsx";
this.tpgBsx.Padding = new System.Windows.Forms.Padding(3);
this.tpgBsx.Size = new System.Drawing.Size(437, 264);
this.tpgBsx.TabIndex = 5;
this.tpgBsx.Text = "BS-X";
this.tpgBsx.UseVisualStyleBackColor = true;
//
// grpBsxDateTime
//
this.grpBsxDateTime.Controls.Add(this.tableLayoutPanel6);
this.grpBsxDateTime.Dock = System.Windows.Forms.DockStyle.Fill;
this.grpBsxDateTime.Location = new System.Drawing.Point(3, 3);
this.grpBsxDateTime.Name = "grpBsxDateTime";
this.grpBsxDateTime.Size = new System.Drawing.Size(431, 258);
this.grpBsxDateTime.TabIndex = 1;
this.grpBsxDateTime.TabStop = false;
this.grpBsxDateTime.Text = "BS-X/Satellaview Date and Time Settings";
//
// tableLayoutPanel6
//
this.tableLayoutPanel6.ColumnCount = 3;
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel6.Controls.Add(this.dtpBsxCustomDate, 1, 1);
this.tableLayoutPanel6.Controls.Add(this.radBsxLocalTime, 0, 0);
this.tableLayoutPanel6.Controls.Add(this.radBsxCustomTime, 0, 1);
this.tableLayoutPanel6.Controls.Add(this.dtpBsxCustomTime, 2, 1);
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel6.Location = new System.Drawing.Point(3, 16);
this.tableLayoutPanel6.Name = "tableLayoutPanel6";
this.tableLayoutPanel6.RowCount = 3;
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel6.Size = new System.Drawing.Size(425, 239);
this.tableLayoutPanel6.TabIndex = 0;
//
// dtpBsxCustomDate
//
this.dtpBsxCustomDate.CustomFormat = "";
this.dtpBsxCustomDate.Dock = System.Windows.Forms.DockStyle.Fill;
this.dtpBsxCustomDate.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtpBsxCustomDate.Location = new System.Drawing.Point(160, 26);
this.dtpBsxCustomDate.MaxDate = new System.DateTime(2100, 12, 31, 0, 0, 0, 0);
this.dtpBsxCustomDate.MinDate = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
this.dtpBsxCustomDate.Name = "dtpBsxCustomDate";
this.dtpBsxCustomDate.Size = new System.Drawing.Size(128, 20);
this.dtpBsxCustomDate.TabIndex = 3;
this.dtpBsxCustomDate.Value = new System.DateTime(2020, 1, 1, 0, 0, 0, 0);
this.dtpBsxCustomDate.Enter += new System.EventHandler(this.dtpBsxCustomDate_Enter);
//
// radBsxLocalTime
//
this.radBsxLocalTime.AutoSize = true;
this.radBsxLocalTime.Location = new System.Drawing.Point(3, 3);
this.radBsxLocalTime.Name = "radBsxLocalTime";
this.radBsxLocalTime.Size = new System.Drawing.Size(127, 17);
this.radBsxLocalTime.TabIndex = 0;
this.radBsxLocalTime.TabStop = true;
this.radBsxLocalTime.Text = "Use current local time";
this.radBsxLocalTime.UseVisualStyleBackColor = true;
//
// radBsxCustomTime
//
this.radBsxCustomTime.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.radBsxCustomTime.AutoSize = true;
this.radBsxCustomTime.Location = new System.Drawing.Point(3, 27);
this.radBsxCustomTime.Name = "radBsxCustomTime";
this.radBsxCustomTime.Size = new System.Drawing.Size(151, 17);
this.radBsxCustomTime.TabIndex = 1;
this.radBsxCustomTime.TabStop = true;
this.radBsxCustomTime.Text = "Use custom date and time:";
this.radBsxCustomTime.UseVisualStyleBackColor = true;
//
// dtpBsxCustomTime
//
this.dtpBsxCustomTime.CustomFormat = "";
this.dtpBsxCustomTime.Dock = System.Windows.Forms.DockStyle.Fill;
this.dtpBsxCustomTime.Format = System.Windows.Forms.DateTimePickerFormat.Time;
this.dtpBsxCustomTime.Location = new System.Drawing.Point(294, 26);
this.dtpBsxCustomTime.MaxDate = new System.DateTime(2000, 1, 2, 0, 0, 0, 0);
this.dtpBsxCustomTime.MinDate = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
this.dtpBsxCustomTime.Name = "dtpBsxCustomTime";
this.dtpBsxCustomTime.ShowUpDown = true;
this.dtpBsxCustomTime.Size = new System.Drawing.Size(128, 20);
this.dtpBsxCustomTime.TabIndex = 2;
this.dtpBsxCustomTime.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
this.dtpBsxCustomTime.Enter += new System.EventHandler(this.dtpBsxCustomTime_Enter);
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -748,6 +853,10 @@
this.tableLayoutPanel5.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tpgBsx.ResumeLayout(false);
this.grpBsxDateTime.ResumeLayout(false);
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel6.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -795,5 +904,12 @@
private Controls.MesenNumericUpDown nudRunAheadFrames;
private System.Windows.Forms.Label lblRunAheadFrames;
private System.Windows.Forms.Label lblRunAhead;
private System.Windows.Forms.TabPage tpgBsx;
private System.Windows.Forms.GroupBox grpBsxDateTime;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.RadioButton radBsxLocalTime;
private System.Windows.Forms.RadioButton radBsxCustomTime;
private System.Windows.Forms.DateTimePicker dtpBsxCustomTime;
private System.Windows.Forms.DateTimePicker dtpBsxCustomDate;
}
}

View file

@ -13,6 +13,8 @@ namespace Mesen.GUI.Forms.Config
{
public partial class frmEmulationConfig : BaseConfigForm
{
private DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public frmEmulationConfig()
{
InitializeComponent();
@ -35,10 +37,34 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesBeforeNmi), nudExtraScanlinesBeforeNmi);
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesAfterNmi), nudExtraScanlinesAfterNmi);
AddBinding(nameof(EmulationConfig.GsuClockSpeed), nudGsuClockSpeed);
long customDate = ConfigManager.Config.Emulation.BsxCustomDate;
if(customDate >= 0) {
DateTime stamp;
try {
stamp = _unixEpoch.AddSeconds(customDate).ToLocalTime();
} catch {
stamp = DateTime.Now;
}
radBsxCustomTime.Checked = true;
dtpBsxCustomDate.Value = stamp.Date;
dtpBsxCustomTime.Value = new DateTime(2000, 01, 01) + stamp.TimeOfDay;
} else {
radBsxLocalTime.Checked = true;
dtpBsxCustomDate.Value = DateTime.Now.Date;
dtpBsxCustomTime.Value = new DateTime(2000, 01, 01) + DateTime.Now.TimeOfDay;
}
}
protected override void OnApply()
{
if(radBsxCustomTime.Checked) {
DateTime dateTime = dtpBsxCustomDate.Value + dtpBsxCustomTime.Value.TimeOfDay;
((EmulationConfig)this.Entity).BsxCustomDate = (long)dateTime.Subtract(_unixEpoch.ToLocalTime()).TotalSeconds;
} else {
((EmulationConfig)this.Entity).BsxCustomDate = -1;
}
ConfigManager.Config.Emulation = (EmulationConfig)this.Entity;
ConfigManager.ApplyChanges();
}
@ -47,5 +73,15 @@ namespace Mesen.GUI.Forms.Config
{
nudGsuClockSpeed.Value = Math.Ceiling(nudGsuClockSpeed.Value / 100) * 100;
}
private void dtpBsxCustomDate_Enter(object sender, EventArgs e)
{
radBsxCustomTime.Checked = true;
}
private void dtpBsxCustomTime_Enter(object sender, EventArgs e)
{
radBsxCustomTime.Checked = true;
}
}
}

View file

@ -26,9 +26,14 @@ namespace Mesen.GUI
}
}
private static void ExtractFile(ZipArchiveEntry entry, string outputFilename)
private static void ExtractFile(ZipArchiveEntry entry, string outputFilename, bool allowOverwrite = true)
{
if(File.Exists(outputFilename)) {
if(!allowOverwrite) {
//File already exists, and we don't want to replace it
return;
}
byte[] zipFileData = new byte[entry.Length];
using(Stream fileStream = entry.Open()) {
fileStream.Read(zipFileData, 0, (int)entry.Length);
@ -100,6 +105,9 @@ namespace Mesen.GUI
} else if(entry.Name == "MesenUpdater.exe") {
string outputFilename = Path.Combine(ConfigManager.HomeFolder, "Resources", entry.Name);
ExtractFile(entry, outputFilename);
} else if(entry.Name.StartsWith("BSX")) {
string outputFilename = Path.Combine(ConfigManager.SatellaviewFolder, entry.Name);
ExtractFile(entry, outputFilename, false);
} else if(entry.Name == "Font.24.spritefont" || entry.Name == "Font.64.spritefont" || entry.Name == "LICENSE.txt" || entry.Name == "PixelFont.ttf") {
string outputFilename = Path.Combine(ConfigManager.HomeFolder, "Resources", entry.Name);
ExtractFile(entry, outputFilename);

View file

@ -969,6 +969,21 @@
<Compile Include="Utilities\RandomGameHelper.cs" />
<Compile Include="Utilities\RomTestHelper.cs" />
<Compile Include="Utilities\XmlColor.cs" />
<None Include="Dependencies\Satellaview\BSX0120-0.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Dependencies\Satellaview\BSX0121-0.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Dependencies\Satellaview\BSX0122-0.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Dependencies\Satellaview\BSX0123-0.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Dependencies\Satellaview\BSX0124-0.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Resources\CloseWhite.png" />
<None Include="Resources\GsuDebugger.png" />
<None Include="Resources\Sa1Debugger.png" />