From 11369f5778a5666414218f102fef9cd93ae6416e Mon Sep 17 00:00:00 2001 From: lzwdgc Date: Sun, 25 Feb 2024 18:56:02 +0300 Subject: [PATCH] Initial language switcher. --- .../language_switcher.cpp | 234 ++++++++++++++++++ sw.cpp | 37 ++- 2 files changed, 258 insertions(+), 13 deletions(-) create mode 100644 src/aim1_language_switcher/language_switcher.cpp diff --git a/src/aim1_language_switcher/language_switcher.cpp b/src/aim1_language_switcher/language_switcher.cpp new file mode 100644 index 0000000..7645e71 --- /dev/null +++ b/src/aim1_language_switcher/language_switcher.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +using namespace std::literals; +namespace fs = std::filesystem; +fs::path datadir = fs::current_path(); + +#include + +#include +#include +#include + +auto read_file(const fs::path &fn) { + auto sz = fs::file_size(fn); + std::string s(sz, 0); + FILE *f = fopen(fn.string().c_str(), "rb"); + fread(s.data(), s.size(), 1, f); + fclose(f); + return s; +} +void write_file(const fs::path &fn, const std::string &s = {}) { + fs::create_directories(fn.parent_path()); + + FILE *f = fopen(fn.string().c_str(), "wb"); + fwrite(s.data(), s.size(), 1, f); + fclose(f); +} +void err(const std::wstring &s) { + MessageBox(0, s.c_str(), TEXT("ERROR"), MB_OK); + exit(1); +} +struct cfg { + fs::path cfgfn; + std::string findstr = "TextFile="s; + size_t pos{UINT64_MAX},pose{UINT64_MAX}; + std::string s; + + cfg() { + if (fs::exists(datadir / "data")) { + datadir /= "data"; + } else if (datadir.filename() == "data") { + } else { + err(L"Can't find data directory!"); + } + cfgfn = datadir / "config" / "cfg.ini"; + } + std::wstring get_current_lang() { + s = read_file(cfgfn); + pos = s.find(findstr); + if (pos == -1) { + err(L"cant find"); + return {}; + } + pose = s.find_first_of("\r\n", pos); + fs::path fn = s.substr(pos + findstr.size(), pose - (pos + findstr.size())); + auto w = fn.stem().wstring(); + std::transform(w.begin(), w.end(), w.begin(), ::towlower); + auto tofind = L"quest_"s; + if (w.starts_with(tofind)) { + auto lang = w.substr(tofind.size()); + std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower); + return lang; + } + return L"no language set (your default language)"; + } + bool set_lang(auto &&lang) { + if (lang.contains(L' ') || lang.contains(L" ")) { + return false; + } + auto out = s.substr(0, pos + findstr.size()) + std::format("Data\\Quest_{}.dat", fs::path{lang}.string()) + s.substr(pose); + write_file(cfgfn, out); + write_file(cfgfn.parent_path() / "language.txt", fs::path{lang}.string()); + return true; + } +} c; + +#ifndef HINST_THISCOMPONENT +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +#define HINST_THISCOMPONENT ((HINSTANCE) & __ImageBase) +#endif + +struct DemoApp { + HRESULT Initialize() { + WNDCLASSEX wcex = {sizeof(WNDCLASSEX)}; + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = DemoApp::WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = sizeof(LONG_PTR); + wcex.hInstance = HINST_THISCOMPONENT; + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.lpszClassName = TEXT("DemoApp"); + RegisterClassEx(&wcex); + + // Create the application window. + int dpiX = 0; + int dpiY = 0; + int horpix{}, vertpix{}; + HDC hdc = GetDC(NULL); + if (hdc) { + dpiX = GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = GetDeviceCaps(hdc, LOGPIXELSY); + + horpix = GetDeviceCaps(hdc, HORZRES); + vertpix = GetDeviceCaps(hdc, VERTRES); + ReleaseDC(NULL, hdc); + } + + int width = 400; + int height = 100; + m_hwnd = CreateWindow(TEXT("DemoApp"), TEXT("AIM1 Language Switcher"), WS_OVERLAPPEDWINDOW + , (horpix - width) / 2 + , (vertpix - height) / 2 + , static_cast(ceil(width * dpiX / 96.f)) + , static_cast(ceil(height * dpiY / 96.f)) + , NULL, NULL, HINST_THISCOMPONENT, this); + + auto hr = m_hwnd ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) { + ShowWindow(m_hwnd, SW_SHOWNORMAL); + UpdateWindow(m_hwnd); + } + + // Create the Combobox + int nwidth = 300; // Width of the window + int nheight = 200; // Height of the window + int xpos = (width - nwidth) / 2; // Horizontal position of the window. + int ypos = 15;//(height - nheight) / 2; // Vertical position of the window. + HWND hwndParent = m_hwnd; // Handle to the parent window + + HWND hWndComboBox = + CreateWindow(WC_COMBOBOX, TEXT(""), CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE + | CBS_AUTOHSCROLL | WS_HSCROLL | WS_VSCROLL, + xpos, ypos, nwidth, nheight, hwndParent, NULL, HINST_THISCOMPONENT, NULL); + + std::set langs; + for (auto &&fn : fs::directory_iterator(datadir)) { + if (!fs::is_regular_file(fn)) { + continue; + } + auto q = fn.path().stem().wstring(); + auto tofind = L"quest_"s; + if (!q.starts_with(tofind)) { + continue; + } + auto lang = q.substr(tofind.size()); + std::transform(lang.begin(), lang.end(), lang.begin(), ::towlower); + langs.insert(lang); + } + auto [it,_] = langs.insert(c.get_current_lang()); + auto dist = std::distance(langs.begin(), it); + for (auto &&l : langs) { + SendMessage(hWndComboBox, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)l.c_str()); + } + // Send the CB_SETCURSEL message to display an initial item in the selection field + SendMessage(hWndComboBox, CB_SETCURSEL, (WPARAM)dist, (LPARAM)0); + return hr; + } + void RunMessageLoop() { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +private: + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { + LRESULT result = 0; + if (message == WM_CREATE) { + auto pcs = (LPCREATESTRUCT)lParam; + auto pDemoApp = (DemoApp *)pcs->lpCreateParams; + ::SetWindowLongPtr(hwnd, GWLP_USERDATA, PtrToUlong(pDemoApp)); + result = 1; + } else { + auto pDemoApp = reinterpret_cast(static_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA))); + bool wasHandled = false; + if (pDemoApp) { + switch (message) { + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE) { + // If the user makes a selection from the list: + // Send CB_GETCURSEL message to get the index of the selected list item. + // Send CB_GETLBTEXT message to get the item. + // Display the item in a messagebox. + int ItemIndex = SendMessage((HWND)lParam, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0); + TCHAR ListItem[256]{}; + SendMessage((HWND)lParam, (UINT)CB_GETLBTEXT, (WPARAM)ItemIndex, (LPARAM)ListItem); + std::wstring s = ListItem; + if (c.set_lang(s)) { + std::wstring msg = L"Language changed to " + s; + MessageBox(hwnd, msg.c_str(), TEXT("Language Changed"), MB_OK); + } + } + wasHandled = true; + result = 0; + break; + case WM_DISPLAYCHANGE: + InvalidateRect(hwnd, NULL, FALSE); + wasHandled = true; + result = 0; + break; + case WM_DESTROY: + PostQuitMessage(0); + wasHandled = true; + result = 1; + break; + } + } + if (!wasHandled) { + result = DefWindowProc(hwnd, message, wParam, lParam); + } + } + return result; + } + +private: + HWND m_hwnd{}; +}; + +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + if (SUCCEEDED(CoInitialize(NULL))) { + { + DemoApp app; + if (SUCCEEDED(app.Initialize())) { + app.RunMessageLoop(); + } + } + CoUninitialize(); + } + return 0; +} diff --git a/sw.cpp b/sw.cpp index 74ba105..7bf25a1 100644 --- a/sw.cpp +++ b/sw.cpp @@ -16,31 +16,31 @@ void build(Solution &s) common.Public += "pub.egorpugin.primitives.templates2"_dep; } - auto add_exe_base = [&](const String &name) -> decltype(auto) + auto add_exe_base = [&](const String &name, String dirname = {}) -> decltype(auto) { + if (dirname.empty()) + dirname = name; auto &t = tools.addExecutable(name); t.PackageDefinitions = true; t += cppstd; - t.setRootDirectory("src/" + name); + t.setRootDirectory("src/" + dirname); return t; }; - auto add_exe = [&](const String &name) -> decltype(auto) + auto add_exe = [&](const String &name, const String &dirname = {}) -> decltype(auto) { - auto &t = add_exe_base(name); + auto &t = add_exe_base(name, dirname); t.Public += "pub.egorpugin.primitives.sw.main"_dep; return t; }; - - auto add_exe_with_common = [&](const String &name) -> decltype(auto) + auto add_exe_with_common = [&](const String &name, const String &dirname = {}) -> decltype(auto) { - auto &t = add_exe(name); + auto &t = add_exe(name, dirname); t.Public += common; return t; }; - - auto add_exe_with_data_manager = [&](const String &name) -> decltype(auto) + auto add_exe_with_data_manager = [&](const String &name, const String &dirname = {}) -> decltype(auto) { - auto &t = add_exe_with_common(name); + auto &t = add_exe_with_common(name, dirname); t.Public += "pub.lzwdgc.Polygon4.DataManager-master"_dep; return t; }; @@ -80,13 +80,24 @@ void build(Solution &s) ; } - auto &aim1_mod_activator = add_exe_with_common("aim1_mod_activator"); + auto &language_switcher = tools.addExecutable("aim1.language_switcher"); + { + auto &t = language_switcher; + t += cppstd; + t += "src/aim1_language_switcher/.*"_rr; + t += "UNICODE"_def; + t += "gdi32.lib"_slib, "ole32.lib"_slib, "user32.lib"_slib; + if (auto L = t.getSelectedTool()->as(); L) + L->Subsystem = vs::Subsystem::Windows; + } + + auto &aim1_mod_activator = add_exe_with_common("aim1.mod_activator", "aim1_mod_activator"); aim1_mod_activator += "pub.egorpugin.primitives.pack"_dep; - auto &aim1_mod_maker = add_exe_with_common("aim1_mod_maker"); // actually a library + auto &aim1_mod_maker = add_exe_with_common("aim1.mod_maker", "aim1_mod_maker"); // actually a library aim1_mod_maker.Public += "pub.egorpugin.primitives.command"_dep; - auto &aim1_community_fix = tools.addExecutable("examples.mods.aim1_community_fix"); + auto &aim1_community_fix = tools.addExecutable("examples.mods.aim1.community_fix"); { auto &t = aim1_community_fix; t.PackageDefinitions = true;