Fix typos
[TortoiseGit.git] / src / ResText / ResModule.cpp
blobc505e816ff6a50721616b18e19d88db3275fed09
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2015-2022 - TortoiseGit
4 // Copyright (C) 2003-2008, 2010-2017, 2019 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "scope_exit_noexcept.h"
21 #include "Utils.h"
22 #include "UnicodeUtils.h"
23 #include "ResModule.h"
24 #include <regex>
25 #include <memory>
26 #include <fstream>
27 #include <string>
28 #include <algorithm>
29 #include <functional>
30 #include <locale>
31 #include <codecvt>
33 #include <Imagehlp.h>
35 #pragma comment(lib, "Imagehlp.lib")
37 #ifndef RT_RIBBON
38 #define RT_RIBBON MAKEINTRESOURCE(28)
39 #endif
42 #define MYERROR {CUtils::Error(); return FALSE;}
44 static const WORD * AlignWORD(const WORD * pWord)
46 const WORD * res = pWord;
47 res += (((reinterpret_cast<UINT_PTR>(pWord) + 3) & ~3) - reinterpret_cast<UINT_PTR>(pWord)) / sizeof(WORD);
48 return res;
51 std::wstring NumToStr(INT_PTR num)
53 wchar_t buf[100];
54 swprintf_s(buf, L"%Id", num);
55 return buf;
58 CResModule::CResModule()
59 : m_bTranslatedStrings(0)
60 , m_bDefaultStrings(0)
61 , m_bTranslatedDialogStrings(0)
62 , m_bDefaultDialogStrings(0)
63 , m_bTranslatedMenuStrings(0)
64 , m_bDefaultMenuStrings(0)
65 , m_bTranslatedAcceleratorStrings(0)
66 , m_bDefaultAcceleratorStrings(0)
67 , m_bTranslatedRibbonTexts(0)
68 , m_bDefaultRibbonTexts(0)
69 , m_wTargetLang(0)
70 , m_hResDll(nullptr)
71 , m_hUpdateRes(nullptr)
72 , m_bQuiet(false)
73 , m_bRTL(false)
74 , m_bAdjustEOLs(false)
78 CResModule::~CResModule()
82 BOOL CResModule::ExtractResources(const std::vector<std::wstring>& filelist, LPCWSTR lpszPOFilePath, BOOL bNoUpdate, LPCWSTR lpszHeaderFile)
84 for (auto I = filelist.cbegin(); I != filelist.cend(); ++I)
86 std::wstring filepath = *I;
87 m_currentHeaderDataDialogs.clear();
88 m_currentHeaderDataMenus.clear();
89 m_currentHeaderDataStrings.clear();
90 auto starpos = I->find('*');
91 if (starpos != std::wstring::npos)
92 filepath = I->substr(0, starpos);
93 while (starpos != std::wstring::npos)
95 auto starposnext = I->find('*', starpos + 1);
96 std::wstring headerfile = I->substr(starpos + 1, starposnext - starpos - 1);
97 ScanHeaderFile(headerfile);
98 starpos = starposnext;
100 m_hResDll = LoadLibraryEx(filepath.c_str(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
101 if (!m_hResDll)
102 MYERROR;
103 SCOPE_EXIT
105 if (m_hResDll)
106 FreeLibrary(m_hResDll);
109 size_t nEntries = m_StringEntries.size();
110 // fill in the std::map with all translatable entries
112 if (!m_bQuiet)
113 fwprintf(stdout, L"Extracting StringTable....");
114 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
115 if (!m_bQuiet)
116 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size() - nEntries);
117 nEntries = m_StringEntries.size();
119 if (!m_bQuiet)
120 fwprintf(stdout, L"Extracting Dialogs........");
121 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
122 if (!m_bQuiet)
123 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size() - nEntries);
124 nEntries = m_StringEntries.size();
126 if (!m_bQuiet)
127 fwprintf(stdout, L"Extracting Menus..........");
128 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
129 if (!m_bQuiet)
130 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size() - nEntries);
131 nEntries = m_StringEntries.size();
132 if (!m_bQuiet)
133 fwprintf(stdout, L"Extracting Accelerators...");
134 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
135 if (!m_bQuiet)
136 fwprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size() - nEntries);
137 nEntries = m_StringEntries.size();
138 if (!m_bQuiet)
139 fwprintf(stdout, L"Extracting Ribbons........");
140 EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
141 if (!m_bQuiet)
142 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size() - nEntries);
143 nEntries = m_StringEntries.size();
145 // parse a probably existing file and update the translations which are
146 // already done
147 m_StringEntries.ParseFile(lpszPOFilePath, !bNoUpdate, m_bAdjustEOLs);
150 // at last, save the new file
151 return m_StringEntries.SaveFile(lpszPOFilePath, lpszHeaderFile);
154 BOOL CResModule::ExtractResources(LPCWSTR lpszSrcLangDllPath, LPCWSTR lpszPoFilePath, BOOL bNoUpdate, LPCWSTR lpszHeaderFile)
156 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
157 if (!m_hResDll)
158 MYERROR;
159 SCOPE_EXIT
161 if (m_hResDll)
162 FreeLibrary(m_hResDll);
165 size_t nEntries = 0;
166 // fill in the std::map with all translatable entries
168 if (!m_bQuiet)
169 fwprintf(stdout, L"Extracting StringTable....");
170 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
171 if (!m_bQuiet)
172 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size());
173 nEntries = m_StringEntries.size();
175 if (!m_bQuiet)
176 fwprintf(stdout, L"Extracting Dialogs........");
177 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
178 if (!m_bQuiet)
179 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size() - nEntries);
180 nEntries = m_StringEntries.size();
182 if (!m_bQuiet)
183 fwprintf(stdout, L"Extracting Menus..........");
184 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
185 if (!m_bQuiet)
186 fwprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
187 nEntries = m_StringEntries.size();
189 if (!m_bQuiet)
190 fwprintf(stdout, L"Extracting Accelerators...");
191 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, reinterpret_cast<LONG_PTR>(this));
192 if (!m_bQuiet)
193 fwprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size()-nEntries);
194 nEntries = m_StringEntries.size();
196 // parse a probably existing file and update the translations which are
197 // already done
198 m_StringEntries.ParseFile(lpszPoFilePath, !bNoUpdate, m_bAdjustEOLs);
200 // at last, save the new file
201 if (!m_StringEntries.SaveFile(lpszPoFilePath, lpszHeaderFile))
202 return FALSE;
204 return TRUE;
207 void CResModule::RemoveSignatures(LPCWSTR lpszDestLangDllPath)
209 // Remove any signatures in the file:
210 // if we don't remove it here, the signature will be invalid after
211 // we modify this file, and the signtool.exe will throw an error and refuse to sign it again.
212 auto hFile = CreateFile(lpszDestLangDllPath, FILE_READ_DATA | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
213 if (hFile == INVALID_HANDLE_VALUE)
215 CUtils::Error();
216 return;
219 DWORD certcount = 0;
220 DWORD indices[100];
221 ImageEnumerateCertificates(hFile, CERT_SECTION_TYPE_ANY, &certcount, indices, _countof(indices));
223 for (DWORD i = 0; i < certcount; ++i)
224 ImageRemoveCertificate(hFile, i);
226 CloseHandle(hFile);
229 BOOL CResModule::CreateTranslatedResources(LPCWSTR lpszSrcLangDllPath, LPCWSTR lpszDestLangDllPath, LPCWSTR lpszPOFilePath)
231 if (!CopyFile(lpszSrcLangDllPath, lpszDestLangDllPath, FALSE))
232 MYERROR;
234 RemoveSignatures(lpszDestLangDllPath);
236 int count = 0;
239 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_IGNORE_CODE_AUTHZ_LEVEL);
240 if (!m_hResDll)
241 Sleep(100);
242 count++;
243 } while (!m_hResDll && (count < 10));
245 if (!m_hResDll)
246 MYERROR;
247 SCOPE_EXIT
249 if (m_hResDll)
250 FreeLibrary(m_hResDll);
253 sDestFile = std::wstring(lpszDestLangDllPath);
255 // get all translated strings
256 if (!m_StringEntries.ParseFile(lpszPOFilePath, FALSE, m_bAdjustEOLs))
257 MYERROR;
258 m_bTranslatedStrings = 0;
259 m_bDefaultStrings = 0;
260 m_bTranslatedDialogStrings = 0;
261 m_bDefaultDialogStrings = 0;
262 m_bTranslatedMenuStrings = 0;
263 m_bDefaultMenuStrings = 0;
264 m_bTranslatedAcceleratorStrings = 0;
265 m_bDefaultAcceleratorStrings = 0;
267 count = 0;
270 m_hUpdateRes = BeginUpdateResource(sDestFile.c_str(), FALSE);
271 if (!m_hUpdateRes)
272 Sleep(100);
273 count++;
274 } while (!m_hUpdateRes && (count < 10));
276 if (!m_hUpdateRes)
277 MYERROR;
279 if (!m_bQuiet)
280 fwprintf(stdout, L"Translating StringTable...");
281 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameWriteCallback, reinterpret_cast<LONG_PTR>(this));
282 if (!m_bQuiet)
283 fwprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedStrings, m_bDefaultStrings);
285 if (!m_bQuiet)
286 fwprintf(stdout, L"Translating Dialogs.......");
287 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameWriteCallback, reinterpret_cast<LONG_PTR>(this));
288 if (!m_bQuiet)
289 fwprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedDialogStrings, m_bDefaultDialogStrings);
291 if (!m_bQuiet)
292 fwprintf(stdout, L"Translating Menus.........");
293 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameWriteCallback, reinterpret_cast<LONG_PTR>(this));
294 if (!m_bQuiet)
295 fwprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedMenuStrings, m_bDefaultMenuStrings);
297 if (!m_bQuiet)
298 fwprintf(stdout, L"Translating Accelerators..");
299 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameWriteCallback, reinterpret_cast<LONG_PTR>(this));
300 if (!m_bQuiet)
301 fwprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedAcceleratorStrings, m_bDefaultAcceleratorStrings);
303 if (!m_bQuiet)
304 fwprintf(stdout, L"Translating Ribbons.......");
305 EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameWriteCallback, reinterpret_cast<LONG_PTR>(this));
306 if (!m_bQuiet)
307 fwprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedRibbonTexts, m_bDefaultRibbonTexts);
308 BOOL bRes = TRUE;
309 if (!EndUpdateResource(m_hUpdateRes, !bRes))
310 MYERROR;
312 AdjustCheckSum(sDestFile);
314 return TRUE;
317 BOOL CResModule::ExtractString(LPCWSTR lpszType)
319 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_STRING);
320 HGLOBAL hglStringTable;
321 LPWSTR p;
323 if (!hrsrc)
324 MYERROR;
325 hglStringTable = LoadResource(m_hResDll, hrsrc);
327 if (!hglStringTable)
328 MYERROR;
329 SCOPE_EXIT { FreeResource(hglStringTable); };
330 p = static_cast<LPWSTR>(LockResource(hglStringTable));
332 if (!p)
333 MYERROR;
334 SCOPE_EXIT { UnlockResource(hglStringTable); };
335 /* [Block of 16 strings. The strings are Pascal style with a WORD
336 length preceding the string. 16 strings are always written, even
337 if not all slots are full. Any slots in the block with no string
338 have a zero WORD for the length.]
341 //first check how much memory we need
342 LPWSTR pp = p;
343 for (int i=0; i<16; ++i)
345 int len = GET_WORD(pp);
346 pp++;
347 std::wstring msgid = std::wstring(pp, len);
348 wchar_t buf[MAX_STRING_LENGTH * 2] = { 0 };
349 wcscpy_s(buf, msgid.c_str());
350 CUtils::StringExtend(buf);
352 if (buf[0])
354 std::wstring str = std::wstring(buf);
355 RESOURCEENTRY entry = m_StringEntries[str];
356 InsertResourceIDs(RT_STRING, 0, entry, (reinterpret_cast<INT_PTR>(lpszType) - 1) * 16 + i, L"");
357 if (wcschr(str.c_str(), '%'))
358 entry.flag = L"#, c-format";
359 m_StringEntries[str] = entry;
361 pp += len;
363 return TRUE;
366 BOOL CResModule::ReplaceString(LPCWSTR lpszType, WORD wLanguage)
368 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_STRING, lpszType, wLanguage);
369 HGLOBAL hglStringTable;
370 LPWSTR p;
372 if (!hrsrc)
373 MYERROR;
374 hglStringTable = LoadResource(m_hResDll, hrsrc);
376 if (!hglStringTable)
377 MYERROR;
378 SCOPE_EXIT { FreeResource(hglStringTable); };
379 p = static_cast<LPWSTR>(LockResource(hglStringTable));
381 if (!p)
382 MYERROR;
383 SCOPE_EXIT { UnlockResource(hglStringTable); };
384 /* [Block of 16 strings. The strings are Pascal style with a WORD
385 length preceding the string. 16 strings are always written, even
386 if not all slots are full. Any slots in the block with no string
387 have a zero WORD for the length.]
390 //first check how much memory we need
391 size_t nMem = 0;
392 LPWSTR pp = p;
393 for (int i=0; i<16; ++i)
395 nMem++;
396 size_t len = GET_WORD(pp);
397 pp++;
398 std::wstring msgid = std::wstring(pp, len);
399 wchar_t buf[MAX_STRING_LENGTH * 2] = { 0 };
400 wcscpy_s(buf, msgid.c_str());
401 CUtils::StringExtend(buf);
402 msgid = std::wstring(buf);
404 RESOURCEENTRY resEntry;
405 resEntry = m_StringEntries[msgid];
406 wcscpy_s(buf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
407 ReplaceWithRegex(buf);
408 CUtils::StringCollapse(buf);
409 size_t newlen = wcslen(buf);
410 if (newlen)
411 nMem += newlen;
412 else
413 nMem += len;
414 pp += len;
417 auto newTable = std::make_unique<WORD[]>(nMem + (nMem % 2));
419 size_t index = 0;
420 for (int i=0; i<16; ++i)
422 int len = GET_WORD(p);
423 p++;
424 std::wstring msgid = std::wstring(p, len);
425 wchar_t buf[MAX_STRING_LENGTH * 2] = { 0 };
426 wcscpy_s(buf, msgid.c_str());
427 CUtils::StringExtend(buf);
428 msgid = std::wstring(buf);
430 RESOURCEENTRY resEntry;
431 resEntry = m_StringEntries[msgid];
432 wcscpy_s(buf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
433 ReplaceWithRegex(buf);
434 CUtils::StringCollapse(buf);
435 size_t newlen = wcslen(buf);
436 if (newlen)
438 newTable.get()[index++] = static_cast<WORD>(newlen);
439 wcsncpy(reinterpret_cast<wchar_t*>(&newTable.get()[index]), buf, newlen);
440 index += newlen;
441 m_bTranslatedStrings++;
443 else
445 newTable.get()[index++] = static_cast<WORD>(len);
446 if (len)
447 wcsncpy(reinterpret_cast<wchar_t*>(&newTable.get()[index]), p, len);
448 index += len;
449 if (len)
450 m_bDefaultStrings++;
452 p += len;
455 if (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newTable.get(), static_cast<DWORD>(nMem + (nMem % 2)) * sizeof(WORD)))
456 MYERROR;
458 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, wLanguage, nullptr, 0)))
459 MYERROR;
461 return TRUE;
464 BOOL CResModule::ExtractMenu(LPCWSTR lpszType)
466 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_MENU);
467 HGLOBAL hglMenuTemplate;
468 WORD version, offset;
469 const WORD *p, *p0;
471 if (!hrsrc)
472 MYERROR;
474 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
476 if (!hglMenuTemplate)
477 MYERROR;
478 SCOPE_EXIT { FreeResource(hglMenuTemplate); };
480 p = static_cast<const WORD*>(LockResource(hglMenuTemplate));
482 if (!p)
483 MYERROR;
484 SCOPE_EXIT { UnlockResource(hglMenuTemplate); };
485 // Standard MENU resource
486 //struct MenuHeader {
487 // WORD wVersion; // Currently zero
488 // WORD cbHeaderSize; // Also zero
489 //};
491 // MENUEX resource
492 //struct MenuExHeader {
493 // WORD wVersion; // One
494 // WORD wOffset;
495 // DWORD dwHelpId;
496 //};
497 p0 = p;
498 version = GET_WORD(p);
500 p++;
502 switch (version)
504 case 0:
506 offset = GET_WORD(p);
507 p += offset;
508 p++;
509 if (!ParseMenuResource(p))
510 MYERROR;
512 break;
513 case 1:
515 offset = GET_WORD(p);
516 p++;
517 //dwHelpId = GET_DWORD(p);
518 if (!ParseMenuExResource(p0 + offset))
519 MYERROR;
521 break;
522 default:
523 MYERROR;
526 return TRUE;
529 BOOL CResModule::ReplaceMenu(LPCWSTR lpszType, WORD wLanguage)
531 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_MENU, lpszType, wLanguage);
532 HGLOBAL hglMenuTemplate;
533 WORD version, offset;
534 LPWSTR p;
535 WORD *p0;
537 if (!hrsrc)
538 MYERROR; //just the language wasn't found
540 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
542 if (!hglMenuTemplate)
543 MYERROR;
544 SCOPE_EXIT { FreeResource(hglMenuTemplate); };
546 p = static_cast<LPWSTR>(LockResource(hglMenuTemplate));
548 if (!p)
549 MYERROR;
550 SCOPE_EXIT { UnlockResource(hglMenuTemplate); };
551 //struct MenuHeader {
552 // WORD wVersion; // Currently zero
553 // WORD cbHeaderSize; // Also zero
554 //};
556 // MENUEX resource
557 //struct MenuExHeader {
558 // WORD wVersion; // One
559 // WORD wOffset;
560 // DWORD dwHelpId;
561 //};
562 p0 = reinterpret_cast<WORD*>(p);
563 version = GET_WORD(p);
565 p++;
567 switch (version)
569 case 0:
571 offset = GET_WORD(p);
572 p += offset;
573 p++;
574 size_t nMem = 0;
575 if (!CountMemReplaceMenuResource(reinterpret_cast<WORD*>(p), &nMem, nullptr))
576 MYERROR;
577 auto newMenu = std::make_unique<WORD[]>(nMem + (nMem % 2) + 2);
578 size_t index = 2; // MenuHeader has 2 WORDs zero
579 if (!CountMemReplaceMenuResource(reinterpret_cast<WORD*>(p), &index, newMenu.get()))
580 MYERROR;
582 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu.get(), static_cast<DWORD>(nMem + (nMem % 2) + 2) * sizeof(WORD)))
583 MYERROR;
585 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
586 MYERROR;
588 break;
589 case 1:
591 offset = GET_WORD(p);
592 p++;
593 //dwHelpId = GET_DWORD(p);
594 size_t nMem = 0;
595 if (!CountMemReplaceMenuExResource(reinterpret_cast<WORD*>(p0 + offset), &nMem, nullptr))
596 MYERROR;
597 auto newMenu = std::make_unique<WORD[]>(nMem + (nMem % 2) + 4);
598 CopyMemory(newMenu.get(), p0, 2 * sizeof(WORD) + sizeof(DWORD));
599 size_t index = 4; // MenuExHeader has 2 x WORD + 1 x DWORD
600 if (!CountMemReplaceMenuExResource(reinterpret_cast<WORD*>(p0 + offset), &index, newMenu.get()))
601 MYERROR;
603 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu.get(), static_cast<DWORD>(nMem + (nMem % 2) + 4) * sizeof(DWORD)))
604 MYERROR;
606 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
607 MYERROR;
609 break;
610 default:
611 MYERROR;
614 return TRUE;
617 const WORD* CResModule::ParseMenuResource(const WORD * res)
619 WORD flags;
620 WORD id = 0;
622 //struct PopupMenuItem {
623 // WORD fItemFlags;
624 // WCHAR szItemText[];
625 //};
626 //struct NormalMenuItem {
627 // WORD fItemFlags;
628 // WORD wMenuID;
629 // WCHAR szItemText[];
630 //};
634 flags = GET_WORD(res);
635 res++;
636 if (!(flags & MF_POPUP))
638 id = GET_WORD(res); //normal menu item
639 res++;
641 else
642 id = static_cast<WORD>(-1); //popup menu item
644 auto str = reinterpret_cast<LPCWSTR>(res);
645 size_t l = wcslen(str)+1;
646 res += l;
648 if (flags & MF_POPUP)
650 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
651 wcscpy_s(buf, str);
652 CUtils::StringExtend(buf);
654 std::wstring wstr = std::wstring(buf);
655 RESOURCEENTRY entry = m_StringEntries[wstr];
656 if (id)
657 InsertResourceIDs(RT_MENU, 0, entry, id, L" - PopupMenu");
659 m_StringEntries[wstr] = entry;
661 if ((res = ParseMenuResource(res))==0)
662 return nullptr;
664 else if (id != 0)
666 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
667 wcscpy_s(buf, str);
668 CUtils::StringExtend(buf);
670 std::wstring wstr = std::wstring(buf);
671 RESOURCEENTRY entry = m_StringEntries[wstr];
672 InsertResourceIDs(RT_MENU, 0, entry, id, L" - Menu");
674 wchar_t szTempBuf[1024] = { 0 };
675 swprintf_s(szTempBuf, L"#: MenuEntry; ID:%u", id);
676 MENUENTRY menu_entry;
677 menu_entry.wID = id;
678 menu_entry.reference = szTempBuf;
679 menu_entry.msgstr = wstr;
681 m_StringEntries[wstr] = entry;
682 m_MenuEntries[id] = menu_entry;
684 } while (!(flags & MF_END));
685 return res;
688 const WORD* CResModule::CountMemReplaceMenuResource(const WORD * res, size_t * wordcount, WORD * newMenu)
690 WORD flags;
691 WORD id = 0;
693 //struct PopupMenuItem {
694 // WORD fItemFlags;
695 // WCHAR szItemText[];
696 //};
697 //struct NormalMenuItem {
698 // WORD fItemFlags;
699 // WORD wMenuID;
700 // WCHAR szItemText[];
701 //};
705 flags = GET_WORD(res);
706 res++;
707 if (!newMenu)
708 (*wordcount)++;
709 else
710 newMenu[(*wordcount)++] = flags;
711 if (!(flags & MF_POPUP))
713 id = GET_WORD(res); //normal menu item
714 res++;
715 if (!newMenu)
716 (*wordcount)++;
717 else
718 newMenu[(*wordcount)++] = id;
720 else
721 id = static_cast<WORD>(-1); //popup menu item
723 if (flags & MF_POPUP)
725 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
726 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
728 if ((res = CountMemReplaceMenuResource(res, wordcount, newMenu))==0)
729 return nullptr;
731 else if (id != 0)
733 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
734 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
736 else
738 if (newMenu)
739 wcscpy(reinterpret_cast<wchar_t*>(&newMenu[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
740 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
741 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
743 } while (!(flags & MF_END));
744 return res;
747 const WORD* CResModule::ParseMenuExResource(const WORD * res)
749 WORD bResInfo;
751 //struct MenuExItem {
752 // DWORD dwType;
753 // DWORD dwState;
754 // DWORD menuId;
755 // WORD bResInfo;
756 // WCHAR szText[];
757 // DWORD dwHelpId; - Popup menu only
758 //};
762 DWORD dwType = GET_DWORD(res);
763 res += 2;
764 //dwState = GET_DWORD(res);
765 res += 2;
766 DWORD menuId = GET_DWORD(res);
767 res += 2;
768 bResInfo = GET_WORD(res);
769 res++;
771 auto str = reinterpret_cast<LPCWSTR>(res);
772 size_t l = wcslen(str)+1;
773 res += l;
774 // Align to DWORD boundary
775 res = AlignWORD(res);
777 if (dwType & MFT_SEPARATOR)
778 continue;
780 if (bResInfo & 0x01)
782 // Popup menu - note this can also have a non-zero ID
783 if (menuId == 0)
784 menuId = static_cast<WORD>(-1);
785 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
786 wcscpy_s(buf, str);
787 CUtils::StringExtend(buf);
789 std::wstring wstr = std::wstring(buf);
790 RESOURCEENTRY entry = m_StringEntries[wstr];
791 // Popup has a DWORD help entry on a DWORD boundary - skip over it
792 res += 2;
794 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - PopupMenuEx");
795 wchar_t szTempBuf[1024] = { 0 };
796 swprintf_s(szTempBuf, L"#: MenuExPopupEntry; ID:%lu", menuId);
797 MENUENTRY menu_entry;
798 menu_entry.wID = static_cast<WORD>(menuId);
799 menu_entry.reference = szTempBuf;
800 menu_entry.msgstr = wstr;
801 m_StringEntries[wstr] = entry;
802 m_MenuEntries[static_cast<WORD>(menuId)] = menu_entry;
804 if ((res = ParseMenuExResource(res)) == 0)
805 return nullptr;
806 } else if (menuId != 0)
808 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
809 wcscpy_s(buf, str);
810 CUtils::StringExtend(buf);
812 std::wstring wstr = std::wstring(buf);
813 RESOURCEENTRY entry = m_StringEntries[wstr];
814 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - MenuEx");
816 wchar_t szTempBuf[1024] = { 0 };
817 swprintf_s(szTempBuf, L"#: MenuExEntry; ID:%lu", menuId);
818 MENUENTRY menu_entry;
819 menu_entry.wID = static_cast<WORD>(menuId);
820 menu_entry.reference = szTempBuf;
821 menu_entry.msgstr = wstr;
822 m_StringEntries[wstr] = entry;
823 m_MenuEntries[static_cast<WORD>(menuId)] = menu_entry;
825 } while (!(bResInfo & 0x80));
826 return res;
829 const WORD* CResModule::CountMemReplaceMenuExResource(const WORD * res, size_t * wordcount, WORD * newMenu)
831 WORD bResInfo;
833 //struct MenuExItem {
834 // DWORD dwType;
835 // DWORD dwState;
836 // DWORD menuId;
837 // WORD bResInfo;
838 // WCHAR szText[];
839 // DWORD dwHelpId; - Popup menu only
840 //};
844 auto p0 = const_cast<WORD*>(res);
845 DWORD dwType = GET_DWORD(res);
846 res += 2;
847 //dwState = GET_DWORD(res);
848 res += 2;
849 DWORD menuId = GET_DWORD(res);
850 res += 2;
851 bResInfo = GET_WORD(res);
852 res++;
854 if (newMenu)
855 CopyMemory(&newMenu[*wordcount], p0, 7 * sizeof(WORD));
857 (*wordcount) += 7;
859 if (dwType & MFT_SEPARATOR) {
860 // Align to DWORD
861 (*wordcount)++;
862 res++;
863 continue;
866 if (bResInfo & 0x01)
868 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
869 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
870 // Align to DWORD
871 res = AlignWORD(res);
872 if ((*wordcount) & 0x01)
873 (*wordcount)++;
875 if (newMenu)
876 CopyMemory(&newMenu[*wordcount], res, sizeof(DWORD)); // Copy Help ID
878 res += 2;
879 (*wordcount) += 2;
881 if ((res = CountMemReplaceMenuExResource(res, wordcount, newMenu)) == 0)
882 return nullptr;
884 else if (menuId != 0)
886 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
887 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
889 else
891 if (newMenu)
892 wcscpy(reinterpret_cast<wchar_t*>(&newMenu[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
893 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
894 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
896 // Align to DWORD
897 res = AlignWORD(res);
898 if ((*wordcount) & 0x01)
899 (*wordcount)++;
900 } while (!(bResInfo & 0x80));
901 return res;
904 BOOL CResModule::ExtractAccelerator(LPCWSTR lpszType)
906 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_ACCELERATOR);
907 HGLOBAL hglAccTable;
908 WORD fFlags, wAnsi, wID;
909 const WORD* p;
910 bool bEnd(false);
912 if (!hrsrc)
913 MYERROR;
915 hglAccTable = LoadResource(m_hResDll, hrsrc);
917 if (!hglAccTable)
918 MYERROR;
919 SCOPE_EXIT { FreeResource(hglAccTable); };
921 p = static_cast<const WORD*>(LockResource(hglAccTable));
923 if (!p)
924 MYERROR;
925 SCOPE_EXIT { UnlockResource(hglAccTable); };
927 struct ACCELTABLEENTRY
929 WORD fFlags; FVIRTKEY, FSHIFT, FCONTROL, FALT, 0x80 - Last in a table
930 WORD wAnsi; ANSI character
931 WORD wId; Keyboard accelerator passed to windows
932 WORD padding; # bytes added to ensure aligned to DWORD boundary
938 fFlags = GET_WORD(p);
939 p++;
940 wAnsi = GET_WORD(p);
941 p++;
942 wID = GET_WORD(p);
943 p++;
944 p++; // Skip over padding
946 if ((fFlags & 0x80) == 0x80)
947 { // 0x80
948 bEnd = true;
951 if ((wAnsi < 0x30) ||
952 (wAnsi > 0x5A) ||
953 (wAnsi >= 0x3A && wAnsi <= 0x40))
954 continue;
956 wchar_t buf[1024] = { 0 };
957 wchar_t buf2[1024] = { 0 };
959 // include the menu ID in the msgid to make sure that 'duplicate'
960 // accelerator keys are listed in the po-file.
961 // without this, we would get entries like this:
962 //#. Accelerator Entry for Menu ID:32809; '&Filter'
963 //#. Accelerator Entry for Menu ID:57636; '&Find'
964 //#: Corresponding Menu ID:32771; '&Find'
965 //msgid "V C +F"
966 //msgstr ""
968 // Since "filter" and "find" are most likely translated to words starting
969 // with different letters, we need to have a separate accelerator entry
970 // for each of those
971 swprintf_s(buf, L"ID:%u:", wID);
973 // EXACTLY 5 characters long "ACS+X"
974 // V = Virtual key (or blank if not used)
975 // A = Alt key (or blank if not used)
976 // C = Ctrl key (or blank if not used)
977 // S = Shift key (or blank if not used)
978 // X = upper case character
979 // e.g. "V CS+Q" == Ctrl + Shift + 'Q'
980 if ((fFlags & FVIRTKEY) == FVIRTKEY) // 0x01
981 wcscat_s(buf, L"V");
982 else
983 wcscat_s(buf, L" ");
985 if ((fFlags & FALT) == FALT) // 0x10
986 wcscat_s(buf, L"A");
987 else
988 wcscat_s(buf, L" ");
990 if ((fFlags & FCONTROL) == FCONTROL) // 0x08
991 wcscat_s(buf, L"C");
992 else
993 wcscat_s(buf, L" ");
995 if ((fFlags & FSHIFT) == FSHIFT) // 0x04
996 wcscat_s(buf, L"S");
997 else
998 wcscat_s(buf, L" ");
1000 swprintf_s(buf2, L"%s+%c", buf, wAnsi);
1002 std::wstring wstr = std::wstring(buf2);
1003 RESOURCEENTRY AKey_entry = m_StringEntries[wstr];
1005 wchar_t szTempBuf[1024] = { 0 };
1006 std::wstring wmenu;
1007 pME_iter = m_MenuEntries.find(wID);
1008 if (pME_iter != m_MenuEntries.end())
1010 wmenu = pME_iter->second.msgstr;
1012 swprintf_s(szTempBuf, L"#. Accelerator Entry for Menu ID:%u; '%s'", wID, wmenu.c_str());
1013 AKey_entry.automaticcomments.push_back(std::wstring(szTempBuf));
1015 m_StringEntries[wstr] = AKey_entry;
1016 } while (!bEnd);
1018 return TRUE;
1021 BOOL CResModule::ReplaceAccelerator(LPCWSTR lpszType, WORD wLanguage)
1023 LPACCEL lpaccelNew; // pointer to new accelerator table
1024 HACCEL haccelOld; // handle to old accelerator table
1025 int cAccelerators; // number of accelerators in table
1026 HGLOBAL hglAccTableNew;
1027 const WORD* p;
1028 int i;
1030 haccelOld = LoadAccelerators(m_hResDll, lpszType);
1032 if (!haccelOld)
1033 MYERROR;
1035 cAccelerators = CopyAcceleratorTable(haccelOld, nullptr, 0);
1037 lpaccelNew = static_cast<LPACCEL>(LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL)));
1039 if (!lpaccelNew)
1040 MYERROR;
1041 SCOPE_EXIT { LocalFree(lpaccelNew); };
1043 CopyAcceleratorTable(haccelOld, lpaccelNew, cAccelerators);
1045 // Find the accelerator that the user modified
1046 // and change its flags and virtual-key code
1047 // as appropriate.
1049 for (i = 0; i < cAccelerators; i++)
1051 if ((lpaccelNew[i].key < 0x30) ||
1052 (lpaccelNew[i].key > 0x5A) ||
1053 (lpaccelNew[i].key >= 0x3A && lpaccelNew[i].key <= 0x40))
1054 continue;
1056 BYTE xfVirt;
1057 wchar_t xkey = { 0 };
1058 wchar_t buf[1024] = { 0 };
1059 wchar_t buf2[1024] = { 0 };
1061 swprintf_s(buf, L"ID:%d:", lpaccelNew[i].cmd);
1063 // get original key combination
1064 if ((lpaccelNew[i].fVirt & FVIRTKEY) == FVIRTKEY) // 0x01
1065 wcscat_s(buf, L"V");
1066 else
1067 wcscat_s(buf, L" ");
1069 if ((lpaccelNew[i].fVirt & FALT) == FALT) // 0x10
1070 wcscat_s(buf, L"A");
1071 else
1072 wcscat_s(buf, L" ");
1074 if ((lpaccelNew[i].fVirt & FCONTROL) == FCONTROL) // 0x08
1075 wcscat_s(buf, L"C");
1076 else
1077 wcscat_s(buf, L" ");
1079 if ((lpaccelNew[i].fVirt & FSHIFT) == FSHIFT) // 0x04
1080 wcscat_s(buf, L"S");
1081 else
1082 wcscat_s(buf, L" ");
1084 swprintf_s(buf2, L"%s+%c", buf, lpaccelNew[i].key);
1086 // Is it there?
1087 std::map<std::wstring, RESOURCEENTRY>::iterator pAK_iter = m_StringEntries.find(buf2);
1088 if (pAK_iter != m_StringEntries.end())
1090 m_bTranslatedAcceleratorStrings++;
1091 xfVirt = 0;
1092 xkey = 0;
1093 std::wstring wtemp = pAK_iter->second.msgstr;
1094 wtemp = wtemp.substr(wtemp.find_last_of(':')+1);
1095 if (wtemp.size() != 6)
1096 continue;
1097 if (wtemp.compare(0, 1, L"V") == 0)
1098 xfVirt |= FVIRTKEY;
1099 else if (wtemp.compare(0, 1, L" ") != 0)
1100 continue; // not a space - user must have made a mistake when translating
1101 if (wtemp.compare(1, 1, L"A") == 0)
1102 xfVirt |= FALT;
1103 else if (wtemp.compare(1, 1, L" ") != 0)
1104 continue; // not a space - user must have made a mistake when translating
1105 if (wtemp.compare(2, 1, L"C") == 0)
1106 xfVirt |= FCONTROL;
1107 else if (wtemp.compare(2, 1, L" ") != 0)
1108 continue; // not a space - user must have made a mistake when translating
1109 if (wtemp.compare(3, 1, L"S") == 0)
1110 xfVirt |= FSHIFT;
1111 else if (wtemp.compare(3, 1, L" ") != 0)
1112 continue; // not a space - user must have made a mistake when translating
1113 if (wtemp.compare(4, 1, L"+") == 0)
1115 swscanf_s(wtemp.substr(5, 1).c_str(), L"%c", &xkey, 1);
1116 lpaccelNew[i].fVirt = xfVirt;
1117 lpaccelNew[i].key = static_cast<DWORD>(xkey);
1120 else
1121 m_bDefaultAcceleratorStrings++;
1125 // Create the new accelerator table
1126 hglAccTableNew = LocalAlloc(LPTR, cAccelerators * 4 * sizeof(WORD));
1127 if (!hglAccTableNew)
1128 MYERROR;
1129 SCOPE_EXIT { LocalFree(hglAccTableNew); };
1130 p = static_cast<WORD*>(hglAccTableNew);
1131 lpaccelNew[cAccelerators-1].fVirt |= 0x80;
1132 for (i = 0; i < cAccelerators; i++)
1134 memcpy(reinterpret_cast<void*>(const_cast<WORD*>(p)), &lpaccelNew[i].fVirt, 1);
1135 p++;
1136 memcpy(reinterpret_cast<void*>(const_cast<WORD*>(p)), &lpaccelNew[i].key, sizeof(WORD));
1137 p++;
1138 memcpy(reinterpret_cast<void*>(const_cast<WORD*>(p)), &lpaccelNew[i].cmd, sizeof(WORD));
1139 p++;
1140 p++;
1143 if (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType,
1144 (m_wTargetLang ? m_wTargetLang : wLanguage), hglAccTableNew /* haccelNew*/, cAccelerators * 4 * sizeof(WORD)))
1146 MYERROR;
1149 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType, wLanguage, nullptr, 0)))
1151 MYERROR;
1154 return TRUE;
1157 BOOL CResModule::ExtractDialog(LPCWSTR lpszType)
1159 const WORD* lpDlg;
1160 const WORD* lpDlgItem;
1161 DIALOGINFO dlg;
1162 DLGITEMINFO dlgItem;
1163 WORD bNumControls;
1164 HRSRC hrsrc;
1165 HGLOBAL hGlblDlgTemplate;
1167 hrsrc = FindResource(m_hResDll, lpszType, RT_DIALOG);
1169 if (!hrsrc)
1170 MYERROR;
1172 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1173 if (!hGlblDlgTemplate)
1174 MYERROR;
1175 SCOPE_EXIT { FreeResource(hGlblDlgTemplate); };
1176 lpDlg = static_cast<const WORD*>(LockResource(hGlblDlgTemplate));
1178 if (!lpDlg)
1179 MYERROR;
1180 SCOPE_EXIT { UnlockResource(hGlblDlgTemplate); };
1182 lpDlgItem = GetDialogInfo(lpDlg, &dlg);
1183 bNumControls = dlg.nbItems;
1185 if (dlg.caption)
1187 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
1188 wcscpy_s(buf, dlg.caption);
1189 CUtils::StringExtend(buf);
1191 std::wstring wstr = std::wstring(buf);
1192 RESOURCEENTRY entry = m_StringEntries[wstr];
1193 InsertResourceIDs(RT_DIALOG, reinterpret_cast<INT_PTR>(lpszType), entry, reinterpret_cast<INT_PTR>(lpszType), L"");
1195 m_StringEntries[wstr] = entry;
1198 while (bNumControls-- != 0)
1200 wchar_t szTitle[500] = { 0 };
1201 BOOL bCode;
1203 lpDlgItem = GetControlInfo(lpDlgItem, &dlgItem, dlg.dialogEx, &bCode);
1205 if (bCode == FALSE)
1206 wcsncpy_s(szTitle, dlgItem.windowName, _countof(szTitle) - 1);
1208 if (szTitle[0])
1210 CUtils::StringExtend(szTitle);
1212 std::wstring wstr = std::wstring(szTitle);
1213 RESOURCEENTRY entry = m_StringEntries[wstr];
1214 InsertResourceIDs(RT_DIALOG, reinterpret_cast<INT_PTR>(lpszType), entry, dlgItem.id, L"");
1216 m_StringEntries[wstr] = entry;
1220 return TRUE;
1223 BOOL CResModule::ReplaceDialog(LPCWSTR lpszType, WORD wLanguage)
1225 const WORD* lpDlg;
1226 HRSRC hrsrc;
1227 HGLOBAL hGlblDlgTemplate;
1229 hrsrc = FindResourceEx(m_hResDll, RT_DIALOG, lpszType, wLanguage);
1231 if (!hrsrc)
1232 MYERROR;
1234 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1236 if (!hGlblDlgTemplate)
1237 MYERROR;
1238 SCOPE_EXIT { FreeResource(hGlblDlgTemplate); };
1240 lpDlg = static_cast<WORD*>(LockResource(hGlblDlgTemplate));
1242 if (!lpDlg)
1243 MYERROR;
1244 SCOPE_EXIT { UnlockResource(hGlblDlgTemplate); };
1246 size_t nMem = 0;
1247 const WORD * p = lpDlg;
1248 if (!CountMemReplaceDialogResource(p, &nMem, nullptr))
1249 MYERROR;
1250 auto newDialog = std::make_unique<WORD[]>(nMem + (nMem % 2));
1252 size_t index = 0;
1253 if (!CountMemReplaceDialogResource(lpDlg, &index, newDialog.get()))
1254 MYERROR;
1256 if (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newDialog.get(), static_cast<DWORD>(nMem + (nMem % 2)) * sizeof(WORD)))
1257 MYERROR;
1259 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, wLanguage, nullptr, 0)))
1260 MYERROR;
1262 return TRUE;
1265 const WORD* CResModule::GetDialogInfo(const WORD * pTemplate, LPDIALOGINFO lpDlgInfo) const
1267 const WORD* p = pTemplate;
1269 lpDlgInfo->style = GET_DWORD(p);
1270 p += 2;
1272 if (lpDlgInfo->style == 0xffff0001) // DIALOGEX resource
1274 lpDlgInfo->dialogEx = TRUE;
1275 lpDlgInfo->helpId = GET_DWORD(p);
1276 p += 2;
1277 lpDlgInfo->exStyle = GET_DWORD(p);
1278 p += 2;
1279 lpDlgInfo->style = GET_DWORD(p);
1280 p += 2;
1282 else
1284 lpDlgInfo->dialogEx = FALSE;
1285 lpDlgInfo->helpId = 0;
1286 lpDlgInfo->exStyle = GET_DWORD(p);
1287 p += 2;
1290 lpDlgInfo->nbItems = GET_WORD(p);
1291 p++;
1293 lpDlgInfo->x = GET_WORD(p);
1294 p++;
1296 lpDlgInfo->y = GET_WORD(p);
1297 p++;
1299 lpDlgInfo->cx = GET_WORD(p);
1300 p++;
1302 lpDlgInfo->cy = GET_WORD(p);
1303 p++;
1305 // Get the menu name
1307 switch (GET_WORD(p))
1309 case 0x0000:
1310 lpDlgInfo->menuName = nullptr;
1311 p++;
1312 break;
1313 case 0xffff:
1314 lpDlgInfo->menuName = reinterpret_cast<LPCWSTR>(static_cast<WORD>(GET_WORD(p + 1)));
1315 p += 2;
1316 break;
1317 default:
1318 lpDlgInfo->menuName = reinterpret_cast<LPCWSTR>(p);
1319 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1320 break;
1323 // Get the class name
1325 switch (GET_WORD(p))
1327 case 0x0000:
1328 lpDlgInfo->className = static_cast<LPCWSTR>(MAKEINTATOM(32770));
1329 p++;
1330 break;
1331 case 0xffff:
1332 lpDlgInfo->className = reinterpret_cast<LPCWSTR>(static_cast<WORD>(GET_WORD(p + 1)));
1333 p += 2;
1334 break;
1335 default:
1336 lpDlgInfo->className = reinterpret_cast<LPCWSTR>(p);
1337 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1338 break;
1341 // Get the window caption
1343 lpDlgInfo->caption = reinterpret_cast<LPCWSTR>(p);
1344 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1346 // Get the font name
1348 if (lpDlgInfo->style & DS_SETFONT)
1350 lpDlgInfo->pointSize = GET_WORD(p);
1351 p++;
1353 if (lpDlgInfo->dialogEx)
1355 lpDlgInfo->weight = GET_WORD(p);
1356 p++;
1357 lpDlgInfo->italic = LOBYTE(GET_WORD(p));
1358 p++;
1360 else
1362 lpDlgInfo->weight = FW_DONTCARE;
1363 lpDlgInfo->italic = FALSE;
1366 lpDlgInfo->faceName = reinterpret_cast<LPCWSTR>(p);
1367 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1369 // First control is on DWORD boundary
1370 p = AlignWORD(p);
1372 return p;
1375 const WORD* CResModule::GetControlInfo(const WORD* p, LPDLGITEMINFO lpDlgItemInfo, BOOL dialogEx, LPBOOL bIsID) const
1377 if (dialogEx)
1379 lpDlgItemInfo->helpId = GET_DWORD(p);
1380 p += 2;
1381 lpDlgItemInfo->exStyle = GET_DWORD(p);
1382 p += 2;
1383 lpDlgItemInfo->style = GET_DWORD(p);
1384 p += 2;
1386 else
1388 lpDlgItemInfo->helpId = 0;
1389 lpDlgItemInfo->style = GET_DWORD(p);
1390 p += 2;
1391 lpDlgItemInfo->exStyle = GET_DWORD(p);
1392 p += 2;
1395 lpDlgItemInfo->x = GET_WORD(p);
1396 p++;
1398 lpDlgItemInfo->y = GET_WORD(p);
1399 p++;
1401 lpDlgItemInfo->cx = GET_WORD(p);
1402 p++;
1404 lpDlgItemInfo->cy = GET_WORD(p);
1405 p++;
1407 if (dialogEx)
1409 // ID is a DWORD for DIALOGEX
1410 lpDlgItemInfo->id = static_cast<WORD>(GET_DWORD(p));
1411 p += 2;
1413 else
1415 lpDlgItemInfo->id = GET_WORD(p);
1416 p++;
1419 if (GET_WORD(p) == 0xffff)
1421 GET_WORD(p + 1);
1423 p += 2;
1425 else
1427 lpDlgItemInfo->className = reinterpret_cast<LPCWSTR>(p);
1428 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1431 if (GET_WORD(p) == 0xffff) // an integer ID?
1433 *bIsID = TRUE;
1434 lpDlgItemInfo->windowName = reinterpret_cast<LPCWSTR>(GET_WORD(p + 1));
1435 p += 2;
1437 else
1439 *bIsID = FALSE;
1440 lpDlgItemInfo->windowName = reinterpret_cast<LPCWSTR>(p);
1441 p += wcslen(reinterpret_cast<LPCWSTR>(p)) + 1;
1444 if (GET_WORD(p))
1446 lpDlgItemInfo->data = const_cast<WORD*>(p + 1);
1447 p += GET_WORD(p) / sizeof(WORD);
1449 else
1450 lpDlgItemInfo->data = nullptr;
1452 p++;
1453 // Next control is on DWORD boundary
1454 p = AlignWORD(p);
1455 return p;
1458 const WORD * CResModule::CountMemReplaceDialogResource(const WORD * res, size_t * wordcount, WORD * newDialog)
1460 BOOL bEx = FALSE;
1461 DWORD style = GET_DWORD(res);
1462 if (newDialog)
1464 newDialog[(*wordcount)++] = GET_WORD(res++);
1465 newDialog[(*wordcount)++] = GET_WORD(res++);
1467 else
1469 res += 2;
1470 (*wordcount) += 2;
1473 if (style == 0xffff0001) // DIALOGEX resource
1475 bEx = TRUE;
1476 if (newDialog)
1478 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1479 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1480 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1481 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1482 style = GET_DWORD(res);
1483 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1484 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1486 else
1488 res += 4;
1489 style = GET_DWORD(res);
1490 res += 2;
1491 (*wordcount) += 6;
1494 else
1496 bEx = FALSE;
1497 if (newDialog)
1499 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1500 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1501 //style = GET_DWORD(res);
1502 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1503 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1505 else
1507 res += 2;
1508 (*wordcount) += 2;
1512 if (newDialog)
1513 newDialog[(*wordcount)] = GET_WORD(res);
1514 WORD nbItems = GET_WORD(res);
1515 (*wordcount)++;
1516 res++;
1518 if (newDialog)
1519 newDialog[(*wordcount)] = GET_WORD(res); //x
1520 (*wordcount)++;
1521 res++;
1523 if (newDialog)
1524 newDialog[(*wordcount)] = GET_WORD(res); //y
1525 (*wordcount)++;
1526 res++;
1528 if (newDialog)
1529 newDialog[(*wordcount)] = GET_WORD(res); //cx
1530 (*wordcount)++;
1531 res++;
1533 if (newDialog)
1534 newDialog[(*wordcount)] = GET_WORD(res); //cy
1535 (*wordcount)++;
1536 res++;
1538 // Get the menu name
1540 switch (GET_WORD(res))
1542 case 0x0000:
1543 if (newDialog)
1544 newDialog[(*wordcount)] = GET_WORD(res);
1545 (*wordcount)++;
1546 res++;
1547 break;
1548 case 0xffff:
1549 if (newDialog)
1551 newDialog[(*wordcount)++] = GET_WORD(res++);
1552 newDialog[(*wordcount)++] = GET_WORD(res++);
1554 else
1556 (*wordcount) += 2;
1557 res += 2;
1559 break;
1560 default:
1561 if (newDialog)
1563 wcscpy(reinterpret_cast<LPWSTR>(&newDialog[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
1565 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1566 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1567 break;
1570 // Get the class name
1572 switch (GET_WORD(res))
1574 case 0x0000:
1575 if (newDialog)
1576 newDialog[(*wordcount)] = GET_WORD(res);
1577 (*wordcount)++;
1578 res++;
1579 break;
1580 case 0xffff:
1581 if (newDialog)
1583 newDialog[(*wordcount)++] = GET_WORD(res++);
1584 newDialog[(*wordcount)++] = GET_WORD(res++);
1586 else
1588 (*wordcount) += 2;
1589 res += 2;
1591 break;
1592 default:
1593 if (newDialog)
1595 wcscpy(reinterpret_cast<LPWSTR>(&newDialog[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
1597 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1598 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1599 break;
1602 // Get the window caption
1604 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1605 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1607 // Get the font name
1609 if (style & DS_SETFONT)
1611 if (newDialog)
1612 newDialog[(*wordcount)] = GET_WORD(res);
1613 res++;
1614 (*wordcount)++;
1616 if (bEx)
1618 if (newDialog)
1620 newDialog[(*wordcount)++] = GET_WORD(res++);
1621 newDialog[(*wordcount)++] = GET_WORD(res++);
1623 else
1625 res += 2;
1626 (*wordcount) += 2;
1630 if (newDialog)
1631 wcscpy(reinterpret_cast<LPWSTR>(&newDialog[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
1632 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1633 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1635 // First control is on DWORD boundary
1636 while ((*wordcount)%2)
1637 (*wordcount)++;
1638 while (reinterpret_cast<UINT_PTR>(res) % 4)
1639 res++;
1641 while (nbItems--)
1643 res = ReplaceControlInfo(res, wordcount, newDialog, bEx);
1645 return res;
1648 const WORD* CResModule::ReplaceControlInfo(const WORD * res, size_t * wordcount, WORD * newDialog, BOOL bEx)
1650 if (bEx)
1652 if (newDialog)
1654 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1655 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1657 else
1659 res += 2;
1660 (*wordcount) += 2;
1663 if (newDialog)
1665 auto exStyle = reinterpret_cast<LONG*>(&newDialog[(*wordcount)]);
1666 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1667 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1668 if (m_bRTL)
1669 *exStyle |= WS_EX_RTLREADING;
1671 else
1673 res += 2;
1674 (*wordcount) += 2;
1677 if (newDialog)
1679 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1680 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1682 else
1684 res += 2;
1685 (*wordcount) += 2;
1688 if (newDialog)
1689 newDialog[(*wordcount)] = GET_WORD(res); //x
1690 res++;
1691 (*wordcount)++;
1693 if (newDialog)
1694 newDialog[(*wordcount)] = GET_WORD(res); //y
1695 res++;
1696 (*wordcount)++;
1698 if (newDialog)
1699 newDialog[(*wordcount)] = GET_WORD(res); //cx
1700 res++;
1701 (*wordcount)++;
1703 if (newDialog)
1704 newDialog[(*wordcount)] = GET_WORD(res); //cy
1705 res++;
1706 (*wordcount)++;
1708 if (bEx)
1710 // ID is a DWORD for DIALOGEX
1711 if (newDialog)
1713 newDialog[(*wordcount)++] = GET_WORD(res++);
1714 newDialog[(*wordcount)++] = GET_WORD(res++);
1716 else
1718 res += 2;
1719 (*wordcount) += 2;
1722 else
1724 if (newDialog)
1725 newDialog[(*wordcount)] = GET_WORD(res);
1726 res++;
1727 (*wordcount)++;
1730 if (GET_WORD(res) == 0xffff) //classID
1732 if (newDialog)
1734 newDialog[(*wordcount)++] = GET_WORD(res++);
1735 newDialog[(*wordcount)++] = GET_WORD(res++);
1737 else
1739 res += 2;
1740 (*wordcount) += 2;
1743 else
1745 if (newDialog)
1746 wcscpy(reinterpret_cast<LPWSTR>(&newDialog[(*wordcount)]), reinterpret_cast<LPCWSTR>(res));
1747 (*wordcount) += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1748 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1751 if (GET_WORD(res) == 0xffff) // an integer ID?
1753 if (newDialog)
1755 newDialog[(*wordcount)++] = GET_WORD(res++);
1756 newDialog[(*wordcount)++] = GET_WORD(res++);
1758 else
1760 res += 2;
1761 (*wordcount) += 2;
1764 else
1766 ReplaceStr(reinterpret_cast<LPCWSTR>(res), newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1767 res += wcslen(reinterpret_cast<LPCWSTR>(res)) + 1;
1770 if (newDialog)
1771 memcpy(&newDialog[(*wordcount)], res, (GET_WORD(res)+1)*sizeof(WORD));
1772 (*wordcount) += (GET_WORD(res)+1);
1773 res += (GET_WORD(res)+1);
1774 // Next control is on DWORD boundary
1775 while ((*wordcount) % 2)
1776 (*wordcount)++;
1777 res = AlignWORD(res);
1779 return res;
1782 BOOL CResModule::ExtractRibbon(LPCWSTR lpszType)
1784 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1785 HGLOBAL hglRibbonTemplate;
1786 const BYTE *p;
1788 if (!hrsrc)
1789 MYERROR;
1791 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1792 if (!hglRibbonTemplate)
1793 MYERROR;
1794 SCOPE_EXIT { FreeResource(hglRibbonTemplate); };
1796 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1798 p = static_cast<const BYTE*>(LockResource(hglRibbonTemplate));
1800 if (!p)
1801 MYERROR;
1802 SCOPE_EXIT { UnlockResource(hglRibbonTemplate); };
1804 // Resource consists of one single string
1805 // that is XML.
1807 // extract all <id><name>blah1</name><value>blah2</value></id><text>blah</text> elements
1809 const std::regex regRevMatch("<ID><NAME>([^<]+)</NAME><VALUE>([^<]+)</VALUE></ID><TEXT>([^<]+)</TEXT>");
1810 std::string ss = std::string(reinterpret_cast<const char*>(p), sizeres);
1811 const std::sregex_iterator end;
1812 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
1814 std::wstring strIdNameVal = CUnicodeUtils::StdGetUnicode((*it)[1].str());
1815 strIdNameVal += L" - Ribbon name";
1817 std::wstring strIdVal = CUnicodeUtils::StdGetUnicode((*it)[2].str());
1819 std::wstring str = CUnicodeUtils::StdGetUnicode((*it)[3].str());
1821 RESOURCEENTRY entry = m_StringEntries[str];
1822 InsertResourceIDs(RT_RIBBON, 0, entry, std::stoi(strIdVal), strIdNameVal.c_str());
1823 if (wcschr(str.c_str(), '%'))
1824 entry.flag = L"#, c-format";
1825 m_StringEntries[str] = entry;
1826 m_bDefaultRibbonTexts++;
1829 // extract all </ELEMENT_NAME><NAME>blahblah</NAME> elements
1831 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1832 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
1834 std::wstring ret = CUnicodeUtils::StdGetUnicode((*it)[1].str());
1835 RESOURCEENTRY entry = m_StringEntries[ret];
1836 InsertResourceIDs(RT_RIBBON, 0, entry, reinterpret_cast<INT_PTR>(lpszType), L" - Ribbon element");
1837 if (wcschr(ret.c_str(), '%'))
1838 entry.flag = L"#, c-format";
1839 m_StringEntries[ret] = entry;
1840 m_bDefaultRibbonTexts++;
1843 return TRUE;
1846 BOOL CResModule::ReplaceRibbon(LPCWSTR lpszType, WORD wLanguage)
1848 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1849 HGLOBAL hglRibbonTemplate;
1850 const BYTE *p;
1852 if (!hrsrc)
1853 MYERROR;
1855 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1857 if (!hglRibbonTemplate)
1858 MYERROR;
1859 SCOPE_EXIT { FreeResource(hglRibbonTemplate); };
1861 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1863 p = static_cast<const BYTE*>(LockResource(hglRibbonTemplate));
1865 if (!p)
1866 MYERROR;
1867 SCOPE_EXIT { UnlockResource(hglRibbonTemplate); };
1869 std::string ss = std::string(reinterpret_cast<const char*>(p), sizeres);
1870 std::wstring ssw = CUnicodeUtils::StdGetUnicode(ss);
1872 const std::regex regRevMatch("<TEXT>([^<]+)</TEXT>");
1873 const std::sregex_iterator end;
1874 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
1876 std::wstring ret = CUnicodeUtils::StdGetUnicode((*it)[1].str());
1878 RESOURCEENTRY entry = m_StringEntries[ret];
1879 ret = L"<TEXT>" + ret + L"</TEXT>";
1881 if (entry.msgstr.size())
1883 size_t buflen = entry.msgstr.size() + 10;
1884 auto sbuf = std::make_unique<wchar_t[]>(buflen);
1885 wcscpy_s(sbuf.get(), buflen, entry.msgstr.c_str());
1886 CUtils::StringCollapse(sbuf.get());
1887 ReplaceWithRegex(sbuf.get(), buflen);
1888 std::wstring sreplace = L"<TEXT>";
1889 sreplace += sbuf.get();
1890 sreplace += L"</TEXT>";
1891 CUtils::SearchReplace(ssw, ret, sreplace);
1892 m_bTranslatedRibbonTexts++;
1894 else
1895 m_bDefaultRibbonTexts++;
1898 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1899 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
1901 std::wstring ret = CUnicodeUtils::StdGetUnicode((*it)[1].str());
1903 RESOURCEENTRY entry = m_StringEntries[ret];
1904 ret = L"</ELEMENT_NAME><NAME>" + ret + L"</NAME>";
1906 if (entry.msgstr.size())
1908 size_t buflen = entry.msgstr.size() + 10;
1909 auto sbuf = std::make_unique<wchar_t[]>(buflen);
1910 wcscpy_s(sbuf.get(), buflen, entry.msgstr.c_str());
1911 CUtils::StringCollapse(sbuf.get());
1912 ReplaceWithRegex(sbuf.get(), buflen);
1913 std::wstring sreplace = L"</ELEMENT_NAME><NAME>";
1914 sreplace += sbuf.get();
1915 sreplace += L"</NAME>";
1916 CUtils::SearchReplace(ssw, ret, sreplace);
1917 m_bTranslatedRibbonTexts++;
1919 else
1920 m_bDefaultRibbonTexts++;
1923 auto buf = std::make_unique<char[]>(ssw.size() * 4 + 1);
1924 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, ssw.c_str(), -1, buf.get(), static_cast<int>(ssw.size()) * 4, nullptr, nullptr);
1927 if (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), buf.get(), lengthIncTerminator-1))
1928 MYERROR;
1930 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, wLanguage, nullptr, 0)))
1931 MYERROR;
1933 return TRUE;
1936 std::wstring CResModule::ReplaceWithRegex(WCHAR* pBuf, size_t bufferSize)
1938 for (const auto& t : m_StringEntries.m_regexes)
1942 std::wregex e(std::get<0>(t), std::regex_constants::icase);
1943 auto replaced = std::regex_replace(pBuf, e, std::get<1>(t));
1944 wcscpy_s(pBuf, bufferSize, replaced.c_str());
1946 catch (std::exception&)
1950 return pBuf;
1953 std::wstring CResModule::ReplaceWithRegex(std::wstring& s)
1955 for (const auto& t : m_StringEntries.m_regexes)
1959 std::wregex e(std::get<0>(t), std::regex_constants::icase);
1960 auto replaced = std::regex_replace(s, e, std::get<1>(t));
1961 s = replaced;
1963 catch (std::exception&)
1967 return s;
1970 BOOL CALLBACK CResModule::EnumResNameCallback(HMODULE /*hModule*/, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
1972 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
1974 if (lpszType == RT_STRING)
1976 if (IS_INTRESOURCE(lpszName))
1978 if (!lpResModule->ExtractString(lpszName))
1979 return FALSE;
1982 else if (lpszType == RT_MENU)
1984 if (IS_INTRESOURCE(lpszName))
1986 if (!lpResModule->ExtractMenu(lpszName))
1987 return FALSE;
1990 else if (lpszType == RT_DIALOG)
1992 if (IS_INTRESOURCE(lpszName))
1994 if (!lpResModule->ExtractDialog(lpszName))
1995 return FALSE;
1998 else if (lpszType == RT_ACCELERATOR)
2000 if (IS_INTRESOURCE(lpszName))
2002 if (!lpResModule->ExtractAccelerator(lpszName))
2003 return FALSE;
2006 else if (lpszType == RT_RIBBON)
2008 if (IS_INTRESOURCE(lpszName))
2010 if (!lpResModule->ExtractRibbon(lpszName))
2011 return FALSE;
2015 return TRUE;
2018 BOOL CALLBACK CResModule::EnumResNameWriteCallback(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
2020 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2021 return EnumResourceLanguages(hModule, lpszType, lpszName, reinterpret_cast<ENUMRESLANGPROC>(&lpResModule->EnumResWriteLangCallback), lParam);
2024 BOOL CALLBACK CResModule::EnumResWriteLangCallback(HMODULE /*hModule*/, LPCWSTR lpszType, LPWSTR lpszName, WORD wLanguage, LONG_PTR lParam)
2026 BOOL bRes = FALSE;
2027 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2029 if (lpszType == RT_STRING)
2031 bRes = lpResModule->ReplaceString(lpszName, wLanguage);
2033 else if (lpszType == RT_MENU)
2035 bRes = lpResModule->ReplaceMenu(lpszName, wLanguage);
2037 else if (lpszType == RT_DIALOG)
2039 bRes = lpResModule->ReplaceDialog(lpszName, wLanguage);
2041 else if (lpszType == RT_ACCELERATOR)
2043 bRes = lpResModule->ReplaceAccelerator(lpszName, wLanguage);
2045 else if (lpszType == RT_RIBBON)
2047 bRes = lpResModule->ReplaceRibbon(lpszName, wLanguage);
2050 return bRes;
2054 void CResModule::ReplaceStr(LPCWSTR src, WORD * dest, size_t * count, int * translated, int * def)
2056 wchar_t buf[MAX_STRING_LENGTH] = { 0 };
2057 wcscpy_s(buf, src);
2058 CUtils::StringExtend(buf);
2060 std::wstring wstr = std::wstring(buf);
2061 ReplaceWithRegex(buf);
2062 RESOURCEENTRY entry = m_StringEntries[wstr];
2063 if (!entry.msgstr.empty())
2065 wcscpy_s(buf, entry.msgstr.c_str());
2066 ReplaceWithRegex(buf);
2067 CUtils::StringCollapse(buf);
2068 if (dest)
2069 wcscpy(reinterpret_cast<wchar_t*>(&dest[(*count)]), buf);
2070 (*count) += wcslen(buf) + 1;
2071 (*translated)++;
2073 else
2075 if (wcscmp(buf, wstr.c_str()))
2077 if (dest)
2078 wcscpy(reinterpret_cast<wchar_t*>(&dest[(*count)]), buf);
2079 (*count) += wcslen(buf) + 1;
2080 (*translated)++;
2082 else
2084 if (dest)
2085 wcscpy(reinterpret_cast<wchar_t*>(&dest[(*count)]), src);
2086 (*count) += wcslen(src) + 1;
2087 if (wcslen(src))
2088 (*def)++;
2093 static bool StartsWith(const std::string& heystacl, const char* needle)
2095 return heystacl.compare(0, strlen(needle), needle) == 0;
2098 static bool StartsWith(const std::wstring& heystacl, const wchar_t* needle)
2100 return heystacl.compare(0, wcslen(needle), needle) == 0;
2103 size_t CResModule::ScanHeaderFile(const std::wstring & filepath)
2105 size_t count = 0;
2107 // open the file and read the contents
2108 DWORD reqLen = GetFullPathName(filepath.c_str(), 0, nullptr, nullptr);
2109 auto wcfullPath = std::make_unique<wchar_t[]>(reqLen + 1);
2110 GetFullPathName(filepath.c_str(), reqLen, wcfullPath.get(), nullptr);
2111 std::wstring fullpath = wcfullPath.get();
2114 // first treat the file as ASCII and try to get the defines
2116 std::ifstream fin(fullpath);
2117 std::string file_line;
2118 while (std::getline(fin, file_line))
2120 auto defpos = file_line.find("#define");
2121 if (defpos != std::string::npos)
2123 std::string text = file_line.substr(defpos + 7);
2124 trim(text);
2125 auto spacepos = text.find(' ');
2126 if (spacepos == std::string::npos)
2127 spacepos = text.find('\t');
2128 if (spacepos != std::string::npos)
2130 auto value = atol(text.substr(spacepos).c_str());
2131 if (value == 0 && text.substr(spacepos).find("0x") != std::string::npos)
2132 value = std::stoul(text.substr(spacepos), nullptr, 16);
2133 text = text.substr(0, spacepos);
2134 trim(text);
2135 if (StartsWith(text, "IDS_") || StartsWith(text, "AFX_IDS_") || StartsWith(text, "AFX_IDP_"))
2137 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2138 ++count;
2140 else if (StartsWith(text, "IDD_") || StartsWith(text, "AFX_IDD_"))
2142 m_currentHeaderDataDialogs[value] = CUnicodeUtils::StdGetUnicode(text);
2143 ++count;
2145 else if (StartsWith(text, "ID_") || StartsWith(text, "AFX_ID_"))
2147 m_currentHeaderDataMenus[value] = CUnicodeUtils::StdGetUnicode(text);
2148 ++count;
2150 else if (StartsWith(text, "cmd"))
2152 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2153 ++count;
2155 else if (text.find("_RESID") != std::string::npos)
2157 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2158 ++count;
2166 // now try the same with the file treated as utf16
2168 // open as a byte stream
2169 std::ifstream wfin(fullpath, std::ios::binary);
2170 // apply BOM-sensitive UTF-16 facet
2171 //std::wifstream wfin(fullpath);
2172 std::string file_line;
2173 while (std::getline(wfin, file_line))
2175 auto defpos = file_line.find("#define");
2176 if (defpos != std::wstring::npos)
2178 std::wstring text = CUnicodeUtils::StdGetUnicode(file_line.substr(defpos + 7));
2179 trim(text);
2180 auto spacepos = text.find(' ');
2181 if (spacepos == std::wstring::npos)
2182 spacepos = text.find('\t');
2183 if (spacepos != std::wstring::npos)
2185 auto value = _wtol(text.substr(spacepos).c_str());
2186 if (value == 0 && text.substr(spacepos).find(L"0x") != std::wstring::npos)
2187 value = std::stoul(text.substr(spacepos), nullptr, 16);
2188 text = text.substr(0, spacepos);
2189 trim(text);
2190 if (StartsWith(text, L"IDS_") || StartsWith(text, L"AFX_IDS_") || StartsWith(text, L"AFX_IDP_"))
2192 m_currentHeaderDataStrings[value] = text;
2193 ++count;
2195 else if (StartsWith(text, L"IDD_") || StartsWith(text, L"AFX_IDD_"))
2197 m_currentHeaderDataDialogs[value] = text;
2198 ++count;
2200 else if (StartsWith(text, L"ID_") || StartsWith(text, L"AFX_ID_"))
2202 m_currentHeaderDataMenus[value] = text;
2203 ++count;
2205 else if (StartsWith(text, L"cmd"))
2207 m_currentHeaderDataStrings[value] = text;
2208 ++count;
2210 else if (text.find(L"_RESID") != std::string::npos)
2212 m_currentHeaderDataStrings[value] = text;
2213 ++count;
2220 return count;
2223 void CResModule::InsertResourceIDs(LPCWSTR lpType, INT_PTR mainId, RESOURCEENTRY& entry, INT_PTR id, LPCWSTR infotext)
2225 if (lpType == RT_DIALOG)
2227 auto foundIt = m_currentHeaderDataDialogs.find(mainId);
2228 if (foundIt != m_currentHeaderDataDialogs.end())
2229 entry.resourceIDs.insert(L"Dialog " + foundIt->second + L": Control id " + NumToStr(id) + infotext);
2230 else
2231 entry.resourceIDs.insert(NumToStr(id) + infotext);
2233 else if (lpType == RT_STRING)
2235 auto foundIt = m_currentHeaderDataStrings.find(id);
2236 if (foundIt != m_currentHeaderDataStrings.end())
2237 entry.resourceIDs.insert(foundIt->second + infotext);
2238 else
2239 entry.resourceIDs.insert(NumToStr(id) + infotext);
2241 else if (lpType == RT_MENU)
2243 auto foundIt = m_currentHeaderDataMenus.find(id);
2244 if (foundIt != m_currentHeaderDataMenus.end())
2245 entry.resourceIDs.insert(foundIt->second + infotext);
2246 else
2247 entry.resourceIDs.insert(NumToStr(id) + infotext);
2249 else if (lpType == RT_RIBBON && infotext && wcsstr(infotext, L"ID") == infotext)
2250 entry.resourceIDs.insert(infotext);
2251 else
2252 entry.resourceIDs.insert(NumToStr(id) + infotext);
2255 bool CResModule::AdjustCheckSum(const std::wstring& resFile)
2257 HANDLE hFile = INVALID_HANDLE_VALUE;
2258 HANDLE hFileMapping = nullptr;
2259 PVOID pBaseAddress = nullptr;
2263 hFile = CreateFile(resFile.c_str(), FILE_READ_DATA | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
2264 if (hFile == INVALID_HANDLE_VALUE)
2265 throw GetLastError();
2267 hFileMapping = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, 0, nullptr);
2268 if (!hFileMapping)
2269 throw GetLastError();
2271 pBaseAddress = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
2272 if (!pBaseAddress)
2273 throw GetLastError();
2275 auto fileSize = GetFileSize(hFile, nullptr);
2276 if (fileSize == INVALID_FILE_SIZE)
2277 throw GetLastError();
2278 DWORD dwChecksum = 0;
2279 DWORD dwHeaderSum = 0;
2280 CheckSumMappedFile(pBaseAddress, fileSize, &dwHeaderSum, &dwChecksum);
2282 PIMAGE_DOS_HEADER pDOSHeader = static_cast<PIMAGE_DOS_HEADER>(pBaseAddress);
2283 if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
2284 throw GetLastError();
2286 PIMAGE_NT_HEADERS pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(static_cast<PBYTE>(pBaseAddress) + pDOSHeader->e_lfanew);
2287 if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
2288 throw GetLastError();
2290 SetLastError(ERROR_SUCCESS);
2292 DWORD* pChecksum = &(pNTHeader->OptionalHeader.CheckSum);
2293 *pChecksum = dwChecksum;
2295 UnmapViewOfFile(pBaseAddress);
2296 CloseHandle(hFileMapping);
2297 CloseHandle(hFile);
2299 catch (...)
2301 if (pBaseAddress)
2302 UnmapViewOfFile(pBaseAddress);
2303 if (hFileMapping)
2304 CloseHandle(hFileMapping);
2305 if (hFile != INVALID_HANDLE_VALUE)
2306 CloseHandle(hFile);
2307 return false;
2310 return true;