Make the RTFM command work as help for new users
[TortoiseGit.git] / src / ResText / ResModule.cpp
blob060455adead7fae3b9647a4491fdb85f48b5270c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2015-2016 - TortoiseGit
4 // Copyright (C) 2003-2008, 2010-2016 - 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>
31 #ifndef RT_RIBBON
32 #define RT_RIBBON MAKEINTRESOURCE(28)
33 #endif
36 #define MYERROR {CUtils::Error(); return FALSE;}
38 static const WORD * AlignWORD(const WORD * pWord)
40 const WORD * res = pWord;
41 res += ((((UINT_PTR)pWord + 3) & ~3) - (UINT_PTR)pWord) / sizeof(WORD);
42 return res;
45 std::wstring NumToStr(INT_PTR num)
47 wchar_t buf[100];
48 swprintf_s(buf, L"%Id", num);
49 return buf;
52 CResModule::CResModule(void)
53 : m_bTranslatedStrings(0)
54 , m_bDefaultStrings(0)
55 , m_bTranslatedDialogStrings(0)
56 , m_bDefaultDialogStrings(0)
57 , m_bTranslatedMenuStrings(0)
58 , m_bDefaultMenuStrings(0)
59 , m_bTranslatedAcceleratorStrings(0)
60 , m_bDefaultAcceleratorStrings(0)
61 , m_bTranslatedRibbonTexts(0)
62 , m_bDefaultRibbonTexts(0)
63 , m_wTargetLang(0)
64 , m_hResDll(nullptr)
65 , m_hUpdateRes(nullptr)
66 , m_bQuiet(false)
67 , m_bRTL(false)
68 , m_bAdjustEOLs(false)
72 CResModule::~CResModule(void)
76 BOOL CResModule::ExtractResources(const std::vector<std::wstring>& filelist, LPCTSTR lpszPOFilePath, BOOL bNoUpdate, LPCTSTR lpszHeaderFile)
78 for (auto I = filelist.cbegin(); I != filelist.cend(); ++I)
80 std::wstring filepath = *I;
81 m_currentHeaderDataDialogs.clear();
82 m_currentHeaderDataMenus.clear();
83 m_currentHeaderDataStrings.clear();
84 auto starpos = I->find('*');
85 if (starpos != std::wstring::npos)
86 filepath = I->substr(0, starpos);
87 while (starpos != std::wstring::npos)
89 auto starposnext = I->find('*', starpos + 1);
90 std::wstring headerfile = I->substr(starpos + 1, starposnext - starpos - 1);
91 ScanHeaderFile(headerfile);
92 starpos = starposnext;
94 m_hResDll = LoadLibraryEx(filepath.c_str(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
95 if (!m_hResDll)
96 MYERROR;
98 size_t nEntries = m_StringEntries.size();
99 // fill in the std::map with all translatable entries
101 if (!m_bQuiet)
102 _ftprintf(stdout, L"Extracting StringTable....");
103 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (LONG_PTR)this);
104 if (!m_bQuiet)
105 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
106 nEntries = m_StringEntries.size();
108 if (!m_bQuiet)
109 _ftprintf(stdout, L"Extracting Dialogs........");
110 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (LONG_PTR)this);
111 if (!m_bQuiet)
112 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
113 nEntries = m_StringEntries.size();
115 if (!m_bQuiet)
116 _ftprintf(stdout, L"Extracting Menus..........");
117 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (LONG_PTR)this);
118 if (!m_bQuiet)
119 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
120 nEntries = m_StringEntries.size();
121 if (!m_bQuiet)
122 _ftprintf(stdout, L"Extracting Accelerators...");
123 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (LONG_PTR)this);
124 if (!m_bQuiet)
125 _ftprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size()-nEntries);
126 nEntries = m_StringEntries.size();
127 if (!m_bQuiet)
128 _ftprintf(stdout, L"Extracting Ribbons........");
129 EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameCallback, (LONG_PTR)this);
130 if (!m_bQuiet)
131 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
132 nEntries = m_StringEntries.size();
134 // parse a probably existing file and update the translations which are
135 // already done
136 m_StringEntries.ParseFile(lpszPOFilePath, !bNoUpdate, m_bAdjustEOLs);
138 FreeLibrary(m_hResDll);
141 // at last, save the new file
142 return m_StringEntries.SaveFile(lpszPOFilePath, lpszHeaderFile);
145 BOOL CResModule::ExtractResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszPoFilePath, BOOL bNoUpdate, LPCTSTR lpszHeaderFile)
147 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
148 if (!m_hResDll)
149 MYERROR;
151 size_t nEntries = 0;
152 // fill in the std::map with all translatable entries
154 if (!m_bQuiet)
155 _ftprintf(stdout, L"Extracting StringTable....");
156 EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (LONG_PTR)this);
157 if (!m_bQuiet)
158 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size());
159 nEntries = m_StringEntries.size();
161 if (!m_bQuiet)
162 _ftprintf(stdout, L"Extracting Dialogs........");
163 EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (LONG_PTR)this);
164 if (!m_bQuiet)
165 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
166 nEntries = m_StringEntries.size();
168 if (!m_bQuiet)
169 _ftprintf(stdout, L"Extracting Menus..........");
170 EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (LONG_PTR)this);
171 if (!m_bQuiet)
172 _ftprintf(stdout, L"%4Iu Strings\n", m_StringEntries.size()-nEntries);
173 nEntries = m_StringEntries.size();
175 if (!m_bQuiet)
176 _ftprintf(stdout, L"Extracting Accelerators...");
177 EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (LONG_PTR)this);
178 if (!m_bQuiet)
179 _ftprintf(stdout, L"%4Iu Accelerators\n", m_StringEntries.size()-nEntries);
180 nEntries = m_StringEntries.size();
182 // parse a probably existing file and update the translations which are
183 // already done
184 m_StringEntries.ParseFile(lpszPoFilePath, !bNoUpdate, m_bAdjustEOLs);
186 // at last, save the new file
187 if (!m_StringEntries.SaveFile(lpszPoFilePath, lpszHeaderFile))
188 goto DONE_ERROR;
190 FreeLibrary(m_hResDll);
191 return TRUE;
193 DONE_ERROR:
194 if (m_hResDll)
195 FreeLibrary(m_hResDll);
196 return FALSE;
199 BOOL CResModule::CreateTranslatedResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszDestLangDllPath, LPCTSTR lpszPOFilePath)
201 if (!CopyFile(lpszSrcLangDllPath, lpszDestLangDllPath, FALSE))
202 MYERROR;
204 int count = 0;
207 m_hResDll = LoadLibraryEx(lpszSrcLangDllPath, nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_IGNORE_CODE_AUTHZ_LEVEL);
208 if (!m_hResDll)
209 Sleep(100);
210 count++;
211 } while (!m_hResDll && (count < 10));
213 if (!m_hResDll)
214 MYERROR;
216 sDestFile = std::wstring(lpszDestLangDllPath);
218 // get all translated strings
219 if (!m_StringEntries.ParseFile(lpszPOFilePath, FALSE, m_bAdjustEOLs))
220 goto DONE_ERROR;
221 m_bTranslatedStrings = 0;
222 m_bDefaultStrings = 0;
223 m_bTranslatedDialogStrings = 0;
224 m_bDefaultDialogStrings = 0;
225 m_bTranslatedMenuStrings = 0;
226 m_bDefaultMenuStrings = 0;
227 m_bTranslatedAcceleratorStrings = 0;
228 m_bDefaultAcceleratorStrings = 0;
230 BOOL bRes = FALSE;
231 count = 0;
234 m_hUpdateRes = BeginUpdateResource(sDestFile.c_str(), FALSE);
235 if (!m_hUpdateRes)
236 Sleep(100);
237 count++;
238 } while (!m_hUpdateRes && (count < 10));
240 if (!m_hUpdateRes)
241 MYERROR;
244 if (!m_bQuiet)
245 _ftprintf(stdout, L"Translating StringTable...");
246 bRes = EnumResourceNames(m_hResDll, RT_STRING, EnumResNameWriteCallback, (LONG_PTR)this);
247 if (!m_bQuiet)
248 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedStrings, m_bDefaultStrings);
250 if (!m_bQuiet)
251 _ftprintf(stdout, L"Translating Dialogs.......");
252 bRes = EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameWriteCallback, (LONG_PTR)this);
253 if (!m_bQuiet)
254 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedDialogStrings, m_bDefaultDialogStrings);
256 if (!m_bQuiet)
257 _ftprintf(stdout, L"Translating Menus.........");
258 bRes = EnumResourceNames(m_hResDll, RT_MENU, EnumResNameWriteCallback, (LONG_PTR)this);
259 if (!m_bQuiet)
260 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedMenuStrings, m_bDefaultMenuStrings);
262 if (!m_bQuiet)
263 _ftprintf(stdout, L"Translating Accelerators..");
264 bRes = EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameWriteCallback, (LONG_PTR)this);
265 if (!m_bQuiet)
266 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedAcceleratorStrings, m_bDefaultAcceleratorStrings);
268 if (!m_bQuiet)
269 _ftprintf(stdout, L"Translating Ribbons.......");
270 bRes = EnumResourceNames(m_hResDll, RT_RIBBON, EnumResNameWriteCallback, (LONG_PTR)this);
271 if (!m_bQuiet)
272 _ftprintf(stdout, L"%4d translated, %4d not translated\n", m_bTranslatedRibbonTexts, m_bDefaultRibbonTexts);
273 bRes = TRUE;
274 if (!EndUpdateResource(m_hUpdateRes, !bRes))
275 MYERROR;
277 FreeLibrary(m_hResDll);
278 return TRUE;
279 DONE_ERROR:
280 if (m_hResDll)
281 FreeLibrary(m_hResDll);
282 return FALSE;
285 BOOL CResModule::ExtractString(LPCTSTR lpszType)
287 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_STRING);
288 HGLOBAL hglStringTable;
289 LPWSTR p;
291 if (!hrsrc)
292 MYERROR;
293 hglStringTable = LoadResource(m_hResDll, hrsrc);
295 if (!hglStringTable)
296 goto DONE_ERROR;
297 p = (LPWSTR)LockResource(hglStringTable);
299 if (!p)
300 goto DONE_ERROR;
301 /* [Block of 16 strings. The strings are Pascal style with a WORD
302 length preceding the string. 16 strings are always written, even
303 if not all slots are full. Any slots in the block with no string
304 have a zero WORD for the length.]
307 //first check how much memory we need
308 LPWSTR pp = p;
309 for (int i=0; i<16; ++i)
311 int len = GET_WORD(pp);
312 pp++;
313 std::wstring msgid = std::wstring(pp, len);
314 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
315 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
316 wcscpy(pBuf, msgid.c_str());
317 CUtils::StringExtend(pBuf);
319 if (pBuf[0])
321 std::wstring str = std::wstring(pBuf);
322 RESOURCEENTRY entry = m_StringEntries[str];
323 InsertResourceIDs(RT_STRING, 0, entry, ((INT_PTR)lpszType - 1) * 16 + i, L"");
324 if (wcschr(str.c_str(), '%'))
325 entry.flag = L"#, c-format";
326 m_StringEntries[str] = entry;
328 delete [] pBuf;
329 pp += len;
331 UnlockResource(hglStringTable);
332 FreeResource(hglStringTable);
333 return TRUE;
334 DONE_ERROR:
335 UnlockResource(hglStringTable);
336 FreeResource(hglStringTable);
337 MYERROR;
340 BOOL CResModule::ReplaceString(LPCTSTR lpszType, WORD wLanguage)
342 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_STRING, lpszType, wLanguage);
343 HGLOBAL hglStringTable;
344 LPWSTR p;
346 if (!hrsrc)
347 MYERROR;
348 hglStringTable = LoadResource(m_hResDll, hrsrc);
350 if (!hglStringTable)
351 goto DONE_ERROR;
352 p = (LPWSTR)LockResource(hglStringTable);
354 if (!p)
355 goto DONE_ERROR;
356 /* [Block of 16 strings. The strings are Pascal style with a WORD
357 length preceding the string. 16 strings are always written, even
358 if not all slots are full. Any slots in the block with no string
359 have a zero WORD for the length.]
362 //first check how much memory we need
363 size_t nMem = 0;
364 LPWSTR pp = p;
365 for (int i=0; i<16; ++i)
367 nMem++;
368 size_t len = GET_WORD(pp);
369 pp++;
370 std::wstring msgid = std::wstring(pp, len);
371 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
372 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
373 wcscpy(pBuf, msgid.c_str());
374 CUtils::StringExtend(pBuf);
375 msgid = std::wstring(pBuf);
377 RESOURCEENTRY resEntry;
378 resEntry = m_StringEntries[msgid];
379 wcscpy(pBuf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
380 ReplaceWithRegex(pBuf);
381 CUtils::StringCollapse(pBuf);
382 size_t newlen = wcslen(pBuf);
383 if (newlen)
384 nMem += newlen;
385 else
386 nMem += len;
387 pp += len;
388 delete [] pBuf;
391 WORD * newTable = new WORD[nMem + (nMem % 2)];
392 SecureZeroMemory(newTable, (nMem + (nMem % 2))*2);
394 size_t index = 0;
395 for (int i=0; i<16; ++i)
397 int len = GET_WORD(p);
398 p++;
399 std::wstring msgid = std::wstring(p, len);
400 WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2];
401 SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR));
402 wcscpy(pBuf, msgid.c_str());
403 CUtils::StringExtend(pBuf);
404 msgid = std::wstring(pBuf);
406 RESOURCEENTRY resEntry;
407 resEntry = m_StringEntries[msgid];
408 wcscpy(pBuf, resEntry.msgstr.empty() ? msgid.c_str() : resEntry.msgstr.c_str());
409 ReplaceWithRegex(pBuf);
410 CUtils::StringCollapse(pBuf);
411 size_t newlen = wcslen(pBuf);
412 if (newlen)
414 newTable[index++] = (WORD)newlen;
415 wcsncpy((wchar_t *)&newTable[index], pBuf, newlen);
416 index += newlen;
417 m_bTranslatedStrings++;
419 else
421 newTable[index++] = (WORD)len;
422 if (len)
423 wcsncpy((wchar_t *)&newTable[index], p, len);
424 index += len;
425 if (len)
426 m_bDefaultStrings++;
428 p += len;
429 delete [] pBuf;
432 if (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newTable, (DWORD)(nMem + (nMem % 2))*2))
434 delete [] newTable;
435 goto DONE_ERROR;
438 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_STRING, lpszType, wLanguage, nullptr, 0)))
440 delete [] newTable;
441 goto DONE_ERROR;
443 delete [] newTable;
444 UnlockResource(hglStringTable);
445 FreeResource(hglStringTable);
446 return TRUE;
447 DONE_ERROR:
448 UnlockResource(hglStringTable);
449 FreeResource(hglStringTable);
450 MYERROR;
453 BOOL CResModule::ExtractMenu(LPCTSTR lpszType)
455 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_MENU);
456 HGLOBAL hglMenuTemplate;
457 WORD version, offset;
458 const WORD *p, *p0;
460 if (!hrsrc)
461 MYERROR;
463 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
465 if (!hglMenuTemplate)
466 MYERROR;
468 p = (const WORD*)LockResource(hglMenuTemplate);
470 if (!p)
471 MYERROR;
473 // Standard MENU resource
474 //struct MenuHeader {
475 // WORD wVersion; // Currently zero
476 // WORD cbHeaderSize; // Also zero
477 //};
479 // MENUEX resource
480 //struct MenuExHeader {
481 // WORD wVersion; // One
482 // WORD wOffset;
483 // DWORD dwHelpId;
484 //};
485 p0 = p;
486 version = GET_WORD(p);
488 p++;
490 switch (version)
492 case 0:
494 offset = GET_WORD(p);
495 p += offset;
496 p++;
497 if (!ParseMenuResource(p))
498 goto DONE_ERROR;
500 break;
501 case 1:
503 offset = GET_WORD(p);
504 p++;
505 //dwHelpId = GET_DWORD(p);
506 if (!ParseMenuExResource(p0 + offset))
507 goto DONE_ERROR;
509 break;
510 default:
511 goto DONE_ERROR;
514 UnlockResource(hglMenuTemplate);
515 FreeResource(hglMenuTemplate);
516 return TRUE;
518 DONE_ERROR:
519 UnlockResource(hglMenuTemplate);
520 FreeResource(hglMenuTemplate);
521 MYERROR;
524 BOOL CResModule::ReplaceMenu(LPCTSTR lpszType, WORD wLanguage)
526 HRSRC hrsrc = FindResourceEx(m_hResDll, RT_MENU, lpszType, wLanguage);
527 HGLOBAL hglMenuTemplate;
528 WORD version, offset;
529 LPWSTR p;
530 WORD *p0;
532 if (!hrsrc)
533 MYERROR; //just the language wasn't found
535 hglMenuTemplate = LoadResource(m_hResDll, hrsrc);
537 if (!hglMenuTemplate)
538 MYERROR;
540 p = (LPWSTR)LockResource(hglMenuTemplate);
542 if (!p)
543 MYERROR;
545 //struct MenuHeader {
546 // WORD wVersion; // Currently zero
547 // WORD cbHeaderSize; // Also zero
548 //};
550 // MENUEX resource
551 //struct MenuExHeader {
552 // WORD wVersion; // One
553 // WORD wOffset;
554 // DWORD dwHelpId;
555 //};
556 p0 = (WORD *)p;
557 version = GET_WORD(p);
559 p++;
561 switch (version)
563 case 0:
565 offset = GET_WORD(p);
566 p += offset;
567 p++;
568 size_t nMem = 0;
569 if (!CountMemReplaceMenuResource((WORD*)p, &nMem, nullptr))
570 goto DONE_ERROR;
571 WORD * newMenu = new WORD[nMem + (nMem % 2)+2];
572 SecureZeroMemory(newMenu, (nMem + (nMem % 2)+2)*2);
573 size_t index = 2; // MenuHeader has 2 WORDs zero
574 if (!CountMemReplaceMenuResource((WORD *)p, &index, newMenu))
576 delete [] newMenu;
577 goto DONE_ERROR;
580 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2)+2)*2))
582 delete [] newMenu;
583 goto DONE_ERROR;
586 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
588 delete [] newMenu;
589 goto DONE_ERROR;
591 delete [] newMenu;
593 break;
594 case 1:
596 offset = GET_WORD(p);
597 p++;
598 //dwHelpId = GET_DWORD(p);
599 size_t nMem = 0;
600 if (!CountMemReplaceMenuExResource((WORD*)(p0 + offset), &nMem, nullptr))
601 goto DONE_ERROR;
602 WORD * newMenu = new WORD[nMem + (nMem % 2) + 4];
603 SecureZeroMemory(newMenu, (nMem + (nMem % 2) + 4) * 2);
604 CopyMemory(newMenu, p0, 2 * sizeof(WORD) + sizeof(DWORD));
605 size_t index = 4; // MenuExHeader has 2 x WORD + 1 x DWORD
606 if (!CountMemReplaceMenuExResource((WORD *)(p0 + offset), &index, newMenu))
608 delete [] newMenu;
609 goto DONE_ERROR;
612 if (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2) + 4) * 2))
614 delete [] newMenu;
615 goto DONE_ERROR;
618 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_MENU, lpszType, wLanguage, nullptr, 0)))
620 delete [] newMenu;
621 goto DONE_ERROR;
623 delete [] newMenu;
625 break;
626 default:
627 goto DONE_ERROR;
630 UnlockResource(hglMenuTemplate);
631 FreeResource(hglMenuTemplate);
632 return TRUE;
634 DONE_ERROR:
635 UnlockResource(hglMenuTemplate);
636 FreeResource(hglMenuTemplate);
637 MYERROR;
640 const WORD* CResModule::ParseMenuResource(const WORD * res)
642 WORD flags;
643 WORD id = 0;
645 //struct PopupMenuItem {
646 // WORD fItemFlags;
647 // WCHAR szItemText[];
648 //};
649 //struct NormalMenuItem {
650 // WORD fItemFlags;
651 // WORD wMenuID;
652 // WCHAR szItemText[];
653 //};
657 flags = GET_WORD(res);
658 res++;
659 if (!(flags & MF_POPUP))
661 id = GET_WORD(res); //normal menu item
662 res++;
664 else
665 id = (WORD)-1; //popup menu item
667 LPCWSTR str = (LPCWSTR)res;
668 size_t l = wcslen(str)+1;
669 res += l;
671 if (flags & MF_POPUP)
673 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
674 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
675 wcscpy(pBuf, str);
676 CUtils::StringExtend(pBuf);
678 std::wstring wstr = std::wstring(pBuf);
679 RESOURCEENTRY entry = m_StringEntries[wstr];
680 if (id)
681 InsertResourceIDs(RT_MENU, 0, entry, id, L" - PopupMenu");
683 m_StringEntries[wstr] = entry;
684 delete [] pBuf;
686 if ((res = ParseMenuResource(res))==0)
687 return nullptr;
689 else if (id != 0)
691 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
692 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
693 wcscpy(pBuf, str);
694 CUtils::StringExtend(pBuf);
696 std::wstring wstr = std::wstring(pBuf);
697 RESOURCEENTRY entry = m_StringEntries[wstr];
698 InsertResourceIDs(RT_MENU, 0, entry, id, L" - Menu");
700 TCHAR szTempBuf[1024] = { 0 };
701 swprintf(szTempBuf, L"#: MenuEntry; ID:%u", id);
702 MENUENTRY menu_entry;
703 menu_entry.wID = id;
704 menu_entry.reference = szTempBuf;
705 menu_entry.msgstr = wstr;
707 m_StringEntries[wstr] = entry;
708 m_MenuEntries[id] = menu_entry;
709 delete [] pBuf;
711 } while (!(flags & MF_END));
712 return res;
715 const WORD* CResModule::CountMemReplaceMenuResource(const WORD * res, size_t * wordcount, WORD * newMenu)
717 WORD flags;
718 WORD id = 0;
720 //struct PopupMenuItem {
721 // WORD fItemFlags;
722 // WCHAR szItemText[];
723 //};
724 //struct NormalMenuItem {
725 // WORD fItemFlags;
726 // WORD wMenuID;
727 // WCHAR szItemText[];
728 //};
732 flags = GET_WORD(res);
733 res++;
734 if (!newMenu)
735 (*wordcount)++;
736 else
737 newMenu[(*wordcount)++] = flags;
738 if (!(flags & MF_POPUP))
740 id = GET_WORD(res); //normal menu item
741 res++;
742 if (!newMenu)
743 (*wordcount)++;
744 else
745 newMenu[(*wordcount)++] = id;
747 else
748 id = (WORD)-1; //popup menu item
750 if (flags & MF_POPUP)
752 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
753 res += wcslen((LPCWSTR)res) + 1;
755 if ((res = CountMemReplaceMenuResource(res, wordcount, newMenu))==0)
756 return nullptr;
758 else if (id != 0)
760 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
761 res += wcslen((LPCWSTR)res) + 1;
763 else
765 if (newMenu)
766 wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res);
767 (*wordcount) += wcslen((LPCWSTR)res) + 1;
768 res += wcslen((LPCWSTR)res) + 1;
770 } while (!(flags & MF_END));
771 return res;
774 const WORD* CResModule::ParseMenuExResource(const WORD * res)
776 WORD bResInfo;
778 //struct MenuExItem {
779 // DWORD dwType;
780 // DWORD dwState;
781 // DWORD menuId;
782 // WORD bResInfo;
783 // WCHAR szText[];
784 // DWORD dwHelpId; - Popup menu only
785 //};
789 DWORD dwType = GET_DWORD(res);
790 res += 2;
791 //dwState = GET_DWORD(res);
792 res += 2;
793 DWORD menuId = GET_DWORD(res);
794 res += 2;
795 bResInfo = GET_WORD(res);
796 res++;
798 LPCWSTR str = (LPCWSTR)res;
799 size_t l = wcslen(str)+1;
800 res += l;
801 // Align to DWORD boundary
802 res = AlignWORD(res);
804 if (dwType & MFT_SEPARATOR)
805 continue;
807 if (bResInfo & 0x01)
809 // Popup menu - note this can also have a non-zero ID
810 if (menuId == 0)
811 menuId = (WORD)-1;
812 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
813 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
814 wcscpy(pBuf, str);
815 CUtils::StringExtend(pBuf);
817 std::wstring wstr = std::wstring(pBuf);
818 RESOURCEENTRY entry = m_StringEntries[wstr];
819 // Popup has a DWORD help entry on a DWORD boundary - skip over it
820 res += 2;
822 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - PopupMenuEx");
823 TCHAR szTempBuf[1024] = { 0 };
824 swprintf(szTempBuf, L"#: MenuExPopupEntry; ID:%lu", menuId);
825 MENUENTRY menu_entry;
826 menu_entry.wID = (WORD)menuId;
827 menu_entry.reference = szTempBuf;
828 menu_entry.msgstr = wstr;
829 m_StringEntries[wstr] = entry;
830 m_MenuEntries[(WORD)menuId] = menu_entry;
831 delete [] pBuf;
833 if ((res = ParseMenuExResource(res)) == 0)
834 return nullptr;
835 } else if (menuId != 0)
837 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
838 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
839 wcscpy(pBuf, str);
840 CUtils::StringExtend(pBuf);
842 std::wstring wstr = std::wstring(pBuf);
843 RESOURCEENTRY entry = m_StringEntries[wstr];
844 InsertResourceIDs(RT_MENU, 0, entry, menuId, L" - MenuEx");
846 TCHAR szTempBuf[1024] = { 0 };
847 swprintf(szTempBuf, L"#: MenuExEntry; ID:%lu", menuId);
848 MENUENTRY menu_entry;
849 menu_entry.wID = (WORD)menuId;
850 menu_entry.reference = szTempBuf;
851 menu_entry.msgstr = wstr;
852 m_StringEntries[wstr] = entry;
853 m_MenuEntries[(WORD)menuId] = menu_entry;
854 delete [] pBuf;
856 } while (!(bResInfo & 0x80));
857 return res;
860 const WORD* CResModule::CountMemReplaceMenuExResource(const WORD * res, size_t * wordcount, WORD * newMenu)
862 WORD bResInfo;
864 //struct MenuExItem {
865 // DWORD dwType;
866 // DWORD dwState;
867 // DWORD menuId;
868 // WORD bResInfo;
869 // WCHAR szText[];
870 // DWORD dwHelpId; - Popup menu only
871 //};
875 WORD * p0 = (WORD *)res;
876 DWORD dwType = GET_DWORD(res);
877 res += 2;
878 //dwState = GET_DWORD(res);
879 res += 2;
880 DWORD menuId = GET_DWORD(res);
881 res += 2;
882 bResInfo = GET_WORD(res);
883 res++;
885 if (newMenu)
886 CopyMemory(&newMenu[*wordcount], p0, 7 * sizeof(WORD));
888 (*wordcount) += 7;
890 if (dwType & MFT_SEPARATOR) {
891 // Align to DWORD
892 (*wordcount)++;
893 res++;
894 continue;
897 if (bResInfo & 0x01)
899 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
900 res += wcslen((LPCWSTR)res) + 1;
901 // Align to DWORD
902 res = AlignWORD(res);
903 if ((*wordcount) & 0x01)
904 (*wordcount)++;
906 if (newMenu)
907 CopyMemory(&newMenu[*wordcount], res, sizeof(DWORD)); // Copy Help ID
909 res += 2;
910 (*wordcount) += 2;
912 if ((res = CountMemReplaceMenuExResource(res, wordcount, newMenu)) == 0)
913 return nullptr;
915 else if (menuId != 0)
917 ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings);
918 res += wcslen((LPCWSTR)res) + 1;
920 else
922 if (newMenu)
923 wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res);
924 (*wordcount) += wcslen((LPCWSTR)res) + 1;
925 res += wcslen((LPCWSTR)res) + 1;
927 // Align to DWORD
928 res = AlignWORD(res);
929 if ((*wordcount) & 0x01)
930 (*wordcount)++;
931 } while (!(bResInfo & 0x80));
932 return res;
935 BOOL CResModule::ExtractAccelerator(LPCTSTR lpszType)
937 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_ACCELERATOR);
938 HGLOBAL hglAccTable;
939 WORD fFlags, wAnsi, wID;
940 const WORD* p;
941 bool bEnd(false);
943 if (!hrsrc)
944 MYERROR;
946 hglAccTable = LoadResource(m_hResDll, hrsrc);
948 if (!hglAccTable)
949 goto DONE_ERROR;
951 p = (const WORD*)LockResource(hglAccTable);
953 if (!p)
954 MYERROR;
957 struct ACCELTABLEENTRY
959 WORD fFlags; FVIRTKEY, FSHIFT, FCONTROL, FALT, 0x80 - Last in a table
960 WORD wAnsi; ANSI character
961 WORD wId; Keyboard accelerator passed to windows
962 WORD padding; # bytes added to ensure aligned to DWORD boundary
968 fFlags = GET_WORD(p);
969 p++;
970 wAnsi = GET_WORD(p);
971 p++;
972 wID = GET_WORD(p);
973 p++;
974 p++; // Skip over padding
976 if ((fFlags & 0x80) == 0x80)
977 { // 0x80
978 bEnd = true;
981 if ((wAnsi < 0x30) ||
982 (wAnsi > 0x5A) ||
983 (wAnsi >= 0x3A && wAnsi <= 0x40))
984 continue;
986 auto pBuf = std::make_unique<WCHAR[]>(1024);
987 auto pBuf2 = std::make_unique<WCHAR[]>(1024);
988 SecureZeroMemory(pBuf.get(), 1024 * sizeof(WCHAR));
989 SecureZeroMemory(pBuf2.get(), 1024 * sizeof(WCHAR));
991 // include the menu ID in the msgid to make sure that 'duplicate'
992 // accelerator keys are listed in the po-file.
993 // without this, we would get entries like this:
994 //#. Accelerator Entry for Menu ID:32809; '&Filter'
995 //#. Accelerator Entry for Menu ID:57636; '&Find'
996 //#: Corresponding Menu ID:32771; '&Find'
997 //msgid "V C +F"
998 //msgstr ""
1000 // Since "filter" and "find" are most likely translated to words starting
1001 // with different letters, we need to have a separate accelerator entry
1002 // for each of those
1003 swprintf(pBuf.get(), L"ID:%u:", wID);
1005 // EXACTLY 5 characters long "ACS+X"
1006 // V = Virtual key (or blank if not used)
1007 // A = Alt key (or blank if not used)
1008 // C = Ctrl key (or blank if not used)
1009 // S = Shift key (or blank if not used)
1010 // X = upper case character
1011 // e.g. "V CS+Q" == Ctrl + Shift + 'Q'
1012 if ((fFlags & FVIRTKEY) == FVIRTKEY) // 0x01
1013 wcscat(pBuf.get(), L"V");
1014 else
1015 wcscat(pBuf.get(), L" ");
1017 if ((fFlags & FALT) == FALT) // 0x10
1018 wcscat(pBuf.get(), L"A");
1019 else
1020 wcscat(pBuf.get(), L" ");
1022 if ((fFlags & FCONTROL) == FCONTROL) // 0x08
1023 wcscat(pBuf.get(), L"C");
1024 else
1025 wcscat(pBuf.get(), L" ");
1027 if ((fFlags & FSHIFT) == FSHIFT) // 0x04
1028 wcscat(pBuf.get(), L"S");
1029 else
1030 wcscat(pBuf.get(), L" ");
1032 swprintf(pBuf2.get(), L"%s+%c", pBuf.get(), wAnsi);
1034 std::wstring wstr = std::wstring(pBuf2.get());
1035 RESOURCEENTRY AKey_entry = m_StringEntries[wstr];
1037 TCHAR szTempBuf[1024] = { 0 };
1038 SecureZeroMemory(szTempBuf, sizeof (szTempBuf));
1039 std::wstring wmenu;
1040 pME_iter = m_MenuEntries.find(wID);
1041 if (pME_iter != m_MenuEntries.end())
1043 wmenu = pME_iter->second.msgstr;
1045 swprintf(szTempBuf, L"#. Accelerator Entry for Menu ID:%u; '%s'", wID, wmenu.c_str());
1046 AKey_entry.automaticcomments.push_back(std::wstring(szTempBuf));
1048 m_StringEntries[wstr] = AKey_entry;
1049 } while (!bEnd);
1051 UnlockResource(hglAccTable);
1052 FreeResource(hglAccTable);
1053 return TRUE;
1055 DONE_ERROR:
1056 UnlockResource(hglAccTable);
1057 FreeResource(hglAccTable);
1058 MYERROR;
1061 BOOL CResModule::ReplaceAccelerator(LPCTSTR lpszType, WORD wLanguage)
1063 LPACCEL lpaccelNew; // pointer to new accelerator table
1064 HACCEL haccelOld; // handle to old accelerator table
1065 int cAccelerators; // number of accelerators in table
1066 HGLOBAL hglAccTableNew;
1067 const WORD* p;
1068 int i;
1070 haccelOld = LoadAccelerators(m_hResDll, lpszType);
1072 if (!haccelOld)
1073 MYERROR;
1075 cAccelerators = CopyAcceleratorTable(haccelOld, nullptr, 0);
1077 lpaccelNew = (LPACCEL) LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL));
1079 if (!lpaccelNew)
1080 MYERROR;
1082 CopyAcceleratorTable(haccelOld, lpaccelNew, cAccelerators);
1084 // Find the accelerator that the user modified
1085 // and change its flags and virtual-key code
1086 // as appropriate.
1088 BYTE xfVirt;
1089 wchar_t xkey;
1090 static const size_t BufferSize = 1024;
1091 auto pBuf = std::make_unique<WCHAR[]>(BufferSize);
1092 auto pBuf2 = std::make_unique<WCHAR[]>(BufferSize);
1093 for (i = 0; i < cAccelerators; i++)
1095 if ((lpaccelNew[i].key < 0x30) ||
1096 (lpaccelNew[i].key > 0x5A) ||
1097 (lpaccelNew[i].key >= 0x3A && lpaccelNew[i].key <= 0x40))
1098 continue;
1100 SecureZeroMemory(pBuf.get(), 1024 * sizeof(WCHAR));
1101 SecureZeroMemory(pBuf2.get(), 1024 * sizeof(WCHAR));
1103 swprintf(pBuf.get(), L"ID:%d:", lpaccelNew[i].cmd);
1105 // get original key combination
1106 if ((lpaccelNew[i].fVirt & FVIRTKEY) == FVIRTKEY) // 0x01
1107 wcscat(pBuf.get(), L"V");
1108 else
1109 wcscat(pBuf.get(), L" ");
1111 if ((lpaccelNew[i].fVirt & FALT) == FALT) // 0x10
1112 wcscat(pBuf.get(), L"A");
1113 else
1114 wcscat(pBuf.get(), L" ");
1116 if ((lpaccelNew[i].fVirt & FCONTROL) == FCONTROL) // 0x08
1117 wcscat(pBuf.get(), L"C");
1118 else
1119 wcscat(pBuf.get(), L" ");
1121 if ((lpaccelNew[i].fVirt & FSHIFT) == FSHIFT) // 0x04
1122 wcscat(pBuf.get(), L"S");
1123 else
1124 wcscat(pBuf.get(), L" ");
1126 swprintf(pBuf2.get(), L"%s+%c", pBuf.get(), lpaccelNew[i].key);
1128 // Is it there?
1129 std::map<std::wstring, RESOURCEENTRY>::iterator pAK_iter = m_StringEntries.find(pBuf2.get());
1130 if (pAK_iter != m_StringEntries.end())
1132 m_bTranslatedAcceleratorStrings++;
1133 xfVirt = 0;
1134 xkey = 0;
1135 std::wstring wtemp = pAK_iter->second.msgstr;
1136 wtemp = wtemp.substr(wtemp.find_last_of(':')+1);
1137 if (wtemp.size() != 6)
1138 continue;
1139 if (wtemp.compare(0, 1, L"V") == 0)
1140 xfVirt |= FVIRTKEY;
1141 else if (wtemp.compare(0, 1, L" ") != 0)
1142 continue; // not a space - user must have made a mistake when translating
1143 if (wtemp.compare(1, 1, L"A") == 0)
1144 xfVirt |= FALT;
1145 else if (wtemp.compare(1, 1, L" ") != 0)
1146 continue; // not a space - user must have made a mistake when translating
1147 if (wtemp.compare(2, 1, L"C") == 0)
1148 xfVirt |= FCONTROL;
1149 else if (wtemp.compare(2, 1, L" ") != 0)
1150 continue; // not a space - user must have made a mistake when translating
1151 if (wtemp.compare(3, 1, L"S") == 0)
1152 xfVirt |= FSHIFT;
1153 else if (wtemp.compare(3, 1, L" ") != 0)
1154 continue; // not a space - user must have made a mistake when translating
1155 if (wtemp.compare(4, 1, L"+") == 0)
1157 swscanf(wtemp.substr(5, 1).c_str(), L"%c", &xkey);
1158 lpaccelNew[i].fVirt = xfVirt;
1159 lpaccelNew[i].key = (DWORD)xkey;
1162 else
1163 m_bDefaultAcceleratorStrings++;
1167 // Create the new accelerator table
1168 hglAccTableNew = LocalAlloc(LPTR, cAccelerators * 4 * sizeof(WORD));
1169 if (!hglAccTableNew)
1170 goto DONE_ERROR;
1171 p = (WORD *)hglAccTableNew;
1172 lpaccelNew[cAccelerators-1].fVirt |= 0x80;
1173 for (i = 0; i < cAccelerators; i++)
1175 memcpy((void *)p, &lpaccelNew[i].fVirt, 1);
1176 p++;
1177 memcpy((void *)p, &lpaccelNew[i].key, sizeof(WORD));
1178 p++;
1179 memcpy((void *)p, &lpaccelNew[i].cmd, sizeof(WORD));
1180 p++;
1181 p++;
1184 if (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType,
1185 (m_wTargetLang ? m_wTargetLang : wLanguage), hglAccTableNew /* haccelNew*/, cAccelerators * 4 * sizeof(WORD)))
1187 goto DONE_ERROR;
1190 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, lpszType, wLanguage, nullptr, 0)))
1192 goto DONE_ERROR;
1195 LocalFree(hglAccTableNew);
1196 LocalFree(lpaccelNew);
1197 return TRUE;
1199 DONE_ERROR:
1200 LocalFree(hglAccTableNew);
1201 LocalFree(lpaccelNew);
1202 MYERROR;
1205 BOOL CResModule::ExtractDialog(LPCTSTR lpszType)
1207 const WORD* lpDlg;
1208 const WORD* lpDlgItem;
1209 DIALOGINFO dlg;
1210 DLGITEMINFO dlgItem;
1211 WORD bNumControls;
1212 HRSRC hrsrc;
1213 HGLOBAL hGlblDlgTemplate;
1215 hrsrc = FindResource(m_hResDll, lpszType, RT_DIALOG);
1217 if (!hrsrc)
1218 MYERROR;
1220 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1221 if (!hGlblDlgTemplate)
1222 MYERROR;
1224 lpDlg = (const WORD*) LockResource(hGlblDlgTemplate);
1226 if (!lpDlg)
1227 MYERROR;
1229 lpDlgItem = (const WORD*) GetDialogInfo(lpDlg, &dlg);
1230 bNumControls = dlg.nbItems;
1232 if (dlg.caption)
1234 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
1235 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
1236 wcscpy(pBuf, dlg.caption);
1237 CUtils::StringExtend(pBuf);
1239 std::wstring wstr = std::wstring(pBuf);
1240 RESOURCEENTRY entry = m_StringEntries[wstr];
1241 InsertResourceIDs(RT_DIALOG, (INT_PTR)lpszType, entry, (INT_PTR)lpszType, L"");
1243 m_StringEntries[wstr] = entry;
1244 delete [] pBuf;
1247 while (bNumControls-- != 0)
1249 TCHAR szTitle[500] = { 0 };
1250 SecureZeroMemory(szTitle, sizeof(szTitle));
1251 BOOL bCode;
1253 lpDlgItem = GetControlInfo((WORD *) lpDlgItem, &dlgItem, dlg.dialogEx, &bCode);
1255 if (bCode == FALSE)
1256 wcsncpy(szTitle, dlgItem.windowName, _countof(szTitle) - 1);
1258 if (szTitle[0])
1260 CUtils::StringExtend(szTitle);
1262 std::wstring wstr = std::wstring(szTitle);
1263 RESOURCEENTRY entry = m_StringEntries[wstr];
1264 InsertResourceIDs(RT_DIALOG, (INT_PTR)lpszType, entry, dlgItem.id, L"");
1266 m_StringEntries[wstr] = entry;
1270 UnlockResource(hGlblDlgTemplate);
1271 FreeResource(hGlblDlgTemplate);
1272 return (TRUE);
1275 BOOL CResModule::ReplaceDialog(LPCTSTR lpszType, WORD wLanguage)
1277 const WORD* lpDlg;
1278 HRSRC hrsrc;
1279 HGLOBAL hGlblDlgTemplate;
1281 hrsrc = FindResourceEx(m_hResDll, RT_DIALOG, lpszType, wLanguage);
1283 if (!hrsrc)
1284 MYERROR;
1286 hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc);
1288 if (!hGlblDlgTemplate)
1289 MYERROR;
1291 lpDlg = (WORD *) LockResource(hGlblDlgTemplate);
1293 if (!lpDlg)
1294 MYERROR;
1296 size_t nMem = 0;
1297 const WORD * p = lpDlg;
1298 if (!CountMemReplaceDialogResource(p, &nMem, nullptr))
1299 goto DONE_ERROR;
1300 WORD * newDialog = new WORD[nMem + (nMem % 2)];
1301 SecureZeroMemory(newDialog, (nMem + (nMem % 2))*2);
1303 size_t index = 0;
1304 if (!CountMemReplaceDialogResource(lpDlg, &index, newDialog))
1306 delete [] newDialog;
1307 goto DONE_ERROR;
1310 if (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), newDialog, (DWORD)(nMem + (nMem % 2))*2))
1312 delete [] newDialog;
1313 goto DONE_ERROR;
1316 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_DIALOG, lpszType, wLanguage, nullptr, 0)))
1318 delete [] newDialog;
1319 goto DONE_ERROR;
1322 delete [] newDialog;
1323 UnlockResource(hGlblDlgTemplate);
1324 FreeResource(hGlblDlgTemplate);
1325 return TRUE;
1327 DONE_ERROR:
1328 UnlockResource(hGlblDlgTemplate);
1329 FreeResource(hGlblDlgTemplate);
1330 MYERROR;
1333 const WORD* CResModule::GetDialogInfo(const WORD * pTemplate, LPDIALOGINFO lpDlgInfo) const
1335 const WORD* p = (const WORD *)pTemplate;
1337 lpDlgInfo->style = GET_DWORD(p);
1338 p += 2;
1340 if (lpDlgInfo->style == 0xffff0001) // DIALOGEX resource
1342 lpDlgInfo->dialogEx = TRUE;
1343 lpDlgInfo->helpId = GET_DWORD(p);
1344 p += 2;
1345 lpDlgInfo->exStyle = GET_DWORD(p);
1346 p += 2;
1347 lpDlgInfo->style = GET_DWORD(p);
1348 p += 2;
1350 else
1352 lpDlgInfo->dialogEx = FALSE;
1353 lpDlgInfo->helpId = 0;
1354 lpDlgInfo->exStyle = GET_DWORD(p);
1355 p += 2;
1358 lpDlgInfo->nbItems = GET_WORD(p);
1359 p++;
1361 lpDlgInfo->x = GET_WORD(p);
1362 p++;
1364 lpDlgInfo->y = GET_WORD(p);
1365 p++;
1367 lpDlgInfo->cx = GET_WORD(p);
1368 p++;
1370 lpDlgInfo->cy = GET_WORD(p);
1371 p++;
1373 // Get the menu name
1375 switch (GET_WORD(p))
1377 case 0x0000:
1378 lpDlgInfo->menuName = nullptr;
1379 p++;
1380 break;
1381 case 0xffff:
1382 lpDlgInfo->menuName = (LPCTSTR) (WORD) GET_WORD(p + 1);
1383 p += 2;
1384 break;
1385 default:
1386 lpDlgInfo->menuName = (LPCTSTR) p;
1387 p += wcslen((LPCWSTR) p) + 1;
1388 break;
1391 // Get the class name
1393 switch (GET_WORD(p))
1395 case 0x0000:
1396 lpDlgInfo->className = (LPCTSTR)MAKEINTATOM(32770);
1397 p++;
1398 break;
1399 case 0xffff:
1400 lpDlgInfo->className = (LPCTSTR) (WORD) GET_WORD(p + 1);
1401 p += 2;
1402 break;
1403 default:
1404 lpDlgInfo->className = (LPCTSTR) p;
1405 p += wcslen((LPCTSTR)p) + 1;
1406 break;
1409 // Get the window caption
1411 lpDlgInfo->caption = (LPCTSTR)p;
1412 p += wcslen((LPCWSTR) p) + 1;
1414 // Get the font name
1416 if (lpDlgInfo->style & DS_SETFONT)
1418 lpDlgInfo->pointSize = GET_WORD(p);
1419 p++;
1421 if (lpDlgInfo->dialogEx)
1423 lpDlgInfo->weight = GET_WORD(p);
1424 p++;
1425 lpDlgInfo->italic = LOBYTE(GET_WORD(p));
1426 p++;
1428 else
1430 lpDlgInfo->weight = FW_DONTCARE;
1431 lpDlgInfo->italic = FALSE;
1434 lpDlgInfo->faceName = (LPCTSTR)p;
1435 p += wcslen((LPCWSTR) p) + 1;
1437 // First control is on DWORD boundary
1438 p = AlignWORD(p);
1440 return p;
1443 const WORD* CResModule::GetControlInfo(const WORD* p, LPDLGITEMINFO lpDlgItemInfo, BOOL dialogEx, LPBOOL bIsID) const
1445 if (dialogEx)
1447 lpDlgItemInfo->helpId = GET_DWORD(p);
1448 p += 2;
1449 lpDlgItemInfo->exStyle = GET_DWORD(p);
1450 p += 2;
1451 lpDlgItemInfo->style = GET_DWORD(p);
1452 p += 2;
1454 else
1456 lpDlgItemInfo->helpId = 0;
1457 lpDlgItemInfo->style = GET_DWORD(p);
1458 p += 2;
1459 lpDlgItemInfo->exStyle = GET_DWORD(p);
1460 p += 2;
1463 lpDlgItemInfo->x = GET_WORD(p);
1464 p++;
1466 lpDlgItemInfo->y = GET_WORD(p);
1467 p++;
1469 lpDlgItemInfo->cx = GET_WORD(p);
1470 p++;
1472 lpDlgItemInfo->cy = GET_WORD(p);
1473 p++;
1475 if (dialogEx)
1477 // ID is a DWORD for DIALOGEX
1478 lpDlgItemInfo->id = (WORD) GET_DWORD(p);
1479 p += 2;
1481 else
1483 lpDlgItemInfo->id = GET_WORD(p);
1484 p++;
1487 if (GET_WORD(p) == 0xffff)
1489 GET_WORD(p + 1);
1491 p += 2;
1493 else
1495 lpDlgItemInfo->className = (LPCTSTR) p;
1496 p += wcslen((LPCWSTR) p) + 1;
1499 if (GET_WORD(p) == 0xffff) // an integer ID?
1501 *bIsID = TRUE;
1502 lpDlgItemInfo->windowName = (LPCTSTR) (UINT_PTR) GET_WORD(p + 1);
1503 p += 2;
1505 else
1507 *bIsID = FALSE;
1508 lpDlgItemInfo->windowName = (LPCTSTR) p;
1509 p += wcslen((LPCWSTR) p) + 1;
1512 if (GET_WORD(p))
1514 lpDlgItemInfo->data = (LPVOID) (p + 1);
1515 p += GET_WORD(p) / sizeof(WORD);
1517 else
1518 lpDlgItemInfo->data = nullptr;
1520 p++;
1521 // Next control is on DWORD boundary
1522 p = AlignWORD(p);
1523 return p;
1526 const WORD * CResModule::CountMemReplaceDialogResource(const WORD * res, size_t * wordcount, WORD * newDialog)
1528 BOOL bEx = FALSE;
1529 DWORD style = GET_DWORD(res);
1530 if (newDialog)
1532 newDialog[(*wordcount)++] = GET_WORD(res++);
1533 newDialog[(*wordcount)++] = GET_WORD(res++);
1535 else
1537 res += 2;
1538 (*wordcount) += 2;
1541 if (style == 0xffff0001) // DIALOGEX resource
1543 bEx = TRUE;
1544 if (newDialog)
1546 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1547 newDialog[(*wordcount)++] = GET_WORD(res++); //help id
1548 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1549 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1550 style = GET_DWORD(res);
1551 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1552 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1554 else
1556 res += 4;
1557 style = GET_DWORD(res);
1558 res += 2;
1559 (*wordcount) += 6;
1562 else
1564 bEx = FALSE;
1565 if (newDialog)
1567 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1568 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1569 //style = GET_DWORD(res);
1570 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1571 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1573 else
1575 res += 2;
1576 (*wordcount) += 2;
1580 if (newDialog)
1581 newDialog[(*wordcount)] = GET_WORD(res);
1582 WORD nbItems = GET_WORD(res);
1583 (*wordcount)++;
1584 res++;
1586 if (newDialog)
1587 newDialog[(*wordcount)] = GET_WORD(res); //x
1588 (*wordcount)++;
1589 res++;
1591 if (newDialog)
1592 newDialog[(*wordcount)] = GET_WORD(res); //y
1593 (*wordcount)++;
1594 res++;
1596 if (newDialog)
1597 newDialog[(*wordcount)] = GET_WORD(res); //cx
1598 (*wordcount)++;
1599 res++;
1601 if (newDialog)
1602 newDialog[(*wordcount)] = GET_WORD(res); //cy
1603 (*wordcount)++;
1604 res++;
1606 // Get the menu name
1608 switch (GET_WORD(res))
1610 case 0x0000:
1611 if (newDialog)
1612 newDialog[(*wordcount)] = GET_WORD(res);
1613 (*wordcount)++;
1614 res++;
1615 break;
1616 case 0xffff:
1617 if (newDialog)
1619 newDialog[(*wordcount)++] = GET_WORD(res++);
1620 newDialog[(*wordcount)++] = GET_WORD(res++);
1622 else
1624 (*wordcount) += 2;
1625 res += 2;
1627 break;
1628 default:
1629 if (newDialog)
1631 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1633 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1634 res += wcslen((LPCWSTR) res) + 1;
1635 break;
1638 // Get the class name
1640 switch (GET_WORD(res))
1642 case 0x0000:
1643 if (newDialog)
1644 newDialog[(*wordcount)] = GET_WORD(res);
1645 (*wordcount)++;
1646 res++;
1647 break;
1648 case 0xffff:
1649 if (newDialog)
1651 newDialog[(*wordcount)++] = GET_WORD(res++);
1652 newDialog[(*wordcount)++] = GET_WORD(res++);
1654 else
1656 (*wordcount) += 2;
1657 res += 2;
1659 break;
1660 default:
1661 if (newDialog)
1663 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1665 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1666 res += wcslen((LPCWSTR) res) + 1;
1667 break;
1670 // Get the window caption
1672 ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1673 res += wcslen((LPCWSTR)res) + 1;
1675 // Get the font name
1677 if (style & DS_SETFONT)
1679 if (newDialog)
1680 newDialog[(*wordcount)] = GET_WORD(res);
1681 res++;
1682 (*wordcount)++;
1684 if (bEx)
1686 if (newDialog)
1688 newDialog[(*wordcount)++] = GET_WORD(res++);
1689 newDialog[(*wordcount)++] = GET_WORD(res++);
1691 else
1693 res += 2;
1694 (*wordcount) += 2;
1698 if (newDialog)
1699 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1700 (*wordcount) += wcslen((LPCWSTR)res) + 1;
1701 res += wcslen((LPCWSTR)res) + 1;
1703 // First control is on DWORD boundary
1704 while ((*wordcount)%2)
1705 (*wordcount)++;
1706 while ((UINT_PTR)res % 4)
1707 res++;
1709 while (nbItems--)
1711 res = ReplaceControlInfo(res, wordcount, newDialog, bEx);
1713 return res;
1716 const WORD* CResModule::ReplaceControlInfo(const WORD * res, size_t * wordcount, WORD * newDialog, BOOL bEx)
1718 if (bEx)
1720 if (newDialog)
1722 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1723 newDialog[(*wordcount)++] = GET_WORD(res++); //helpid
1725 else
1727 res += 2;
1728 (*wordcount) += 2;
1731 if (newDialog)
1733 LONG * exStyle = (LONG*)&newDialog[(*wordcount)];
1734 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1735 newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle
1736 if (m_bRTL)
1737 *exStyle |= WS_EX_RTLREADING;
1739 else
1741 res += 2;
1742 (*wordcount) += 2;
1745 if (newDialog)
1747 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1748 newDialog[(*wordcount)++] = GET_WORD(res++); //style
1750 else
1752 res += 2;
1753 (*wordcount) += 2;
1756 if (newDialog)
1757 newDialog[(*wordcount)] = GET_WORD(res); //x
1758 res++;
1759 (*wordcount)++;
1761 if (newDialog)
1762 newDialog[(*wordcount)] = GET_WORD(res); //y
1763 res++;
1764 (*wordcount)++;
1766 if (newDialog)
1767 newDialog[(*wordcount)] = GET_WORD(res); //cx
1768 res++;
1769 (*wordcount)++;
1771 if (newDialog)
1772 newDialog[(*wordcount)] = GET_WORD(res); //cy
1773 res++;
1774 (*wordcount)++;
1776 if (bEx)
1778 // ID is a DWORD for DIALOGEX
1779 if (newDialog)
1781 newDialog[(*wordcount)++] = GET_WORD(res++);
1782 newDialog[(*wordcount)++] = GET_WORD(res++);
1784 else
1786 res += 2;
1787 (*wordcount) += 2;
1790 else
1792 if (newDialog)
1793 newDialog[(*wordcount)] = GET_WORD(res);
1794 res++;
1795 (*wordcount)++;
1798 if (GET_WORD(res) == 0xffff) //classID
1800 if (newDialog)
1802 newDialog[(*wordcount)++] = GET_WORD(res++);
1803 newDialog[(*wordcount)++] = GET_WORD(res++);
1805 else
1807 res += 2;
1808 (*wordcount) += 2;
1811 else
1813 if (newDialog)
1814 wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res);
1815 (*wordcount) += wcslen((LPCWSTR) res) + 1;
1816 res += wcslen((LPCWSTR) res) + 1;
1819 if (GET_WORD(res) == 0xffff) // an integer ID?
1821 if (newDialog)
1823 newDialog[(*wordcount)++] = GET_WORD(res++);
1824 newDialog[(*wordcount)++] = GET_WORD(res++);
1826 else
1828 res += 2;
1829 (*wordcount) += 2;
1832 else
1834 ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings);
1835 res += wcslen((LPCWSTR)res) + 1;
1838 if (newDialog)
1839 memcpy(&newDialog[(*wordcount)], res, (GET_WORD(res)+1)*sizeof(WORD));
1840 (*wordcount) += (GET_WORD(res)+1);
1841 res += (GET_WORD(res)+1);
1842 // Next control is on DWORD boundary
1843 while ((*wordcount) % 2)
1844 (*wordcount)++;
1845 res = AlignWORD(res);
1847 return res;
1850 BOOL CResModule::ExtractRibbon(LPCTSTR lpszType)
1852 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1853 HGLOBAL hglRibbonTemplate;
1854 const BYTE *p;
1856 if (!hrsrc)
1857 MYERROR;
1859 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1861 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1863 if (!hglRibbonTemplate)
1864 MYERROR;
1866 p = (const BYTE*)LockResource(hglRibbonTemplate);
1868 if (!p)
1869 MYERROR;
1871 // Resource consists of one single string
1872 // that is XML.
1874 // extract all <id><name>blah1</name><value>blah2</value></id><text>blah</text> elements
1876 const std::regex regRevMatch("<ID><NAME>([^<]+)</NAME><VALUE>([^<]+)</VALUE></ID><TEXT>([^<]+)</TEXT>");
1877 std::string ss = std::string((const char*)p, sizeres);
1878 const std::sregex_iterator end;
1879 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
1881 size_t len;
1883 std::string str1 = (*it)[1];
1884 len = str1.size();
1885 auto bufw1 = std::make_unique<wchar_t[]>(len * 4 + 1);
1886 SecureZeroMemory(bufw1.get(), (len * 4 + 1) * sizeof(wchar_t));
1887 MultiByteToWideChar(CP_UTF8, 0, str1.c_str(), -1, bufw1.get(), (int)len * 4);
1888 std::wstring strIdNameVal = bufw1.get();
1889 strIdNameVal += L" - Ribbon name";
1891 std::string str2 = (*it)[2];
1892 len = str2.size();
1893 auto bufw2 = std::make_unique<wchar_t[]>(len * 4 + 1);
1894 SecureZeroMemory(bufw2.get(), (len * 4 + 1)*sizeof(wchar_t));
1895 MultiByteToWideChar(CP_UTF8, 0, str2.c_str(), -1, bufw2.get(), (int)len * 4);
1896 std::wstring strIdVal = bufw2.get();
1898 std::string str3 = (*it)[3];
1899 len = str3.size();
1900 auto bufw3 = std::make_unique<wchar_t[]>(len * 4 + 1);
1901 SecureZeroMemory(bufw3.get(), (len * 4 + 1)*sizeof(wchar_t));
1902 MultiByteToWideChar(CP_UTF8, 0, str3.c_str(), -1, bufw3.get(), (int)len * 4);
1903 std::wstring str = bufw3.get();
1905 RESOURCEENTRY entry = m_StringEntries[str];
1906 InsertResourceIDs(RT_RIBBON, 0, entry, std::stoi(strIdVal), strIdNameVal.c_str());
1907 if (wcschr(str.c_str(), '%'))
1908 entry.flag = L"#, c-format";
1909 m_StringEntries[str] = entry;
1910 m_bDefaultRibbonTexts++;
1913 // extract all </ELEMENT_NAME><NAME>blahblah</NAME> elements
1915 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1916 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
1918 std::string str = (*it)[1];
1919 size_t len = str.size();
1920 auto bufw = std::make_unique<wchar_t[]>(len * 4 + 1);
1921 SecureZeroMemory(bufw.get(), (len*4 + 1)*sizeof(wchar_t));
1922 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw.get(), (int)len*4);
1923 std::wstring ret = bufw.get();
1924 RESOURCEENTRY entry = m_StringEntries[ret];
1925 InsertResourceIDs(RT_RIBBON, 0, entry, (INT_PTR)lpszType, L" - Ribbon element");
1926 if (wcschr(ret.c_str(), '%'))
1927 entry.flag = L"#, c-format";
1928 m_StringEntries[ret] = entry;
1929 m_bDefaultRibbonTexts++;
1932 UnlockResource(hglRibbonTemplate);
1933 FreeResource(hglRibbonTemplate);
1934 return TRUE;
1937 BOOL CResModule::ReplaceRibbon(LPCTSTR lpszType, WORD wLanguage)
1939 HRSRC hrsrc = FindResource(m_hResDll, lpszType, RT_RIBBON);
1940 HGLOBAL hglRibbonTemplate;
1941 const BYTE *p;
1943 if (!hrsrc)
1944 MYERROR;
1946 hglRibbonTemplate = LoadResource(m_hResDll, hrsrc);
1948 DWORD sizeres = SizeofResource(m_hResDll, hrsrc);
1950 if (!hglRibbonTemplate)
1951 MYERROR;
1953 p = (const BYTE*)LockResource(hglRibbonTemplate);
1955 if (!p)
1956 MYERROR;
1958 std::string ss = std::string((const char*)p, sizeres);
1959 size_t len = ss.size();
1960 auto bufw = std::make_unique<wchar_t[]>(len * 4 + 1);
1961 SecureZeroMemory(bufw.get(), (len*4 + 1)*sizeof(wchar_t));
1962 MultiByteToWideChar(CP_UTF8, 0, ss.c_str(), -1, bufw.get(), (int)len*4);
1963 std::wstring ssw = bufw.get();
1966 const std::regex regRevMatch("<TEXT>([^<]+)</TEXT>");
1967 const std::sregex_iterator end;
1968 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatch); it != end; ++it)
1970 std::string str = (*it)[1];
1971 size_t slen = str.size();
1972 auto bufw2 = std::make_unique<wchar_t[]>(slen * 4 + 1);
1973 SecureZeroMemory(bufw2.get(), (slen*4 + 1)*sizeof(wchar_t));
1974 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw2.get(), (int)slen*4);
1975 std::wstring ret = bufw2.get();
1977 RESOURCEENTRY entry = m_StringEntries[ret];
1978 ret = L"<TEXT>" + ret + L"</TEXT>";
1980 if (entry.msgstr.size())
1982 auto sbuf = std::make_unique<wchar_t[]>(entry.msgstr.size() + 10);
1983 wcscpy(sbuf.get(), entry.msgstr.c_str());
1984 CUtils::StringCollapse(sbuf.get());
1985 ReplaceWithRegex(sbuf.get());
1986 std::wstring sreplace = L"<TEXT>";
1987 sreplace += sbuf.get();
1988 sreplace += L"</TEXT>";
1989 CUtils::SearchReplace(ssw, ret, sreplace);
1990 m_bTranslatedRibbonTexts++;
1992 else
1993 m_bDefaultRibbonTexts++;
1996 const std::regex regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1997 for (std::sregex_iterator it(ss.cbegin(), ss.cend(), regRevMatchName); it != end; ++it)
1999 std::string str = (*it)[1];
2000 size_t slen = str.size();
2001 auto bufw2 = std::make_unique<wchar_t[]>(slen * 4 + 1);
2002 SecureZeroMemory(bufw2.get(), (slen*4 + 1)*sizeof(wchar_t));
2003 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bufw2.get(), (int)slen*4);
2004 std::wstring ret = bufw2.get();
2006 RESOURCEENTRY entry = m_StringEntries[ret];
2007 ret = L"</ELEMENT_NAME><NAME>" + ret + L"</NAME>";
2009 if (entry.msgstr.size())
2011 auto sbuf = std::make_unique<wchar_t[]>(entry.msgstr.size() + 10);
2012 wcscpy(sbuf.get(), entry.msgstr.c_str());
2013 CUtils::StringCollapse(sbuf.get());
2014 ReplaceWithRegex(sbuf.get());
2015 std::wstring sreplace = L"</ELEMENT_NAME><NAME>";
2016 sreplace += sbuf.get();
2017 sreplace += L"</NAME>";
2018 CUtils::SearchReplace(ssw, ret, sreplace);
2019 m_bTranslatedRibbonTexts++;
2021 else
2022 m_bDefaultRibbonTexts++;
2025 auto buf = std::make_unique<char[]>(ssw.size() * 4 + 1);
2026 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, ssw.c_str(), -1, buf.get(), (int)len * 4, nullptr, nullptr);
2029 if (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, (m_wTargetLang ? m_wTargetLang : wLanguage), buf.get(), lengthIncTerminator-1))
2031 goto DONE_ERROR;
2034 if (m_wTargetLang && (!UpdateResource(m_hUpdateRes, RT_RIBBON, lpszType, wLanguage, nullptr, 0)))
2036 goto DONE_ERROR;
2040 UnlockResource(hglRibbonTemplate);
2041 FreeResource(hglRibbonTemplate);
2042 return TRUE;
2044 DONE_ERROR:
2045 UnlockResource(hglRibbonTemplate);
2046 FreeResource(hglRibbonTemplate);
2047 MYERROR;
2050 std::wstring CResModule::ReplaceWithRegex(WCHAR* pBuf)
2052 for (const auto& t : m_StringEntries.m_regexes)
2056 std::wregex e(std::get<0>(t), std::regex_constants::icase);
2057 auto replaced = std::regex_replace(pBuf, e, std::get<1>(t));
2058 wcscpy(pBuf, replaced.c_str());
2060 catch (std::exception&)
2064 return pBuf;
2067 std::wstring CResModule::ReplaceWithRegex(std::wstring& s)
2069 for (const auto& t : m_StringEntries.m_regexes)
2073 std::wregex e(std::get<0>(t), std::regex_constants::icase);
2074 auto replaced = std::regex_replace(s, e, std::get<1>(t));
2075 s = replaced;
2077 catch (std::exception&)
2081 return s;
2084 BOOL CALLBACK CResModule::EnumResNameCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
2086 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2088 if (lpszType == RT_STRING)
2090 if (IS_INTRESOURCE(lpszName))
2092 if (!lpResModule->ExtractString(lpszName))
2093 return FALSE;
2096 else if (lpszType == RT_MENU)
2098 if (IS_INTRESOURCE(lpszName))
2100 if (!lpResModule->ExtractMenu(lpszName))
2101 return FALSE;
2104 else if (lpszType == RT_DIALOG)
2106 if (IS_INTRESOURCE(lpszName))
2108 if (!lpResModule->ExtractDialog(lpszName))
2109 return FALSE;
2112 else if (lpszType == RT_ACCELERATOR)
2114 if (IS_INTRESOURCE(lpszName))
2116 if (!lpResModule->ExtractAccelerator(lpszName))
2117 return FALSE;
2120 else if (lpszType == RT_RIBBON)
2122 if (IS_INTRESOURCE(lpszName))
2124 if (!lpResModule->ExtractRibbon(lpszName))
2125 return FALSE;
2129 return TRUE;
2132 #pragma warning(push)
2133 #pragma warning(disable: 4189)
2134 BOOL CALLBACK CResModule::EnumResNameWriteCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
2136 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2137 return EnumResourceLanguages(hModule, lpszType, lpszName, (ENUMRESLANGPROC)&lpResModule->EnumResWriteLangCallback, lParam);
2139 #pragma warning(pop)
2141 BOOL CALLBACK CResModule::EnumResWriteLangCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, WORD wLanguage, LONG_PTR lParam)
2143 BOOL bRes = FALSE;
2144 auto lpResModule = reinterpret_cast<CResModule*>(lParam);
2146 if (lpszType == RT_STRING)
2148 bRes = lpResModule->ReplaceString(lpszName, wLanguage);
2150 else if (lpszType == RT_MENU)
2152 bRes = lpResModule->ReplaceMenu(lpszName, wLanguage);
2154 else if (lpszType == RT_DIALOG)
2156 bRes = lpResModule->ReplaceDialog(lpszName, wLanguage);
2158 else if (lpszType == RT_ACCELERATOR)
2160 bRes = lpResModule->ReplaceAccelerator(lpszName, wLanguage);
2162 else if (lpszType == RT_RIBBON)
2164 bRes = lpResModule->ReplaceRibbon(lpszName, wLanguage);
2167 return bRes;
2171 void CResModule::ReplaceStr(LPCWSTR src, WORD * dest, size_t * count, int * translated, int * def)
2173 TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH];
2174 SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR));
2175 wcscpy(pBuf, src);
2176 CUtils::StringExtend(pBuf);
2178 std::wstring wstr = std::wstring(pBuf);
2179 ReplaceWithRegex(pBuf);
2180 RESOURCEENTRY entry = m_StringEntries[wstr];
2181 if (!entry.msgstr.empty())
2183 wcscpy(pBuf, entry.msgstr.c_str());
2184 ReplaceWithRegex(pBuf);
2185 CUtils::StringCollapse(pBuf);
2186 if (dest)
2187 wcscpy((wchar_t *)&dest[(*count)], pBuf);
2188 (*count) += wcslen(pBuf)+1;
2189 (*translated)++;
2191 else
2193 if (wcscmp(pBuf, wstr.c_str()))
2195 if (dest)
2196 wcscpy((wchar_t*)&dest[(*count)], pBuf);
2197 (*count) += wcslen(pBuf) + 1;
2198 (*translated)++;
2200 else
2202 if (dest)
2203 wcscpy((wchar_t*)&dest[(*count)], src);
2204 (*count) += wcslen(src) + 1;
2205 if (wcslen(src))
2206 (*def)++;
2209 delete [] pBuf;
2212 static bool StartsWith(const std::string& heystacl, const char* needle)
2214 return heystacl.compare(0, strlen(needle), needle) == 0;
2217 static bool StartsWith(const std::wstring& heystacl, const wchar_t* needle)
2219 return heystacl.compare(0, wcslen(needle), needle) == 0;
2222 size_t CResModule::ScanHeaderFile(const std::wstring & filepath)
2224 size_t count = 0;
2226 // open the file and read the contents
2227 DWORD reqLen = GetFullPathName(filepath.c_str(), 0, nullptr, nullptr);
2228 auto wcfullPath = std::make_unique<TCHAR[]>(reqLen + 1);
2229 GetFullPathName(filepath.c_str(), reqLen, wcfullPath.get(), nullptr);
2230 std::wstring fullpath = wcfullPath.get();
2233 // first treat the file as ASCII and try to get the defines
2235 std::ifstream fin(fullpath);
2236 std::string file_line;
2237 while (std::getline(fin, file_line))
2239 auto defpos = file_line.find("#define");
2240 if (defpos != std::string::npos)
2242 std::string text = file_line.substr(defpos + 7);
2243 trim(text);
2244 auto spacepos = text.find(' ');
2245 if (spacepos == std::string::npos)
2246 spacepos = text.find('\t');
2247 if (spacepos != std::string::npos)
2249 auto value = atol(text.substr(spacepos).c_str());
2250 if (value == 0 && text.substr(spacepos).find("0x") != std::string::npos)
2251 value = std::stoul(text.substr(spacepos), nullptr, 16);
2252 text = text.substr(0, spacepos);
2253 trim(text);
2254 if (StartsWith(text, "IDS_"))
2256 m_currentHeaderDataStrings[value] = CUnicodeUtils::StdGetUnicode(text);
2257 ++count;
2259 else if (StartsWith(text, "IDD_"))
2261 m_currentHeaderDataDialogs[value] = CUnicodeUtils::StdGetUnicode(text);
2262 ++count;
2264 else if (StartsWith(text, "ID_"))
2266 m_currentHeaderDataMenus[value] = CUnicodeUtils::StdGetUnicode(text);
2267 ++count;
2275 // now try the same with the file treated as utf16
2277 // open as a byte stream
2278 std::wifstream wfin(fullpath, std::ios::binary);
2279 // apply BOM-sensitive UTF-16 facet
2280 wfin.imbue(std::locale(wfin.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
2281 //std::wifstream wfin(fullpath);
2282 std::wstring wfile_line;
2283 while (std::getline(wfin, wfile_line))
2285 auto defpos = wfile_line.find(L"#define");
2286 if (defpos != std::wstring::npos)
2288 std::wstring text = wfile_line.substr(defpos + 7);
2289 trim(text);
2290 auto spacepos = text.find(' ');
2291 if (spacepos == std::wstring::npos)
2292 spacepos = text.find('\t');
2293 if (spacepos != std::wstring::npos)
2295 auto value = _wtol(text.substr(spacepos).c_str());
2296 if (value == 0 && text.substr(spacepos).find(L"0x") != std::wstring::npos)
2297 value = std::stoul(text.substr(spacepos), nullptr, 16);
2298 text = text.substr(0, spacepos);
2299 trim(text);
2300 if (StartsWith(text, L"IDS_"))
2302 m_currentHeaderDataStrings[value] = text;
2303 ++count;
2305 else if (StartsWith(text, L"IDD_"))
2307 m_currentHeaderDataDialogs[value] = text;
2308 ++count;
2310 else if (StartsWith(text, L"ID_"))
2312 m_currentHeaderDataMenus[value] = text;
2313 ++count;
2320 return count;
2323 void CResModule::InsertResourceIDs(LPCWSTR lpType, INT_PTR mainId, RESOURCEENTRY& entry, INT_PTR id, LPCWSTR infotext)
2325 if (lpType == RT_DIALOG)
2327 auto foundIt = m_currentHeaderDataDialogs.find(mainId);
2328 if (foundIt != m_currentHeaderDataDialogs.end())
2329 entry.resourceIDs.insert(L"Dialog " + foundIt->second + L": Control id " + NumToStr(id) + infotext);
2330 else
2331 entry.resourceIDs.insert(NumToStr(id) + infotext);
2333 else if (lpType == RT_STRING)
2335 auto foundIt = m_currentHeaderDataStrings.find(id);
2336 if (foundIt != m_currentHeaderDataStrings.end())
2337 entry.resourceIDs.insert(foundIt->second + infotext);
2338 else
2339 entry.resourceIDs.insert(NumToStr(id) + infotext);
2341 else if (lpType == RT_MENU)
2343 auto foundIt = m_currentHeaderDataMenus.find(id);
2344 if (foundIt != m_currentHeaderDataMenus.end())
2345 entry.resourceIDs.insert(foundIt->second + infotext);
2346 else
2347 entry.resourceIDs.insert(NumToStr(id) + infotext);
2349 else if (lpType == RT_RIBBON && infotext && wcsstr(infotext, L"ID") == infotext)
2350 entry.resourceIDs.insert(infotext);
2351 else
2352 entry.resourceIDs.insert(NumToStr(id) + infotext);