Replace the upgradefluxfile builtin with a seperate upgrade-flux-file tool.

This allows us to remove all the SQL stuff from the main program, and restores
the ability to upgrade from version 2 SQL files.
This commit is contained in:
David Given
2022-02-04 21:27:24 +01:00
parent 091ef6d972
commit 89688394f8
23 changed files with 334 additions and 479 deletions

View File

@@ -7,7 +7,7 @@ AllowAllArgumentsOnNextLine: 'true'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None

View File

@@ -1,5 +1,4 @@
#include "globals.h"
#include "sql.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fluxsink/fluxsink.h"

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fluxsink/fluxsink.h"
@@ -18,8 +17,13 @@ class Fl2FluxSink : public FluxSink
{
public:
Fl2FluxSink(const Fl2FluxSinkProto& lconfig):
_config(lconfig),
_of(lconfig.filename(), std::ios::out | std::ios::binary)
Fl2FluxSink(lconfig.filename())
{
}
Fl2FluxSink(const std::string& filename):
_filename(filename),
_of(_filename, std::ios::out | std::ios::binary)
{
if (!_of.is_open())
Error() << "cannot open output file";
@@ -53,11 +57,11 @@ public:
operator std::string () const
{
return fmt::format("fl2({})", _config.filename());
return fmt::format("fl2({})", _filename);
}
private:
const Fl2FluxSinkProto& _config;
std::string _filename;
std::ofstream _of;
std::map<std::pair<unsigned, unsigned>, Bytes> _data;
};
@@ -67,4 +71,9 @@ std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const Fl2FluxSinkProto& co
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config));
}
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const std::string& filename)
{
return std::unique_ptr<FluxSink>(new Fl2FluxSink(filename));
}

View File

@@ -11,9 +11,6 @@ std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
{
switch (config.dest_case())
{
case FluxSinkProto::kFluxfile:
return createSqliteFluxSink(config.fluxfile());
case FluxSinkProto::kDrive:
return createHardwareFluxSink(config.drive());

View File

@@ -17,13 +17,14 @@ class FluxSink
public:
virtual ~FluxSink() {}
static std::unique_ptr<FluxSink> createSqliteFluxSink(const std::string& filename);
static std::unique_ptr<FluxSink> createHardwareFluxSink(const HardwareFluxSinkProto& config);
static std::unique_ptr<FluxSink> createAuFluxSink(const AuFluxSinkProto& config);
static std::unique_ptr<FluxSink> createVcdFluxSink(const VcdFluxSinkProto& config);
static std::unique_ptr<FluxSink> createScpFluxSink(const ScpFluxSinkProto& config);
static std::unique_ptr<FluxSink> createFl2FluxSink(const Fl2FluxSinkProto& config);
static std::unique_ptr<FluxSink> createFl2FluxSink(const std::string& filename);
static std::unique_ptr<FluxSink> create(const FluxSinkProto& config);
static void updateConfigForFilename(FluxSinkProto* proto, const std::string& filename);

View File

@@ -31,7 +31,6 @@ message Fl2FluxSinkProto {
message FluxSinkProto {
optional double rescale = 7 [ default = 1.0, (help) = "amount to multiply pulse periods by" ];
oneof dest {
string fluxfile = 1 [(help) = "name of destination flux file"];
HardwareFluxSinkProto drive = 2;
AuFluxSinkProto au = 3;
VcdFluxSinkProto vcd = 4;

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fluxsink/fluxsink.h"

View File

@@ -1,60 +0,0 @@
#include "globals.h"
#include "fluxmap.h"
#include "sql.h"
#include "fluxsink/fluxsink.h"
#include "flags.h"
#include "fmt/format.h"
#include <unistd.h>
class SqliteFluxSink : public FluxSink
{
public:
SqliteFluxSink(const std::string& filename):
_filename(filename)
{
if ((access(filename.c_str(), F_OK) == 0) && (remove(filename.c_str()) != 0))
Error() << fmt::format("failed to overwrite flux file");
_outdb = sqlOpen(filename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
int oldVersion = sqlReadIntProperty(_outdb, "version");
if ((oldVersion != 0) && (oldVersion != FLUX_VERSION_CURRENT))
Error() << fmt::format("that flux file is version {}, but this client is for version {}",
oldVersion, FLUX_VERSION_CURRENT);
sqlPrepareFlux(_outdb);
sqlStmt(_outdb, "BEGIN;");
sqlWriteIntProperty(_outdb, "version", FLUX_VERSION_CURRENT);
}
~SqliteFluxSink()
{
if (_outdb)
{
sqlStmt(_outdb, "COMMIT;");
sqlClose(_outdb);
}
}
public:
void writeFlux(int track, int side, Fluxmap& fluxmap)
{
return sqlWriteFlux(_outdb, track, side, fluxmap);
}
operator std::string () const
{
return fmt::format("fluxfile {}", _filename);
}
private:
sqlite3* _outdb;
std::string _filename;
};
std::unique_ptr<FluxSink> FluxSink::createSqliteFluxSink(const std::string& filename)
{
return std::unique_ptr<FluxSink>(new SqliteFluxSink(filename));
}

View File

@@ -1,7 +1,6 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fluxsink/fluxsink.h"

View File

@@ -53,10 +53,8 @@ std::unique_ptr<FluxSource> FluxSource::createFl2FluxSource(const Fl2FluxSourceP
char buffer[16];
std::ifstream(config.filename(), std::ios::in | std::ios::binary).read(buffer, 16);
if (strncmp(buffer, "SQLite format 3", 16) == 0)
{
std::cerr << "Warning: reading a deprecated flux file format; please upgrade it\n";
return FluxSource::createSqliteFluxSource(config.filename());
}
Error() << "this flux file is too old; please use the upgrade-flux-file to upgrade it";
return std::unique_ptr<FluxSource>(new Fl2FluxSource(config));
}

View File

@@ -18,9 +18,6 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
{
switch (config.source_case())
{
case FluxSourceProto::kFluxfile:
return createSqliteFluxSource(config.fluxfile());
case FluxSourceProto::kDrive:
return createHardwareFluxSource(config.drive());

View File

@@ -26,7 +26,6 @@ private:
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& config);
static std::unique_ptr<FluxSource> createScpFluxSource(const ScpFluxSourceProto& config);
static std::unique_ptr<FluxSource> createSqliteFluxSource(const std::string& filename);
static std::unique_ptr<FluxSource> createTestPatternFluxSource(const TestPatternFluxSourceProto& config);
public:

View File

@@ -41,7 +41,6 @@ message Fl2FluxSourceProto {
message FluxSourceProto {
optional double rescale = 9 [ default = 1.0, (help) = "amount to divide pulse periods by" ];
oneof source {
string fluxfile = 1 [default = "name of source flux file"];
HardwareFluxSourceProto drive = 2;
TestPatternFluxSourceProto test_pattern = 3;
EraseFluxSourceProto erase = 4;

View File

@@ -1,42 +0,0 @@
#include "globals.h"
#include "fluxmap.h"
#include "sql.h"
#include "fluxsource/fluxsource.h"
#include "fmt/format.h"
class SqliteFluxSource : public FluxSource
{
public:
SqliteFluxSource(const std::string& filename)
{
_indb = sqlOpen(filename, SQLITE_OPEN_READONLY);
int version = sqlGetVersion(_indb);
if (version != FLUX_VERSION_CURRENT)
Error() << fmt::format("that flux file is version {}, but this client is for version {}",
version, FLUX_VERSION_CURRENT);
}
~SqliteFluxSource()
{
if (_indb)
sqlClose(_indb);
}
public:
std::unique_ptr<Fluxmap> readFlux(int track, int side)
{
return sqlReadFlux(_indb, track, side);
}
void recalibrate() {}
private:
sqlite3* _indb;
};
std::unique_ptr<FluxSource> FluxSource::createSqliteFluxSource(const std::string& filename)
{
return std::unique_ptr<FluxSource>(new SqliteFluxSource(filename));
}

View File

@@ -5,7 +5,6 @@
#include "fluxsink/fluxsink.h"
#include "reader.h"
#include "fluxmap.h"
#include "sql.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "bytes.h"

View File

@@ -1,272 +0,0 @@
#include "globals.h"
#include "sql.h"
#include "fluxmap.h"
#include "bytes.h"
#include "fmt/format.h"
enum
{
COMPRESSION_NONE,
COMPRESSION_ZLIB
};
static bool hasProperties(sqlite3* db);
void sqlCheck(sqlite3* db, int i)
{
if (i != SQLITE_OK)
Error() << "database error: " << sqlite3_errmsg(db);
}
sqlite3* sqlOpen(const std::string filename, int flags)
{
sqlite3* db;
int i = sqlite3_open_v2(filename.c_str(), &db, flags, NULL);
if (i != SQLITE_OK)
Error() << "failed: " << sqlite3_errstr(i);
return db;
}
void sqlClose(sqlite3* db)
{
sqlite3_close(db);
}
void sqlStmt(sqlite3* db, const char* sql)
{
char* errmsg;
int i = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if (i != SQLITE_OK)
Error() << "database error: %s" << errmsg;
}
int sqlGetVersion(sqlite3* db)
{
return sqlReadIntProperty(db, "version");
}
bool hasProperties(sqlite3* db)
{
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='properties'",
-1, &stmt, NULL));
int i = sqlite3_step(stmt);
if (i != SQLITE_ROW)
Error() << "Error accessing sqlite metadata";
bool has_properties = sqlite3_column_int(stmt, 0) != 0;
sqlCheck(db, sqlite3_finalize(stmt));
return has_properties;
}
void sql_bind_blob(sqlite3* db, sqlite3_stmt* stmt, const char* name,
const void* ptr, size_t bytes)
{
sqlCheck(db, sqlite3_bind_blob(stmt,
sqlite3_bind_parameter_index(stmt, name),
ptr, bytes, SQLITE_TRANSIENT));
}
void sql_bind_int(sqlite3* db, sqlite3_stmt* stmt, const char* name, int value)
{
sqlCheck(db, sqlite3_bind_int(stmt,
sqlite3_bind_parameter_index(stmt, name),
value));
}
void sql_bind_string(sqlite3* db, sqlite3_stmt* stmt, const char* name, const char* value)
{
sqlCheck(db, sqlite3_bind_text(stmt,
sqlite3_bind_parameter_index(stmt, name),
value, -1, SQLITE_TRANSIENT));
}
void sqlPrepareFlux(sqlite3* db)
{
sqlStmt(db, "PRAGMA synchronous = OFF;");
sqlStmt(db, "PRAGMA auto_vacuum = FULL;");
sqlStmt(db, "BEGIN;");
sqlStmt(db, "CREATE TABLE IF NOT EXISTS properties ("
" key TEXT UNIQUE NOT NULL PRIMARY KEY,"
" value TEXT"
");");
sqlStmt(db, "CREATE TABLE IF NOT EXISTS zdata ("
" track INTEGER,"
" side INTEGER,"
" data BLOB,"
" compression INTEGER,"
" PRIMARY KEY(track, side)"
");");
sqlStmt(db, "COMMIT;");
}
void sqlWriteFlux(sqlite3* db, int track, int side, const Fluxmap& fluxmap)
{
const auto compressed = fluxmap.rawBytes().compress();
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"INSERT OR REPLACE INTO zdata (track, side, data, compression)"
" VALUES (:track, :side, :data, :compression)",
-1, &stmt, NULL));
sql_bind_int(db, stmt, ":track", track);
sql_bind_int(db, stmt, ":side", side);
sql_bind_blob(db, stmt, ":data", &compressed[0], compressed.size());
sql_bind_int(db, stmt, ":compression", COMPRESSION_ZLIB);
if (sqlite3_step(stmt) != SQLITE_DONE)
Error() << "failed to write to database: " << sqlite3_errmsg(db);
sqlCheck(db, sqlite3_finalize(stmt));
}
std::unique_ptr<Fluxmap> sqlReadFlux(sqlite3* db, int track, int side)
{
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"SELECT data, compression FROM zdata WHERE track=:track AND side=:side",
-1, &stmt, NULL));
sql_bind_int(db, stmt, ":track", track);
sql_bind_int(db, stmt, ":side", side);
auto fluxmap = std::unique_ptr<Fluxmap>(new Fluxmap());
int i = sqlite3_step(stmt);
if (i != SQLITE_DONE)
{
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
const uint8_t* blobptr = (const uint8_t*) sqlite3_column_blob(stmt, 0);
size_t bloblen = sqlite3_column_bytes(stmt, 0);
int compression = sqlite3_column_int(stmt, 1);
Bytes data(blobptr, bloblen);
switch (compression)
{
case COMPRESSION_NONE:
break;
case COMPRESSION_ZLIB:
data = data.decompress();
break;
default:
Error() << fmt::format("unsupported compression type {}", compression);
}
fluxmap->appendBytes(data);
}
sqlCheck(db, sqlite3_finalize(stmt));
return fluxmap;
}
std::vector<std::pair<unsigned, unsigned>> sqlFindFlux(sqlite3* db)
{
std::vector<std::pair<unsigned, unsigned>> output;
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"SELECT track, side FROM zdata",
-1, &stmt, NULL));
for (;;)
{
int i = sqlite3_step(stmt);
if (i == SQLITE_DONE)
break;
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
unsigned track = sqlite3_column_int(stmt, 0);
unsigned side = sqlite3_column_int(stmt, 1);
output.push_back(std::make_pair(track, side));
}
sqlCheck(db, sqlite3_finalize(stmt));
return output;
}
void sqlWriteStringProperty(sqlite3* db, const std::string& name, const std::string& value)
{
if (!hasProperties(db))
sqlPrepareFlux(db);
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"INSERT OR REPLACE INTO properties (key, value) VALUES (:key, :value)",
-1, &stmt, NULL));
sql_bind_string(db, stmt, ":key", name.c_str());
sql_bind_string(db, stmt, ":value", value.c_str());
if (sqlite3_step(stmt) != SQLITE_DONE)
Error() << "failed to write to database: " << sqlite3_errmsg(db);
sqlCheck(db, sqlite3_finalize(stmt));
}
std::string sqlReadStringProperty(sqlite3* db, const std::string& name)
{
if (!hasProperties(db))
return "";
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"SELECT value FROM properties WHERE key=:key",
-1, &stmt, NULL));
sql_bind_string(db, stmt, ":key", name.c_str());
int i = sqlite3_step(stmt);
std::string result;
if (i != SQLITE_DONE)
{
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
result = (const char*) sqlite3_column_text(stmt, 0);
}
sqlCheck(db, sqlite3_finalize(stmt));
return result;
}
void sqlWriteIntProperty(sqlite3* db, const std::string& name, long value)
{
if (!hasProperties(db))
sqlPrepareFlux(db);
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"INSERT OR REPLACE INTO properties (key, value) VALUES (:key, :value)",
-1, &stmt, NULL));
sql_bind_string(db, stmt, ":key", name.c_str());
sql_bind_int(db, stmt, ":value", value);
if (sqlite3_step(stmt) != SQLITE_DONE)
Error() << "failed to write to database: " << sqlite3_errmsg(db);
sqlCheck(db, sqlite3_finalize(stmt));
}
long sqlReadIntProperty(sqlite3* db, const std::string& name)
{
if (!hasProperties(db))
return 0;
sqlite3_stmt* stmt;
sqlCheck(db, sqlite3_prepare_v2(db,
"SELECT value FROM properties WHERE key=:key",
-1, &stmt, NULL));
sql_bind_string(db, stmt, ":key", name.c_str());
int i = sqlite3_step(stmt);
long result = 0;
if (i != SQLITE_DONE)
{
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
result = sqlite3_column_int(stmt, 0);
}
sqlCheck(db, sqlite3_finalize(stmt));
return result;
}

View File

@@ -1,35 +0,0 @@
#ifndef SQL_H
#define SQL_H
#include <sqlite3.h>
class Fluxmap;
enum
{
FLUX_VERSION_0, /* without properties table */
FLUX_VERSION_1,
FLUX_VERSION_2, /* new bytecode with index marks */
FLUX_VERSION_3, /* simplified bytecode with six-bit timer */
FLUX_VERSION_CURRENT = FLUX_VERSION_3,
};
extern void sqlCheck(sqlite3* db, int i);
extern sqlite3* sqlOpen(const std::string filename, int flags);
extern void sqlClose(sqlite3* db);
extern void sqlStmt(sqlite3* db, const char* sql);
extern int sqlGetVersion(sqlite3* db);
extern void sqlPrepareFlux(sqlite3* db);
extern void sqlWriteFlux(sqlite3* db, int track, int side, const Fluxmap& fluxmap);
extern std::unique_ptr<Fluxmap> sqlReadFlux(sqlite3* db, int track, int side);
extern std::vector<std::pair<unsigned, unsigned>> sqlFindFlux(sqlite3* db);
extern void sqlWriteStringProperty(sqlite3* db, const std::string& name, const std::string& value);
extern std::string sqlReadStringProperty(sqlite3* db, const std::string& name);
extern void sqlWriteIntProperty(sqlite3* db, const std::string& name, long value);
extern long sqlReadIntProperty(sqlite3* db, const std::string& name);
#endif

View File

@@ -2,7 +2,6 @@
#include "flags.h"
#include "fluxmap.h"
#include "writer.h"
#include "sql.h"
#include "protocol.h"
#include "usb/usb.h"
#include "encoders/encoders.h"
@@ -16,8 +15,6 @@
#include "lib/config.pb.h"
#include "proto.h"
static sqlite3* outdb;
void writeTracks(
FluxSink& fluxSink,
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)

View File

@@ -438,7 +438,6 @@ buildlibrary libbackend.a \
lib/fluxsink/fluxsink.cc \
lib/fluxsink/hardwarefluxsink.cc \
lib/fluxsink/scpfluxsink.cc \
lib/fluxsink/sqlitefluxsink.cc \
lib/fluxsink/vcdfluxsink.cc \
lib/fluxsource/cwffluxsource.cc \
lib/fluxsource/erasefluxsource.cc \
@@ -448,7 +447,6 @@ buildlibrary libbackend.a \
lib/fluxsource/kryoflux.cc \
lib/fluxsource/kryofluxfluxsource.cc \
lib/fluxsource/scpfluxsource.cc \
lib/fluxsource/sqlitefluxsource.cc \
lib/fluxsource/testpatternfluxsource.cc \
lib/globals.cc \
lib/hexdump.cc \
@@ -477,7 +475,6 @@ buildlibrary libbackend.a \
lib/proto.cc \
lib/reader.cc \
lib/sector.cc \
lib/sql.cc \
lib/usb/fluxengineusb.cc \
lib/usb/greaseweazle.cc \
lib/usb/greaseweazleusb.cc \
@@ -559,7 +556,6 @@ buildlibrary libfrontend.a \
src/fe-seek.cc \
src/fe-testbandwidth.cc \
src/fe-testvoltages.cc \
src/fe-upgradefluxfile.cc \
src/fe-write.cc \
src/fluxengine.cc \
@@ -590,6 +586,14 @@ buildsimpleprogram brother240tool \
libemu.a \
libfmt.a \
buildsimpleprogram upgrade-flux-file \
-Idep/emu \
tools/upgrade-flux-file.cc \
libbackend.a \
libfl2.a \
libemu.a \
libfmt.a \
buildproto libtestproto.a \
-d $OBJDIR/proto/lib/common.pb.h \
tests/testproto.proto \

View File

@@ -1,36 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "sql.h"
#include "fluxmap.h"
#include "writer.h"
#include "proto.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/fluxsink/fluxsink.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "fmt/format.h"
#include <fstream>
int mainUpgradeFluxFile(int argc, const char* argv[])
{
if (argc != 2)
Error() << "syntax: fluxengine upgradefluxfile <fluxfile>";
std::string filename = argv[1];
std::string newfilename = filename + ".new";
setRange(config.mutable_cylinders(), "0-79");
setRange(config.mutable_heads(), "0-1");
FluxSourceProto fluxSourceProto;
fluxSourceProto.mutable_fl2()->set_filename(filename);
FluxSinkProto fluxSinkProto;
fluxSinkProto.mutable_fl2()->set_filename(newfilename);
auto fluxSource = FluxSource::create(fluxSourceProto);
auto fluxSink = FluxSink::create(fluxSinkProto);
writeRawDiskCommand(*fluxSource, *fluxSink);
rename(newfilename.c_str(), filename.c_str());
return 0;
}

View File

@@ -14,7 +14,6 @@ extern command_cb mainRpm;
extern command_cb mainSeek;
extern command_cb mainTestBandwidth;
extern command_cb mainTestVoltages;
extern command_cb mainUpgradeFluxFile;
extern command_cb mainWrite;
struct Command
@@ -38,7 +37,6 @@ static std::vector<Command> commands =
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
{ "seek", mainSeek, "Moves the disk head.", },
{ "test", mainTest, "Various testing commands.", },
{ "upgradefluxfile", mainUpgradeFluxFile, "Upgrades a flux file from a previous version of this software.", },
};
static std::vector<Command> analysables =

307
tools/upgrade-flux-file.cc Normal file
View File

@@ -0,0 +1,307 @@
#include "globals.h"
#include "fluxmap.h"
#include "fluxsink/fluxsink.h"
#include "bytes.h"
#include "fmt/format.h"
#include <string.h>
#include <fstream>
#include <sqlite3.h>
/* --- SQL library ------------------------------------------------------- */
enum
{
FLUX_VERSION_0, /* without properties table */
FLUX_VERSION_1,
FLUX_VERSION_2, /* new bytecode with index marks */
FLUX_VERSION_3, /* simplified bytecode with six-bit timer */
};
enum
{
COMPRESSION_NONE,
COMPRESSION_ZLIB
};
static bool hasProperties(sqlite3* db);
void sqlCheck(sqlite3* db, int i)
{
if (i != SQLITE_OK)
Error() << "database error: " << sqlite3_errmsg(db);
}
sqlite3* sqlOpen(const std::string filename, int flags)
{
sqlite3* db;
int i = sqlite3_open_v2(filename.c_str(), &db, flags, NULL);
if (i != SQLITE_OK)
Error() << "failed: " << sqlite3_errstr(i);
return db;
}
void sqlClose(sqlite3* db)
{
sqlite3_close(db);
}
void sqlStmt(sqlite3* db, const char* sql)
{
char* errmsg;
int i = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if (i != SQLITE_OK)
Error() << "database error: %s" << errmsg;
}
bool hasProperties(sqlite3* db)
{
sqlite3_stmt* stmt;
sqlCheck(db,
sqlite3_prepare_v2(db,
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND "
"name='properties'",
-1,
&stmt,
NULL));
int i = sqlite3_step(stmt);
if (i != SQLITE_ROW)
Error() << "Error accessing sqlite metadata";
bool has_properties = sqlite3_column_int(stmt, 0) != 0;
sqlCheck(db, sqlite3_finalize(stmt));
return has_properties;
}
void sql_bind_blob(sqlite3* db,
sqlite3_stmt* stmt,
const char* name,
const void* ptr,
size_t bytes)
{
sqlCheck(db,
sqlite3_bind_blob(stmt,
sqlite3_bind_parameter_index(stmt, name),
ptr,
bytes,
SQLITE_TRANSIENT));
}
void sql_bind_int(sqlite3* db, sqlite3_stmt* stmt, const char* name, int value)
{
sqlCheck(db,
sqlite3_bind_int(
stmt, sqlite3_bind_parameter_index(stmt, name), value));
}
void sql_bind_string(
sqlite3* db, sqlite3_stmt* stmt, const char* name, const char* value)
{
sqlCheck(db,
sqlite3_bind_text(stmt,
sqlite3_bind_parameter_index(stmt, name),
value,
-1,
SQLITE_TRANSIENT));
}
std::vector<std::pair<unsigned, unsigned>> sqlFindFlux(sqlite3* db)
{
std::vector<std::pair<unsigned, unsigned>> output;
sqlite3_stmt* stmt;
sqlCheck(db,
sqlite3_prepare_v2(
db, "SELECT track, side FROM zdata", -1, &stmt, NULL));
for (;;)
{
int i = sqlite3_step(stmt);
if (i == SQLITE_DONE)
break;
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
unsigned track = sqlite3_column_int(stmt, 0);
unsigned side = sqlite3_column_int(stmt, 1);
output.push_back(std::make_pair(track, side));
}
sqlCheck(db, sqlite3_finalize(stmt));
return output;
}
long sqlReadIntProperty(sqlite3* db, const std::string& name)
{
if (!hasProperties(db))
return 0;
sqlite3_stmt* stmt;
sqlCheck(db,
sqlite3_prepare_v2(db,
"SELECT value FROM properties WHERE key=:key",
-1,
&stmt,
NULL));
sql_bind_string(db, stmt, ":key", name.c_str());
int i = sqlite3_step(stmt);
long result = 0;
if (i != SQLITE_DONE)
{
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
result = sqlite3_column_int(stmt, 0);
}
sqlCheck(db, sqlite3_finalize(stmt));
return result;
}
int sqlGetVersion(sqlite3* db)
{
return sqlReadIntProperty(db, "version");
}
/* --- Actual program ---------------------------------------------------- */
static void syntax()
{
std::cerr
<< "syntax: upgrade-flux-file <filename>\n"
<< "This tool upgrades the flux file in-place to the current format.\n";
exit(0);
}
static Bytes sqlReadFluxBytes(sqlite3* db, int track, int side)
{
sqlite3_stmt* stmt;
sqlCheck(db,
sqlite3_prepare_v2(db,
"SELECT data, compression FROM zdata WHERE track=:track AND "
"side=:side",
-1,
&stmt,
NULL));
sql_bind_int(db, stmt, ":track", track);
sql_bind_int(db, stmt, ":side", side);
Bytes data;
int i = sqlite3_step(stmt);
if (i != SQLITE_DONE)
{
if (i != SQLITE_ROW)
Error() << "failed to read from database: " << sqlite3_errmsg(db);
const uint8_t* blobptr = (const uint8_t*)sqlite3_column_blob(stmt, 0);
size_t bloblen = sqlite3_column_bytes(stmt, 0);
int compression = sqlite3_column_int(stmt, 1);
data = Bytes(blobptr, bloblen);
switch (compression)
{
case COMPRESSION_NONE:
break;
case COMPRESSION_ZLIB:
data = data.decompress();
break;
default:
Error() << fmt::format(
"unsupported compression type {}", compression);
}
}
sqlCheck(db, sqlite3_finalize(stmt));
return data;
}
static bool isSqlite(const std::string& filename)
{
char buffer[16];
std::ifstream(filename, std::ios::in | std::ios::binary).read(buffer, 16);
if (strncmp(buffer, "SQLite format 3", 16) == 0)
return true;
return false;
}
static void translateFluxVersion2(Fluxmap& fluxmap, const Bytes& bytes)
{
unsigned pending = 0;
for (uint8_t b : bytes)
{
switch (b)
{
case 0x80: /* pulse */
fluxmap.appendInterval(pending);
fluxmap.appendPulse();
pending = 0;
break;
case 0x81: /* index */
fluxmap.appendInterval(pending);
fluxmap.appendIndex();
pending = 0;
break;
default:
pending += b;
break;
}
}
fluxmap.appendInterval(pending);
}
int main(int argc, const char* argv[])
{
if ((argc != 2) || (strcmp(argv[1], "--help") == 0))
syntax();
std::string filename = argv[1];
if (!isSqlite(filename))
{
std::cout << "File is up to date.\n";
exit(0);
}
std::string outFilename = filename + ".out.flux";
auto db = sqlOpen(filename, SQLITE_OPEN_READONLY);
int version = sqlGetVersion(db);
{
auto fluxsink = FluxSink::createFl2FluxSink(outFilename);
for (const auto& locations : sqlFindFlux(db))
{
unsigned cylinder = locations.first;
unsigned head = locations.second;
Bytes bytes = sqlReadFluxBytes(db, cylinder, head);
Fluxmap fluxmap;
switch (version)
{
case FLUX_VERSION_2:
translateFluxVersion2(fluxmap, bytes);
break;
case FLUX_VERSION_3:
fluxmap.appendBytes(bytes);
break;
default:
Error() << fmt::format(
"you cannot upgrade version {} files (please file a "
"bug)",
version);
}
fluxsink->writeFlux(cylinder, head, fluxmap);
std::cout << '.' << std::flush;
}
std::cout << "Writing output file...\n";
}
if (rename(outFilename.c_str(), filename.c_str()) != 0)
Error() << fmt::format(
"couldn't replace input file: {}", strerror(errno));
return 0;
}