From 269870255cc65ce69462653853513d9b97ef7e60 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 7 Feb 2009 21:59:21 +0800 Subject: [PATCH] Add ResText Source code from SVN 14346 --- src/ResText/POFile.cpp | 236 ++++++ src/ResText/POFile.h | 53 ++ src/ResText/ResModule.cpp | 1900 ++++++++++++++++++++++++++++++++++++++++++++ src/ResText/ResModule.h | 137 ++++ src/ResText/ResText.cpp | 145 ++++ src/ResText/ResText.vcproj | 871 ++++++++++++++++++++ src/ResText/Utils.cpp | 152 ++++ src/ResText/Utils.h | 32 + src/ResText/codecvt.cpp | 137 ++++ src/ResText/codecvt.h | 64 ++ src/ResText/stdafx.cpp | 5 + src/ResText/stdafx.h | 13 + 12 files changed, 3745 insertions(+) create mode 100644 src/ResText/POFile.cpp create mode 100644 src/ResText/POFile.h create mode 100644 src/ResText/ResModule.cpp create mode 100644 src/ResText/ResModule.h create mode 100644 src/ResText/ResText.cpp create mode 100644 src/ResText/ResText.vcproj create mode 100644 src/ResText/Utils.cpp create mode 100644 src/ResText/Utils.h create mode 100644 src/ResText/codecvt.cpp create mode 100644 src/ResText/codecvt.h create mode 100644 src/ResText/stdafx.cpp create mode 100644 src/ResText/stdafx.h diff --git a/src/ResText/POFile.cpp b/src/ResText/POFile.cpp new file mode 100644 index 0000000..e4d5fbc --- /dev/null +++ b/src/ResText/POFile.cpp @@ -0,0 +1,236 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2008 - TortoiseSVN + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#include "StdAfx.h" +#include "shlwapi.h" +#include +#include "codecvt.h" +#include "Utils.h" +#include "ResModule.h" +#include ".\pofile.h" + +#define MYERROR {CUtils::Error(); return FALSE;} + +CPOFile::CPOFile() +{ +} + +CPOFile::~CPOFile(void) +{ +} + +BOOL CPOFile::ParseFile(LPCTSTR szPath, BOOL bUpdateExisting /* = TRUE */) +{ + if (!PathFileExists(szPath)) + return FALSE; + + if (!m_bQuiet) + _ftprintf(stdout, _T("parsing file %s...\n"), szPath); + + int nEntries = 0; + int nDeleted = 0; + int nTranslated = 0; + //since stream classes still expect the filepath in char and not wchar_t + //we need to convert the filepath to multibyte + char filepath[MAX_PATH+1]; + SecureZeroMemory(filepath, sizeof(filepath)); + WideCharToMultiByte(CP_ACP, NULL, szPath, -1, filepath, MAX_PATH, NULL, NULL); + + std::wifstream File; + File.imbue(std::locale(std::locale(), new utf8_conversion())); + File.open(filepath); + if (!File.good()) + { + _ftprintf(stderr, _T("can't open input file %s\n"), szPath); + return FALSE; + } + TCHAR line[2*MAX_STRING_LENGTH]; + std::vector entry; + do + { + File.getline(line, sizeof(line)/sizeof(TCHAR)); + if (line[0]==0) + { + //empty line means end of entry! + RESOURCEENTRY resEntry; + std::wstring msgid; + int type = 0; + for (std::vector::iterator I = entry.begin(); I != entry.end(); ++I) + { + if (_tcsncmp(I->c_str(), _T("# "), 2)==0) + { + //user comment + resEntry.translatorcomments.push_back(I->c_str()); + type = 0; + } + if (_tcsncmp(I->c_str(), _T("#."), 2)==0) + { + //automatic comments + resEntry.automaticcomments.push_back(I->c_str()); + type = 0; + } + if (_tcsncmp(I->c_str(), _T("#,"), 2)==0) + { + //flag + resEntry.flag = I->c_str(); + type = 0; + } + if (_tcsncmp(I->c_str(), _T("msgid"), 5)==0) + { + //message id + msgid = I->c_str(); + msgid = std::wstring(msgid.substr(7, msgid.size() - 8)); + nEntries++; + type = 1; + } + if (_tcsncmp(I->c_str(), _T("msgstr"), 6)==0) + { + //message string + resEntry.msgstr = I->c_str(); + resEntry.msgstr = resEntry.msgstr.substr(8, resEntry.msgstr.length() - 9); + if (resEntry.msgstr.size()>0) + nTranslated++; + type = 2; + } + if (_tcsncmp(I->c_str(), _T("\""), 1)==0) + { + if (type == 1) + { + std::wstring temp = I->c_str(); + temp = temp.substr(1, temp.length()-2); + msgid += temp; + } + if (type == 2) + { + if (resEntry.msgstr.size() == 0) + nTranslated++; + std::wstring temp = I->c_str(); + temp = temp.substr(1, temp.length()-2); + resEntry.msgstr += temp; + } + } + } + entry.clear(); + if ((bUpdateExisting)&&(this->count(msgid) == 0)) + nDeleted++; + else + (*this)[msgid] = resEntry; + msgid.clear(); + } + else + { + entry.push_back(line); + } + } while (File.gcount() > 0); + printf(File.getloc().name().c_str()); + File.close(); + RESOURCEENTRY emptyentry; + (*this)[std::wstring(_T(""))] = emptyentry; + if (!m_bQuiet) + _ftprintf(stdout, _T("%d Entries found, %d were already translated and %d got deleted\n"), nEntries, nTranslated, nDeleted); + return TRUE; +} + +BOOL CPOFile::SaveFile(LPCTSTR szPath) +{ + //since stream classes still expect the filepath in char and not wchar_t + //we need to convert the filepath to multibyte + char filepath[MAX_PATH+1]; + int nEntries = 0; + SecureZeroMemory(filepath, sizeof(filepath)); + WideCharToMultiByte(CP_ACP, NULL, szPath, -1, filepath, MAX_PATH, NULL, NULL); + + std::wofstream File; +// File.open(filepath, std::ios_base::binary); +// File << _T("\xEF\xBB\xBF"); +// File.close(); + File.imbue(std::locale(std::locale(), new utf8_conversion())); + File.open(filepath, std::ios_base::binary); + File << _T("# SOME DESCRIPTIVE TITLE.\n"); + File << _T("# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n"); + File << _T("# This file is distributed under the same license as the PACKAGE package.\n"); + File << _T("# FIRST AUTHOR , YEAR.\n"); + File << _T("#\n"); + File << _T("#, fuzzy\n"); + File << _T("msgid \"\"\n"); + File << _T("msgstr \"\"\n"); + File << _T("\"Project-Id-Version: PACKAGE VERSION\\n\"\n"); + File << _T("\"Report-Msgid-Bugs-To: \\n\"\n"); + File << _T("\"POT-Creation-Date: 1900-01-01 00:00+0000\\n\"\n"); + File << _T("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"); + File << _T("\"Last-Translator: FULL NAME \\n\"\n"); + File << _T("\"Language-Team: LANGUAGE \\n\"\n"); + File << _T("\"MIME-Version: 1.0\\n\"\n"); + File << _T("\"Content-Type: text/plain; charset=UTF-8\\n\"\n"); + File << _T("\"Content-Transfer-Encoding: 8bit\\n\"\n\n"); + File << _T("\n"); + File << _T("# msgid/msgstr fields for Accelerator keys\n"); + File << _T("# Format is: \"ID:xxxxxx:VACS+X\" where:\n"); + File << _T("# ID:xxxxx = the menu ID corresponding to the accelerator\n"); + File << _T("# V = Virtual key (or blank if not used) - nearly always set!\n"); + File << _T("# A = Alt key (or blank if not used)\n"); + File << _T("# C = Ctrl key (or blank if not used)\n"); + File << _T("# S = Shift key (or blank if not used)\n"); + File << _T("# X = upper case character\n"); + File << _T("# e.g. \"V CS+Q\" == Ctrl + Shift + 'Q'\n"); + File << _T("\n"); + File << _T("# ONLY Accelerator Keys with corresponding alphanumeric characters can be\n"); + File << _T("# updated i.e. function keys (F2), special keys (Delete, HoMe) etc. will not.\n"); + File << _T("\n"); + File << _T("# ONLY change the msgstr field. Do NOT change any other.\n"); + File << _T("# If you do not want to change an Accelerator Key, copy msgid to msgstr\n"); + File << _T("\n"); + + for (std::map::iterator I = this->begin(); I != this->end(); ++I) + { + if (I->first.size() == 0) + continue; + RESOURCEENTRY entry = I->second; + for (std::vector::iterator II = entry.automaticcomments.begin(); II != entry.automaticcomments.end(); ++II) + { + File << II->c_str() << _T("\n"); + } + for (std::vector::iterator II = entry.translatorcomments.begin(); II != entry.translatorcomments.end(); ++II) + { + File << II->c_str() << _T("\n"); + } + if (I->second.resourceIDs.size() > 0) + { + File << _T("#. Resource IDs: ("); + + std::set::const_iterator II = I->second.resourceIDs.begin(); + File << (*II); + ++II; + while (II != I->second.resourceIDs.end()) + { + File << _T(", "); + File << (*II); + ++II; + }; + File << _T(")\n"); + } + if (I->second.flag.length() > 0) + File << (I->second.flag.c_str()) << _T("\n"); + File << (_T("msgid \"")) << (I->first.c_str()) << _T("\"\n"); + File << (_T("msgstr \"")) << (I->second.msgstr.c_str()) << _T("\"\n\n"); + nEntries++; + } + File.close(); + if (!m_bQuiet) + _ftprintf(stdout, _T("File %s saved, containing %d entries\n"), szPath, nEntries); + return TRUE; +} diff --git a/src/ResText/POFile.h b/src/ResText/POFile.h new file mode 100644 index 0000000..3111479 --- /dev/null +++ b/src/ResText/POFile.h @@ -0,0 +1,53 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2007 - TortoiseSVN + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#pragma once +#include +#include +#include +#include + +typedef struct tagResourceEntry +{ + WORD menuID; + std::vector translatorcomments; + std::vector automaticcomments; + std::set resourceIDs; + std::wstring flag; + std::wstring msgstr; +} RESOURCEENTRY, * LPRESOURCEENTRY; + +/** + * \ingroup ResText + * Class to handle po-files. Inherits from an std::map which assigns + * string IDs to additional information, including the translated strings. + * + * Provides methods to load and save a po-file with the translation information + * we need for ResText. + */ +class CPOFile : public std::map +{ +public: + CPOFile(); + ~CPOFile(void); + + BOOL ParseFile(LPCTSTR szPath, BOOL bUpdateExisting = TRUE); + BOOL SaveFile(LPCTSTR szPath); + void SetQuiet(BOOL bQuiet = TRUE) {m_bQuiet = bQuiet;} +private: + BOOL m_bQuiet; +}; diff --git a/src/ResText/ResModule.cpp b/src/ResText/ResModule.cpp new file mode 100644 index 0000000..1a14fe1 --- /dev/null +++ b/src/ResText/ResModule.cpp @@ -0,0 +1,1900 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2008 - TortoiseSVN + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#include "StdAfx.h" +#include "Utils.h" +#include ".\resmodule.h" + +#define MYERROR {CUtils::Error(); return FALSE;} + +CResModule::CResModule(void) + : m_bTranslatedStrings(0) + , m_bDefaultStrings(0) + , m_bTranslatedDialogStrings(0) + , m_bDefaultDialogStrings(0) + , m_bTranslatedMenuStrings(0) + , m_bDefaultMenuStrings(0) + , m_bTranslatedAcceleratorStrings(0) + , m_bDefaultAcceleratorStrings(0) + , m_wTargetLang(0) + , m_hResDll(NULL) + , m_hUpdateRes(NULL) + , m_bQuiet(false) + , m_bRTL(false) +{ +} + +CResModule::~CResModule(void) +{ +} + +BOOL CResModule::ExtractResources(std::vector filelist, LPCTSTR lpszPOFilePath, BOOL bNoUpdate) +{ + BOOL bRet = TRUE; + for (std::vector::iterator I = filelist.begin(); I != filelist.end(); ++I) + { + m_hResDll = LoadLibrary(I->c_str()); + if (m_hResDll == NULL) + MYERROR; + + size_t nEntries = m_StringEntries.size(); + // fill in the std::map with all translatable entries + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting StringTable....")); + EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Dialogs........")); + EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Menus..........")); + EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Accelerators...")); + EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Accelerators\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + // parse a probably existing file and update the translations which are + // already done + m_StringEntries.ParseFile(lpszPOFilePath, !bNoUpdate); + + FreeLibrary(m_hResDll); + continue; + } + + // at last, save the new file + if (bRet) + return m_StringEntries.SaveFile(lpszPOFilePath); + return FALSE; +} + +BOOL CResModule::ExtractResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszPoFilePath, BOOL bNoUpdate) +{ + m_hResDll = LoadLibrary(lpszSrcLangDllPath); + if (m_hResDll == NULL) + MYERROR; + + size_t nEntries = 0; + // fill in the std::map with all translatable entries + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting StringTable....")); + EnumResourceNames(m_hResDll, RT_STRING, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()); + nEntries = m_StringEntries.size(); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Dialogs........")); + EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Menus..........")); + EnumResourceNames(m_hResDll, RT_MENU, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Strings\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Extracting Accelerators...")); + EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d Accelerators\n"), m_StringEntries.size()-nEntries); + nEntries = m_StringEntries.size(); + + // parse a probably existing file and update the translations which are + // already done + m_StringEntries.ParseFile(lpszPoFilePath, !bNoUpdate); + + // at last, save the new file + if (!m_StringEntries.SaveFile(lpszPoFilePath)) + goto DONE_ERROR; + + FreeLibrary(m_hResDll); + return TRUE; + +DONE_ERROR: + if (m_hResDll) + FreeLibrary(m_hResDll); + return FALSE; +} + +BOOL CResModule::CreateTranslatedResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszDestLangDllPath, LPCTSTR lpszPOFilePath) +{ + if (!CopyFile(lpszSrcLangDllPath, lpszDestLangDllPath, FALSE)) + MYERROR; + + int count = 0; + do + { + m_hResDll = LoadLibraryEx (lpszSrcLangDllPath, NULL, LOAD_LIBRARY_AS_DATAFILE|LOAD_IGNORE_CODE_AUTHZ_LEVEL); + if (m_hResDll == NULL) + Sleep(100); + count++; + } while ((m_hResDll == NULL)&&(count < 5)); + + if (m_hResDll == NULL) + MYERROR; + + sDestFile = std::wstring(lpszDestLangDllPath); + + // get all translated strings + if (!m_StringEntries.ParseFile(lpszPOFilePath, FALSE)) + goto DONE_ERROR; + m_bTranslatedStrings = 0; + m_bDefaultStrings = 0; + m_bTranslatedDialogStrings = 0; + m_bDefaultDialogStrings = 0; + m_bTranslatedMenuStrings = 0; + m_bDefaultMenuStrings = 0; + m_bTranslatedAcceleratorStrings = 0; + m_bDefaultAcceleratorStrings = 0; + + if (!m_bQuiet) + _ftprintf(stdout, _T("Translating StringTable...")); + EnumResourceNames(m_hResDll, RT_STRING, EnumResNameWriteCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d translated, %4d not translated\n"), m_bTranslatedStrings, m_bDefaultStrings); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Translating Dialogs.......")); + EnumResourceNames(m_hResDll, RT_DIALOG, EnumResNameWriteCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d translated, %4d not translated\n"), m_bTranslatedDialogStrings, m_bDefaultDialogStrings); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Translating Menus.........")); + EnumResourceNames(m_hResDll, RT_MENU, EnumResNameWriteCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d translated, %4d not translated\n"), m_bTranslatedMenuStrings, m_bDefaultMenuStrings); + + if (!m_bQuiet) + _ftprintf(stdout, _T("Translating Accelerators..")); + EnumResourceNames(m_hResDll, RT_ACCELERATOR, EnumResNameWriteCallback, (long)this); + if (!m_bQuiet) + _ftprintf(stdout, _T("%4d translated, %4d not translated\n"), m_bTranslatedAcceleratorStrings, m_bDefaultAcceleratorStrings); + + FreeLibrary(m_hResDll); + return TRUE; +DONE_ERROR: + if (m_hResDll) + FreeLibrary(m_hResDll); + return FALSE; +} + +BOOL CResModule::ExtractString(UINT nID) +{ + HRSRC hrsrc = FindResource(m_hResDll, MAKEINTRESOURCE(nID), RT_STRING); + HGLOBAL hglStringTable; + LPWSTR p; + + if (!hrsrc) + MYERROR; + hglStringTable = LoadResource(m_hResDll, hrsrc); + + if (!hglStringTable) + goto DONE_ERROR; + p = (LPWSTR)LockResource(hglStringTable); + + if (p == NULL) + goto DONE_ERROR; + /* [Block of 16 strings. The strings are Pascal style with a WORD + length preceding the string. 16 strings are always written, even + if not all slots are full. Any slots in the block with no string + have a zero WORD for the length.] + */ + + //first check how much memory we need + LPWSTR pp = p; + for (int i=0; i<16; ++i) + { + int len = GET_WORD(pp); + pp++; + std::wstring msgid = std::wstring(pp, len); + WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR)); + wcscpy(pBuf, msgid.c_str()); + CUtils::StringExtend(pBuf); + + if (wcslen(pBuf)) + { + std::wstring str = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[str]; + entry.resourceIDs.insert(nID); + if (wcschr(str.c_str(), '%')) + entry.flag = _T("#, c-format"); + m_StringEntries[str] = entry; + } + delete [] pBuf; + pp += len; + } + UnlockResource(hglStringTable); + FreeResource(hglStringTable); + return TRUE; +DONE_ERROR: + UnlockResource(hglStringTable); + FreeResource(hglStringTable); + MYERROR; +} + +BOOL CResModule::ReplaceString(UINT nID, WORD wLanguage) +{ + HRSRC hrsrc = FindResourceEx(m_hResDll, RT_STRING, MAKEINTRESOURCE(nID), wLanguage); + HGLOBAL hglStringTable; + LPWSTR p; + + if (!hrsrc) + MYERROR; + hglStringTable = LoadResource(m_hResDll, hrsrc); + + if (!hglStringTable) + goto DONE_ERROR; + p = (LPWSTR)LockResource(hglStringTable); + + if (p == NULL) + goto DONE_ERROR; +/* [Block of 16 strings. The strings are Pascal style with a WORD + length preceding the string. 16 strings are always written, even + if not all slots are full. Any slots in the block with no string + have a zero WORD for the length.] +*/ + + //first check how much memory we need + size_t nMem = 0; + LPWSTR pp = p; + for (int i=0; i<16; ++i) + { + nMem++; + size_t len = GET_WORD(pp); + pp++; + std::wstring msgid = std::wstring(pp, len); + WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR)); + wcscpy(pBuf, msgid.c_str()); + CUtils::StringExtend(pBuf); + msgid = std::wstring(pBuf); + + RESOURCEENTRY resEntry; + resEntry = m_StringEntries[msgid]; + wcscpy(pBuf, resEntry.msgstr.c_str()); + CUtils::StringCollapse(pBuf); + size_t newlen = wcslen(pBuf); + if (newlen) + nMem += newlen; + else + nMem += len; + pp += len; + delete [] pBuf; + } + + WORD * newTable = new WORD[nMem + (nMem % 2)]; + SecureZeroMemory(newTable, (nMem + (nMem % 2))*2); + + size_t index = 0; + for (int i=0; i<16; ++i) + { + int len = GET_WORD(p); + p++; + std::wstring msgid = std::wstring(p, len); + WCHAR * pBuf = new WCHAR[MAX_STRING_LENGTH*2]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH*2*sizeof(WCHAR)); + wcscpy(pBuf, msgid.c_str()); + CUtils::StringExtend(pBuf); + msgid = std::wstring(pBuf); + + RESOURCEENTRY resEntry; + resEntry = m_StringEntries[msgid]; + wcscpy(pBuf, resEntry.msgstr.c_str()); + CUtils::StringCollapse(pBuf); + size_t newlen = wcslen(pBuf); + if (newlen) + { + newTable[index++] = (WORD)newlen; + wcsncpy((wchar_t *)&newTable[index], pBuf, newlen); + index += newlen; + m_bTranslatedStrings++; + } + else + { + newTable[index++] = (WORD)len; + if (len) + wcsncpy((wchar_t *)&newTable[index], p, len); + index += len; + if (len) + m_bDefaultStrings++; + } + p += len; + delete [] pBuf; + } + + if (!UpdateResource(m_hUpdateRes, RT_STRING, MAKEINTRESOURCE(nID), (m_wTargetLang ? m_wTargetLang : wLanguage), newTable, (DWORD)(nMem + (nMem % 2))*2)) + { + delete [] newTable; + goto DONE_ERROR; + } + + if ((m_wTargetLang)&&(!UpdateResource(m_hUpdateRes, RT_STRING, MAKEINTRESOURCE(nID), wLanguage, NULL, 0))) + { + delete [] newTable; + goto DONE_ERROR; + } + delete [] newTable; + UnlockResource(hglStringTable); + FreeResource(hglStringTable); + return TRUE; +DONE_ERROR: + UnlockResource(hglStringTable); + FreeResource(hglStringTable); + MYERROR; +} + +BOOL CResModule::ExtractMenu(UINT nID) +{ + HRSRC hrsrc = FindResource(m_hResDll, MAKEINTRESOURCE(nID), RT_MENU); + HGLOBAL hglMenuTemplate; + WORD version, offset; + DWORD dwHelpId; + const WORD *p, *p0; + + if (!hrsrc) + MYERROR; + + hglMenuTemplate = LoadResource(m_hResDll, hrsrc); + + if (!hglMenuTemplate) + MYERROR; + + p = (const WORD*)LockResource(hglMenuTemplate); + + if (p == NULL) + MYERROR; + + // Standard MENU resource + //struct MenuHeader { + // WORD wVersion; // Currently zero + // WORD cbHeaderSize; // Also zero + //}; + + // MENUEX resource + //struct MenuExHeader { + // WORD wVersion; // One + // WORD wOffset; + // DWORD dwHelpId; + //}; + p0 = p; + version = GET_WORD(p); + + p++; + + switch (version) + { + case 0: + { + offset = GET_WORD(p); + p += offset; + p++; + if (!ParseMenuResource(p)) + goto DONE_ERROR; + } + break; + case 1: + { + offset = GET_WORD(p); + p++; + dwHelpId = GET_DWORD(p); + if (!ParseMenuExResource(p0 + offset)) + goto DONE_ERROR; + } + break; + default: + goto DONE_ERROR; + } + + UnlockResource(hglMenuTemplate); + FreeResource(hglMenuTemplate); + return TRUE; + +DONE_ERROR: + UnlockResource(hglMenuTemplate); + FreeResource(hglMenuTemplate); + MYERROR; +} + +BOOL CResModule::ReplaceMenu(UINT nID, WORD wLanguage) +{ + HRSRC hrsrc = FindResourceEx(m_hResDll, RT_MENU, MAKEINTRESOURCE(nID), wLanguage); + HGLOBAL hglMenuTemplate; + WORD version, offset; + LPWSTR p; + WORD *p0; + DWORD dwHelpId; + + if (!hrsrc) + MYERROR; //just the language wasn't found + + hglMenuTemplate = LoadResource(m_hResDll, hrsrc); + + if (!hglMenuTemplate) + MYERROR; + + p = (LPWSTR)LockResource(hglMenuTemplate); + + if (p == NULL) + MYERROR; + + //struct MenuHeader { + // WORD wVersion; // Currently zero + // WORD cbHeaderSize; // Also zero + //}; + + // MENUEX resource + //struct MenuExHeader { + // WORD wVersion; // One + // WORD wOffset; + // DWORD dwHelpId; + //}; + p0 = (WORD *)p; + version = GET_WORD(p); + + p++; + + switch (version) + { + case 0: + { + offset = GET_WORD(p); + p += offset; + p++; + size_t nMem = 0; + if (!CountMemReplaceMenuResource((WORD *)p, &nMem, NULL)) + goto DONE_ERROR; + WORD * newMenu = new WORD[nMem + (nMem % 2)+2]; + SecureZeroMemory(newMenu, (nMem + (nMem % 2)+2)*2); + size_t index = 2; // MenuHeader has 2 WORDs zero + if (!CountMemReplaceMenuResource((WORD *)p, &index, newMenu)) + { + delete [] newMenu; + goto DONE_ERROR; + } + + if (!UpdateResource(m_hUpdateRes, RT_MENU, MAKEINTRESOURCE(nID), (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2)+2)*2)) + { + delete [] newMenu; + goto DONE_ERROR; + } + + if ((m_wTargetLang)&&(!UpdateResource(m_hUpdateRes, RT_MENU, MAKEINTRESOURCE(nID), wLanguage, NULL, 0))) + { + delete [] newMenu; + goto DONE_ERROR; + } + delete [] newMenu; + } + break; + case 1: + { + offset = GET_WORD(p); + p++; + dwHelpId = GET_DWORD(p); + size_t nMem = 0; + if (!CountMemReplaceMenuExResource((WORD *)(p0 + offset), &nMem, NULL)) + goto DONE_ERROR; + WORD * newMenu = new WORD[nMem + (nMem % 2) + 4]; + SecureZeroMemory(newMenu, (nMem + (nMem % 2) + 4) * 2); + CopyMemory(newMenu, p0, 2 * sizeof(WORD) + sizeof(DWORD)); + size_t index = 4; // MenuExHeader has 2 x WORD + 1 x DWORD + if (!CountMemReplaceMenuExResource((WORD *)(p0 + offset), &index, newMenu)) + { + delete [] newMenu; + goto DONE_ERROR; + } + + if (!UpdateResource(m_hUpdateRes, RT_MENU, MAKEINTRESOURCE(nID), (m_wTargetLang ? m_wTargetLang : wLanguage), newMenu, (DWORD)(nMem + (nMem % 2) + 4) * 2)) + { + delete [] newMenu; + goto DONE_ERROR; + } + + if ((m_wTargetLang)&&(!UpdateResource(m_hUpdateRes, RT_MENU, MAKEINTRESOURCE(nID), wLanguage, NULL, 0))) + { + delete [] newMenu; + goto DONE_ERROR; + } + delete [] newMenu; + } + break; + default: + goto DONE_ERROR; + } + + UnlockResource(hglMenuTemplate); + FreeResource(hglMenuTemplate); + return TRUE; + +DONE_ERROR: + UnlockResource(hglMenuTemplate); + FreeResource(hglMenuTemplate); + MYERROR; +} + +const WORD* CResModule::ParseMenuResource(const WORD * res) +{ + WORD flags; + WORD id = 0; + LPCWSTR str; + WORD *p0; + + //struct PopupMenuItem { + // WORD fItemFlags; + // WCHAR szItemText[]; + //}; + //struct NormalMenuItem { + // WORD fItemFlags; + // WORD wMenuID; + // WCHAR szItemText[]; + //}; + + do + { + p0 = (WORD *)res; + flags = GET_WORD(res); + res++; + if (!(flags & MF_POPUP)) + { + id = GET_WORD(res); //normal menu item + res++; + } + else + id = (WORD)-1; //popup menu item + + str = (LPCWSTR)res; + size_t l = wcslen(str)+1; + res += l; + + if (flags & MF_POPUP) + { + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + _tcscpy(pBuf, str); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + if (id) + entry.resourceIDs.insert(id); + + m_StringEntries[wstr] = entry; + delete [] pBuf; + + if ((res = ParseMenuResource(res))==0) + return NULL; + } + else if (id != 0) + { + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + _tcscpy(pBuf, str); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + entry.resourceIDs.insert(id); + + TCHAR szTempBuf[1024]; + _stprintf(szTempBuf, _T("#: MenuEntry; ID:%d"), id); + MENUENTRY menu_entry; + menu_entry.wID = id; + menu_entry.reference = szTempBuf; + menu_entry.msgstr = wstr; + + m_StringEntries[wstr] = entry; + m_MenuEntries[id] = menu_entry; + delete [] pBuf; + } + } while (!(flags & MF_END)); + return res; +} + +const WORD* CResModule::CountMemReplaceMenuResource(const WORD * res, size_t * wordcount, WORD * newMenu) +{ + WORD flags; + WORD id = 0; + + //struct PopupMenuItem { + // WORD fItemFlags; + // WCHAR szItemText[]; + //}; + //struct NormalMenuItem { + // WORD fItemFlags; + // WORD wMenuID; + // WCHAR szItemText[]; + //}; + + do + { + flags = GET_WORD(res); + res++; + if (newMenu == NULL) + (*wordcount)++; + else + newMenu[(*wordcount)++] = flags; + if (!(flags & MF_POPUP)) + { + id = GET_WORD(res); //normal menu item + res++; + if (newMenu == NULL) + (*wordcount)++; + else + newMenu[(*wordcount)++] = id; + } + else + id = (WORD)-1; //popup menu item + + if (flags & MF_POPUP) + { + ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings); + res += wcslen((LPCWSTR)res) + 1; + + if ((res = CountMemReplaceMenuResource(res, wordcount, newMenu))==0) + return NULL; + } + else if (id != 0) + { + ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings); + res += wcslen((LPCWSTR)res) + 1; + } + else + { + if (newMenu) + wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res); + (*wordcount) += wcslen((LPCWSTR)res) + 1; + res += wcslen((LPCWSTR)res) + 1; + } + } while (!(flags & MF_END)); + return res; +} + +const WORD* CResModule::ParseMenuExResource(const WORD * res) +{ + DWORD dwType, dwState, menuId; + WORD bResInfo; + LPCWSTR str; + WORD *p0; + + //struct MenuExItem { + // DWORD dwType; + // DWORD dwState; + // DWORD menuId; + // WORD bResInfo; + // WCHAR szText[]; + // DWORD dwHelpId; - Popup menu only + //}; + + do + { + p0 = (WORD *)res; + dwType = GET_DWORD(res); + res += 2; + dwState = GET_DWORD(res); + res += 2; + menuId = GET_DWORD(res); + res += 2; + bResInfo = GET_WORD(res); + res++; + + str = (LPCWSTR)res; + size_t l = wcslen(str)+1; + res += l; + // Align to DWORD boundary + res += ((((WORD)res + 3) & ~3) - (WORD)res)/sizeof(WORD); + + if (dwType & MFT_SEPARATOR) + continue; + + if (bResInfo & 0x01) + { + // Popup menu - note this can also have a non-zero ID + if (menuId == 0) + menuId = (WORD)-1; + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + _tcscpy(pBuf, str); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + // Popup has a DWORD help entry on a DWORD boundary - skip over it + res += 2; + + entry.resourceIDs.insert(menuId); + TCHAR szTempBuf[1024]; + _stprintf(szTempBuf, _T("#: MenuExPopupEntry; ID:%d"), menuId); + MENUENTRY menu_entry; + menu_entry.wID = (WORD)menuId; + menu_entry.reference = szTempBuf; + menu_entry.msgstr = wstr; + m_StringEntries[wstr] = entry; + m_MenuEntries[(WORD)menuId] = menu_entry; + delete [] pBuf; + + if ((res = ParseMenuExResource(res)) == 0) + return NULL; + } else if (menuId != 0) + { + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + _tcscpy(pBuf, str); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + entry.resourceIDs.insert(menuId); + + TCHAR szTempBuf[1024]; + _stprintf(szTempBuf, _T("#: MenuExEntry; ID:%d"), menuId); + MENUENTRY menu_entry; + menu_entry.wID = (WORD)menuId; + menu_entry.reference = szTempBuf; + menu_entry.msgstr = wstr; + m_StringEntries[wstr] = entry; + m_MenuEntries[(WORD)menuId] = menu_entry; + delete [] pBuf; + } + } while (!(bResInfo & 0x80)); + return res; +} + +const WORD* CResModule::CountMemReplaceMenuExResource(const WORD * res, size_t * wordcount, WORD * newMenu) +{ + DWORD dwType, dwState, menuId; + WORD bResInfo; + WORD *p0; + + //struct MenuExItem { + // DWORD dwType; + // DWORD dwState; + // DWORD menuId; + // WORD bResInfo; + // WCHAR szText[]; + // DWORD dwHelpId; - Popup menu only + //}; + + do + { + p0 = (WORD *)res; + dwType = GET_DWORD(res); + res += 2; + dwState = GET_DWORD(res); + res += 2; + menuId = GET_DWORD(res); + res += 2; + bResInfo = GET_WORD(res); + res++; + + if (newMenu != NULL) { + CopyMemory(&newMenu[*wordcount], p0, 7 * sizeof(WORD)); + } + (*wordcount) += 7; + + if (dwType & MFT_SEPARATOR) { + // Align to DWORD + (*wordcount)++; + res++; + continue; + } + + if (bResInfo & 0x01) + { + ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings); + res += wcslen((LPCWSTR)res) + 1; + // Align to DWORD + res += ((((WORD)res + 3) & ~3) - (WORD)res)/sizeof(WORD); + if ((*wordcount) & 0x01) + (*wordcount)++; + + if (newMenu != NULL) + CopyMemory(&newMenu[*wordcount], res, sizeof(DWORD)); // Copy Help ID + + res += 2; + (*wordcount) += 2; + + if ((res = CountMemReplaceMenuExResource(res, wordcount, newMenu)) == 0) + return NULL; + } + else if (menuId != 0) + { + ReplaceStr((LPCWSTR)res, newMenu, wordcount, &m_bTranslatedMenuStrings, &m_bDefaultMenuStrings); + res += wcslen((LPCWSTR)res) + 1; + } + else + { + if (newMenu) + wcscpy((wchar_t *)&newMenu[(*wordcount)], (LPCWSTR)res); + (*wordcount) += wcslen((LPCWSTR)res) + 1; + res += wcslen((LPCWSTR)res) + 1; + } + // Align to DWORD + res += ((((WORD)res + 3) & ~3) - (WORD)res)/sizeof(WORD); + if ((*wordcount) & 0x01) + (*wordcount)++; + } while (!(bResInfo & 0x80)); + return res; +} + +BOOL CResModule::ExtractAccelerator(UINT nID) +{ + HRSRC hrsrc = FindResource(m_hResDll, MAKEINTRESOURCE(nID), RT_ACCELERATOR); + HGLOBAL hglAccTable; + WORD fFlags, wAnsi, wID; + const WORD* p; + bool bEnd(false); + + if (!hrsrc) + MYERROR; + + hglAccTable = LoadResource(m_hResDll, hrsrc); + + if (!hglAccTable) + goto DONE_ERROR; + + p = (const WORD*)LockResource(hglAccTable); + + if (p == NULL) + MYERROR; + + /* + struct ACCELTABLEENTRY + { + WORD fFlags; FVIRTKEY, FSHIFT, FCONTROL, FALT, 0x80 - Last in a table + WORD wAnsi; ANSI character + WORD wId; Keyboard accelerator passed to windows + WORD padding; # bytes added to ensure aligned to DWORD boundary + }; + */ + + do + { + fFlags = GET_WORD(p); + p++; + wAnsi = GET_WORD(p); + p++; + wID = GET_WORD(p); + p++; + p++; // Skip over padding + + if ((fFlags & 0x80) == 0x80) + { // 0x80 + bEnd = true; + } + + if ((wAnsi < 0x30) || + (wAnsi > 0x5A) || + (wAnsi >= 0x3A && wAnsi <= 0x40)) + continue; + + TCHAR * pBuf = new TCHAR[1024]; + SecureZeroMemory(pBuf, 1024 * sizeof(TCHAR)); + + // include the menu ID in the msgid to make sure that 'duplicate' + // accelerator keys are listed in the po-file. + // without this, we would get entries like this: + //#. Accelerator Entry for Menu ID:32809; '&Filter' + //#. Accelerator Entry for Menu ID:57636; '&Find' + //#: Corresponding Menu ID:32771; '&Find' + //msgid "V C +F" + //msgstr "" + // + // Since "filter" and "find" are most likely translated to words starting + // with different letters, we need to have a separate accelerator entry + // for each of those + _stprintf(pBuf, _T("ID:%d:"), wID); + + // EXACTLY 5 characters long "ACS+X" + // V = Virtual key (or blank if not used) + // A = Alt key (or blank if not used) + // C = Ctrl key (or blank if not used) + // S = Shift key (or blank if not used) + // X = upper case character + // e.g. "V CS+Q" == Ctrl + Shift + 'Q' + if ((fFlags & FVIRTKEY) == FVIRTKEY) // 0x01 + _tcscat(pBuf, _T("V")); + else + _tcscat(pBuf, _T(" ")); + + if ((fFlags & FALT) == FALT) // 0x10 + _tcscat(pBuf, _T("A")); + else + _tcscat(pBuf, _T(" ")); + + if ((fFlags & FCONTROL) == FCONTROL) // 0x08 + _tcscat(pBuf, _T("C")); + else + _tcscat(pBuf, _T(" ")); + + if ((fFlags & FSHIFT) == FSHIFT) // 0x04 + _tcscat(pBuf, _T("S")); + else + _tcscat(pBuf, _T(" ")); + + _stprintf(pBuf, _T("%s+%c"), pBuf, wAnsi); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY AKey_entry = m_StringEntries[wstr]; + + TCHAR szTempBuf[1024]; + SecureZeroMemory(szTempBuf, 1024 * sizeof(TCHAR)); + std::wstring wmenu = _T(""); + pME_iter = m_MenuEntries.find(wID); + if (pME_iter != m_MenuEntries.end()) + { + wmenu = pME_iter->second.msgstr; + } + _stprintf(szTempBuf, _T("#. Accelerator Entry for Menu ID:%d; '%s'"), wID, wmenu.c_str()); + AKey_entry.automaticcomments.push_back(std::wstring(szTempBuf)); + + m_StringEntries[wstr] = AKey_entry; + delete [] pBuf; + } while (!bEnd); + + UnlockResource(hglAccTable); + FreeResource(hglAccTable); + return TRUE; + +DONE_ERROR: + UnlockResource(hglAccTable); + FreeResource(hglAccTable); + MYERROR; +} + +BOOL CResModule::ReplaceAccelerator(UINT nID, WORD wLanguage) +{ + LPACCEL lpaccelNew; // pointer to new accelerator table + HACCEL haccelOld; // handle to old accelerator table + int cAccelerators; // number of accelerators in table + HGLOBAL hglAccTableNew; + const WORD* p; + int i; + + haccelOld = LoadAccelerators(m_hResDll, MAKEINTRESOURCE(nID)); + + if (haccelOld == NULL) + MYERROR; + + cAccelerators = CopyAcceleratorTable(haccelOld, NULL, 0); + + lpaccelNew = (LPACCEL) LocalAlloc(LPTR, cAccelerators * sizeof(ACCEL)); + + if (lpaccelNew == NULL) + MYERROR; + + CopyAcceleratorTable(haccelOld, lpaccelNew, cAccelerators); + + // Find the accelerator that the user modified + // and change its flags and virtual-key code + // as appropriate. + + BYTE xfVirt; + WORD xkey; + for (i = 0; i < cAccelerators; i++) + { + m_bDefaultAcceleratorStrings++; + if ((lpaccelNew[i].key < 0x30) || + (lpaccelNew[i].key > 0x5A) || + (lpaccelNew[i].key >= 0x3A && lpaccelNew[i].key <= 0x40)) + continue; + + TCHAR * pBuf = new TCHAR[1024]; + SecureZeroMemory(pBuf, 1024 * sizeof(TCHAR)); + + _stprintf(pBuf, _T("ID:%d:"), lpaccelNew[i].cmd); + + // get original key combination + if ((lpaccelNew[i].fVirt & FVIRTKEY) == FVIRTKEY) // 0x01 + _tcscat(pBuf, _T("V")); + else + _tcscat(pBuf, _T(" ")); + + if ((lpaccelNew[i].fVirt & FALT) == FALT) // 0x10 + _tcscat(pBuf, _T("A")); + else + _tcscat(pBuf, _T(" ")); + + if ((lpaccelNew[i].fVirt & FCONTROL) == FCONTROL) // 0x08 + _tcscat(pBuf, _T("C")); + else + _tcscat(pBuf, _T(" ")); + + if ((lpaccelNew[i].fVirt & FSHIFT) == FSHIFT) // 0x04 + _tcscat(pBuf, _T("S")); + else + _tcscat(pBuf, _T(" ")); + + _stprintf(pBuf, _T("%s+%c"), pBuf, lpaccelNew[i].key); + + // Is it there? + std::map::iterator pAK_iter = m_StringEntries.find(pBuf); + if (pAK_iter != m_StringEntries.end()) + { + m_bTranslatedAcceleratorStrings++; + xfVirt = 0; + xkey = 0; + std::wstring wtemp = pAK_iter->second.msgstr; + wtemp = wtemp.substr(wtemp.find_last_of(':')+1); + if (wtemp.size() != 6) + continue; + if (wtemp.compare(0, 1, _T("V")) == 0) + xfVirt |= FVIRTKEY; + else if (wtemp.compare(0, 1, _T(" ")) != 0) + continue; // not a space - user must have made a mistake when translating + if (wtemp.compare(1, 1, _T("A")) == 0) + xfVirt |= FALT; + else if (wtemp.compare(1, 1, _T(" ")) != 0) + continue; // not a space - user must have made a mistake when translating + if (wtemp.compare(2, 1, _T("C")) == 0) + xfVirt |= FCONTROL; + else if (wtemp.compare(2, 1, _T(" ")) != 0) + continue; // not a space - user must have made a mistake when translating + if (wtemp.compare(3, 1, _T("S")) == 0) + xfVirt |= FSHIFT; + else if (wtemp.compare(3, 1, _T(" ")) != 0) + continue; // not a space - user must have made a mistake when translating + if (wtemp.compare(4, 1, _T("+")) == 0) + { + _stscanf(wtemp.substr(5, 1).c_str(), _T("%c"), &xkey); + lpaccelNew[i].fVirt = xfVirt; + lpaccelNew[i].key = xkey; + } + } + } + + // Create the new accelerator table + hglAccTableNew = LocalAlloc(LPTR, cAccelerators * 4 * sizeof(WORD)); + p = (WORD *)hglAccTableNew; + lpaccelNew[cAccelerators-1].fVirt |= 0x80; + for (i = 0; i < cAccelerators; i++) + { + memcpy((void *)p, &lpaccelNew[i].fVirt, 1); + p++; + memcpy((void *)p, &lpaccelNew[i].key, sizeof(WORD)); + p++; + memcpy((void *)p, &lpaccelNew[i].cmd, sizeof(WORD)); + p++; + p++; + } + + if (!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, MAKEINTRESOURCE(nID), + (m_wTargetLang ? m_wTargetLang : wLanguage), hglAccTableNew /* haccelNew*/, cAccelerators * 4 * sizeof(WORD))) + { + goto DONE_ERROR; + } + + if ((m_wTargetLang)&&(!UpdateResource(m_hUpdateRes, RT_ACCELERATOR, MAKEINTRESOURCE(nID), wLanguage, NULL, 0))) + { + goto DONE_ERROR; + } + + LocalFree(lpaccelNew); + return TRUE; + +DONE_ERROR: + LocalFree(lpaccelNew); + MYERROR; +} + +BOOL CResModule::ExtractDialog(UINT nID) +{ + const WORD* lpDlg; + const WORD* lpDlgItem; + DIALOGINFO dlg; + DLGITEMINFO dlgItem; + WORD bNumControls; + HRSRC hrsrc; + HGLOBAL hGlblDlgTemplate; + + hrsrc = FindResource(m_hResDll, MAKEINTRESOURCE(nID), RT_DIALOG); + + if (hrsrc == NULL) + MYERROR; + + hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc); + if (hGlblDlgTemplate == NULL) + MYERROR; + + lpDlg = (const WORD*) LockResource(hGlblDlgTemplate); + + if (lpDlg == NULL) + MYERROR; + + lpDlgItem = (const WORD*) GetDialogInfo(lpDlg, &dlg); + bNumControls = dlg.nbItems; + + if (dlg.caption) + { + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + _tcscpy(pBuf, dlg.caption); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + entry.resourceIDs.insert(nID); + + m_StringEntries[wstr] = entry; + delete [] pBuf; + } + + while (bNumControls-- != 0) + { + TCHAR szTitle[500]; + SecureZeroMemory(szTitle, sizeof(szTitle)); + BOOL bCode; + + lpDlgItem = GetControlInfo((WORD *) lpDlgItem, &dlgItem, dlg.dialogEx, &bCode); + + if (bCode == FALSE) + _tcscpy(szTitle, dlgItem.windowName); + + if (_tcslen(szTitle) > 0) + { + CUtils::StringExtend(szTitle); + + std::wstring wstr = std::wstring(szTitle); + RESOURCEENTRY entry = m_StringEntries[wstr]; + entry.resourceIDs.insert(dlgItem.id); + + m_StringEntries[wstr] = entry; + } + } + + UnlockResource(hGlblDlgTemplate); + FreeResource(hGlblDlgTemplate); + return (TRUE); +} + +BOOL CResModule::ReplaceDialog(UINT nID, WORD wLanguage) +{ + const WORD* lpDlg; + HRSRC hrsrc; + HGLOBAL hGlblDlgTemplate; + + hrsrc = FindResourceEx(m_hResDll, RT_DIALOG, MAKEINTRESOURCE(nID), wLanguage); + + if (hrsrc == NULL) + MYERROR; + + hGlblDlgTemplate = LoadResource(m_hResDll, hrsrc); + + if (hGlblDlgTemplate == NULL) + MYERROR; + + lpDlg = (WORD *) LockResource(hGlblDlgTemplate); + + if (lpDlg == NULL) + MYERROR; + + size_t nMem = 0; + const WORD * p = lpDlg; + if (!CountMemReplaceDialogResource(p, &nMem, NULL)) + goto DONE_ERROR; + WORD * newDialog = new WORD[nMem + (nMem % 2)]; + SecureZeroMemory(newDialog, (nMem + (nMem % 2))*2); + + size_t index = 0; + if (!CountMemReplaceDialogResource(lpDlg, &index, newDialog)) + { + delete [] newDialog; + goto DONE_ERROR; + } + + if (!UpdateResource(m_hUpdateRes, RT_DIALOG, MAKEINTRESOURCE(nID), (m_wTargetLang ? m_wTargetLang : wLanguage), newDialog, (DWORD)(nMem + (nMem % 2))*2)) + { + delete [] newDialog; + goto DONE_ERROR; + } + + if ((m_wTargetLang)&&(!UpdateResource(m_hUpdateRes, RT_DIALOG, MAKEINTRESOURCE(nID), wLanguage, NULL, 0))) + { + delete [] newDialog; + goto DONE_ERROR; + } + + delete [] newDialog; + UnlockResource(hGlblDlgTemplate); + FreeResource(hGlblDlgTemplate); + return TRUE; + +DONE_ERROR: + UnlockResource(hGlblDlgTemplate); + FreeResource(hGlblDlgTemplate); + MYERROR; +} + +const WORD* CResModule::GetDialogInfo(const WORD * pTemplate, LPDIALOGINFO lpDlgInfo) +{ + const WORD* p = (const WORD *)pTemplate; + + lpDlgInfo->style = GET_DWORD(p); + p += 2; + + if (lpDlgInfo->style == 0xffff0001) // DIALOGEX resource + { + lpDlgInfo->dialogEx = TRUE; + lpDlgInfo->helpId = GET_DWORD(p); + p += 2; + lpDlgInfo->exStyle = GET_DWORD(p); + p += 2; + lpDlgInfo->style = GET_DWORD(p); + p += 2; + } + else + { + lpDlgInfo->dialogEx = FALSE; + lpDlgInfo->helpId = 0; + lpDlgInfo->exStyle = GET_DWORD(p); + p += 2; + } + + lpDlgInfo->nbItems = GET_WORD(p); + p++; + + lpDlgInfo->x = GET_WORD(p); + p++; + + lpDlgInfo->y = GET_WORD(p); + p++; + + lpDlgInfo->cx = GET_WORD(p); + p++; + + lpDlgInfo->cy = GET_WORD(p); + p++; + + // Get the menu name + + switch (GET_WORD(p)) + { + case 0x0000: + lpDlgInfo->menuName = NULL; + p++; + break; + case 0xffff: + lpDlgInfo->menuName = (LPCTSTR) (WORD) GET_WORD(p + 1); + p += 2; + break; + default: + lpDlgInfo->menuName = (LPCTSTR) p; + p += wcslen((LPCWSTR) p) + 1; + break; + } + + // Get the class name + + switch (GET_WORD(p)) + { + case 0x0000: + lpDlgInfo->className = (LPCTSTR)MAKEINTATOM(32770); + p++; + break; + case 0xffff: + lpDlgInfo->className = (LPCTSTR) (WORD) GET_WORD(p + 1); + p += 2; + break; + default: + lpDlgInfo->className = (LPCTSTR) p; + p += wcslen((LPCTSTR)p) + 1; + break; + } + + // Get the window caption + + lpDlgInfo->caption = (LPCTSTR)p; + p += wcslen((LPCWSTR) p) + 1; + + // Get the font name + + if (lpDlgInfo->style & DS_SETFONT) + { + lpDlgInfo->pointSize = GET_WORD(p); + p++; + + if (lpDlgInfo->dialogEx) + { + lpDlgInfo->weight = GET_WORD(p); + p++; + lpDlgInfo->italic = LOBYTE(GET_WORD(p)); + p++; + } + else + { + lpDlgInfo->weight = FW_DONTCARE; + lpDlgInfo->italic = FALSE; + } + + lpDlgInfo->faceName = (LPCTSTR)p; + p += wcslen((LPCWSTR) p) + 1; + } + // First control is on DWORD boundary + return (const WORD *) ((((long)p) + 3) & ~3); +} + +const WORD* CResModule::GetControlInfo(const WORD* p, LPDLGITEMINFO lpDlgItemInfo, BOOL dialogEx, LPBOOL bIsID) +{ + if (dialogEx) + { + lpDlgItemInfo->helpId = GET_DWORD(p); + p += 2; + lpDlgItemInfo->exStyle = GET_DWORD(p); + p += 2; + lpDlgItemInfo->style = GET_DWORD(p); + p += 2; + } + else + { + lpDlgItemInfo->helpId = 0; + lpDlgItemInfo->style = GET_DWORD(p); + p += 2; + lpDlgItemInfo->exStyle = GET_DWORD(p); + p += 2; + } + + lpDlgItemInfo->x = GET_WORD(p); + p++; + + lpDlgItemInfo->y = GET_WORD(p); + p++; + + lpDlgItemInfo->cx = GET_WORD(p); + p++; + + lpDlgItemInfo->cy = GET_WORD(p); + p++; + + if (dialogEx) + { + // ID is a DWORD for DIALOGEX + lpDlgItemInfo->id = (WORD) GET_DWORD(p); + p += 2; + } + else + { + lpDlgItemInfo->id = GET_WORD(p); + p++; + } + + if (GET_WORD(p) == 0xffff) + { + GET_WORD(p + 1); + + p += 2; + } + else + { + lpDlgItemInfo->className = (LPCTSTR) p; + p += wcslen((LPCWSTR) p) + 1; + } + + if (GET_WORD(p) == 0xffff) // an integer ID? + { + *bIsID = TRUE; + lpDlgItemInfo->windowName = (LPCTSTR) (DWORD) GET_WORD(p + 1); + p += 2; + } + else + { + *bIsID = FALSE; + lpDlgItemInfo->windowName = (LPCTSTR) p; + p += wcslen((LPCWSTR) p) + 1; + } + + if (GET_WORD(p)) + { + lpDlgItemInfo->data = (LPVOID) (p + 1); + p += GET_WORD(p) / sizeof(WORD); + } + else + lpDlgItemInfo->data = NULL; + + p++; + // Next control is on DWORD boundary + return (const WORD *)((((long)p) + 3) & ~3); +} + +const WORD * CResModule::CountMemReplaceDialogResource(const WORD * res, size_t * wordcount, WORD * newDialog) +{ + BOOL bEx = FALSE; + DWORD style = GET_DWORD(res); + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + res += 2; + (*wordcount) += 2; + } + + if (style == 0xffff0001) // DIALOGEX resource + { + bEx = TRUE; + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); //help id + newDialog[(*wordcount)++] = GET_WORD(res++); //help id + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + style = GET_DWORD(res); + newDialog[(*wordcount)++] = GET_WORD(res++); //style + newDialog[(*wordcount)++] = GET_WORD(res++); //style + } + else + { + res += 4; + style = GET_DWORD(res); + res += 2; + (*wordcount) += 6; + } + } + else + { + bEx = FALSE; + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + //style = GET_DWORD(res); + //newDialog[(*wordcount)++] = GET_WORD(res++); //style + //newDialog[(*wordcount)++] = GET_WORD(res++); //style + } + else + { + res += 2; + (*wordcount) += 2; + } + } + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); + WORD nbItems = GET_WORD(res); + (*wordcount)++; + res++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //x + (*wordcount)++; + res++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //y + (*wordcount)++; + res++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //cx + (*wordcount)++; + res++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //cy + (*wordcount)++; + res++; + + // Get the menu name + + switch (GET_WORD(res)) + { + case 0x0000: + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); + (*wordcount)++; + res++; + break; + case 0xffff: + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + (*wordcount) += 2; + res += 2; + } + break; + default: + if (newDialog) + { + wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res); + } + (*wordcount) += wcslen((LPCWSTR) res) + 1; + res += wcslen((LPCWSTR) res) + 1; + break; + } + + // Get the class name + + switch (GET_WORD(res)) + { + case 0x0000: + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); + (*wordcount)++; + res++; + break; + case 0xffff: + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + (*wordcount) += 2; + res += 2; + } + break; + default: + if (newDialog) + { + wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res); + } + (*wordcount) += wcslen((LPCWSTR) res) + 1; + res += wcslen((LPCWSTR) res) + 1; + break; + } + + // Get the window caption + + ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings); + res += wcslen((LPCWSTR)res) + 1; + + // Get the font name + + if (style & DS_SETFONT) + { + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); + res++; + (*wordcount)++; + + if (bEx) + { + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + res += 2; + (*wordcount) += 2; + } + } + + if (newDialog) + wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res); + (*wordcount) += wcslen((LPCWSTR)res) + 1; + res += wcslen((LPCWSTR)res) + 1; + } + // First control is on DWORD boundary + while ((*wordcount)%2) + (*wordcount)++; + while ((ULONG)res % 4) + res++; + + while (nbItems--) + { + res = ReplaceControlInfo(res, wordcount, newDialog, bEx); + } + return res; +} + +const WORD* CResModule::ReplaceControlInfo(const WORD * res, size_t * wordcount, WORD * newDialog, BOOL bEx) +{ + if (bEx) + { + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); //helpid + newDialog[(*wordcount)++] = GET_WORD(res++); //helpid + } + else + { + res += 2; + (*wordcount) += 2; + } + } + if (newDialog) + { + LONG * exStyle = (LONG*)&newDialog[(*wordcount)]; + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + newDialog[(*wordcount)++] = GET_WORD(res++); //exStyle + if (m_bRTL) + *exStyle |= WS_EX_RTLREADING; + } + else + { + res += 2; + (*wordcount) += 2; + } + + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); //style + newDialog[(*wordcount)++] = GET_WORD(res++); //style + } + else + { + res += 2; + (*wordcount) += 2; + } + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //x + res++; + (*wordcount)++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //y + res++; + (*wordcount)++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //cx + res++; + (*wordcount)++; + + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); //cy + res++; + (*wordcount)++; + + if (bEx) + { + // ID is a DWORD for DIALOGEX + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + res += 2; + (*wordcount) += 2; + } + } + else + { + if (newDialog) + newDialog[(*wordcount)] = GET_WORD(res); + res++; + (*wordcount)++; + } + + if (GET_WORD(res) == 0xffff) //classID + { + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + res += 2; + (*wordcount) += 2; + } + } + else + { + if (newDialog) + wcscpy((LPWSTR)&newDialog[(*wordcount)], (LPCWSTR)res); + (*wordcount) += wcslen((LPCWSTR) res) + 1; + res += wcslen((LPCWSTR) res) + 1; + } + + if (GET_WORD(res) == 0xffff) // an integer ID? + { + if (newDialog) + { + newDialog[(*wordcount)++] = GET_WORD(res++); + newDialog[(*wordcount)++] = GET_WORD(res++); + } + else + { + res += 2; + (*wordcount) += 2; + } + } + else + { + ReplaceStr((LPCWSTR)res, newDialog, wordcount, &m_bTranslatedDialogStrings, &m_bDefaultDialogStrings); + res += wcslen((LPCWSTR)res) + 1; + } + + if (newDialog) + memcpy(&newDialog[(*wordcount)], res, (GET_WORD(res)+1)*sizeof(WORD)); + (*wordcount) += (GET_WORD(res)+1); + res += (GET_WORD(res)+1); + // Next control is on DWORD boundary + while ((*wordcount) % 2) + (*wordcount)++; + return (const WORD *)((((long)res) + 3) & ~3); +} + +BOOL CALLBACK CResModule::EnumResNameCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) +{ + CResModule* lpResModule = (CResModule*)lParam; + + if (lpszType == RT_STRING) + { + if (IS_INTRESOURCE(lpszName)) + { + if (!lpResModule->ExtractString(LOWORD(lpszName))) + return FALSE; + } + } + else if (lpszType == RT_MENU) + { + if (IS_INTRESOURCE(lpszName)) + { + if (!lpResModule->ExtractMenu(LOWORD(lpszName))) + return FALSE; + } + } + else if (lpszType == RT_DIALOG) + { + if (IS_INTRESOURCE(lpszName)) + { + if (!lpResModule->ExtractDialog(LOWORD(lpszName))) + return FALSE; + } + } + else if (lpszType == RT_ACCELERATOR) + { + if (IS_INTRESOURCE(lpszName)) + { + if (!lpResModule->ExtractAccelerator(LOWORD(lpszName))) + return FALSE; + } + } + + return TRUE; +} + +#pragma warning(push) +#pragma warning(disable: 4189) +BOOL CALLBACK CResModule::EnumResNameWriteCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) +{ + CResModule* lpResModule = (CResModule*)lParam; + return EnumResourceLanguages(hModule, lpszType, lpszName, (ENUMRESLANGPROC)&lpResModule->EnumResWriteLangCallback, lParam); +} +#pragma warning(pop) + +BOOL CALLBACK CResModule::EnumResWriteLangCallback(HMODULE /*hModule*/, LPCTSTR lpszType, LPTSTR lpszName, WORD wLanguage, LONG_PTR lParam) +{ + BOOL bRes = FALSE; + CResModule* lpResModule = (CResModule*)lParam; + + + int count = 0; + do + { + lpResModule->m_hUpdateRes = BeginUpdateResource(lpResModule->sDestFile.c_str(), FALSE); + if (lpResModule->m_hUpdateRes == NULL) + Sleep(100); + count++; + } while ((lpResModule->m_hUpdateRes == NULL)&&(count < 5)); + + if (lpszType == RT_STRING) + { + if (IS_INTRESOURCE(lpszName)) + { + bRes = lpResModule->ReplaceString(LOWORD(lpszName), wLanguage); + } + } + else if (lpszType == RT_MENU) + { + if (IS_INTRESOURCE(lpszName)) + { + bRes = lpResModule->ReplaceMenu(LOWORD(lpszName), wLanguage); + } + } + else if (lpszType == RT_DIALOG) + { + if (IS_INTRESOURCE(lpszName)) + { + bRes = lpResModule->ReplaceDialog(LOWORD(lpszName), wLanguage); + } + } + else if (lpszType == RT_ACCELERATOR) + { + if (IS_INTRESOURCE(lpszName)) + { + bRes = lpResModule->ReplaceAccelerator(LOWORD(lpszName), wLanguage); + } + } + + if (!EndUpdateResource(lpResModule->m_hUpdateRes, !bRes)) + MYERROR; + return bRes; + +} + +void CResModule::ReplaceStr(LPCWSTR src, WORD * dest, size_t * count, int * translated, int * def) +{ + TCHAR * pBuf = new TCHAR[MAX_STRING_LENGTH]; + SecureZeroMemory(pBuf, MAX_STRING_LENGTH * sizeof(TCHAR)); + wcscpy(pBuf, src); + CUtils::StringExtend(pBuf); + + std::wstring wstr = std::wstring(pBuf); + RESOURCEENTRY entry = m_StringEntries[wstr]; + if (entry.msgstr.size()) + { + wcscpy(pBuf, entry.msgstr.c_str()); + CUtils::StringCollapse(pBuf); + if (dest) + wcscpy((wchar_t *)&dest[(*count)], pBuf); + (*count) += wcslen(pBuf)+1; + (*translated)++; + } + else + { + if (dest) + wcscpy((wchar_t *)&dest[(*count)], src); + (*count) += wcslen(src) + 1; + if (wcslen(src)) + (*def)++; + } + delete [] pBuf; +} diff --git a/src/ResText/ResModule.h b/src/ResText/ResModule.h new file mode 100644 index 0000000..918475f --- /dev/null +++ b/src/ResText/ResModule.h @@ -0,0 +1,137 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2007 - TortoiseSVN + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#pragma once +#include +#include +#include +#include "POFile.h" + +#define GET_WORD(ptr) (*(WORD *)(ptr)) +#define GET_DWORD(ptr) (*(DWORD *)(ptr)) +#define ALIGN_DWORD(type, p) ((type)(((DWORD)p + 3) & ~3)) + +#define MAX_STRING_LENGTH (32*1024) + +// DIALOG CONTROL INFORMATION +typedef struct tagDlgItemInfo +{ + DWORD style; + DWORD exStyle; + DWORD helpId; + short x; + short y; + short cx; + short cy; + WORD id; + LPCTSTR className; + LPCTSTR windowName; + LPVOID data; +} DLGITEMINFO, * LPDLGITEMINFO; + +// DIALOG TEMPLATE +typedef struct tagDialogInfo +{ + DWORD style; + DWORD exStyle; + DWORD helpId; + WORD nbItems; + short x; + short y; + short cx; + short cy; + LPCTSTR menuName; + LPCTSTR className; + LPCTSTR caption; + WORD pointSize; + WORD weight; + BOOL italic; + LPCTSTR faceName; + BOOL dialogEx; +} DIALOGINFO, * LPDIALOGINFO; +// MENU resource +typedef struct tagMenuEntry +{ + WORD wID; + std::wstring reference; + std::wstring msgstr; +} MENUENTRY, * LPMENUENTRY; + +/** + * \ingroup ResText + * Class to handle a resource module (*.exe or *.dll file). + * + * Provides methods to extract and apply resource strings. + */ +class CResModule +{ +public: + CResModule(void); + ~CResModule(void); + + BOOL ExtractResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszPOFilePath, BOOL bNoUpdate); + BOOL ExtractResources(std::vector filelist, LPCTSTR lpszPOFilePath, BOOL bNoUpdate); + BOOL CreateTranslatedResources(LPCTSTR lpszSrcLangDllPath, LPCTSTR lpszDestLangDllPath, LPCTSTR lpszPOFilePath); + void SetQuiet(BOOL bQuiet = TRUE) {m_bQuiet = bQuiet; m_StringEntries.SetQuiet(bQuiet);} + void SetLanguage(WORD wLangID) {m_wTargetLang = wLangID;} + void SetRTL(bool bRTL = true) {m_bRTL = bRTL;} + +private: + static BOOL CALLBACK EnumResNameCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam); + static BOOL CALLBACK EnumResNameWriteCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam); + static BOOL CALLBACK EnumResWriteLangCallback(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, WORD wLanguage, LONG_PTR lParam); + BOOL ExtractString(UINT nID); + BOOL ExtractDialog(UINT nID); + BOOL ExtractMenu(UINT nID); + BOOL ReplaceString(UINT nID, WORD wLanguage); + BOOL ReplaceDialog(UINT nID, WORD wLanguage); + BOOL ReplaceMenu(UINT nID, WORD wLanguage); + BOOL ExtractAccelerator(UINT nID); + BOOL ReplaceAccelerator(UINT nID, WORD wLanguage); + + const WORD* ParseMenuResource(const WORD * res); + const WORD* CountMemReplaceMenuResource(const WORD * res, size_t * wordcount, WORD * newMenu); + const WORD* ParseMenuExResource(const WORD * res); + const WORD* CountMemReplaceMenuExResource(const WORD * res, size_t * wordcount, WORD * newMenu); + const WORD* GetControlInfo(const WORD* p, LPDLGITEMINFO lpDlgItemInfo, BOOL dialogEx, LPBOOL bIsID); + const WORD* GetDialogInfo(const WORD * pTemplate, LPDIALOGINFO lpDlgInfo); + const WORD* CountMemReplaceDialogResource(const WORD * res, size_t * wordcount, WORD * newMenu); + const WORD* ReplaceControlInfo(const WORD * res, size_t * wordcount, WORD * newDialog, BOOL bEx); + + void ReplaceStr(LPCWSTR src, WORD * dest, size_t * count, int * translated, int * def); + + HMODULE m_hResDll; + HANDLE m_hUpdateRes; + CPOFile m_StringEntries; + std::map m_MenuEntries; + std::map::iterator pME_iter; + std::wstring sDestFile; + BOOL m_bQuiet; + + bool m_bRTL; + + int m_bTranslatedStrings; + int m_bDefaultStrings; + int m_bTranslatedDialogStrings; + int m_bDefaultDialogStrings; + int m_bTranslatedMenuStrings; + int m_bDefaultMenuStrings; + int m_bTranslatedAcceleratorStrings; + int m_bDefaultAcceleratorStrings; + + WORD m_wTargetLang; +}; diff --git a/src/ResText/ResText.cpp b/src/ResText/ResText.cpp new file mode 100644 index 0000000..b1ebb93 --- /dev/null +++ b/src/ResText/ResText.cpp @@ -0,0 +1,145 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2006 - Stefan Kueng + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#include "stdafx.h" +#include "ResModule.h" + +#include +#include +#include "shlwapi.h" +#pragma comment(lib, "shlwapi.lib") + +typedef std::basic_string wstring; +#ifdef UNICODE +# define stdstring wstring +#else +# define stdstring std::string +#endif + +int _tmain(int argc, _TCHAR* argv[]) +{ + bool bShowHelp = true; + bool bQuiet = false; + bool bNoUpdate = false; + bool bRTL = false; + //parse the command line + std::vector arguments; + std::vector switches; + for (int i=1; i::iterator I = switches.begin(); I != switches.end(); ++I) + { + if (_tcscmp(I->c_str(), _T("?"))==0) + bShowHelp = true; + if (_tcscmp(I->c_str(), _T("help"))==0) + bShowHelp = true; + if (_tcscmp(I->c_str(), _T("quiet"))==0) + bQuiet = true; + if (_tcscmp(I->c_str(), _T("noupdate"))==0) + bNoUpdate = true; + if (_tcscmp(I->c_str(), _T("rtl"))==0) + bRTL = true; + } + std::vector::iterator arg = arguments.begin(); + + if (arg != arguments.end()) + { + if (_tcscmp(arg->c_str(), _T("extract"))==0) + { + stdstring sDllFile; + stdstring sPoFile; + ++arg; + + std::vector filelist = arguments; + filelist.erase(filelist.begin()); + sPoFile = stdstring((--filelist.end())->c_str()); + filelist.erase(--filelist.end()); + + CResModule module; + module.SetQuiet(bQuiet); + if (!module.ExtractResources(filelist, sPoFile.c_str(), bNoUpdate)) + return -1; + bShowHelp = false; + } + else if (_tcscmp(arg->c_str(), _T("apply"))==0) + { + stdstring sSrcDllFile; + stdstring sDstDllFile; + stdstring sPoFile; + WORD wLang = 0; + ++arg; + if (!PathFileExists(arg->c_str())) + { + _ftprintf(stderr, _T("the resource dll <%s> does not exist!\n"), arg->c_str()); + return -1; + } + sSrcDllFile = stdstring(arg->c_str()); + ++arg; + sDstDllFile = stdstring(arg->c_str()); + ++arg; + if (!PathFileExists(arg->c_str())) + { + _ftprintf(stderr, _T("the po-file <%s> does not exist!\n"), arg->c_str()); + return -1; + } + sPoFile = stdstring(arg->c_str()); + ++arg; + if (arg != arguments.end()) + { + wLang = (WORD)_ttoi(arg->c_str()); + } + CResModule module; + module.SetQuiet(bQuiet); + module.SetLanguage(wLang); + module.SetRTL(bRTL); + if (!module.CreateTranslatedResources(sSrcDllFile.c_str(), sDstDllFile.c_str(), sPoFile.c_str())) + return -1; + bShowHelp = false; + } + } + + if (bShowHelp) + { + _ftprintf(stdout, _T("usage:\n")); + _ftprintf(stdout, _T("\n")); + _ftprintf(stdout, _T("ResText extract [ ...] [-quiet] [-noupdate]\n")); + _ftprintf(stdout, _T("Extracts all strings from the resource dll and writes them to the po-file\n")); + _ftprintf(stdout, _T("-quiet: don't print progress messages\n")); + _ftprintf(stdout, _T("-noupdate: overwrite the po-file\n")); + _ftprintf(stdout, _T("\n")); + _ftprintf(stdout, _T("ResText apply [langID] [-quiet][-rtl]\n")); + _ftprintf(stdout, _T("Replaces all strings in the dst resource.dll with the po-file translations\n")); + _ftprintf(stdout, _T("-quiet: don't print progress messages\n")); + _ftprintf(stdout, _T("-rtl : change the controls to RTL reading\n")); + _ftprintf(stdout, _T("\n")); + } + + return 0; +} + diff --git a/src/ResText/ResText.vcproj b/src/ResText/ResText.vcproj new file mode 100644 index 0000000..50c8b55 --- /dev/null +++ b/src/ResText/ResText.vcproj @@ -0,0 +1,871 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ResText/Utils.cpp b/src/ResText/Utils.cpp new file mode 100644 index 0000000..5d27a40 --- /dev/null +++ b/src/ResText/Utils.cpp @@ -0,0 +1,152 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2006 - Stefan Kueng + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#include "StdAfx.h" +#include ".\utils.h" + +CUtils::CUtils(void) +{ +} + +CUtils::~CUtils(void) +{ +} + +void CUtils::StringExtend(LPTSTR str) +{ + TCHAR * cPos = str; + do + { + cPos = _tcschr(cPos, '\\'); + if (cPos) + { + memmove(cPos+1, cPos, _tcslen(cPos)*sizeof(TCHAR)); + *cPos = '\\'; + *(cPos+1) = '\\'; + cPos++; + cPos++; + } + } while (cPos != NULL); + cPos = str; + do + { + cPos = _tcschr(cPos, '\n'); + if (cPos) + { + memmove(cPos+1, cPos, _tcslen(cPos)*sizeof(TCHAR)); + *cPos = '\\'; + *(cPos+1) = 'n'; + } + } while (cPos != NULL); + cPos = str; + do + { + cPos = _tcschr(cPos, '\r'); + if (cPos) + { + memmove(cPos+1, cPos, _tcslen(cPos)*sizeof(TCHAR)); + *cPos = '\\'; + *(cPos+1) = 'r'; + } + } while (cPos != NULL); + cPos = str; + do + { + cPos = _tcschr(cPos, '\t'); + if (cPos) + { + memmove(cPos+1, cPos, _tcslen(cPos)*sizeof(TCHAR)); + *cPos = '\\'; + *(cPos+1) = 't'; + } + } while (cPos != NULL); + cPos = str; + do + { + cPos = _tcschr(cPos, '"'); + if (cPos) + { + memmove(cPos+1, cPos, _tcslen(cPos)*sizeof(TCHAR)); + *cPos = '\\'; + *(cPos+1) = '"'; + cPos++; + cPos++; + } + } while (cPos != NULL); +} + +void CUtils::StringCollapse(LPTSTR str) +{ + TCHAR * cPos = str; + do + { + cPos = _tcschr(cPos, '\\'); + if (cPos) + { + if (*(cPos+1) == 'n') + { + *cPos = '\n'; + memmove(cPos+1, cPos+2, (_tcslen(cPos+2)+1)*sizeof(TCHAR)); + } + else if (*(cPos+1) == 'r') + { + *cPos = '\r'; + memmove(cPos+1, cPos+2, (_tcslen(cPos+2)+1)*sizeof(TCHAR)); + } + else if (*(cPos+1) == 't') + { + *cPos = '\t'; + memmove(cPos+1, cPos+2, (_tcslen(cPos+2)+1)*sizeof(TCHAR)); + } + else if (*(cPos+1) == '"') + { + *cPos = '"'; + memmove(cPos+1, cPos+2, (_tcslen(cPos+2)+1)*sizeof(TCHAR)); + } + else if (*(cPos+1) == '\\') + { + *cPos = '\\'; + memmove(cPos+1, cPos+2, (_tcslen(cPos+2)+1)*sizeof(TCHAR)); + } + cPos++; + } + } while (cPos != NULL); +} + +void CUtils::Error() +{ + LPVOID lpMsgBuf; + if (!FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL )) + { + return; + } + + // Display the string. + _ftprintf(stderr, _T("%s\n"), (LPCTSTR)lpMsgBuf); + + // Free the buffer. + LocalFree( lpMsgBuf ); +} \ No newline at end of file diff --git a/src/ResText/Utils.h b/src/ResText/Utils.h new file mode 100644 index 0000000..f1a456b --- /dev/null +++ b/src/ResText/Utils.h @@ -0,0 +1,32 @@ +// TortoiseSVN - a Windows shell extension for easy version control + +// Copyright (C) 2003-2007 - TortoiseSVN + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +#pragma once + +/** + * \ingroup ResText + * static helper methods for ResText. + */ +class CUtils +{ +public: + CUtils(void); + ~CUtils(void); + static void StringExtend(LPTSTR str); + static void StringCollapse(LPTSTR str); + static void Error(); +}; diff --git a/src/ResText/codecvt.cpp b/src/ResText/codecvt.cpp new file mode 100644 index 0000000..a87f93b --- /dev/null +++ b/src/ResText/codecvt.cpp @@ -0,0 +1,137 @@ +#include "codecvt.h" + +using namespace std; + +ucs2_conversion::result +ucs2_conversion::do_in(mbstate_t&, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_limit, wchar_t*& to_next) const +{ + size_t max_input = (from_end - from) & ~1; + size_t max_output = (to_limit - to); + size_t count = min(max_input/2, max_output); + + result res = ok; + + from_next = from; + to_next = to; + for (;count--; from_next += 2, ++to_next) { + unsigned char c1 = *from_next, c2 = *(from_next + 1); + *to_next = c1 | c2 << 8; + } + if (to_next == to && from_next == from_end - 1) res = partial; + return res; +} + +ucs2_conversion::result +ucs2_conversion::do_out(mbstate_t&, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_limit, char*& to_next) const +{ + size_t max_input = (from_end - from); + size_t max_output = (to_limit - to) & ~1; + size_t count = min(max_input, max_output/2); + + from_next = from; + to_next = to; + for (;count--; ++from_next, to_next += 2) { + *(to_next + 0) = (char)(*from_next & 0xFF); + *(to_next + 1) = (char)(*from_next >> 8 & 0xFF); + } + return ok; +} + +typedef unsigned char uchar; + +inline unsigned char +take_6_bits(unsigned value, size_t right_position) +{ + return uchar((value >> right_position) & 63); +} + + + +inline size_t +most_signifant_bit_position(unsigned value) +{ + size_t result(0); + + size_t half = 16; + for(; half > 0; half >>= 1) { + if (1u << (result + half) <= value ) result += half; + } + return result + 1; + //return *lower_bound(range(0u, 31u), \x -> (1 << x <= value)); +} + + + +utf8_conversion::result +utf8_conversion::do_in(mbstate_t&, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_limit, wchar_t*& to_next) const +{ + from_next = from; + to_next = to; + + for(; to_next < to_limit && from_next < from_end; ++to_next) { + + if (uchar(*from_next) < 0x80) *to_next = uchar(*from_next++); + else { + + // 111zxxxx : z = 0 xxxx are data bits + size_t zero_bit_pos = most_signifant_bit_position(~*from_next); + size_t extra_bytes = 7 - zero_bit_pos; + + if (size_t(from_end - from_next) < extra_bytes + 1) + return partial; + + *to_next = uchar(*from_next++) & (wchar_t)((1 << (zero_bit_pos - 1)) - 1); + + for (;extra_bytes--; ++from_next) { + *to_next = *to_next << 6 | uchar(*from_next) & 63; + } + } + } + + return ok; +} + + +utf8_conversion::result +utf8_conversion::do_out(mbstate_t&, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_limit, char*& to_next) const +{ + from_next = from; + to_next = to; + + for (;from_next < from_end; ++from_next) { + + unsigned symbol = *from_next; + + if (symbol < 0x7F) { + if (to_next < to_limit) + *to_next++ = (unsigned char)symbol; + else + return ok; + } else { + + size_t msb_pos = most_signifant_bit_position(symbol); + size_t extra_bytes = msb_pos / 6; + + if (size_t(to_limit - to_next) >= extra_bytes + 1) { + + *to_next = uchar(0xFF80 >> extra_bytes); + *to_next++ |= take_6_bits(symbol, extra_bytes*6); + + for(;extra_bytes--;) + *to_next++ = 0x80 | take_6_bits(symbol, extra_bytes*6); + } + else + return ok; + } + } + return ok; +} + diff --git a/src/ResText/codecvt.h b/src/ResText/codecvt.h new file mode 100644 index 0000000..f190806 --- /dev/null +++ b/src/ResText/codecvt.h @@ -0,0 +1,64 @@ + +/** Several code conversions facets. + + E.g. this is how to convert UCS-2 to UTF-8 + + @code + wifstream ifs("input", ios_base::binary); + wofstream ofs("output", ios_base::binary); + + ifs.rdbuf()->pubimbue(locale(locale(), new ucs2_conversion())); + ofs.rbbuf()->pubimbue(locale(locale(), new utf8_conversion())); + ofs << ifs.rdbuf(); + @endcode + + @author Vladimir Prus + + @file +*/ + +#include +#pragma warning(push) +#pragma warning(disable: 4511) +#pragma warning(disable: 4512) + +/** Conversion facet that allows to use Unicode files in UCS-2 encoding */ +class ucs2_conversion +: public std::codecvt +{ +protected: + + result do_in(std::mbstate_t& state, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_limit, wchar_t*& to_next) const; + + result do_out(std::mbstate_t& state, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_limit, char*& to_next) const; + + bool do_always_noconv() const throw() { return false; } + int do_encoding() const throw() { return 2; } +}; + + +/** Conversion facet that allows to read Unicode files in UTF-8 encoding */ +class utf8_conversion +: public std::codecvt +{ +protected: + + result do_in(std::mbstate_t& state, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_limit, wchar_t*& to_next) const; + + result do_out(std::mbstate_t& state, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_limit, char*& to_next) const; + + bool do_always_noconv() const throw() { return false; } + int do_encoding() const throw() { return 2; } +}; +#pragma warning(pop) + + + diff --git a/src/ResText/stdafx.cpp b/src/ResText/stdafx.cpp new file mode 100644 index 0000000..b48f861 --- /dev/null +++ b/src/ResText/stdafx.cpp @@ -0,0 +1,5 @@ +// stdafx.cpp : source file that includes just the standard includes +// ResText.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/src/ResText/stdafx.h b/src/ResText/stdafx.h new file mode 100644 index 0000000..78c9065 --- /dev/null +++ b/src/ResText/stdafx.h @@ -0,0 +1,13 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#pragma warning(push) +#pragma warning(disable: 4702) // Unreachable code warnings in xtree +#include +#include +#include +#pragma warning(pop) -- 2.11.0