diff --git a/assets/svencoop/vgui2ext/resource/gameui_english.txt b/assets/svencoop/vgui2ext/resource/gameui_english.txt new file mode 100644 index 0000000..33f35f4 Binary files /dev/null and b/assets/svencoop/vgui2ext/resource/gameui_english.txt differ diff --git a/assets/svencoop/vgui2ext/resource/gameui_schinese.txt b/assets/svencoop/vgui2ext/resource/gameui_schinese.txt new file mode 100644 index 0000000..09d504c Binary files /dev/null and b/assets/svencoop/vgui2ext/resource/gameui_schinese.txt differ diff --git a/docs/en/features.md b/docs/en/features.md index fb96059..ecc5122 100644 --- a/docs/en/features.md +++ b/docs/en/features.md @@ -62,6 +62,13 @@ When enabled, the following resource directories are added with the `SKIN` tag: ## Condition Zero career profiles +The native missing/corrupt mission-data dialog uses +`VGUI2Extension_CareerDataErrorTitle` and `VGUI2Extension_CareerDataErrorText`. +Translations live in `/vgui2ext/resource/gameui_.txt` (UTF-16 LE +with a BOM). English is loaded first, then the current game language; English +and Simplified Chinese are included. Missing tokens retain the native English +message. This localization is limited to czero, excluding Deleted Scenes. + Windows x86 czero with the verified hl-10210 GameUI uses `u8hex-` followed by the lowercase hex of the complete UTF-8 player name for every new save, including ASCII names. Chinese names, `A/a`, and names sharing their first 31 bytes retain diff --git a/docs/zh-CN/features.md b/docs/zh-CN/features.md index 46671ca..aad6f03 100644 --- a/docs/zh-CN/features.md +++ b/docs/zh-CN/features.md @@ -55,6 +55,12 @@ HiDPI 支持为 VGUI2 控件应用 HDProportional 缩放,并通过文件系统 ## Condition Zero 战役档案 +原生任务数据缺失或损坏弹窗使用 `VGUI2Extension_CareerDataErrorTitle` 和 +`VGUI2Extension_CareerDataErrorText`。翻译文件位于 +`/vgui2ext/resource/gameui_.txt`,采用带 BOM 的 UTF-16 LE 编码。 +插件先加载英文,再加载当前游戏语言;随附英文与简体中文。缺失的键保留原生英文提示。 +此本地化仅用于 czero,不包含 Deleted Scenes。 + Windows x86 czero 配合已核验的 hl-10210 GameUI,所有新档案均使用 `u8hex-` 加完整 UTF-8 原名的小写十六进制,包括纯 ASCII 名字。中文名、`A/a`、前 31 字节相同的长名字保持独立身份; UI 与 CZP 内仍保留原名。其他构建和 Deleted Scenes 不启用此存储接入。 diff --git a/memory/build_and_verification.md b/memory/build_and_verification.md index cf66997..4f77c37 100644 --- a/memory/build_and_verification.md +++ b/memory/build_and_verification.md @@ -167,3 +167,13 @@ Chinese numeric-format follow-up: GetCurrentGameLanguage() values schinese/tchin - Fix: ConfirmOperation reuses the existing delete and overwrite title/text tokens, converts the complete identity.rawUtf8 with ConvertCareerPlayerNameToUnicode, and formats the delete body with ILocalize::ConstructString. Allocate from the format and complete Unicode name lengths; use readable English title/body fallbacks for missing tokens. Prepare display text before issuing the sidecar confirmation; preserve existing confirmation-token commands, cancel handling and verified deletion ownership. No language-file override or gamedata change. - Verification: MSVC x86 Release DLL build, all 8 existing CTests, pinned format-check (66 files) passed. Existing ClientVGUI.cpp C4018 warning remains. Windows czero engine 10210, schinese, 1280x720 live: delete title/body localized and displayed the complete Issue951_123456789012345678901234中文Z name beyond the native 31-byte limit. Delete cancel and overwrite cancel preserved the task profile SHA-256; overwrite title/body localized. ENTER confirmed deletion of the task profile, while pre-existing profile hashes were unchanged. - Scope/cleanup: game PID 67992 quit normally with exit code 0. Original 21 backed-up DLL/PDB/config/profile/list/appid files were restored and SHA-256 verified. Five task screenshots moved to build/issue951-runtime/screenshots (HalfLife47.tga deletion; HalfLife48.tga overwrite). English language, forced missing-token fallback, other builds and extreme-length layout were not tested in-game. This fixes localization, not the separate native difficulty-label ellipsis. + + +## 2026-10-25 — czero Career mission-data error localization (#952) + +- Trigger: Career mission data yields no map triplets, or native checksums fail; the native error dialog has no translation entry point. +- Root cause: hl-10210 GameUI `CCareerMapFrame::OnThink` constructs `vgui2::MessageBox` with the literals `Cannot start game` and `WARNING: Game data is corrupt or missing.`. This is separate from the plugin Career profile `ShowError` path and #951. Windows OnThink VA 0x10018350 calls constructor VA 0x1005B070; Linux OnThink 0x6c620 and restored `goldsrc-reverse/src/gameui/consolidated/GameUI/CareerMapFrame.cpp:497-513` corroborate the branch. Missing per-pack difficulty files may fall back to global `CareerGame.vdf`. +- Fix: reuse the existing gamedata-resolved narrow MessageBox constructor, install/uninstall a czero-only (excluding CZDS) hook, and replace only that exact title/body pair with independently checked plugin localization tokens. Load plugin English first and the effective game language second in GameUI Initialize; missing tokens retain the native literals. Ship UTF-16 LE BOM English and Simplified Chinese resources and document the translation entry points. No new gamedata record or native object layout is needed. +- Verification: Windows x86 Release build, 66-file format-check, all 8 existing CTests and staged install passed. Live czero build 10210 at 1280x720 used a task-only empty-Maps mission pack to exercise the same no-triplet branch without removing stock data: schinese title/body rendered correctly (HalfLife44.tga), English title/body rendered correctly (HalfLife46.tga), and a fresh schinese process with both plugin language files absent retained literal English without displaying keys (HalfLife48.tga). OK dismissed the modal back to profile selection. This validates the native error branch, not a direct deleted-Normal.vdf reproduction; checksum failure and other engine builds were not separately exercised. +- Cleanup: all task-owned game processes exited cleanly. Temporary fixture, profile and screenshots were moved into ignored `build/issue952-runtime/`; 21 preexisting game files were restored and SHA256-verified. Evidence screenshots remain under `build/issue952-runtime/screenshots/`. +- Scope: localization only; mission-data validation, fallback selection, modal behavior and storage are unchanged. Existing gamedata sync was disabled in the local cache; no claim is made about a newly downloaded online catalog. diff --git a/src/GameUI.cpp b/src/GameUI.cpp index 3a2926a..3469abf 100644 --- a/src/GameUI.cpp +++ b/src/GameUI.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,7 @@ static hook_t* g_phook_GameUI_ComboBox_AddItemW = NULL; static hook_t* g_phook_CBotButton_SetCharacter = NULL; static hook_t* g_phook_CCareerDifficultyBox_ctor = NULL; static hook_t* g_phook_CCareerBaseFrame_Activate = NULL; +static hook_t* g_phook_MessageBox_ctor = NULL; static ICareerUI* g_pCareerUI = nullptr; @@ -1513,6 +1515,17 @@ void CGameUIProxy::Initialize(CreateInterfaceFn* factories, int count) return; } + if (g_bIsCZero && !g_bIsCZDS) + { + vgui::localize()->AddFile(g_pFullFileSystem, "vgui2ext/resource/gameui_english.txt"); + const auto language = GetCurrentGameLanguage(); + if (language[0] && strcmp(language, "english")) + { + const auto path = std::string("vgui2ext/resource/gameui_") + language + ".txt"; + vgui::localize()->AddFile(g_pFullFileSystem, path.c_str()); + } + } + VGUI2ExtensionInternal()->GameUI_Initialize(factories, count); } @@ -2531,6 +2544,21 @@ void GameUI_FillAddress_PropertySheet(const mh_dll_info_t&, const mh_dll_info_t& gPrivateFuncs.vfunc_index_PropertySheet_ChangeActiveTab = GamedataResolveVFuncIndex(g_GameUIDllInfo.ImageBase, "gameui", "vgui2::PropertySheet::ChangeActiveTab(int)"); } +void* __fastcall MessageBox_ctor(void* pthis, int dummy, const char* title, const char* text, void* parent) +{ + //Only the native Career data-error dialog uses this exact title/text pair. + //Keep unrelated dialogs and missing translations on the original path. + auto* localization = vgui::localize(); + if (localization && title && text && !strcmp(title, "Cannot start game") && !strcmp(text, "WARNING: Game data is corrupt or missing.")) + { + if (localization->Find("#VGUI2Extension_CareerDataErrorTitle")) + title = "#VGUI2Extension_CareerDataErrorTitle"; + if (localization->Find("#VGUI2Extension_CareerDataErrorText")) + text = "#VGUI2Extension_CareerDataErrorText"; + } + return gPrivateFuncs.MessageBox_ctor(pthis, dummy, title, text, parent); +} + void GameUI_FillAddress_MessageBox(const mh_dll_info_t&, const mh_dll_info_t&) { gPrivateFuncs.MessageBox_ctor = (decltype(gPrivateFuncs.MessageBox_ctor)) @@ -2898,6 +2926,12 @@ void GameUI_InstallHooks(void) } Install_InlineHook(GameUI_Panel_Init); + if (g_bIsCZero && !g_bIsCZDS) + { + Install_InlineHook(MessageBox_ctor); + if (!g_phook_MessageBox_ctor) + Sys_Error("Could not install GameUI Career data-error localization hook."); + } Install_InlineHook(CGameConsoleDialog_ctor); Install_InlineHook(CCreateMultiplayerGameDialog_ctor); @@ -3034,6 +3068,7 @@ void GameUI_UninstallHooks(void) CareerStorage_UninstallHooks(); VGUI2ExtensionInternal()->UnregisterKeyValuesCallbacks(&s_ContentControlKeyValuesCallbacks); + Uninstall_Hook(MessageBox_ctor); Uninstall_Hook(MissionPackMenu_PerformLayout); Uninstall_Hook(GameUI_ComboBox_AddItemW); Uninstall_Hook(CBotButton_SetCharacter);