gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 5af59f1e225d927862d1bc7badb9b8b214abaf8a
parent 92387e4c1132f7909a3a965649b29e9b359f6965
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Wed, 23 Mar 2022 21:44:06 +0100

add additional validation for demo packets including packet ordering and checksum

Diffstat:
Msource/virusLib/CMakeLists.txt | 1+
Asource/virusLib/demopacketvalidator.cpp | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/virusLib/demopacketvalidator.h | 33+++++++++++++++++++++++++++++++++
Msource/virusLib/demoplayback.cpp | 50+++++++++++---------------------------------------
4 files changed, 179 insertions(+), 39 deletions(-)

diff --git a/source/virusLib/CMakeLists.txt b/source/virusLib/CMakeLists.txt @@ -4,6 +4,7 @@ project(virusLib) add_library(virusLib STATIC) set(SOURCES + demopacketvalidator.cpp demopacketvalidator.h demoplayback.cpp demoplayback.h device.cpp device.h midiOutParser.cpp midiOutParser.h diff --git a/source/virusLib/demopacketvalidator.cpp b/source/virusLib/demopacketvalidator.cpp @@ -0,0 +1,134 @@ +#include "demopacketvalidator.h" + +#include <cassert> + +#include "dsp56kEmu/logging.h" + +namespace virusLib +{ + bool DemoPacketValidator::add(const Packet& _packet) + { + if(isComplete()) + return isValid(); + + if(_packet.size() < 11) + return isValid(); + + const auto cmd = _packet[5]; + + switch (cmd) + { + case 0x50: // Virus A second + case 0x55: // Virus B second + case 0x57: // Virus C second + { + const auto msb = _packet[6]; // packet number MSB + const auto lsb = _packet[7]; // packet number LSB + + uint8_t checksum = 0; + for(size_t i=5; i<_packet.size()-2; ++i) + checksum += _packet[i]; + checksum &= 0x7f; + + if(checksum != _packet[_packet.size()-2]) + { + LOG("Packet MSB " << static_cast<int>(msb) << " LSB " << static_cast<int>(lsb) << " is invalid, wrong checksum"); + m_valid = false; + return false; + } + + auto packetInvalid = [&]() + { + LOG("Packet invalid, expected packet index " << static_cast<int>(m_expectedMSB) << " " << static_cast<int>(m_expectedLSB) << " but got " << static_cast<int>(msb) << " " << static_cast<int>(lsb)); + m_valid = false; + return false; + }; + + auto matchExpected = [&]() + { + return msb == m_expectedMSB && lsb == m_expectedLSB; + }; + + if(msb == 127 && lsb >= 3) + { + if(lsb == 3) + { + if(!matchExpected()) + return packetInvalid(); + + m_packets.push_back(_packet); + + m_expectedLSB = ++m_offset; + } + else if(lsb < 126) + { + if(!matchExpected()) + return packetInvalid(); + m_expectedLSB = 0; + m_expectedMSB = 0; + } + else if(lsb == 127) + { + if(m_expectedMSB != msb) + { + LOG("Terminating too soon, missing packets"); + m_valid = false; + return false; + } + + m_complete = true; + if(!toBinary(m_data)) + m_valid = false; + return isComplete(); + } + } + else + { + if(!matchExpected()) + return packetInvalid(); + + m_packets.push_back(_packet); + + if(lsb == 3) + { + ++m_expectedMSB; + m_expectedLSB = 0; + } + else + ++m_expectedLSB; + } + } + break; + default: + // skip unknown packets + return true; + } + + return false; + } + + bool DemoPacketValidator::toBinary(std::vector<uint8_t>& _binary) const + { + if(!isComplete()) + return false; + + for (const auto& p : m_packets) + { + // midi bytes in a sysex frame can only carry 7 bit, not 8. They've chosen the easy way that costs more storage + // They transfer only one nibble of a ROM byte in one midi byte to ensure that the most significant nibble is + // always zero. By concating two nibbles together we get one ROM byte + for(size_t s=8; s<p.size()-2; s += 2) + { + const uint8_t a = p[s]; + const uint8_t b = p[s+1]; + if(a > 0xf || b > 0xf) + { + LOG("Invalid data, high nibble must be 0"); + return false; + } + _binary.push_back(static_cast<uint8_t>(b << 4) | a); + } + } + return true; + } +} diff --git a/source/virusLib/demopacketvalidator.h b/source/virusLib/demopacketvalidator.h @@ -0,0 +1,33 @@ +#pragma once + +#include <cstdint> +#include <vector> + +namespace virusLib +{ + class DemoPacketValidator + { + public: + using Packet = std::vector<uint8_t>; + + bool add(const Packet& _packet); + + bool isValid() const { return m_valid; } + bool isComplete() const { return isValid() && m_complete; } + + const std::vector<uint8_t>& getData() const { return m_data; } + + private: + bool toBinary(std::vector<uint8_t>& _binary) const; + + std::vector<Packet> m_packets; + std::vector<uint8_t> m_data; + + uint8_t m_expectedMSB = 127; + uint8_t m_expectedLSB = 8; + uint8_t m_offset = 8; + + bool m_valid = true; + bool m_complete = false; + }; +} diff --git a/source/virusLib/demoplayback.cpp b/source/virusLib/demoplayback.cpp @@ -12,6 +12,8 @@ #include <cstring> // memcpy +#include "demopacketvalidator.h" + namespace virusLib { constexpr auto g_timeScale_C = 57; // C OS 6.6 @@ -32,50 +34,20 @@ namespace virusLib std::vector<std::vector<uint8_t>> packets; synthLib::MidiToSysex::splitMultipleSysex(packets, sysex); - std::vector<uint8_t> temp; - - for(size_t i=0; i<packets.size(); ++i) - { - const auto& p = packets[i]; - - const auto cmd = p[5]; - - const uint32_t indexA = p[6]; // packet number MSB - const uint32_t indexB = p[7]; // packet number LSB + DemoPacketValidator validator; - switch(cmd) - { - case 0x50: // Virus A second - case 0x55: // Virus B second - case 0x57: // Virus C second - { - LOG("Packet " << i << " indexA = " << indexA << " indexB = " << indexB); - - if(indexB > 3) - { - continue; - } + for (const auto& packet : packets) + validator.add(packet); - // midi bytes in a sysex fram can only carry 7 bit, not 8. They've chosen the easy way that costs more storage - // They transfer one nibble of a ROM byte in each midi byte to ensure that the most significant nibble is always zero - // By concating two nibbles together we get one ROM byte - for(size_t s=8; s<p.size()-2; s += 2) - { - const uint8_t a = p[s]; - const uint8_t b = p[s+1]; - assert(a <= 0xf); - assert(b <= 0xf); - temp.push_back(static_cast<uint8_t>(b << 4) | a); - } - } - break; - } + if(!validator.isValid()) + { + LOG("Packet validation failed, packets missing or invalid"); + return false; } - if(temp.empty()) - return false; + const auto& data = validator.getData(); - return loadBinData(temp); + return loadBinData(data); } bool DemoPlayback::loadBinData(const std::vector<uint8_t>& _data)