2017-07-11 01:43:58 +00:00
|
|
|
//
|
|
|
|
// CSW.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 10/07/2017.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
2017-07-11 01:43:58 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#include "CSW.hpp"
|
|
|
|
|
2019-08-25 19:09:04 +00:00
|
|
|
#include "../../FileHolder.hpp"
|
|
|
|
|
2017-11-12 22:46:06 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
2017-07-11 01:43:58 +00:00
|
|
|
using namespace Storage::Tape;
|
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
CSW::CSW(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
|
|
|
|
|
|
|
|
CSW::CSW(const std::vector<uint8_t> &&data, CompressionType type, bool initial_level, uint32_t sampling_rate) :
|
|
|
|
Tape(serialiser_),
|
|
|
|
serialiser_(std::move(data), type, initial_level, sampling_rate) {}
|
|
|
|
|
|
|
|
|
2024-12-04 03:54:29 +00:00
|
|
|
CSW::Serialiser::Serialiser(const std::string &file_name) : source_data_pointer_(0) {
|
2024-12-03 14:21:13 +00:00
|
|
|
Storage::FileHolder file(file_name, FileHolder::FileMode::Read);
|
2018-02-23 02:28:12 +00:00
|
|
|
if(file.stats().st_size < 0x20) throw ErrorNotCSW;
|
2017-07-12 02:41:10 +00:00
|
|
|
|
|
|
|
// Check signature.
|
2018-02-23 02:28:12 +00:00
|
|
|
if(!file.check_signature("Compressed Square Wave")) {
|
2017-11-03 02:32:00 +00:00
|
|
|
throw ErrorNotCSW;
|
|
|
|
}
|
2017-07-12 02:41:10 +00:00
|
|
|
|
|
|
|
// Check terminating byte.
|
2018-02-23 02:28:12 +00:00
|
|
|
if(file.get8() != 0x1a) throw ErrorNotCSW;
|
2017-07-12 02:41:10 +00:00
|
|
|
|
|
|
|
// Get version file number.
|
2024-12-04 03:54:29 +00:00
|
|
|
const uint8_t major_version = file.get8();
|
|
|
|
const uint8_t minor_version = file.get8();
|
2017-07-12 02:41:10 +00:00
|
|
|
|
|
|
|
// Reject if this is an unknown version.
|
|
|
|
if(major_version > 2 || !major_version || minor_version > 1) throw ErrorNotCSW;
|
|
|
|
|
|
|
|
// The header now diverges based on version.
|
2017-07-13 01:23:59 +00:00
|
|
|
uint32_t number_of_waves = 0;
|
2017-07-12 02:41:10 +00:00
|
|
|
if(major_version == 1) {
|
2018-02-23 02:28:12 +00:00
|
|
|
pulse_.length.clock_rate = file.get16le();
|
2017-07-12 02:41:10 +00:00
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
if(file.get8() != 1) throw ErrorNotCSW;
|
2017-11-12 21:42:53 +00:00
|
|
|
compression_type_ = CompressionType::RLE;
|
2017-07-12 02:41:10 +00:00
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
pulse_.type = (file.get8() & 1) ? Pulse::High : Pulse::Low;
|
2017-07-12 02:41:10 +00:00
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
file.seek(0x20, SEEK_SET);
|
2017-07-12 02:41:10 +00:00
|
|
|
} else {
|
2018-02-23 02:28:12 +00:00
|
|
|
pulse_.length.clock_rate = file.get32le();
|
|
|
|
number_of_waves = file.get32le();
|
|
|
|
switch(file.get8()) {
|
2017-11-12 21:42:53 +00:00
|
|
|
case 1: compression_type_ = CompressionType::RLE; break;
|
|
|
|
case 2: compression_type_ = CompressionType::ZRLE; break;
|
2017-07-12 02:41:10 +00:00
|
|
|
default: throw ErrorNotCSW;
|
|
|
|
}
|
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
pulse_.type = (file.get8() & 1) ? Pulse::High : Pulse::Low;
|
|
|
|
uint8_t extension_length = file.get8();
|
2017-07-12 02:41:10 +00:00
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
if(file.stats().st_size < 0x34 + extension_length) throw ErrorNotCSW;
|
|
|
|
file.seek(0x34 + extension_length, SEEK_SET);
|
2017-07-12 02:41:10 +00:00
|
|
|
}
|
|
|
|
|
2018-02-23 02:28:12 +00:00
|
|
|
// Grab all data remaining in the file.
|
|
|
|
std::vector<uint8_t> file_data;
|
2024-12-04 03:54:29 +00:00
|
|
|
const std::size_t remaining_data = size_t(file.stats().st_size) - size_t(file.tell());
|
2018-02-23 02:28:12 +00:00
|
|
|
file_data.resize(remaining_data);
|
|
|
|
file.read(file_data.data(), remaining_data);
|
|
|
|
|
2017-11-12 21:42:53 +00:00
|
|
|
if(compression_type_ == CompressionType::ZRLE) {
|
2017-07-17 23:04:25 +00:00
|
|
|
// The only clue given by CSW as to the output size in bytes is that there will be
|
|
|
|
// number_of_waves waves. Waves are usually one byte, but may be five. So this code
|
|
|
|
// is pessimistic.
|
2020-05-10 03:00:39 +00:00
|
|
|
source_data_.resize(size_t(number_of_waves) * 5);
|
2017-07-13 01:23:59 +00:00
|
|
|
|
2017-07-17 23:04:25 +00:00
|
|
|
// uncompress will tell how many compressed bytes there actually were, so use its
|
|
|
|
// modification of output_length to throw away all the memory that isn't actually
|
|
|
|
// needed.
|
2020-05-10 03:00:39 +00:00
|
|
|
uLongf output_length = uLongf(number_of_waves * 5);
|
2017-07-13 01:23:59 +00:00
|
|
|
uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size());
|
2020-05-10 03:00:39 +00:00
|
|
|
source_data_.resize(std::size_t(output_length));
|
2017-07-13 01:23:59 +00:00
|
|
|
} else {
|
2018-02-23 02:28:12 +00:00
|
|
|
source_data_ = std::move(file_data);
|
2017-07-12 02:41:10 +00:00
|
|
|
}
|
2017-07-13 01:23:59 +00:00
|
|
|
|
|
|
|
invert_pulse();
|
2017-07-11 01:43:58 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:54:29 +00:00
|
|
|
CSW::Serialiser::Serialiser(
|
|
|
|
const std::vector<uint8_t> &&data,
|
2024-12-04 03:57:38 +00:00
|
|
|
const CompressionType compression_type,
|
|
|
|
const bool initial_level,
|
|
|
|
const uint32_t sampling_rate
|
2024-12-04 03:54:29 +00:00
|
|
|
) : compression_type_(compression_type) {
|
2018-02-23 02:28:12 +00:00
|
|
|
pulse_.length.clock_rate = sampling_rate;
|
|
|
|
pulse_.type = initial_level ? Pulse::High : Pulse::Low;
|
|
|
|
source_data_ = std::move(data);
|
|
|
|
}
|
2017-11-12 22:46:06 +00:00
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
uint8_t CSW::Serialiser::get_next_byte() {
|
2018-02-23 02:28:12 +00:00
|
|
|
if(source_data_pointer_ == source_data_.size()) return 0xff;
|
2024-12-04 03:54:29 +00:00
|
|
|
|
|
|
|
const uint8_t result = source_data_[source_data_pointer_];
|
2018-02-23 02:28:12 +00:00
|
|
|
source_data_pointer_++;
|
|
|
|
return result;
|
2017-07-12 02:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
uint32_t CSW::Serialiser::get_next_int32le() {
|
2018-02-23 02:28:12 +00:00
|
|
|
if(source_data_pointer_ > source_data_.size() - 4) return 0xffff;
|
2024-12-04 03:54:29 +00:00
|
|
|
|
|
|
|
const uint32_t result = uint32_t(
|
2018-02-23 02:28:12 +00:00
|
|
|
(source_data_[source_data_pointer_ + 0] << 0) |
|
|
|
|
(source_data_[source_data_pointer_ + 1] << 8) |
|
|
|
|
(source_data_[source_data_pointer_ + 2] << 16) |
|
|
|
|
(source_data_[source_data_pointer_ + 3] << 24));
|
|
|
|
source_data_pointer_ += 4;
|
|
|
|
return result;
|
2017-07-14 00:57:27 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
void CSW::Serialiser::invert_pulse() {
|
2017-07-13 01:23:59 +00:00
|
|
|
pulse_.type = (pulse_.type == Pulse::High) ? Pulse::Low : Pulse::High;
|
|
|
|
}
|
2017-07-12 02:41:10 +00:00
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
bool CSW::Serialiser::is_at_end() const {
|
2018-02-23 02:28:12 +00:00
|
|
|
return source_data_pointer_ == source_data_.size();
|
2017-07-11 01:43:58 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:28:57 +00:00
|
|
|
void CSW::Serialiser::reset() {
|
2018-02-23 02:28:12 +00:00
|
|
|
source_data_pointer_ = 0;
|
2017-07-11 01:43:58 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:54:29 +00:00
|
|
|
Pulse CSW::Serialiser::next_pulse() {
|
2017-07-13 01:23:59 +00:00
|
|
|
invert_pulse();
|
|
|
|
pulse_.length.length = get_next_byte();
|
2017-07-14 00:57:27 +00:00
|
|
|
if(!pulse_.length.length) pulse_.length.length = get_next_int32le();
|
2017-07-13 01:23:59 +00:00
|
|
|
return pulse_;
|
2017-07-11 01:43:58 +00:00
|
|
|
}
|