2017-11-19 23:08:23 -05:00
|
|
|
#pragma once
|
|
|
|
#include "stdafx.h"
|
2017-11-24 21:38:12 -05:00
|
|
|
#include "../Utilities/Base64.h"
|
2017-11-19 23:08:23 -05:00
|
|
|
#include "BaseControlDevice.h"
|
2018-07-01 15:21:05 -04:00
|
|
|
#include "Console.h"
|
2017-11-19 23:08:23 -05:00
|
|
|
#include "CPU.h"
|
|
|
|
|
|
|
|
class FamilyBasicDataRecorder : public BaseControlDevice
|
|
|
|
{
|
|
|
|
private:
|
2018-07-01 15:21:05 -04:00
|
|
|
static constexpr int32_t SamplingRate = 88;
|
2017-11-24 21:38:12 -05:00
|
|
|
vector<uint8_t> _data;
|
|
|
|
vector<uint8_t> _fileData;
|
2017-11-19 23:08:23 -05:00
|
|
|
bool _enabled = false;
|
2017-11-24 21:38:12 -05:00
|
|
|
bool _isPlaying = false;
|
2019-05-12 12:28:01 -04:00
|
|
|
uint64_t _cycle = 0;
|
2017-11-24 21:38:12 -05:00
|
|
|
|
|
|
|
bool _isRecording = false;
|
|
|
|
string _recordFilePath;
|
2017-11-19 23:08:23 -05:00
|
|
|
|
|
|
|
protected:
|
|
|
|
void StreamState(bool saving) override
|
|
|
|
{
|
|
|
|
BaseControlDevice::StreamState(saving);
|
2017-11-24 21:38:12 -05:00
|
|
|
|
2017-11-26 17:02:26 -05:00
|
|
|
VectorInfo<uint8_t> data{ &_data };
|
|
|
|
Stream(_enabled, _isPlaying, _cycle, data);
|
2017-11-24 21:38:12 -05:00
|
|
|
|
|
|
|
if(!saving && _isRecording) {
|
|
|
|
StopRecording();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-29 23:37:23 -05:00
|
|
|
bool IsRawString() override
|
2017-11-24 21:38:12 -05:00
|
|
|
{
|
|
|
|
return true;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
2017-11-29 23:37:23 -05:00
|
|
|
void InternalSetStateFromInput() override
|
|
|
|
{
|
|
|
|
if(_fileData.size() > 0) {
|
|
|
|
SetTextState(Base64::Encode(_fileData));
|
|
|
|
_fileData.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
public:
|
2018-07-13 22:19:26 -04:00
|
|
|
FamilyBasicDataRecorder(shared_ptr<Console> console) : BaseControlDevice(console, BaseControlDevice::ExpDevicePort2)
|
2017-11-24 21:38:12 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~FamilyBasicDataRecorder()
|
|
|
|
{
|
|
|
|
if(_isRecording) {
|
|
|
|
StopRecording();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-29 23:37:23 -05:00
|
|
|
void OnAfterSetState() override
|
2017-11-24 21:38:12 -05:00
|
|
|
{
|
2017-12-03 23:12:16 -05:00
|
|
|
if(GetRawState().State.size() > 0) {
|
2017-11-24 21:38:12 -05:00
|
|
|
_data = Base64::Decode(GetTextState());
|
2018-07-01 15:21:05 -04:00
|
|
|
_cycle = _console->GetCpu()->GetCycleCount();
|
2017-11-24 21:38:12 -05:00
|
|
|
_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();
|
2018-07-01 15:21:05 -04:00
|
|
|
_cycle = _console->GetCpu()->GetCycleCount();
|
2017-11-24 21:38:12 -05:00
|
|
|
_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());
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ReadRAM(uint16_t addr) override
|
|
|
|
{
|
2017-11-24 21:38:12 -05:00
|
|
|
if(addr == 0x4016 && _isPlaying) {
|
2020-01-26 19:54:52 -05:00
|
|
|
uint32_t readPos = (uint32_t)((_console->GetCpu()->GetCycleCount() - _cycle) / FamilyBasicDataRecorder::SamplingRate);
|
2017-11-24 21:38:12 -05:00
|
|
|
|
2019-05-12 12:28:01 -04:00
|
|
|
if((uint32_t)_data.size() > readPos / 8) {
|
2017-11-24 21:38:12 -05:00
|
|
|
uint8_t value = ((_data[readPos / 8] >> (readPos % 8)) & 0x01) << 1;
|
|
|
|
return _enabled ? value : 0;
|
2017-11-19 23:08:23 -05:00
|
|
|
} else {
|
2017-11-24 21:38:12 -05:00
|
|
|
_isPlaying = false;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteRAM(uint16_t addr, uint8_t value) override
|
|
|
|
{
|
|
|
|
_enabled = (value & 0x04) != 0;
|
|
|
|
|
2017-11-24 21:38:12 -05:00
|
|
|
if(_isRecording) {
|
2019-05-12 12:28:01 -04:00
|
|
|
while(_console->GetCpu()->GetCycleCount() - _cycle > FamilyBasicDataRecorder::SamplingRate) {
|
2017-11-24 21:38:12 -05:00
|
|
|
_data.push_back(value & 0x01);
|
|
|
|
_cycle += 88;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|