mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-24 11:11:02 -07:00
320 lines
8.0 KiB
C++
320 lines
8.0 KiB
C++
#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 <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: {}", 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("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);
|
|
}
|
|
}
|