#include "priv.h" #include #include "w95wraps.h" //RC Stream Type #define RCST_RAW 0 #define RCST_COMPRESSED 1 class CRCStream : public IStream { public: CRCStream(LPCWSTR pcszFileName, int iResID, HRESULT* phres = NULL); ~CRCStream(); public: // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid,void **pv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // ISequentialStream methods (copied from sdk\inc\objidl.h and edited) STDMETHODIMP Read(void* pv, ULONG cb, ULONG* pcbRead); STDMETHODIMP Write(const void* pv, ULONG cb, ULONG* pcbWritten) { return E_NOTIMPL; } // IStream methods (copied from sdk\inc\objidl.h and edited) STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) { return E_NOTIMPL; } STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize) { return E_NOTIMPL; } STDMETHODIMP CopyTo(IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { return E_NOTIMPL; } STDMETHODIMP Commit(DWORD grfCommitFlags) { return E_NOTIMPL; } STDMETHODIMP Revert(void) { return E_NOTIMPL; } STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return E_NOTIMPL; } STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return E_NOTIMPL; } STDMETHODIMP Stat(STATSTG* pstatstg, DWORD grfStatFlag) { return E_NOTIMPL; } STDMETHODIMP Clone(IStream** ppstm) { return E_NOTIMPL; } private: STDMETHODIMP CRCStream::Read_RCST_COMPRESSED(void* pv, ULONG cb, ULONG* pcbRead); STDMETHODIMP CRCStream::Read_RCST_RAW(void* pv, ULONG cb, ULONG* pcbRead); private: LONG _cRef; HINSTANCE _hinstModule; PBYTE _pbData; ULONG _cbData; WORD _wType; PBYTE _pbPos; // RCST_COMPRESSED // UnCompressed data to this point ULONG _cbUnComp; // Total data to UnCompress ULONG _cbTotalUnComp; BYTE _cbRemain; }; CRCStream::CRCStream(LPCWSTR pcszFileName, int iResID, HRESULT* phres) : _cRef(1) { ASSERT(pcszFileName && *pcszFileName); HRESULT hres = E_FAIL; HINSTANCE _hinstModule = LoadLibraryW(pcszFileName); if (_hinstModule) { HRSRC hRsrc = FindResource(_hinstModule, MAKEINTRESOURCE(iResID), RT_RCDATA); if (hRsrc) { HGLOBAL hLoadedRes = LoadResource(_hinstModule, hRsrc); if (hLoadedRes) { _pbData = (PBYTE)LockResource(hLoadedRes); hres = (_pbData?S_OK:E_OUTOFMEMORY); int cbResData = SizeofResource(_hinstModule, hRsrc); // Extract type WORD* pwType = (WORD*)_pbData; _wType = *pwType; _pbData = (PBYTE)(pwType + 1); switch(_wType) { case RCST_RAW: { // The first DWORD is the actual size of the blob we've put in the resource // this needs to be done because RCDATA takes only WORDs and we may need to // put an odd number of bytes in RCDATA ASSERT(sizeof(ULONG) == sizeof(DWORD)); DWORD* pdwBuf = (DWORD*)_pbData; _cbData = *pdwBuf; _pbData = (PBYTE)(pdwBuf + 1); _pbPos = _pbData; break; } case RCST_COMPRESSED: { ASSERT(sizeof(ULONG) == sizeof(DWORD)); DWORD* pdwBuf = (DWORD*)_pbData; _cbTotalUnComp = *pdwBuf; ++pdwBuf; // Get compress file size _cbData = *pdwBuf; _pbData = (PBYTE)(pdwBuf + 1); _pbPos = _pbData; break; } default: { hres = E_FAIL; break; } } } else hres = E_OUTOFMEMORY; } else hres = E_INVALIDARG; } else hres = E_INVALIDARG; if (phres) *phres = hres; } CRCStream::~CRCStream() { } STDMETHODIMP CRCStream::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CRCStream, IStream), { 0 }, }; return QISearch(this, (LPCQITAB)qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CRCStream::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CRCStream::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDMETHODIMP CRCStream::Read(void* pv, ULONG cb, ULONG* pcbRead) { ASSERT(pv); HRESULT hres = E_FAIL; switch(_wType) { case RCST_RAW: { hres = Read_RCST_RAW(pv, cb, pcbRead); break; } case RCST_COMPRESSED: { hres = Read_RCST_COMPRESSED(pv, cb, pcbRead); break; } default: { break; } } return hres; } STDMETHODIMP CRCStream::Read_RCST_COMPRESSED(void* pv, ULONG cb, ULONG* pcbRead) { HRESULT hres = E_FAIL; ULONG cbCopy = 0; if (pv) { if (cb > 0) { // Is the requested nb of bytes completely fitting in buffer from // actual position? if (cb <= (_cbTotalUnComp - _cbUnComp)) { // Yes cbCopy = cb; } else { // No, is there at least one byte to copy? if (_cbUnComp < _cbTotalUnComp) { // Yes cbCopy = _cbTotalUnComp - _cbUnComp; } else { // No ASSERT(_cbUnComp == _cbTotalUnComp); cbCopy = 0; } } // Is there anything to copy? if (cbCopy > 0) { ULONG cbTmpCopy = cbCopy; PBYTE pbOut = (PBYTE)pv; // Yes, copy it and move the position pointer, if appropriate while (cbTmpCopy) { // Do we have a repeated byte? if (0 != *_pbPos) { // Yes, expand it // e.g.: 0x05, 0x98 // 0x05 : 5 repeated bytes // 0x98 : the byte that needs to be repeated PBYTE pbTmpPos = _pbPos; // Is section already partially expanded? if (!_cbRemain) { // Total number of repeated bytes _cbRemain = *_pbPos; } BYTE cbExpand = 0; // Is the remaining bytes to expand less than/equal to the // requested bytes? if (_cbRemain <= cbTmpCopy) { // Yes copy all the repeated bytes cbExpand = _cbRemain; cbTmpCopy -= cbExpand; _cbRemain = 0; // Next time, go to the next repeated/non-repeated section _pbPos += 2; } else { // No, there will be some remaining // We know cbTmpCopy <= 255, so OK to cast cbExpand = (BYTE)cbTmpCopy; cbTmpCopy = 0; _cbRemain -= cbExpand; } // Do the copy while (cbExpand) { *pbOut = *(pbTmpPos + 1); ++pbOut; --cbExpand; } } else { // No, copy the non-repeated bytes as is // e.g.: 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 // 0x00 : non-repeat bytes flag // 0x05 : there will 5 non repeated bytes // ... : the non-repeated bytes that need to be copied as is PBYTE pbTmpPos = _pbPos + 2; // Is section already partially expanded? if (!_cbRemain) { // No // Total number of repeated bytes _cbRemain = *(_pbPos + 1); } else pbTmpPos += *(_pbPos + 1) - _cbRemain ; BYTE cbExpand = 0; // Is the remaining bytes to expand less than/equal to the // requested bytes? if (_cbRemain <= cbTmpCopy) { // Yes copy all the repeated bytes cbExpand = _cbRemain; cbTmpCopy -= cbExpand; _cbRemain = 0; // Next time, go to the next repeated/non-repeated section _pbPos += 1 + 1 + (*(_pbPos + 1)); } else { // No, there will be some remaining // We know cbTmpCopy <= 255, so OK to cast cbExpand = (BYTE)cbTmpCopy; cbTmpCopy = 0; _cbRemain -= cbExpand; } // Do the copy memcpy(pbOut, pbTmpPos, cbExpand); pbOut += cbExpand; } } } hres = S_OK; _cbUnComp += cbCopy; } else { if (cb == 0) hres = S_OK; else hres = S_FALSE; } } else hres = STG_E_INVALIDPOINTER; if (pcbRead) *pcbRead = cbCopy; return hres; } STDMETHODIMP CRCStream::Read_RCST_RAW(void* pv, ULONG cb, ULONG* pcbRead) { HRESULT hres = E_FAIL; ULONG cbCopy = 0; if (pv) { if (cb > 0) { // Is the requested nb of bytes completely fitting in buffer from // actual position? if ((_pbPos + cb) <= (_pbData + _cbData)) { // Yes cbCopy = cb; } else { // No, is there at least one byte to copy? if (_pbPos < (_pbData + _cbData)) { // Yes cbCopy = (ULONG)((_pbData + _cbData) - _pbPos); } else { // No ASSERT(_pbPos == (_pbData + _cbData)); cbCopy = 0; } } // Is there anything to copy? if (cbCopy > 0) { // Yes, copy it and move the position pointer memcpy(pv, _pbPos, cbCopy); _pbPos += cbCopy; } hres = S_OK; } else { if (cb == 0) hres = S_OK; else hres = S_FALSE; } } else hres = STG_E_INVALIDPOINTER; if (pcbRead) *pcbRead = cbCopy; return hres; } STDAPI SHOpenStreamOnRCFromRegValueW(HKEY hkey, LPCWSTR pszSubKey, LPCWSTR pszValue, IStream** pStream) { ASSERT(pStream); HRESULT hres = E_FAIL; *pStream = NULL; HKEY hOpenedKey = NULL; if (ERROR_SUCCESS == RegOpenKeyExW(hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hOpenedKey)) { // +20 for the comma and the resource id WCHAR szModule[MAX_PATH + 20]; WCHAR* pszExpanded = szModule; DWORD dwModule = ARRAYSIZE(szModule); DWORD dwType = 0; LONG lRes = RegQueryValueExW(hOpenedKey, pszValue, NULL, &dwType, (LPBYTE)szModule, &dwModule); if (ERROR_SUCCESS == lRes) { WCHAR szExpanded[MAX_PATH + 20]; // Do we need to expand Env variable? if (REG_EXPAND_SZ == dwType) { // Yes SHExpandEnvironmentStringsW(szModule, szExpanded, ARRAYSIZE(szExpanded)); pszExpanded = szExpanded; } // pszExpanded should now contain something like: TEXT("C:\\WINNT\\SYSTEM32\\shell32.dll,1234") if (dwRes && pszExpanded && *pszExpanded) { int iResID = PathParseIconLocationW(pszExpanded); // Create the object *pStream = new CRCStream(pszExpanded, iResID, &hres); } } RegCloseKey(hOpenedKey); } return hres; } STDAPI SHOpenStreamOnRCFromRegValueA(HKEY hkey, LPCSTR pszSubKey, LPCSTR pszValue, IStream** pStream) { ASSERT(pStream); *pStream = NULL; HRESULT hres = E_INVALIDARG; if (pszSubKey) { WCHAR wszSubKey[MAX_PATH]; WCHAR wszValue[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, pszSubKey, -1, wszSubKey, SIZECHARS(wszSubKey)); pszSubKey = (LPCSTR)wszSubKey; if (pszValue) { MultiByteToWideChar(CP_ACP, 0, pszValue, -1, wszValue, SIZECHARS(wszValue)); pszValue = (LPCSTR)wszValue; } SHOpenStreamOnRCFromRegValueW(hkey, (LPCWSTR)pszSubKey, (LPCWSTR)pszValue, pStream); } return hres; }