Differentiate between version 0 and version 1 property storages.
diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c
index b24a795..3518b1d 100644
--- a/dlls/ole32/stg_prop.c
+++ b/dlls/ole32/stg_prop.c
@@ -31,14 +31,13 @@
* below, but it gives the best "big picture" that I've found.
*
* TODO:
- * - There are some restrictions I don't honor, like maximum property set
- * size and maximum property name length.
+ * - I don't honor the maximum property set size.
* - Certain bogus files could result in reading past the end of a buffer.
* - Mac-generated files won't be read correctly, even if they're little
* endian, because I disregard whether the generator was a Mac. This means
* strings will probably be munged (as I don't understand Mac scripts.)
* - Not all PROPVARIANT types are supported.
- * There are lots more unimplemented features, see FIXMEs below.
+ * - IPropertyStorage::Enum is unimplemented
*/
#include <assert.h>
@@ -78,6 +77,8 @@
#define CP_UNICODE 1200
+#define MAX_VERSION_0_PROP_NAME_LENGTH 256
+
/* The format version (and what it implies) is described here:
* http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
*/
@@ -158,6 +159,7 @@
BOOL dirty;
FMTID fmtid;
CLSID clsid;
+ WORD format;
DWORD originatorOS;
DWORD grfFlags;
DWORD grfMode;
@@ -342,6 +344,7 @@
return hr;
}
+/* FIXME: the arguments are in a screwy order here, bub. */
static HRESULT PropertyStorage_StringCopy(LPCSTR src, LPSTR *dst, LCID targetCP,
LCID srcCP)
{
@@ -446,6 +449,8 @@
* storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's
* type is VT_LPSTR, converts the string using lcid as the source code page
* and This->codePage as the target code page before storing.
+ * As a side effect, may change This->format to 1 if the type of propvar is
+ * a version 1-only property.
*/
static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
PROPID propid, const PROPVARIANT *propvar, LCID lcid)
@@ -455,6 +460,17 @@
assert(This);
assert(propvar);
+ if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
+ This->format = 1;
+ switch (propvar->vt)
+ {
+ case VT_DECIMAL:
+ case VT_I1:
+ case VT_INT:
+ case VT_UINT:
+ case VT_VECTOR|VT_I1:
+ This->format = 1;
+ }
TRACE("Setting 0x%08lx to type %d\n", propid, propvar->vt);
if (prop)
{
@@ -486,26 +502,38 @@
}
/* Adds the name srcName to the name dictionaries, mapped to property ID id.
+ * srcName is encoded in code page cp, and is converted to This->codePage.
+ * If cp is CP_UNICODE, srcName is actually a unicode string.
+ * As a side effect, may change This->format to 1 if srcName is too long for
+ * a version 0 property storage.
* Doesn't validate id.
*/
-static HRESULT PropertyStorage_StoreNameWithIdW(PropertyStorage_impl *This,
- LPCWSTR srcName, PROPID id)
+static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
+ LPCSTR srcName, LCID cp, PROPID id)
{
LPSTR name;
HRESULT hr;
assert(srcName);
- hr = PropertyStorage_StringCopy((LPCSTR)srcName, &name, This->codePage,
- CP_UNICODE);
+ hr = PropertyStorage_StringCopy((LPCSTR)srcName, &name, This->codePage, cp);
if (SUCCEEDED(hr))
{
+ if (This->codePage == CP_UNICODE)
+ {
+ if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
+ This->format = 1;
+ }
+ else
+ {
+ if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
+ This->format = 1;
+ }
TRACE("Adding prop name %s, propid %ld\n",
This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
debugstr_a(name), id);
dictionary_insert(This->name_to_propid, name, (void *)id);
dictionary_insert(This->propid_to_name, (void *)id, name);
- hr = S_OK;
}
return hr;
}
@@ -556,8 +584,8 @@
{
PROPID nextId = max(propidNameFirst, This->highestProp + 1);
- hr = PropertyStorage_StoreNameWithIdW(This,
- rgpspec[i].u.lpwstr, nextId);
+ hr = PropertyStorage_StoreNameWithId(This,
+ (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
if (SUCCEEDED(hr))
hr = PropertyStorage_StorePropWithId(This, nextId,
&rgpropvar[i], GetACP());
@@ -727,8 +755,8 @@
for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
{
if (rgpropid[i] != PID_ILLEGAL)
- hr = PropertyStorage_StoreNameWithIdW(This, rglpwstrName[i],
- rgpropid[i]);
+ hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
+ CP_UNICODE, rgpropid[i]);
}
if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
IPropertyStorage_Commit(iface, STGC_DEFAULT);
@@ -981,39 +1009,24 @@
{
PROPID propid;
DWORD cbEntry;
- LPSTR name = NULL;
StorageUtl_ReadDWord(ptr, 0, &propid);
ptr += sizeof(PROPID);
StorageUtl_ReadDWord(ptr, 0, &cbEntry);
ptr += sizeof(DWORD);
TRACE("Reading entry with ID 0x%08lx, %ld bytes\n", propid, cbEntry);
+ /* Make sure the source string is NULL-terminated */
if (This->codePage != CP_UNICODE)
- {
- /* Make sure the source string is NULL-terminated */
ptr[cbEntry - 1] = '\0';
- hr = PropertyStorage_StringCopy(ptr, &name, This->codePage,
- This->codePage);
- }
else
- {
- /* Make sure the source string is NULL-terminated */
*((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
- PropertyStorage_ByteSwapString(ptr, cbEntry / sizeof(WCHAR));
- hr = PropertyStorage_StringCopy(ptr, &name, This->codePage,
- This->codePage);
+ hr = PropertyStorage_StoreNameWithId(This, ptr, This->codePage, propid);
+ if (This->codePage == CP_UNICODE)
+ {
/* Unicode entries are padded to DWORD boundaries */
if (cbEntry % sizeof(DWORD))
ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
}
- if (name)
- {
- dictionary_insert(This->name_to_propid, name, (void *)propid);
- dictionary_insert(This->propid_to_name, (void *)propid, name);
- TRACE("Property %s maps to id %ld\n",
- This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
- debugstr_a(name), propid);
- }
ptr += sizeof(DWORD) + cbEntry;
}
return hr;
@@ -1022,7 +1035,8 @@
/* FIXME: there isn't any checking whether the read property extends past the
* end of the buffer.
*/
-static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data)
+static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
+ PROPVARIANT *prop, const BYTE *data)
{
HRESULT hr = S_OK;
@@ -1051,10 +1065,12 @@
StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
TRACE("Read ushort %d\n", prop->u.uiVal);
break;
+ case VT_INT:
case VT_I4:
StorageUtl_ReadDWord(data, 0, &prop->u.lVal);
TRACE("Read long %ld\n", prop->u.lVal);
break;
+ case VT_UINT:
case VT_UI4:
StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
TRACE("Read ulong %ld\n", prop->u.ulVal);
@@ -1064,20 +1080,37 @@
DWORD count;
StorageUtl_ReadDWord(data, 0, &count);
- prop->u.pszVal = CoTaskMemAlloc(count);
- if (prop->u.pszVal)
+ if (This->codePage == CP_UNICODE && count / 2)
{
- memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
- /* This is stored in the code page specified in This->codePage.
- * Don't convert it, the caller will just store it as-is.
- * (Note the trace will be misleading if the code page is Unicode.)
- * But make sure it's NULL-terminated:
- */
- prop->u.pszVal[count - 1] = '\0';
- TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
+ WARN("Unicode string has odd number of bytes\n");
+ hr = STG_E_INVALIDHEADER;
}
else
- hr = STG_E_INSUFFICIENTMEMORY;
+ {
+ prop->u.pszVal = CoTaskMemAlloc(count);
+ if (prop->u.pszVal)
+ {
+ memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
+ /* This is stored in the code page specified in This->codePage.
+ * Don't convert it, the caller will just store it as-is.
+ */
+ if (This->codePage == CP_UNICODE)
+ {
+ /* Make sure it's NULL-terminated */
+ prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
+ TRACE("Read string value %s\n",
+ debugstr_w(prop->u.pwszVal));
+ }
+ else
+ {
+ /* Make sure it's NULL-terminated */
+ prop->u.pszVal[count - 1] = '\0';
+ TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
+ }
+ }
+ else
+ hr = STG_E_INSUFFICIENTMEMORY;
+ }
break;
}
case VT_LPWSTR:
@@ -1262,6 +1295,7 @@
hr = STG_E_INVALIDHEADER;
goto end;
}
+ This->format = hdr.wFormat;
memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
This->originatorOS = hdr.dwOSVer;
if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
@@ -1333,7 +1367,7 @@
{
PROPVARIANT prop;
- if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
+ if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
{
TRACE("Read property with ID 0x%08lx, type %d\n",
@@ -1351,6 +1385,8 @@
case PID_BEHAVIOR:
if (prop.vt == VT_I4 && prop.u.lVal)
This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
+ /* The format should already be 1, but just in case */
+ This->format = 1;
break;
default:
hr = PropertyStorage_StorePropWithId(This,
@@ -1398,11 +1434,7 @@
assert(hdr);
StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
PROPSETHDR_BYTEORDER_MAGIC);
- /* FIXME: should be able to write format 0 property sets too, depending
- * on whether I have too long string names or if case-sensitivity is set.
- * For now always write format 1.
- */
- StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, 1);
+ StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
@@ -1971,7 +2003,10 @@
hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
if (SUCCEEDED(hr))
{
+ ps->format = 0;
ps->grfFlags = grfFlags;
+ if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
+ ps->format = 1;
/* default to Unicode unless told not to, as specified here:
* http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
*/