mirror of
https://github.com/aimrebirth/tools.git
synced 2026-04-14 17:33:25 +00:00
Initial language switcher.
This commit is contained in:
parent
f443a5df8f
commit
11369f5778
2 changed files with 258 additions and 13 deletions
234
src/aim1_language_switcher/language_switcher.cpp
Normal file
234
src/aim1_language_switcher/language_switcher.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <set>
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
fs::path datadir = fs::current_path();
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <CommCtrl.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
|
||||||
|
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<UINT>(ceil(width * dpiX / 96.f))
|
||||||
|
, static_cast<UINT>(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<std::wstring> 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<DemoApp *>(static_cast<LONG_PTR>(::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;
|
||||||
|
}
|
||||||
37
sw.cpp
37
sw.cpp
|
|
@ -16,31 +16,31 @@ void build(Solution &s)
|
||||||
common.Public += "pub.egorpugin.primitives.templates2"_dep;
|
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);
|
auto &t = tools.addExecutable(name);
|
||||||
t.PackageDefinitions = true;
|
t.PackageDefinitions = true;
|
||||||
t += cppstd;
|
t += cppstd;
|
||||||
t.setRootDirectory("src/" + name);
|
t.setRootDirectory("src/" + dirname);
|
||||||
return t;
|
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;
|
t.Public += "pub.egorpugin.primitives.sw.main"_dep;
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
auto add_exe_with_common = [&](const String &name, const String &dirname = {}) -> decltype(auto)
|
||||||
auto add_exe_with_common = [&](const String &name) -> decltype(auto)
|
|
||||||
{
|
{
|
||||||
auto &t = add_exe(name);
|
auto &t = add_exe(name, dirname);
|
||||||
t.Public += common;
|
t.Public += common;
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
auto add_exe_with_data_manager = [&](const String &name, const String &dirname = {}) -> decltype(auto)
|
||||||
auto add_exe_with_data_manager = [&](const String &name) -> 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;
|
t.Public += "pub.lzwdgc.Polygon4.DataManager-master"_dep;
|
||||||
return t;
|
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<VisualStudioLinker*>(); 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;
|
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;
|
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;
|
auto &t = aim1_community_fix;
|
||||||
t.PackageDefinitions = true;
|
t.PackageDefinitions = true;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue