public void Check(BinaryFileSchema schema)
{
bool formatfound = false;
foreach (IBfsDataBlock block in schema.DatablockList)
{
bool elsefound = false;
//Check that only one block is declared 'format'.
if (block.IsFormat && formatfound == false)
{
formatfound = true;
schema.FormatBlock = block;
}
else if (block.IsFormat && formatfound == true)
BfsCompiler.ReportError(block.SourceRange,
"Only one 'format' specifier is allowed per schema");
//Check that no two blocks have the same name and build map over types.
if (schema.DataBlocks.ContainsKey(block.Name))
BfsCompiler.ReportError(block.SourceRange,
"Duplicate datablock name not allowed: '" + block.Name + "'");
else
schema.DataBlocks.Add(block.Name, block);
//Build map over local variables
foreach (BfsLocalField localfield in block.LocalFieldList)
if (localfield.Name == "value")
BfsCompiler.ReportError(localfield.SourceRange,
"'value' is a reserved name for use in expressions in consumable types.");
else if (block.LocalFields.ContainsKey(localfield.Name))
BfsCompiler.ReportError(localfield.SourceRange,
"Duplicate local variable name not allowed: '" + localfield.Name + "'");
else
block.LocalFields.Add(localfield.Name, localfield);
//Check that no two struct fields in the same block have the same name.
if (block is IBfsStructType)
{
IBfsStructType structblock = block as IBfsStructType;
foreach (BfsStructField field in structblock.StructFieldList)
{
if (field.Name == "value")
BfsCompiler.ReportError(field.SourceRange,
"'value' is a reserved name for use in expressions in consumable types.");
else if (structblock.StructFields.ContainsKey(field.Name))
BfsCompiler.ReportError(field.SourceRange,
"Duplicate field name not allowed: '" + field.Name + "'");
else
structblock.StructFields.Add(field.Name, field);
}
//Check that no local variables have the same name
foreach (BfsStructField field in structblock.StructFieldList)
if (structblock.LocalFields.ContainsKey(field.Name))
BfsCompiler.ReportError(field.SourceRange, "Name clash with local variable: '" + field.Name + "'");
//Check that only supported compressions methods are defined
if (structblock.CompressionMethod != null)
if(structblock.CompressionMethod != "GZip" && structblock.CompressionMethod != "Deflate")
BfsCompiler.ReportError(structblock.CompressionRange,
"Unknown compression method: '" + structblock.CompressionMethod + "'. Expected 'GZip' or 'Deflate'.");
}
if (block is BfsEnum)
{
BfsEnum enumblock = block as BfsEnum;
enumblock.SizeInBytes = GetSizeOfPrimitiveType(enumblock.PrimitiveType);
HashSet<long> enumvalues = new HashSet<long>();
HashSet<BfsEnumRange> enumranges = new HashSet<BfsEnumRange>();
foreach (BfsEnumField field in enumblock.EnumFields)
{
//Check that there is only one 'else'.
bool isElse = (field.EnumMatch is BfsEnumElse);
if (isElse && elsefound == false)
elsefound = true;
else if (isElse && elsefound == true)
BfsCompiler.ReportError(field.SourceRange, "Only one 'else' event allowed");
//Building map from name to enum field aliases
if (field.Alias != null && !enumblock.EnumAliases.ContainsKey(field.Alias))
{
BfsEnumFieldAlias alias = new BfsEnumFieldAlias();
alias.Name = field.Alias;
enumblock.EnumAliases.Add(field.Alias, alias);
}
//Check that no numbers or ranges intersect.
if (field.EnumMatch is BfsEnumValue)
{
BfsEnumValue e_val = (field.EnumMatch as BfsEnumValue);
foreach (int val in enumvalues)
if (e_val.Value == val)
BfsCompiler.ReportError(field.SourceRange, "Value already defined: '" + val + "'");
foreach (BfsEnumRange range in enumranges)
CheckValueOnRange(e_val.Value, range, field);
enumvalues.Add(e_val.Value);
}
if (field.EnumMatch is BfsEnumRange)
{
BfsEnumRange range = field.EnumMatch as BfsEnumRange;
if (range.EndValue <= range.StartValue)
BfsCompiler.ReportError(range.SourceRange, "End-value must be larger than start-value in the range");
long actualRange = range.EndValue - range.StartValue + 1;
if (range.StartInclusion == BfsInclusionEnum.Excluded)
actualRange--;
if (range.EndInclusion == BfsInclusionEnum.Excluded)
actualRange--;
if (actualRange == 0)
BfsCompiler.ReportError(range.SourceRange, "Range is empty because of the inclusions brackets: '" + range + "'");
if (actualRange == 1)
BfsCompiler.ReportWarning(range.SourceRange, "Range is of length 1, why not use a single value instead?: '" + range + "'");
foreach (int val in enumvalues)
CheckValueOnRange(val, range, field);
foreach (BfsEnumRange rb in enumranges)
CheckRangeOnRange(range, rb, field);
enumranges.Add(range);
}
}
}
HashSet<long> bits = new HashSet<long>();
HashSet<string> bitnames = new HashSet<string>();
if (block is BfsBitfield)
{
BfsBitfield bitfield = block as BfsBitfield;
bitfield.SizeInBytes = GetSizeOfPrimitiveType(bitfield.PrimitiveType);
foreach (BfsBitfieldField field in bitfield.BitFieldFields)
{
//Check that no bit-indexes are listed twice.
if (bits.Contains(field.BitNumber))
BfsCompiler.ReportError(field.SourceRange, "Bit may only be listed once: '" + field.BitNumber + "'");
else
bits.Add(field.BitNumber);
//Check that no bit-names are listed twice.
if (field.Name != null && bitnames.Contains(field.Name))
BfsCompiler.ReportError(field.SourceRange, "Two identical bitnames found: '" + field.Name + "'");
else
if(field.Name != null)
bitnames.Add(field.Name);
//Check that the bit-indexes doesn't exeed the size of the primitive type.
long sizeoftype = GetSizeOfPrimitiveType(bitfield.PrimitiveType) * 8 - 1;
if (field.BitNumber > sizeoftype)
BfsCompiler.ReportError(field.SourceRange,
"Bit-index exceeds the boundary of the primitive type: '" + field.BitNumber + "' > " + sizeoftype);
}
}
}
if (schema.FormatBlock == null)
BfsCompiler.ReportError("No 'format' block specified. Schema needs an entry point.");
}