private static Dictionary<string, string> ParseExtendedStrings(byte[] data, int extendedBeginning)
{
const int ExtendedHeaderSize = 10;
if (extendedBeginning + ExtendedHeaderSize >= data.Length)
{
// Exit out as there's no extended information.
return null;
}
// Read in extended counts, and exit out if we got any incorrect info
int extendedBoolCount = ReadInt16(data, extendedBeginning);
int extendedNumberCount = ReadInt16(data, extendedBeginning + 2);
int extendedStringCount = ReadInt16(data, extendedBeginning + 4);
int extendedStringNumOffsets = ReadInt16(data, extendedBeginning + 6);
int extendedStringTableByteSize = ReadInt16(data, extendedBeginning + 8);
if (extendedBoolCount < 0 ||
extendedNumberCount < 0 ||
extendedStringCount < 0 ||
extendedStringNumOffsets < 0 ||
extendedStringTableByteSize < 0)
{
// The extended header contained invalid data. Bail.
return null;
}
// Skip over the extended bools. We don't need them now and can add this in later
// if needed. Also skip over extended numbers, for the same reason.
// Get the location where the extended string offsets begin. These point into
// the extended string table.
int extendedOffsetsStart =
extendedBeginning + // go past the normal data
ExtendedHeaderSize + // and past the extended header
RoundUpToEven(extendedBoolCount) + // and past all of the extended Booleans
(extendedNumberCount * 2); // and past all of the extended numbers
// Get the location where the extended string table begins. This area contains
// null-terminated strings.
int extendedStringTableStart =
extendedOffsetsStart +
(extendedStringCount * 2) + // and past all of the string offsets
((extendedBoolCount + extendedNumberCount + extendedStringCount) * 2); // and past all of the name offsets
// Get the location where the extended string table ends. We shouldn't read past this.
int extendedStringTableEnd =
extendedStringTableStart +
extendedStringTableByteSize;
if (extendedStringTableEnd > data.Length)
{
// We don't have enough data to parse everything. Bail.
return null;
}
// Now we need to parse all of the extended string values. These aren't necessarily
// "in order", meaning the offsets aren't guaranteed to be increasing. Instead, we parse
// the offsets in order, pulling out each string it references and storing them into our
// results list in the order of the offsets.
var values = new List<string>(extendedStringCount);
int lastEnd = 0;
for (int i = 0; i < extendedStringCount; i++)
{
int offset = extendedStringTableStart + ReadInt16(data, extendedOffsetsStart + (i * 2));
if (offset < 0 || offset >= data.Length)
{
// If the offset is invalid, bail.
return null;
}
// Add the string
int end = FindNullTerminator(data, offset);
values.Add(Encoding.ASCII.GetString(data, offset, end - offset));
// Keep track of where the last string ends. The name strings will come after that.
lastEnd = Math.Max(end, lastEnd);
}
// Now parse all of the names.
var names = new List<string>(extendedBoolCount + extendedNumberCount + extendedStringCount);
for (int pos = lastEnd + 1; pos < extendedStringTableEnd; pos++)
{
int end = FindNullTerminator(data, pos);
names.Add(Encoding.ASCII.GetString(data, pos, end - pos));
pos = end;
}
// The names are in order for the Booleans, then the numbers, and then the strings.
// Skip over the bools and numbers, and associate the names with the values.
var extendedStrings = new Dictionary<string, string>(extendedStringCount);
for (int iName = extendedBoolCount + extendedNumberCount, iValue = 0;
iName < names.Count && iValue < values.Count;
iName++, iValue++)
{
extendedStrings.Add(names[iName], values[iValue]);
}
return extendedStrings;
}