#ifndef FRC_REVLIB_SRC_MAIN_DRIVER_INCLUDE_REV_CANUPDATE_DFUSEFILE_H_
#define FRC_REVLIB_SRC_MAIN_DRIVER_INCLUDE_REV_CANUPDATE_DFUSEFILE_H_
#pragma once
#include <stdint.h>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
namespace dfuse {
class DFUElement {
public:
uint32_t Address() { return m_prefix.Address; }
int Size() const { return m_prefix.Size; }
const std::vector<uint8_t>& Data() const { return m_data; }
private:
friend std::istream& operator>>(std::istream& in, DFUElement& obj) {
in >> obj.m_prefix;
obj.m_data.resize(obj.m_prefix.Size);
in.read((char*)obj.m_data.data(), obj.m_prefix.Size);
return in;
}
struct Prefix {
uint32_t Address;
uint32_t Size;
friend std::istream& operator>>(std::istream& in, Prefix& obj) {
in.read((char*)&obj.Address, 4);
in.read((char*)&obj.Size, 4);
return in;
}
};
Prefix m_prefix;
std::vector<uint8_t> m_data;
};
namespace writer {
class FileWriter {
public:
FileWriter() {}
virtual void Write(std::ofstream& out, const DFUElement& target) = 0;
virtual std::unique_ptr<FileWriter> Clone() = 0;
};
class BinWriter : public FileWriter {
public:
BinWriter() {}
virtual void Write(std::ofstream& out, const DFUElement& target) override {
out.write((const char*)target.Data().data(), target.Size());
}
virtual std::unique_ptr<FileWriter> Clone() override {
return std::make_unique<BinWriter>(*this);
}
};
static BinWriter Bin;
}
class DFUImage {
public:
int Id() { return m_prefix.AltSetting; }
const char* Name() { return m_prefix.Name; }
int Size() { return m_prefix.Size; }
const std::vector<DFUElement>& Elements() const { return m_elements; }
unsigned int ElementsSize(int numElementsToSkip = 0) const {
if (m_elements.empty()) {
return 0;
}
if (numElementsToSkip >= m_elements.size()) {
return 0;
}
uint32_t size = 0;
DFUElement first = m_elements[numElementsToSkip];
DFUElement last = m_elements[m_elements.size() - 1];
size = last.Address() + last.Size() - first.Address();
return size;
}
void Write(const std::string filename, const int elementIndex,
writer::FileWriter& writer) {
std::remove(filename.c_str());
std::ofstream outputFile(filename, std::ofstream::binary);
auto fw = writer.Clone();
fw->Write(outputFile, m_elements[elementIndex]);
outputFile.close();
}
void WriteAll(const std::string filename, int numElementsToSkip = 0,
writer::FileWriter& writer = writer::Bin) {
std::remove(filename.c_str());
std::ofstream outputFile(filename, std::ofstream::binary);
if (!outputFile) {
std::cerr << "Failed to open file for writing: " << filename
<< std::endl;
return;
}
auto fw = writer.Clone();
for (int i = numElementsToSkip; i < m_elements.size(); i++) {
outputFile.seekp(m_elements[i].Address() -
m_elements[numElementsToSkip].Address(),
std::ios_base::beg);
fw->Write(outputFile, m_elements[i]);
}
}
operator bool() const { return m_valid; }
bool operator!() const { return !m_valid; }
private:
friend std::istream& operator>>(std::istream& in, DFUImage& obj) {
obj.m_valid = false;
in >> obj.m_prefix;
if (!in || std::memcmp(obj.m_prefix.Signature, "Target", 6) != 0) {
return in;
}
obj.m_elements.resize(obj.m_prefix.Elements);
for (DFUElement& target : obj.m_elements) {
in >> target;
if (!in) {
return in;
}
}
obj.m_valid = true;
return in;
}
struct Prefix {
uint8_t Signature[6];
uint8_t AltSetting;
uint32_t IsNamed;
char Name[255];
uint32_t Size;
uint32_t Elements;
friend std::istream& operator>>(std::istream& in, Prefix& obj) {
in.read((char*)obj.Signature, 6);
in.read((char*)&obj.AltSetting, 1);
in.read((char*)&obj.IsNamed, 4);
in.read(obj.Name, 255);
in.read((char*)&obj.Size, 4);
in.read((char*)&obj.Elements, 4);
return in;
}
};
Prefix m_prefix;
std::vector<DFUElement> m_elements;
bool m_valid;
};
class DFUFile {
public:
DFUFile(const char* filename) {
std::ifstream dfuFile(filename, std::ios_base::binary);
m_valid = false;
if (!dfuFile) {
return;
}
dfuFile >> m_prefix;
if (!dfuFile || std::memcmp(m_prefix.Signature, "DfuSe", 5) != 0) {
return;
}
m_images.resize(m_prefix.Targets);
assert(m_prefix.Targets == 1);
for (DFUImage& image : m_images) {
dfuFile >> image;
if (!dfuFile || !image) {
return;
}
}
dfuFile >> m_suffix;
m_valid = true;
dfuFile.close();
};
operator bool() const { return m_valid; }
bool operator!() const { return !m_valid; }
unsigned int FileFormatVersion() { return m_prefix.Version; }
unsigned int Vendor() { return m_suffix.Vendor; }
unsigned int Product() { return m_suffix.Product; }
unsigned int DeviceVersion() { return m_suffix.DeviceVersion; }
const std::vector<DFUImage>& Images() const { return m_images; }
uint32_t Crc() { return m_suffix.Crc32; }
private:
DFUFile(){};
bool m_valid;
struct Prefix {
uint8_t Signature[5];
uint8_t Version;
uint32_t Size;
uint8_t Targets;
friend std::istream& operator>>(std::istream& in, Prefix& obj) {
in.read((char*)obj.Signature, 5);
in.read((char*)&obj.Version, 1);
in.read((char*)&obj.Size, 4);
in.read((char*)&obj.Targets, 1);
return in;
}
};
Prefix m_prefix;
std::vector<DFUImage> m_images;
struct Suffix {
uint16_t DeviceVersion;
uint16_t Product;
uint16_t Vendor;
uint16_t DfuFormat;
uint8_t Ufd[3];
uint8_t Length;
uint32_t Crc32;
friend std::istream& operator>>(std::istream& in, Suffix& obj) {
in.read((char*)&obj.DeviceVersion, 2);
in.read((char*)&obj.Product, 2);
in.read((char*)&obj.Vendor, 2);
in.read((char*)&obj.DfuFormat, 2);
in.read((char*)obj.Ufd, 3);
in.read((char*)&obj.Length, 1);
in.read((char*)&obj.Crc32, 4);
return in;
}
};
Suffix m_suffix;
};
}
extern "C" {
inline bool c_Spark_DFUtoBin(const char* dfuFilePath, const char* binFilePath,
int elementsToSkip = 0) {
if (const dfuse::DFUFile dfuFile(dfuFilePath);
dfuFile && !dfuFile.Images().empty() && dfuFile.Images()[0]) {
auto image0 = dfuFile.Images()[0];
image0.WriteAll(binFilePath, elementsToSkip);
return true;
}
std::fprintf(stderr, "Failed to convert DFU to .bin\n");
return false;
}
}
#endif