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.
21 #include "UnicodeUtils.h"
22 #include "ResModule.h"
32 #define RT_RIBBON MAKEINTRESOURCE(28)
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
);
45 std::wstring
NumToStr(INT_PTR num
)
48 swprintf_s(buf
, L
"%Id", num
);
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)
65 , m_hUpdateRes(nullptr)
68 , m_bAdjustEOLs(false)
72 CResModule::~CResModule(void)
76 BOOL
CResModule::ExtractResources(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
);
98 size_t nEntries
= m_StringEntries
.size();
99 // fill in the std::map with all translatable entries
102 _ftprintf(stdout
, L
"Extracting StringTable....");
103 EnumResourceNames(m_hResDll
, RT_STRING
, EnumResNameCallback
, (LONG_PTR
)this);
105 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size()-nEntries
);
106 nEntries
= m_StringEntries
.size();
109 _ftprintf(stdout
, L
"Extracting Dialogs........");
110 EnumResourceNames(m_hResDll
, RT_DIALOG
, EnumResNameCallback
, (LONG_PTR
)this);
112 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size()-nEntries
);
113 nEntries
= m_StringEntries
.size();
116 _ftprintf(stdout
, L
"Extracting Menus..........");
117 EnumResourceNames(m_hResDll
, RT_MENU
, EnumResNameCallback
, (LONG_PTR
)this);
119 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size()-nEntries
);
120 nEntries
= m_StringEntries
.size();
122 _ftprintf(stdout
, L
"Extracting Accelerators...");
123 EnumResourceNames(m_hResDll
, RT_ACCELERATOR
, EnumResNameCallback
, (LONG_PTR
)this);
125 _ftprintf(stdout
, L
"%4Iu Accelerators\n", m_StringEntries
.size()-nEntries
);
126 nEntries
= m_StringEntries
.size();
128 _ftprintf(stdout
, L
"Extracting Ribbons........");
129 EnumResourceNames(m_hResDll
, RT_RIBBON
, EnumResNameCallback
, (LONG_PTR
)this);
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
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
);
152 // fill in the std::map with all translatable entries
155 _ftprintf(stdout
, L
"Extracting StringTable....");
156 EnumResourceNames(m_hResDll
, RT_STRING
, EnumResNameCallback
, (LONG_PTR
)this);
158 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size());
159 nEntries
= m_StringEntries
.size();
162 _ftprintf(stdout
, L
"Extracting Dialogs........");
163 EnumResourceNames(m_hResDll
, RT_DIALOG
, EnumResNameCallback
, (LONG_PTR
)this);
165 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size()-nEntries
);
166 nEntries
= m_StringEntries
.size();
169 _ftprintf(stdout
, L
"Extracting Menus..........");
170 EnumResourceNames(m_hResDll
, RT_MENU
, EnumResNameCallback
, (LONG_PTR
)this);
172 _ftprintf(stdout
, L
"%4Iu Strings\n", m_StringEntries
.size()-nEntries
);
173 nEntries
= m_StringEntries
.size();
176 _ftprintf(stdout
, L
"Extracting Accelerators...");
177 EnumResourceNames(m_hResDll
, RT_ACCELERATOR
, EnumResNameCallback
, (LONG_PTR
)this);
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
184 m_StringEntries
.ParseFile(lpszPoFilePath
, !bNoUpdate
, m_bAdjustEOLs
);
186 // at last, save the new file
187 if (!m_StringEntries
.SaveFile(lpszPoFilePath
, lpszHeaderFile
))
190 FreeLibrary(m_hResDll
);
195 FreeLibrary(m_hResDll
);
199 BOOL
CResModule::CreateTranslatedResources(LPCTSTR lpszSrcLangDllPath
, LPCTSTR lpszDestLangDllPath
, LPCTSTR lpszPOFilePath
)
201 if (!CopyFile(lpszSrcLangDllPath
, lpszDestLangDllPath
, FALSE
))
207 m_hResDll
= LoadLibraryEx(lpszSrcLangDllPath
, nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
| LOAD_LIBRARY_AS_IMAGE_RESOURCE
| LOAD_IGNORE_CODE_AUTHZ_LEVEL
);
211 } while (!m_hResDll
&& (count
< 10));
216 sDestFile
= std::wstring(lpszDestLangDllPath
);
218 // get all translated strings
219 if (!m_StringEntries
.ParseFile(lpszPOFilePath
, FALSE
, m_bAdjustEOLs
))
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;
234 m_hUpdateRes
= BeginUpdateResource(sDestFile
.c_str(), FALSE
);
238 } while (!m_hUpdateRes
&& (count
< 10));
245 _ftprintf(stdout
, L
"Translating StringTable...");
246 bRes
= EnumResourceNames(m_hResDll
, RT_STRING
, EnumResNameWriteCallback
, (LONG_PTR
)this);
248 _ftprintf(stdout
, L
"%4d translated, %4d not translated\n", m_bTranslatedStrings
, m_bDefaultStrings
);
251 _ftprintf(stdout
, L
"Translating Dialogs.......");
252 bRes
= EnumResourceNames(m_hResDll
, RT_DIALOG
, EnumResNameWriteCallback
, (LONG_PTR
)this);
254 _ftprintf(stdout
, L
"%4d translated, %4d not translated\n", m_bTranslatedDialogStrings
, m_bDefaultDialogStrings
);
257 _ftprintf(stdout
, L
"Translating Menus.........");
258 bRes
= EnumResourceNames(m_hResDll
, RT_MENU
, EnumResNameWriteCallback
, (LONG_PTR
)this);
260 _ftprintf(stdout
, L
"%4d translated, %4d not translated\n", m_bTranslatedMenuStrings
, m_bDefaultMenuStrings
);
263 _ftprintf(stdout
, L
"Translating Accelerators..");
264 bRes
= EnumResourceNames(m_hResDll
, RT_ACCELERATOR
, EnumResNameWriteCallback
, (LONG_PTR
)this);
266 _ftprintf(stdout
, L
"%4d translated, %4d not translated\n", m_bTranslatedAcceleratorStrings
, m_bDefaultAcceleratorStrings
);
269 _ftprintf(stdout
, L
"Translating Ribbons.......");
270 bRes
= EnumResourceNames(m_hResDll
, RT_RIBBON
, EnumResNameWriteCallback
, (LONG_PTR
)this);
272 _ftprintf(stdout
, L
"%4d translated, %4d not translated\n", m_bTranslatedRibbonTexts
, m_bDefaultRibbonTexts
);
274 if (!EndUpdateResource(m_hUpdateRes
, !bRes
))
277 FreeLibrary(m_hResDll
);
281 FreeLibrary(m_hResDll
);
285 BOOL
CResModule::ExtractString(LPCTSTR lpszType
)
287 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_STRING
);
288 HGLOBAL hglStringTable
;
293 hglStringTable
= LoadResource(m_hResDll
, hrsrc
);
297 p
= (LPWSTR
)LockResource(hglStringTable
);
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
309 for (int i
=0; i
<16; ++i
)
311 int len
= GET_WORD(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
);
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
;
331 UnlockResource(hglStringTable
);
332 FreeResource(hglStringTable
);
335 UnlockResource(hglStringTable
);
336 FreeResource(hglStringTable
);
340 BOOL
CResModule::ReplaceString(LPCTSTR lpszType
, WORD wLanguage
)
342 HRSRC hrsrc
= FindResourceEx(m_hResDll
, RT_STRING
, lpszType
, wLanguage
);
343 HGLOBAL hglStringTable
;
348 hglStringTable
= LoadResource(m_hResDll
, hrsrc
);
352 p
= (LPWSTR
)LockResource(hglStringTable
);
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
365 for (int i
=0; i
<16; ++i
)
368 size_t len
= GET_WORD(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
.c_str());
380 CUtils::StringCollapse(pBuf
);
381 size_t newlen
= wcslen(pBuf
);
390 WORD
* newTable
= new WORD
[nMem
+ (nMem
% 2)];
391 SecureZeroMemory(newTable
, (nMem
+ (nMem
% 2))*2);
394 for (int i
=0; i
<16; ++i
)
396 int len
= GET_WORD(p
);
398 std::wstring msgid
= std::wstring(p
, len
);
399 WCHAR
* pBuf
= new WCHAR
[MAX_STRING_LENGTH
*2];
400 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
*2*sizeof(WCHAR
));
401 wcscpy(pBuf
, msgid
.c_str());
402 CUtils::StringExtend(pBuf
);
403 msgid
= std::wstring(pBuf
);
405 RESOURCEENTRY resEntry
;
406 resEntry
= m_StringEntries
[msgid
];
407 wcscpy(pBuf
, resEntry
.msgstr
.c_str());
408 CUtils::StringCollapse(pBuf
);
409 size_t newlen
= wcslen(pBuf
);
412 newTable
[index
++] = (WORD
)newlen
;
413 wcsncpy((wchar_t *)&newTable
[index
], pBuf
, newlen
);
415 m_bTranslatedStrings
++;
419 newTable
[index
++] = (WORD
)len
;
421 wcsncpy((wchar_t *)&newTable
[index
], p
, len
);
430 if (!UpdateResource(m_hUpdateRes
, RT_STRING
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newTable
, (DWORD
)(nMem
+ (nMem
% 2))*2))
436 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_STRING
, lpszType
, wLanguage
, nullptr, 0)))
442 UnlockResource(hglStringTable
);
443 FreeResource(hglStringTable
);
446 UnlockResource(hglStringTable
);
447 FreeResource(hglStringTable
);
451 BOOL
CResModule::ExtractMenu(LPCTSTR lpszType
)
453 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_MENU
);
454 HGLOBAL hglMenuTemplate
;
455 WORD version
, offset
;
461 hglMenuTemplate
= LoadResource(m_hResDll
, hrsrc
);
463 if (!hglMenuTemplate
)
466 p
= (const WORD
*)LockResource(hglMenuTemplate
);
471 // Standard MENU resource
472 //struct MenuHeader {
473 // WORD wVersion; // Currently zero
474 // WORD cbHeaderSize; // Also zero
478 //struct MenuExHeader {
479 // WORD wVersion; // One
484 version
= GET_WORD(p
);
492 offset
= GET_WORD(p
);
495 if (!ParseMenuResource(p
))
501 offset
= GET_WORD(p
);
503 //dwHelpId = GET_DWORD(p);
504 if (!ParseMenuExResource(p0
+ offset
))
512 UnlockResource(hglMenuTemplate
);
513 FreeResource(hglMenuTemplate
);
517 UnlockResource(hglMenuTemplate
);
518 FreeResource(hglMenuTemplate
);
522 BOOL
CResModule::ReplaceMenu(LPCTSTR lpszType
, WORD wLanguage
)
524 HRSRC hrsrc
= FindResourceEx(m_hResDll
, RT_MENU
, lpszType
, wLanguage
);
525 HGLOBAL hglMenuTemplate
;
526 WORD version
, offset
;
531 MYERROR
; //just the language wasn't found
533 hglMenuTemplate
= LoadResource(m_hResDll
, hrsrc
);
535 if (!hglMenuTemplate
)
538 p
= (LPWSTR
)LockResource(hglMenuTemplate
);
543 //struct MenuHeader {
544 // WORD wVersion; // Currently zero
545 // WORD cbHeaderSize; // Also zero
549 //struct MenuExHeader {
550 // WORD wVersion; // One
555 version
= GET_WORD(p
);
563 offset
= GET_WORD(p
);
567 if (!CountMemReplaceMenuResource((WORD
*)p
, &nMem
, nullptr))
569 WORD
* newMenu
= new WORD
[nMem
+ (nMem
% 2)+2];
570 SecureZeroMemory(newMenu
, (nMem
+ (nMem
% 2)+2)*2);
571 size_t index
= 2; // MenuHeader has 2 WORDs zero
572 if (!CountMemReplaceMenuResource((WORD
*)p
, &index
, newMenu
))
578 if (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newMenu
, (DWORD
)(nMem
+ (nMem
% 2)+2)*2))
584 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, wLanguage
, nullptr, 0)))
594 offset
= GET_WORD(p
);
596 //dwHelpId = GET_DWORD(p);
598 if (!CountMemReplaceMenuExResource((WORD
*)(p0
+ offset
), &nMem
, nullptr))
600 WORD
* newMenu
= new WORD
[nMem
+ (nMem
% 2) + 4];
601 SecureZeroMemory(newMenu
, (nMem
+ (nMem
% 2) + 4) * 2);
602 CopyMemory(newMenu
, p0
, 2 * sizeof(WORD
) + sizeof(DWORD
));
603 size_t index
= 4; // MenuExHeader has 2 x WORD + 1 x DWORD
604 if (!CountMemReplaceMenuExResource((WORD
*)(p0
+ offset
), &index
, newMenu
))
610 if (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newMenu
, (DWORD
)(nMem
+ (nMem
% 2) + 4) * 2))
616 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, wLanguage
, nullptr, 0)))
628 UnlockResource(hglMenuTemplate
);
629 FreeResource(hglMenuTemplate
);
633 UnlockResource(hglMenuTemplate
);
634 FreeResource(hglMenuTemplate
);
638 const WORD
* CResModule::ParseMenuResource(const WORD
* res
)
643 //struct PopupMenuItem {
645 // WCHAR szItemText[];
647 //struct NormalMenuItem {
650 // WCHAR szItemText[];
655 flags
= GET_WORD(res
);
657 if (!(flags
& MF_POPUP
))
659 id
= GET_WORD(res
); //normal menu item
663 id
= (WORD
)-1; //popup menu item
665 LPCWSTR str
= (LPCWSTR
)res
;
666 size_t l
= wcslen(str
)+1;
669 if (flags
& MF_POPUP
)
671 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
672 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
674 CUtils::StringExtend(pBuf
);
676 std::wstring wstr
= std::wstring(pBuf
);
677 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
679 InsertResourceIDs(RT_MENU
, 0, entry
, id
, L
" - PopupMenu");
681 m_StringEntries
[wstr
] = entry
;
684 if ((res
= ParseMenuResource(res
))==0)
689 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
690 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
692 CUtils::StringExtend(pBuf
);
694 std::wstring wstr
= std::wstring(pBuf
);
695 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
696 InsertResourceIDs(RT_MENU
, 0, entry
, id
, L
" - Menu");
698 TCHAR szTempBuf
[1024] = { 0 };
699 swprintf(szTempBuf
, L
"#: MenuEntry; ID:%u", id
);
700 MENUENTRY menu_entry
;
702 menu_entry
.reference
= szTempBuf
;
703 menu_entry
.msgstr
= wstr
;
705 m_StringEntries
[wstr
] = entry
;
706 m_MenuEntries
[id
] = menu_entry
;
709 } while (!(flags
& MF_END
));
713 const WORD
* CResModule::CountMemReplaceMenuResource(const WORD
* res
, size_t * wordcount
, WORD
* newMenu
)
718 //struct PopupMenuItem {
720 // WCHAR szItemText[];
722 //struct NormalMenuItem {
725 // WCHAR szItemText[];
730 flags
= GET_WORD(res
);
735 newMenu
[(*wordcount
)++] = flags
;
736 if (!(flags
& MF_POPUP
))
738 id
= GET_WORD(res
); //normal menu item
743 newMenu
[(*wordcount
)++] = id
;
746 id
= (WORD
)-1; //popup menu item
748 if (flags
& MF_POPUP
)
750 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
751 res
+= wcslen((LPCWSTR
)res
) + 1;
753 if ((res
= CountMemReplaceMenuResource(res
, wordcount
, newMenu
))==0)
758 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
759 res
+= wcslen((LPCWSTR
)res
) + 1;
764 wcscpy((wchar_t *)&newMenu
[(*wordcount
)], (LPCWSTR
)res
);
765 (*wordcount
) += wcslen((LPCWSTR
)res
) + 1;
766 res
+= wcslen((LPCWSTR
)res
) + 1;
768 } while (!(flags
& MF_END
));
772 const WORD
* CResModule::ParseMenuExResource(const WORD
* res
)
776 //struct MenuExItem {
782 // DWORD dwHelpId; - Popup menu only
787 DWORD dwType
= GET_DWORD(res
);
789 //dwState = GET_DWORD(res);
791 DWORD menuId
= GET_DWORD(res
);
793 bResInfo
= GET_WORD(res
);
796 LPCWSTR str
= (LPCWSTR
)res
;
797 size_t l
= wcslen(str
)+1;
799 // Align to DWORD boundary
800 res
= AlignWORD(res
);
802 if (dwType
& MFT_SEPARATOR
)
807 // Popup menu - note this can also have a non-zero ID
810 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
811 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
813 CUtils::StringExtend(pBuf
);
815 std::wstring wstr
= std::wstring(pBuf
);
816 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
817 // Popup has a DWORD help entry on a DWORD boundary - skip over it
820 InsertResourceIDs(RT_MENU
, 0, entry
, menuId
, L
" - PopupMenuEx");
821 TCHAR szTempBuf
[1024] = { 0 };
822 swprintf(szTempBuf
, L
"#: MenuExPopupEntry; ID:%lu", menuId
);
823 MENUENTRY menu_entry
;
824 menu_entry
.wID
= (WORD
)menuId
;
825 menu_entry
.reference
= szTempBuf
;
826 menu_entry
.msgstr
= wstr
;
827 m_StringEntries
[wstr
] = entry
;
828 m_MenuEntries
[(WORD
)menuId
] = menu_entry
;
831 if ((res
= ParseMenuExResource(res
)) == 0)
833 } else if (menuId
!= 0)
835 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
836 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
838 CUtils::StringExtend(pBuf
);
840 std::wstring wstr
= std::wstring(pBuf
);
841 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
842 InsertResourceIDs(RT_MENU
, 0, entry
, menuId
, L
" - MenuEx");
844 TCHAR szTempBuf
[1024] = { 0 };
845 swprintf(szTempBuf
, L
"#: MenuExEntry; ID:%lu", menuId
);
846 MENUENTRY menu_entry
;
847 menu_entry
.wID
= (WORD
)menuId
;
848 menu_entry
.reference
= szTempBuf
;
849 menu_entry
.msgstr
= wstr
;
850 m_StringEntries
[wstr
] = entry
;
851 m_MenuEntries
[(WORD
)menuId
] = menu_entry
;
854 } while (!(bResInfo
& 0x80));
858 const WORD
* CResModule::CountMemReplaceMenuExResource(const WORD
* res
, size_t * wordcount
, WORD
* newMenu
)
862 //struct MenuExItem {
868 // DWORD dwHelpId; - Popup menu only
873 WORD
* p0
= (WORD
*)res
;
874 DWORD dwType
= GET_DWORD(res
);
876 //dwState = GET_DWORD(res);
878 DWORD menuId
= GET_DWORD(res
);
880 bResInfo
= GET_WORD(res
);
884 CopyMemory(&newMenu
[*wordcount
], p0
, 7 * sizeof(WORD
));
888 if (dwType
& MFT_SEPARATOR
) {
897 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
898 res
+= wcslen((LPCWSTR
)res
) + 1;
900 res
= AlignWORD(res
);
901 if ((*wordcount
) & 0x01)
905 CopyMemory(&newMenu
[*wordcount
], res
, sizeof(DWORD
)); // Copy Help ID
910 if ((res
= CountMemReplaceMenuExResource(res
, wordcount
, newMenu
)) == 0)
913 else if (menuId
!= 0)
915 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
916 res
+= wcslen((LPCWSTR
)res
) + 1;
921 wcscpy((wchar_t *)&newMenu
[(*wordcount
)], (LPCWSTR
)res
);
922 (*wordcount
) += wcslen((LPCWSTR
)res
) + 1;
923 res
+= wcslen((LPCWSTR
)res
) + 1;
926 res
= AlignWORD(res
);
927 if ((*wordcount
) & 0x01)
929 } while (!(bResInfo
& 0x80));
933 BOOL
CResModule::ExtractAccelerator(LPCTSTR lpszType
)
935 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_ACCELERATOR
);
937 WORD fFlags
, wAnsi
, wID
;
944 hglAccTable
= LoadResource(m_hResDll
, hrsrc
);
949 p
= (const WORD
*)LockResource(hglAccTable
);
955 struct ACCELTABLEENTRY
957 WORD fFlags; FVIRTKEY, FSHIFT, FCONTROL, FALT, 0x80 - Last in a table
958 WORD wAnsi; ANSI character
959 WORD wId; Keyboard accelerator passed to windows
960 WORD padding; # bytes added to ensure aligned to DWORD boundary
966 fFlags
= GET_WORD(p
);
972 p
++; // Skip over padding
974 if ((fFlags
& 0x80) == 0x80)
979 if ((wAnsi
< 0x30) ||
981 (wAnsi
>= 0x3A && wAnsi
<= 0x40))
984 auto pBuf
= std::make_unique
<WCHAR
[]>(1024);
985 auto pBuf2
= std::make_unique
<WCHAR
[]>(1024);
986 SecureZeroMemory(pBuf
.get(), 1024 * sizeof(WCHAR
));
987 SecureZeroMemory(pBuf2
.get(), 1024 * sizeof(WCHAR
));
989 // include the menu ID in the msgid to make sure that 'duplicate'
990 // accelerator keys are listed in the po-file.
991 // without this, we would get entries like this:
992 //#. Accelerator Entry for Menu ID:32809; '&Filter'
993 //#. Accelerator Entry for Menu ID:57636; '&Find'
994 //#: Corresponding Menu ID:32771; '&Find'
998 // Since "filter" and "find" are most likely translated to words starting
999 // with different letters, we need to have a separate accelerator entry
1000 // for each of those
1001 swprintf(pBuf
.get(), L
"ID:%u:", wID
);
1003 // EXACTLY 5 characters long "ACS+X"
1004 // V = Virtual key (or blank if not used)
1005 // A = Alt key (or blank if not used)
1006 // C = Ctrl key (or blank if not used)
1007 // S = Shift key (or blank if not used)
1008 // X = upper case character
1009 // e.g. "V CS+Q" == Ctrl + Shift + 'Q'
1010 if ((fFlags
& FVIRTKEY
) == FVIRTKEY
) // 0x01
1011 wcscat(pBuf
.get(), L
"V");
1013 wcscat(pBuf
.get(), L
" ");
1015 if ((fFlags
& FALT
) == FALT
) // 0x10
1016 wcscat(pBuf
.get(), L
"A");
1018 wcscat(pBuf
.get(), L
" ");
1020 if ((fFlags
& FCONTROL
) == FCONTROL
) // 0x08
1021 wcscat(pBuf
.get(), L
"C");
1023 wcscat(pBuf
.get(), L
" ");
1025 if ((fFlags
& FSHIFT
) == FSHIFT
) // 0x04
1026 wcscat(pBuf
.get(), L
"S");
1028 wcscat(pBuf
.get(), L
" ");
1030 swprintf(pBuf2
.get(), L
"%s+%c", pBuf
.get(), wAnsi
);
1032 std::wstring wstr
= std::wstring(pBuf2
.get());
1033 RESOURCEENTRY AKey_entry
= m_StringEntries
[wstr
];
1035 TCHAR szTempBuf
[1024] = { 0 };
1036 SecureZeroMemory(szTempBuf
, sizeof (szTempBuf
));
1037 std::wstring wmenu
= L
"";
1038 pME_iter
= m_MenuEntries
.find(wID
);
1039 if (pME_iter
!= m_MenuEntries
.end())
1041 wmenu
= pME_iter
->second
.msgstr
;
1043 swprintf(szTempBuf
, L
"#. Accelerator Entry for Menu ID:%u; '%s'", wID
, wmenu
.c_str());
1044 AKey_entry
.automaticcomments
.push_back(std::wstring(szTempBuf
));
1046 m_StringEntries
[wstr
] = AKey_entry
;
1049 UnlockResource(hglAccTable
);
1050 FreeResource(hglAccTable
);
1054 UnlockResource(hglAccTable
);
1055 FreeResource(hglAccTable
);
1059 BOOL
CResModule::ReplaceAccelerator(LPCTSTR lpszType
, WORD wLanguage
)
1061 LPACCEL lpaccelNew
; // pointer to new accelerator table
1062 HACCEL haccelOld
; // handle to old accelerator table
1063 int cAccelerators
; // number of accelerators in table
1064 HGLOBAL hglAccTableNew
;
1068 haccelOld
= LoadAccelerators(m_hResDll
, lpszType
);
1073 cAccelerators
= CopyAcceleratorTable(haccelOld
, nullptr, 0);
1075 lpaccelNew
= (LPACCEL
) LocalAlloc(LPTR
, cAccelerators
* sizeof(ACCEL
));
1080 CopyAcceleratorTable(haccelOld
, lpaccelNew
, cAccelerators
);
1082 // Find the accelerator that the user modified
1083 // and change its flags and virtual-key code
1088 static const size_t BufferSize
= 1024;
1089 auto pBuf
= std::make_unique
<WCHAR
[]>(BufferSize
);
1090 auto pBuf2
= std::make_unique
<WCHAR
[]>(BufferSize
);
1091 for (i
= 0; i
< cAccelerators
; i
++)
1093 if ((lpaccelNew
[i
].key
< 0x30) ||
1094 (lpaccelNew
[i
].key
> 0x5A) ||
1095 (lpaccelNew
[i
].key
>= 0x3A && lpaccelNew
[i
].key
<= 0x40))
1098 SecureZeroMemory(pBuf
.get(), 1024 * sizeof(WCHAR
));
1099 SecureZeroMemory(pBuf2
.get(), 1024 * sizeof(WCHAR
));
1101 swprintf(pBuf
.get(), L
"ID:%d:", lpaccelNew
[i
].cmd
);
1103 // get original key combination
1104 if ((lpaccelNew
[i
].fVirt
& FVIRTKEY
) == FVIRTKEY
) // 0x01
1105 wcscat(pBuf
.get(), L
"V");
1107 wcscat(pBuf
.get(), L
" ");
1109 if ((lpaccelNew
[i
].fVirt
& FALT
) == FALT
) // 0x10
1110 wcscat(pBuf
.get(), L
"A");
1112 wcscat(pBuf
.get(), L
" ");
1114 if ((lpaccelNew
[i
].fVirt
& FCONTROL
) == FCONTROL
) // 0x08
1115 wcscat(pBuf
.get(), L
"C");
1117 wcscat(pBuf
.get(), L
" ");
1119 if ((lpaccelNew
[i
].fVirt
& FSHIFT
) == FSHIFT
) // 0x04
1120 wcscat(pBuf
.get(), L
"S");
1122 wcscat(pBuf
.get(), L
" ");
1124 swprintf(pBuf2
.get(), L
"%s+%c", pBuf
.get(), lpaccelNew
[i
].key
);
1127 std::map
<std::wstring
, RESOURCEENTRY
>::iterator pAK_iter
= m_StringEntries
.find(pBuf2
.get());
1128 if (pAK_iter
!= m_StringEntries
.end())
1130 m_bTranslatedAcceleratorStrings
++;
1133 std::wstring wtemp
= pAK_iter
->second
.msgstr
;
1134 wtemp
= wtemp
.substr(wtemp
.find_last_of(':')+1);
1135 if (wtemp
.size() != 6)
1137 if (wtemp
.compare(0, 1, L
"V") == 0)
1139 else if (wtemp
.compare(0, 1, L
" ") != 0)
1140 continue; // not a space - user must have made a mistake when translating
1141 if (wtemp
.compare(1, 1, L
"A") == 0)
1143 else if (wtemp
.compare(1, 1, L
" ") != 0)
1144 continue; // not a space - user must have made a mistake when translating
1145 if (wtemp
.compare(2, 1, L
"C") == 0)
1147 else if (wtemp
.compare(2, 1, L
" ") != 0)
1148 continue; // not a space - user must have made a mistake when translating
1149 if (wtemp
.compare(3, 1, L
"S") == 0)
1151 else if (wtemp
.compare(3, 1, L
" ") != 0)
1152 continue; // not a space - user must have made a mistake when translating
1153 if (wtemp
.compare(4, 1, L
"+") == 0)
1155 _stscanf(wtemp
.substr(5, 1).c_str(), L
"%c", &xkey
);
1156 lpaccelNew
[i
].fVirt
= xfVirt
;
1157 lpaccelNew
[i
].key
= (DWORD
)xkey
;
1161 m_bDefaultAcceleratorStrings
++;
1165 // Create the new accelerator table
1166 hglAccTableNew
= LocalAlloc(LPTR
, cAccelerators
* 4 * sizeof(WORD
));
1167 if (!hglAccTableNew
)
1169 p
= (WORD
*)hglAccTableNew
;
1170 lpaccelNew
[cAccelerators
-1].fVirt
|= 0x80;
1171 for (i
= 0; i
< cAccelerators
; i
++)
1173 memcpy((void *)p
, &lpaccelNew
[i
].fVirt
, 1);
1175 memcpy((void *)p
, &lpaccelNew
[i
].key
, sizeof(WORD
));
1177 memcpy((void *)p
, &lpaccelNew
[i
].cmd
, sizeof(WORD
));
1182 if (!UpdateResource(m_hUpdateRes
, RT_ACCELERATOR
, lpszType
,
1183 (m_wTargetLang
? m_wTargetLang
: wLanguage
), hglAccTableNew
/* haccelNew*/, cAccelerators
* 4 * sizeof(WORD
)))
1188 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_ACCELERATOR
, lpszType
, wLanguage
, nullptr, 0)))
1193 LocalFree(hglAccTableNew
);
1194 LocalFree(lpaccelNew
);
1198 LocalFree(hglAccTableNew
);
1199 LocalFree(lpaccelNew
);
1203 BOOL
CResModule::ExtractDialog(LPCTSTR lpszType
)
1206 const WORD
* lpDlgItem
;
1208 DLGITEMINFO dlgItem
;
1211 HGLOBAL hGlblDlgTemplate
;
1213 hrsrc
= FindResource(m_hResDll
, lpszType
, RT_DIALOG
);
1218 hGlblDlgTemplate
= LoadResource(m_hResDll
, hrsrc
);
1219 if (!hGlblDlgTemplate
)
1222 lpDlg
= (const WORD
*) LockResource(hGlblDlgTemplate
);
1227 lpDlgItem
= (const WORD
*) GetDialogInfo(lpDlg
, &dlg
);
1228 bNumControls
= dlg
.nbItems
;
1232 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
1233 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
1234 wcscpy(pBuf
, dlg
.caption
);
1235 CUtils::StringExtend(pBuf
);
1237 std::wstring wstr
= std::wstring(pBuf
);
1238 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
1239 InsertResourceIDs(RT_DIALOG
, (INT_PTR
)lpszType
, entry
, (INT_PTR
)lpszType
, L
"");
1241 m_StringEntries
[wstr
] = entry
;
1245 while (bNumControls
-- != 0)
1247 TCHAR szTitle
[500] = { 0 };
1248 SecureZeroMemory(szTitle
, sizeof(szTitle
));
1251 lpDlgItem
= GetControlInfo((WORD
*) lpDlgItem
, &dlgItem
, dlg
.dialogEx
, &bCode
);
1254 wcsncpy(szTitle
, dlgItem
.windowName
, _countof(szTitle
) - 1);
1256 if (wcslen(szTitle
) > 0)
1258 CUtils::StringExtend(szTitle
);
1260 std::wstring wstr
= std::wstring(szTitle
);
1261 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
1262 InsertResourceIDs(RT_DIALOG
, (INT_PTR
)lpszType
, entry
, dlgItem
.id
, L
"");
1264 m_StringEntries
[wstr
] = entry
;
1268 UnlockResource(hGlblDlgTemplate
);
1269 FreeResource(hGlblDlgTemplate
);
1273 BOOL
CResModule::ReplaceDialog(LPCTSTR lpszType
, WORD wLanguage
)
1277 HGLOBAL hGlblDlgTemplate
;
1279 hrsrc
= FindResourceEx(m_hResDll
, RT_DIALOG
, lpszType
, wLanguage
);
1284 hGlblDlgTemplate
= LoadResource(m_hResDll
, hrsrc
);
1286 if (!hGlblDlgTemplate
)
1289 lpDlg
= (WORD
*) LockResource(hGlblDlgTemplate
);
1295 const WORD
* p
= lpDlg
;
1296 if (!CountMemReplaceDialogResource(p
, &nMem
, nullptr))
1298 WORD
* newDialog
= new WORD
[nMem
+ (nMem
% 2)];
1299 SecureZeroMemory(newDialog
, (nMem
+ (nMem
% 2))*2);
1302 if (!CountMemReplaceDialogResource(lpDlg
, &index
, newDialog
))
1304 delete [] newDialog
;
1308 if (!UpdateResource(m_hUpdateRes
, RT_DIALOG
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newDialog
, (DWORD
)(nMem
+ (nMem
% 2))*2))
1310 delete [] newDialog
;
1314 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_DIALOG
, lpszType
, wLanguage
, nullptr, 0)))
1316 delete [] newDialog
;
1320 delete [] newDialog
;
1321 UnlockResource(hGlblDlgTemplate
);
1322 FreeResource(hGlblDlgTemplate
);
1326 UnlockResource(hGlblDlgTemplate
);
1327 FreeResource(hGlblDlgTemplate
);
1331 const WORD
* CResModule::GetDialogInfo(const WORD
* pTemplate
, LPDIALOGINFO lpDlgInfo
) const
1333 const WORD
* p
= (const WORD
*)pTemplate
;
1335 lpDlgInfo
->style
= GET_DWORD(p
);
1338 if (lpDlgInfo
->style
== 0xffff0001) // DIALOGEX resource
1340 lpDlgInfo
->dialogEx
= TRUE
;
1341 lpDlgInfo
->helpId
= GET_DWORD(p
);
1343 lpDlgInfo
->exStyle
= GET_DWORD(p
);
1345 lpDlgInfo
->style
= GET_DWORD(p
);
1350 lpDlgInfo
->dialogEx
= FALSE
;
1351 lpDlgInfo
->helpId
= 0;
1352 lpDlgInfo
->exStyle
= GET_DWORD(p
);
1356 lpDlgInfo
->nbItems
= GET_WORD(p
);
1359 lpDlgInfo
->x
= GET_WORD(p
);
1362 lpDlgInfo
->y
= GET_WORD(p
);
1365 lpDlgInfo
->cx
= GET_WORD(p
);
1368 lpDlgInfo
->cy
= GET_WORD(p
);
1371 // Get the menu name
1373 switch (GET_WORD(p
))
1376 lpDlgInfo
->menuName
= nullptr;
1380 lpDlgInfo
->menuName
= (LPCTSTR
) (WORD
) GET_WORD(p
+ 1);
1384 lpDlgInfo
->menuName
= (LPCTSTR
) p
;
1385 p
+= wcslen((LPCWSTR
) p
) + 1;
1389 // Get the class name
1391 switch (GET_WORD(p
))
1394 lpDlgInfo
->className
= (LPCTSTR
)MAKEINTATOM(32770);
1398 lpDlgInfo
->className
= (LPCTSTR
) (WORD
) GET_WORD(p
+ 1);
1402 lpDlgInfo
->className
= (LPCTSTR
) p
;
1403 p
+= wcslen((LPCTSTR
)p
) + 1;
1407 // Get the window caption
1409 lpDlgInfo
->caption
= (LPCTSTR
)p
;
1410 p
+= wcslen((LPCWSTR
) p
) + 1;
1412 // Get the font name
1414 if (lpDlgInfo
->style
& DS_SETFONT
)
1416 lpDlgInfo
->pointSize
= GET_WORD(p
);
1419 if (lpDlgInfo
->dialogEx
)
1421 lpDlgInfo
->weight
= GET_WORD(p
);
1423 lpDlgInfo
->italic
= LOBYTE(GET_WORD(p
));
1428 lpDlgInfo
->weight
= FW_DONTCARE
;
1429 lpDlgInfo
->italic
= FALSE
;
1432 lpDlgInfo
->faceName
= (LPCTSTR
)p
;
1433 p
+= wcslen((LPCWSTR
) p
) + 1;
1435 // First control is on DWORD boundary
1441 const WORD
* CResModule::GetControlInfo(const WORD
* p
, LPDLGITEMINFO lpDlgItemInfo
, BOOL dialogEx
, LPBOOL bIsID
) const
1445 lpDlgItemInfo
->helpId
= GET_DWORD(p
);
1447 lpDlgItemInfo
->exStyle
= GET_DWORD(p
);
1449 lpDlgItemInfo
->style
= GET_DWORD(p
);
1454 lpDlgItemInfo
->helpId
= 0;
1455 lpDlgItemInfo
->style
= GET_DWORD(p
);
1457 lpDlgItemInfo
->exStyle
= GET_DWORD(p
);
1461 lpDlgItemInfo
->x
= GET_WORD(p
);
1464 lpDlgItemInfo
->y
= GET_WORD(p
);
1467 lpDlgItemInfo
->cx
= GET_WORD(p
);
1470 lpDlgItemInfo
->cy
= GET_WORD(p
);
1475 // ID is a DWORD for DIALOGEX
1476 lpDlgItemInfo
->id
= (WORD
) GET_DWORD(p
);
1481 lpDlgItemInfo
->id
= GET_WORD(p
);
1485 if (GET_WORD(p
) == 0xffff)
1493 lpDlgItemInfo
->className
= (LPCTSTR
) p
;
1494 p
+= wcslen((LPCWSTR
) p
) + 1;
1497 if (GET_WORD(p
) == 0xffff) // an integer ID?
1500 lpDlgItemInfo
->windowName
= (LPCTSTR
) (UINT_PTR
) GET_WORD(p
+ 1);
1506 lpDlgItemInfo
->windowName
= (LPCTSTR
) p
;
1507 p
+= wcslen((LPCWSTR
) p
) + 1;
1512 lpDlgItemInfo
->data
= (LPVOID
) (p
+ 1);
1513 p
+= GET_WORD(p
) / sizeof(WORD
);
1516 lpDlgItemInfo
->data
= nullptr;
1519 // Next control is on DWORD boundary
1524 const WORD
* CResModule::CountMemReplaceDialogResource(const WORD
* res
, size_t * wordcount
, WORD
* newDialog
)
1527 DWORD style
= GET_DWORD(res
);
1530 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1531 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1539 if (style
== 0xffff0001) // DIALOGEX resource
1544 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //help id
1545 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //help id
1546 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1547 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1548 style
= GET_DWORD(res
);
1549 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1550 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1555 style
= GET_DWORD(res
);
1565 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1566 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1567 //style = GET_DWORD(res);
1568 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1569 //newDialog[(*wordcount)++] = GET_WORD(res++); //style
1579 newDialog
[(*wordcount
)] = GET_WORD(res
);
1580 WORD nbItems
= GET_WORD(res
);
1585 newDialog
[(*wordcount
)] = GET_WORD(res
); //x
1590 newDialog
[(*wordcount
)] = GET_WORD(res
); //y
1595 newDialog
[(*wordcount
)] = GET_WORD(res
); //cx
1600 newDialog
[(*wordcount
)] = GET_WORD(res
); //cy
1604 // Get the menu name
1606 switch (GET_WORD(res
))
1610 newDialog
[(*wordcount
)] = GET_WORD(res
);
1617 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1618 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1629 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1631 (*wordcount
) += wcslen((LPCWSTR
) res
) + 1;
1632 res
+= wcslen((LPCWSTR
) res
) + 1;
1636 // Get the class name
1638 switch (GET_WORD(res
))
1642 newDialog
[(*wordcount
)] = GET_WORD(res
);
1649 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1650 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1661 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1663 (*wordcount
) += wcslen((LPCWSTR
) res
) + 1;
1664 res
+= wcslen((LPCWSTR
) res
) + 1;
1668 // Get the window caption
1670 ReplaceStr((LPCWSTR
)res
, newDialog
, wordcount
, &m_bTranslatedDialogStrings
, &m_bDefaultDialogStrings
);
1671 res
+= wcslen((LPCWSTR
)res
) + 1;
1673 // Get the font name
1675 if (style
& DS_SETFONT
)
1678 newDialog
[(*wordcount
)] = GET_WORD(res
);
1686 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1687 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1697 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1698 (*wordcount
) += wcslen((LPCWSTR
)res
) + 1;
1699 res
+= wcslen((LPCWSTR
)res
) + 1;
1701 // First control is on DWORD boundary
1702 while ((*wordcount
)%2)
1704 while ((UINT_PTR
)res
% 4)
1709 res
= ReplaceControlInfo(res
, wordcount
, newDialog
, bEx
);
1714 const WORD
* CResModule::ReplaceControlInfo(const WORD
* res
, size_t * wordcount
, WORD
* newDialog
, BOOL bEx
)
1720 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //helpid
1721 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //helpid
1731 LONG
* exStyle
= (LONG
*)&newDialog
[(*wordcount
)];
1732 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1733 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1735 *exStyle
|= WS_EX_RTLREADING
;
1745 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1746 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1755 newDialog
[(*wordcount
)] = GET_WORD(res
); //x
1760 newDialog
[(*wordcount
)] = GET_WORD(res
); //y
1765 newDialog
[(*wordcount
)] = GET_WORD(res
); //cx
1770 newDialog
[(*wordcount
)] = GET_WORD(res
); //cy
1776 // ID is a DWORD for DIALOGEX
1779 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1780 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1791 newDialog
[(*wordcount
)] = GET_WORD(res
);
1796 if (GET_WORD(res
) == 0xffff) //classID
1800 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1801 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1812 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1813 (*wordcount
) += wcslen((LPCWSTR
) res
) + 1;
1814 res
+= wcslen((LPCWSTR
) res
) + 1;
1817 if (GET_WORD(res
) == 0xffff) // an integer ID?
1821 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1822 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1832 ReplaceStr((LPCWSTR
)res
, newDialog
, wordcount
, &m_bTranslatedDialogStrings
, &m_bDefaultDialogStrings
);
1833 res
+= wcslen((LPCWSTR
)res
) + 1;
1837 memcpy(&newDialog
[(*wordcount
)], res
, (GET_WORD(res
)+1)*sizeof(WORD
));
1838 (*wordcount
) += (GET_WORD(res
)+1);
1839 res
+= (GET_WORD(res
)+1);
1840 // Next control is on DWORD boundary
1841 while ((*wordcount
) % 2)
1843 res
= AlignWORD(res
);
1848 BOOL
CResModule::ExtractRibbon(LPCTSTR lpszType
)
1850 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_RIBBON
);
1851 HGLOBAL hglRibbonTemplate
;
1857 hglRibbonTemplate
= LoadResource(m_hResDll
, hrsrc
);
1859 DWORD sizeres
= SizeofResource(m_hResDll
, hrsrc
);
1861 if (!hglRibbonTemplate
)
1864 p
= (const BYTE
*)LockResource(hglRibbonTemplate
);
1869 // Resource consists of one single string
1872 // extract all <id><name>blah1</name><value>blah2</value></id><text>blah</text> elements
1874 const std::regex
regRevMatch("<ID><NAME>([^<]+)</NAME><VALUE>([^<]+)</VALUE></ID><TEXT>([^<]+)</TEXT>");
1875 std::string ss
= std::string((const char*)p
, sizeres
);
1876 const std::sregex_iterator end
;
1877 for (std::sregex_iterator
it(ss
.cbegin(), ss
.cend(), regRevMatch
); it
!= end
; ++it
)
1881 std::string str1
= (*it
)[1];
1883 auto bufw1
= std::make_unique
<wchar_t[]>(len
* 4 + 1);
1884 SecureZeroMemory(bufw1
.get(), (len
* 4 + 1) * sizeof(wchar_t));
1885 MultiByteToWideChar(CP_UTF8
, 0, str1
.c_str(), -1, bufw1
.get(), (int)len
* 4);
1886 std::wstring strIdNameVal
= bufw1
.get();
1887 strIdNameVal
+= L
" - Ribbon name";
1889 std::string str2
= (*it
)[2];
1891 auto bufw2
= std::make_unique
<wchar_t[]>(len
* 4 + 1);
1892 SecureZeroMemory(bufw2
.get(), (len
* 4 + 1)*sizeof(wchar_t));
1893 MultiByteToWideChar(CP_UTF8
, 0, str2
.c_str(), -1, bufw2
.get(), (int)len
* 4);
1894 std::wstring strIdVal
= bufw2
.get();
1896 std::string str3
= (*it
)[3];
1898 auto bufw3
= std::make_unique
<wchar_t[]>(len
* 4 + 1);
1899 SecureZeroMemory(bufw3
.get(), (len
* 4 + 1)*sizeof(wchar_t));
1900 MultiByteToWideChar(CP_UTF8
, 0, str3
.c_str(), -1, bufw3
.get(), (int)len
* 4);
1901 std::wstring str
= bufw3
.get();
1903 RESOURCEENTRY entry
= m_StringEntries
[str
];
1904 InsertResourceIDs(RT_RIBBON
, 0, entry
, std::stoi(strIdVal
), strIdNameVal
.c_str());
1905 if (wcschr(str
.c_str(), '%'))
1906 entry
.flag
= L
"#, c-format";
1907 m_StringEntries
[str
] = entry
;
1908 m_bDefaultRibbonTexts
++;
1911 // extract all </ELEMENT_NAME><NAME>blahblah</NAME> elements
1913 const std::regex
regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1914 for (std::sregex_iterator
it(ss
.cbegin(), ss
.cend(), regRevMatchName
); it
!= end
; ++it
)
1916 std::string str
= (*it
)[1];
1917 size_t len
= str
.size();
1918 auto bufw
= std::make_unique
<wchar_t[]>(len
* 4 + 1);
1919 SecureZeroMemory(bufw
.get(), (len
*4 + 1)*sizeof(wchar_t));
1920 MultiByteToWideChar(CP_UTF8
, 0, str
.c_str(), -1, bufw
.get(), (int)len
*4);
1921 std::wstring ret
= bufw
.get();
1922 RESOURCEENTRY entry
= m_StringEntries
[ret
];
1923 InsertResourceIDs(RT_RIBBON
, 0, entry
, (INT_PTR
)lpszType
, L
" - Ribbon element");
1924 if (wcschr(ret
.c_str(), '%'))
1925 entry
.flag
= L
"#, c-format";
1926 m_StringEntries
[ret
] = entry
;
1927 m_bDefaultRibbonTexts
++;
1930 UnlockResource(hglRibbonTemplate
);
1931 FreeResource(hglRibbonTemplate
);
1935 BOOL
CResModule::ReplaceRibbon(LPCTSTR lpszType
, WORD wLanguage
)
1937 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_RIBBON
);
1938 HGLOBAL hglRibbonTemplate
;
1944 hglRibbonTemplate
= LoadResource(m_hResDll
, hrsrc
);
1946 DWORD sizeres
= SizeofResource(m_hResDll
, hrsrc
);
1948 if (!hglRibbonTemplate
)
1951 p
= (const BYTE
*)LockResource(hglRibbonTemplate
);
1956 std::string ss
= std::string((const char*)p
, sizeres
);
1957 size_t len
= ss
.size();
1958 auto bufw
= std::make_unique
<wchar_t[]>(len
* 4 + 1);
1959 SecureZeroMemory(bufw
.get(), (len
*4 + 1)*sizeof(wchar_t));
1960 MultiByteToWideChar(CP_UTF8
, 0, ss
.c_str(), -1, bufw
.get(), (int)len
*4);
1961 std::wstring ssw
= bufw
.get();
1964 const std::regex
regRevMatch("<TEXT>([^<]+)</TEXT>");
1965 const std::sregex_iterator end
;
1966 for (std::sregex_iterator
it(ss
.cbegin(), ss
.cend(), regRevMatch
); it
!= end
; ++it
)
1968 std::string str
= (*it
)[1];
1969 size_t slen
= str
.size();
1970 auto bufw2
= std::make_unique
<wchar_t[]>(slen
* 4 + 1);
1971 SecureZeroMemory(bufw2
.get(), (slen
*4 + 1)*sizeof(wchar_t));
1972 MultiByteToWideChar(CP_UTF8
, 0, str
.c_str(), -1, bufw2
.get(), (int)slen
*4);
1973 std::wstring ret
= bufw2
.get();
1975 RESOURCEENTRY entry
= m_StringEntries
[ret
];
1976 ret
= L
"<TEXT>" + ret
+ L
"</TEXT>";
1978 if (entry
.msgstr
.size())
1980 auto sbuf
= std::make_unique
<wchar_t[]>(entry
.msgstr
.size() + 10);
1981 wcscpy(sbuf
.get(), entry
.msgstr
.c_str());
1982 CUtils::StringCollapse(sbuf
.get());
1983 std::wstring sreplace
= L
"<TEXT>";
1984 sreplace
+= sbuf
.get();
1985 sreplace
+= L
"</TEXT>";
1986 CUtils::SearchReplace(ssw
, ret
, sreplace
);
1987 m_bTranslatedRibbonTexts
++;
1990 m_bDefaultRibbonTexts
++;
1993 const std::regex
regRevMatchName("</ELEMENT_NAME><NAME>([^<]+)</NAME>");
1994 for (std::sregex_iterator
it(ss
.cbegin(), ss
.cend(), regRevMatchName
); it
!= end
; ++it
)
1996 std::string str
= (*it
)[1];
1997 size_t slen
= str
.size();
1998 auto bufw2
= std::make_unique
<wchar_t[]>(slen
* 4 + 1);
1999 SecureZeroMemory(bufw2
.get(), (slen
*4 + 1)*sizeof(wchar_t));
2000 MultiByteToWideChar(CP_UTF8
, 0, str
.c_str(), -1, bufw2
.get(), (int)slen
*4);
2001 std::wstring ret
= bufw2
.get();
2003 RESOURCEENTRY entry
= m_StringEntries
[ret
];
2004 ret
= L
"</ELEMENT_NAME><NAME>" + ret
+ L
"</NAME>";
2006 if (entry
.msgstr
.size())
2008 auto sbuf
= std::make_unique
<wchar_t[]>(entry
.msgstr
.size() + 10);
2009 wcscpy(sbuf
.get(), entry
.msgstr
.c_str());
2010 CUtils::StringCollapse(sbuf
.get());
2011 std::wstring sreplace
= L
"</ELEMENT_NAME><NAME>";
2012 sreplace
+= sbuf
.get();
2013 sreplace
+= L
"</NAME>";
2014 CUtils::SearchReplace(ssw
, ret
, sreplace
);
2015 m_bTranslatedRibbonTexts
++;
2018 m_bDefaultRibbonTexts
++;
2021 auto buf
= std::make_unique
<char[]>(ssw
.size() * 4 + 1);
2022 int lengthIncTerminator
= WideCharToMultiByte(CP_UTF8
, 0, ssw
.c_str(), -1, buf
.get(), (int)len
* 4, nullptr, nullptr);
2025 if (!UpdateResource(m_hUpdateRes
, RT_RIBBON
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), buf
.get(), lengthIncTerminator
-1))
2030 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_RIBBON
, lpszType
, wLanguage
, nullptr, 0)))
2036 UnlockResource(hglRibbonTemplate
);
2037 FreeResource(hglRibbonTemplate
);
2041 UnlockResource(hglRibbonTemplate
);
2042 FreeResource(hglRibbonTemplate
);
2046 BOOL CALLBACK
CResModule::EnumResNameCallback(HMODULE
/*hModule*/, LPCTSTR lpszType
, LPTSTR lpszName
, LONG_PTR lParam
)
2048 auto lpResModule
= reinterpret_cast<CResModule
*>(lParam
);
2050 if (lpszType
== RT_STRING
)
2052 if (IS_INTRESOURCE(lpszName
))
2054 if (!lpResModule
->ExtractString(lpszName
))
2058 else if (lpszType
== RT_MENU
)
2060 if (IS_INTRESOURCE(lpszName
))
2062 if (!lpResModule
->ExtractMenu(lpszName
))
2066 else if (lpszType
== RT_DIALOG
)
2068 if (IS_INTRESOURCE(lpszName
))
2070 if (!lpResModule
->ExtractDialog(lpszName
))
2074 else if (lpszType
== RT_ACCELERATOR
)
2076 if (IS_INTRESOURCE(lpszName
))
2078 if (!lpResModule
->ExtractAccelerator(lpszName
))
2082 else if (lpszType
== RT_RIBBON
)
2084 if (IS_INTRESOURCE(lpszName
))
2086 if (!lpResModule
->ExtractRibbon(lpszName
))
2094 #pragma warning(push)
2095 #pragma warning(disable: 4189)
2096 BOOL CALLBACK
CResModule::EnumResNameWriteCallback(HMODULE hModule
, LPCTSTR lpszType
, LPTSTR lpszName
, LONG_PTR lParam
)
2098 auto lpResModule
= reinterpret_cast<CResModule
*>(lParam
);
2099 return EnumResourceLanguages(hModule
, lpszType
, lpszName
, (ENUMRESLANGPROC
)&lpResModule
->EnumResWriteLangCallback
, lParam
);
2101 #pragma warning(pop)
2103 BOOL CALLBACK
CResModule::EnumResWriteLangCallback(HMODULE
/*hModule*/, LPCTSTR lpszType
, LPTSTR lpszName
, WORD wLanguage
, LONG_PTR lParam
)
2106 auto lpResModule
= reinterpret_cast<CResModule
*>(lParam
);
2108 if (lpszType
== RT_STRING
)
2110 bRes
= lpResModule
->ReplaceString(lpszName
, wLanguage
);
2112 else if (lpszType
== RT_MENU
)
2114 bRes
= lpResModule
->ReplaceMenu(lpszName
, wLanguage
);
2116 else if (lpszType
== RT_DIALOG
)
2118 bRes
= lpResModule
->ReplaceDialog(lpszName
, wLanguage
);
2120 else if (lpszType
== RT_ACCELERATOR
)
2122 bRes
= lpResModule
->ReplaceAccelerator(lpszName
, wLanguage
);
2124 else if (lpszType
== RT_RIBBON
)
2126 bRes
= lpResModule
->ReplaceRibbon(lpszName
, wLanguage
);
2133 void CResModule::ReplaceStr(LPCWSTR src
, WORD
* dest
, size_t * count
, int * translated
, int * def
)
2135 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
2136 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
2138 CUtils::StringExtend(pBuf
);
2140 std::wstring wstr
= std::wstring(pBuf
);
2141 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
2142 if (!entry
.msgstr
.empty())
2144 wcscpy(pBuf
, entry
.msgstr
.c_str());
2145 CUtils::StringCollapse(pBuf
);
2147 wcscpy((wchar_t *)&dest
[(*count
)], pBuf
);
2148 (*count
) += wcslen(pBuf
)+1;
2154 wcscpy((wchar_t *)&dest
[(*count
)], src
);
2155 (*count
) += wcslen(src
) + 1;
2162 size_t CResModule::ScanHeaderFile(const std::wstring
& filepath
)
2166 // open the file and read the contents
2167 DWORD reqLen
= GetFullPathName(filepath
.c_str(), 0, nullptr, nullptr);
2168 auto wcfullPath
= std::make_unique
<TCHAR
[]>(reqLen
+ 1);
2169 GetFullPathName(filepath
.c_str(), reqLen
, wcfullPath
.get(), nullptr);
2170 std::wstring fullpath
= wcfullPath
.get();
2173 // first treat the file as ASCII and try to get the defines
2175 std::ifstream
fin(fullpath
);
2176 std::string file_line
;
2177 while (std::getline(fin
, file_line
))
2179 auto defpos
= file_line
.find("#define");
2180 if (defpos
!= std::string::npos
)
2182 std::string text
= file_line
.substr(defpos
+ 7);
2184 auto spacepos
= text
.find(' ');
2185 if (spacepos
== std::string::npos
)
2186 spacepos
= text
.find('\t');
2187 if (spacepos
!= std::string::npos
)
2189 auto value
= atol(text
.substr(spacepos
).c_str());
2190 if (value
== 0 && text
.substr(spacepos
).find("0x") != std::string::npos
)
2191 value
= std::stoul(text
.substr(spacepos
), nullptr, 16);
2192 text
= text
.substr(0, spacepos
);
2194 if (text
.compare(0, 4, "IDS_") == 0)
2196 m_currentHeaderDataStrings
[value
] = CUnicodeUtils::StdGetUnicode(text
);
2199 else if (text
.compare(0, 4, "IDD_") == 0)
2201 m_currentHeaderDataDialogs
[value
] = CUnicodeUtils::StdGetUnicode(text
);
2204 else if (text
.compare(0, 3, "ID_") == 0)
2206 m_currentHeaderDataMenus
[value
] = CUnicodeUtils::StdGetUnicode(text
);
2215 // now try the same with the file treated as utf16
2217 // open as a byte stream
2218 std::wifstream
wfin(fullpath
, std::ios::binary
);
2219 // apply BOM-sensitive UTF-16 facet
2220 wfin
.imbue(std::locale(wfin
.getloc(), new std::codecvt_utf16
<wchar_t, 0x10ffff, std::consume_header
>));
2221 //std::wifstream wfin(fullpath);
2222 std::wstring wfile_line
;
2223 while (std::getline(wfin
, wfile_line
))
2225 auto defpos
= wfile_line
.find(L
"#define");
2226 if (defpos
!= std::wstring::npos
)
2228 std::wstring text
= wfile_line
.substr(defpos
+ 7);
2230 auto spacepos
= text
.find(' ');
2231 if (spacepos
== std::wstring::npos
)
2232 spacepos
= text
.find('\t');
2233 if (spacepos
!= std::wstring::npos
)
2235 auto value
= _wtol(text
.substr(spacepos
).c_str());
2236 if (value
== 0 && text
.substr(spacepos
).find(L
"0x") != std::wstring::npos
)
2237 value
= std::stoul(text
.substr(spacepos
), nullptr, 16);
2238 text
= text
.substr(0, spacepos
);
2240 if (text
.compare(0, 4, L
"IDS_") == 0)
2242 m_currentHeaderDataStrings
[value
] = text
;
2245 else if (text
.compare(0, 4, L
"IDD_") == 0)
2247 m_currentHeaderDataDialogs
[value
] = text
;
2250 else if (text
.compare(0, 3, L
"ID_") == 0)
2252 m_currentHeaderDataMenus
[value
] = text
;
2263 void CResModule::InsertResourceIDs(LPCWSTR lpType
, INT_PTR mainId
, RESOURCEENTRY
& entry
, INT_PTR id
, LPCWSTR infotext
)
2265 if (lpType
== RT_DIALOG
)
2267 auto foundIt
= m_currentHeaderDataDialogs
.find(mainId
);
2268 if (foundIt
!= m_currentHeaderDataDialogs
.end())
2269 entry
.resourceIDs
.insert(L
"Dialog " + foundIt
->second
+ L
": Control id " + NumToStr(id
) + infotext
);
2271 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);
2273 else if (lpType
== RT_STRING
)
2275 auto foundIt
= m_currentHeaderDataStrings
.find(id
);
2276 if (foundIt
!= m_currentHeaderDataStrings
.end())
2277 entry
.resourceIDs
.insert(foundIt
->second
+ infotext
);
2279 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);
2281 else if (lpType
== RT_MENU
)
2283 auto foundIt
= m_currentHeaderDataMenus
.find(id
);
2284 if (foundIt
!= m_currentHeaderDataMenus
.end())
2285 entry
.resourceIDs
.insert(foundIt
->second
+ infotext
);
2287 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);
2289 else if (lpType
== RT_RIBBON
&& infotext
&& wcsstr(infotext
, L
"ID") == infotext
)
2290 entry
.resourceIDs
.insert(infotext
);
2292 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);