windows-nt/Source/XPSP1/NT/shell/themes/packthem/packthem.cpp
2020-09-26 16:20:57 +08:00

2052 lines
59 KiB
C++
Raw Permalink Blame History

//---------------------------------------------------------------------------
// PackThem.cpp - packs up theme files into a theme DLL
//---------------------------------------------------------------------------
#include "stdafx.h"
#include <uxthemep.h>
#include <utils.h>
#include "SimpStr.h"
#include "Scanner.h"
#include "shlwapip.h"
#include "parser.h"
#include "TmSchema.h"
#include "signing.h"
#include "localsign.h"
#include "ThemeLdr.h"
#include "TmUtils.h"
#include "StringTable.h"
HRESULT ParseTheme(LPCWSTR pszThemeName);
//---------------------------------------------------------------------------
struct FILEINFO
{
CWideString wsName;
BOOL fIniFile;
};
//---------------------------------------------------------------------------
#define MAX_COLORS 50
#define MAX_SIZES 20
#define TEMP_FILENAME_BASE L"$temp$"
#define kRESFILECHAR L'$'
//---------------------------------------------------------------------------
enum PACKFILETYPE
{
PACK_INIFILE,
PACK_IMAGEFILE,
PACK_NTLFILE,
PACK_OTHER
};
//---------------------------------------------------------------------------
CSimpleArray<FILEINFO> FileInfo;
CSimpleArray<CWideString> ColorSchemes;
CSimpleArray<CWideString> ColorDisplays;
CSimpleArray<CWideString> ColorToolTips;
CSimpleArray<int> MinDepths;
CSimpleArray<CWideString> SizeNames;
CSimpleArray<CWideString> SizeDisplays;
CSimpleArray<CWideString> SizeToolTips;
typedef struct
{
CWideString sName;
int iFirstIndex;
UINT cItems;
} sSubstTable;
CSimpleArray<sSubstTable> SubstNames;
CSimpleArray<CWideString> SubstIds;
CSimpleArray<CWideString> SubstValues;
CSimpleArray<CWideString> BaseResFileNames;
CSimpleArray<CWideString> ResFileNames;
CSimpleArray<CWideString> OrigFileNames;
CSimpleArray<CWideString> PropValuePairs;
//---------------------------------------------------------------------------
SHORT Combos[MAX_SIZES][MAX_COLORS];
int g_iMaxColor;
int g_iMaxSize;
int g_LineCount = 0;
int iTempBitmapNum = 1;
BOOL g_fQuietRun = FALSE; // don't show needless output
BOOL g_fKeepTempFiles = FALSE;
FILE *ConsoleFile = NULL;
WCHAR g_szInputDir[_MAX_PATH+1];
WCHAR g_szTempPath[_MAX_PATH+1];
WCHAR g_szBaseIniName[_MAX_PATH+1];
WCHAR g_szCurrentClass[_MAX_PATH+1];
//---------------------------------------------------------------------------
#define DOCPROPCNT (1+TMT_LAST_RCSTRING_NAME - TMT_FIRST_RCSTRING_NAME)
CWideString DocProperties[DOCPROPCNT];
//---------------------------------------------------------------------------
HRESULT ReportError(HRESULT hr, LPWSTR pszDefaultMsg)
{
WCHAR szErrorMsg[2*_MAX_PATH+1];
PARSE_ERROR_INFO Info = {sizeof(Info)};
BOOL fGotMsg = FALSE;
if (THEME_PARSING_ERROR(hr))
{
if (SUCCEEDED(_GetThemeParseErrorInfo(&Info)))
{
lstrcpy(szErrorMsg, Info.szMsg);
fGotMsg = TRUE;
}
}
if (! fGotMsg)
{
lstrcpy(szErrorMsg, pszDefaultMsg);
}
if (*Info.szFileName) // input file error
{
fwprintf(ConsoleFile, L"%s(%d): error - %s\n",
Info.szFileName, Info.iLineNum, szErrorMsg);
fwprintf(ConsoleFile, L"%s\n", Info.szSourceLine);
}
else // general error
{
fwprintf(ConsoleFile, L"%s(): error - %s\n",
g_szInputDir, szErrorMsg);
}
SET_LAST_ERROR(hr);
return hr;
}
//---------------------------------------------------------------------------
void MakeResName(LPCWSTR pszName, LPWSTR pszResName, bool bUseClassName = false)
{
WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szBaseName[_MAX_FNAME], szExt[_MAX_EXT];
//---- isolate base name (no path) ----
_wsplitpath(pszName, szDrive, szDir, szBaseName, szExt);
if (szBaseName[0] == kRESFILECHAR) // Don't put $ in resource names
{
wcscpy(szBaseName, szBaseName + 1);
}
//---- replace the "." with a '_' ----
//---- if a file section name without .ini, append _INI so that the extracted files has .ini extension
if (*szExt)
{
wsprintf(pszResName, L"%s%s_%s", bUseClassName ? g_szCurrentClass : L"", szBaseName, szExt+1);
} else
{
wsprintf(pszResName, L"%s%s_INI", bUseClassName ? g_szCurrentClass : L"", szBaseName);
}
//---- all uppercase ----
CharUpperBuff(pszResName, lstrlen(pszResName));
//---- replace any spaces with underscores ----
WCHAR *q = pszResName;
while (*q)
{
if (*q == ' ')
*q = '_';
q++;
}
}
//---------------------------------------------------------------------------
HRESULT BuildThemeDll(LPCWSTR pszRcName, LPCWSTR pszResName, LPCWSTR pszDllName)
{
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"compiling resources\n");
HRESULT hr = SyncCmdLineRun(L"rc.exe", pszRcName);
if (FAILED(hr))
return ReportError(hr, L"Error during resource compiliation");
//---- run LINK to create the DLL ----
WCHAR params[2*_MAX_PATH+1];
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"linking theme dll\n");
wsprintf(params, L"/out:%s /machine:ix86 /dll /noentry %s", pszDllName, pszResName);
hr = SyncCmdLineRun(L"link.exe", params);
if (FAILED(hr))
return ReportError(hr, L"Error during DLL linking");
return S_OK;
}
//---------------------------------------------------------------------------
void OutputDashLine(FILE *outfile)
{
fwprintf(outfile, L"//----------------------------------------------------------------------\n");
}
//---------------------------------------------------------------------------
inline void ValueLine(FILE *outfile, LPCWSTR pszName, LPCWSTR pszValue)
{
fwprintf(outfile, L" VALUE \"%s\", \"%s\\0\"\n", pszName, pszValue);
}
//---------------------------------------------------------------------------
HRESULT OutputVersionInfo(FILE *outfile, LPCWSTR pszFileName, LPCWSTR pszBaseName)
{
fwprintf(outfile, L"1 PACKTHEM_VERSION\n");
fwprintf(outfile, L"BEGIN\n");
fwprintf(outfile, L" %d\n", PACKTHEM_VERSION);
fwprintf(outfile, L"END\n");
OutputDashLine(outfile);
WCHAR *Company = L"Microsoft";
WCHAR *Copyright = L"Copyright <20> 2000";
WCHAR szDescription[2*_MAX_PATH+1];
wsprintf(szDescription, L"%s Theme for Windows", pszBaseName);
fwprintf(outfile, L"1 VERSIONINFO\n");
fwprintf(outfile, L" FILEVERSION 1,0,0,1\n");
fwprintf(outfile, L" PRODUCTVERSION 1,0,0,1\n");
fwprintf(outfile, L" FILEFLAGSMASK 0x3fL\n");
fwprintf(outfile, L" FILEFLAGS 0x0L\n");
fwprintf(outfile, L" FILEOS 0x40004L\n");
fwprintf(outfile, L" FILETYPE 0x1L\n");
fwprintf(outfile, L" FILESUBTYPE 0x0L\n");
fwprintf(outfile, L"BEGIN\n");
fwprintf(outfile, L" BLOCK \"StringFileInfo\"\n");
fwprintf(outfile, L" BEGIN\n");
fwprintf(outfile, L" BLOCK \"040904b0\"\n");
fwprintf(outfile, L" BEGIN\n");
ValueLine(outfile, L"Comments", L"");
ValueLine(outfile, L"CompanyName", Company);
ValueLine(outfile, L"FileDescription", szDescription);
ValueLine(outfile, L"FileVersion", L"1, 0, 0, 1");
ValueLine(outfile, L"InternalName", pszFileName);
ValueLine(outfile, L"LegalCopyright", Copyright);
ValueLine(outfile, L"LegalTrademarks", L"");
ValueLine(outfile, L"OriginalFilename", pszFileName);
ValueLine(outfile, L"PrivateBuild", L"");
ValueLine(outfile, L"ProductName", szDescription);
ValueLine(outfile, L"ProductVersion", L"1, 0, 0, 1");
ValueLine(outfile, L"SpecialBuild", L"");
fwprintf(outfile, L" END\n");
fwprintf(outfile, L" END\n");
fwprintf(outfile, L" BLOCK \"VarFileInfo\"\n");
fwprintf(outfile, L" BEGIN\n");
fwprintf(outfile, L" VALUE \"Translation\", 0x409, 1200\n");
fwprintf(outfile, L" END\n");
fwprintf(outfile, L"END\n");
OutputDashLine(outfile);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT RemoveTempFiles(LPCWSTR szRcName, LPCWSTR szResName)
{
DeleteFile(szRcName);
DeleteFile(szResName);
//---- find & delete all temp files in temp directory ----
HANDLE hFile = NULL;
BOOL bFile = TRUE;
WIN32_FIND_DATA wfd;
WCHAR szPattern[_MAX_PATH+1];
WCHAR szTempName[_MAX_PATH+1];
wsprintf(szPattern, L"%s\\%s*.*", g_szTempPath, TEMP_FILENAME_BASE);
for( hFile = FindFirstFile( szPattern, &wfd ); hFile != INVALID_HANDLE_VALUE && bFile;
bFile = FindNextFile( hFile, &wfd ) )
{
wsprintf(szTempName, L"%s\\%s", g_szTempPath, wfd.cFileName);
DeleteFile(szTempName);
}
if (hFile)
{
FindClose( hFile );
}
// Remove files generated by the substitution tables
for (int i = 0; i < SubstNames.GetSize(); i++)
{
wsprintf(szTempName, L"%s\\$%s.ini", g_szTempPath, SubstNames[i].sName);
DeleteFile(szTempName);
}
return S_OK;
}
//---------------------------------------------------------------------------
int GetSubstTableIndex(LPCWSTR pszTableName)
{
// Search for an existing subst table
for (int i = 0; i < SubstNames.GetSize(); i++)
{
if (0 == AsciiStrCmpI(SubstNames[i].sName, pszTableName))
return i;
}
return -1;
}
//---------------------------------------------------------------------------
HRESULT GetSubstValue(LPCWSTR pszIniFileName, LPCWSTR pszName, LPWSTR pszResult)
{
UINT cTablesCount = SubstNames.GetSize();
if (pszIniFileName && pszIniFileName[0] == kRESFILECHAR)
{
pszIniFileName++;
}
for (UINT i = 0; i < cTablesCount; i++)
{
if (0 == AsciiStrCmpI(SubstNames[i].sName, pszIniFileName))
{
for (UINT j = SubstNames[i].iFirstIndex; j < SubstNames[i].iFirstIndex + SubstNames[i].cItems; j++)
{
if (0 == AsciiStrCmpI(SubstIds[j], pszName))
{
lstrcpy(pszResult, SubstValues[j]);
return S_OK;
}
}
}
}
return MakeError32(E_FAIL); // unknown sizename
}
//---------------------------------------------------------------------------
LPWSTR FindSymbolToken(LPWSTR pSrc, int nLen)
{
LPWSTR p = wcschr(pSrc, INI_MACRO_SYMBOL);
// Skip single #s
while (p != NULL && (p - pSrc < nLen - 1) && *(p + 1) != INI_MACRO_SYMBOL)
{
p = wcschr(p + 1, INI_MACRO_SYMBOL);
}
return p;
}
LPWSTR ReallocTextBuffer(LPWSTR pSrc, UINT *pnLen)
{
*pnLen *= 2; // Double the size each time
LPWSTR pszNew = (LPWSTR) LocalReAlloc(pSrc, *pnLen * sizeof(WCHAR), 0);
if (!pszNew)
{
LocalFree(pSrc);
return NULL;
}
return pszNew;
}
LPWSTR SubstituteSymbols(LPWSTR szTableName, LPWSTR pszText)
{
UINT nLen = wcslen(pszText);
UINT nNewLen = nLen * 2; // Reserve some additional space
UINT iSymbol;
UINT nBlockSize;
LPWSTR pszNew = (LPWSTR) LocalAlloc(0, nNewLen * sizeof(WCHAR));
LPWSTR pSrc = FindSymbolToken(pszText, nLen);
LPWSTR pOldSrc = pszText;
LPWSTR pDest = pszNew;
WCHAR szSymbol[MAX_INPUT_LINE+1];
HRESULT hr;
if (!pszNew)
return NULL;
while (pSrc != NULL)
{
nBlockSize = UINT(pSrc - pOldSrc);
// Check for enough space after substitution
if (pDest + nBlockSize >= pszNew + nNewLen &&
NULL == (pszNew = ReallocTextBuffer(pszNew, &nNewLen)))
{
return NULL;
}
// Copy from the last # to the new one
wcsncpy(pDest, pOldSrc, nBlockSize);
pDest += nBlockSize;
pSrc += 2; // Skip the ##
// Copy the symbol name
iSymbol = 0;
while (IsCharAlphaNumericW(*pSrc) || (*pSrc == '_') || (*pSrc == '-'))
{
szSymbol[iSymbol++] = *pSrc++;
}
szSymbol[iSymbol] = 0;
// Get the symbol value
hr = GetSubstValue(szTableName, szSymbol, szSymbol);
if (FAILED(hr))
{
// There's a problem, abort and return the buffer untouched
LocalFree(pszNew);
WCHAR szErrorText[MAX_INPUT_LINE + 1];
wsprintf(szErrorText, L"Substitution symbol not found: %s", szSymbol);
ReportError(hr, szErrorText);
return NULL;
}
// Make sure we have enough room for one symbol
if (pDest + MAX_INPUT_LINE + 1 >= pszNew + nNewLen &&
NULL == (pszNew = ReallocTextBuffer(pszNew, &nNewLen)))
{
return NULL;
}
// Copy the symbol value to the new text
iSymbol = 0;
while (szSymbol[iSymbol] != 0)
{
*pDest++ = szSymbol[iSymbol++];
}
// Advance to the next iteration
pOldSrc = pSrc;
pSrc = FindSymbolToken(pSrc, nLen - UINT(pSrc - pszText));
}
if (pDest == pszNew)
{
// We did nothing, return NULL
LocalFree(pszNew);
return NULL;
}
// Copy the remainder text (after the last #)
if (pDest + wcslen(pOldSrc) >= pszNew + nNewLen &&
NULL == (pszNew = ReallocTextBuffer(pszNew, &nNewLen)))
{
return NULL;
}
wcscpy(pDest, pOldSrc);
return pszNew;
}
//---------------------------------------------------------------------------
HRESULT OutputResourceLine(LPCWSTR pszFilename, FILE *outfile, PACKFILETYPE ePackFileType)
{
HRESULT hr;
//---- did we already process this filename? ----
UINT cNames = FileInfo.GetSize();
for (UINT c=0; c < cNames; c++)
{
if (lstrcmpi(FileInfo[c].wsName, pszFilename)==0)
return S_OK;
}
WCHAR szTempName[_MAX_PATH+1];
WCHAR szResName[_MAX_PATH];
WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szBaseName[_MAX_FNAME], szExt[_MAX_EXT];
WCHAR *filetype;
LPWSTR pszText;
BOOL fWasAnsi;
BOOL fFileChecked = FALSE;
WCHAR szOrigName[_MAX_PATH];
lstrcpy(szOrigName, pszFilename);
_wsplitpath(pszFilename, szDrive, szDir, szBaseName, szExt);
if (ePackFileType == PACK_INIFILE)
{
//---- translate to UNICODE, if needed ----
hr = AllocateTextFile(pszFilename, &pszText, &fWasAnsi);
if (FAILED(hr))
return hr;
if (szBaseName[0] == kRESFILECHAR)
{
wcscpy(szBaseName, szBaseName + 1);
}
// If this an INI file with a subst table, process the substitution
for (int i = 0; i < SubstNames.GetSize(); i++)
{
if (0 == AsciiStrCmpI(SubstNames[i].sName, szBaseName))
{
SetLastError(0);
LPWSTR pszNewText = SubstituteSymbols(szBaseName, pszText);
if (pszNewText != NULL)
{
LPWSTR pszTemp = pszText;
pszText = pszNewText;
LocalFree(pszTemp);
}
hr = GetLastError();
if (SUCCEEDED(hr))
{
HRESULT hr = TextToFile(pszFilename, pszText); // Local hr, ignore failure later
if (SUCCEEDED(hr))
{
fWasAnsi = FALSE; // We don't need another temp file
}
}
break;
}
}
if (SUCCEEDED(hr) && fWasAnsi) // write out as temp file
{
DWORD len = lstrlen(g_szTempPath);
if ((len) && (g_szTempPath[len-1] == '\\'))
wsprintf(szTempName, L"%s%s%d%s", g_szTempPath, TEMP_FILENAME_BASE, iTempBitmapNum++, L".uni");
else
wsprintf(szTempName, L"%s\\%s%d%s", g_szTempPath, TEMP_FILENAME_BASE, iTempBitmapNum++, L".uni");
hr = TextToFile(szTempName, pszText);
pszFilename = szTempName; // use this name in .rc file
}
LocalFree(pszText);
if (FAILED(hr))
return hr;
fFileChecked = TRUE;
}
if (! fFileChecked)
{
//---- ensure the file is accessible ----
if (_waccess(pszFilename, 0) != 0)
{
fwprintf(ConsoleFile, L"Error - cannot access file: %s\n", pszFilename);
return MakeError32(E_FAIL); // cannot access (open) file
}
}
bool bUseClassName = false;
if (ePackFileType == PACK_IMAGEFILE)
{
filetype = L"BITMAP";
bUseClassName = true;
}
else if (ePackFileType == PACK_NTLFILE)
{
filetype = L"NTL";
}
else if (AsciiStrCmpI(szExt, L".ini")==0)
{
filetype = L"TEXTFILE";
}
else if (AsciiStrCmpI(szExt, L".wav")==0)
{
filetype = L"WAVE";
bUseClassName = true;
}
else
{
filetype = L"CUSTOM";
bUseClassName = true;
}
MakeResName(szOrigName, szResName, bUseClassName);
//---- replace all single backslashes with double ones ----
WCHAR DblName[_MAX_PATH+1];
WCHAR *d = DblName;
LPCWSTR p = pszFilename;
while (*p)
{
if (*p == '\\')
*d++ = '\\';
*d++ = *p++;
}
*d = 0;
//---- output the line to the .rc file ----
fwprintf(outfile, L"%-30s \t %s DISCARDABLE \"%s\"\n", szResName, filetype, DblName);
FILEINFO fileinfo;
fileinfo.wsName = pszFilename;
fileinfo.fIniFile = (ePackFileType == PACK_INIFILE);
FileInfo.Add(fileinfo);
g_LineCount++;
return S_OK;
}
//---------------------------------------------------------------------------
void ClearCombos()
{
for (int s=0; s < MAX_SIZES; s++)
{
for (int c=0; c < MAX_COLORS; c++)
{
Combos[s][c] = -1; // -1 means no file supports this combo
}
}
g_iMaxColor = -1;
g_iMaxSize = -1;
}
//---------------------------------------------------------------------------
HRESULT OutputCombos(FILE *outfile)
{
if ((g_iMaxColor < 0) || (g_iMaxSize < 0)) // no combos found
return ReportError(E_FAIL, L"No size/color combinations found");
fwprintf(outfile, L"COMBO COMBODATA\n");
fwprintf(outfile, L"BEGIN\n");
fwprintf(outfile, L" %d, %d // cColors, cSizes\n", g_iMaxColor+1, g_iMaxSize+1);
for (int s=0; s <= g_iMaxSize; s++)
{
for (int c=0; c <= g_iMaxColor; c++)
{
fwprintf(outfile, L" %d, ", Combos[s][c]);
}
fwprintf(outfile, L" // size=%d row\n", s);
}
fwprintf(outfile, L"END\n");
OutputDashLine(outfile);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT GetFileIndex(LPCWSTR pszName, int *piIndex)
{
int cCount = ResFileNames.GetSize();
for (int i=0; i < cCount; i++)
{
if (lstrcmpi(ResFileNames[i], pszName)==0)
{
*piIndex = i;
return S_OK;
}
}
return MakeError32(E_FAIL); // unknown filename
}
//---------------------------------------------------------------------------
HRESULT GetColorIndex(LPCWSTR pszName, int *piIndex)
{
int cCount = ColorSchemes.GetSize();
for (int i=0; i < cCount; i++)
{
if (lstrcmpi(ColorSchemes[i], pszName)==0)
{
*piIndex = i;
return S_OK;
}
}
return MakeError32(E_FAIL); // unknown colorname
}
//---------------------------------------------------------------------------
HRESULT GetSizeIndex(LPCWSTR pszName, int *piIndex)
{
int cCount = SizeNames.GetSize();
for (int i=0; i < cCount; i++)
{
if (lstrcmpi(SizeNames[i], pszName)==0)
{
*piIndex = i;
return S_OK;
}
}
return MakeError32(E_FAIL); // unknown sizename
}
//---------------------------------------------------------------------------
HRESULT ApplyCombos(LPCWSTR pszResFileName, LPCWSTR pszColors, LPCWSTR pszSizes)
{
//---- get index of pszResFileName ----
int iFileNum;
HRESULT hr = GetFileIndex(pszResFileName, &iFileNum);
if (FAILED(hr))
return hr;
//---- parse colors in pszColors ----
CScanner scan(pszColors);
WCHAR szName[_MAX_PATH+1];
int iColors[MAX_COLORS];
int cColors = 0;
while (1)
{
if (! scan.GetId(szName))
return MakeError32(E_FAIL); // bad color list
//---- get index of szName ----
int index;
HRESULT hr = GetColorIndex(szName, &index);
if (FAILED(hr))
return hr;
if (cColors == MAX_COLORS)
return MakeError32(E_FAIL); // too many colors specified
iColors[cColors++] = index;
if (scan.EndOfLine())
break;
if (! scan.GetChar(L','))
return MakeError32(E_FAIL); // names must be comma separated
}
//---- parse sizes in pszSizes ----
scan.AttachLine(pszSizes);
int iSizes[MAX_SIZES];
int cSizes = 0;
while (1)
{
if (! scan.GetId(szName))
return MakeError32(E_FAIL); // bad color list
//---- get index of szName ----
int index;
HRESULT hr = GetSizeIndex(szName, &index);
if (FAILED(hr))
return hr;
if (cSizes == MAX_SIZES)
return MakeError32(E_FAIL); // too many sizes specified
iSizes[cSizes++] = index;
if (scan.EndOfLine())
break;
if (! scan.GetChar(L','))
return MakeError32(E_FAIL); // names must be comma separated
}
//---- now form all combos of specified colors & sizes ----
for (int c=0; c < cColors; c++) // for each color
{
int color = iColors[c];
for (int s=0; s < cSizes; s++) // for each size
{
int size = iSizes[s];
Combos[size][color] = (SHORT)iFileNum;
//---- update our max's ----
if (size > g_iMaxSize)
g_iMaxSize = size;
if (color > g_iMaxColor)
g_iMaxColor = color;
}
}
return S_OK;
}
//---------------------------------------------------------------------------
void WriteProperty(CSimpleArray<CWideString> &csa, LPCWSTR pszSection, LPCWSTR pszPropName,
LPCWSTR pszValue)
{
WCHAR buff[MAX_PATH*2];
wsprintf(buff, L"%s@[%s]%s=%s", g_szBaseIniName, pszSection, pszPropName, pszValue);
csa.Add(CWideString(buff));
}
//---------------------------------------------------------------------------
BOOL FnCallBack(enum THEMECALLBACK tcbType, LPCWSTR pszName, LPCWSTR pszName2,
LPCWSTR pszName3, int iIndex, LPARAM lParam)
{
HRESULT hr = S_OK;
int nDefaultDepth = 15;
switch (tcbType)
{
case TCB_FILENAME:
WCHAR szFullName[_MAX_PATH+1];
hr = AddPathIfNeeded(pszName, g_szInputDir, szFullName, ARRAYSIZE(szFullName));
if (FAILED(hr))
{
SET_LAST_ERROR(hr);
return FALSE;
}
if ((iIndex == TMT_IMAGEFILE) || (iIndex == TMT_GLYPHIMAGEFILE) || (iIndex == TMT_STOCKIMAGEFILE))
hr = OutputResourceLine(szFullName, (FILE *)lParam, PACK_IMAGEFILE);
else if ((iIndex >= TMT_IMAGEFILE1) && (iIndex <= TMT_IMAGEFILE5))
hr = OutputResourceLine(szFullName, (FILE *)lParam, PACK_IMAGEFILE);
#if 0 // not yet implemented
else if (iIndex == TMT_NTLFILE)
hr = OutputResourceLine(szFullName, (FILE *)lParam, PACK_NTLFILE);
#endif
else
hr = MakeError32(E_FAIL); // unexpected type
if (FAILED(hr))
{
SET_LAST_ERROR(hr);
return FALSE;
}
break;
case TCB_FONT:
WriteProperty(PropValuePairs, pszName2, pszName3, pszName);
break;
case TCB_MIRRORIMAGE:
{
LPCWSTR p;
if (lParam)
p = L"1";
else
p = L"0";
WriteProperty(PropValuePairs, pszName2, pszName3, p);
}
break;
case TCB_LOCALIZABLE_RECT:
{
WCHAR szBuff[100];
RECT *prc = (RECT *)lParam;
wsprintf(szBuff, L"%d, %d, %d, %d", prc->left, prc->top, prc->right, prc->bottom);
WriteProperty(PropValuePairs, pszName2, pszName3, szBuff);
}
break;
case TCB_COLORSCHEME:
ColorSchemes.Add(CWideString(pszName));
ColorDisplays.Add(CWideString(pszName2));
ColorToolTips.Add(CWideString(pszName3));
break;
case TCB_SIZENAME:
SizeNames.Add(CWideString(pszName));
SizeDisplays.Add(CWideString(pszName2));
SizeToolTips.Add(CWideString(pszName3));
break;
case TCB_SUBSTTABLE:
{
int iTableIndex = GetSubstTableIndex(pszName);
if (iTableIndex == -1) // Not found, add one
{
sSubstTable s;
s.sName = pszName;
s.iFirstIndex = -1;
s.cItems = 0;
SubstNames.Add(s);
iTableIndex = SubstNames.GetSize() - 1;
}
if (0 == AsciiStrCmpI(pszName2, SUBST_TABLE_INCLUDE))
{
int iSecondTableIndex = GetSubstTableIndex(pszName3);
if (iSecondTableIndex == -1)
{
SET_LAST_ERROR(MakeError32(ERROR_NOT_FOUND));
return FALSE;
}
else
{
// Copy the symbols in the new table
for (UINT iSymbol = SubstNames[iSecondTableIndex].iFirstIndex;
iSymbol < SubstNames[iSecondTableIndex].iFirstIndex + SubstNames[iSecondTableIndex].cItems;
iSymbol++)
{
if (SubstNames[iTableIndex].iFirstIndex == -1)
{
SubstNames[iTableIndex].iFirstIndex = SubstValues.GetSize();
}
SubstNames[iTableIndex].cItems++;
SubstIds.Add(CWideString(SubstIds[iSymbol]));
SubstValues.Add(CWideString(SubstValues[iSymbol]));
}
}
}
else if (pszName2 != NULL && pszName3 != NULL)
{
// If the table was pre-created, update it
if (SubstNames[iTableIndex].iFirstIndex == -1)
{
SubstNames[iTableIndex].iFirstIndex = SubstValues.GetSize();
}
SubstNames[iTableIndex].cItems++;
SubstIds.Add(CWideString(pszName2));
SubstValues.Add(CWideString(pszName3));
}
break;
}
case TCB_NEEDSUBST:
GetSubstValue(pszName, pszName2, (LPWSTR) pszName3);
break;
case TCB_CDFILENAME:
WCHAR szResName[_MAX_PATH+1];
MakeResName(pszName, szResName);
ResFileNames.Add(CWideString(szResName));
MinDepths.Add(nDefaultDepth);
BaseResFileNames.Add(CWideString(pszName));
OrigFileNames.Add(CWideString(pszName2));
break;
case TCB_CDFILECOMBO:
MakeResName(pszName, szResName);
hr = ApplyCombos(szResName, pszName2, pszName3);
if (FAILED(hr))
{
SET_LAST_ERROR(hr);
return FALSE;
}
break;
case TCB_DOCPROPERTY:
if ((iIndex < 0) || (iIndex >= ARRAYSIZE(DocProperties)))
return FALSE;
DocProperties[iIndex] = pszName;
break;
case TCB_MINCOLORDEPTH:
MakeResName(pszName, szResName);
int iRes;
if (SUCCEEDED(GetFileIndex(szResName, &iRes)))
{
MinDepths[iRes] = iIndex;
}
break;
}
SET_LAST_ERROR(hr);
return TRUE;
}
//---------------------------------------------------------------------------
HRESULT OpenOutFile(FILE *&outfile, LPCWSTR pszRcName, LPCWSTR pszBaseName)
{
if (! outfile) // first time thru
{
//---- open out file ----
outfile = _wfopen(pszRcName, L"wt");
if (! outfile)
{
fwprintf(ConsoleFile, L"Error - cannot open file: %s\n", pszRcName);
return MakeError32(E_FAIL);
}
OutputDashLine(outfile);
fwprintf(outfile, L"// %s.rc - used to build the %s theme DLL\n", pszBaseName, pszBaseName);
OutputDashLine(outfile);
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT ProcessContainerFile(LPCWSTR pszDir, LPCWSTR pszInputName, FILE *&outfile)
{
HRESULT hr;
//---- output .ini filename as a resource ----
WCHAR fullname[_MAX_PATH+1];
wsprintf(fullname, L"%s\\%s", pszDir, pszInputName);
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"processing container file: %s\n", fullname);
hr = OutputResourceLine(fullname, outfile, PACK_INIFILE);
if (FAILED(hr))
{
ReportError(hr, L"Error reading themes.ini file");
goto exit;
}
OutputDashLine(outfile);
int oldcnt = g_LineCount;
//---- scan the themes.ini files for color, size, & file sections; write 'em to the .rc file ----
DWORD flags = PTF_CONTAINER_PARSE | PTF_CALLBACK_COLORSECTION | PTF_CALLBACK_SIZESECTION
| PTF_CALLBACK_FILESECTION | PTF_CALLBACK_DOCPROPERTIES | PTF_CALLBACK_SUBSTTABLE;
WCHAR szErrMsg[4096];
hr = _ParseThemeIniFile(fullname, flags, FnCallBack, (LPARAM)outfile);
if (FAILED(hr))
{
ReportError(hr, L"Error parsing themes.ini file");
goto exit;
}
if (g_LineCount > oldcnt)
OutputDashLine(outfile);
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT ProcessClassDataFile(LPCWSTR pszFileName, FILE *&outfile, LPCWSTR pszResFileName, LPCWSTR pszInputDir)
{
HRESULT hr;
WCHAR szFullName[MAX_PATH];
WCHAR szTempName[MAX_PATH];
LPWSTR pBS = NULL;
hr = hr_lstrcpy(g_szCurrentClass, pszFileName, ARRAYSIZE(g_szCurrentClass)); // make avail to everybody
if (SUCCEEDED(hr))
{
pBS = wcschr(g_szCurrentClass, L'\\');
if (pBS)
{
*pBS = L'_';
*(pBS + 1) = L'\0';
}
}
if (pBS == NULL) // If there's no '\', don't use the class name
{
g_szCurrentClass[0] = 0;
}
hr = hr_lstrcpy(g_szInputDir, pszInputDir, ARRAYSIZE(g_szInputDir)); // make avail to everybody
if (FAILED(hr))
goto exit;
hr = AddPathIfNeeded(pszFileName, pszInputDir, szFullName, ARRAYSIZE(szFullName));
if (FAILED(hr))
goto exit;
//---- extract base ini name ----
WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szExt[_MAX_EXT];
_wsplitpath(szFullName, szDrive, szDir, g_szBaseIniName, szExt);
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"processing classdata file: %s\n", pszFileName);
//---- Create a temporary INI file with the substituted values
UINT cTablesCount = SubstNames.GetSize();
for (UINT i = 0; i < cTablesCount; i++)
{
if (0 == AsciiStrCmpI(SubstNames[i].sName, pszResFileName))
{
// Section used in the string table
wcscpy(g_szBaseIniName, SubstNames[i].sName);
// Create the temp file
DWORD len = lstrlen(g_szTempPath);
if ((len) && (g_szTempPath[len-1] == '\\'))
wsprintf(szTempName, L"%s$%s%s", g_szTempPath, pszResFileName, szExt);
else
wsprintf(szTempName, L"%s\\$%s%s", g_szTempPath, pszResFileName, szExt);
if (lstrcmpi(szFullName, szTempName))
{
CopyFile(szFullName, szTempName, FALSE);
SetFileAttributes(szTempName, FILE_ATTRIBUTE_NORMAL);
wcscpy(szFullName, szTempName);
}
break;
}
}
//---- output .ini filename as a resource ----
hr = OutputResourceLine(szFullName, outfile, PACK_INIFILE);
if (FAILED(hr))
goto exit;
OutputDashLine(outfile);
int oldcnt;
oldcnt = g_LineCount;
//---- scan the classdata .ini file for valid filenames & fonts; write 'em to the .rc file ----
WCHAR szErrMsg[4096];
DWORD flags;
flags = PTF_CLASSDATA_PARSE | PTF_CALLBACK_FILENAMES | PTF_CALLBACK_LOCALIZATIONS
| PTF_CALLBACK_MINCOLORDEPTH;
hr = _ParseThemeIniFile(szFullName, flags, FnCallBack, (LPARAM)outfile);
if (FAILED(hr))
{
ReportError(hr, L"Error parsing classdata .ini file");
goto exit;
}
if (g_LineCount > oldcnt)
OutputDashLine(outfile);
hr = S_OK;
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT ProcessClassDataFiles(FILE *&outfile, LPCWSTR pszInputDir)
{
int cNames = OrigFileNames.GetSize();
for (int i=0; i < cNames; i++)
{
HRESULT hr = ProcessClassDataFile(OrigFileNames[i], outfile, BaseResFileNames[i], pszInputDir);
if (FAILED(hr))
return hr;
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT OutputStringTable(FILE *outfile, CWideString *ppszStrings, UINT cStrings, int iBaseNum,
LPCWSTR pszTitle, BOOL fLocalizable=TRUE, BOOL fMinDepths=FALSE)
{
if (! cStrings)
return S_OK;
if (fLocalizable)
{
fwprintf(outfile, L"STRINGTABLE DISCARDABLE // %s\n", pszTitle);
}
else // custom resource type
{
fwprintf(outfile, L"1 %s DISCARDABLE\n", pszTitle);
}
fwprintf(outfile, L"BEGIN\n");
for (UINT c=0; c < cStrings; c++)
{
LPCWSTR p = ppszStrings[c];
if (! p)
p = L"";
if (fLocalizable)
fwprintf(outfile, L" %d \t\"%s\"\n", iBaseNum, p);
else
{
if (fMinDepths)
{
fwprintf(outfile, L" %d,\n", MinDepths[c]);
}
else
{
fwprintf(outfile, L" L\"%s\\0\",\n", p);
}
if (c == cStrings-1) // last entry
{
if (fMinDepths)
{
fwprintf(outfile, L" 0\n");
}
else
{
fwprintf(outfile, L" L\"\\0\",\n");
}
}
}
iBaseNum++;
}
fwprintf(outfile, L"END\n");
OutputDashLine(outfile);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT OutputAllStringTables(FILE *outfile)
{
//---- output all non-localizable strings ----
if (ColorSchemes.GetSize())
{
OutputStringTable(outfile, &ColorSchemes[0], ColorSchemes.GetSize(),
0, L"COLORNAMES", FALSE);
}
if (SizeNames.GetSize())
{
OutputStringTable(outfile, &SizeNames[0], SizeNames.GetSize(),
0, L"SIZENAMES", FALSE);
}
if (ResFileNames.GetSize())
{
OutputStringTable(outfile, &ResFileNames[0], ResFileNames.GetSize(),
0, L"FILERESNAMES", FALSE);
}
if (MinDepths.GetSize())
{
OutputStringTable(outfile, &ResFileNames[0], ResFileNames.GetSize(),
0, L"MINDEPTH", FALSE, TRUE);
}
if (OrigFileNames.GetSize())
{
OutputStringTable(outfile, &OrigFileNames[0], OrigFileNames.GetSize(),
0, L"ORIGFILENAMES", FALSE);
}
//---- output all localizable strings ----
if (ColorDisplays.GetSize())
{
OutputStringTable(outfile, &ColorDisplays[0], ColorDisplays.GetSize(),
RES_BASENUM_COLORDISPLAYS, L"Color Display Names");
}
if (ColorToolTips.GetSize())
{
OutputStringTable(outfile, &ColorToolTips[0], ColorToolTips.GetSize(),
RES_BASENUM_COLORTOOLTIPS, L"Color ToolTips");
}
if (SizeDisplays.GetSize())
{
OutputStringTable(outfile, &SizeDisplays[0], SizeDisplays.GetSize(),
RES_BASENUM_SIZEDISPLAYS, L"Size Display Names");
}
if (SizeToolTips.GetSize())
{
OutputStringTable(outfile, &SizeToolTips[0], SizeToolTips.GetSize(),
RES_BASENUM_SIZETOOLTIPS, L"Size ToolTips");
}
OutputStringTable(outfile, &DocProperties[0], ARRAYSIZE(DocProperties),
RES_BASENUM_DOCPROPERTIES, L"Doc PropValuePairs");
OutputStringTable(outfile, &PropValuePairs[0], PropValuePairs.GetSize(),
RES_BASENUM_PROPVALUEPAIRS, L"PropValuePairs");
return S_OK;
}
//---------------------------------------------------------------------------
BOOL WriteBitmapHeader(CSimpleFile &cfOut, BYTE *pBytes, DWORD dwBytes)
{
BOOL fOK = FALSE;
BYTE pbHdr1[] = {0x42, 0x4d};
BYTE pbHdr2[] = {0x0, 0x0, 0x0, 0x0};
int iFileLen;
//---- add bitmap hdr at front ----
HRESULT hr = cfOut.Write(pbHdr1, sizeof(pbHdr1));
if (FAILED(hr))
{
ReportError(hr, L"Cannot write to output file");
goto exit;
}
//---- add length of data ----
iFileLen = dwBytes + sizeof(BITMAPFILEHEADER);
hr = cfOut.Write(&iFileLen, sizeof(int));
if (FAILED(hr))
{
ReportError(hr, L"Cannot write to output file");
goto exit;
}
hr = cfOut.Write(pbHdr2, sizeof(pbHdr2));
if (FAILED(hr))
{
ReportError(hr, L"Cannot write to output file");
goto exit;
}
//---- offset to bits (who's idea was *this* field?) ----
int iOffset, iColorTableSize;
DWORD dwSize;
iOffset = sizeof(BITMAPFILEHEADER);
dwSize = *(DWORD *)pBytes;
iOffset += dwSize;
iColorTableSize = 0;
switch (dwSize)
{
case sizeof(BITMAPCOREHEADER):
BITMAPCOREHEADER *hdr1;
hdr1 = (BITMAPCOREHEADER *)pBytes;
if (hdr1->bcBitCount == 1)
iColorTableSize = 2*sizeof(RGBTRIPLE);
else if (hdr1->bcBitCount == 4)
iColorTableSize = 16*sizeof(RGBTRIPLE);
else if (hdr1->bcBitCount == 8)
iColorTableSize = 256*sizeof(RGBTRIPLE);
break;
case sizeof(BITMAPINFOHEADER):
case sizeof(BITMAPV4HEADER):
case sizeof(BITMAPV5HEADER):
BITMAPINFOHEADER *hdr2;
hdr2 = (BITMAPINFOHEADER *)pBytes;
if (hdr2->biClrUsed)
iColorTableSize = hdr2->biClrUsed*sizeof(RGBQUAD);
else
{
if (hdr2->biBitCount == 1)
iColorTableSize = 2*sizeof(RGBQUAD);
else if (hdr2->biBitCount == 4)
iColorTableSize = 16*sizeof(RGBQUAD);
else if (hdr2->biBitCount == 8)
iColorTableSize = 256*sizeof(RGBQUAD);
}
break;
}
iOffset += iColorTableSize;
hr = cfOut.Write(&iOffset, sizeof(int));
if (FAILED(hr))
{
ReportError(hr, L"Cannot write to output file");
goto exit;
}
fOK = TRUE;
exit:
return fOK;
}
//---------------------------------------------------------------------------
BOOL CALLBACK ResEnumerator(HMODULE hModule, LPCWSTR pszType, LPWSTR pszResName, LONG_PTR lParam)
{
HRESULT hr;
BOOL fAnsi = (BOOL)lParam;
BOOL fText = FALSE;
RESOURCE BYTE *pBytes = NULL;
CSimpleFile cfOut;
DWORD dwBytes;
if (pszType != RT_BITMAP)
fText = TRUE;
hr = GetPtrToResource(hModule, pszType, pszResName, (void **)&pBytes, &dwBytes);
if (FAILED(hr))
{
ReportError(hr, L"error reading file resources");
goto exit;
}
//---- convert name to filename ----
WCHAR szFileName[_MAX_PATH+1];
lstrcpy(szFileName, pszResName);
WCHAR *q;
q = wcsrchr(szFileName, '_');
if (q)
*q = '.';
if (! fText)
fAnsi = FALSE; // don't translate if binary data
hr = cfOut.Create(szFileName, fAnsi);
if (FAILED(hr))
{
ReportError(hr, L"Cannot create output file");
goto exit;
}
if (! fText)
{
if (! WriteBitmapHeader(cfOut, pBytes, dwBytes))
goto exit;
}
hr = cfOut.Write(pBytes, dwBytes);
if (FAILED(hr))
{
ReportError(hr, L"Cannot write to output file");
goto exit;
}
exit:
return (SUCCEEDED(hr));
}
//---------------------------------------------------------------------------
void WriteBitmap(LPWSTR pszFileName, BITMAPINFOHEADER* pbmi, DWORD* pdwData)
{
DWORD dwLen = pbmi->biWidth * pbmi->biHeight;
CSimpleFile cfOut;
cfOut.Create(pszFileName, FALSE);
BITMAPFILEHEADER bmfh = {0};
bmfh.bfType = 'MB';
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (dwLen * sizeof(DWORD));
bmfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
cfOut.Write(&bmfh, sizeof(BITMAPFILEHEADER));
cfOut.Write(pbmi, sizeof(BITMAPINFOHEADER));
cfOut.Write(pdwData, dwLen * sizeof(DWORD));
}
HRESULT ColorShift(LPWSTR pszFileName, int cchFileName)
{
HDC hdc = GetDC(NULL);
if (hdc)
{
HBITMAP hbm = (HBITMAP)LoadImage(0, pszFileName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
if (hbm)
{
BITMAP bm;
GetObject(hbm, sizeof(bm), &bm);
DWORD dwLen = bm.bmWidth * bm.bmHeight;
DWORD* pPixelQuads = new DWORD[dwLen];
if (pPixelQuads)
{
BITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = bm.bmWidth;
bi.bmiHeader.biHeight = bm.bmHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
if (GetDIBits(hdc, hbm, 0, bm.bmHeight, pPixelQuads, &bi, DIB_RGB_COLORS))
{
pszFileName[lstrlen(pszFileName) - 4] = 0;
WCHAR szFileNameR[MAX_PATH];
wsprintf(szFileNameR, L"%sR.bmp", pszFileName);
WCHAR szFileNameG[MAX_PATH];
wsprintf(szFileNameG, L"%sG.bmp", pszFileName);
WCHAR szFileNameB[MAX_PATH];
wsprintf(szFileNameB, L"%sB.bmp", pszFileName);
WriteBitmap(szFileNameB, &bi.bmiHeader, pPixelQuads);
DWORD *pdw = pPixelQuads;
for (DWORD i = 0; i < dwLen; i++)
{
COLORREF crTemp = *pdw;
if (crTemp != RGB(255, 0, 255))
{
crTemp = (crTemp & 0xff000000) | RGB(GetGValue(crTemp), GetBValue(crTemp), GetRValue(crTemp));
}
*pdw = crTemp;
pdw++;
}
WriteBitmap(szFileNameR, &bi.bmiHeader, pPixelQuads);
pdw = pPixelQuads;
for (DWORD i = 0; i < dwLen; i++)
{
COLORREF crTemp = *pdw;
if (crTemp != RGB(255, 0, 255))
{
crTemp = (crTemp & 0xff000000) | RGB(GetGValue(crTemp), GetBValue(crTemp), GetRValue(crTemp));
}
*pdw = crTemp;
pdw++;
}
WriteBitmap(szFileNameG, &bi.bmiHeader, pPixelQuads);
}
delete[] pPixelQuads;
}
DeleteObject(hbm);
}
ReleaseDC(NULL, hdc);
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT UnpackTheme(LPCWSTR pszFileName, BOOL fAnsi)
{
HRESULT hr = S_OK;
//---- load the file as a resource only DLL ----
RESOURCE HINSTANCE hInst = LoadLibraryEx(pszFileName, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (hInst)
{
//---- enum all bitmaps & write as files ----
if (! EnumResourceNames(hInst, RT_BITMAP, ResEnumerator, LPARAM(fAnsi)))
hr = GetLastError();
//---- enum all .ini files & write as files ----
if (! EnumResourceNames(hInst, L"TEXTFILE", ResEnumerator, LPARAM(fAnsi)))
hr = GetLastError();
//---- close the file ----
if (hInst)
FreeLibrary(hInst);
}
return hr;
}
//---------------------------------------------------------------------------
HRESULT PackTheme(LPCWSTR pszInputDir, LPWSTR pszOutputName, DWORD cchSize)
{
//---- is it a valid dir ----
DWORD dwMask = GetFileAttributes(pszInputDir);
if ((dwMask == 0xffffffff) || (! (dwMask & FILE_ATTRIBUTE_DIRECTORY)))
{
fwprintf(ConsoleFile, L"\nError - not a valid directory name: %s\n", pszInputDir);
return MakeError32(E_FAIL);
}
//---- build: szDllName ----
WCHAR szDllName[_MAX_PATH+1];
BOOL fOutputDir = FALSE;
if (! *pszOutputName) // not specified - build from pszInputDir
{
WCHAR szFullDir[_MAX_PATH+1];
WCHAR *pszBaseName;
DWORD val = GetFullPathName(pszInputDir, ARRAYSIZE(szFullDir), szFullDir, &pszBaseName);
if (! val)
return MakeErrorLast();
//---- make output dir same as input dir ----
wsprintf(szDllName, L"%s\\%s%s", pszInputDir, pszBaseName, THEMEDLL_EXT);
}
else // get full name of output file
{
DWORD val = GetFullPathName(pszOutputName, ARRAYSIZE(szDllName), szDllName, NULL);
if (! val)
return MakeErrorLast();
fOutputDir = TRUE; // don't remove temp files
}
// Give the caller the path so the file can be signed.
lstrcpyn(pszOutputName, szDllName, cchSize);
//--- delete the old target in case we have errors ----
DeleteFile(pszOutputName);
//---- build: g_szTempPath, szDllRoot, szRcName, and szResName ----
WCHAR szDllRoot[_MAX_PATH+1];
WCHAR szResName[_MAX_PATH+1];
WCHAR szRcName[_MAX_PATH+1];
WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szBaseName[_MAX_FNAME], szExt[_MAX_EXT];
_wsplitpath(szDllName, szDrive, szDir, szBaseName, szExt);
_wmakepath(szDllRoot, L"", L"", szBaseName, szExt);
_wmakepath(szRcName, szDrive, szDir, szBaseName, L".rc");
_wmakepath(szResName, szDrive, szDir, szBaseName, L".res");
if (fOutputDir)
_wmakepath(g_szTempPath, szDrive, szDir, L"", L"");
else
lstrcpy(g_szTempPath, L".");
FILE *outfile = NULL;
OpenOutFile(outfile, szRcName, szBaseName);
ClearCombos();
//---- process the main container file ----
HRESULT hr = ProcessContainerFile(pszInputDir, CONTAINER_NAME, outfile);
if (FAILED(hr))
goto exit;
//---- process all classdata files that were defined in container file ----
hr = ProcessClassDataFiles(outfile, pszInputDir);
if (FAILED(hr))
goto exit;
//---- output all string tables ----
hr = OutputAllStringTables(outfile);
if (FAILED(hr))
goto exit;
hr = OutputCombos(outfile);
if (FAILED(hr))
goto exit;
hr = OutputVersionInfo(outfile, szDllRoot, szBaseName);
if (FAILED(hr))
goto exit;
fclose(outfile);
outfile = NULL;
hr = BuildThemeDll(szRcName, szResName, szDllName);
exit:
if (outfile)
fclose(outfile);
if (ConsoleFile != stdout)
fclose(ConsoleFile);
if (! g_fKeepTempFiles)
RemoveTempFiles(szRcName, szResName);
if (SUCCEEDED(hr))
{
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"Created %s\n", szDllName);
return S_OK;
}
if (! g_fQuietRun)
fwprintf(ConsoleFile, L"Error occured - theme DLL not created\n");
return hr;
}
//---------------------------------------------------------------------------
void PrintUsage()
{
fwprintf(ConsoleFile, L"\nUsage: \n\n");
fwprintf(ConsoleFile, L" packthem [-o <output name> ] [-k] [-q] <dirname>\n");
fwprintf(ConsoleFile, L" -m specifies the (full path) name of the image file you want to color shift\n");
fwprintf(ConsoleFile, L" -o specifies the (full path) name of the output file\n");
fwprintf(ConsoleFile, L" -k specifies that temp. files should be kept (not deleted)\n");
fwprintf(ConsoleFile, L" -d do not sign the file when building it\n");
fwprintf(ConsoleFile, L" -q quite mode (don't print header and progress msgs)\n\n");
fwprintf(ConsoleFile, L" packthem -u [-a] <packed filename> \n");
fwprintf(ConsoleFile, L" -u unpacks the packed file into its separate files in current dir\n");
fwprintf(ConsoleFile, L" -a writes .ini files as ANSI (defaults to UNICODE)\n\n");
fwprintf(ConsoleFile, L" packthem -p [-q] <packed filename> \n");
fwprintf(ConsoleFile, L" -p Parses the localized packed file and reports errors\n\n");
fwprintf(ConsoleFile, L" packthem [-c] [-q] <packed filename> \n");
fwprintf(ConsoleFile, L" -c check the signature of the already created file\n\n");
fwprintf(ConsoleFile, L" packthem [-s] [-q] <packed filename> \n");
fwprintf(ConsoleFile, L" -s sign the already created file\n\n");
fwprintf(ConsoleFile, L" packthem [-g] [-q] <packed filename> \n");
fwprintf(ConsoleFile, L" -g generate public and private keys\n\n");
}
//---------------------------------------------------------------------------
enum eOperation
{
opPack = 1,
opUnPack,
opSign,
opCheckSignature,
opGenerateKeys,
opParse,
opColorShift
};
//---------------------------------------------------------------------------
extern "C" WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE previnst,
LPTSTR pszCmdLine, int nShowCmd)
{
//---- initialize globals from themeldr.lib ----
ThemeLibStartUp(FALSE);
//---- initialize our globals ----
HRESULT hr = S_OK;
int nWeek = -1;
UtilsStartUp();
LogStartUp();
WCHAR szOutputName[_MAX_PATH+1] = {0};
int retval = 1; // error, until prove otherwise
BOOL fAnsi = FALSE;
BOOL fSkipSigning = FALSE;
eOperation Operation = opPack;
LPCWSTR p = pszCmdLine;
szOutputName[0] = 0; // Much faster than ={0};
//---- default to console until something else is specified ----
if (! ConsoleFile)
{
ConsoleFile = stdout;
}
while ((*p == '-') || (*p == '/'))
{
p++;
WCHAR sw = *p;
if (isupper(sw))
sw = (WCHAR)tolower(sw);
if (sw == 'e')
{
ConsoleFile = _wfopen(L"packthem.err", L"wt");
g_fQuietRun = TRUE;
p++;
}
else if (sw == 'o')
{
WCHAR *q = szOutputName;
p++; // skip over switch
while (iswspace(*p))
p++;
while ((*p) && (! iswspace(*p)))
*q++ = *p++;
*q = 0; // terminate the output name
}
else if (sw == 'k')
{
g_fKeepTempFiles = TRUE;
p++;
}
else if (sw == 'q')
{
g_fQuietRun = TRUE;
p++;
}
else if (sw == 'm')
{
Operation = opColorShift;
WCHAR *q = szOutputName;
p++; // skip over switch
while (iswspace(*p))
p++;
while ((*p) && (! iswspace(*p)))
*q++ = *p++;
*q = 0; // terminate the output name
}
else if (sw == 'u')
{
Operation = opUnPack;
p++;
}
else if (sw == 'd')
{
fSkipSigning = TRUE;
p++;
}
else if (sw == 'c')
{
Operation = opCheckSignature;
p++;
}
else if (sw == 'g')
{
Operation = opGenerateKeys;
p++;
}
else if (sw == 's')
{
Operation = opSign;
p++;
}
else if (sw == 'a')
{
fAnsi = TRUE;
p++;
}
else if (sw == 'w')
{
fAnsi = TRUE;
p++;
LARGE_INTEGER uli;
WCHAR szWeek[3];
szWeek[0] = p[0];
szWeek[1] = p[1];
szWeek[2] = 0;
if (StrToInt64ExInternalW(szWeek, 0, &(uli.QuadPart)) &&
(uli.QuadPart > 0))
{
nWeek = (int)uli.LowPart;
}
while ((L' ' != p[0]) && (0 != p[0]))
{
p++;
}
}
else if (sw == 'p')
{
Operation = opParse;
p++;
}
else if (sw == '?')
{
PrintUsage();
retval = 0;
goto exit;
}
else
{
fwprintf(ConsoleFile, L"Error - unrecognized switch: %s\n", p);
goto exit;
}
while (iswspace(*p))
p++;
}
LPCWSTR pszInputDir;
pszInputDir = p;
if (! g_fQuietRun)
{
fwprintf(ConsoleFile, L"Microsoft (R) Theme Packager (Version %d)\n", PACKTHEM_VERSION);
fwprintf(ConsoleFile, L"Copyright (C) Microsoft Corp 2000. All rights reserved.\n");
}
//---- any cmdline arg specified? ----
if (Operation != opColorShift)
{
if ((! pszInputDir) || (! *pszInputDir))
{
PrintUsage();
goto exit;
}
}
switch (Operation)
{
case opPack:
hr = PackTheme(pszInputDir, szOutputName, ARRAYSIZE(szOutputName));
if (SUCCEEDED(hr) && !fSkipSigning)
{
hr = SignTheme(szOutputName, nWeek);
if (!g_fQuietRun)
{
if (SUCCEEDED(hr))
{
wprintf(L"Creating the signature succeeded\n");
}
else
{
wprintf(L"The signature failed to be created. hr=%#08lx\n", hr);
}
}
}
break;
case opUnPack:
hr = UnpackTheme(pszInputDir, fAnsi);
break;
case opSign:
// We don't sign it again if the signature is already valid.
if (FAILED(CheckThemeFileSignature(pszInputDir)))
{
// Needs signing.
hr = SignTheme(pszInputDir, nWeek);
if (!g_fQuietRun)
{
if (SUCCEEDED(hr))
{
wprintf(L"Creating the signature succeeded\n");
}
else
{
wprintf(L"The signature failed to be created. hr=%#08lx\n", hr);
}
}
}
else
{
if (!g_fQuietRun)
{
wprintf(L"The file was already signed and the signature is still valid.");
}
}
break;
case opCheckSignature:
hr = CheckThemeFileSignature(pszInputDir);
if (!g_fQuietRun)
{
if (SUCCEEDED(hr))
{
wprintf(L"The signature is valid\n");
}
else
{
wprintf(L"The signature is not valid. hr=%#08lx\n", hr);
}
}
break;
case opGenerateKeys:
hr = GenerateKeys(pszInputDir);
break;
case opParse:
hr = ParseTheme(pszInputDir);
if (FAILED(hr))
{
ReportError(hr, L"Error during parsing");
goto exit;
} else
{
wprintf(L"No errors parsing theme file\n");
}
break;
case opColorShift:
hr = ColorShift(szOutputName, ARRAYSIZE(szOutputName));
break;
default:
if (FAILED(hr))
{
hr = E_FAIL;
goto exit;
}
break;
};
retval = 0; // all OK
exit:
UtilsShutDown();
LogShutDown();
return retval;
}
//---------------------------------------------------------------------------
HRESULT LoadClassDataIni(HINSTANCE hInst, LPCWSTR pszColorName,
LPCWSTR pszSizeName, LPWSTR pszFoundIniName, DWORD dwMaxIniNameChars, LPWSTR *ppIniData)
{
COLORSIZECOMBOS *combos;
HRESULT hr = FindComboData(hInst, &combos);
if (FAILED(hr))
return hr;
int iSizeIndex = 0;
int iColorIndex = 0;
if ((pszColorName) && (* pszColorName))
{
hr = GetColorSchemeIndex(hInst, pszColorName, &iColorIndex);
if (FAILED(hr))
return hr;
}
if ((pszSizeName) && (* pszSizeName))
{
hr = GetSizeIndex(hInst, pszSizeName, &iSizeIndex);
if (FAILED(hr))
return hr;
}
int filenum = COMBOENTRY(combos, iColorIndex, iSizeIndex);
if (filenum == -1)
return MakeError32(ERROR_NOT_FOUND);
//---- locate resname for classdata file "filenum" ----
hr = GetResString(hInst, L"FILERESNAMES", filenum, pszFoundIniName, dwMaxIniNameChars);
if (SUCCEEDED(hr))
{
hr = AllocateTextResource(hInst, pszFoundIniName, ppIniData);
}
return hr;
}
//---------------------------------------------------------------------------
// Parse the theme to detect localization errors
HRESULT ParseTheme(LPCWSTR pszThemeName)
{
// Dummy callback class needed by the parser
class CParserCallBack: public IParserCallBack
{
HRESULT AddIndex(LPCWSTR pszAppName, LPCWSTR pszClassName,
int iPartNum, int iStateNum, int iIndex, int iLen) { return S_OK; };
HRESULT AddData(SHORT sTypeNum, PRIMVAL ePrimVal, const void *pData, DWORD dwLen) { return S_OK; };
int GetNextDataIndex() { return 0; };
};
CParserCallBack *pParserCallBack = NULL;
CThemeParser *pParser = NULL;
HRESULT hr;
HINSTANCE hInst = NULL;
WCHAR *pDataIni = NULL;
WCHAR szClassDataName[_MAX_PATH+1];
//---- load the Color Scheme from "themes.ini" ----
hr = LoadThemeLibrary(pszThemeName, &hInst);
if (FAILED(hr))
goto exit;
pParser = new CThemeParser(FALSE);
if (! pParser)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
pParserCallBack = new CParserCallBack;
if (!pParserCallBack)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
THEMENAMEINFO tniColors;
THEMENAMEINFO tniSizes;
for (DWORD c = 0; ; c++)
{
if (FAILED(_EnumThemeColors(hInst, pszThemeName, NULL, c, &tniColors, FALSE)))
break;
for (DWORD s = 0 ; ; s++)
{
if (FAILED(_EnumThemeSizes(hInst, pszThemeName, tniColors.szName, s, &tniSizes, FALSE)))
break;
//---- load the classdata file resource into memory ----
hr = LoadClassDataIni(hInst, tniColors.szName, tniSizes.szName, szClassDataName, ARRAYSIZE(szClassDataName), &pDataIni);
if (FAILED(hr))
goto exit;
//---- parse & build binary theme ----
hr = pParser->ParseThemeBuffer(pDataIni, szClassDataName, NULL, hInst,
pParserCallBack, FnCallBack, NULL, PTF_CLASSDATA_PARSE);
if (FAILED(hr))
goto exit;
if (pDataIni)
{
delete [] pDataIni;
pDataIni = NULL;
}
}
}
exit:
if (hInst)
FreeLibrary(hInst);
if (pDataIni)
delete [] pDataIni;
if (pParser)
delete pParser;
if (pParserCallBack)
delete pParserCallBack;
return hr;
}