/* SPDX-License-Identifier: GPL-3.0-or-later Copyright © 2015-2022 Amebis */ #include "pch.h" using namespace std; using namespace stdex; using namespace winstd; ////////////////////////////////////////////////////////////////////////// // ZRCola::DBSource::character_bank ////////////////////////////////////////////////////////////////////////// void ZRCola::DBSource::character_bank::build_related() { SYSTEM_INFO si; GetSystemInfo(&si); // Launch workers. build_related_worker **workers = new build_related_worker*[si.dwNumberOfProcessors]; size_type from = 0, total = size(); iterator chr_from = begin(), chr_to; for (DWORD i = 0; i < si.dwNumberOfProcessors; i++, chr_from = chr_to) { size_type to = MulDiv(i + 1, total, si.dwNumberOfProcessors); for (chr_to = chr_from; from < to; from++, ++chr_to); workers[i] = new build_related_worker(this, chr_from, chr_to); } // Wait for workers. for (DWORD i = 0; i < si.dwNumberOfProcessors; i++) { if (workers[i]) { workers[i]->join(); delete workers[i]; } } delete [] workers; // This line of code sounds horrible, I know. } ZRCola::DBSource::character_bank::build_related_worker::build_related_worker(_In_ const character_bank *cb, _In_ iterator from, _In_ iterator to) : win_handle((HANDLE)_beginthreadex(NULL, 0, process, this, CREATE_SUSPENDED, NULL)), m_heap(HeapCreate(0, 0, 0)), m_cb(cb), m_from(from), m_to(to) { // Now that members of this class are surely initialized, proceed. ResumeThread(m_h); } unsigned int ZRCola::DBSource::character_bank::build_related_worker::process() { heap_allocator al(m_heap); vector > rel(al); set, heap_allocator > matching(less(), al); for (auto c = m_from; c != m_to; c++) { rel.clear(); // Skip all unexisting, or self related characters. auto m_cb_end = m_cb->cend(); for (std::vector::const_pointer c_rel = c->second.rel.data(), c_rel_end = c_rel + c->second.rel.size(), c_rel_next = c_rel_end; c_rel < c_rel_end; c_rel = c_rel_next) { c_rel_next = c_rel + wcsnlen(c_rel, c_rel_end - c_rel) + 1; if (m_cb->find(c_rel) != m_cb_end && c->first.compare(c_rel) != 0) rel.insert(rel.end(), c_rel, c_rel_next); } // Add all characters that share enough keywords. for (auto c2 = m_cb->cbegin(), c2_end = m_cb->cend(); c2 != c2_end; ++c2) { if (c == c2) continue; bool already_present = false; for (std::vector::const_pointer c_rel = rel.data(), c_rel_end = c_rel + rel.size(), c_rel_next = c_rel_end; c_rel < c_rel_end; c_rel = c_rel_next) { c_rel_next = c_rel + wcsnlen(c_rel, c_rel_end - c_rel) + 1; if (c2->first.compare(c_rel) == 0) { already_present = true; break; } } if (already_present) continue; set::size_type comparisons = 0; matching.clear(); for (auto term = c->second.terms_rel.cbegin(), term_end = c->second.terms_rel.cend(); term != term_end; ++term) { for (auto term2 = c2->second.terms_rel.cbegin(), term2_end = c2->second.terms_rel.cend(); term2 != term2_end; ++term2) { comparisons++; if (*term == *term2) matching.insert(*term); } } if (comparisons) { // If 1/2 terms match, assume related. auto hits = matching.size(); if (hits*hits*2 >= comparisons) rel.insert(rel.end(), c2->first.data(), c2->first.data() + c2->first.length() + 1); } } c->second.rel.assign(rel.cbegin(), rel.cend()); } return 0; } unsigned int __stdcall ZRCola::DBSource::character_bank::build_related_worker::process(_In_ void *param) { return ((ZRCola::DBSource::character_bank::build_related_worker*)param)->process(); } ////////////////////////////////////////////////////////////////////////// // ZRCola::DBSource::character_desc_idx ////////////////////////////////////////////////////////////////////////// void ZRCola::DBSource::character_desc_idx::parse_keywords(_In_ const wchar_t *str, _Inout_ set &terms) { wxASSERT_MSG(str, wxT("string is NULL")); while (*str) { // Skip white space. for (;;) { if (*str == 0) return; else if (!iswspace(*str)) break; else str++; } // Get term. wstring term; if (*str == L'"') { const wchar_t *str_end = ++str; for (;;) { if (*str_end == 0) { term.assign(str, str_end); break; } else if (*str_end == L'"') { term.assign(str, str_end); str_end++; break; } else str_end++; } str = str_end; } else { const wchar_t *str_end = str + 1; for (; *str_end && !iswspace(*str_end); str_end++); term.assign(str, str_end); str = str_end; } if (!term.empty()) { transform(term.begin(), term.end(), term.begin(), towlower); terms.insert(term); } } } void ZRCola::DBSource::character_desc_idx::add_keywords(const set &terms, const wstring &chr, size_t sub) { for (auto term = terms.cbegin(), term_end = terms.cend(); term != term_end; ++term) { if (sub) { wstring::size_type j_end = term->size(); if (j_end >= sub) { // Insert all keyword substrings "sub" or more characters long. for (wstring::size_type i = 0, i_end = j_end - sub; i <= i_end; ++i) { for (wstring::size_type j = i + sub; j <= j_end; ++j) add_keyword(term->substr(i, j - i), chr); } } } else { // Insert exact keyword only. add_keyword(*term, chr); } } } void ZRCola::DBSource::character_desc_idx::save(ZRCola::textindex &idx) const { idx .clear(); idx.keys .clear(); idx.values.clear(); // Pre-allocate memory. vector::size_type size_keys = 0; vector::size_type size_values = 0; for (const_iterator i = cbegin(), i_end = cend(); i != i_end; ++i) { size_keys += i->first.size(); size_values += i->second.size(); } idx .reserve(size() ); idx.keys .reserve(size_keys ); idx.values.reserve(size_values); // Convert the index. for (const_iterator i = cbegin(), i_end = cend(); i != i_end; ++i) { ZRCola::mappair_t p = { idx.keys.size(), idx.values.size() }; idx.push_back(p); idx.keys.insert(idx.keys.end(), i->first.cbegin(), i->first.cend()); idx.values.insert(idx.values.end(), i->second.cbegin(), i->second.cend()); } } ////////////////////////////////////////////////////////////////////////// // ZRCola::DBSource ////////////////////////////////////////////////////////////////////////// ZRCola::DBSource::DBSource() : m_locale(nullptr) { // Initialize ignore list. m_terms_ignore.insert(L"letter"); m_terms_ignore.insert(L"modifier"); m_terms_ignore.insert(L"symbol"); m_terms_ignore.insert(L"accent"); m_terms_ignore.insert(L"with"); m_terms_ignore.insert(L"and"); m_terms_ignore.insert(L"capital"); m_terms_ignore.insert(L"small"); m_terms_ignore.insert(L"combining"); } ZRCola::DBSource::~DBSource() { // Manually release all COM objects related to the database before we close the database. m_pTranslationSets1.free(); m_comTranslationSets.free(); m_pTranslation1.free(); m_comTranslation.free(); m_pCharacterGroup1.free(); m_comCharacterGroup.free(); m_pHighlight1.free(); m_comHighlight.free(); if (m_db) m_db->Close(); if (m_locale) _free_locale(m_locale); } bool ZRCola::DBSource::Open(LPCTSTR filename) { wxASSERT_MSG(!m_db, wxT("database already open")); // Create COM object. HRESULT hr = ::CoCreateInstance(CLSID_CADOConnection, NULL, CLSCTX_ALL, IID_IADOConnection, (LPVOID*)&m_db); if (SUCCEEDED(hr)) { // Open the database. wstring cn; cn = L"Driver={Microsoft Access Driver (*.mdb)};"; cn += L"Dbq="; cn += filename; cn += L";Uid=;Pwd=;"; #pragma warning(push) #pragma warning(disable: 6387) // Connection15::Open() declaration is wrong: it defaults username and password parameters to NULL, but annotates them as required non-NULL. hr = m_db->Open(bstr(cn.c_str())); #pragma warning(pop) if (SUCCEEDED(hr)) { // Database open and ready. m_filename = filename; m_locale = _create_locale(LC_ALL, "Slovenian_Slovenia.1250"); // Create ADO command(s). wxASSERT_MSG(!m_comCharacterGroup, wxT("ADO command already created")); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADOCommand, NULL, CLSCTX_ALL, IID_IADOCommand, (LPVOID*)&m_comCharacterGroup))); wxVERIFY(SUCCEEDED(m_comCharacterGroup->put_ActiveConnection(variant(m_db)))); wxVERIFY(SUCCEEDED(m_comCharacterGroup->put_CommandType(adCmdText))); wxVERIFY(SUCCEEDED(m_comCharacterGroup->put_CommandText(bstr(L"SELECT [VRS_SkupineZnakov].[Znak], [VRS_SkupineZnakov].[pogost] " L"FROM [VRS_SkupineZnakov] " L"LEFT JOIN [VRS_CharList] ON [VRS_SkupineZnakov].[Znak]=[VRS_CharList].[znak] " L"WHERE [VRS_CharList].[aktiven]=1 AND [VRS_SkupineZnakov].[Skupina]=? " L"ORDER BY [VRS_SkupineZnakov].[Rang] ASC, [VRS_SkupineZnakov].[Znak] ASC")))); { // Create and add command parameters. com_obj params; wxVERIFY(SUCCEEDED(m_comCharacterGroup->get_Parameters(¶ms))); wxASSERT_MSG(!m_pCharacterGroup1, wxT("ADO command parameter already created")); wxVERIFY(SUCCEEDED(m_comCharacterGroup->CreateParameter(bstr(L"@Skupina"), adVarWChar, adParamInput, 50, variant(DISP_E_PARAMNOTFOUND, VT_ERROR), &m_pCharacterGroup1))); wxVERIFY(SUCCEEDED(params->Append(m_pCharacterGroup1))); } wxASSERT_MSG(!m_comTranslation, wxT("ADO command already created")); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADOCommand, NULL, CLSCTX_ALL, IID_IADOCommand, (LPVOID*)&m_comTranslation))); wxVERIFY(SUCCEEDED(m_comTranslation->put_ActiveConnection(variant(m_db)))); wxVERIFY(SUCCEEDED(m_comTranslation->put_CommandType(adCmdText))); wxVERIFY(SUCCEEDED(m_comTranslation->put_CommandText(bstr(L"SELECT [Komb1] AS [komb], [rang_komb1] AS [rang_komb], '' AS [Kano], 0 AS [Kanoniziraj], [Komb2] AS [znak], [rang_komb2] AS [rang_znak] " L"FROM [VRS_ScriptRepl2] " L"WHERE [Script]=? " L"ORDER BY [Komb2], [rang_komb2], [rang_komb1], [Komb1]")))); { // Create and add command parameters. com_obj params; wxVERIFY(SUCCEEDED(m_comTranslation->get_Parameters(¶ms))); wxASSERT_MSG(!m_pTranslation1, wxT("ADO command parameter already created")); wxVERIFY(SUCCEEDED(m_comTranslation->CreateParameter(bstr(L"@Script"), adSmallInt, adParamInput, 0, variant(DISP_E_PARAMNOTFOUND, VT_ERROR), &m_pTranslation1))); wxVERIFY(SUCCEEDED(params->Append(m_pTranslation1))); } wxASSERT_MSG(!m_comTranslationSets, wxT("ADO command already created")); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADOCommand, NULL, CLSCTX_ALL, IID_IADOCommand, (LPVOID*)&m_comTranslationSets))); wxVERIFY(SUCCEEDED(m_comTranslationSets->put_ActiveConnection(variant(m_db)))); wxVERIFY(SUCCEEDED(m_comTranslationSets->put_CommandType(adCmdText))); wxVERIFY(SUCCEEDED(m_comTranslationSets->put_CommandText(bstr(L"SELECT [Script] " L"FROM [VRS_Script2SeqScr] " L"WHERE [ID]=? " L"ORDER BY [Rank] ASC")))); { // Create and add command parameters. com_obj params; wxVERIFY(SUCCEEDED(m_comTranslationSets->get_Parameters(¶ms))); wxASSERT_MSG(!m_pTranslationSets1, wxT("ADO command parameter already created")); wxVERIFY(SUCCEEDED(m_comTranslationSets->CreateParameter(bstr(L"@ID"), adSmallInt, adParamInput, 0, variant(DISP_E_PARAMNOTFOUND, VT_ERROR), &m_pTranslationSets1))); wxVERIFY(SUCCEEDED(params->Append(m_pTranslationSets1))); } wxASSERT_MSG(!m_comHighlight, wxT("ADO command already created")); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADOCommand, NULL, CLSCTX_ALL, IID_IADOCommand, (LPVOID*)&m_comHighlight))); wxVERIFY(SUCCEEDED(m_comHighlight->put_ActiveConnection(variant(m_db)))); wxVERIFY(SUCCEEDED(m_comHighlight->put_CommandType(adCmdText))); wxVERIFY(SUCCEEDED(m_comHighlight->put_CommandText(bstr(L"SELECT [komb] " L"FROM [VRS_HighlightChars2] " L"WHERE [group]=? " L"ORDER BY [komb]")))); { // Create and add command parameters. com_obj params; wxVERIFY(SUCCEEDED(m_comHighlight->get_Parameters(¶ms))); wxASSERT_MSG(!m_pHighlight1, wxT("ADO command parameter already created")); wxVERIFY(SUCCEEDED(m_comHighlight->CreateParameter(bstr(L"@group"), adSmallInt, adParamInput, 0, variant(DISP_E_PARAMNOTFOUND, VT_ERROR), &m_pHighlight1))); wxVERIFY(SUCCEEDED(params->Append(m_pHighlight1))); } return true; } else { _ftprintf(stderr, wxT("%s: error ZCC0011: Could not open database (0x%x).\n"), (LPCTSTR)filename, hr); LogErrors(); } m_db.free(); } else _ftprintf(stderr, wxT("%s: error ZCC0012: Creating ADOConnection object failed (0x%x).\n"), (LPCTSTR)filename, hr); return false; } void ZRCola::DBSource::LogErrors() const { wxASSERT_MSG(m_db, wxT("database does not exist")); // Get array of errors. ADOErrors *errors = NULL; if (SUCCEEDED(m_db->get_Errors(&errors))) { // Get number of errors. long n = 0; wxVERIFY(SUCCEEDED(errors->get_Count(&n))); // Iterate the errors. for (long i = 0; i < n; i++) { ADOError *err = NULL; if (SUCCEEDED(errors->get_Item(variant(i), &err))) { // Write error number and description to the log. long num = 0; wxVERIFY(SUCCEEDED(err->get_Number(&num))); bstr desc; wxVERIFY(SUCCEEDED(err->get_Description(&desc))); _ftprintf(stderr, wxT(" error ADO%x: %ls\n"), num, (BSTR)desc); err->Release(); } } errors->Release(); } } bool ZRCola::DBSource::GetValue(const com_obj& f, bool& val) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); wxCHECK(SUCCEEDED(v.change_type(VT_BOOL)), false); val = V_BOOL(&v) ? true : false; return true; } bool ZRCola::DBSource::GetValue(const com_obj& f, short& val) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); wxCHECK(SUCCEEDED(v.change_type(VT_I2)), false); val = V_I2(&v); return true; } bool ZRCola::DBSource::GetValue(const com_obj& f, string& val) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); WideCharToMultiByte(CP_ACP, 0, V_BSTR(&v), ::SysStringLen(V_BSTR(&v)), val, NULL, NULL); } else val.clear(); return true; } bool ZRCola::DBSource::GetValue(const com_obj& f, wstring& val) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); val.reserve(::SysStringLen(V_BSTR(&v))); val = V_BSTR(&v); } else val.clear(); return true; } bool ZRCola::DBSource::GetUnicodeCharacter(const com_obj& f, wchar_t& chr) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Parse the field. Must be exactly one Unicode code. UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); chr = 0; for (; i < n && V_BSTR(&v)[i]; i++) { if (L'0' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'9') chr = chr*0x10 + (V_BSTR(&v)[i] - L'0'); else if (L'A' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'F') chr = chr*0x10 + (V_BSTR(&v)[i] - L'A' + 10); else if (L'a' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'f') chr = chr*0x10 + (V_BSTR(&v)[i] - L'a' + 10); else break; } if (i <= 0 && 4 < i) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0030: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } else if (i != n) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0031: Syntax error in \"%.*ls\" field (\"%.*ls\"). Extra trailing characters.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } } else chr = 0; return true; } bool ZRCola::DBSource::GetUnicodeString(const com_obj& f, wstring& str) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); str.clear(); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Parse the field. Must be "xxxx+xxxx+xxxx..." sequence. for (UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); i < n && V_BSTR(&v)[i];) { // Parse Unicode code. UINT j = 0; wchar_t c = 0; for (; i < n && V_BSTR(&v)[i]; i++, j++) { if (L'0' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'9') c = c*0x10 + (V_BSTR(&v)[i] - L'0'); else if (L'A' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'F') c = c*0x10 + (V_BSTR(&v)[i] - L'A' + 10); else if (L'a' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'f') c = c*0x10 + (V_BSTR(&v)[i] - L'a' + 10); else break; } if (j <= 0 || 4 < j) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0020: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } str += c; // Skip delimiter(s) and whitespace. for (; i < n && V_BSTR(&v)[i] && (V_BSTR(&v)[i] == L'+' || _iswspace_l(V_BSTR(&v)[i], m_locale)); i++); } } return true; } bool ZRCola::DBSource::GetNormPerm(const winstd::com_obj& f, normperm& np) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); np.clear(); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Parse the field. Must be "nnnn,nnnn,nnnn..." sequence. for (UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); i < n && V_BSTR(&v)[i];) { // Parse Unicode code. UINT j = 0; std::vector p; for (; i < n && V_BSTR(&v)[i]; i++, j++) { if (L'0' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'9') p.push_back(V_BSTR(&v)[i] - L'0'); else break; } if (j <= 0) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0150: Syntax error in \"%.*ls\" field (\"%.*ls\"). Permutation sequence must be at least one decimal digit long.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } np.insert(std::move(p)); // Skip delimiter(s) and whitespace. for (; i < n && V_BSTR(&v)[i] && (V_BSTR(&v)[i] == L',' || _iswspace_l(V_BSTR(&v)[i], m_locale)); i++); } } return true; } bool ZRCola::DBSource::GetLanguage(const com_obj& f, ZRCola::langid_t& lang) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Convert to lowercase. _wcslwr_l(V_BSTR(&v), m_locale); // Parse the field. size_t n = wcsnlen(V_BSTR(&v), ::SysStringLen(V_BSTR(&v))); if (n != 3) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0080: Syntax error in \"%.*ls\" field (\"%.*ls\"). Language ID must be exactly three (3) characters long.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } for (size_t i = 0;; i++) { if (i < sizeof(lang)) { if (i < n) { wchar_t c = V_BSTR(&v)[i]; if ((unsigned short)c > 0x7f) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0081: Syntax error in \"%.*ls\" field (\"%.*ls\"). Language ID must contain ASCII characters only.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } lang.data[i] = (char)c; } else lang.data[i] = 0; } else break; } return true; } bool ZRCola::DBSource::GetChrCat(const com_obj& f, chrcatid_t& cc) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); if (V_VT(&v) != VT_NULL) { wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Parse the field. size_t n = wcsnlen(V_BSTR(&v), ::SysStringLen(V_BSTR(&v))); if (n < 1 || 2 < n) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0110: Syntax error in \"%.*ls\" field (\"%.*ls\"). Character category ID must be one (1) or two (2) characters long.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } for (size_t i = 0;; i++) { if (i < sizeof(cc)) { if (i < n) { wchar_t c = V_BSTR(&v)[i]; if ((unsigned short)c > 0x7f) { bstr fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); _ftprintf(stderr, wxT("%s: error ZCC0111: Syntax error in \"%.*ls\" field (\"%.*ls\"). Character category ID must contain ASCII characters only.\n"), m_filename.c_str(), fieldname.length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } cc.data[i] = (char)c; } else cc.data[i] = 0; } else break; } } else memset(cc.data, 0, sizeof(cc)); return true; } bool ZRCola::DBSource::GetTagNames(const winstd::com_obj& f, LCID lcid, list& names) const { wxASSERT_MSG(f, wxT("field is empty")); variant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); wxCHECK(SUCCEEDED(v.change_type(VT_BSTR)), false); // Parse the field. Must be "name, name, name..." sequence. names.clear(); for (UINT i = 0, i_end = ::SysStringLen(V_BSTR(&v)); i < i_end && V_BSTR(&v)[i];) { if (iswspace(V_BSTR(&v)[i])) { // Skip leading white space. i++; continue; } // Parse name. UINT j = i, j_end = i; for (; i < i_end && V_BSTR(&v)[i]; i++) { if (V_BSTR(&v)[i] == L',' || V_BSTR(&v)[i] == L';') { // Delimiter found. i++; break; } else if (!iswspace(V_BSTR(&v)[i])) { // Remember last non-white space character. j_end = i + 1; } } wstring name(V_BSTR(&v) + j, V_BSTR(&v) + j_end); for (auto n = names.cbegin(), n_end = names.cend(); ; ++n) { if (n == n_end) { // Add name to the list. names.push_back(std::move(name)); break; } else if (ZRCola::tagname_db::tagname::CompareName(lcid, n->data(), (unsigned __int16)n->length(), name.data(), (unsigned __int16)name.length()) == CSTR_EQUAL) { // Name is already on the list. break; } } } return true; } bool ZRCola::DBSource::SelectNormPermSets(winstd::com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT [oblika], [oblike] " L"FROM [VRS_CharCanoOblike] " L"ORDER BY [oblika], [oblike]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0160: Error loading normalization permutation sets from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetNormPerm(const winstd::com_obj& rs, std::string& norm, normperm& np) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"oblika"), &f))); wxCHECK(GetValue(f, norm), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"oblike"), &f))); wxCHECK(GetNormPerm(f, np), false); } // Verify all lengths match. size_t n = norm.length(); for (auto p = np.cbegin(), p_end = np.cend(); p != p_end; ++p) { if (p->size() != n) { _ftprintf(stderr, wxT("%s: error ZCC0170: Inconsistent normalization sequence \"%.*hs\" permutation length. Please make sure all permutation lengths match normalization sequence length (%u).\n"), m_filename.c_str(), n, norm.c_str(), n); return false; } } return true; } bool ZRCola::DBSource::SelectTranslations(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT [komb], [rang_komb], [Kano], [Kanoniziraj], [znak], [rang_znak] " L"FROM [VRS_ReplChar] " L"WHERE [rang_komb]=1 " L"ORDER BY [znak], [rang_znak], [rang_komb], [komb]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0040: Error loading translations from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::SelectTranslations(short set, winstd::com_obj& rs) const { // Create a new recordset. rs.free(); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs))); wxVERIFY(SUCCEEDED(rs->put_CursorLocation(adUseClient))); wxVERIFY(SUCCEEDED(rs->put_CursorType(adOpenForwardOnly))); wxVERIFY(SUCCEEDED(rs->put_LockType(adLockReadOnly))); // Open it. wxVERIFY(SUCCEEDED(m_pTranslation1->put_Value(variant(set)))); if (FAILED(rs->Open(variant(m_comTranslation), variant(DISP_E_PARAMNOTFOUND, VT_ERROR)))) { _ftprintf(stderr, wxT("%s: error ZCC0100: Error loading translations from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetTranslation(const com_obj& rs, ZRCola::DBSource::translation& t) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"komb"), &f))); wxCHECK(GetUnicodeString(f, t.src.str), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"rang_komb"), &f))); wxCHECK(GetValue(f, t.src.rank), false); } { bool norm; com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Kanoniziraj"), &f))); wxCHECK(GetValue(f, norm), false); if (norm) { com_obj f2; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Kano"), &f2))); wxCHECK(GetValue(f2, t.norm), false); } else t.norm.clear(); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"rang_znak"), &f))); wxCHECK(GetValue(f, t.dst.rank), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak"), &f))); wxCHECK(GetUnicodeString(f, t.dst.str), false); } return true; } bool ZRCola::DBSource::SelectTranlationSets(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [entCode], [Src_En], [Dst_En] " L"FROM [VRS_Script2] " L"ORDER BY [entCode], [Src_En], [Dst_En]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0060: Error loading translation sets from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetTranslationSet(const com_obj& rs, ZRCola::DBSource::transet& ts) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"entCode"), &f))); wxCHECK(GetValue(f, ts.set), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Src_En"), &f))); wxCHECK(GetValue(f, ts.src), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Dst_En"), &f))); wxCHECK(GetValue(f, ts.dst), false); } return true; } bool ZRCola::DBSource::SelectTranlationSeqs(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [ID], [Descr], [Rank] " L"FROM [VRS_Script2Seq] " L"ORDER BY [Rank], [Descr]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0060: Error loading translation sequences from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetTranslationSeq(const com_obj& rs, ZRCola::DBSource::transeq& ts) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"ID"), &f))); wxCHECK(GetValue(f, ts.seq), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Rank"), &f))); wxCHECK(GetValue(f, ts.rank), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Descr"), &f))); wxCHECK(GetValue(f, ts.name), false); } // Read translation sequence sets from database. wxVERIFY(SUCCEEDED(m_pTranslationSets1->put_Value(variant(ts.seq)))); com_obj rs_chars; wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs_chars))); wxVERIFY(SUCCEEDED(rs_chars->put_CursorLocation(adUseClient))); wxVERIFY(SUCCEEDED(rs_chars->put_CursorType(adOpenForwardOnly))); wxVERIFY(SUCCEEDED(rs_chars->put_LockType(adLockReadOnly))); if (FAILED(rs_chars->Open(variant(m_comTranslationSets), variant(DISP_E_PARAMNOTFOUND, VT_ERROR)))) { _ftprintf(stderr, wxT("%s: error ZCC0140: Error loading character group characters from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } { ts.sets.clear(); com_obj flds2; wxVERIFY(SUCCEEDED(rs_chars->get_Fields(&flds2))); com_obj f_set; wxVERIFY(SUCCEEDED(flds2->get_Item(variant(L"Script"), &f_set))); size_t n = 0; for (VARIANT_BOOL eof = VARIANT_TRUE; SUCCEEDED(rs_chars->get_EOF(&eof)) && !eof; rs_chars->MoveNext(), n++) { short set; wxCHECK(GetValue(f_set, set), false); ts.sets.push_back(set); } } return true; } bool ZRCola::DBSource::SelectKeySequences(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [VRS_KeyCodes].[Znak], [VRS_CharGroup].[CharGroup], IIF([VRS_CharGroup].[Arg1] IS NOT NULL, [VRS_CharGroup].[Arg1], 0)+IIF([VRS_CharGroup].[Arg2] IS NOT NULL, [VRS_CharGroup].[Arg2], 0)+IIF([VRS_CharGroup].[Arg3] IS NOT NULL, [VRS_CharGroup].[Arg3], 0) AS [Modifiers], IIF([VRS_CharGroup].[Arg4] IS NOT NULL, [VRS_CharGroup].[Arg4], 0) AS [KeyCodePre], [VRS_KeyCodes].[KeyCode], [VRS_KeyCodes].[Shift] " L"FROM [VRS_KeyCodes] LEFT JOIN [VRS_CharGroup] ON [VRS_CharGroup].[CharGroup]=[VRS_KeyCodes].[CharGroup] " L"ORDER BY [VRS_CharGroup].[CharGroup], [VRS_KeyCodes].[KeyCode], [VRS_KeyCodes].[Shift], [VRS_KeyCodes].[Znak]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0050: Error loading key sequences from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetKeySequence(const com_obj& rs, ZRCola::DBSource::keyseq& ks) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Znak"), &f))); wxCHECK(GetUnicodeString(f, ks.chr), false); } short modifiers; { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Modifiers"), &f))); wxCHECK(GetValue(f, modifiers), false); } short keycode1; { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"KeyCodePre"), &f))); wxCHECK(GetValue(f, keycode1), false); } short keycode; { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"KeyCode"), &f))); wxCHECK(GetValue(f, keycode), false); } bool shift; { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Shift"), &f))); wxCHECK(GetValue(f, shift), false); } ks.seq.clear(); if (keycode1) { // First key in the sequence is complete. keyseq::keycode kc1 = { keyseq::keycode::translate_slen(static_cast(keycode1)), (modifiers & 0x100) != 0, (modifiers & 0x200) != 0, (modifiers & 0x400) != 0 }; ks.seq.push_back(kc1); keyseq::keycode kc2 = { keyseq::keycode::translate_slen(static_cast(keycode)), shift }; ks.seq.push_back(kc2); } else { // First key in the sequence is only modifier(s). keyseq::keycode kc1 = { keyseq::keycode::translate_slen(static_cast(keycode)), shift || (modifiers & 0x100) != 0, (modifiers & 0x200) != 0, (modifiers & 0x400) != 0 }; ks.seq.push_back(kc1); } return true; } bool ZRCola::DBSource::SelectLanguages(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [entCode], [Jezik_En] " L"FROM [VRS_Jezik] " L"ORDER BY [entCode], [Jezik_En]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0060: Error loading languages from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetLanguage(const com_obj& rs, ZRCola::DBSource::language& lang) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"entCode"), &f))); wxCHECK(GetLanguage(f, lang.lang), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Jezik_En"), &f))); wxCHECK(GetValue(f, lang.name), false); } return true; } bool ZRCola::DBSource::SelectLanguageCharacters(com_obj &rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [znak], [lang] " L"FROM [VRS_CharLocal] " L"ORDER BY [znak], [lang]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0090: Error loading language characters from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetLanguageCharacter(const com_obj& rs, ZRCola::DBSource::langchar& lc) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak"), &f))); wxCHECK(GetUnicodeString(f, lc.chr), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"lang"), &f))); wxCHECK(GetLanguage(f, lc.lang), false); } return true; } bool ZRCola::DBSource::SelectCharacterGroups(com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [id], [Skupina], [opis_en], [Rang] " L"FROM [VRS_SkupinaZnakov] " L"ORDER BY [Rang], [opis_en]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0090: Error loading character groups from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetCharacterGroup(const com_obj& rs, chrgrp& cg) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); wstring grp; { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"id"), &f))); wxCHECK(GetValue(f, cg.grp), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Skupina"), &f))); wxCHECK(GetValue(f, grp), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Rang"), &f))); wxCHECK(GetValue(f, cg.rank), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_en"), &f))); wxCHECK(GetValue(f, cg.name), false); } // Read character list from database. wxVERIFY(SUCCEEDED(m_pCharacterGroup1->put_Value(variant(grp.c_str())))); com_obj rs_chars; wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs_chars))); wxVERIFY(SUCCEEDED(rs_chars->put_CursorLocation(adUseClient))); wxVERIFY(SUCCEEDED(rs_chars->put_CursorType(adOpenForwardOnly))); wxVERIFY(SUCCEEDED(rs_chars->put_LockType(adLockReadOnly))); if (FAILED(rs_chars->Open(variant(m_comCharacterGroup), variant(DISP_E_PARAMNOTFOUND, VT_ERROR)))) { _ftprintf(stderr, wxT("%s: error ZCC0140: Error loading character group characters from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } { cg.chars.clear(); cg.show.clear(); com_obj flds2; wxVERIFY(SUCCEEDED(rs_chars->get_Fields(&flds2))); com_obj f_char, f_show; wxVERIFY(SUCCEEDED(flds2->get_Item(variant(L"Znak" ), &f_char))); wxVERIFY(SUCCEEDED(flds2->get_Item(variant(L"pogost"), &f_show))); size_t n = 0; for (VARIANT_BOOL eof = VARIANT_TRUE; SUCCEEDED(rs_chars->get_EOF(&eof)) && !eof; rs_chars->MoveNext(), n++) { wstring c; wxCHECK(GetUnicodeString(f_char, c), false); cg.chars.insert(cg.chars.end(), c.data(), c.data() + c.length() + 1); bool show; wxCHECK(GetValue(f_show, show), false); if ((n % 16) == 0) cg.show.push_back(show ? 1 : 0); else if (show) cg.show[n / 16] |= 1 << (n % 16); } } return true; } bool ZRCola::DBSource::SelectCharacters(com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [znak], [opis_en], [kat], [znak_v], [znak_m] " L"FROM [VRS_CharList] " L"WHERE " L"[aktiven]=1 AND " // Active characters only L"[kat]<>'g' " // Ignore "Other, Control" category! L"ORDER BY [znak]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0120: Error loading characters from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetCharacter(const com_obj& rs, character& chr) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); wstring c; chr.second.terms.clear(); chr.second.terms_rel.clear(); chr.second.rel.clear(); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak"), &f))); wxCHECK(GetUnicodeString(f, chr.first), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak_v"), &f))); wxCHECK(GetUnicodeString(f, c), false); if (!c.empty() && c != chr.first) chr.second.rel.insert(chr.second.rel.end(), c.data(), c.data() + c.length() + 1); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak_m"), &f))); wxCHECK(GetUnicodeString(f, c), false); if (!c.empty() && c != chr.first) chr.second.rel.insert(chr.second.rel.end(), c.data(), c.data() + c.length() + 1); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_en"), &f))); wxCHECK(GetValue(f, chr.second.desc), false); ZRCola::DBSource::character_desc_idx::parse_keywords(chr.second.desc.c_str(), chr.second.terms); for (auto term = chr.second.terms.cbegin(), term_end = chr.second.terms.cend(); term != term_end; ++term) { if (m_terms_ignore.find(*term) != m_terms_ignore.cend()) continue; chr.second.terms_rel.insert(*term); } } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"kat"), &f))); wxCHECK(GetChrCat(f, chr.second.cat), false); } return true; } bool ZRCola::DBSource::SelectCharacterCategories(com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [kat], [opis_en], [Rang] " L"FROM [VRS_CharCategory] " L"WHERE [kat]<>'g' " // Ignore "Other, Control" category! L"ORDER BY [Rang], [opis_en]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0130: Error loading character categories from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetCharacterCategory(const com_obj& rs, chrcat& cc) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"kat"), &f))); wxCHECK(GetChrCat(f, cc.cat), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"Rang"), &f))); wxCHECK(GetValue(f, cc.rank), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_en"), &f))); wxCHECK(GetValue(f, cc.name), false); } return true; } bool ZRCola::DBSource::SelectCharacterTags(winstd::com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [znak], [oznaka] " L"FROM [VRS_CharTags] " L"ORDER BY [znak], [oznaka]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0130: Error loading character tags from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetCharacterTag(const winstd::com_obj& rs, chrtag& ct) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"znak"), &f))); wxCHECK(GetUnicodeString(f, ct.chr), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"oznaka"), &f))); wxCHECK(GetValue(f, ct.tag), false); } return true; } bool ZRCola::DBSource::SelectTagNames(winstd::com_obj& rs) const { // Create a new recordset. rs.free(); wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); // Open it. if (FAILED(rs->Open(variant( L"SELECT DISTINCT [oznaka], [opis_en], [opis_sl], [opis_ru] " L"FROM [VRS_Tags] " L"ORDER BY [oznaka]"), variant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { _ftprintf(stderr, wxT("%s: error ZCC0130: Error loading tags from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetTagName(const winstd::com_obj& rs, tagname& tn) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); tn.names.clear(); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"oznaka"), &f))); wxCHECK(GetValue(f, tn.tag), false); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_en"), &f))); LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT); list names; wxCHECK(GetTagNames(f, lcid, names), false); tn.names.insert(std::move(pair >(lcid, std::move(names)))); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_sl"), &f))); LCID lcid = MAKELCID(MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT), SORT_DEFAULT); list names; wxCHECK(GetTagNames(f, lcid, names), false); tn.names.insert(std::move(pair >(lcid, std::move(names)))); } { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"opis_ru"), &f))); LCID lcid = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), SORT_DEFAULT); list names; wxCHECK(GetTagNames(f, lcid, names), false); tn.names.insert(std::move(pair >(lcid, std::move(names)))); } return true; } bool ZRCola::DBSource::SelectHighlights(short set, winstd::com_obj& rs) const { // Create a new recordset. rs.free(); wxVERIFY(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs))); wxVERIFY(SUCCEEDED(rs->put_CursorLocation(adUseClient))); wxVERIFY(SUCCEEDED(rs->put_CursorType(adOpenForwardOnly))); wxVERIFY(SUCCEEDED(rs->put_LockType(adLockReadOnly))); // Open it. wxVERIFY(SUCCEEDED(m_pHighlight1->put_Value(variant(set)))); if (FAILED(rs->Open(variant(m_comHighlight), variant(DISP_E_PARAMNOTFOUND, VT_ERROR)))) { _ftprintf(stderr, wxT("%s: error ZCC0101: Error loading highlights from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } return true; } bool ZRCola::DBSource::GetHighlight(const com_obj& rs, ZRCola::DBSource::highlight& h) const { wxASSERT_MSG(rs, wxT("recordset is empty")); com_obj flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { com_obj f; wxVERIFY(SUCCEEDED(flds->get_Item(variant(L"komb"), &f))); wxCHECK(GetUnicodeString(f, h.chr), false); } return true; }