diff --git a/SpaceCadetPinball/SpaceCadetPinball.rc b/SpaceCadetPinball/SpaceCadetPinball.rc index cf997a6..d1661eb 100644 Binary files a/SpaceCadetPinball/SpaceCadetPinball.rc and b/SpaceCadetPinball/SpaceCadetPinball.rc differ diff --git a/SpaceCadetPinball/control.cpp b/SpaceCadetPinball/control.cpp index 006ed11..ff6b56c 100644 --- a/SpaceCadetPinball/control.cpp +++ b/SpaceCadetPinball/control.cpp @@ -3737,7 +3737,7 @@ void control::SecretMissionYellowController(int code, TPinballComponent* caller) void control::SelectMissionController(int code, TPinballComponent* caller) { - char Buffer[64]; + char Buffer[128]; switch (code) { diff --git a/SpaceCadetPinball/gdrv.cpp b/SpaceCadetPinball/gdrv.cpp index d135d5d..1cd1ba2 100644 --- a/SpaceCadetPinball/gdrv.cpp +++ b/SpaceCadetPinball/gdrv.cpp @@ -3,6 +3,7 @@ #include "fullscrn.h" #include "memory.h" +#include "options.h" #include "pinball.h" #include "winmain.h" @@ -445,10 +446,24 @@ void gdrv::grtext_draw_ttext_in_box(LPCSTR text, int xOff, int yOff, int width, sscanf_s(fontColor, "%d %d %d", &grtext_red, &grtext_green, &grtext_blue); } + // DEFAULT_CHARSET in unicode build. + int charset; + switch (options::Options.Language) + { + default: + case Languages::English: + charset = ANSI_CHARSET; + break; + case Languages::Russian: + charset = RUSSIAN_CHARSET; + break; + } + // Default font does not scale well auto hNewFont = CreateFont(fontSize, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, - ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial"); - HFONT hOldFont = (HFONT)SelectObject(dc, hNewFont); + charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + DEFAULT_PITCH | FF_SWISS, "Arial"); + HFONT hOldFont = static_cast(SelectObject(dc, hNewFont)); int prevMode = SetBkMode(dc, TRANSPARENT); COLORREF color = SetTextColor(dc, grtext_red | grtext_green << 8 | grtext_blue << 16); diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index fc7550c..6122f79 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -1,4 +1,4 @@ -#include "pch.h" +#include "pch.h" #include "options.h" #include "fullscrn.h" @@ -68,9 +68,14 @@ short options::vk_list[28] -1 }; -void options::init(HMENU menuHandle) +LanguageMenuEntry options::LanguageMenu[] +{ + {L"English", Languages::English}, + {L"Русский", Languages::Russian}, +}; + +void options::ReadOptions() { - MenuHandle = menuHandle; Options.Sounds = 1; Options.Music = 0; Options.FullScreen = 0; @@ -110,6 +115,45 @@ void options::init(HMENU menuHandle) Options.UniformScaling = get_int(nullptr, "Uniform scaling", true); Options.AlternativeRender = get_int(nullptr, "Alternative Render", false); + auto defaultLanguage = Languages::English; + auto language = static_cast(get_int(nullptr, "Language", static_cast(defaultLanguage))); + bool languageDefined = false; + for (auto menuEntry : LanguageMenu) + { + if (menuEntry.Language == language) + { + languageDefined = true; + break; + } + } + if (!languageDefined) + language = defaultLanguage; + Options.Language = language; + + // Alternative approaches: resource DLLs, single-language builds. + // SetThreadUILanguage does not work properly on Windows XP. + bool winXp = false; + OSVERSIONINFO version{}; + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + +#pragma warning (disable : 4996) // XP has no versionhelpers.h + if (GetVersionEx(&version)) +#pragma warning (default : 4996) + { + if (version.dwMajorVersion < 6) + winXp = true; + } + + if (winXp) + SetThreadLocale(MAKELCID(static_cast(Options.Language), SORT_DEFAULT)); + else + SetThreadUILanguage(static_cast(Options.Language)); +} + +void options::init(HMENU menuHandle) +{ + MenuHandle = menuHandle; + menu_check(Menu1_Sounds, Options.Sounds); Sound::Enable(0, 7, Options.Sounds); menu_check(Menu1_Music, Options.Music); @@ -136,6 +180,21 @@ void options::init(HMENU menuHandle) } update_resolution_menu(); + + // Add language menu from code to decouple it from rc. + // AppendMenuW works with A window + auto hSubmenu = CreatePopupMenu(); + auto index = Menu1_Language; + for (auto menuEntry : LanguageMenu) + { + UINT flags = MF_STRING; + if (menuEntry.Language == Options.Language) + flags |= MF_CHECKED; + AppendMenuW(hSubmenu, flags, index++, menuEntry.Name); + } + + auto optionsMenu = GetSubMenu(MenuHandle, 1); + AppendMenu(optionsMenu, MF_STRING | MF_POPUP, reinterpret_cast(hSubmenu), "Language"); } void options::uninit() @@ -153,6 +212,7 @@ void options::uninit() set_int(nullptr, "Screen Resolution", Options.Resolution); set_int(nullptr, "Uniform scaling", Options.UniformScaling); set_int(nullptr, "Alternative Render", Options.AlternativeRender); + set_int(nullptr, "Language", static_cast(Options.Language)); } void options::path_init(LPCSTR regPath) @@ -356,6 +416,13 @@ void options::toggle(UINT uIDCheckItem) default: break; } + + if (uIDCheckItem >= Menu1_Language && uIDCheckItem < Menu1_LanguageMax) + { + auto languageId = uIDCheckItem - Menu1_Language; + Options.Language = LanguageMenu[languageId].Language; + winmain::Restart(); + } } void options::update_resolution_menu() diff --git a/SpaceCadetPinball/options.h b/SpaceCadetPinball/options.h index ec3fabe..37e78f4 100644 --- a/SpaceCadetPinball/options.h +++ b/SpaceCadetPinball/options.h @@ -1,5 +1,18 @@ #pragma once #include "pinball.h" +#include "resource.h" + +enum class Languages +{ + English = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + Russian = MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT), +}; + +struct LanguageMenuEntry +{ + LPCWSTR Name; + Languages Language; +}; struct optionsStruct { @@ -24,12 +37,14 @@ struct optionsStruct int Resolution; bool UniformScaling; bool AlternativeRender; + Languages Language; }; class options { public: + static void ReadOptions(); static void init(HMENU menuHandle); static void uninit(); static void path_init(LPCSTR regPath); @@ -57,4 +72,5 @@ private: static HMENU MenuHandle; static winhelp_entry keymap_help[18]; static short vk_list[28]; + static LanguageMenuEntry LanguageMenu[Menu1_LanguageMax - Menu1_Language]; }; diff --git a/SpaceCadetPinball/resource.h b/SpaceCadetPinball/resource.h index 837ee92..73894c2 100644 --- a/SpaceCadetPinball/resource.h +++ b/SpaceCadetPinball/resource.h @@ -241,6 +241,8 @@ #define DLG_HIGHSCORES_EditName4 604 #define DLG_HIGHSCORES_EditName5 605 #define Menu1_AlternativeRender 601 +#define Menu1_Language 700 +#define Menu1_LanguageMax 702 // Next default values for new objects // diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index a051dee..56a1ed7 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -47,6 +47,7 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi ++memory::critical_allocation; auto optionsRegPath = pinball::get_rc_string(165, 0); options::path_init(optionsRegPath); + options::ReadOptions(); auto regSpaceCadet = pinball::get_rc_string(166, 0); if (options::get_int(regSpaceCadet, "Table Version", 1) <= 1) @@ -223,10 +224,10 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi auto dt = static_cast(curTime - prevTime) * 0.001f; if (!options::Options.AlternativeRender) sprintf_s(buf, "Frames/sec = %02.02f", 300.0f / dt); - else + else { sprintf_s(buf, "Updates/sec = %02.02f Frames/sec = %02.02f", - 300.0f / dt, pb::frameCounter / dt); + 300.0f / dt, pb::frameCounter / dt); pb::frameCounter = 0; } @@ -665,6 +666,10 @@ LRESULT CALLBACK winmain::message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LP default: break; } + + if (wParam >= Menu1_Language && wParam < Menu1_LanguageMax) + options::toggle(wParamI); + return DefWindowProcA(hWnd, Msg, wParam, lParam); case WM_LBUTTONDOWN: if (pb::game_mode)