Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework the writing of DictionaryProperty #107

Merged
merged 1 commit into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}