2022-12-05 08:57:58 +00:00
|
|
|
#include "FdbToSqlite.h"
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <fstream>
|
|
|
|
#include <cassert>
|
|
|
|
#include <iomanip>
|
|
|
|
|
|
|
|
#include "BinaryIO.h"
|
|
|
|
#include "CDClientDatabase.h"
|
|
|
|
#include "GeneralUtils.h"
|
|
|
|
#include "Game.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2023-01-07 05:04:20 +00:00
|
|
|
#include "AssetManager.h"
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
#include "eSqliteDataType.h"
|
|
|
|
|
2022-12-22 06:34:11 +00:00
|
|
|
std::map<eSqliteDataType, std::string> FdbToSqlite::Convert::m_SqliteType = {
|
2022-12-05 08:57:58 +00:00
|
|
|
{ eSqliteDataType::NONE, "none"},
|
|
|
|
{ eSqliteDataType::INT32, "int32"},
|
|
|
|
{ eSqliteDataType::REAL, "real"},
|
|
|
|
{ eSqliteDataType::TEXT_4, "text_4"},
|
|
|
|
{ eSqliteDataType::INT_BOOL, "int_bool"},
|
|
|
|
{ eSqliteDataType::INT64, "int64"},
|
|
|
|
{ eSqliteDataType::TEXT_8, "text_8"}
|
|
|
|
};
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
|
2022-12-22 06:34:11 +00:00
|
|
|
this->m_BinaryOutPath = binaryOutPath;
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-12-23 17:24:16 +00:00
|
|
|
bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) {
|
2022-12-22 06:34:11 +00:00
|
|
|
if (m_ConversionStarted) return false;
|
2023-01-07 05:04:20 +00:00
|
|
|
|
2022-12-22 06:34:11 +00:00
|
|
|
this->m_ConversionStarted = true;
|
2022-12-05 08:57:58 +00:00
|
|
|
try {
|
2022-12-22 06:34:11 +00:00
|
|
|
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
|
|
|
|
2023-12-23 17:24:16 +00:00
|
|
|
int32_t numberOfTables = ReadInt32(buffer);
|
|
|
|
ReadTables(numberOfTables, buffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
CDClientDatabase::ExecuteQuery("COMMIT;");
|
|
|
|
} catch (CppSQLite3Exception& e) {
|
2023-10-21 23:31:55 +00:00
|
|
|
LOG("Encountered error %s converting FDB to SQLite", e.errorMessage());
|
2022-12-05 08:57:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t FdbToSqlite::Convert::ReadInt32(std::istream& cdClientBuffer) {
|
2022-12-08 12:44:56 +00:00
|
|
|
int32_t nextInt{};
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, nextInt);
|
2022-12-08 12:44:56 +00:00
|
|
|
return nextInt;
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int64_t FdbToSqlite::Convert::ReadInt64(std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
int64_t value{};
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, value);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
std::string FdbToSqlite::Convert::ReadString(std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
auto readString = BinaryIO::ReadString(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
return readString;
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t FdbToSqlite::Convert::SeekPointer(std::istream& cdClientBuffer) {
|
2022-12-05 08:57:58 +00:00
|
|
|
int32_t position{};
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, position);
|
|
|
|
int32_t prevPosition = cdClientBuffer.tellg();
|
|
|
|
cdClientBuffer.seekg(position);
|
2022-12-05 08:57:58 +00:00
|
|
|
return prevPosition;
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
std::string FdbToSqlite::Convert::ReadColumnHeader(std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t numberOfColumns = ReadInt32(cdClientBuffer);
|
|
|
|
std::string tableName = ReadString(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
auto columns = ReadColumns(numberOfColumns, cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
std::string newTable = "CREATE TABLE IF NOT EXISTS '" + tableName + "' (" + columns + ");";
|
|
|
|
CDClientDatabase::ExecuteDML(newTable);
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
return tableName;
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadTables(int32_t& numberOfTables, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
for (int32_t i = 0; i < numberOfTables; i++) {
|
2023-01-07 05:04:20 +00:00
|
|
|
auto columnHeader = ReadColumnHeader(cdClientBuffer);
|
|
|
|
ReadRowHeader(columnHeader, cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
std::string FdbToSqlite::Convert::ReadColumns(int32_t& numberOfColumns, std::istream& cdClientBuffer) {
|
2022-12-05 08:57:58 +00:00
|
|
|
std::stringstream columnsToCreate;
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
std::string name{};
|
|
|
|
eSqliteDataType dataType{};
|
|
|
|
for (int32_t i = 0; i < numberOfColumns; i++) {
|
|
|
|
if (i != 0) columnsToCreate << ", ";
|
2023-01-07 05:04:20 +00:00
|
|
|
dataType = static_cast<eSqliteDataType>(ReadInt32(cdClientBuffer));
|
|
|
|
name = ReadString(cdClientBuffer);
|
2022-12-22 06:34:11 +00:00
|
|
|
columnsToCreate << "'" << name << "' " << FdbToSqlite::Convert::m_SqliteType[dataType];
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
return columnsToCreate.str();
|
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadRowHeader(std::string& tableName, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t numberOfAllocatedRows = ReadInt32(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
if (numberOfAllocatedRows != 0) assert((numberOfAllocatedRows & (numberOfAllocatedRows - 1)) == 0); // assert power of 2 allocation size
|
2023-01-07 05:04:20 +00:00
|
|
|
ReadRows(numberOfAllocatedRows, tableName, cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
int32_t rowid = 0;
|
|
|
|
for (int32_t row = 0; row < numberOfAllocatedRows; row++) {
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t rowPointer = ReadInt32(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
if (rowPointer == -1) rowid++;
|
2023-01-07 05:04:20 +00:00
|
|
|
else ReadRow(rowPointer, tableName, cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadRow(int32_t& position, std::string& tableName, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = cdClientBuffer.tellg();
|
|
|
|
cdClientBuffer.seekg(position);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
while (true) {
|
2023-01-07 05:04:20 +00:00
|
|
|
ReadRowInfo(tableName, cdClientBuffer);
|
|
|
|
int32_t linked = ReadInt32(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
if (linked == -1) break;
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(linked);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadRowInfo(std::string& tableName, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
int32_t numberOfColumns = ReadInt32(cdClientBuffer);
|
|
|
|
ReadRowValues(numberOfColumns, tableName, cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 05:04:20 +00:00
|
|
|
void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& tableName, std::istream& cdClientBuffer) {
|
|
|
|
int32_t prevPosition = SeekPointer(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
|
|
|
|
int32_t emptyValue{};
|
|
|
|
int32_t intValue{};
|
|
|
|
float_t floatValue{};
|
|
|
|
std::string stringValue{};
|
|
|
|
int32_t boolValue{};
|
|
|
|
int64_t int64Value{};
|
|
|
|
bool insertedFirstEntry = false;
|
|
|
|
std::stringstream insertedRow;
|
|
|
|
insertedRow << "INSERT INTO " << tableName << " values (";
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < numberOfColumns; i++) {
|
|
|
|
if (i != 0) insertedRow << ", "; // Only append comma and space after first entry in row.
|
2023-01-07 05:04:20 +00:00
|
|
|
switch (static_cast<eSqliteDataType>(ReadInt32(cdClientBuffer))) {
|
2022-12-05 08:57:58 +00:00
|
|
|
case eSqliteDataType::NONE:
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, emptyValue);
|
2022-12-05 08:57:58 +00:00
|
|
|
assert(emptyValue == 0);
|
2022-12-08 12:44:56 +00:00
|
|
|
insertedRow << "NULL";
|
2022-12-05 08:57:58 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case eSqliteDataType::INT32:
|
2023-01-07 05:04:20 +00:00
|
|
|
intValue = ReadInt32(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
insertedRow << intValue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eSqliteDataType::REAL:
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, floatValue);
|
2022-12-05 08:57:58 +00:00
|
|
|
insertedRow << std::fixed << std::setprecision(34) << floatValue; // maximum precision of floating point number
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eSqliteDataType::TEXT_4:
|
|
|
|
case eSqliteDataType::TEXT_8: {
|
2023-01-07 05:04:20 +00:00
|
|
|
stringValue = ReadString(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
size_t position = 0;
|
|
|
|
|
|
|
|
// Need to escape quote with a double of ".
|
|
|
|
while (position < stringValue.size()) {
|
|
|
|
if (stringValue.at(position) == '\"') {
|
|
|
|
stringValue.insert(position, "\"");
|
|
|
|
position++;
|
|
|
|
}
|
|
|
|
position++;
|
|
|
|
}
|
|
|
|
insertedRow << "\"" << stringValue << "\"";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case eSqliteDataType::INT_BOOL:
|
2023-01-07 05:04:20 +00:00
|
|
|
BinaryIO::BinaryRead(cdClientBuffer, boolValue);
|
2022-12-05 08:57:58 +00:00
|
|
|
insertedRow << static_cast<bool>(boolValue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eSqliteDataType::INT64:
|
2023-01-07 05:04:20 +00:00
|
|
|
int64Value = ReadInt64(cdClientBuffer);
|
2022-12-05 08:57:58 +00:00
|
|
|
insertedRow << std::to_string(int64Value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw std::invalid_argument("Unsupported SQLite type encountered.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
insertedRow << ");";
|
|
|
|
|
|
|
|
auto copiedString = insertedRow.str();
|
|
|
|
CDClientDatabase::ExecuteDML(copiedString);
|
2023-01-07 05:04:20 +00:00
|
|
|
cdClientBuffer.seekg(prevPosition);
|
2022-12-05 08:57:58 +00:00
|
|
|
}
|