Provide (experimental) clang-format file
[TortoiseGit.git] / src / ResText / ResModule.cpp
blob517c1a9a2e03fa01cf7b8c77a1eeedc4b75968d2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2015-2017 - TortoiseGit
4 // Copyright (C) 2003-2008, 2010-2017 - 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 "Utils.h"
21 #include "UnicodeUtils.h"
22 #include "ResModule.h"
23 #include <regex>
24 #include <memory>
25 #include <fstream>
26 #include <string>
27 #include <algorithm>
28 #include <functional>
29 #include <locale>
30 #include <codecvt>
32 #pragma warning(push)
33 #pragma warning(disable: 4091) // 'typedef ': ignored on left of '' when no variable is declared
34 #include <Imagehlp.h>
35 #pragma warning(pop)
37 #pragma comment(lib, "Imagehlp.lib")
39 #ifndef RT_RIBBON
40 #define RT_RIBBON MAKEINTRESOURCE(28)
41 #endif
44 #define MYERROR {CUtils::Error(); return FALSE;}
46 static const WORD * AlignWORD(const WORD * pWord)
48 const WORD * res = pWord;
49 res += ((((UINT_PTR)pWord + 3) & ~3) - (UINT_PTR)pWord) / sizeof(WORD);
50 return res;
53 std::wstring NumToStr(INT_PTR num)
55 wchar_t buf[100];
56 swprintf_s(buf, L"%Id", num);
57 return buf;
60 CResModule::CResModule(void)
61 : m_bTranslatedStrings(0)
62 , m_bDefaultStrings(0)
63 , m_bTranslatedDialogStrings(0)
64 , m_bDefaultDialogStrings(0)
65 , m_bTranslatedMenuStrings(0)
66 , m_bDefaultMenuStrings(0)
67 , m_bTranslatedAcceleratorStrings(0)
68 , m_bDefaultAcceleratorStrings(0)
69 , m_bTranslatedRibbonTexts(0)
70 , m_bDefaultRibbonTexts(0)
71 , m_wTargetLang(0)
72 , m_hResDll(nullptr)
73 , m_hUpdateRes(nullptr)
74 , m_bQuiet(false)
75 , m_bRTL(false)
76 , m_bAdjustEOLs(false)
80 CResModule::~CResModule(void)
84 BOOL CResModule::ExtractResources(const std::vector<std::wstring>& filelist, LPCTSTR lpszPOFilePath, BOOL bNoUpdate, LPCTSTR lpszHeaderFile)
86 for (auto I = filelist.cbegin(); I != filelist.cend(); ++I)
88 std::wstring filepath = *I;
89 m_currentHeaderDataDialogs.clear();
90 m_currentHeaderDataMenus.clear();
91 m_currentHeaderDataStrings.clear();
92 auto starpos = I->find('*');
93 if (starpos != std::wstring::npos)
94 filepath = I->substr(0, starpos);
95 while (starpos != std::wstring::npos)
97 auto starposnext = I->find('*', starpos + 1);
98 std::wstring headerfile = I->substr(starpos + 1, starposnext - starpos - 1);
99 ScanHeaderFile(headerfile);
100 starpos = starposnext;
102 m_hResDll = LoadLibraryEx(filepath.c_str(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
103 if (!m_hResDll)
104 MYERROR;
106 size_t nEntries = m_StringEntries.size();
107 // fill in the std::map with all translatable entries
109 if (!m_bQuiet)
110 _ftprintf(stdout, L"Extracting StringTable....");
111 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (LONG_PTR)this);
112 if (!m_bQuiet)
113 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
114 nEntries = m_StringEntries.size();
116 if (!m_bQuiet)
117 _ftprintf(stdout, L"Extracting Dialogs........");
118 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (LONG_PTR)this);
119 if (!m_bQuiet)
120 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
121 nEntries = m_StringEntries.size();
123 if (!m_bQuiet)
124 _ftprintf(stdout, L"Extracting Menus..........");
125 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (LONG_PTR)this);
126 if (!m_bQuiet)
127 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
128 nEntries = m_StringEntries.size();
129 if (!m_bQuiet)
130 _ftprintf(stdout, L"Extracting Accelerators...");
131 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (LONG_PTR)this);
132 if (!m_bQuiet)
133 _ftprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size()-nEntries);
134 nEntries = m_StringEntries.size();
135 if (!m_bQuiet)
136 _ftprintf(stdout, L"Extracting Ribbons........");
137 EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameCallback, (LONG_PTR)this);
138 if (!m_bQuiet)
139 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
140 nEntries = m_StringEntries.size();
142 // parse a probably existing file and update the translations which are
143 // already done
144 m_StringEntries.ParseFile(lpszPOFilePath, !bNoUpdate, m_bAdjustEOLs);
146 FreeLibrary(m_hResDll);
149 // at last, save the new file
150 return m_StringEntries.SaveFile(lpszPOFilePath, lpszHeaderFile);
153 BOOL CResModule::ExtractResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszPoFilePath, BOOL bNoUpdate, LPCTSTR lpszHeaderFile)
155 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
156 if (!m_hResDll)
157 MYERROR;
159 size_t nEntries = 0;
160 // fill in the std::map with all translatable entries
162 if (!m_bQuiet)
163 _ftprintf(stdout, L"Extracting StringTable....");
164 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (LONG_PTR)this);
165 if (!m_bQuiet)
166 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size());
167 nEntries = m_StringEntries.size();
169 if (!m_bQuiet)
170 _ftprintf(stdout, L"Extracting Dialogs........");
171 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (LONG_PTR)this);
172 if (!m_bQuiet)
173 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
174 nEntries = m_StringEntries.size();
176 if (!m_bQuiet)
177 _ftprintf(stdout, L"Extracting Menus..........");
178 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (LONG_PTR)this);
179 if (!m_bQuiet)
180 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
181 nEntries = m_StringEntries.size();
183 if (!m_bQuiet)
184 _ftprintf(stdout, L"Extracting Accelerators...");
185 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (LONG_PTR)this);
186 if (!m_bQuiet)
187 _ftprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size()-nEntries);
188 nEntries = m_StringEntries.size();
190 // parse a probably existing file and update the translations which are
191 // already done
192 m_StringEntries.ParseFile(lpszPoFilePath, !bNoUpdate, m_bAdjustEOLs);
194 // at last, save the new file
195 if (!m_StringEntries.SaveFile(lpszPoFilePath, lpszHeaderFile))
196 goto DONE_ERROR;
198 FreeLibrary(m_hResDll);
200 AdjustCheckSum(sDestFile);
202 return TRUE;
204 DONE_ERROR:
205 if (m_hResDll)
206 FreeLibrary(m_hResDll);
207 return FALSE;
210 void CResModule::RemoveSignatures(LPCTSTR lpszDestLangDllPath)
212 // Remove any signatures in the file:
213 // if we don't remove it here, the signature will be invalid after
214 // we modify this file, and the signtool.exe will throw an error and refuse to sign it again.
215 auto hFile = CreateFile(lpszDestLangDllPath, FILE_READ_DATA | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
216 if (hFile == INVALID_HANDLE_VALUE)
218 CUtils::Error();
219 return;
222 DWORD certcount = 0;
223 DWORD indices[100];
224 ImageEnumerateCertificates(hFile, CERT_SECTION_TYPE_ANY, &certcount, indices, _countof(indices));
226 for (DWORD i = 0; i < certcount; ++i)
227 ImageRemoveCertificate(hFile, i);
229 CloseHandle(hFile);
232 BOOL CResModule::CreateTranslatedResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszDestLangDllPath, LPCTSTR lpszPOFilePath)
234 if (!CopyFile(lpszSrcLangDllPath, lpszDestLangDllPath, FALSE))
235 MYERROR;
237 RemoveSignatures(lpszDestLangDllPath);
239 int count = 0;
242 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_IGNORE_CODE_AUTHZ_LEVEL);
243 if (!m_hResDll)
244 Sleep(100);
245 count++;
246 } while (!m_hResDll && (count < 10));
248 if (!m_hResDll)
249 MYERROR;
251 sDestFile = std::wstring(lpszDestLangDllPath);
253 // get all translated strings
254 if (!m_StringEntries.ParseFile(lpszPOFilePath, FALSE, m_bAdjustEOLs))
255 goto DONE_ERROR;
256 m_bTranslatedStrings = 0;
257 m_bDefaultStrings = 0;
258 m_bTranslatedDialogStrings = 0;
259 m_bDefaultDialogStrings = 0;
260 m_bTranslatedMenuStrings = 0;
261 m_bDefaultMenuStrings = 0;
262 m_bTranslatedAcceleratorStrings = 0;
263 m_bDefaultAcceleratorStrings = 0;
265 BOOL bRes = FALSE;
266 count = 0;
269 m_hUpdateRes = BeginUpdateResource(sDestFile.c_str(), FALSE);
270 if (!m_hUpdateRes)
271 Sleep(100);
272 count++;
273 } while (!m_hUpdateRes && (count < 10));
275 if (!m_hUpdateRes)
276 MYERROR;
279 if (!m_bQuiet)
280 _ftprintf(stdout, L"Translating StringTable...");
281 bRes = EnumResourceNames(m_hResDll, RT_STRING, EnumResNameWriteCallback, (LONG_PTR)this);
282 if (!m_bQuiet)
283 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedStrings, m_bDefaultStrings);
285 if (!m_bQuiet)
286 _ftprintf(stdout, L"Translating Dialogs.......");
287 bRes = EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameWriteCallback, (LONG_PTR)this);
288 if (!m_bQuiet)
289 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedDialogStrings, m_bDefaultDialogStrings);
291 if (!m_bQuiet)
292 _ftprintf(stdout, L"Translating Menus.........");
293 bRes = EnumResourceNames(m_hResDll, RT_MENU, EnumResNameWriteCallback, (LONG_PTR)this);
294 if (!m_bQuiet)
295 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedMenuStrings, m_bDefaultMenuStrings);
297 if (!m_bQuiet)
298 _ftprintf(stdout, L"Translating Accelerators..");
299 bRes = EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameWriteCallback, (LONG_PTR)this);
300 if (!m_bQuiet)
301 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedAcceleratorStrings, m_bDefaultAcceleratorStrings);
303 if (!m_bQuiet)
304 _ftprintf(stdout, L"Translating Ribbons.......");
305 bRes = EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameWriteCallback, (LONG_PTR)this);
306 if (!m_bQuiet)
307 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedRibbonTexts, m_bDefaultRibbonTexts);
308 bRes = TRUE;
309 if (!EndUpdateResource(m_hUpdateRes, !bRes))
310 MYERROR;
312 FreeLibrary(m_hResDll);
313 return TRUE;
314 DONE_ERROR:
315 if (m_hResDll)
316 FreeLibrary(m_hResDll);
317 return FALSE;
320 BOOL CResModule::ExtractString(LPCTSTR lpszType)
322 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_STRING);
323 HGLOBAL hglStringTable;
324 LPWSTR p;
326 if (!hrsrc)
327 MYERROR;
328 hglStringTable = LoadResource(m_hResDll, hrsrc);
330 if (!hglStringTable)
331 goto DONE_ERROR;
332 p = (LPWSTR)LockResource(hglStringTable);
334 if (!p)
335 goto DONE_ERROR;
336 /* [Block of 16 strings. The strings are Pascal style with a WORD
337 length preceding the string. 16 strings are always written, even
338 if not all slots are full. Any slots in the block with no string
339 have a zero WORD for the length.]
342 //first check how much memory we need
343 LPWSTR pp = p;
344 for (int i=0; i<16; ++i)
346 int len = GET_WORD(pp);
347 pp++;
348 std::wstring msgid = std::wstring(pp, len);
349 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
350 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
351 wcscpy(pBuf, msgid.c_str());
352 CUtils::StringExtend(pBuf);
354 if (pBuf[0])
356 std::wstring str = std::wstring(pBuf);
357 RESOURCEENTRY entry = m_StringEntries[str];
358 InsertResourceIDs(RT_STRING, 0, entry, ((INT_PTR)lpszType - 1) * 16 + i, L"");
359 if (wcschr(str.c_str(), '%'))
360 entry.flag = L"#, c-format";
361 m_StringEntries[str] = entry;
363 delete [] pBuf;
364 pp += len;
366 UnlockResource(hglStringTable);
367 FreeResource(hglStringTable);
368 return TRUE;
369 DONE_ERROR:
370 UnlockResource(hglStringTable);
371 FreeResource(hglStringTable);
372 MYERROR;
375 BOOL CResModule::ReplaceString(LPCTSTR lpszType, WORD wLanguage)
377 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_STRING, lpszType, wLanguage);
378 HGLOBAL hglStringTable;
379 LPWSTR p;
381 if (!hrsrc)
382 MYERROR;
383 hglStringTable = LoadResource(m_hResDll, hrsrc);
385 if (!hglStringTable)
386 goto DONE_ERROR;
387 p = (LPWSTR)LockResource(hglStringTable);
389 if (!p)
390 goto DONE_ERROR;
391 /* [Block of 16 strings. The strings are Pascal style with a WORD
392 length preceding the string. 16 strings are always written, even
393 if not all slots are full. Any slots in the block with no string
394 have a zero WORD for the length.]
397 //first check how much memory we need
398 size_t nMem = 0;
399 LPWSTR pp = p;
400 for (int i=0; i<16; ++i)
402 nMem++;
403 size_t len = GET_WORD(pp);
404 pp++;
405 std::wstring msgid = std::wstring(pp, len);
406 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
407 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
408 wcscpy(pBuf, msgid.c_str());
409 CUtils::StringExtend(pBuf);
410 msgid = std::wstring(pBuf);
412 RESOURCEENTRY resEntry;
413 resEntry = m_StringEntries[msgid];
414 wcscpy(pBuf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
415 ReplaceWithRegex(pBuf);
416 CUtils::StringCollapse(pBuf);
417 size_t newlen = wcslen(pBuf);
418 if (newlen)
419 nMem += newlen;
420 else
421 nMem += len;
422 pp += len;
423 delete [] pBuf;
426 WORD * newTable = new WORD[nMem + (nMem % 2)];
427 SecureZeroMemory(newTable, (nMem + (nMem % 2))*2);
429 size_t index = 0;
430 for (int i=0; i<16; ++i)
432 int len = GET_WORD(p);
433 p++;
434 std::wstring msgid = std::wstring(p, len);
435 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
436 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
437 wcscpy(pBuf, msgid.c_str());
438 CUtils::StringExtend(pBuf);
439 msgid = std::wstring(pBuf);
441 RESOURCEENTRY resEntry;
442 resEntry = m_StringEntries[msgid];
443 wcscpy(pBuf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
444 ReplaceWithRegex(pBuf);
445 CUtils::StringCollapse(pBuf);
446 size_t newlen = wcslen(pBuf);
447 if (newlen)
449 newTable[index++] = (WORD)newlen;
450 wcsncpy((wchar_t *)&newTable[index], pBuf, newlen);
451 index += newlen;
452 m_bTranslatedStrings++;
454 else
456 newTable[index++] = (WORD)len;
457 if (len)
458 wcsncpy((wchar_t *)&newTable[index], p, len);
459 index += len;
460 if (len)
461 m_bDefaultStrings++;
463 p += len;
464 delete [] pBuf;
467 if (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newTable, (DWORD)(nMem + (nMem % 2))*2))
469 delete [] newTable;
470 goto DONE_ERROR;
473 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, wLanguage, nullptr, 0)))
475 delete [] newTable;
476 goto DONE_ERROR;
478 delete [] newTable;
479 UnlockResource(hglStringTable);
480 FreeResource(hglStringTable);
481 return TRUE;
482 DONE_ERROR:
483 UnlockResource(hglStringTable);
484 FreeResource(hglStringTable);
485 MYERROR;
488 BOOL CResModule::ExtractMenu(LPCTSTR lpszType)
490 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_MENU);
491 HGLOBAL hglMenuTemplate;
492 WORD version, offset;
493 const WORD *p, *p0;
495 if (!hrsrc)
496 MYERROR;
498 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
500 if (!hglMenuTemplate)
501 MYERROR;
503 p = (const WORD*)LockResource(hglMenuTemplate);
505 if (!p)
506 MYERROR;
508 // Standard MENU resource
509 //struct MenuHeader {
510 // WORD wVersion; // Currently zero
511 // WORD cbHeaderSize; // Also zero
512 //};
514 // MENUEX resource
515 //struct MenuExHeader {
516 // WORD wVersion; // One
517 // WORD wOffset;
518 // DWORD dwHelpId;
519 //};
520 p0 = p;
521 version = GET_WORD(p);
523 p++;
525 switch (version)
527 case 0:
529 offset = GET_WORD(p);
530 p += offset;
531 p++;
532 if (!ParseMenuResource(p))
533 goto DONE_ERROR;
535 break;
536 case 1:
538 offset = GET_WORD(p);
539 p++;
540 //dwHelpId = GET_DWORD(p);
541 if (!ParseMenuExResource(p0 + offset))
542 goto DONE_ERROR;
544 break;
545 default:
546 goto DONE_ERROR;
549 UnlockResource(hglMenuTemplate);
550 FreeResource(hglMenuTemplate);
551 return TRUE;
553 DONE_ERROR:
554 UnlockResource(hglMenuTemplate);
555 FreeResource(hglMenuTemplate);
556 MYERROR;
559 BOOL CResModule::ReplaceMenu(LPCTSTR lpszType, WORD wLanguage)
561 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_MENU, lpszType, wLanguage);
562 HGLOBAL hglMenuTemplate;
563 WORD version, offset;
564 LPWSTR p;
565 WORD *p0;
567 if (!hrsrc)
568 MYERROR; //just the language wasn't found
570 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
572 if (!hglMenuTemplate)
573 MYERROR;
575 p = (LPWSTR)LockResource(hglMenuTemplate);
577 if (!p)
578 MYERROR;
580 //struct MenuHeader {
581 // WORD wVersion; // Currently zero
582 // WORD cbHeaderSize; // Also zero
583 //};
585 // MENUEX resource
586 //struct MenuExHeader {
587 // WORD wVersion; // One
588 // WORD wOffset;
589 // DWORD dwHelpId;
590 //};
591 p0 = (WORD *)p;
592 version = GET_WORD(p);
594 p++;
596 switch (version)
598 case 0:
600 offset = GET_WORD(p);
601 p += offset;
602 p++;
603 size_t nMem = 0;
604 if (!CountMemReplaceMenuResource((WORD*)p, &nMem, nullptr))
605 goto DONE_ERROR;
606 WORD * newMenu = new WORD[nMem + (nMem % 2)+2];
607 SecureZeroMemory(newMenu, (nMem + (nMem % 2)+2)*2);
608 size_t index = 2; // MenuHeader has 2 WORDs zero
609 if (!CountMemReplaceMenuResource((WORD *)p, &index, newMenu))
611 delete [] newMenu;
612 goto DONE_ERROR;
615 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2)+2)*2))
617 delete [] newMenu;
618 goto DONE_ERROR;
621 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
623 delete [] newMenu;
624 goto DONE_ERROR;
626 delete [] newMenu;
628 break;
629 case 1:
631 offset = GET_WORD(p);
632 p++;
633 //dwHelpId = GET_DWORD(p);
634 size_t nMem = 0;
635 if (!CountMemReplaceMenuExResource((WORD*)(p0 + offset), &nMem, nullptr))
636 goto DONE_ERROR;
637 WORD * newMenu = new WORD[nMem + (nMem % 2) + 4];
638 SecureZeroMemory(newMenu, (nMem + (nMem % 2) + 4) * 2);
639 CopyMemory(newMenu, p0, 2 * sizeof(WORD) + sizeof(DWORD));
640 size_t index = 4; // MenuExHeader has 2 x WORD + 1 x DWORD
641 if (!CountMemReplaceMenuExResource((WORD *)(p0 + offset), &index, newMenu))
643 delete [] newMenu;
644 goto DONE_ERROR;
647 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2) + 4) * 2))
649 delete [] newMenu;
650 goto DONE_ERROR;
653 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
655 delete [] newMenu;
656 goto DONE_ERROR;
658 delete [] newMenu;
660 break;
661 default:
662 goto DONE_ERROR;
665 UnlockResource(hglMenuTemplate);
666 FreeResource(hglMenuTemplate);
667 return TRUE;
669 DONE_ERROR:
670 UnlockResource(hglMenuTemplate);
671 FreeResource(hglMenuTemplate);
672 MYERROR;
675 const WORD* CResModule::ParseMenuResource(const WORD * res)
677 WORD flags;
678 WORD id = 0;
680 //struct PopupMenuItem {
681 // WORD fItemFlags;
682 // WCHAR szItemText[];
683 //};
684 //struct NormalMenuItem {
685 // WORD fItemFlags;
686 // WORD wMenuID;
687 // WCHAR szItemText[];
688 //};
692 flags = GET_WORD(res);
693 res++;
694 if (!(flags & MF_POPUP))
696 id = GET_WORD(res); //normal menu item
697 res++;
699 else
700 id = (WORD)-1; //popup menu item
702 LPCWSTR str = (LPCWSTR)res;
703 size_t l = wcslen(str)+1;
704 res += l;
706 if (flags & MF_POPUP)
708 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
709 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
710 wcscpy(pBuf, str);
711 CUtils::StringExtend(pBuf);
713 std::wstring wstr = std::wstring(pBuf);
714 RESOURCEENTRY entry = m_StringEntries[wstr];
715 if (id)
716 InsertResourceIDs(RT_MENU, 0, entry, id, L" - PopupMenu");
718 m_StringEntries[wstr] = entry;
719 delete [] pBuf;
721 if ((res = ParseMenuResource(res))==0)
722 return nullptr;
724 else if (id != 0)
726 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
727 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
728 wcscpy(pBuf, str);
729 CUtils::StringExtend(pBuf);
731 std::wstring wstr = std::wstring(pBuf);
732 RESOURCEENTRY entry = m_StringEntries[wstr];
733 InsertResourceIDs(RT_MENU, 0, entry, id, L" - Menu");
735 TCHAR szTempBuf[1024] = { 0 };
736 swprintf(szTempBuf, L"#: MenuEntry; ID:%u", id);
737 MENUENTRY menu_entry;
738 menu_entry.wID = id;
739 menu_entry.reference = szTempBuf;
740 menu_entry.msgstr = wstr;
742 m_StringEntries[wstr] = entry;
743 m_MenuEntries[id] = menu_entry;
744 delete [] pBuf;
746 } while (!(flags & MF_END));
747 return res;
750 const WORD* CResModule::CountMemReplaceMenuResource(const WORD * res, size_t * wordcount, WORD * newMenu)
752 WORD flags;
753 WORD id = 0;
755 //struct PopupMenuItem {
756 // WORD fItemFlags;
757 // WCHAR szItemText[];
758 //};
759 //struct NormalMenuItem {
760 // WORD fItemFlags;
761 // WORD wMenuID;
762 // WCHAR szItemText[];
763 //};
767 flags = GET_WORD(res);
768 res++;
769 if (!newMenu)
770 (*wordcount)++;
771 else
772 newMenu[(*wordcount)++] = flags;
773 if (!(flags & MF_POPUP))
775 id = GET_WORD(res); //normal menu item
776 res++;
777 if (!newMenu)
778 (*wordcount)++;
779 else
780 newMenu[(*wordcount)++] = id;
782 else
783 id = (WORD)-1; //popup menu item
785 if (flags & MF_POPUP)
787 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
788 res += wcslen((LPCWSTR)res) + 1;
790 if ((res = CountMemReplaceMenuResource(res, wordcount, newMenu))==0)
791 return nullptr;
793 else if (id != 0)
795 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
796 res += wcslen((LPCWSTR)res) + 1;
798 else
800 if (newMenu)
801 wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res);
802 (*wordcount) += wcslen((LPCWSTR)res) + 1;
803 res += wcslen((LPCWSTR)res) + 1;
805 } while (!(flags & MF_END));
806 return res;
809 const WORD* CResModule::ParseMenuExResource(const WORD * res)
811 WORD bResInfo;
813 //struct MenuExItem {
814 // DWORD dwType;
815 // DWORD dwState;
816 // DWORD menuId;
817 // WORD bResInfo;
818 // WCHAR szText[];
819 // DWORD dwHelpId; - Popup menu only
820 //};
824 DWORD dwType = GET_DWORD(res);
825 res += 2;
826 //dwState = GET_DWORD(res);
827 res += 2;
828 DWORD menuId = GET_DWORD(res);
829 res += 2;
830 bResInfo = GET_WORD(res);
831 res++;
833 LPCWSTR str = (LPCWSTR)res;
834 size_t l = wcslen(str)+1;
835 res += l;
836 // Align to DWORD boundary
837 res = AlignWORD(res);
839 if (dwType & MFT_SEPARATOR)
840 continue;
842 if (bResInfo & 0x01)
844 // Popup menu - note this can also have a non-zero ID
845 if (menuId == 0)
846 menuId = (WORD)-1;
847 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
848 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
849 wcscpy(pBuf, str);
850 CUtils::StringExtend(pBuf);
852 std::wstring wstr = std::wstring(pBuf);
853 RESOURCEENTRY entry = m_StringEntries[wstr];
854 // Popup has a DWORD help entry on a DWORD boundary - skip over it
855 res += 2;
857 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - PopupMenuEx");
858 TCHAR szTempBuf[1024] = { 0 };
859 swprintf(szTempBuf, L"#: MenuExPopupEntry; ID:%lu", menuId);
860 MENUENTRY menu_entry;
861 menu_entry.wID = (WORD)menuId;
862 menu_entry.reference = szTempBuf;
863 menu_entry.msgstr = wstr;
864 m_StringEntries[wstr] = entry;
865 m_MenuEntries[(WORD)menuId] = menu_entry;
866 delete [] pBuf;
868 if ((res = ParseMenuExResource(res)) == 0)
869 return nullptr;
870 } else if (menuId != 0)
872 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
873 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
874 wcscpy(pBuf, str);
875 CUtils::StringExtend(pBuf);
877 std::wstring wstr = std::wstring(pBuf);
878 RESOURCEENTRY entry = m_StringEntries[wstr];
879 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - MenuEx");
881 TCHAR szTempBuf[1024] = { 0 };
882 swprintf(szTempBuf, L"#: MenuExEntry; ID:%lu", menuId);
883 MENUENTRY menu_entry;
884 menu_entry.wID = (WORD)menuId;
885 menu_entry.reference = szTempBuf;
886 menu_entry.msgstr = wstr;
887 m_StringEntries[wstr] = entry;
888 m_MenuEntries[(WORD)menuId] = menu_entry;
889 delete [] pBuf;
891 } while (!(bResInfo & 0x80));
892 return res;
895 const WORD* CResModule::CountMemReplaceMenuExResource(const WORD * res, size_t * wordcount, WORD * newMenu)
897 WORD bResInfo;
899 //struct MenuExItem {
900 // DWORD dwType;
901 // DWORD dwState;
902 // DWORD menuId;
903 // WORD bResInfo;
904 // WCHAR szText[];
905 // DWORD dwHelpId; - Popup menu only
906 //};
910 WORD * p0 = (WORD *)res;
911 DWORD dwType = GET_DWORD(res);
912 res += 2;
913 //dwState = GET_DWORD(res);
914 res += 2;
915 DWORD menuId = GET_DWORD(res);
916 res += 2;
917 bResInfo = GET_WORD(res);
918 res++;
920 if (newMenu)
921 CopyMemory(&newMenu[*wordcount], p0, 7 * sizeof(WORD));
923 (*wordcount) += 7;
925 if (dwType & MFT_SEPARATOR) {
926 // Align to DWORD
927 (*wordcount)++;
928 res++;
929 continue;
932 if (bResInfo & 0x01)
934 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
935 res += wcslen((LPCWSTR)res) + 1;
936 // Align to DWORD
937 res = AlignWORD(res);
938 if ((*wordcount) & 0x01)
939 (*wordcount)++;
941 if (newMenu)
942 CopyMemory(&newMenu[*wordcount], res, sizeof(DWORD)); // Copy Help ID
944 res += 2;
945 (*wordcount) += 2;
947 if ((res = CountMemReplaceMenuExResource(res, wordcount, newMenu)) == 0)
948 return nullptr;
950 else if (menuId != 0)
952 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
953 res += wcslen((LPCWSTR)res) + 1;
955 else
957 if (newMenu)
958 wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res);
959 (*wordcount) += wcslen((LPCWSTR)res) + 1;
960 res += wcslen((LPCWSTR)res) + 1;
962 // Align to DWORD
963 res = AlignWORD(res);
964 if ((*wordcount) & 0x01)
965 (*wordcount)++;
966 } while (!(bResInfo & 0x80));
967 return res;
970 BOOL CResModule::ExtractAccelerator(LPCTSTR lpszType)
972 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_ACCELERATOR);
973 HGLOBAL hglAccTable;
974 WORD fFlags, wAnsi, wID;
975 const WORD* p;
976 bool bEnd(false);
978 if (!hrsrc)
979 MYERROR;
981 hglAccTable = LoadResource(m_hResDll, hrsrc);
983 if (!hglAccTable)
984 goto DONE_ERROR;
986 p = (const WORD*)LockResource(hglAccTable);
988 if (!p)
989 MYERROR;
992 struct ACCELTABLEENTRY
994 WORD fFlags; FVIRTKEY, FSHIFT, FCONTROL, FALT, 0x80 - Last in a table
995 WORD wAnsi; ANSI character
996 WORD wId; Keyboard accelerator passed to windows
997 WORD padding; # bytes added to ensure aligned to DWORD boundary
1003 fFlags = GET_WORD(p);
1004 p++;
1005 wAnsi = GET_WORD(p);
1006 p++;
1007 wID = GET_WORD(p);
1008 p++;
1009 p++; // Skip over padding
1011 if ((fFlags & 0x80) == 0x80)
1012 { // 0x80
1013 bEnd = true;
1016 if ((wAnsi < 0x30) ||
1017 (wAnsi > 0x5A) ||
1018 (wAnsi >= 0x3A && wAnsi <= 0x40))
1019 continue;
1021 auto pBuf = std::make_unique<WCHAR[]>(1024);
1022 auto pBuf2 = std::make_unique<WCHAR[]>(1024);
1023 SecureZeroMemory(pBuf.get(), 1024 * sizeof(WCHAR));
1024 SecureZeroMemory(pBuf2.get(), 1024 * sizeof(WCHAR));
1026 // include the menu ID in the msgid to make sure that 'duplicate'
1027 // accelerator keys are listed in the po-file.
1028 // without this, we would get entries like this:
1029 //#. Accelerator Entry for Menu ID:32809; '&Filter'
1030 //#. Accelerator Entry for Menu ID:57636; '&Find'
1031 //#: Corresponding Menu ID:32771; '&Find'
1032 //msgid "V C +F"
1033 //msgstr ""
1035 // Since "filter" and "find" are most likely translated to words starting
1036 // with different letters, we need to have a separate accelerator entry
1037 // for each of those
1038 swprintf(pBuf.get(), L"ID:%u:", wID);
1040 // EXACTLY 5 characters long "ACS+X"
1041 // V = Virtual key (or blank if not used)
1042 // A = Alt key (or blank if not used)
1043 // C = Ctrl key (or blank if not used)
1044 // S = Shift key (or blank if not used)
1045 // X = upper case character
1046 // e.g. "V CS+Q" == Ctrl + Shift + 'Q'
1047 if ((fFlags & FVIRTKEY) == FVIRTKEY) // 0x01
1048 wcscat(pBuf.get(), L"V");
1049 else
1050 wcscat(pBuf.get(), L" ");
1052 if ((fFlags & FALT) == FALT) // 0x10
1053 wcscat(pBuf.get(), L"A");
1054 else
1055 wcscat(pBuf.get(), L" ");
1057 if ((fFlags & FCONTROL) == FCONTROL) // 0x08
1058 wcscat(pBuf.get(), L"C");
1059 else
1060 wcscat(pBuf.get(), L" ");
1062 if ((fFlags & FSHIFT) == FSHIFT) // 0x04
1063 wcscat(pBuf.get(), L"S");
1064 else
1065 wcscat(pBuf.get(), L" ");
1067 swprintf(pBuf2.get(), L"%s+%c", pBuf.get(), wAnsi);
1069 std::wstring wstr = std::wstring(pBuf2.get());
1070 RESOURCEENTRY AKey_entry = m_StringEntries[wstr];
1072 TCHAR szTempBuf[1024] = { 0 };
1073 SecureZeroMemory(szTempBuf, sizeof (szTempBuf));
1074 std::wstring wmenu;
1075 pME_iter = m_MenuEntries.find(wID);
1076 if (pME_iter != m_MenuEntries.end())
1078 wmenu = pME_iter->second.msgstr;
1080 swprintf(szTempBuf, L"#. Accelerator Entry for Menu ID:%u; '%s'", wID, wmenu.c_str());
1081 AKey_entry.automaticcomments.push_back(std::wstring(szTempBuf));
1083 m_StringEntries[wstr] = AKey_entry;
1084 } while (!bEnd);
1086 UnlockResource(hglAccTable);
1087 FreeResource(hglAccTable);
1088 return TRUE;
1090 DONE_ERROR:
1091 UnlockResource(hglAccTable);
1092 FreeResource(hglAccTable);
1093 MYERROR;
1096 BOOL CResModule::ReplaceAccelerator(LPCTSTR lpszType, WORD wLanguage)
1098 LPACCEL lpaccelNew; // pointer to new accelerator table
1099 HACCEL haccelOld; // handle to old accelerator table
1100 int cAccelerators; // number of accelerators in table
1101 HGLOBAL hglAccTableNew;
1102 const WORD* p;
1103 int i;
1105 haccelOld = LoadAccelerators(m_hResDll, lpszType);
1107 if (!haccelOld)
1108 MYERROR;
1110 cAccelerators = CopyAcceleratorTable(haccelOld, nullptr, 0);
1112 lpaccelNew = (LPACCEL) LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL));
1114 if (!lpaccelNew)
1115 MYERROR;
1117 CopyAcceleratorTable(haccelOld, lpaccelNew, cAccelerators);
1119 // Find the accelerator that the user modified
1120 // and change its flags and virtual-key code
1121 // as appropriate.
1123 BYTE xfVirt;
1124 wchar_t xkey;
1125 static const size_t BufferSize = 1024;
1126 auto pBuf = std::make_unique<WCHAR[]>(BufferSize);
1127 auto pBuf2 = std::make_unique<WCHAR[]>(BufferSize);
1128 for (i = 0; i < cAccelerators; i++)
1130 if ((lpaccelNew[i].key < 0x30) ||
1131 (lpaccelNew[i].key > 0x5A) ||
1132 (lpaccelNew[i].key >= 0x3A && lpaccelNew[i].key <= 0x40))
1133 continue;
1135 SecureZeroMemory(pBuf.get(), 1024 * sizeof(WCHAR));
1136 SecureZeroMemory(pBuf2.get(), 1024 * sizeof(WCHAR));
1138 swprintf(pBuf.get(), L"ID:%d:", lpaccelNew[i].cmd);
1140 // get original key combination
1141 if ((lpaccelNew[i].fVirt & FVIRTKEY) == FVIRTKEY) // 0x01
1142 wcscat(pBuf.get(), L"V");
1143 else
1144 wcscat(pBuf.get(), L" ");
1146 if ((lpaccelNew[i].fVirt & FALT) == FALT) // 0x10
1147 wcscat(pBuf.get(), L"A");
1148 else
1149 wcscat(pBuf.get(), L" ");
1151 if ((lpaccelNew[i].fVirt & FCONTROL) == FCONTROL) // 0x08
1152 wcscat(pBuf.get(), L"C");
1153 else
1154 wcscat(pBuf.get(), L" ");
1156 if ((lpaccelNew[i].fVirt & FSHIFT) == FSHIFT) // 0x04
1157 wcscat(pBuf.get(), L"S");
1158 else
1159 wcscat(pBuf.get(), L" ");
1161 swprintf(pBuf2.get(), L"%s+%c", pBuf.get(), lpaccelNew[i].key);
1163 // Is it there?
1164 std::map<std::wstring, RESOURCEENTRY>::iterator pAK_iter = m_StringEntries.find(pBuf2.get());
1165 if (pAK_iter != m_StringEntries.end())
1167 m_bTranslatedAcceleratorStrings++;
1168 xfVirt = 0;
1169 xkey = 0;
1170 std::wstring wtemp = pAK_iter->second.msgstr;
1171 wtemp = wtemp.substr(wtemp.find_last_of(':')+1);
1172 if (wtemp.size() != 6)
1173 continue;
1174 if (wtemp.compare(0, 1, L"V") == 0)
1175 xfVirt |= FVIRTKEY;
1176 else if (wtemp.compare(0, 1, L" ") != 0)
1177 continue; // not a space - user must have made a mistake when translating
1178 if (wtemp.compare(1, 1, L"A") == 0)
1179 xfVirt |= FALT;
1180 else if (wtemp.compare(1, 1, L" ") != 0)
1181 continue; // not a space - user must have made a mistake when translating
1182 if (wtemp.compare(2, 1, L"C") == 0)
1183 xfVirt |= FCONTROL;
1184 else if (wtemp.compare(2, 1, L" ") != 0)
1185 continue; // not a space - user must have made a mistake when translating
1186 if (wtemp.compare(3, 1, L"S") == 0)
1187 xfVirt |= FSHIFT;
1188 else if (wtemp.compare(3, 1, L" ") != 0)
1189 continue; // not a space - user must have made a mistake when translating
1190 if (wtemp.compare(4, 1, L"+") == 0)
1192 swscanf(wtemp.substr(5, 1).c_str(), L"%c", &xkey);
1193 lpaccelNew[i].fVirt = xfVirt;
1194 lpaccelNew[i].key = (DWORD)xkey;
1197 else
1198 m_bDefaultAcceleratorStrings++;
1202 // Create the new accelerator table
1203 hglAccTableNew = LocalAlloc(LPTR, cAccelerators * 4 * sizeof(WORD));
1204 if (!hglAccTableNew)
1205 goto DONE_ERROR;
1206 p = (WORD *)hglAccTableNew;
1207 lpaccelNew[cAccelerators-1].fVirt |= 0x80;
1208 for (i = 0; i < cAccelerators; i++)
1210 memcpy((void *)p, &lpaccelNew[i].fVirt, 1);
1211 p++;
1212 memcpy((void *)p, &lpaccelNew[i].key, sizeof(WORD));
1213 p++;
1214 memcpy((void *)p, &lpaccelNew[i].cmd, sizeof(WORD));
1215 p++;
1216 p++;
1219 if (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType,
1220 (m_wTargetLang ? m_wTargetLang : wLanguage), hglAccTableNew /* haccelNew*/, cAccelerators * 4 * sizeof(WORD)))
1222 goto DONE_ERROR;
1225 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType, wLanguage, nullptr, 0)))
1227 goto DONE_ERROR;
1230 LocalFree(hglAccTableNew);
1231 LocalFree(lpaccelNew);
1232 return TRUE;
1234 DONE_ERROR:
1235 LocalFree(hglAccTableNew);
1236 LocalFree(lpaccelNew);
1237 MYERROR;
1240 BOOL CResModule::ExtractDialog(LPCTSTR lpszType)
1242 const WORD* lpDlg;
1243 const WORD* lpDlgItem;
1244 DIALOGINFO dlg;
1245 DLGITEMINFO dlgItem;
1246 WORD bNumControls;
1247 HRSRC hrsrc;
1248 HGLOBAL hGlblDlgTemplate;
1250 hrsrc = FindResource(m_hResDll, lpszType, RT_DIALOG);
1252 if (!hrsrc)
1253 MYERROR;
1255 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1256 if (!hGlblDlgTemplate)
1257 MYERROR;
1259 lpDlg = (const WORD*) LockResource(hGlblDlgTemplate);
1261 if (!lpDlg)
1262 MYERROR;
1264 lpDlgItem = (const WORD*) GetDialogInfo(lpDlg, &dlg);
1265 bNumControls = dlg.nbItems;
1267 if (dlg.caption)
1269 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
1270 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
1271 wcscpy(pBuf, dlg.caption);
1272 CUtils::StringExtend(pBuf);
1274 std::wstring wstr = std::wstring(pBuf);
1275 RESOURCEENTRY entry = m_StringEntries[wstr];
1276 InsertResourceIDs(RT_DIALOG, (INT_PTR)lpszType, entry, (INT_PTR)lpszType, L"");
1278 m_StringEntries[wstr] = entry;
1279 delete [] pBuf;
1282 while (bNumControls-- != 0)
1284 TCHAR szTitle[500] = { 0 };
1285 SecureZeroMemory(szTitle, sizeof(szTitle));
1286 BOOL bCode;
1288 lpDlgItem = GetControlInfo((WORD *) lpDlgItem, &dlgItem, dlg.dialogEx, &bCode);
1290 if (bCode == FALSE)
1291 wcsncpy(szTitle, dlgItem.windowName, _countof(szTitle) - 1);
1293 if (szTitle[0])
1295 CUtils::StringExtend(szTitle);
1297 std::wstring wstr = std::wstring(szTitle);
1298 RESOURCEENTRY entry = m_StringEntries[wstr];
1299 InsertResourceIDs(RT_DIALOG, (INT_PTR)lpszType, entry, dlgItem.id, L"");
1301 m_StringEntries[wstr] = entry;
1305 UnlockResource(hGlblDlgTemplate);
1306 FreeResource(hGlblDlgTemplate);
1307 return (TRUE);
1310 BOOL CResModule::ReplaceDialog(LPCTSTR lpszType, WORD wLanguage)
1312 const WORD* lpDlg;
1313 HRSRC hrsrc;
1314 HGLOBAL hGlblDlgTemplate;
1316 hrsrc = FindResourceEx(m_hResDll, RT_DIALOG, lpszType, wLanguage);
1318 if (!hrsrc)
1319 MYERROR;
1321 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1323 if (!hGlblDlgTemplate)
1324 MYERROR;
1326 lpDlg = (WORD *) LockResource(hGlblDlgTemplate);
1328 if (!lpDlg)
1329 MYERROR;
1331 size_t nMem = 0;
1332 const WORD * p = lpDlg;
1333 if (!CountMemReplaceDialogResource(p, &nMem, nullptr))
1334 goto DONE_ERROR;
1335 WORD * newDialog = new WORD[nMem + (nMem % 2)];
1336 SecureZeroMemory(newDialog, (nMem + (nMem % 2))*2);
1338 size_t index = 0;
1339 if (!CountMemReplaceDialogResource(lpDlg, &index, newDialog))
1341 delete [] newDialog;
1342 goto DONE_ERROR;
1345 if (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newDialog, (DWORD)(nMem + (nMem % 2))*2))
1347 delete [] newDialog;
1348 goto DONE_ERROR;
1351 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, wLanguage, nullptr, 0)))
1353 delete [] newDialog;
1354 goto DONE_ERROR;
1357 delete [] newDialog;
1358 UnlockResource(hGlblDlgTemplate);
1359 FreeResource(hGlblDlgTemplate);
1360 return TRUE;
1362 DONE_ERROR:
1363 UnlockResource(hGlblDlgTemplate);
1364 FreeResource(hGlblDlgTemplate);
1365 MYERROR;
1368 const WORD* CResModule::GetDialogInfo(const WORD * pTemplate, LPDIALOGINFO lpDlgInfo) const
1370 const WORD* p = (const WORD *)pTemplate;
1372 lpDlgInfo->style = GET_DWORD(p);
1373 p += 2;
1375 if (lpDlgInfo->style == 0xffff0001) // DIALOGEX resource
1377 lpDlgInfo->dialogEx = TRUE;
1378 lpDlgInfo->helpId = GET_DWORD(p);
1379 p += 2;
1380 lpDlgInfo->exStyle = GET_DWORD(p);
1381 p += 2;
1382 lpDlgInfo->style = GET_DWORD(p);
1383 p += 2;
1385 else
1387 lpDlgInfo->dialogEx = FALSE;
1388 lpDlgInfo->helpId = 0;
1389 lpDlgInfo->exStyle = GET_DWORD(p);
1390 p += 2;
1393 lpDlgInfo->nbItems = GET_WORD(p);
1394 p++;
1396 lpDlgInfo->x = GET_WORD(p);
1397 p++;
1399 lpDlgInfo->y = GET_WORD(p);
1400 p++;
1402 lpDlgInfo->cx = GET_WORD(p);
1403 p++;
1405 lpDlgInfo->cy = GET_WORD(p);
1406 p++;
1408 // Get the menu name
1410 switch (GET_WORD(p))
1412 case 0x0000:
1413 lpDlgInfo->menuName = nullptr;
1414 p++;
1415 break;
1416 case 0xffff:
1417 lpDlgInfo->menuName = (LPCTSTR) (WORD) GET_WORD(p + 1);
1418 p += 2;
1419 break;
1420 default:
1421 lpDlgInfo->menuName = (LPCTSTR) p;
1422 p += wcslen((LPCWSTR) p) + 1;
1423 break;
1426 // Get the class name
1428 switch (GET_WORD(p))
1430 case 0x0000:
1431 lpDlgInfo->className = (LPCTSTR)MAKEINTATOM(32770);
1432 p++;
1433 break;
1434 case 0xffff:
1435 lpDlgInfo->className = (LPCTSTR) (WORD) GET_WORD(p + 1);
1436 p += 2;
1437 break;
1438 default:
1439 lpDlgInfo->className = (LPCTSTR) p;
1440 p += wcslen((LPCTSTR)p) + 1;
1441 break;
1444 // Get the window caption
1446 lpDlgInfo->caption = (LPCTSTR)p;
1447 p += wcslen((LPCWSTR) p) + 1;
1449 // Get the font name
1451 if (lpDlgInfo->style & DS_SETFONT)
1453 lpDlgInfo->pointSize = GET_WORD(p);
1454 p++;
1456 if (lpDlgInfo->dialogEx)
1458 lpDlgInfo->weight = GET_WORD(p);
1459 p++;
1460 lpDlgInfo->italic = LOBYTE(GET_WORD(p));
1461 p++;
1463 else
1465 lpDlgInfo->weight = FW_DONTCARE;
1466 lpDlgInfo->italic = FALSE;
1469 lpDlgInfo->faceName = (LPCTSTR)p;
1470 p += wcslen((LPCWSTR) p) + 1;
1472 // First control is on DWORD boundary
1473 p = AlignWORD(p);
1475 return p;
1478 const WORD* CResModule::GetControlInfo(const WORD* p, LPDLGITEMINFO lpDlgItemInfo, BOOL dialogEx, LPBOOL bIsID) const
1480 if (dialogEx)
1482 lpDlgItemInfo->helpId = GET_DWORD(p);
1483 p += 2;
1484 lpDlgItemInfo->exStyle = GET_DWORD(p);
1485 p += 2;
1486 lpDlgItemInfo->style = GET_DWORD(p);
1487 p += 2;
1489 else
1491 lpDlgItemInfo->helpId = 0;
1492 lpDlgItemInfo->style = GET_DWORD(p);
1493 p += 2;
1494 lpDlgItemInfo->exStyle = GET_DWORD(p);
1495 p += 2;
1498 lpDlgItemInfo->x = GET_WORD(p);
1499 p++;
1501 lpDlgItemInfo->y = GET_WORD(p);
1502 p++;
1504 lpDlgItemInfo->cx = GET_WORD(p);
1505 p++;
1507 lpDlgItemInfo->cy = GET_WORD(p);
1508 p++;
1510 if (dialogEx)
1512 // ID is a DWORD for DIALOGEX
1513 lpDlgItemInfo->id = (WORD) GET_DWORD(p);
1514 p += 2;
1516 else
1518 lpDlgItemInfo->id = GET_WORD(p);
1519 p++;
1522 if (GET_WORD(p) == 0xffff)
1524 GET_WORD(p + 1);
1526 p += 2;
1528 else
1530 lpDlgItemInfo->className = (LPCTSTR) p;
1531 p += wcslen((LPCWSTR) p) + 1;
1534 if (GET_WORD(p) == 0xffff) // an integer ID?
1536 *bIsID = TRUE;
1537 lpDlgItemInfo->windowName = (LPCTSTR) (UINT_PTR) GET_WORD(p + 1);
1538 p += 2;
1540 else
1542 *bIsID = FALSE;
1543 lpDlgItemInfo->windowName = (LPCTSTR) p;
1544 p += wcslen((LPCWSTR) p) + 1;
1547 if (GET_WORD(p))
1549 lpDlgItemInfo->data = (LPVOID) (p + 1);
1550 p += GET_WORD(p) / sizeof(WORD);
1552 else
1553 lpDlgItemInfo->data = nullptr;
1555 p++;
1556 // Next control is on DWORD boundary
1557 p = AlignWORD(p);
1558 return p;
1561 const WORD * CResModule::CountMemReplaceDialogResource(const WORD * res, size_t * wordcount, WORD * newDialog)
1563 BOOL bEx = FALSE;
1564 DWORD style = GET_DWORD(res);
1565 if (newDialog)
1567 newDialog[(*wordcount)++] = GET_WORD(res++);
1568 newDialog[(*wordcount)++] = GET_WORD(res++);
1570 else
1572 res += 2;
1573 (*wordcount) += 2;
1576 if (style == 0xffff0001) // DIALOGEX resource
1578 bEx = TRUE;
1579 if (newDialog)
1581 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1582 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1583 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1584 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1585 style = GET_DWORD(res);
1586 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1587 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1589 else
1591 res += 4;
1592 style = GET_DWORD(res);
1593 res += 2;
1594 (*wordcount) += 6;
1597 else
1599 bEx = FALSE;
1600 if (newDialog)
1602 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1603 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1604 //style = GET_DWORD(res);
1605 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1606 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1608 else
1610 res += 2;
1611 (*wordcount) += 2;
1615 if (newDialog)
1616 newDialog[(*wordcount)] = GET_WORD(res);
1617 WORD nbItems = GET_WORD(res);
1618 (*wordcount)++;
1619 res++;
1621 if (newDialog)
1622 newDialog[(*wordcount)] = GET_WORD(res); //x
1623 (*wordcount)++;
1624 res++;
1626 if (newDialog)
1627 newDialog[(*wordcount)] = GET_WORD(res); //y
1628 (*wordcount)++;
1629 res++;
1631 if (newDialog)
1632 newDialog[(*wordcount)] = GET_WORD(res); //cx
1633 (*wordcount)++;
1634 res++;
1636 if (newDialog)
1637 newDialog[(*wordcount)] = GET_WORD(res); //cy
1638 (*wordcount)++;
1639 res++;
1641 // Get the menu name
1643 switch (GET_WORD(res))
1645 case 0x0000:
1646 if (newDialog)
1647 newDialog[(*wordcount)] = GET_WORD(res);
1648 (*wordcount)++;
1649 res++;
1650 break;
1651 case 0xffff:
1652 if (newDialog)
1654 newDialog[(*wordcount)++] = GET_WORD(res++);
1655 newDialog[(*wordcount)++] = GET_WORD(res++);
1657 else
1659 (*wordcount) += 2;
1660 res += 2;
1662 break;
1663 default:
1664 if (newDialog)
1666 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1668 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1669 res += wcslen((LPCWSTR) res) + 1;
1670 break;
1673 // Get the class name
1675 switch (GET_WORD(res))
1677 case 0x0000:
1678 if (newDialog)
1679 newDialog[(*wordcount)] = GET_WORD(res);
1680 (*wordcount)++;
1681 res++;
1682 break;
1683 case 0xffff:
1684 if (newDialog)
1686 newDialog[(*wordcount)++] = GET_WORD(res++);
1687 newDialog[(*wordcount)++] = GET_WORD(res++);
1689 else
1691 (*wordcount) += 2;
1692 res += 2;
1694 break;
1695 default:
1696 if (newDialog)
1698 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1700 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1701 res += wcslen((LPCWSTR) res) + 1;
1702 break;
1705 // Get the window caption
1707 ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1708 res += wcslen((LPCWSTR)res) + 1;
1710 // Get the font name
1712 if (style & DS_SETFONT)
1714 if (newDialog)
1715 newDialog[(*wordcount)] = GET_WORD(res);
1716 res++;
1717 (*wordcount)++;
1719 if (bEx)
1721 if (newDialog)
1723 newDialog[(*wordcount)++] = GET_WORD(res++);
1724 newDialog[(*wordcount)++] = GET_WORD(res++);
1726 else
1728 res += 2;
1729 (*wordcount) += 2;
1733 if (newDialog)
1734 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1735 (*wordcount) += wcslen((LPCWSTR)res) + 1;
1736 res += wcslen((LPCWSTR)res) + 1;
1738 // First control is on DWORD boundary
1739 while ((*wordcount)%2)
1740 (*wordcount)++;
1741 while ((UINT_PTR)res % 4)
1742 res++;
1744 while (nbItems--)
1746 res = ReplaceControlInfo(res, wordcount, newDialog, bEx);
1748 return res;
1751 const WORD* CResModule::ReplaceControlInfo(const WORD * res, size_t * wordcount, WORD * newDialog, BOOL bEx)
1753 if (bEx)
1755 if (newDialog)
1757 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1758 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1760 else
1762 res += 2;
1763 (*wordcount) += 2;
1766 if (newDialog)
1768 LONG * exStyle = (LONG*)&newDialog[(*wordcount)];
1769 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1770 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1771 if (m_bRTL)
1772 *exStyle |= WS_EX_RTLREADING;
1774 else
1776 res += 2;
1777 (*wordcount) += 2;
1780 if (newDialog)
1782 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1783 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1785 else
1787 res += 2;
1788 (*wordcount) += 2;
1791 if (newDialog)
1792 newDialog[(*wordcount)] = GET_WORD(res); //x
1793 res++;
1794 (*wordcount)++;
1796 if (newDialog)
1797 newDialog[(*wordcount)] = GET_WORD(res); //y
1798 res++;
1799 (*wordcount)++;
1801 if (newDialog)
1802 newDialog[(*wordcount)] = GET_WORD(res); //cx
1803 res++;
1804 (*wordcount)++;
1806 if (newDialog)
1807 newDialog[(*wordcount)] = GET_WORD(res); //cy
1808 res++;
1809 (*wordcount)++;
1811 if (bEx)
1813 // ID is a DWORD for DIALOGEX
1814 if (newDialog)
1816 newDialog[(*wordcount)++] = GET_WORD(res++);
1817 newDialog[(*wordcount)++] = GET_WORD(res++);
1819 else
1821 res += 2;
1822 (*wordcount) += 2;
1825 else
1827 if (newDialog)
1828 newDialog[(*wordcount)] = GET_WORD(res);
1829 res++;
1830 (*wordcount)++;
1833 if (GET_WORD(res) == 0xffff) //classID
1835 if (newDialog)
1837 newDialog[(*wordcount)++] = GET_WORD(res++);
1838 newDialog[(*wordcount)++] = GET_WORD(res++);
1840 else
1842 res += 2;
1843 (*wordcount) += 2;
1846 else
1848 if (newDialog)
1849 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1850 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1851 res += wcslen((LPCWSTR) res) + 1;
1854 if (GET_WORD(res) == 0xffff) // an integer ID?
1856 if (newDialog)
1858 newDialog[(*wordcount)++] = GET_WORD(res++);
1859 newDialog[(*wordcount)++] = GET_WORD(res++);
1861 else
1863 res += 2;
1864 (*wordcount) += 2;
1867 else
1869 ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1870 res += wcslen((LPCWSTR)res) + 1;
1873 if (newDialog)
1874 memcpy(&newDialog[(*wordcount)], res, (GET_WORD(res)+1)*sizeof(WORD));
1875 (*wordcount) += (GET_WORD(res)+1);
1876 res += (GET_WORD(res)+1);
1877 // Next control is on DWORD boundary
1878 while ((*wordcount) % 2)
1879 (*wordcount)++;
1880 res = AlignWORD(res);
1882 return res;
1885 BOOL CResModule::ExtractRibbon(LPCTSTR lpszType)
1887 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1888 HGLOBAL hglRibbonTemplate;
1889 const BYTE *p;
1891 if (!hrsrc)
1892 MYERROR;
1894 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1896 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1898 if (!hglRibbonTemplate)
1899 MYERROR;
1901 p = (const BYTE*)LockResource(hglRibbonTemplate);
1903 if (!p)
1904 MYERROR;
1906 // Resource consists of one single string
1907 // that is XML.
1909 // extract all <id><name>blah1</name><value>blah2</value></id><text>blah</text> elements
1911 const std::regex regRevMatch("<ID><NAME>([^<]+)</NAME><VALUE>([^<]+)</VALUE></ID><TEXT>([^<]+)</TEXT>");
1912 std::string ss = std::string((const char*)p, sizeres);
1913 const std::sregex_iterator end;
1914 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
1916 size_t len;
1918 std::string str1 = (*it)[1];
1919 len = str1.size();
1920 auto bufw1 = std::make_unique<wchar_t[]>(len * 4 + 1);
1921 SecureZeroMemory(bufw1.get(), (len * 4 + 1) * sizeof(wchar_t));
1922 MultiByteToWideChar(CP_UTF8, 0, str1.c_str(), -1, bufw1.get(), (int)len * 4);
1923 std::wstring strIdNameVal = bufw1.get();
1924 strIdNameVal += L" - Ribbon name";
1926 std::string str2 = (*it)[2];
1927 len = str2.size();
1928 auto bufw2 = std::make_unique<wchar_t[]>(len * 4 + 1);
1929 SecureZeroMemory(bufw2.get(), (len * 4 + 1)*sizeof(wchar_t));
1930 MultiByteToWideChar(CP_UTF8, 0, str2.c_str(), -1, bufw2.get(), (int)len * 4);
1931 std::wstring strIdVal = bufw2.get();
1933 std::string str3 = (*it)[3];
1934 len = str3.size();
1935 auto bufw3 = std::make_unique<wchar_t[]>(len * 4 + 1);
1936 SecureZeroMemory(bufw3.get(), (len * 4 + 1)*sizeof(wchar_t));
1937 MultiByteToWideChar(CP_UTF8, 0, str3.c_str(), -1, bufw3.get(), (int)len * 4);
1938 std::wstring str = bufw3.get();
1940 RESOURCEENTRY entry = m_StringEntries[str];
1941 InsertResourceIDs(RT_RIBBON, 0, entry, std::stoi(strIdVal), strIdNameVal.c_str());
1942 if (wcschr(str.c_str(), '%'))
1943 entry.flag = L"#, c-format";
1944 m_StringEntries[str] = entry;
1945 m_bDefaultRibbonTexts++;
1948 // extract all </ELEMENT_NAME><NAME>blahblah</NAME> elements
1950 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1951 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
1953 std::string str = (*it)[1];
1954 size_t len = str.size();
1955 auto bufw = std::make_unique<wchar_t[]>(len * 4 + 1);
1956 SecureZeroMemory(bufw.get(), (len*4 + 1)*sizeof(wchar_t));
1957 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw.get(), (int)len*4);
1958 std::wstring ret = bufw.get();
1959 RESOURCEENTRY entry = m_StringEntries[ret];
1960 InsertResourceIDs(RT_RIBBON, 0, entry, (INT_PTR)lpszType, L" - Ribbon element");
1961 if (wcschr(ret.c_str(), '%'))
1962 entry.flag = L"#, c-format";
1963 m_StringEntries[ret] = entry;
1964 m_bDefaultRibbonTexts++;
1967 UnlockResource(hglRibbonTemplate);
1968 FreeResource(hglRibbonTemplate);
1969 return TRUE;
1972 BOOL CResModule::ReplaceRibbon(LPCTSTR lpszType, WORD wLanguage)
1974 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1975 HGLOBAL hglRibbonTemplate;
1976 const BYTE *p;
1978 if (!hrsrc)
1979 MYERROR;
1981 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1983 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1985 if (!hglRibbonTemplate)
1986 MYERROR;
1988 p = (const BYTE*)LockResource(hglRibbonTemplate);
1990 if (!p)
1991 MYERROR;
1993 std::string ss = std::string((const char*)p, sizeres);
1994 size_t len = ss.size();
1995 auto bufw = std::make_unique<wchar_t[]>(len * 4 + 1);
1996 SecureZeroMemory(bufw.get(), (len*4 + 1)*sizeof(wchar_t));
1997 MultiByteToWideChar(CP_UTF8, 0, ss.c_str(), -1, bufw.get(), (int)len*4);
1998 std::wstring ssw = bufw.get();
2001 const std::regex regRevMatch("<TEXT>([^<]+)</TEXT>");
2002 const std::sregex_iterator end;
2003 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
2005 std::string str = (*it)[1];
2006 size_t slen = str.size();
2007 auto bufw2 = std::make_unique<wchar_t[]>(slen * 4 + 1);
2008 SecureZeroMemory(bufw2.get(), (slen*4 + 1)*sizeof(wchar_t));
2009 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw2.get(), (int)slen*4);
2010 std::wstring ret = bufw2.get();
2012 RESOURCEENTRY entry = m_StringEntries[ret];
2013 ret = L"<TEXT>" + ret + L"</TEXT>";
2015 if (entry.msgstr.size())
2017 auto sbuf = std::make_unique<wchar_t[]>(entry.msgstr.size() + 10);
2018 wcscpy(sbuf.get(), entry.msgstr.c_str());
2019 CUtils::StringCollapse(sbuf.get());
2020 ReplaceWithRegex(sbuf.get());
2021 std::wstring sreplace = L"<TEXT>";
2022 sreplace += sbuf.get();
2023 sreplace += L"</TEXT>";
2024 CUtils::SearchReplace(ssw, ret, sreplace);
2025 m_bTranslatedRibbonTexts++;
2027 else
2028 m_bDefaultRibbonTexts++;
2031 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
2032 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
2034 std::string str = (*it)[1];
2035 size_t slen = str.size();
2036 auto bufw2 = std::make_unique<wchar_t[]>(slen * 4 + 1);
2037 SecureZeroMemory(bufw2.get(), (slen*4 + 1)*sizeof(wchar_t));
2038 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw2.get(), (int)slen*4);
2039 std::wstring ret = bufw2.get();
2041 RESOURCEENTRY entry = m_StringEntries[ret];
2042 ret = L"</ELEMENT_NAME><NAME>" + ret + L"</NAME>";
2044 if (entry.msgstr.size())
2046 auto sbuf = std::make_unique<wchar_t[]>(entry.msgstr.size() + 10);
2047 wcscpy(sbuf.get(), entry.msgstr.c_str());
2048 CUtils::StringCollapse(sbuf.get());
2049 ReplaceWithRegex(sbuf.get());
2050 std::wstring sreplace = L"</ELEMENT_NAME><NAME>";
2051 sreplace += sbuf.get();
2052 sreplace += L"</NAME>";
2053 CUtils::SearchReplace(ssw, ret, sreplace);
2054 m_bTranslatedRibbonTexts++;
2056 else
2057 m_bDefaultRibbonTexts++;
2060 auto buf = std::make_unique<char[]>(ssw.size() * 4 + 1);
2061 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, ssw.c_str(), -1, buf.get(), (int)len * 4, nullptr, nullptr);
2064 if (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), buf.get(), lengthIncTerminator-1))
2066 goto DONE_ERROR;
2069 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, wLanguage, nullptr, 0)))
2071 goto DONE_ERROR;
2075 UnlockResource(hglRibbonTemplate);
2076 FreeResource(hglRibbonTemplate);
2077 return TRUE;
2079 DONE_ERROR:
2080 UnlockResource(hglRibbonTemplate);
2081 FreeResource(hglRibbonTemplate);
2082 MYERROR;
2085 std::wstring CResModule::ReplaceWithRegex(WCHAR* pBuf)
2087 for (const auto& t : m_StringEntries.m_regexes)
2091 std::wregex e(std::get<0>(t), std::regex_constants::icase);
2092 auto replaced = std::regex_replace(pBuf, e, std::get<1>(t));
2093 wcscpy(pBuf, replaced.c_str());
2095 catch (std::exception&)
2099 return pBuf;
2102 std::wstring CResModule::ReplaceWithRegex(std::wstring& s)
2104 for (const auto& t : m_StringEntries.m_regexes)
2108 std::wregex e(std::get<0>(t), std::regex_constants::icase);
2109 auto replaced = std::regex_replace(s, e, std::get<1>(t));
2110 s = replaced;
2112 catch (std::exception&)
2116 return s;
2119 BOOL CALLBACK CResModule::EnumResNameCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
2121 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2123 if (lpszType == RT_STRING)
2125 if (IS_INTRESOURCE(lpszName))
2127 if (!lpResModule->ExtractString(lpszName))
2128 return FALSE;
2131 else if (lpszType == RT_MENU)
2133 if (IS_INTRESOURCE(lpszName))
2135 if (!lpResModule->ExtractMenu(lpszName))
2136 return FALSE;
2139 else if (lpszType == RT_DIALOG)
2141 if (IS_INTRESOURCE(lpszName))
2143 if (!lpResModule->ExtractDialog(lpszName))
2144 return FALSE;
2147 else if (lpszType == RT_ACCELERATOR)
2149 if (IS_INTRESOURCE(lpszName))
2151 if (!lpResModule->ExtractAccelerator(lpszName))
2152 return FALSE;
2155 else if (lpszType == RT_RIBBON)
2157 if (IS_INTRESOURCE(lpszName))
2159 if (!lpResModule->ExtractRibbon(lpszName))
2160 return FALSE;
2164 return TRUE;
2167 #pragma warning(push)
2168 #pragma warning(disable: 4189)
2169 BOOL CALLBACK CResModule::EnumResNameWriteCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
2171 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2172 return EnumResourceLanguages(hModule, lpszType, lpszName, (ENUMRESLANGPROC)&lpResModule->EnumResWriteLangCallback, lParam);
2174 #pragma warning(pop)
2176 BOOL CALLBACK CResModule::EnumResWriteLangCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, WORD wLanguage, LONG_PTR lParam)
2178 BOOL bRes = FALSE;
2179 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2181 if (lpszType == RT_STRING)
2183 bRes = lpResModule->ReplaceString(lpszName, wLanguage);
2185 else if (lpszType == RT_MENU)
2187 bRes = lpResModule->ReplaceMenu(lpszName, wLanguage);
2189 else if (lpszType == RT_DIALOG)
2191 bRes = lpResModule->ReplaceDialog(lpszName, wLanguage);
2193 else if (lpszType == RT_ACCELERATOR)
2195 bRes = lpResModule->ReplaceAccelerator(lpszName, wLanguage);
2197 else if (lpszType == RT_RIBBON)
2199 bRes = lpResModule->ReplaceRibbon(lpszName, wLanguage);
2202 return bRes;
2206 void CResModule::ReplaceStr(LPCWSTR src, WORD * dest, size_t * count, int * translated, int * def)
2208 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
2209 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
2210 wcscpy(pBuf, src);
2211 CUtils::StringExtend(pBuf);
2213 std::wstring wstr = std::wstring(pBuf);
2214 ReplaceWithRegex(pBuf);
2215 RESOURCEENTRY entry = m_StringEntries[wstr];
2216 if (!entry.msgstr.empty())
2218 wcscpy(pBuf, entry.msgstr.c_str());
2219 ReplaceWithRegex(pBuf);
2220 CUtils::StringCollapse(pBuf);
2221 if (dest)
2222 wcscpy((wchar_t *)&dest[(*count)], pBuf);
2223 (*count) += wcslen(pBuf)+1;
2224 (*translated)++;
2226 else
2228 if (wcscmp(pBuf, wstr.c_str()))
2230 if (dest)
2231 wcscpy((wchar_t*)&dest[(*count)], pBuf);
2232 (*count) += wcslen(pBuf) + 1;
2233 (*translated)++;
2235 else
2237 if (dest)
2238 wcscpy((wchar_t*)&dest[(*count)], src);
2239 (*count) += wcslen(src) + 1;
2240 if (wcslen(src))
2241 (*def)++;
2244 delete [] pBuf;
2247 static bool StartsWith(const std::string& heystacl, const char* needle)
2249 return heystacl.compare(0, strlen(needle), needle) == 0;
2252 static bool StartsWith(const std::wstring& heystacl, const wchar_t* needle)
2254 return heystacl.compare(0, wcslen(needle), needle) == 0;
2257 size_t CResModule::ScanHeaderFile(const std::wstring & filepath)
2259 size_t count = 0;
2261 // open the file and read the contents
2262 DWORD reqLen = GetFullPathName(filepath.c_str(), 0, nullptr, nullptr);
2263 auto wcfullPath = std::make_unique<TCHAR[]>(reqLen + 1);
2264 GetFullPathName(filepath.c_str(), reqLen, wcfullPath.get(), nullptr);
2265 std::wstring fullpath = wcfullPath.get();
2268 // first treat the file as ASCII and try to get the defines
2270 std::ifstream fin(fullpath);
2271 std::string file_line;
2272 while (std::getline(fin, file_line))
2274 auto defpos = file_line.find("#define");
2275 if (defpos != std::string::npos)
2277 std::string text = file_line.substr(defpos + 7);
2278 trim(text);
2279 auto spacepos = text.find(' ');
2280 if (spacepos == std::string::npos)
2281 spacepos = text.find('\t');
2282 if (spacepos != std::string::npos)
2284 auto value = atol(text.substr(spacepos).c_str());
2285 if (value == 0 && text.substr(spacepos).find("0x") != std::string::npos)
2286 value = std::stoul(text.substr(spacepos), nullptr, 16);
2287 text = text.substr(0, spacepos);
2288 trim(text);
2289 if (StartsWith(text, "IDS_") || StartsWith(text, "AFX_IDS_") || StartsWith(text, "AFX_IDP_"))
2291 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2292 ++count;
2294 else if (StartsWith(text, "IDD_") || StartsWith(text, "AFX_IDD_"))
2296 m_currentHeaderDataDialogs[value] = CUnicodeUtils::StdGetUnicode(text);
2297 ++count;
2299 else if (StartsWith(text, "ID_") || StartsWith(text, "AFX_ID_"))
2301 m_currentHeaderDataMenus[value] = CUnicodeUtils::StdGetUnicode(text);
2302 ++count;
2304 else if (StartsWith(text, "cmd"))
2306 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2307 ++count;
2309 else if (text.find("_RESID") != std::string::npos)
2311 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2312 ++count;
2320 // now try the same with the file treated as utf16
2322 // open as a byte stream
2323 std::wifstream wfin(fullpath, std::ios::binary);
2324 // apply BOM-sensitive UTF-16 facet
2325 wfin.imbue(std::locale(wfin.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
2326 //std::wifstream wfin(fullpath);
2327 std::wstring wfile_line;
2328 while (std::getline(wfin, wfile_line))
2330 auto defpos = wfile_line.find(L"#define");
2331 if (defpos != std::wstring::npos)
2333 std::wstring text = wfile_line.substr(defpos + 7);
2334 trim(text);
2335 auto spacepos = text.find(' ');
2336 if (spacepos == std::wstring::npos)
2337 spacepos = text.find('\t');
2338 if (spacepos != std::wstring::npos)
2340 auto value = _wtol(text.substr(spacepos).c_str());
2341 if (value == 0 && text.substr(spacepos).find(L"0x") != std::wstring::npos)
2342 value = std::stoul(text.substr(spacepos), nullptr, 16);
2343 text = text.substr(0, spacepos);
2344 trim(text);
2345 if (StartsWith(text, L"IDS_") || StartsWith(text, L"AFX_IDS_") || StartsWith(text, L"AFX_IDP_"))
2347 m_currentHeaderDataStrings[value] = text;
2348 ++count;
2350 else if (StartsWith(text, L"IDD_") || StartsWith(text, L"AFX_IDD_"))
2352 m_currentHeaderDataDialogs[value] = text;
2353 ++count;
2355 else if (StartsWith(text, L"ID_") || StartsWith(text, L"AFX_ID_"))
2357 m_currentHeaderDataMenus[value] = text;
2358 ++count;
2360 else if (StartsWith(text, L"cmd"))
2362 m_currentHeaderDataStrings[value] = text;
2363 ++count;
2365 else if (text.find(L"_RESID") != std::string::npos)
2367 m_currentHeaderDataStrings[value] = text;
2368 ++count;
2375 return count;
2378 void CResModule::InsertResourceIDs(LPCWSTR lpType, INT_PTR mainId, RESOURCEENTRY& entry, INT_PTR id, LPCWSTR infotext)
2380 if (lpType == RT_DIALOG)
2382 auto foundIt = m_currentHeaderDataDialogs.find(mainId);
2383 if (foundIt != m_currentHeaderDataDialogs.end())
2384 entry.resourceIDs.insert(L"Dialog " + foundIt->second + L": Control id " + NumToStr(id) + infotext);
2385 else
2386 entry.resourceIDs.insert(NumToStr(id) + infotext);
2388 else if (lpType == RT_STRING)
2390 auto foundIt = m_currentHeaderDataStrings.find(id);
2391 if (foundIt != m_currentHeaderDataStrings.end())
2392 entry.resourceIDs.insert(foundIt->second + infotext);
2393 else
2394 entry.resourceIDs.insert(NumToStr(id) + infotext);
2396 else if (lpType == RT_MENU)
2398 auto foundIt = m_currentHeaderDataMenus.find(id);
2399 if (foundIt != m_currentHeaderDataMenus.end())
2400 entry.resourceIDs.insert(foundIt->second + infotext);
2401 else
2402 entry.resourceIDs.insert(NumToStr(id) + infotext);
2404 else if (lpType == RT_RIBBON && infotext && wcsstr(infotext, L"ID") == infotext)
2405 entry.resourceIDs.insert(infotext);
2406 else
2407 entry.resourceIDs.insert(NumToStr(id) + infotext);
2410 bool CResModule::AdjustCheckSum(const std::wstring& resFile)
2412 HANDLE hFile = INVALID_HANDLE_VALUE;
2413 HANDLE hFileMapping = nullptr;
2414 PVOID pBaseAddress = nullptr;
2418 hFile = CreateFile(resFile.c_str(), FILE_READ_DATA | FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
2419 if (hFile == INVALID_HANDLE_VALUE)
2420 throw GetLastError();
2422 hFileMapping = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, 0, nullptr);
2423 if (!hFileMapping)
2424 throw GetLastError();
2426 pBaseAddress = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
2427 if (!pBaseAddress)
2428 throw GetLastError();
2430 auto fileSize = GetFileSize(hFile, nullptr);
2431 if (fileSize == INVALID_FILE_SIZE)
2432 throw GetLastError();
2433 DWORD dwChecksum = 0;
2434 DWORD dwHeaderSum = 0;
2435 CheckSumMappedFile(pBaseAddress, fileSize, &dwHeaderSum, &dwChecksum);
2437 PIMAGE_DOS_HEADER pDOSHeader = static_cast<PIMAGE_DOS_HEADER>(pBaseAddress);
2438 if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
2439 throw GetLastError();
2441 PIMAGE_NT_HEADERS pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>((PBYTE)pBaseAddress + pDOSHeader->e_lfanew);
2442 if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
2443 throw GetLastError();
2445 SetLastError(ERROR_SUCCESS);
2447 DWORD* pChecksum = &(pNTHeader->OptionalHeader.CheckSum);
2448 *pChecksum = dwChecksum;
2450 UnmapViewOfFile(pBaseAddress);
2451 CloseHandle(hFileMapping);
2452 CloseHandle(hFile);
2454 catch (...)
2456 if (pBaseAddress)
2457 UnmapViewOfFile(pBaseAddress);
2458 if (hFileMapping)
2459 CloseHandle(hFileMapping);
2460 if (hFile != INVALID_HANDLE_VALUE)
2461 CloseHandle(hFile);
2462 return false;
2465 return true;