feat: basically finish standalone mode.

- finish standalone mode but only tested on Virtools 2.1 environment.
- add DROP TABLE statement before CREATE TABLE to resolve fail to create table issue when opening an existing database file.
This commit is contained in:
2024-08-11 17:51:48 +08:00
parent 0e08e518c4
commit 1dba1e0674
3 changed files with 121 additions and 17 deletions

View File

@@ -124,6 +124,20 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
AbstractDatabase(file) { AbstractDatabase(file) {
// initialize table // initialize table
BEGIN_CTOR; BEGIN_CTOR;
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [script];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [behavior];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [bIO];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pTarget];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pIn];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pOut];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [bLink];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pLocal];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pAttr];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pLink];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [pOper];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [eLink];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [data];");
CTOR_SQL_EXEC("CREATE TABLE [script] ([beobj] INTEGER, [beobj_name] TEXT, [behavior_index] INTEGER, [behavior] INTEGER);"); CTOR_SQL_EXEC("CREATE TABLE [script] ([beobj] INTEGER, [beobj_name] TEXT, [behavior_index] INTEGER, [behavior] INTEGER);");
CTOR_SQL_EXEC("CREATE TABLE [behavior] ([thisobj] INTEGER, [name] TEXT, [type] INTEGER, [proto_name] TEXT, [proto_guid] INTEGER, [flags] INTEGER, [priority] INTEGER, [version] INTEGER, [pin_count_ptarget] INTEGER, [pin_count_pin] INTEGER, [pin_count_pout] INTEGER, [pin_count_bin] INTEGER, [pin_count_bout] INTEGER, [parent] INTEGER);"); CTOR_SQL_EXEC("CREATE TABLE [behavior] ([thisobj] INTEGER, [name] TEXT, [type] INTEGER, [proto_name] TEXT, [proto_guid] INTEGER, [flags] INTEGER, [priority] INTEGER, [version] INTEGER, [pin_count_ptarget] INTEGER, [pin_count_pin] INTEGER, [pin_count_pout] INTEGER, [pin_count_bin] INTEGER, [pin_count_bout] INTEGER, [parent] INTEGER);");
CTOR_SQL_EXEC("CREATE TABLE [pTarget] ([thisobj] INTEGER, [name] TEXT, [type] INTEGER, [parent] INTEGER, [direct_source] INTEGER, [shared_source] INTEGER);"); CTOR_SQL_EXEC("CREATE TABLE [pTarget] ([thisobj] INTEGER, [name] TEXT, [type] INTEGER, [parent] INTEGER, [direct_source] INTEGER, [shared_source] INTEGER);");
@@ -144,7 +158,6 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
ScriptDatabase::~ScriptDatabase() { ScriptDatabase::~ScriptDatabase() {
// create index for quick select in following process // create index for quick select in following process
BEGIN_DTOR; BEGIN_DTOR;
DTOR_SQL_EXEC("begin;");
DTOR_SQL_EXEC("CREATE INDEX [quick_where1] ON [behavior] ([parent])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where1] ON [behavior] ([parent])");
DTOR_SQL_EXEC("CREATE INDEX [quick_where2] ON [pOper] ([parent], [thisobj])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where2] ON [pOper] ([parent], [thisobj])");
DTOR_SQL_EXEC("CREATE INDEX [quick_where3] ON [pTarget] ([parent])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where3] ON [pTarget] ([parent])");
@@ -158,7 +171,6 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
DTOR_SQL_EXEC("CREATE INDEX [quick_where11] ON [elink] ([parent])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where11] ON [elink] ([parent])");
DTOR_SQL_EXEC("CREATE INDEX [quick_where12] ON [pAttr] ([thisobj])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where12] ON [pAttr] ([thisobj])");
DTOR_SQL_EXEC("CREATE INDEX [quick_where13] ON [data] ([parent])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where13] ON [data] ([parent])");
DTOR_SQL_EXEC("commit;");
END_DTOR; END_DTOR;
} }
@@ -312,6 +324,9 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
AbstractDatabase(file) { AbstractDatabase(file) {
// initialize table // initialize table
BEGIN_CTOR; BEGIN_CTOR;
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [msg];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [obj];");
CTOR_SQL_EXEC("CREATE TABLE [msg] ([index] INTEGER, [name] TEXT);"); CTOR_SQL_EXEC("CREATE TABLE [msg] ([index] INTEGER, [name] TEXT);");
CTOR_SQL_EXEC("CREATE TABLE [obj] ([id] INTEGER, [name] TEXT, [classid] INTEGER, [classid_name] TEXT);"); CTOR_SQL_EXEC("CREATE TABLE [obj] ([id] INTEGER, [name] TEXT, [classid] INTEGER, [classid_name] TEXT);");
END_CTOR; END_CTOR;
@@ -320,10 +335,8 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
DocumentDatabase::~DocumentDatabase() { DocumentDatabase::~DocumentDatabase() {
// create index for quick select in following process // create index for quick select in following process
BEGIN_DTOR; BEGIN_DTOR;
DTOR_SQL_EXEC("begin;");
DTOR_SQL_EXEC("CREATE INDEX [quick_where1] ON [msg] ([index])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where1] ON [msg] ([index])");
DTOR_SQL_EXEC("CREATE INDEX [quick_where2] ON [obj] ([id])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where2] ON [obj] ([id])");
DTOR_SQL_EXEC("commit;");
END_DTOR; END_DTOR;
} }
@@ -350,6 +363,12 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
AbstractDatabase(file) { AbstractDatabase(file) {
// initialize table // initialize table
BEGIN_CTOR; BEGIN_CTOR;
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [op];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [param];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [attr];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [plugin];");
CTOR_SQL_EXEC("DROP TABLE IF EXISTS [variable];");
CTOR_SQL_EXEC("CREATE TABLE [op] ([func_ptr] TEXT, [in1_guid] INTEGER, [in2_guid] INTEGER, [out_guid] INTEGER, [op_guid] INTEGER, [op_name] TEXT, [op_code] INTEGER);"); CTOR_SQL_EXEC("CREATE TABLE [op] ([func_ptr] TEXT, [in1_guid] INTEGER, [in2_guid] INTEGER, [out_guid] INTEGER, [op_guid] INTEGER, [op_name] TEXT, [op_code] INTEGER);");
CTOR_SQL_EXEC("CREATE TABLE [param] ([index] INTEGER, [guid] INTEGER, [derived_from] INTEGER, [name] TEXT, [default_size] INTEGER, [func_CreateDefault] TEXT, [func_Delete] TEXT, [func_SaveLoad] TEXT, [func_Check] TEXT, [func_Copy] TEXT, [func_String] TEXT, [func_UICreator] TEXT, [dll_name] TEXT, [dll_index] INTEGER, [position_in_dll] INTEGER, [flags] INTEGER, [dw_param] INTEGER, [cid] INTEGER, [saver_manager] INTEGER);"); CTOR_SQL_EXEC("CREATE TABLE [param] ([index] INTEGER, [guid] INTEGER, [derived_from] INTEGER, [name] TEXT, [default_size] INTEGER, [func_CreateDefault] TEXT, [func_Delete] TEXT, [func_SaveLoad] TEXT, [func_Check] TEXT, [func_Copy] TEXT, [func_String] TEXT, [func_UICreator] TEXT, [dll_name] TEXT, [dll_index] INTEGER, [position_in_dll] INTEGER, [flags] INTEGER, [dw_param] INTEGER, [cid] INTEGER, [saver_manager] INTEGER);");
CTOR_SQL_EXEC("CREATE TABLE [attr] ([index] INTEGER, [name] TEXT, [category_index] INTEGER, [category_name] TEXT, [flags] INTEGER, [param_index] INTEGER, [param_guid] INTEGER, [compatible_classid] INTEGER, [default_value] TEXT);"); CTOR_SQL_EXEC("CREATE TABLE [attr] ([index] INTEGER, [name] TEXT, [category_index] INTEGER, [category_name] TEXT, [flags] INTEGER, [param_index] INTEGER, [param_guid] INTEGER, [compatible_classid] INTEGER, [default_value] TEXT);");
@@ -359,7 +378,9 @@ failed: throw std::runtime_error("fail to bind value for prepared statement.");
} }
EnvironmentDatabase::~EnvironmentDatabase() { EnvironmentDatabase::~EnvironmentDatabase() {
// do nothing // create index for quick select in following process
BEGIN_DTOR;
END_DTOR;
} }
void EnvironmentDatabase::Write(const DataTypes::Environment::Table_op& data) { void EnvironmentDatabase::Write(const DataTypes::Environment::Table_op& data) {

View File

@@ -1,6 +1,7 @@
#include "StandaloneMain.hpp" #include "StandaloneMain.hpp"
#include "ExportCore.hpp" #include "ExportCore.hpp"
#include "GenericHelper.hpp" #include "GenericHelper.hpp"
#include <optional>
namespace VSW::Materializer::StandaloneMain { namespace VSW::Materializer::StandaloneMain {
@@ -35,6 +36,7 @@ namespace VSW::Materializer::StandaloneMain {
m_Version(YYCC_U8("version"), YYCC_U8_CHAR('v'), YYCC_U8("Print version infomation about this program and exit.")), m_Version(YYCC_U8("version"), YYCC_U8_CHAR('v'), YYCC_U8("Print version infomation about this program and exit.")),
m_Help(YYCC_U8("help"), YYCC_U8_CHAR('h'), YYCC_U8("Print this help page and exit.")), m_Help(YYCC_U8("help"), YYCC_U8_CHAR('h'), YYCC_U8("Print this help page and exit.")),
m_OptionContext(YYCC_U8("Virtools Schematic Weaver - Materializer"), YYCC_U8("The exporter of Virtools Schematic Weaver"), { m_OptionContext(YYCC_U8("Virtools Schematic Weaver - Materializer"), YYCC_U8("The exporter of Virtools Schematic Weaver"), {
&m_InputFilePath,
&m_ScriptDbPath, &m_DocumentDbPath, &m_EnvironmentDbPath, &m_CodePage, &m_ScriptDbPath, &m_DocumentDbPath, &m_EnvironmentDbPath, &m_CodePage,
&m_Version, &m_Help &m_Version, &m_Help
}) {} }) {}
@@ -109,6 +111,67 @@ namespace VSW::Materializer::StandaloneMain {
#pragma region Assistant Functions #pragma region Assistant Functions
class TemporaryFile {
public:
TemporaryFile(CKContext* ctx, const YYCC::yycc_u8string_view& u8_vt_file) :
m_IsSuccess(false), m_TempFile() {
if (ctx == nullptr) throw std::invalid_argument("Invalid CKContext");
if (u8_vt_file.empty()) throw std::invalid_argument("Invalid Virtools file path");
// build source virtools file
auto vt_file = YYCC::FsPathPatch::FromUTF8Path(u8_vt_file.data());
// build cache path located in Windows temp path and keep its extension
YYCC::yycc_u8string u8_temp_dir;
if (!YYCC::WinFctHelper::GetTempDirectory(u8_temp_dir))
throw std::runtime_error("Fail to fetch Windows Temp directory");
auto temp_dir = YYCC::FsPathPatch::FromUTF8Path(u8_temp_dir.c_str());
temp_dir /= YYCC::FsPathPatch::FromUTF8Path(YYCC_U8("07159749-81e5-4ec2-b649-87b8eb9c1f5a"));
temp_dir.replace_extension(vt_file.extension());
m_TempFile = YYCC::FsPathPatch::ToUTF8Path(temp_dir);
// copy it to temp directory
std::wstring w_temp_file, w_vt_file;
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_vt_file, w_vt_file))
throw std::runtime_error("Fail to fetch source file.");
if (!YYCC::EncodingHelper::UTF8ToWchar(m_TempFile, w_temp_file))
throw std::runtime_error("Fail to fetch temporary file.");
if (!::CopyFileW(w_vt_file.c_str(), w_temp_file.c_str(), FALSE))
throw std::runtime_error("Fail to copy file.");
// okey
m_IsSuccess = true;
}
~TemporaryFile() {
if (m_IsSuccess) {
std::wstring w_temp_file;
if (!YYCC::EncodingHelper::UTF8ToWchar(m_TempFile, w_temp_file)) return;
::DeleteFileW(w_temp_file.c_str());
}
}
TemporaryFile(TemporaryFile&& rhs) :
m_IsSuccess(rhs.m_IsSuccess), m_TempFile(rhs.m_TempFile) {
rhs.m_IsSuccess = false;
rhs.m_TempFile.clear();
}
TemporaryFile& operator=(TemporaryFile&& rhs) {
this->m_IsSuccess = rhs.m_IsSuccess;
this->m_TempFile = rhs.m_TempFile;
rhs.m_IsSuccess = false;
rhs.m_TempFile.clear();
return *this;
}
YYCC_DEL_CLS_COPY(TemporaryFile);
public:
bool IsSuccess() const { return m_IsSuccess; }
const YYCC::yycc_u8string& GetPath() const { return m_TempFile; }
private:
bool m_IsSuccess;
YYCC::yycc_u8string m_TempFile;
};
static void CustomAssert(VSW::Reporter& reporter, bool condition, const YYCC::yycc_char8_t* msg) { static void CustomAssert(VSW::Reporter& reporter, bool condition, const YYCC::yycc_char8_t* msg) {
if (!condition) { if (!condition) {
if (msg != nullptr) if (msg != nullptr)
@@ -143,8 +206,8 @@ namespace VSW::Materializer::StandaloneMain {
CUSTOM_ASSERT(plugin_mgr->ParsePlugins("Managers") > 0, "Error loading Managers"); CUSTOM_ASSERT(plugin_mgr->ParsePlugins("Managers") > 0, "Error loading Managers");
CUSTOM_ASSERT(plugin_mgr->ParsePlugins("BuildingBlocks") > 0, "Error loading BuildingBlocks"); CUSTOM_ASSERT(plugin_mgr->ParsePlugins("BuildingBlocks") > 0, "Error loading BuildingBlocks");
CUSTOM_ASSERT(plugin_mgr->ParsePlugins("Plugins") > 0, "Error loading Plugins"); CUSTOM_ASSERT(plugin_mgr->ParsePlugins("Plugins") > 0, "Error loading Plugins");
// ========== Create CKContext and Load File ========== // ========== Create CKContext ==========
CKContext* ctx = nullptr; CKContext* ctx = nullptr;
#if defined(VIRTOOLS_21) #if defined(VIRTOOLS_21)
CUSTOM_ASSERT(CKCreateContext(&ctx, NULL, 0, 0) == CK_OK, "CKCreateContext Error"); CUSTOM_ASSERT(CKCreateContext(&ctx, NULL, 0, 0) == CK_OK, "CKCreateContext Error");
@@ -152,11 +215,22 @@ namespace VSW::Materializer::StandaloneMain {
CUSTOM_ASSERT(CKCreateContext(&ctx, NULL) == CK_OK, "CKCreateContext Error"); CUSTOM_ASSERT(CKCreateContext(&ctx, NULL) == CK_OK, "CKCreateContext Error");
#endif #endif
CKObjectArray* array = CreateCKObjectArray(); // ========== Load File ==========
CUSTOM_ASSERT(!ctx->Load((char*)virtools_composition, array), "CKContext::Load() Error"); std::optional<TemporaryFile> loaded_file;
CKObjectArray* loaded_file_objs = CreateCKObjectArray();
if (!captured.m_InputFilePath.empty()) {
TemporaryFile cache(ctx, captured.m_InputFilePath);
if (cache.IsSuccess()) {
std::string temp_file;
if (YYCC::EncodingHelper::UTF8ToChar(cache.GetPath(), temp_file, CP_ACP)) {
if (ctx->Load(const_cast<CKSTRING>(temp_file.c_str()), loaded_file_objs) == CK_OK) {
loaded_file = std::move(cache);
}
}
}
CUSTOM_ASSERT(loaded_file.has_value(), "Fail to open specified file.");
}
printf("Parsing %s...\n", virtools_composition);
// ========== Export Data ========== // ========== Export Data ==========
if (!captured.m_ScriptDbPath.empty()) { if (!captured.m_ScriptDbPath.empty()) {
reporter.Info(YYCC_U8("Exporting script database...")); reporter.Info(YYCC_U8("Exporting script database..."));
@@ -171,18 +245,19 @@ namespace VSW::Materializer::StandaloneMain {
ExportEnvironment::Export(ctx, captured.m_EnvironmentDbPath, captured.m_CodePage); ExportEnvironment::Export(ctx, captured.m_EnvironmentDbPath, captured.m_CodePage);
} }
// ====================== free resources and shutdown engine // ========== Unload File and Clear CKContext ==========
DeleteCKObjectArray(array); DeleteCKObjectArray(loaded_file_objs);
loaded_file.reset();
ctx->Reset(); ctx->Reset();
ctx->ClearAll(); ctx->ClearAll();
// ========== Destroy CKContext ========== // ========== Destroy CKContext ==========
// todo: Virtools 4.0 standalone version throw exception in there, but i don't know why // todo: Virtools 4.0 standalone version throw exception in there, but i don't know why
// but it doesn't affect SSMaterializerDatabase export, perhaps // but it doesn't affect SSMaterializerDatabase export, perhaps
CUSTOM_ASSERT(CKCloseContext(ctx) == CK_OK, "CKCloseContext Error."); CKCloseContext(ctx);
// ========== Shutdown CK2 Engine ========== // ========== Shutdown CK2 Engine ==========
CUSTOM_ASSERT(CKShutdown() == CK_OK, "CKShutdown() Error."); CKShutdown();
// todo: Virtools 2.5 standalone version throw exception in there, but i don't know why // todo: Virtools 2.5 standalone version throw exception in there, but i don't know why
// but it doesn't affect SSMaterializerDatabase export, perhaps // but it doesn't affect SSMaterializerDatabase export, perhaps

View File

@@ -110,13 +110,18 @@ def output_result(decls: tuple[StructDecl, ...]) -> None:
else: else:
return f'WRITER_BIND(sqlite3_bind_int(WRITER_STMT, WRITER_INDEX, data.{decl_pair.m_DeclName}));' return f'WRITER_BIND(sqlite3_bind_int(WRITER_STMT, WRITER_INDEX, data.{decl_pair.m_DeclName}));'
ret_sql_drop_table: list[str] = []
ret_sql_create_table: list[str] = [] ret_sql_create_table: list[str] = []
ret_sql_insert: list[str] = [] ret_sql_insert: list[str] = []
ret_cpp_bind: list[tuple[str, str]] = [] ret_cpp_bind: list[tuple[str, str]] = []
for struct_decl in decls: for struct_decl in decls:
# generate drop table statement
gen_statement: str = f'DROP TABLE IF EXISTS [{struct_decl.m_StructName}];'
ret_sql_drop_table.append(gen_statement)
# generate sql create table statement # generate sql create table statement
table_string: str = ', '.join(map(conv_sql_create_table, struct_decl.m_StructFields)) table_string: str = ', '.join(map(conv_sql_create_table, struct_decl.m_StructFields))
gen_statement: str = f'CREATE TABLE [{struct_decl.m_StructName}] ({table_string});' gen_statement = f'CREATE TABLE [{struct_decl.m_StructName}] ({table_string});'
ret_sql_create_table.append(gen_statement) ret_sql_create_table.append(gen_statement)
# generate sql insert statement # generate sql insert statement
@@ -128,6 +133,9 @@ def output_result(decls: tuple[StructDecl, ...]) -> None:
bind_string: str = '\n'.join(map(conv_cpp, struct_decl.m_StructFields)) bind_string: str = '\n'.join(map(conv_cpp, struct_decl.m_StructFields))
ret_cpp_bind.append((struct_decl.m_StructName, bind_string)) ret_cpp_bind.append((struct_decl.m_StructName, bind_string))
print('========== SQL Drop Table ==========')
for item in ret_sql_drop_table:
print(item)
print('========== SQL Create Table ==========') print('========== SQL Create Table ==========')
for item in ret_sql_create_table: for item in ret_sql_create_table:
print(item) print(item)