Skip to content

Commit

Permalink
Rework the writing of DictionaryProperty
Browse files Browse the repository at this point in the history
  • Loading branch information
Numpsy committed Feb 25, 2024
1 parent 0bc739c commit 1fba379
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 8 deletions.
5 changes: 5 additions & 0 deletions sources/OpenMcdf.Extensions/OLEProperties/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ public enum PropertyType
{
TypedPropertyValue = 0, DictionaryProperty = 1
}

internal static class CodePages
{
public const int CP_WINUNICODE = 0x04B0;
}
}
4 changes: 1 addition & 3 deletions sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ namespace OpenMcdf.Extensions.OLEProperties
{
public class DictionaryEntry
{
private const int CP_WINUNICODE = 0x04B0;

int codePage;

public DictionaryEntry(int codePage)
Expand All @@ -27,7 +25,7 @@ public void Read(BinaryReader br)
PropertyIdentifier = br.ReadUInt32();
Length = br.ReadInt32();

if (codePage != CP_WINUNICODE)
if (codePage != CodePages.CP_WINUNICODE)
{
nameBytes = br.ReadBytes(Length);
}
Expand Down
76 changes: 71 additions & 5 deletions sources/OpenMcdf.Extensions/OLEProperties/DictionaryProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,85 @@ public void Read(BinaryReader br)

}

/// <summary>
/// Write the dictionary and all its values into the specified <see cref="BinaryWriter"/>.
/// </summary>
/// <remarks>
/// Based on the Microsoft specifications at https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oleps/99127b7f-c440-4697-91a4-c853086d6b33
/// </remarks>
/// <param name="bw">A writer to write the dictionary into.</param>
public void Write(BinaryWriter bw)
{
long curPos = bw.BaseStream.Position;

bw.Write(entries.Count);

foreach (KeyValuePair<uint, string> kv in entries)
{
bw.Write(kv.Key);
string s = kv.Value;
if (!s.EndsWith("\0"))
s += "\0";
bw.Write(Encoding.GetEncoding(this.codePage).GetBytes(s));
WriteEntry(bw, kv.Key, kv.Value);
}

var size = (int)(bw.BaseStream.Position - curPos);
WritePaddingIfNeeded(bw, size);
}

// Write a single entry to the dictionary, and handle and required null termination and padding.
private void WriteEntry(BinaryWriter bw, uint propertyIdentifier, string name)
{
// Write the PropertyIdentifier
bw.Write(propertyIdentifier);

// Encode string data with the current codepage
var nameBytes = Encoding.GetEncoding(this.codePage).GetBytes(name);
uint byteLength = (uint)nameBytes.Length;

// If the code page is WINUNICODE, write the length as the number of characters and pad the length to a multiple of 4 bytes
// Otherwise, write the length as the number of bytes and don't pad.
// In either case, the string must be null terminated
if (codePage == CodePages.CP_WINUNICODE)
{
bool addNullTerminator =
byteLength == 0 || nameBytes[byteLength - 1] != '\0' || nameBytes[byteLength - 2] != '\0';

if (addNullTerminator)
byteLength += 2;

bw.Write((uint)byteLength / 2);
bw.Write(nameBytes);

if (addNullTerminator)
{
bw.Write((byte)0);
bw.Write((byte)0);
}

WritePaddingIfNeeded(bw, (int)byteLength);
}
else
{
bool addNullTerminator =
byteLength == 0 || nameBytes[byteLength - 1] != '\0';

if (addNullTerminator)
byteLength += 1;

bw.Write(byteLength);
bw.Write(nameBytes);

if (addNullTerminator)
bw.Write((byte)0);
}
}

// Write as much padding as needed to pad fieldLength to a multiple of 4 bytes
private void WritePaddingIfNeeded(BinaryWriter bw, int fieldLength)
{
var m = fieldLength % 4;

if (m > 0)
for (int i = 0; i < 4 - m; i++) // padding
bw.Write((byte)0);
}
}
}

0 comments on commit 1fba379

Please sign in to comment.