From 1dba1e06740da791397daf969781a406fdce09c9 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 11 Aug 2024 17:51:48 +0800 Subject: [PATCH] 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. --- materializer/Database.cpp | 31 +++++++++-- materializer/StandaloneMain.cpp | 97 +++++++++++++++++++++++++++++---- script/sqlite_helper.py | 10 +++- 3 files changed, 121 insertions(+), 17 deletions(-) diff --git a/materializer/Database.cpp b/materializer/Database.cpp index d0fda10..d5bbc08 100644 --- a/materializer/Database.cpp +++ b/materializer/Database.cpp @@ -124,6 +124,20 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); AbstractDatabase(file) { // initialize table 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 [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);"); @@ -144,7 +158,6 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); ScriptDatabase::~ScriptDatabase() { // create index for quick select in following process BEGIN_DTOR; - DTOR_SQL_EXEC("begin;"); 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_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_where12] ON [pAttr] ([thisobj])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where13] ON [data] ([parent])"); - DTOR_SQL_EXEC("commit;"); END_DTOR; } @@ -312,6 +324,9 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); AbstractDatabase(file) { // initialize table 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 [obj] ([id] INTEGER, [name] TEXT, [classid] INTEGER, [classid_name] TEXT);"); END_CTOR; @@ -320,10 +335,8 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); DocumentDatabase::~DocumentDatabase() { // create index for quick select in following process BEGIN_DTOR; - DTOR_SQL_EXEC("begin;"); DTOR_SQL_EXEC("CREATE INDEX [quick_where1] ON [msg] ([index])"); DTOR_SQL_EXEC("CREATE INDEX [quick_where2] ON [obj] ([id])"); - DTOR_SQL_EXEC("commit;"); END_DTOR; } @@ -350,6 +363,12 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); AbstractDatabase(file) { // initialize table 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 [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);"); @@ -359,7 +378,9 @@ failed: throw std::runtime_error("fail to bind value for prepared statement."); } 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) { diff --git a/materializer/StandaloneMain.cpp b/materializer/StandaloneMain.cpp index a6dc185..d9f1e7c 100644 --- a/materializer/StandaloneMain.cpp +++ b/materializer/StandaloneMain.cpp @@ -1,6 +1,7 @@ #include "StandaloneMain.hpp" #include "ExportCore.hpp" #include "GenericHelper.hpp" +#include 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_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_InputFilePath, &m_ScriptDbPath, &m_DocumentDbPath, &m_EnvironmentDbPath, &m_CodePage, &m_Version, &m_Help }) {} @@ -109,6 +111,67 @@ namespace VSW::Materializer::StandaloneMain { #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) { if (!condition) { 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("BuildingBlocks") > 0, "Error loading BuildingBlocks"); CUSTOM_ASSERT(plugin_mgr->ParsePlugins("Plugins") > 0, "Error loading Plugins"); - - // ========== Create CKContext and Load File ========== + + // ========== Create CKContext ========== CKContext* ctx = nullptr; #if defined(VIRTOOLS_21) 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"); #endif - CKObjectArray* array = CreateCKObjectArray(); - CUSTOM_ASSERT(!ctx->Load((char*)virtools_composition, array), "CKContext::Load() Error"); + // ========== Load File ========== + std::optional 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(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 ========== if (!captured.m_ScriptDbPath.empty()) { reporter.Info(YYCC_U8("Exporting script database...")); @@ -171,18 +245,19 @@ namespace VSW::Materializer::StandaloneMain { ExportEnvironment::Export(ctx, captured.m_EnvironmentDbPath, captured.m_CodePage); } - // ====================== free resources and shutdown engine - DeleteCKObjectArray(array); + // ========== Unload File and Clear CKContext ========== + DeleteCKObjectArray(loaded_file_objs); + loaded_file.reset(); ctx->Reset(); ctx->ClearAll(); - + // ========== Destroy CKContext ========== // todo: Virtools 4.0 standalone version throw exception in there, but i don't know why // but it doesn't affect SSMaterializerDatabase export, perhaps - CUSTOM_ASSERT(CKCloseContext(ctx) == CK_OK, "CKCloseContext Error."); + CKCloseContext(ctx); // ========== 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 // but it doesn't affect SSMaterializerDatabase export, perhaps diff --git a/script/sqlite_helper.py b/script/sqlite_helper.py index fbe7594..b456f28 100644 --- a/script/sqlite_helper.py +++ b/script/sqlite_helper.py @@ -110,13 +110,18 @@ def output_result(decls: tuple[StructDecl, ...]) -> None: else: 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_insert: list[str] = [] ret_cpp_bind: list[tuple[str, str]] = [] 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 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) # 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)) 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 ==========') for item in ret_sql_create_table: print(item)