//--------------------------------------------------------------------------- // PackThem.cpp - packs up theme files into a theme DLL //--------------------------------------------------------------------------- #include "stdafx.h" #include #include #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; CSimpleArray ColorSchemes; CSimpleArray ColorDisplays; CSimpleArray ColorToolTips; CSimpleArray MinDepths; CSimpleArray SizeNames; CSimpleArray SizeDisplays; CSimpleArray SizeToolTips; typedef struct { CWideString sName; int iFirstIndex; UINT cItems; } sSubstTable; CSimpleArray SubstNames; CSimpleArray SubstIds; CSimpleArray SubstValues; CSimpleArray BaseResFileNames; CSimpleArray ResFileNames; CSimpleArray OrigFileNames; CSimpleArray 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))) { StringCchCopyW(szErrorMsg, ARRAYSIZE(szErrorMsg), Info.szMsg); fGotMsg = TRUE; } } if (! fGotMsg) { StringCchCopyW(szErrorMsg, ARRAYSIZE(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, ULONG cchResName, bool bUseClassName = false) { WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szBaseName[_MAX_FNAME], szExt[_MAX_EXT]; LPWSTR pszBaseName = szBaseName; *szDrive = *szDir = *pszBaseName = *szExt = 0; //---- isolate base name (no path) ---- _wsplitpath(pszName, szDrive, szDir, szBaseName, szExt); if (*pszBaseName == kRESFILECHAR) // Don't put $ in resource names { pszBaseName++; } //---- replace the "." with a '_' ---- //---- if a file section name without .ini, append _INI so that the extracted files has .ini extension if (*szExt) { StringCchPrintfW(pszResName, cchResName, L"%s%s_%s", bUseClassName ? g_szCurrentClass : L"", pszBaseName, szExt+1); } else { StringCchPrintfW(pszResName, cchResName, L"%s%s_INI", bUseClassName ? g_szCurrentClass : L"", pszBaseName); } //---- 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 szParams[2*_MAX_PATH+1]; if (! g_fQuietRun) fwprintf(ConsoleFile, L"linking theme dll\n"); StringCchPrintfW(szParams, ARRAYSIZE(szParams), L"/out:%s /machine:ix86 /dll /noentry %s", pszDllName, pszResName); hr = SyncCmdLineRun(L"link.exe", szParams); 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 © 2000"; WCHAR szDescription[2*_MAX_PATH+1]; StringCchPrintfW(szDescription, ARRAYSIZE(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]; StringCchPrintfW(szPattern, ARRAYSIZE(szPattern), L"%s\\%s*.*", g_szTempPath, TEMP_FILENAME_BASE); for( hFile = FindFirstFile( szPattern, &wfd ); hFile != INVALID_HANDLE_VALUE && bFile; bFile = FindNextFile( hFile, &wfd ) ) { StringCchPrintfW(szTempName, ARRAYSIZE(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++) { StringCchPrintfW(szTempName, ARRAYSIZE(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, ULONG cchResult) { 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)) { StringCchCopyW(pszResult, cchResult, 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 iSymbol; WCHAR szSymbol[MAX_INPUT_LINE+1]; UINT cchText = wcslen(pszText); UINT cchTextNew = cchText * 2; // Reserve some additional space UINT nBlockSize; LPWSTR pszNew = (LPWSTR) LocalAlloc(0, cchTextNew * sizeof(WCHAR)); LPWSTR pszDest = pszNew; LPWSTR pszSrc = FindSymbolToken(pszText, cchText); LPWSTR pszOldSrc = pszText; HRESULT hr; if (!pszNew) return NULL; while (pszSrc != NULL) { nBlockSize = UINT(pszSrc - pszOldSrc); // Check for enough space after substitution if (pszDest + nBlockSize >= pszNew + cchTextNew && NULL == (pszNew = ReallocTextBuffer(pszNew, &cchTextNew))) { return NULL; } // Copy from the last # to the new one CopyMemory(pszDest, pszOldSrc, nBlockSize * sizeof(WCHAR)); // don't want the terminating NULL! pszDest += nBlockSize; pszSrc += 2; // Skip the ## // Copy the symbol name iSymbol = 0; while (IsCharAlphaNumericW(*pszSrc) || (*pszSrc == '_') || (*pszSrc == '-')) { szSymbol[iSymbol++] = *pszSrc++; } szSymbol[iSymbol] = 0; // Get the symbol value hr = GetSubstValue(szTableName, szSymbol, szSymbol, ARRAYSIZE(szSymbol)); if (FAILED(hr)) { // There's a problem, abort and return the buffer untouched LocalFree(pszNew); WCHAR szErrorText[MAX_INPUT_LINE + 1]; StringCchPrintfW(szErrorText, ARRAYSIZE(szErrorText), L"Substitution symbol not found: %s", szSymbol); ReportError(hr, szErrorText); return NULL; } // Make sure we have enough room for one symbol if (pszDest + MAX_INPUT_LINE + 1 >= pszNew + cchTextNew && NULL == (pszNew = ReallocTextBuffer(pszNew, &cchTextNew))) { return NULL; } // Copy the symbol value to the new text iSymbol = 0; while (szSymbol[iSymbol] != 0) { *pszDest++ = szSymbol[iSymbol++]; } // Advance to the next iteration pszOldSrc = pszSrc; pszSrc = FindSymbolToken(pszSrc, cchText - UINT(pszSrc - pszText)); } if (pszDest == pszNew) { // We did nothing, return NULL LocalFree(pszNew); return NULL; } // Copy the remainder text (after the last #) if (pszDest + wcslen(pszOldSrc) >= pszNew + cchTextNew && NULL == (pszNew = ReallocTextBuffer(pszNew, &cchTextNew))) { return NULL; } StringCchCopyW(pszDest, lstrlenW(pszOldSrc) + 1, pszOldSrc); 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 *pszBaseName = szBaseName, *filetype; LPWSTR pszText; BOOL fWasAnsi; BOOL fFileChecked = FALSE; WCHAR szOrigName[_MAX_PATH]; StringCchCopyW(szOrigName, ARRAYSIZE(szOrigName), pszFilename); _wsplitpath(pszFilename, szDrive, szDir, pszBaseName, szExt); if (ePackFileType == PACK_INIFILE) { //---- translate to UNICODE, if needed ---- hr = AllocateTextFile(pszFilename, &pszText, &fWasAnsi); if (FAILED(hr)) return hr; if (*pszBaseName == kRESFILECHAR) { pszBaseName++; } // 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, pszBaseName)) { SetLastError(0); LPWSTR pszNewText = SubstituteSymbols(pszBaseName, 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] == '\\')) StringCchPrintfW(szTempName, ARRAYSIZE(szTempName), L"%s%s%d%s", g_szTempPath, TEMP_FILENAME_BASE, iTempBitmapNum++, L".uni"); else StringCchPrintfW(szTempName, ARRAYSIZE(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, ARRAYSIZE(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 &csa, LPCWSTR pszSection, LPCWSTR pszPropName, LPCWSTR pszValue) { WCHAR szBuff[MAX_PATH*2]; StringCchPrintfW(szBuff, ARRAYSIZE(szBuff), L"%s@[%s]%s=%s", g_szBaseIniName, pszSection, pszPropName, pszValue); csa.Add(CWideString(szBuff)); } //--------------------------------------------------------------------------- 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); 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; StringCchPrintfW(szBuff, ARRAYSIZE(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, MAX_INPUT_LINE); break; case TCB_CDFILENAME: WCHAR szResName[_MAX_PATH+1]; MakeResName(pszName, szResName, ARRAYSIZE(szResName)); ResFileNames.Add(CWideString(szResName)); MinDepths.Add(nDefaultDepth); BaseResFileNames.Add(CWideString(pszName)); OrigFileNames.Add(CWideString(pszName2)); break; case TCB_CDFILECOMBO: MakeResName(pszName, szResName, ARRAYSIZE(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, ARRAYSIZE(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 szFullName[_MAX_PATH+1]; StringCchPrintfW(szFullName, ARRAYSIZE(szFullName), L"%s\\%s", pszDir, pszInputName); if (! g_fQuietRun) fwprintf(ConsoleFile, L"processing container file: %s\n", szFullName); hr = OutputResourceLine(szFullName, 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(szFullName, 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 = SafeStringCchCopyW(g_szCurrentClass, ARRAYSIZE(g_szCurrentClass), pszFileName); // 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 = SafeStringCchCopyW(g_szInputDir, ARRAYSIZE(g_szInputDir), pszInputDir ); // 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 StringCchCopyW(g_szBaseIniName, ARRAYSIZE(g_szBaseIniName), SubstNames[i].sName); // Create the temp file DWORD len = lstrlen(g_szTempPath); if ((len) && (g_szTempPath[len-1] == '\\')) StringCchPrintfW(szTempName, ARRAYSIZE(szTempName), L"%s$%s%s", g_szTempPath, pszResFileName, szExt); else StringCchPrintfW(szTempName, ARRAYSIZE(szTempName), L"%s\\$%s%s", g_szTempPath, pszResFileName, szExt); if (lstrcmpi(szFullName, szTempName)) { CopyFile(szFullName, szTempName, FALSE); SetFileAttributes(szTempName, FILE_ATTRIBUTE_NORMAL); StringCchCopyW(szFullName, ARRAYSIZE(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]; StringCchCopyW(szFileName, ARRAYSIZE(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]; StringCchPrintfW(szFileNameR, ARRAYSIZE(szFileNameR), L"%sR.bmp", pszFileName); WCHAR szFileNameG[MAX_PATH]; StringCchPrintfW(szFileNameG, ARRAYSIZE(szFileNameG), L"%sG.bmp", pszFileName); WCHAR szFileNameB[MAX_PATH]; StringCchPrintfW(szFileNameB, ARRAYSIZE(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 cchOutputName) { //---- 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 ---- StringCchPrintfW(szDllName, ARRAYSIZE(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. StringCchCopyW(pszOutputName, cchOutputName, szDllName); //--- 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 StringCchCopyW(g_szTempPath, ARRAYSIZE(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 ] [-k] [-q] \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] \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] \n"); fwprintf(ConsoleFile, L" -p Parses the localized packed file and reports errors\n\n"); fwprintf(ConsoleFile, L" packthem [-c] [-q] \n"); fwprintf(ConsoleFile, L" -c check the signature of the already created file\n\n"); fwprintf(ConsoleFile, L" packthem [-s] [-q] \n"); fwprintf(ConsoleFile, L" -s sign the already created file\n\n"); } //--------------------------------------------------------------------------- enum eOperation { opPack = 1, opUnPack, opSign, opCheckSignature, 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 == '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 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; }