Input: Added UI for family basic tape recorder

This commit is contained in:
Souryo 2017-11-24 21:38:12 -05:00
parent 12321c48fa
commit 4b3edac310
33 changed files with 431 additions and 116 deletions

View file

@ -85,7 +85,7 @@ public:
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4017) {
int32_t elapsedCycles = CPU::GetCycleCount() - _insertCycle;
int32_t elapsedCycles = CPU::GetElapsedCycles(_insertCycle);
constexpr uint32_t cyclesPerBit = CPU::ClockRateNtsc / 1200;
uint32_t streamPosition = elapsedCycles / cyclesPerBit;

View file

@ -42,7 +42,8 @@ public:
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;
static const uint8_t ExpDevicePort2 = 7;
static const uint8_t PortCount = ExpDevicePort2 + 1;
BaseControlDevice(uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet());
virtual ~BaseControlDevice();

View file

@ -785,7 +785,18 @@ public:
static const uint32_t ClockRateDendy = 1773448;
CPU(MemoryManager *memoryManager);
static int32_t GetCycleCount() { return CPU::Instance->_cycleCount; }
static int32_t GetElapsedCycles(int32_t prevCycleCount)
{
if(prevCycleCount > Instance->_cycleCount) {
return 0xFFFFFFFF - prevCycleCount + Instance->_cycleCount + 1;
} else {
return Instance->_cycleCount - prevCycleCount;
}
}
static void SetNMIFlag() { CPU::Instance->_state.NMIFlag = true; }
static void ClearNMIFlag() { CPU::Instance->_state.NMIFlag = false; }
static void SetIRQMask(uint8_t mask) { CPU::Instance->_irqMask = mask; }

View file

@ -33,6 +33,7 @@
#include "SystemActionManager.h"
#include "FdsSystemActionManager.h"
#include "VsSystemActionManager.h"
#include "FamilyBasicDataRecorder.h"
#include "IBarcodeReader.h"
#include "IBattery.h"
#include "KeyManager.h"
@ -784,6 +785,10 @@ ConsoleFeatures Console::GetAvailableFeatures()
if(std::dynamic_pointer_cast<IBarcodeReader>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort))) {
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::BarcodeReader);
}
if(std::dynamic_pointer_cast<FamilyBasicDataRecorder>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2))) {
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::TapeRecorder);
}
}
return features;
}
@ -799,4 +804,44 @@ void Console::InputBarcode(uint64_t barcode, uint32_t digitCount)
if(barcodeReader) {
barcodeReader->InputBarcode(barcode, digitCount);
}
}
void Console::LoadTapeFile(string filepath)
{
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
if(dataRecorder) {
Console::Pause();
dataRecorder->LoadFromFile(filepath);
Console::Resume();
}
}
void Console::StartRecordingTapeFile(string filepath)
{
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
if(dataRecorder) {
Console::Pause();
dataRecorder->StartRecording(filepath);
Console::Resume();
}
}
void Console::StopRecordingTapeFile()
{
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
if(dataRecorder) {
Console::Pause();
dataRecorder->StopRecording();
Console::Resume();
}
}
bool Console::IsRecordingTapeFile()
{
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(_controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
if(dataRecorder) {
return dataRecorder->IsRecording();
}
return false;
}

View file

@ -88,6 +88,11 @@ class Console
ConsoleFeatures GetAvailableFeatures();
void InputBarcode(uint64_t barcode, uint32_t digitCount);
void LoadTapeFile(string filepath);
void StartRecordingTapeFile(string filepath);
void StopRecordingTapeFile();
bool IsRecordingTapeFile();
static std::thread::id GetEmulationThreadId();
static void RequestReset();

View file

@ -97,11 +97,13 @@ vector<ControlDeviceState> ControlManager::GetPortStates()
shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port)
{
auto lock = _deviceLock.AcquireSafe();
if(_instance) {
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;
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;
}
@ -204,6 +206,11 @@ void ControlManager::UpdateControlDevices()
if(_mapperControlDevice) {
ControlManager::RegisterControlDevice(_mapperControlDevice);
}
if(std::dynamic_pointer_cast<FamilyBasicKeyboard>(expDevice)) {
//Automatically connect the data recorder if the keyboard is connected
ControlManager::RegisterControlDevice(shared_ptr<FamilyBasicDataRecorder>(new FamilyBasicDataRecorder()));
}
}
uint8_t ControlManager::GetOpenBusMask(uint8_t port)

View file

@ -58,7 +58,7 @@ public:
uint8_t GetOutput()
{
int32_t elapsedCycles = CPU::GetCycleCount() - _insertCycle;
int32_t elapsedCycles = CPU::GetElapsedCycles(_insertCycle);
int32_t bitNumber = elapsedCycles / 1000;
if(bitNumber < (int32_t)_data.size()) {
return _data[bitNumber];

View file

@ -1,50 +1,133 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "../Utilities/Base64.h"
#include "BaseControlDevice.h"
#include "CPU.h"
//TODO: Integration with UI
class FamilyBasicDataRecorder : public BaseControlDevice
{
private:
static const uint32_t SamplingRate = 88;
vector<uint8_t> _saveData;
vector<uint8_t> _data;
vector<uint8_t> _fileData;
bool _enabled = false;
int32_t _lastCycle = -1;
int32_t _readStartCycle = -1;
bool _isPlaying = false;
int32_t _cycle = -1;
bool _isRecording = false;
string _recordFilePath;
protected:
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
Stream(_enabled);
uint32_t dataSize = _data.size();
Stream(_enabled, _isPlaying, _cycle, dataSize);
if(!saving) {
_data.resize(dataSize);
}
ArrayInfo<uint8_t> data{ _data.data(), _data.size() };
Stream(data);
if(!saving && _isRecording) {
StopRecording();
}
}
bool IsRawString()
{
return true;
}
public:
FamilyBasicDataRecorder() : BaseControlDevice(BaseControlDevice::ExpDevicePort)
FamilyBasicDataRecorder() : BaseControlDevice(BaseControlDevice::ExpDevicePort2)
{
}
~FamilyBasicDataRecorder()
{
if(_isRecording) {
StopRecording();
}
}
void InternalSetStateFromInput()
{
if(_fileData.size() > 0) {
SetTextState(Base64::Encode(_fileData));
_fileData.clear();
}
}
void OnAfterSetState()
{
if(_state.State.size() > 0) {
_data = Base64::Decode(GetTextState());
_cycle = CPU::GetCycleCount();
_isPlaying = true;
_isRecording = false;
}
}
void LoadFromFile(VirtualFile file)
{
if(file.IsValid()) {
vector<uint8_t> fileData;
file.ReadFile(fileData);
_fileData = fileData;
}
}
bool IsRecording()
{
return _isRecording;
}
void StartRecording(string filePath)
{
_isPlaying = false;
_recordFilePath = filePath;
_data.clear();
_cycle = CPU::GetCycleCount();
_isRecording = true;
}
void StopRecording()
{
_isRecording = false;
vector<uint8_t> fileData;
int bitPos = 0;
uint8_t currentByte = 0;
for(uint8_t bitValue : _data) {
currentByte |= (bitValue & 0x01) << bitPos;
bitPos = (bitPos + 1) % 8;
if(bitPos == 0) {
fileData.push_back(currentByte);
currentByte = 0;
}
}
ofstream out(_recordFilePath, ios::binary);
if(out) {
out.write((char*)fileData.data(), fileData.size());
}
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4016) {
if(EmulationSettings::CheckFlag(EmulationFlags::ShowFrameCounter)) {
if(_readStartCycle == -1) {
_readStartCycle = CPU::GetCycleCount();
}
if(addr == 0x4016 && _isPlaying) {
int32_t readPos = CPU::GetElapsedCycles(_cycle) / FamilyBasicDataRecorder::SamplingRate;
int readPos = (CPU::GetCycleCount() - _readStartCycle) / FamilyBasicDataRecorder::SamplingRate;
if((int32_t)_saveData.size() > readPos) {
uint8_t value = (_saveData[readPos] & 0x01) << 1;
return _enabled ? value : 0;
}
if((int32_t)_data.size() > readPos / 8) {
uint8_t value = ((_data[readPos / 8] >> (readPos % 8)) & 0x01) << 1;
return _enabled ? value : 0;
} else {
if(!EmulationSettings::CheckFlag(EmulationFlags::ShowFPS)) {
_lastCycle = -1;
_readStartCycle = -1;
}
_isPlaying = false;
}
}
@ -55,19 +138,10 @@ public:
{
_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;
if(_isRecording) {
while(CPU::GetElapsedCycles(_cycle) > FamilyBasicDataRecorder::SamplingRate) {
_data.push_back(value & 0x01);
_cycle += 88;
}
}
}

View file

@ -2,30 +2,11 @@
#include <algorithm>
#include "../Utilities/StringUtilities.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/Base64.h"
#include "ControlManager.h"
#include "FceuxMovie.h"
#include "Console.h"
vector<uint8_t> FceuxMovie::Base64Decode(string in)
{
vector<uint8_t> out;
vector<int> T(256, -1);
for(int i = 0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
int val = 0, valb = -8;
for(uint8_t c : in) {
if(T[c] == -1) break;
val = (val << 6) + T[c];
valb += 6;
if(valb >= 0) {
out.push_back(val >> valb);
valb -= 8;
}
}
return out;
}
bool FceuxMovie::InitializeData(stringstream &filestream)
{
bool result = false;
@ -39,7 +20,7 @@ bool FceuxMovie::InitializeData(stringstream &filestream)
string line;
std::getline(filestream, line);
if(line.compare(0, 19, "romChecksum base64:", 19) == 0) {
vector<uint8_t> md5array = Base64Decode(line.substr(19, line.size() - 20));
vector<uint8_t> md5array = Base64::Decode(line.substr(19, line.size() - 20));
HashInfo hashInfo;
hashInfo.PrgChrMd5Hash = HexUtilities::ToHex(md5array);
if(Console::LoadROM("", hashInfo)) {

View file

@ -7,7 +7,6 @@
class FceuxMovie : public BizhawkMovie
{
private:
vector<uint8_t> Base64Decode(string in);
bool InitializeData(stringstream &filestream);
public:

View file

@ -250,4 +250,5 @@ enum class ConsoleFeatures
Nsf = 2,
VsSystem = 4,
BarcodeReader = 8,
TapeRecorder = 16,
};

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Insereix moneda (1)</Control>
<Control ID="mnuInsertCoin2">Insereix moneda (2)</Control>
<Control ID="mnuInputBarcode">Input Barcode</Control>
<Control ID="mnuTapeRecorder">Tape Recorder</Control>
<Control ID="mnuLoadTapeFile">Load from file...</Control>
<Control ID="mnuStartRecordTapeFile">Record to file...</Control>
<Control ID="mnuStopRecordTapeFile">Stop recording</Control>
<Control ID="mnuOptions">Opcions</Control>
<Control ID="mnuEmulationSpeed">Velocitat</Control>
<Control ID="mnuEmuSpeedNormal">Normal (100%)</Control>
@ -615,6 +619,7 @@
<Message ID="FilterTest">Fitxers de proves (*.mtp)|*.mtp|Tots els fitxers (*.*)|*.*</Message>
<Message ID="FilterCheat">Tots els formats suportats (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Partides guardades de Mesen (*.mst)|*.mst|Tots els fitxers (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|Tots els fitxers (*.*)|*.*</Message>
<Message ID="LoadFromFile">Carrega des d'un fitxer...</Message>
<Message ID="SaveToFile">Desa al fitxer...</Message>

View file

@ -11,6 +11,7 @@
<Message ID="FilterTest">Test files (*.mtp)|*.mtp|All (*.*)|*.*</Message>
<Message ID="FilterCheat">All supported formats (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Mesen Savestates (*.mst)|*.mst|All files (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.*</Message>
<Message ID="LoadFromFile">Load from file...</Message>
<Message ID="SaveToFile">Save to file...</Message>

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Insertar moneda (1)</Control>
<Control ID="mnuInsertCoin2">Insertar moneda (2)</Control>
<Control ID="mnuInputBarcode">Input Barcode</Control>
<Control ID="mnuTapeRecorder">Tape Recorder</Control>
<Control ID="mnuLoadTapeFile">Load from file...</Control>
<Control ID="mnuStartRecordTapeFile">Record to file...</Control>
<Control ID="mnuStopRecordTapeFile">Stop recording</Control>
<Control ID="mnuOptions">Opciones</Control>
<Control ID="mnuEmulationSpeed">Velocidad</Control>
<Control ID="mnuEmuSpeedNormal">Normal (100%)</Control>
@ -634,6 +638,7 @@
<Message ID="FilterTest">Archivos de test (*.mtp)|*.mtp|Todos los archivos (*.*)|*.*</Message>
<Message ID="FilterCheat">Todos los formatos soportados (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Partidas de juegos de Mesen (*.mst)|*.mst|Todos los archivos (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|Todos los archivos (*.*)|*.*</Message>
<Message ID="LoadFromFile">Cargar desde archivo...</Message>
<Message ID="SaveToFile">Guardar en archivo...</Message>

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Insérer une pièce (1)</Control>
<Control ID="mnuInsertCoin2">Insérer une pièce (2)</Control>
<Control ID="mnuInputBarcode">Entrer un code-barres</Control>
<Control ID="mnuTapeRecorder">Enregistreur de données</Control>
<Control ID="mnuLoadTapeFile">Charger un fichier...</Control>
<Control ID="mnuStartRecordTapeFile">Enregistrer dans un fichier...</Control>
<Control ID="mnuStopRecordTapeFile">Arrêter l'enregistrement</Control>
<Control ID="mnuOptions">Options</Control>
<Control ID="mnuEmulationSpeed">Vitesse</Control>
<Control ID="mnuEmuSpeedNormal">Normale (100%)</Control>
@ -647,6 +651,7 @@
<Message ID="FilterTest">Fichiers de test (*.mtp)|*.mtp|Tous les fichiers (*.*)|*.*</Message>
<Message ID="FilterCheat">Tous les formats supportés (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Sauvegardes d'états Mesen (*.mst)|*.mst|Tous les fichiers (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Fichiers de cassettes Family Basic (*.fbt)|*.fbt|Tous les fichiers (*.*)|*.*</Message>
<Message ID="LoadFromFile">Charger à partir d'un fichier...</Message>
<Message ID="SaveToFile">Sauvegarder dans un fichier...</Message>

View file

@ -19,7 +19,11 @@
<Control ID="mnuVsGameConfig">VSゲームの設定</Control>
<Control ID="mnuInsertCoin1">インサートコイン 1</Control>
<Control ID="mnuInsertCoin2">インサートコイン 2</Control>
<Control ID="mnuInputCode">バーコードを入力</Control>
<Control ID="mnuInputBarcode">バーコードを入力</Control>
<Control ID="mnuTapeRecorder">テープレコーダー</Control>
<Control ID="mnuLoadTapeFile">ファイルから再生する</Control>
<Control ID="mnuStartRecordTapeFile">ファイルに録音する</Control>
<Control ID="mnuStopRecordTapeFile">録音を停止する</Control>
<Control ID="mnuOptions">設定</Control>
<Control ID="mnuEmulationSpeed">速度</Control>
<Control ID="mnuEmuSpeedNormal">通常 (100%)</Control>
@ -631,6 +635,7 @@
<Message ID="FilterTest">テストファイル (*.mtp)|*.mtp|すべてのファイル (*.*)|*.*</Message>
<Message ID="FilterCheat">対応するすべてのファイル (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Mesenのクイックセーブデータ (*.mst)|*.mst|すべてのファイル (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basicテープファイル (*.fbt)|*.fbt|すべてのファイル (*.*)|*.*</Message>
<Message ID="LoadFromFile">ファイルからロードする…</Message>
<Message ID="SaveToFile">ファイルに保存する…</Message>

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Inserir moeda (1)</Control>
<Control ID="mnuInsertCoin2">Inserir moeda (2)</Control>
<Control ID="mnuInputBarcode">Input Barcode</Control>
<Control ID="mnuTapeRecorder">Tape Recorder</Control>
<Control ID="mnuLoadTapeFile">Load from file...</Control>
<Control ID="mnuStartRecordTapeFile">Record to file...</Control>
<Control ID="mnuStopRecordTapeFile">Stop recording</Control>
<Control ID="mnuOptions">Opções</Control>
<Control ID="mnuEmulationSpeed">Velocidade</Control>
<Control ID="mnuEmuSpeedNormal">Normal (100%)</Control>
@ -632,6 +636,7 @@
<Message ID="FilterTest">Arquivos de teste (*.mtp)|*.mtp|Todos os arquivos (*.*)|*.*</Message>
<Message ID="FilterCheat">Todos os formatos suportados (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Estados salvos do Mesen (*.mst)|*.mst|Todos os arquivos (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|Todos os arquivos (*.*)|*.*</Message>
<Message ID="LoadFromFile">Carregar de um arquivo...</Message>
<Message ID="SaveToFile">Salvar para arquivo...</Message>

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Вставить монету в слот 1</Control>
<Control ID="mnuInsertCoin2">Вставить монету в слот 2</Control>
<Control ID="mnuInputBarcode">Input Barcode</Control>
<Control ID="mnuTapeRecorder">Tape Recorder</Control>
<Control ID="mnuLoadTapeFile">Load from file...</Control>
<Control ID="mnuStartRecordTapeFile">Record to file...</Control>
<Control ID="mnuStopRecordTapeFile">Stop recording</Control>
<Control ID="mnuOptions">Опции</Control>
<Control ID="mnuEmulationSpeed">Скорость эмуляции</Control>
<Control ID="mnuEmuSpeedNormal">Нормальная (100%)</Control>
@ -636,6 +640,7 @@
<Message ID="FilterTest">Test files (*.mtp)|*.mtp|All (*.*)|*.*</Message>
<Message ID="FilterCheat">Все поддерживаемые форматы (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.*</Message>
<Message ID="LoadFromFile">Load from file...</Message>
<Message ID="SaveToFile">Save to file...</Message>

View file

@ -20,6 +20,10 @@
<Control ID="mnuInsertCoin1">Вставити монету в слот 1</Control>
<Control ID="mnuInsertCoin2">Вставити монету в слот 2</Control>
<Control ID="mnuInputBarcode">Input Barcode</Control>
<Control ID="mnuTapeRecorder">Tape Recorder</Control>
<Control ID="mnuLoadTapeFile">Load from file...</Control>
<Control ID="mnuStartRecordTapeFile">Record to file...</Control>
<Control ID="mnuStopRecordTapeFile">Stop recording</Control>
<Control ID="mnuOptions">Опції</Control>
<Control ID="mnuEmulationSpeed">Швидкість емуляції</Control>
<Control ID="mnuEmuSpeedNormal">Нормальна (100%)</Control>
@ -636,6 +640,7 @@
<Message ID="FilterTest">Test files (*.mtp)|*.mtp|All (*.*)|*.*</Message>
<Message ID="FilterCheat">Всі підтримувані формати (*.cht, *.xml)|*.cht;*.xml</Message>
<Message ID="FilterSavestate">Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.*</Message>
<Message ID="FilterTapeFiles">Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.*</Message>
<Message ID="LoadFromFile">Завантажитти з файлу...</Message>
<Message ID="SaveToFile">Зберегти в файл...</Message>

View file

@ -66,6 +66,10 @@ namespace Mesen.GUI.Forms
this.mnuInsertCoin2 = new System.Windows.Forms.ToolStripMenuItem();
this.sepBarcode = new System.Windows.Forms.ToolStripSeparator();
this.mnuInputBarcode = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTapeRecorder = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLoadTapeFile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStartRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStopRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuOptions = new System.Windows.Forms.ToolStripMenuItem();
this.mnuEmulationSpeed = new System.Windows.Forms.ToolStripMenuItem();
this.mnuEmuSpeedNormal = new System.Windows.Forms.ToolStripMenuItem();
@ -403,10 +407,12 @@ namespace Mesen.GUI.Forms
this.mnuInsertCoin1,
this.mnuInsertCoin2,
this.sepBarcode,
this.mnuInputBarcode});
this.mnuInputBarcode,
this.mnuTapeRecorder});
this.mnuGame.Name = "mnuGame";
this.mnuGame.Size = new System.Drawing.Size(50, 20);
this.mnuGame.Text = "Game";
this.mnuGame.DropDownOpening += new System.EventHandler(this.mnuGame_DropDownOpening);
//
// mnuPause
//
@ -507,12 +513,47 @@ namespace Mesen.GUI.Forms
//
// mnuInputBarcode
//
this.mnuInputBarcode.Image = global::Mesen.GUI.Properties.Resources.CheatCode;
this.mnuInputBarcode.Image = global::Mesen.GUI.Properties.Resources.Barcode;
this.mnuInputBarcode.Name = "mnuInputBarcode";
this.mnuInputBarcode.Size = new System.Drawing.Size(182, 22);
this.mnuInputBarcode.Text = "Input Barcode...";
this.mnuInputBarcode.Visible = false;
//
// mnuTapeRecorder
//
this.mnuTapeRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuLoadTapeFile,
this.mnuStartRecordTapeFile,
this.mnuStopRecordTapeFile});
this.mnuTapeRecorder.Image = global::Mesen.GUI.Properties.Resources.Tape;
this.mnuTapeRecorder.Name = "mnuTapeRecorder";
this.mnuTapeRecorder.Size = new System.Drawing.Size(182, 22);
this.mnuTapeRecorder.Text = "Tape Recorder";
//
// mnuLoadTapeFile
//
this.mnuLoadTapeFile.Image = global::Mesen.GUI.Properties.Resources.Import;
this.mnuLoadTapeFile.Name = "mnuLoadTapeFile";
this.mnuLoadTapeFile.Size = new System.Drawing.Size(179, 22);
this.mnuLoadTapeFile.Text = "Load from file...";
this.mnuLoadTapeFile.Click += new System.EventHandler(this.mnuLoadTapeFile_Click);
//
// mnuStartRecordTapeFile
//
this.mnuStartRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Export;
this.mnuStartRecordTapeFile.Name = "mnuStartRecordTapeFile";
this.mnuStartRecordTapeFile.Size = new System.Drawing.Size(179, 22);
this.mnuStartRecordTapeFile.Text = "Record to file...";
this.mnuStartRecordTapeFile.Click += new System.EventHandler(this.mnuStartRecordTapeFile_Click);
//
// mnuStopRecordTapeFile
//
this.mnuStopRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Stop;
this.mnuStopRecordTapeFile.Name = "mnuStopRecordTapeFile";
this.mnuStopRecordTapeFile.Size = new System.Drawing.Size(179, 22);
this.mnuStopRecordTapeFile.Text = "Stop recording";
this.mnuStopRecordTapeFile.Click += new System.EventHandler(this.mnuStopRecordTapeFile_Click);
//
// mnuOptions
//
this.mnuOptions.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -1759,6 +1800,10 @@ namespace Mesen.GUI.Forms
private System.Windows.Forms.ToolStripSeparator sepBarcode;
private System.Windows.Forms.ToolStripMenuItem mnuInputBarcode;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer5;
private System.Windows.Forms.ToolStripMenuItem mnuTapeRecorder;
private System.Windows.Forms.ToolStripMenuItem mnuLoadTapeFile;
private System.Windows.Forms.ToolStripMenuItem mnuStartRecordTapeFile;
private System.Windows.Forms.ToolStripMenuItem mnuStopRecordTapeFile;
}
}

View file

@ -11,57 +11,52 @@ namespace Mesen.GUI.Forms
{
public partial class frmMain
{
private void mnuGame_DropDownOpening(object sender, EventArgs e)
{
InitializeVsSystemMenu();
InitializeFdsDiskMenu();
bool hasBarcodeReader = InteropEmu.GetAvailableFeatures().HasFlag(ConsoleFeatures.BarcodeReader);
mnuInputBarcode.Visible = hasBarcodeReader;
bool hasTapeRecorder = InteropEmu.GetAvailableFeatures().HasFlag(ConsoleFeatures.TapeRecorder);
mnuTapeRecorder.Visible = hasTapeRecorder;
sepBarcode.Visible = hasBarcodeReader || hasTapeRecorder;
}
private void InitializeFdsDiskMenu()
{
if(this.InvokeRequired) {
this.BeginInvoke((MethodInvoker)(() => this.InitializeFdsDiskMenu()));
} else {
UInt32 sideCount = InteropEmu.FdsGetSideCount();
UInt32 sideCount = InteropEmu.FdsGetSideCount();
mnuSelectDisk.DropDownItems.Clear();
mnuSelectDisk.DropDownItems.Clear();
if(sideCount > 0) {
for(UInt32 i = 0; i < sideCount; i++) {
UInt32 diskNumber = i;
ToolStripItem item = mnuSelectDisk.DropDownItems.Add(ResourceHelper.GetMessage("FdsDiskSide", (diskNumber/2+1).ToString(), (diskNumber % 2 == 0 ? "A" : "B")));
item.Click += (object sender, EventArgs args) => {
InteropEmu.FdsInsertDisk(diskNumber);
};
}
sepFdsDisk.Visible = true;
mnuSelectDisk.Visible = true;
mnuEjectDisk.Visible = true;
mnuSwitchDiskSide.Visible = sideCount > 1;
} else {
sepFdsDisk.Visible = false;
mnuSelectDisk.Visible = false;
mnuEjectDisk.Visible = false;
mnuSwitchDiskSide.Visible = false;
if(sideCount > 0) {
for(UInt32 i = 0; i < sideCount; i++) {
UInt32 diskNumber = i;
ToolStripItem item = mnuSelectDisk.DropDownItems.Add(ResourceHelper.GetMessage("FdsDiskSide", (diskNumber/2+1).ToString(), (diskNumber % 2 == 0 ? "A" : "B")));
item.Click += (object sender, EventArgs args) => {
InteropEmu.FdsInsertDisk(diskNumber);
};
}
sepFdsDisk.Visible = true;
mnuSelectDisk.Visible = true;
mnuEjectDisk.Visible = true;
mnuSwitchDiskSide.Visible = sideCount > 1;
} else {
sepFdsDisk.Visible = false;
mnuSelectDisk.Visible = false;
mnuEjectDisk.Visible = false;
mnuSwitchDiskSide.Visible = false;
}
}
private void InitializeVsSystemMenu()
{
if(this.InvokeRequired) {
this.BeginInvoke((MethodInvoker)(() => InitializeVsSystemMenu()));
} else {
sepVsSystem.Visible = InteropEmu.IsVsSystem();
mnuInsertCoin1.Visible = InteropEmu.IsVsSystem();
mnuInsertCoin2.Visible = InteropEmu.IsVsSystem();
mnuVsGameConfig.Visible = InteropEmu.IsVsSystem();
}
}
private void InitializeBarcodeReaderMenu()
{
if(this.InvokeRequired) {
this.BeginInvoke((MethodInvoker)(() => InitializeBarcodeReaderMenu()));
} else {
bool hasBarcodeReader = InteropEmu.GetAvailableFeatures().HasFlag(ConsoleFeatures.BarcodeReader);
sepBarcode.Visible = hasBarcodeReader;
mnuInputBarcode.Visible = hasBarcodeReader;
}
sepVsSystem.Visible = InteropEmu.IsVsSystem();
mnuInsertCoin1.Visible = InteropEmu.IsVsSystem();
mnuInsertCoin2.Visible = InteropEmu.IsVsSystem();
mnuVsGameConfig.Visible = InteropEmu.IsVsSystem();
}
private void ShowVsGameConfig()
@ -78,5 +73,34 @@ namespace Mesen.GUI.Forms
{
ShowVsGameConfig();
}
private void mnuLoadTapeFile_Click(object sender, EventArgs e)
{
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.SetFilter(ResourceHelper.GetMessage("FilterTapeFiles"));
ofd.InitialDirectory = ConfigManager.SaveFolder;
ofd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".fbt";
if(ofd.ShowDialog() == DialogResult.OK) {
InteropEmu.LoadTapeFile(ofd.FileName);
}
}
}
private void mnuStartRecordTapeFile_Click(object sender, EventArgs e)
{
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.SetFilter(ResourceHelper.GetMessage("FilterTapeFiles"));
sfd.InitialDirectory = ConfigManager.SaveFolder;
sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".fbt";
if(sfd.ShowDialog() == DialogResult.OK) {
InteropEmu.StartRecordingTapeFile(sfd.FileName);
}
}
}
private void mnuStopRecordTapeFile_Click(object sender, EventArgs e)
{
InteropEmu.StopRecordingTapeFile();
}
}
}

View file

@ -122,7 +122,6 @@ namespace Mesen.GUI.Forms
ResourceHelper.ApplyResources(this);
UpdateMenus();
UpdateRecentFiles();
InitializeFdsDiskMenu();
InitializeNsfMode(true);
ctrlRecentGames.UpdateGameInfo();
} else {

View file

@ -156,10 +156,7 @@ namespace Mesen.GUI.Forms
_overrideWindowSize = true;
}
InitializeVsSystemMenu();
InitializeFdsDiskMenu();
InitializeEmulationSpeedMenu();
InitializeBarcodeReaderMenu();
UpdateVideoSettings();
@ -426,9 +423,6 @@ namespace Mesen.GUI.Forms
_currentGame = InteropEmu.GetRomInfo().GetRomName();
InteropEmu.SetNesModel(ConfigManager.Config.Region);
InitializeNsfMode(false, true);
InitializeFdsDiskMenu();
InitializeVsSystemMenu();
InitializeBarcodeReaderMenu();
CheatInfo.ApplyCheats();
VsConfigInfo.ApplyConfig();
UpdateStateMenu(mnuSaveState, true);
@ -874,6 +868,12 @@ namespace Mesen.GUI.Forms
mnuTestRecordTest.Enabled = !netPlay && !moviePlaying && !movieRecording;
mnuTestRecordFrom.Enabled = (mnuTestRecordStart.Enabled || mnuTestRecordNow.Enabled || mnuTestRecordMovie.Enabled || mnuTestRecordTest.Enabled);
bool tapeRecording = InteropEmu.IsRecordingTapeFile();
mnuTapeRecorder.Enabled = !isNetPlayClient;
mnuLoadTapeFile.Enabled = !isNetPlayClient;
mnuStartRecordTapeFile.Enabled = !tapeRecording && !isNetPlayClient;
mnuStopRecordTapeFile.Enabled = tapeRecording;
mnuDebugger.Visible = !devMode;
mnuHdPackEditor.Enabled = !netPlay && running;

View file

@ -861,6 +861,7 @@
<Compile Include="ResourceManager.cs" />
<Compile Include="RuntimeChecker.cs" />
<Compile Include="SingleInstance.cs" />
<None Include="Resources\Tape.png" />
<None Include="Resources\DownArrowWin10.png" />
<None Include="app.manifest" />
<None Include="Resources\Exclamation.png" />
@ -1275,6 +1276,7 @@
<None Include="Resources\BreakpointDisabled.png" />
<None Include="Resources\BreakpointEnableDisable.png" />
<None Include="Resources\ArrowKeys.png" />
<None Include="Resources\Barcode.png" />
<Content Include="Resources\coins.png" />
<None Include="Resources\DipSwitches.png" />
<None Include="Resources\MesenIcon.png" />

View file

@ -140,6 +140,11 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void InputBarcode(UInt64 barcode, Int32 digitCount);
[DllImport(DLLPath)] public static extern void LoadTapeFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath);
[DllImport(DLLPath)] public static extern void StartRecordingTapeFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath);
[DllImport(DLLPath)] public static extern void StopRecordingTapeFile();
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRecordingTapeFile();
[DllImport(DLLPath)] public static extern void SetCheats([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]InteropCheatInfo[] cheats, UInt32 length);
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool CheckFlag(EmulationFlags flag);
@ -1836,6 +1841,7 @@ namespace Mesen.GUI
Nsf = 2,
VsSystem = 4,
BarcodeReader = 8,
TapeRecorder = 16,
}
public enum ScreenRotation

View file

@ -19,7 +19,7 @@ namespace Mesen.GUI.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@ -100,6 +100,16 @@ namespace Mesen.GUI.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Barcode {
get {
object obj = ResourceManager.GetObject("Barcode", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@ -790,6 +800,16 @@ namespace Mesen.GUI.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Tape {
get {
object obj = ResourceManager.GetObject("Tape", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View file

@ -358,4 +358,10 @@
<data name="DownArrowWin10" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DownArrowWin10.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Barcode" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Barcode.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Tape" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Tape.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

BIN
GUI.NET/Resources/Tape.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

View file

@ -460,6 +460,11 @@ namespace InteropEmu {
DllExport void __stdcall InputBarcode(uint64_t barCode, int32_t digitCount) { Console::GetInstance()->InputBarcode(barCode, digitCount); }
DllExport void __stdcall LoadTapeFile(char *filepath) { Console::GetInstance()->LoadTapeFile(filepath); }
DllExport void __stdcall StartRecordingTapeFile(char *filepath) { Console::GetInstance()->StartRecordingTapeFile(filepath); }
DllExport void __stdcall StopRecordingTapeFile() { Console::GetInstance()->StopRecordingTapeFile(); }
DllExport bool __stdcall IsRecordingTapeFile() { return Console::GetInstance()->IsRecordingTapeFile(); }
DllExport ConsoleFeatures __stdcall GetAvailableFeatures() { return Console::GetInstance()->GetAvailableFeatures(); }
//NSF functions

44
Utilities/Base64.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include "stdafx.h"
class Base64
{
public:
static string Encode(const vector<uint8_t> data)
{
std::string out;
int val = 0, valb = -6;
for(uint8_t c : data) {
val = (val << 8) + c;
valb += 8;
while(valb >= 0) {
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]);
valb -= 6;
}
}
if(valb>-6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]);
while(out.size() % 4) out.push_back('=');
return out;
}
static vector<uint8_t> Decode(string in)
{
vector<uint8_t> out;
vector<int> T(256, -1);
for(int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
int val = 0, valb = -8;
for(uint8_t c : in) {
if(T[c] == -1) break;
val = (val << 6) + T[c];
valb += 6;
if(valb >= 0) {
out.push_back(val >> valb);
valb -= 8;
}
}
return out;
}
};

View file

@ -323,6 +323,7 @@
<ItemGroup>
<ClInclude Include="ArchiveReader.h" />
<ClInclude Include="AviWriter.h" />
<ClInclude Include="Base64.h" />
<ClInclude Include="blip_buf.h" />
<ClInclude Include="BpsPatcher.h" />
<ClInclude Include="CamstudioCodec.h" />

View file

@ -161,6 +161,9 @@
<ClInclude Include="stb_vorbis.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Base64.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">