mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Brother disks are now fully decoded and stitched together into images. Hurray!
This commit is contained in:
18
lib/crc.cc
18
lib/crc.cc
@@ -8,10 +8,26 @@ uint16_t crc16(uint16_t poly, const uint8_t* start, const uint8_t* end)
|
|||||||
while (start != end)
|
while (start != end)
|
||||||
{
|
{
|
||||||
crc ^= *start++ << 8;
|
crc ^= *start++ << 8;
|
||||||
for (int i=0; i < 8; i++)
|
for (int i=0; i<8; i++)
|
||||||
crc = (crc & 0x8000) ? ((crc<<1)^poly) : (crc<<1);
|
crc = (crc & 0x8000) ? ((crc<<1)^poly) : (crc<<1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Thanks to user202729 on StackOverflow for miraculously reverse engineering
|
||||||
|
* this. */
|
||||||
|
uint32_t crcbrother(const uint8_t* start, const uint8_t* end)
|
||||||
|
{
|
||||||
|
uint32_t crc = *start++;
|
||||||
|
|
||||||
|
while (start != end)
|
||||||
|
{
|
||||||
|
for (int i=0; i<8; i++)
|
||||||
|
crc = (crc & 0x800000) ? ((crc<<1)^BROTHER_POLY) : (crc<<1);
|
||||||
|
crc ^= *start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
#define CRC_H
|
#define CRC_H
|
||||||
|
|
||||||
#define CCITT_POLY 0x1021
|
#define CCITT_POLY 0x1021
|
||||||
|
#define BROTHER_POLY 0x000201
|
||||||
|
|
||||||
extern uint16_t crc16(uint16_t poly, const uint8_t* start, const uint8_t* end);
|
extern uint16_t crc16(uint16_t poly, const uint8_t* start, const uint8_t* end);
|
||||||
|
extern uint32_t crcbrother(const uint8_t* start, const uint8_t* end);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "decoders.h"
|
#include "decoders.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
#include "crc.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsBrother(const std::vector<std::vector<uint8_t>>& records)
|
std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsBrother(const std::vector<std::vector<uint8_t>>& records)
|
||||||
@@ -20,19 +21,22 @@ std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsBrother(const std::vec
|
|||||||
nextTrack = record[1];
|
nextTrack = record[1];
|
||||||
nextSector = record[2];
|
nextSector = record[2];
|
||||||
hasHeader = true;
|
hasHeader = true;
|
||||||
/* TODO: check CRC! */
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BROTHER_DATA_RECORD & 0xff:
|
case BROTHER_DATA_RECORD & 0xff:
|
||||||
{
|
{
|
||||||
if (record.size() < (BROTHER_DATA_RECORD_PAYLOAD+1))
|
if (record.size() < (BROTHER_DATA_RECORD_PAYLOAD+1))
|
||||||
goto garbage;
|
goto garbage;
|
||||||
/* TODO: check CRC! */
|
if (!hasHeader)
|
||||||
|
goto garbage;
|
||||||
|
uint32_t realCrc = crcbrother(&record[1], &record[257]);
|
||||||
|
uint32_t wantCrc = (record[257]<<16) | (record[258]<<8) | record[259];
|
||||||
|
int status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||||
|
|
||||||
std::vector<uint8_t> sectordata(BROTHER_DATA_RECORD_PAYLOAD);
|
std::vector<uint8_t> sectordata(BROTHER_DATA_RECORD_PAYLOAD);
|
||||||
memcpy(§ordata[0], &record[1], BROTHER_DATA_RECORD_PAYLOAD);
|
memcpy(§ordata[0], &record[1], BROTHER_DATA_RECORD_PAYLOAD);
|
||||||
|
|
||||||
auto sector = std::unique_ptr<Sector>(new Sector(Sector::OK, nextTrack, 0, nextSector, sectordata));
|
auto sector = std::unique_ptr<Sector>(new Sector(status, nextTrack, 0, nextSector, sectordata));
|
||||||
sectors.push_back(std::move(sector));
|
sectors.push_back(std::move(sector));
|
||||||
hasHeader = false;
|
hasHeader = false;
|
||||||
break;
|
break;
|
||||||
|
|||||||
12
lib/flags.cc
12
lib/flags.cc
@@ -79,6 +79,16 @@ void Flag::parseFlags(int argc, const char* argv[])
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BoolFlag::set(const std::string& value)
|
||||||
|
{
|
||||||
|
if ((value == "true") || (value == "y"))
|
||||||
|
_value = true;
|
||||||
|
else if ((value == "false") || (value == "n"))
|
||||||
|
_value = false;
|
||||||
|
else
|
||||||
|
Error() << "can't parse '" << value << "'; try 'true' or 'false'";
|
||||||
|
}
|
||||||
|
|
||||||
static void doHelp()
|
static void doHelp()
|
||||||
{
|
{
|
||||||
std::cout << "FluxEngine options:" << std::endl;
|
std::cout << "FluxEngine options:" << std::endl;
|
||||||
@@ -104,4 +114,4 @@ static void doHelp()
|
|||||||
static ActionFlag helpFlag = ActionFlag(
|
static ActionFlag helpFlag = ActionFlag(
|
||||||
{ "--help", "-h" },
|
{ "--help", "-h" },
|
||||||
"Shows the help.",
|
"Shows the help.",
|
||||||
doHelp);
|
doHelp);
|
||||||
|
|||||||
24
lib/flags.h
24
lib/flags.h
@@ -15,7 +15,7 @@ public:
|
|||||||
|
|
||||||
virtual bool hasArgument() const = 0;
|
virtual bool hasArgument() const = 0;
|
||||||
virtual const std::string defaultValue() const = 0;
|
virtual const std::string defaultValue() const = 0;
|
||||||
virtual void set(const std::string value) = 0;
|
virtual void set(const std::string& value) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::vector<std::string> _names;
|
const std::vector<std::string> _names;
|
||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
|
|
||||||
bool hasArgument() const { return false; }
|
bool hasArgument() const { return false; }
|
||||||
const std::string defaultValue() const { return ""; }
|
const std::string defaultValue() const { return ""; }
|
||||||
void set(const std::string value) { _callback(); }
|
void set(const std::string& value) { _callback(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::function<void(void)> _callback;
|
const std::function<void(void)> _callback;
|
||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
|
|
||||||
bool hasArgument() const { return false; }
|
bool hasArgument() const { return false; }
|
||||||
const std::string defaultValue() const { return "false"; }
|
const std::string defaultValue() const { return "false"; }
|
||||||
void set(const std::string value) { _value = true; }
|
void set(const std::string& value) { _value = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _value = false;
|
bool _value = false;
|
||||||
@@ -86,7 +86,7 @@ public:
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
const std::string defaultValue() const { return _defaultValue; }
|
const std::string defaultValue() const { return _defaultValue; }
|
||||||
void set(const std::string value) { _value = value; }
|
void set(const std::string& value) { _value = value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class IntFlag : public ValueFlag<int>
|
class IntFlag : public ValueFlag<int>
|
||||||
@@ -98,7 +98,7 @@ public:
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
||||||
void set(const std::string value) { _value = std::stoi(value); }
|
void set(const std::string& value) { _value = std::stoi(value); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class DoubleFlag : public ValueFlag<double>
|
class DoubleFlag : public ValueFlag<double>
|
||||||
@@ -110,7 +110,19 @@ public:
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
const std::string defaultValue() const { return std::to_string(_defaultValue); }
|
||||||
void set(const std::string value) { _value = std::stod(value); }
|
void set(const std::string& value) { _value = std::stod(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class BoolFlag : public ValueFlag<double>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BoolFlag(const std::vector<std::string>& names, const std::string helptext,
|
||||||
|
bool defaultValue = false):
|
||||||
|
ValueFlag(names, helptext, defaultValue)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string defaultValue() const { return _defaultValue ? "true" : "false"; }
|
||||||
|
void set(const std::string& value);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,19 +5,30 @@
|
|||||||
#include "decoders.h"
|
#include "decoders.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
static StringFlag outputFilename(
|
static StringFlag outputFilename(
|
||||||
{ "--output", "-o" },
|
{ "--output", "-o" },
|
||||||
"The output image file to write to.",
|
"The output image file to write to.",
|
||||||
"brother.img");
|
"brother.img");
|
||||||
|
|
||||||
|
static SettableFlag dumpRecords(
|
||||||
|
{ "--dump-records" },
|
||||||
|
"Dump the parsed records.");
|
||||||
|
|
||||||
|
#define SECTOR_COUNT 12
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
Flag::parseFlags(argc, argv);
|
Flag::parseFlags(argc, argv);
|
||||||
|
|
||||||
|
bool failures = false;
|
||||||
|
std::ofstream o("records.txt");
|
||||||
std::vector<std::unique_ptr<Sector>> allSectors;
|
std::vector<std::unique_ptr<Sector>> allSectors;
|
||||||
for (auto& track : readTracks())
|
for (auto& track : readTracks())
|
||||||
{
|
{
|
||||||
|
int retries = 5;
|
||||||
|
retry:
|
||||||
Fluxmap& fluxmap = track->read();
|
Fluxmap& fluxmap = track->read();
|
||||||
|
|
||||||
nanoseconds_t clockPeriod = fluxmap.guessClock();
|
nanoseconds_t clockPeriod = fluxmap.guessClock();
|
||||||
@@ -32,16 +43,69 @@ int main(int argc, const char* argv[])
|
|||||||
auto sectors = parseRecordsToSectorsBrother(records);
|
auto sectors = parseRecordsToSectorsBrother(records);
|
||||||
std::cout << " " << sectors.size() << " sectors; ";
|
std::cout << " " << sectors.size() << " sectors; ";
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Sector>> goodSectors(SECTOR_COUNT);
|
||||||
|
for (auto& sector : sectors)
|
||||||
|
{
|
||||||
|
if (sector->status == Sector::OK)
|
||||||
|
goodSectors.at(sector->sector) = std::move(sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasBadSectors = false;
|
||||||
|
for (int i=0; i<SECTOR_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (!goodSectors.at(i))
|
||||||
|
{
|
||||||
|
std::cout << std::endl
|
||||||
|
<< " Failed to read sector " << i << "; ";
|
||||||
|
hasBadSectors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasBadSectors)
|
||||||
|
{
|
||||||
|
if (retries == 0)
|
||||||
|
failures = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << std::endl
|
||||||
|
<< " " << retries << " retries remaining" << std::endl;
|
||||||
|
retries--;
|
||||||
|
track->forceReread();
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
for (auto& sector : sectors)
|
for (auto& sector : goodSectors)
|
||||||
{
|
{
|
||||||
size += sector->data.size();
|
size += sector->data.size();
|
||||||
allSectors.push_back(std::move(sector));
|
allSectors.push_back(std::move(sector));
|
||||||
}
|
}
|
||||||
std::cout << size << " bytes decoded." << std::endl;
|
std::cout << size << " bytes decoded." << std::endl;
|
||||||
|
|
||||||
|
if (dumpRecords)
|
||||||
|
{
|
||||||
|
std::cout << "\nRaw records follow:\n\n";
|
||||||
|
for (auto& record : records)
|
||||||
|
{
|
||||||
|
hexdump(std::cout, record);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& record : records)
|
||||||
|
{
|
||||||
|
if ((record.size() >= 260) && (record[0] == 0xdb))
|
||||||
|
{
|
||||||
|
for (int i=1; i<260; i++)
|
||||||
|
o << fmt::format("{:02x}", record[i]);
|
||||||
|
o << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSectorsToFile(allSectors, outputFilename);
|
writeSectorsToFile(allSectors, outputFilename);
|
||||||
|
if (failures)
|
||||||
|
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user