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(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
);
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
.empty() ? msgid
.c_str() : resEntry
.msgstr
.c_str());
380 ReplaceWithRegex(pBuf
);
381 CUtils::StringCollapse(pBuf
);
382 size_t newlen
= wcslen(pBuf
);
391 WORD
* newTable
= new WORD
[nMem
+ (nMem
% 2)];
392 SecureZeroMemory(newTable
, (nMem
+ (nMem
% 2))*2);
395 for (int i
=0; i
<16; ++i
)
397 int len
= GET_WORD(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
);
414 newTable
[index
++] = (WORD
)newlen
;
415 wcsncpy((wchar_t *)&newTable
[index
], pBuf
, newlen
);
417 m_bTranslatedStrings
++;
421 newTable
[index
++] = (WORD
)len
;
423 wcsncpy((wchar_t *)&newTable
[index
], p
, len
);
432 if (!UpdateResource(m_hUpdateRes
, RT_STRING
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newTable
, (DWORD
)(nMem
+ (nMem
% 2))*2))
438 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_STRING
, lpszType
, wLanguage
, nullptr, 0)))
444 UnlockResource(hglStringTable
);
445 FreeResource(hglStringTable
);
448 UnlockResource(hglStringTable
);
449 FreeResource(hglStringTable
);
453 BOOL
CResModule::ExtractMenu(LPCTSTR lpszType
)
455 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_MENU
);
456 HGLOBAL hglMenuTemplate
;
457 WORD version
, offset
;
463 hglMenuTemplate
= LoadResource(m_hResDll
, hrsrc
);
465 if (!hglMenuTemplate
)
468 p
= (const WORD
*)LockResource(hglMenuTemplate
);
473 // Standard MENU resource
474 //struct MenuHeader {
475 // WORD wVersion; // Currently zero
476 // WORD cbHeaderSize; // Also zero
480 //struct MenuExHeader {
481 // WORD wVersion; // One
486 version
= GET_WORD(p
);
494 offset
= GET_WORD(p
);
497 if (!ParseMenuResource(p
))
503 offset
= GET_WORD(p
);
505 //dwHelpId = GET_DWORD(p);
506 if (!ParseMenuExResource(p0
+ offset
))
514 UnlockResource(hglMenuTemplate
);
515 FreeResource(hglMenuTemplate
);
519 UnlockResource(hglMenuTemplate
);
520 FreeResource(hglMenuTemplate
);
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
;
533 MYERROR
; //just the language wasn't found
535 hglMenuTemplate
= LoadResource(m_hResDll
, hrsrc
);
537 if (!hglMenuTemplate
)
540 p
= (LPWSTR
)LockResource(hglMenuTemplate
);
545 //struct MenuHeader {
546 // WORD wVersion; // Currently zero
547 // WORD cbHeaderSize; // Also zero
551 //struct MenuExHeader {
552 // WORD wVersion; // One
557 version
= GET_WORD(p
);
565 offset
= GET_WORD(p
);
569 if (!CountMemReplaceMenuResource((WORD
*)p
, &nMem
, nullptr))
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
))
580 if (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newMenu
, (DWORD
)(nMem
+ (nMem
% 2)+2)*2))
586 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, wLanguage
, nullptr, 0)))
596 offset
= GET_WORD(p
);
598 //dwHelpId = GET_DWORD(p);
600 if (!CountMemReplaceMenuExResource((WORD
*)(p0
+ offset
), &nMem
, nullptr))
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
))
612 if (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newMenu
, (DWORD
)(nMem
+ (nMem
% 2) + 4) * 2))
618 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_MENU
, lpszType
, wLanguage
, nullptr, 0)))
630 UnlockResource(hglMenuTemplate
);
631 FreeResource(hglMenuTemplate
);
635 UnlockResource(hglMenuTemplate
);
636 FreeResource(hglMenuTemplate
);
640 const WORD
* CResModule::ParseMenuResource(const WORD
* res
)
645 //struct PopupMenuItem {
647 // WCHAR szItemText[];
649 //struct NormalMenuItem {
652 // WCHAR szItemText[];
657 flags
= GET_WORD(res
);
659 if (!(flags
& MF_POPUP
))
661 id
= GET_WORD(res
); //normal menu item
665 id
= (WORD
)-1; //popup menu item
667 LPCWSTR str
= (LPCWSTR
)res
;
668 size_t l
= wcslen(str
)+1;
671 if (flags
& MF_POPUP
)
673 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
674 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
676 CUtils::StringExtend(pBuf
);
678 std::wstring wstr
= std::wstring(pBuf
);
679 RESOURCEENTRY entry
= m_StringEntries
[wstr
];
681 InsertResourceIDs(RT_MENU
, 0, entry
, id
, L
" - PopupMenu");
683 m_StringEntries
[wstr
] = entry
;
686 if ((res
= ParseMenuResource(res
))==0)
691 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
692 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
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
;
704 menu_entry
.reference
= szTempBuf
;
705 menu_entry
.msgstr
= wstr
;
707 m_StringEntries
[wstr
] = entry
;
708 m_MenuEntries
[id
] = menu_entry
;
711 } while (!(flags
& MF_END
));
715 const WORD
* CResModule::CountMemReplaceMenuResource(const WORD
* res
, size_t * wordcount
, WORD
* newMenu
)
720 //struct PopupMenuItem {
722 // WCHAR szItemText[];
724 //struct NormalMenuItem {
727 // WCHAR szItemText[];
732 flags
= GET_WORD(res
);
737 newMenu
[(*wordcount
)++] = flags
;
738 if (!(flags
& MF_POPUP
))
740 id
= GET_WORD(res
); //normal menu item
745 newMenu
[(*wordcount
)++] = id
;
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)
760 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
761 res
+= wcslen((LPCWSTR
)res
) + 1;
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
));
774 const WORD
* CResModule::ParseMenuExResource(const WORD
* res
)
778 //struct MenuExItem {
784 // DWORD dwHelpId; - Popup menu only
789 DWORD dwType
= GET_DWORD(res
);
791 //dwState = GET_DWORD(res);
793 DWORD menuId
= GET_DWORD(res
);
795 bResInfo
= GET_WORD(res
);
798 LPCWSTR str
= (LPCWSTR
)res
;
799 size_t l
= wcslen(str
)+1;
801 // Align to DWORD boundary
802 res
= AlignWORD(res
);
804 if (dwType
& MFT_SEPARATOR
)
809 // Popup menu - note this can also have a non-zero ID
812 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
813 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
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
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
;
833 if ((res
= ParseMenuExResource(res
)) == 0)
835 } else if (menuId
!= 0)
837 TCHAR
* pBuf
= new TCHAR
[MAX_STRING_LENGTH
];
838 SecureZeroMemory(pBuf
, MAX_STRING_LENGTH
* sizeof(TCHAR
));
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
;
856 } while (!(bResInfo
& 0x80));
860 const WORD
* CResModule::CountMemReplaceMenuExResource(const WORD
* res
, size_t * wordcount
, WORD
* newMenu
)
864 //struct MenuExItem {
870 // DWORD dwHelpId; - Popup menu only
875 WORD
* p0
= (WORD
*)res
;
876 DWORD dwType
= GET_DWORD(res
);
878 //dwState = GET_DWORD(res);
880 DWORD menuId
= GET_DWORD(res
);
882 bResInfo
= GET_WORD(res
);
886 CopyMemory(&newMenu
[*wordcount
], p0
, 7 * sizeof(WORD
));
890 if (dwType
& MFT_SEPARATOR
) {
899 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
900 res
+= wcslen((LPCWSTR
)res
) + 1;
902 res
= AlignWORD(res
);
903 if ((*wordcount
) & 0x01)
907 CopyMemory(&newMenu
[*wordcount
], res
, sizeof(DWORD
)); // Copy Help ID
912 if ((res
= CountMemReplaceMenuExResource(res
, wordcount
, newMenu
)) == 0)
915 else if (menuId
!= 0)
917 ReplaceStr((LPCWSTR
)res
, newMenu
, wordcount
, &m_bTranslatedMenuStrings
, &m_bDefaultMenuStrings
);
918 res
+= wcslen((LPCWSTR
)res
) + 1;
923 wcscpy((wchar_t *)&newMenu
[(*wordcount
)], (LPCWSTR
)res
);
924 (*wordcount
) += wcslen((LPCWSTR
)res
) + 1;
925 res
+= wcslen((LPCWSTR
)res
) + 1;
928 res
= AlignWORD(res
);
929 if ((*wordcount
) & 0x01)
931 } while (!(bResInfo
& 0x80));
935 BOOL
CResModule::ExtractAccelerator(LPCTSTR lpszType
)
937 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_ACCELERATOR
);
939 WORD fFlags
, wAnsi
, wID
;
946 hglAccTable
= LoadResource(m_hResDll
, hrsrc
);
951 p
= (const WORD
*)LockResource(hglAccTable
);
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
);
974 p
++; // Skip over padding
976 if ((fFlags
& 0x80) == 0x80)
981 if ((wAnsi
< 0x30) ||
983 (wAnsi
>= 0x3A && wAnsi
<= 0x40))
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'
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");
1015 wcscat(pBuf
.get(), L
" ");
1017 if ((fFlags
& FALT
) == FALT
) // 0x10
1018 wcscat(pBuf
.get(), L
"A");
1020 wcscat(pBuf
.get(), L
" ");
1022 if ((fFlags
& FCONTROL
) == FCONTROL
) // 0x08
1023 wcscat(pBuf
.get(), L
"C");
1025 wcscat(pBuf
.get(), L
" ");
1027 if ((fFlags
& FSHIFT
) == FSHIFT
) // 0x04
1028 wcscat(pBuf
.get(), L
"S");
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
));
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
;
1051 UnlockResource(hglAccTable
);
1052 FreeResource(hglAccTable
);
1056 UnlockResource(hglAccTable
);
1057 FreeResource(hglAccTable
);
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
;
1070 haccelOld
= LoadAccelerators(m_hResDll
, lpszType
);
1075 cAccelerators
= CopyAcceleratorTable(haccelOld
, nullptr, 0);
1077 lpaccelNew
= (LPACCEL
) LocalAlloc(LPTR
, cAccelerators
* sizeof(ACCEL
));
1082 CopyAcceleratorTable(haccelOld
, lpaccelNew
, cAccelerators
);
1084 // Find the accelerator that the user modified
1085 // and change its flags and virtual-key code
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))
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");
1109 wcscat(pBuf
.get(), L
" ");
1111 if ((lpaccelNew
[i
].fVirt
& FALT
) == FALT
) // 0x10
1112 wcscat(pBuf
.get(), L
"A");
1114 wcscat(pBuf
.get(), L
" ");
1116 if ((lpaccelNew
[i
].fVirt
& FCONTROL
) == FCONTROL
) // 0x08
1117 wcscat(pBuf
.get(), L
"C");
1119 wcscat(pBuf
.get(), L
" ");
1121 if ((lpaccelNew
[i
].fVirt
& FSHIFT
) == FSHIFT
) // 0x04
1122 wcscat(pBuf
.get(), L
"S");
1124 wcscat(pBuf
.get(), L
" ");
1126 swprintf(pBuf2
.get(), L
"%s+%c", pBuf
.get(), lpaccelNew
[i
].key
);
1129 std::map
<std::wstring
, RESOURCEENTRY
>::iterator pAK_iter
= m_StringEntries
.find(pBuf2
.get());
1130 if (pAK_iter
!= m_StringEntries
.end())
1132 m_bTranslatedAcceleratorStrings
++;
1135 std::wstring wtemp
= pAK_iter
->second
.msgstr
;
1136 wtemp
= wtemp
.substr(wtemp
.find_last_of(':')+1);
1137 if (wtemp
.size() != 6)
1139 if (wtemp
.compare(0, 1, L
"V") == 0)
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)
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)
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)
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
;
1163 m_bDefaultAcceleratorStrings
++;
1167 // Create the new accelerator table
1168 hglAccTableNew
= LocalAlloc(LPTR
, cAccelerators
* 4 * sizeof(WORD
));
1169 if (!hglAccTableNew
)
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);
1177 memcpy((void *)p
, &lpaccelNew
[i
].key
, sizeof(WORD
));
1179 memcpy((void *)p
, &lpaccelNew
[i
].cmd
, sizeof(WORD
));
1184 if (!UpdateResource(m_hUpdateRes
, RT_ACCELERATOR
, lpszType
,
1185 (m_wTargetLang
? m_wTargetLang
: wLanguage
), hglAccTableNew
/* haccelNew*/, cAccelerators
* 4 * sizeof(WORD
)))
1190 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_ACCELERATOR
, lpszType
, wLanguage
, nullptr, 0)))
1195 LocalFree(hglAccTableNew
);
1196 LocalFree(lpaccelNew
);
1200 LocalFree(hglAccTableNew
);
1201 LocalFree(lpaccelNew
);
1205 BOOL
CResModule::ExtractDialog(LPCTSTR lpszType
)
1208 const WORD
* lpDlgItem
;
1210 DLGITEMINFO dlgItem
;
1213 HGLOBAL hGlblDlgTemplate
;
1215 hrsrc
= FindResource(m_hResDll
, lpszType
, RT_DIALOG
);
1220 hGlblDlgTemplate
= LoadResource(m_hResDll
, hrsrc
);
1221 if (!hGlblDlgTemplate
)
1224 lpDlg
= (const WORD
*) LockResource(hGlblDlgTemplate
);
1229 lpDlgItem
= (const WORD
*) GetDialogInfo(lpDlg
, &dlg
);
1230 bNumControls
= dlg
.nbItems
;
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
;
1247 while (bNumControls
-- != 0)
1249 TCHAR szTitle
[500] = { 0 };
1250 SecureZeroMemory(szTitle
, sizeof(szTitle
));
1253 lpDlgItem
= GetControlInfo((WORD
*) lpDlgItem
, &dlgItem
, dlg
.dialogEx
, &bCode
);
1256 wcsncpy(szTitle
, dlgItem
.windowName
, _countof(szTitle
) - 1);
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
);
1275 BOOL
CResModule::ReplaceDialog(LPCTSTR lpszType
, WORD wLanguage
)
1279 HGLOBAL hGlblDlgTemplate
;
1281 hrsrc
= FindResourceEx(m_hResDll
, RT_DIALOG
, lpszType
, wLanguage
);
1286 hGlblDlgTemplate
= LoadResource(m_hResDll
, hrsrc
);
1288 if (!hGlblDlgTemplate
)
1291 lpDlg
= (WORD
*) LockResource(hGlblDlgTemplate
);
1297 const WORD
* p
= lpDlg
;
1298 if (!CountMemReplaceDialogResource(p
, &nMem
, nullptr))
1300 WORD
* newDialog
= new WORD
[nMem
+ (nMem
% 2)];
1301 SecureZeroMemory(newDialog
, (nMem
+ (nMem
% 2))*2);
1304 if (!CountMemReplaceDialogResource(lpDlg
, &index
, newDialog
))
1306 delete [] newDialog
;
1310 if (!UpdateResource(m_hUpdateRes
, RT_DIALOG
, lpszType
, (m_wTargetLang
? m_wTargetLang
: wLanguage
), newDialog
, (DWORD
)(nMem
+ (nMem
% 2))*2))
1312 delete [] newDialog
;
1316 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_DIALOG
, lpszType
, wLanguage
, nullptr, 0)))
1318 delete [] newDialog
;
1322 delete [] newDialog
;
1323 UnlockResource(hGlblDlgTemplate
);
1324 FreeResource(hGlblDlgTemplate
);
1328 UnlockResource(hGlblDlgTemplate
);
1329 FreeResource(hGlblDlgTemplate
);
1333 const WORD
* CResModule::GetDialogInfo(const WORD
* pTemplate
, LPDIALOGINFO lpDlgInfo
) const
1335 const WORD
* p
= (const WORD
*)pTemplate
;
1337 lpDlgInfo
->style
= GET_DWORD(p
);
1340 if (lpDlgInfo
->style
== 0xffff0001) // DIALOGEX resource
1342 lpDlgInfo
->dialogEx
= TRUE
;
1343 lpDlgInfo
->helpId
= GET_DWORD(p
);
1345 lpDlgInfo
->exStyle
= GET_DWORD(p
);
1347 lpDlgInfo
->style
= GET_DWORD(p
);
1352 lpDlgInfo
->dialogEx
= FALSE
;
1353 lpDlgInfo
->helpId
= 0;
1354 lpDlgInfo
->exStyle
= GET_DWORD(p
);
1358 lpDlgInfo
->nbItems
= GET_WORD(p
);
1361 lpDlgInfo
->x
= GET_WORD(p
);
1364 lpDlgInfo
->y
= GET_WORD(p
);
1367 lpDlgInfo
->cx
= GET_WORD(p
);
1370 lpDlgInfo
->cy
= GET_WORD(p
);
1373 // Get the menu name
1375 switch (GET_WORD(p
))
1378 lpDlgInfo
->menuName
= nullptr;
1382 lpDlgInfo
->menuName
= (LPCTSTR
) (WORD
) GET_WORD(p
+ 1);
1386 lpDlgInfo
->menuName
= (LPCTSTR
) p
;
1387 p
+= wcslen((LPCWSTR
) p
) + 1;
1391 // Get the class name
1393 switch (GET_WORD(p
))
1396 lpDlgInfo
->className
= (LPCTSTR
)MAKEINTATOM(32770);
1400 lpDlgInfo
->className
= (LPCTSTR
) (WORD
) GET_WORD(p
+ 1);
1404 lpDlgInfo
->className
= (LPCTSTR
) p
;
1405 p
+= wcslen((LPCTSTR
)p
) + 1;
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
);
1421 if (lpDlgInfo
->dialogEx
)
1423 lpDlgInfo
->weight
= GET_WORD(p
);
1425 lpDlgInfo
->italic
= LOBYTE(GET_WORD(p
));
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
1443 const WORD
* CResModule::GetControlInfo(const WORD
* p
, LPDLGITEMINFO lpDlgItemInfo
, BOOL dialogEx
, LPBOOL bIsID
) const
1447 lpDlgItemInfo
->helpId
= GET_DWORD(p
);
1449 lpDlgItemInfo
->exStyle
= GET_DWORD(p
);
1451 lpDlgItemInfo
->style
= GET_DWORD(p
);
1456 lpDlgItemInfo
->helpId
= 0;
1457 lpDlgItemInfo
->style
= GET_DWORD(p
);
1459 lpDlgItemInfo
->exStyle
= GET_DWORD(p
);
1463 lpDlgItemInfo
->x
= GET_WORD(p
);
1466 lpDlgItemInfo
->y
= GET_WORD(p
);
1469 lpDlgItemInfo
->cx
= GET_WORD(p
);
1472 lpDlgItemInfo
->cy
= GET_WORD(p
);
1477 // ID is a DWORD for DIALOGEX
1478 lpDlgItemInfo
->id
= (WORD
) GET_DWORD(p
);
1483 lpDlgItemInfo
->id
= GET_WORD(p
);
1487 if (GET_WORD(p
) == 0xffff)
1495 lpDlgItemInfo
->className
= (LPCTSTR
) p
;
1496 p
+= wcslen((LPCWSTR
) p
) + 1;
1499 if (GET_WORD(p
) == 0xffff) // an integer ID?
1502 lpDlgItemInfo
->windowName
= (LPCTSTR
) (UINT_PTR
) GET_WORD(p
+ 1);
1508 lpDlgItemInfo
->windowName
= (LPCTSTR
) p
;
1509 p
+= wcslen((LPCWSTR
) p
) + 1;
1514 lpDlgItemInfo
->data
= (LPVOID
) (p
+ 1);
1515 p
+= GET_WORD(p
) / sizeof(WORD
);
1518 lpDlgItemInfo
->data
= nullptr;
1521 // Next control is on DWORD boundary
1526 const WORD
* CResModule::CountMemReplaceDialogResource(const WORD
* res
, size_t * wordcount
, WORD
* newDialog
)
1529 DWORD style
= GET_DWORD(res
);
1532 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1533 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1541 if (style
== 0xffff0001) // DIALOGEX resource
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
1557 style
= GET_DWORD(res
);
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
1581 newDialog
[(*wordcount
)] = GET_WORD(res
);
1582 WORD nbItems
= GET_WORD(res
);
1587 newDialog
[(*wordcount
)] = GET_WORD(res
); //x
1592 newDialog
[(*wordcount
)] = GET_WORD(res
); //y
1597 newDialog
[(*wordcount
)] = GET_WORD(res
); //cx
1602 newDialog
[(*wordcount
)] = GET_WORD(res
); //cy
1606 // Get the menu name
1608 switch (GET_WORD(res
))
1612 newDialog
[(*wordcount
)] = GET_WORD(res
);
1619 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1620 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1631 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1633 (*wordcount
) += wcslen((LPCWSTR
) res
) + 1;
1634 res
+= wcslen((LPCWSTR
) res
) + 1;
1638 // Get the class name
1640 switch (GET_WORD(res
))
1644 newDialog
[(*wordcount
)] = GET_WORD(res
);
1651 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1652 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1663 wcscpy((LPWSTR
)&newDialog
[(*wordcount
)], (LPCWSTR
)res
);
1665 (*wordcount
) += wcslen((LPCWSTR
) res
) + 1;
1666 res
+= wcslen((LPCWSTR
) res
) + 1;
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
)
1680 newDialog
[(*wordcount
)] = GET_WORD(res
);
1688 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1689 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
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)
1706 while ((UINT_PTR
)res
% 4)
1711 res
= ReplaceControlInfo(res
, wordcount
, newDialog
, bEx
);
1716 const WORD
* CResModule::ReplaceControlInfo(const WORD
* res
, size_t * wordcount
, WORD
* newDialog
, BOOL bEx
)
1722 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //helpid
1723 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //helpid
1733 LONG
* exStyle
= (LONG
*)&newDialog
[(*wordcount
)];
1734 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1735 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //exStyle
1737 *exStyle
|= WS_EX_RTLREADING
;
1747 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1748 newDialog
[(*wordcount
)++] = GET_WORD(res
++); //style
1757 newDialog
[(*wordcount
)] = GET_WORD(res
); //x
1762 newDialog
[(*wordcount
)] = GET_WORD(res
); //y
1767 newDialog
[(*wordcount
)] = GET_WORD(res
); //cx
1772 newDialog
[(*wordcount
)] = GET_WORD(res
); //cy
1778 // ID is a DWORD for DIALOGEX
1781 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1782 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1793 newDialog
[(*wordcount
)] = GET_WORD(res
);
1798 if (GET_WORD(res
) == 0xffff) //classID
1802 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1803 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
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?
1823 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1824 newDialog
[(*wordcount
)++] = GET_WORD(res
++);
1834 ReplaceStr((LPCWSTR
)res
, newDialog
, wordcount
, &m_bTranslatedDialogStrings
, &m_bDefaultDialogStrings
);
1835 res
+= wcslen((LPCWSTR
)res
) + 1;
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)
1845 res
= AlignWORD(res
);
1850 BOOL
CResModule::ExtractRibbon(LPCTSTR lpszType
)
1852 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_RIBBON
);
1853 HGLOBAL hglRibbonTemplate
;
1859 hglRibbonTemplate
= LoadResource(m_hResDll
, hrsrc
);
1861 DWORD sizeres
= SizeofResource(m_hResDll
, hrsrc
);
1863 if (!hglRibbonTemplate
)
1866 p
= (const BYTE
*)LockResource(hglRibbonTemplate
);
1871 // Resource consists of one single string
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
)
1883 std::string str1
= (*it
)[1];
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];
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];
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
);
1937 BOOL
CResModule::ReplaceRibbon(LPCTSTR lpszType
, WORD wLanguage
)
1939 HRSRC hrsrc
= FindResource(m_hResDll
, lpszType
, RT_RIBBON
);
1940 HGLOBAL hglRibbonTemplate
;
1946 hglRibbonTemplate
= LoadResource(m_hResDll
, hrsrc
);
1948 DWORD sizeres
= SizeofResource(m_hResDll
, hrsrc
);
1950 if (!hglRibbonTemplate
)
1953 p
= (const BYTE
*)LockResource(hglRibbonTemplate
);
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
++;
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
++;
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))
2034 if (m_wTargetLang
&& (!UpdateResource(m_hUpdateRes
, RT_RIBBON
, lpszType
, wLanguage
, nullptr, 0)))
2040 UnlockResource(hglRibbonTemplate
);
2041 FreeResource(hglRibbonTemplate
);
2045 UnlockResource(hglRibbonTemplate
);
2046 FreeResource(hglRibbonTemplate
);
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
&)
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
));
2077 catch (std::exception
&)
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
))
2096 else if (lpszType
== RT_MENU
)
2098 if (IS_INTRESOURCE(lpszName
))
2100 if (!lpResModule
->ExtractMenu(lpszName
))
2104 else if (lpszType
== RT_DIALOG
)
2106 if (IS_INTRESOURCE(lpszName
))
2108 if (!lpResModule
->ExtractDialog(lpszName
))
2112 else if (lpszType
== RT_ACCELERATOR
)
2114 if (IS_INTRESOURCE(lpszName
))
2116 if (!lpResModule
->ExtractAccelerator(lpszName
))
2120 else if (lpszType
== RT_RIBBON
)
2122 if (IS_INTRESOURCE(lpszName
))
2124 if (!lpResModule
->ExtractRibbon(lpszName
))
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
)
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
);
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
));
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
);
2187 wcscpy((wchar_t *)&dest
[(*count
)], pBuf
);
2188 (*count
) += wcslen(pBuf
)+1;
2193 if (wcscmp(pBuf
, wstr
.c_str()))
2196 wcscpy((wchar_t*)&dest
[(*count
)], pBuf
);
2197 (*count
) += wcslen(pBuf
) + 1;
2203 wcscpy((wchar_t*)&dest
[(*count
)], src
);
2204 (*count
) += wcslen(src
) + 1;
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
)
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);
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
);
2254 if (StartsWith(text
, "IDS_"))
2256 m_currentHeaderDataStrings
[value
] = CUnicodeUtils::StdGetUnicode(text
);
2259 else if (StartsWith(text
, "IDD_"))
2261 m_currentHeaderDataDialogs
[value
] = CUnicodeUtils::StdGetUnicode(text
);
2264 else if (StartsWith(text
, "ID_"))
2266 m_currentHeaderDataMenus
[value
] = CUnicodeUtils::StdGetUnicode(text
);
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);
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
);
2300 if (StartsWith(text
, L
"IDS_"))
2302 m_currentHeaderDataStrings
[value
] = text
;
2305 else if (StartsWith(text
, L
"IDD_"))
2307 m_currentHeaderDataDialogs
[value
] = text
;
2310 else if (StartsWith(text
, L
"ID_"))
2312 m_currentHeaderDataMenus
[value
] = text
;
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
);
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
);
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
);
2347 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);
2349 else if (lpType
== RT_RIBBON
&& infotext
&& wcsstr(infotext
, L
"ID") == infotext
)
2350 entry
.resourceIDs
.insert(infotext
);
2352 entry
.resourceIDs
.insert(NumToStr(id
) + infotext
);