summaryrefslogtreecommitdiff
path: root/src/vm/assemblynativeresource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/assemblynativeresource.cpp')
-rw-r--r--src/vm/assemblynativeresource.cpp585
1 files changed, 585 insertions, 0 deletions
diff --git a/src/vm/assemblynativeresource.cpp b/src/vm/assemblynativeresource.cpp
new file mode 100644
index 0000000000..40b1260ec9
--- /dev/null
+++ b/src/vm/assemblynativeresource.cpp
@@ -0,0 +1,585 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+////////////////////////////////////////////////////////////////////////////////
+// ResFile.CPP
+
+
+
+#include "common.h"
+
+#include "assemblynativeresource.h"
+#include <limits.h>
+
+#ifndef CP_WINUNICODE
+ #define CP_WINUNICODE 1200
+#endif
+
+#ifndef MAKEINTRESOURCE
+ #define MAKEINTRESOURCE MAKEINTRESOURCEW
+#endif
+
+Win32Res::Win32Res()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ m_szFile = NULL;
+ m_Icon = NULL;
+ int i;
+ for (i = 0; i < NUM_VALUES; i++)
+ m_Values[i] = NULL;
+ for (i = 0; i < NUM_VALUES; i++)
+ m_Values[i] = NULL;
+ m_fDll = false;
+ m_pData = NULL;
+ m_pCur = NULL;
+ m_pEnd = NULL;
+}
+
+Win32Res::~Win32Res()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ m_szFile = NULL;
+ m_Icon = NULL;
+ int i;
+ for (i = 0; i < NUM_VALUES; i++)
+ m_Values[i] = NULL;
+ for (i = 0; i < NUM_VALUES; i++)
+ m_Values[i] = NULL;
+ m_fDll = false;
+ if (m_pData)
+ delete [] m_pData;
+ m_pData = NULL;
+ m_pCur = NULL;
+
+ m_pEnd = NULL;
+}
+
+//*****************************************************************************
+// Initializes the structures with version information.
+//*****************************************************************************
+VOID Win32Res::SetInfo(
+ LPCWSTR szFile,
+ LPCWSTR szTitle,
+ LPCWSTR szIconName,
+ LPCWSTR szDescription,
+ LPCWSTR szCopyright,
+ LPCWSTR szTrademark,
+ LPCWSTR szCompany,
+ LPCWSTR szProduct,
+ LPCWSTR szProductVersion,
+ LPCWSTR szFileVersion,
+ LCID lcid,
+ BOOL fDLL)
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(szFile != NULL);
+
+ m_szFile = szFile;
+ if (szIconName && szIconName[0] != 0)
+ m_Icon = szIconName; // a non-mepty string
+
+#define NonNull(sz) (sz == NULL || *sz == W('\0') ? W(" ") : sz)
+ m_Values[v_Description] = NonNull(szDescription);
+ m_Values[v_Title] = NonNull(szTitle);
+ m_Values[v_Copyright] = NonNull(szCopyright);
+ m_Values[v_Trademark] = NonNull(szTrademark);
+ m_Values[v_Product] = NonNull(szProduct);
+ m_Values[v_ProductVersion] = NonNull(szProductVersion);
+ m_Values[v_Company] = NonNull(szCompany);
+ m_Values[v_FileVersion] = NonNull(szFileVersion);
+#undef NonNull
+
+ m_fDll = fDLL;
+ m_lcid = lcid;
+}
+
+VOID Win32Res::MakeResFile(const void **pData, DWORD *pcbData)
+{
+ STANDARD_VM_CONTRACT;
+
+ static const RESOURCEHEADER magic = { 0x00000000, 0x00000020, 0xFFFF, 0x0000, 0xFFFF, 0x0000,
+ 0x00000000, 0x0000, 0x0000, 0x00000000, 0x00000000 };
+ _ASSERTE(pData != NULL && pcbData != NULL);
+
+ *pData = NULL;
+ *pcbData = 0;
+ m_pData = new BYTE[(sizeof(RESOURCEHEADER) * 3 + sizeof(EXEVERRESOURCE))];
+
+ m_pCur = m_pData;
+ m_pEnd = m_pData + sizeof(RESOURCEHEADER) * 3 + sizeof(EXEVERRESOURCE);
+
+ // inject the magic empty entry
+ Write( &magic, sizeof(magic) );
+
+ WriteVerResource();
+
+ if (m_Icon)
+ {
+ WriteIconResource();
+ }
+
+ *pData = m_pData;
+ *pcbData = (DWORD)(m_pCur - m_pData);
+ return;
+}
+
+
+/*
+ * WriteIconResource
+ * Writes the Icon resource into the RES file.
+ *
+ * RETURNS: TRUE on succes, FALSE on failure (errors reported to user)
+ */
+VOID Win32Res::WriteIconResource()
+{
+ STANDARD_VM_CONTRACT;
+
+ HandleHolder hIconFile = INVALID_HANDLE_VALUE;
+ WORD wTemp, wCount, resID = 2; // Skip 1 for the version ID
+ DWORD dwRead = 0, dwWritten = 0;
+
+ RESOURCEHEADER grpHeader = { 0x00000000, 0x00000020, 0xFFFF, (WORD)(size_t)RT_GROUP_ICON, 0xFFFF, 0x7F00, // 0x7F00 == IDI_APPLICATION
+ 0x00000000, 0x1030, 0x0000, 0x00000000, 0x00000000 };
+
+ // Read the icon
+ hIconFile = WszCreateFile( m_Icon, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (hIconFile == INVALID_HANDLE_VALUE) {
+ COMPlusThrowWin32();
+ }
+
+ // Read the magic reserved WORD
+ if (ReadFile( hIconFile, &wTemp, sizeof(WORD), &dwRead, NULL) == FALSE) {
+ COMPlusThrowWin32();
+ } else if (wTemp != 0 || dwRead != sizeof(WORD)) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+ }
+
+ // Verify the Type WORD
+ if (ReadFile( hIconFile, &wCount, sizeof(WORD), &dwRead, NULL) == FALSE) {
+ COMPlusThrowWin32();
+ } else if (wCount != 1 || dwRead != sizeof(WORD)) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+ }
+
+ // Read the Count WORD
+ if (ReadFile( hIconFile, &wCount, sizeof(WORD), &dwRead, NULL) == FALSE) {
+ COMPlusThrowWin32();
+ } else if (wCount == 0 || dwRead != sizeof(WORD)) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+ }
+
+ NewArrayHolder<ICONRESDIR> grp = new ICONRESDIR[wCount];
+ grpHeader.DataSize = 3 * sizeof(WORD) + wCount * sizeof(ICONRESDIR);
+
+ // For each Icon
+ for (WORD i = 0; i < wCount; i++) {
+ ICONDIRENTRY ico;
+ DWORD icoPos, newPos;
+ RESOURCEHEADER icoHeader = { 0x00000000, 0x00000020, 0xFFFF, (WORD)(size_t)RT_ICON, 0xFFFF, 0x0000,
+ 0x00000000, 0x1010, 0x0000, 0x00000000, 0x00000000 };
+ icoHeader.Name = resID++;
+
+ // Read the Icon header
+ if (ReadFile( hIconFile, &ico, sizeof(ICONDIRENTRY), &dwRead, NULL) == FALSE) {
+ COMPlusThrowWin32();
+ }
+ else if (dwRead != sizeof(ICONDIRENTRY)) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+ }
+
+ _ASSERTE(sizeof(ICONRESDIR) + sizeof(WORD) == sizeof(ICONDIRENTRY));
+ memcpy(grp + i, &ico, sizeof(ICONRESDIR));
+ grp[i].IconId = icoHeader.Name;
+ icoHeader.DataSize = ico.dwBytesInRes;
+
+ NewArrayHolder<BYTE> icoBuffer = new BYTE[icoHeader.DataSize];
+
+ // Write the header to the RES file
+ Write( &icoHeader, sizeof(RESOURCEHEADER) );
+
+ // Position to read the Icon data
+ icoPos = SetFilePointer( hIconFile, 0, NULL, FILE_CURRENT);
+ if (icoPos == INVALID_SET_FILE_POINTER) {
+ COMPlusThrowWin32();
+ }
+ newPos = SetFilePointer( hIconFile, ico.dwImageOffset, NULL, FILE_BEGIN);
+ if (newPos == INVALID_SET_FILE_POINTER) {
+ COMPlusThrowWin32();
+ }
+
+ // Actually read the data
+ if (ReadFile( hIconFile, icoBuffer, icoHeader.DataSize, &dwRead, NULL) == FALSE) {
+ COMPlusThrowWin32();
+ }
+ else if (dwRead != icoHeader.DataSize) {
+ COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+ }
+
+ // Because Icon files don't seem to record the actual Planes and BitCount in
+ // the ICONDIRENTRY, get the info from the BITMAPINFOHEADER at the beginning
+ // of the data here:
+ grp[i].Planes = ((BITMAPINFOHEADER*)(BYTE*)icoBuffer)->biPlanes;
+ grp[i].BitCount = ((BITMAPINFOHEADER*)(BYTE*)icoBuffer)->biBitCount;
+
+ // Now write the data to the RES file
+ Write( (BYTE*)icoBuffer, icoHeader.DataSize );
+
+ // Reposition to read the next Icon header
+ newPos = SetFilePointer( hIconFile, icoPos, NULL, FILE_BEGIN);
+ if (newPos != icoPos) {
+ COMPlusThrowWin32();
+ }
+ }
+
+ // inject the icon group
+ Write( &grpHeader, sizeof(RESOURCEHEADER) );
+
+ // Write the header to the RES file
+ wTemp = 0; // the reserved WORD
+ Write( &wTemp, sizeof(WORD) );
+
+ wTemp = RES_ICON; // the GROUP type
+ Write( &wTemp, sizeof(WORD) );
+
+ Write( &wCount, sizeof(WORD) );
+
+ // now write the entries
+ Write( grp, sizeof(ICONRESDIR) * wCount );
+
+ return;
+}
+
+/*
+ * WriteVerResource
+ * Writes the version resource into the RES file.
+ *
+ * RETURNS: TRUE on succes, FALSE on failure (errors reported to user)
+ */
+VOID Win32Res::WriteVerResource()
+{
+ STANDARD_VM_CONTRACT;
+
+ WCHAR szLangCp[9]; // language/codepage string.
+ EXEVERRESOURCE VerResource;
+ WORD cbStringBlocks;
+ int i;
+ bool bUseFileVer = false;
+ WCHAR rcFile[_MAX_PATH] = {0}; // Name of file without path
+ WCHAR rcFileExtension[_MAX_PATH] = {0}; // file extension
+ WCHAR rcFileName[_MAX_PATH]; // Name of file with extension but without path
+ DWORD cbTmp;
+
+ SplitPath(m_szFile, 0, 0, 0, 0, rcFile, _MAX_PATH, rcFileExtension, _MAX_PATH);
+
+ wcscpy_s(rcFileName, COUNTOF(rcFileName), rcFile);
+ wcscat_s(rcFileName, COUNTOF(rcFileName), rcFileExtension);
+
+ static const EXEVERRESOURCE VerResourceTemplate = {
+ sizeof(EXEVERRESOURCE), sizeof(VS_FIXEDFILEINFO), 0, W("VS_VERSION_INFO"),
+ {
+ VS_FFI_SIGNATURE, // Signature
+ VS_FFI_STRUCVERSION, // structure version
+ 0, 0, // file version number
+ 0, 0, // product version number
+ VS_FFI_FILEFLAGSMASK, // file flags mask
+ 0, // file flags
+ VOS__WINDOWS32,
+ VFT_APP, // file type
+ 0, // subtype
+ 0, 0 // file date/time
+ },
+ sizeof(WORD) * 2 + 2 * HDRSIZE + KEYBYTES("VarFileInfo") + KEYBYTES("Translation"),
+ 0,
+ 1,
+ W("VarFileInfo"),
+ sizeof(WORD) * 2 + HDRSIZE + KEYBYTES("Translation"),
+ sizeof(WORD) * 2,
+ 0,
+ W("Translation"),
+ 0,
+ 0,
+ 2 * HDRSIZE + KEYBYTES("StringFileInfo") + KEYBYTES("12345678"),
+ 0,
+ 1,
+ W("StringFileInfo"),
+ HDRSIZE + KEYBYTES("12345678"),
+ 0,
+ 1,
+ W("12345678")
+ };
+ static const WCHAR szComments[] = W("Comments");
+ static const WCHAR szCompanyName[] = W("CompanyName");
+ static const WCHAR szFileDescription[] = W("FileDescription");
+ static const WCHAR szCopyright[] = W("LegalCopyright");
+ static const WCHAR szTrademark[] = W("LegalTrademarks");
+ static const WCHAR szProdName[] = W("ProductName");
+ static const WCHAR szFileVerResName[] = W("FileVersion");
+ static const WCHAR szProdVerResName[] = W("ProductVersion");
+ static const WCHAR szInternalNameResName[] = W("InternalName");
+ static const WCHAR szOriginalNameResName[] = W("OriginalFilename");
+
+ // If there's no product version, use the file version
+ if (m_Values[v_ProductVersion][0] == 0) {
+ m_Values[v_ProductVersion] = m_Values[v_FileVersion];
+ bUseFileVer = true;
+ }
+
+ // Keep the two following arrays in the same order
+#define MAX_KEY 10
+ static const LPCWSTR szKeys [MAX_KEY] = {
+ szComments,
+ szCompanyName,
+ szFileDescription,
+ szFileVerResName,
+ szInternalNameResName,
+ szCopyright,
+ szTrademark,
+ szOriginalNameResName,
+ szProdName,
+ szProdVerResName,
+ };
+ LPCWSTR szValues [MAX_KEY] = { // values for keys
+ m_Values[v_Description], //compiler->assemblyDescription == NULL ? W("") : compiler->assemblyDescription,
+ m_Values[v_Company], // Company Name
+ m_Values[v_Title], // FileDescription //compiler->assemblyTitle == NULL ? W("") : compiler->assemblyTitle,
+ m_Values[v_FileVersion], // FileVersion
+ rcFileName, // InternalName
+ m_Values[v_Copyright], // Copyright
+ m_Values[v_Trademark], // Trademark
+ rcFileName, // OriginalName
+ m_Values[v_Product], // Product Name //compiler->assemblyTitle == NULL ? W("") : compiler->assemblyTitle,
+ m_Values[v_ProductVersion] // Product Version
+ };
+
+ memcpy(&VerResource, &VerResourceTemplate, sizeof(VerResource));
+
+ if (m_fDll)
+ VerResource.vsFixed.dwFileType = VFT_DLL;
+ else
+ VerResource.vsFixed.dwFileType = VFT_APP;
+
+ // Extract the numeric version from the string.
+ m_Version[0] = m_Version[1] = m_Version[2] = m_Version[3] = 0;
+ int nNumStrings = swscanf_s(m_Values[v_FileVersion], W("%hu.%hu.%hu.%hu"), m_Version, m_Version + 1, m_Version + 2, m_Version + 3);
+
+ // Fill in the FIXEDFILEINFO
+ VerResource.vsFixed.dwFileVersionMS =
+ ((DWORD)m_Version[0] << 16) + m_Version[1];
+
+ VerResource.vsFixed.dwFileVersionLS =
+ ((DWORD)m_Version[2] << 16) + m_Version[3];
+
+ if (bUseFileVer) {
+ VerResource.vsFixed.dwProductVersionLS = VerResource.vsFixed.dwFileVersionLS;
+ VerResource.vsFixed.dwProductVersionMS = VerResource.vsFixed.dwFileVersionMS;
+ }
+ else {
+ WORD v[4];
+ v[0] = v[1] = v[2] = v[3] = 0;
+ // Try to get the version numbers, but don't waste time or give any errors
+ // just default to zeros
+ nNumStrings = swscanf_s(m_Values[v_ProductVersion], W("%hu.%hu.%hu.%hu"), v, v + 1, v + 2, v + 3);
+
+ VerResource.vsFixed.dwProductVersionMS =
+ ((DWORD)v[0] << 16) + v[1];
+
+ VerResource.vsFixed.dwProductVersionLS =
+ ((DWORD)v[2] << 16) + v[3];
+ }
+
+ // There is no documentation on what units to use for the date! So we use zero.
+ // The Windows resource compiler does too.
+ VerResource.vsFixed.dwFileDateMS = VerResource.vsFixed.dwFileDateLS = 0;
+
+ // Fill in codepage/language -- we'll assume the IDE language/codepage
+ // is the right one.
+ if (m_lcid != -1)
+ VerResource.langid = static_cast<WORD>(m_lcid);
+ else
+ VerResource.langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
+ VerResource.codepage = CP_WINUNICODE; // Unicode codepage.
+
+ swprintf_s(szLangCp, NumItems(szLangCp), W("%04x%04x"), VerResource.langid, VerResource.codepage);
+ wcscpy_s(VerResource.szLangCpKey, COUNTOF(VerResource.szLangCpKey), szLangCp);
+
+ // Determine the size of all the string blocks.
+ cbStringBlocks = 0;
+ for (i = 0; i < MAX_KEY; i++) {
+ if (szValues[i] == NULL || wcslen(szValues[i]) == 0)
+ continue;
+ cbTmp = SizeofVerString( szKeys[i], szValues[i]);
+ if ((cbStringBlocks + cbTmp) > USHRT_MAX / 2)
+ COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
+ cbStringBlocks += (WORD) cbTmp;
+ }
+
+ if ((cbStringBlocks + VerResource.cbLangCpBlock) > USHRT_MAX / 2)
+ COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
+ VerResource.cbLangCpBlock += cbStringBlocks;
+
+ if ((cbStringBlocks + VerResource.cbStringBlock) > USHRT_MAX / 2)
+ COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
+ VerResource.cbStringBlock += cbStringBlocks;
+
+ if ((cbStringBlocks + VerResource.cbRootBlock) > USHRT_MAX / 2)
+ COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
+ VerResource.cbRootBlock += cbStringBlocks;
+
+ // Call this VS_VERSION_INFO
+ RESOURCEHEADER verHeader = { 0x00000000, 0x0000003C, 0xFFFF, (WORD)(size_t)RT_VERSION, 0xFFFF, 0x0001,
+ 0x00000000, 0x0030, 0x0000, 0x00000000, 0x00000000 };
+ verHeader.DataSize = VerResource.cbRootBlock;
+
+ // Write the header
+ Write( &verHeader, sizeof(RESOURCEHEADER) );
+
+ // Write the version resource
+ Write( &VerResource, sizeof(VerResource) );
+
+
+ // Write each string block.
+ for (i = 0; i < MAX_KEY; i++) {
+ if (szValues[i] == NULL || wcslen(szValues[i]) == 0)
+ continue;
+ WriteVerString( szKeys[i], szValues[i] );
+ }
+#undef MAX_KEY
+
+ return;
+}
+
+/*
+ * SizeofVerString
+ * Determines the size of a version string to the given stream.
+ * RETURNS: size of block in bytes.
+ */
+WORD Win32Res::SizeofVerString(LPCWSTR lpszKey, LPCWSTR lpszValue)
+{
+ STANDARD_VM_CONTRACT;
+
+ size_t cbKey, cbValue;
+
+ cbKey = (wcslen(lpszKey) + 1) * 2; // Make room for the NULL
+ cbValue = (wcslen(lpszValue) + 1) * 2;
+ if (cbValue == 2)
+ cbValue = 4; // Empty strings need a space and NULL terminator (for Win9x)
+ if (cbKey + cbValue >= 0xFFF0)
+ COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities"
+#endif
+
+ return (WORD)(PadKeyLen(cbKey) + // key, 0 padded to DWORD boundary
+ PadValLen(cbValue) + // value, 0 padded to dword boundary
+ HDRSIZE); // block header.
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+}
+
+/*----------------------------------------------------------------------------
+ * WriteVerString
+ * Writes a version string to the given file.
+ */
+VOID Win32Res::WriteVerString( LPCWSTR lpszKey, LPCWSTR lpszValue)
+{
+ STANDARD_VM_CONTRACT;
+
+ size_t cbKey, cbValue, cbBlock;
+ bool bNeedsSpace = false;
+
+ cbKey = (wcslen(lpszKey) + 1) * 2; // includes terminating NUL
+ cbValue = wcslen(lpszValue);
+ if (cbValue > 0)
+ cbValue++; // make room for NULL
+ else {
+ bNeedsSpace = true;
+ cbValue = 2; // Make room for space and NULL (for Win9x)
+ }
+ cbBlock = SizeofVerString(lpszKey, lpszValue);
+
+ NewArrayHolder<BYTE> pbBlock = new BYTE[(DWORD)cbBlock + HDRSIZE];
+ ZeroMemory(pbBlock, (DWORD)cbBlock + HDRSIZE);
+
+ _ASSERTE(cbValue < USHRT_MAX && cbKey < USHRT_MAX && cbBlock < USHRT_MAX);
+
+ // Copy header, key and value to block.
+ *(WORD *)((BYTE *)pbBlock) = (WORD)cbBlock;
+ *(WORD *)(pbBlock + sizeof(WORD)) = (WORD)cbValue;
+ *(WORD *)(pbBlock + 2 * sizeof(WORD)) = 1; // 1 = text value
+ // size = (cbBlock + HDRSIZE - HDRSIZE) / sizeof(WCHAR)
+ wcscpy_s((WCHAR*)(pbBlock + HDRSIZE), (cbBlock / sizeof(WCHAR)), lpszKey);
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities"
+#endif
+
+ if (bNeedsSpace)
+ *((WCHAR*)(pbBlock + (HDRSIZE + PadKeyLen(cbKey)))) = W(' ');
+ else
+ {
+ wcscpy_s((WCHAR*)(pbBlock + (HDRSIZE + PadKeyLen(cbKey))),
+ //size = ((cbBlock + HDRSIZE) - (HDRSIZE + PadKeyLen(cbKey))) / sizeof(WCHAR)
+ (cbBlock - PadKeyLen(cbKey))/sizeof(WCHAR),
+ lpszValue);
+ }
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ // Write block
+ Write( pbBlock, cbBlock);
+
+ return;
+}
+
+VOID Win32Res::Write(LPCVOID pData, size_t len)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (m_pCur + len > m_pEnd) {
+ // Grow
+ size_t newSize = (m_pEnd - m_pData);
+
+ // double the size unless we need more than that
+ if (len > newSize)
+ newSize += len;
+ else
+ newSize *= 2;
+
+ LPBYTE pNew = new BYTE[newSize];
+ memcpy(pNew, m_pData, m_pCur - m_pData);
+ delete [] m_pData;
+ // Relocate the pointers
+ m_pCur = pNew + (m_pCur - m_pData);
+ m_pData = pNew;
+ m_pEnd = pNew + newSize;
+ }
+
+ // Copy it in
+ memcpy(m_pCur, pData, len);
+ m_pCur += len;
+ return;
+}
+