#include "lib/core/globals.h" #include "lib/data/fluxmap.h" #include "lib/fluxsink/fluxsink.h" #include "lib/core/bytes.h" #include "fmt/format.h" #include #include #include /* --- 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: {}", 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> sqlFindFlux(sqlite3* db) { std::vector> 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 \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("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[]) { try { 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( "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"; } sqlite3_close(db); if (remove(filename.c_str()) != 0) error("couldn't remove input file: {}", strerror(errno)); if (rename(outFilename.c_str(), filename.c_str()) != 0) error("couldn't replace input file: {}", strerror(errno)); return 0; } catch (const ErrorException& e) { e.print(); exit(1); } }