internal override void InternalWrite(BinaryWriter writer) { int posFileStart = (int)writer.BaseStream.Position; // seek past the header, type table and code labels writer.BaseStream.Seek(DATA_START + (_procedures.Length * BFCodeLabel.SIZE) + (_jumpLabels.Length * BFCodeLabel.SIZE), SeekOrigin.Current); // create code label type table entries int procsLength = _procedures.Length * BFCodeLabel.SIZE; int jumpsLength = _jumpLabels.Length * BFCodeLabel.SIZE; int opCodeDataStart = (int)(writer.BaseStream.Position - posFileStart); int numProcsSet = 0; int numJumpsSet = 0; for (int i = 0; i < _opcodes.Count; i++) { int procIdx = -1; // try to find a procedure with this index if (numProcsSet != _procedures.Length) { procIdx = Array.FindIndex(_procedures, p => p.OpcodeIndex == i); if (procIdx != -1) { _procedures[procIdx].Offset = (uint)(((writer.BaseStream.Position - posFileStart) - opCodeDataStart) / 4); numProcsSet++; } } // if we haven't found a procedure, try to find a jump label with this index if (procIdx == -1 && numJumpsSet != _jumpLabels.Length) { int jumpIdx = Array.FindIndex(_jumpLabels, j => j.OpcodeIndex == i); if (jumpIdx != -1) { _jumpLabels[jumpIdx].Offset = (uint)(((writer.BaseStream.Position - posFileStart) - opCodeDataStart) / 4); numJumpsSet++; } } // write the opcode data writer.Write((ushort)_opcodes[i].Instruction); if (_opcodes[i].Operand != null) { switch (_opcodes[i].Operand.Type) { case BFOperandType.Immediate: if (_opcodes[i].Instruction == BFInstruction.PushUInt32) { writer.Write((ushort)0); writer.Write((uint)_opcodes[i].Operand.ImmediateValue); } else { writer.Write((ushort)_opcodes[i].Operand.ImmediateValue); } break; case BFOperandType.FloatingPoint: writer.Write((ushort)0); writer.Write((float)_opcodes[i].Operand.FloatValue); break; } } else { writer.Write((ushort)0); } } int opCodeDataEnd = (int)(writer.BaseStream.Position - posFileStart); // set type table entries TypeTableEntry[] typeTableEntries = new TypeTableEntry[TYPE_TABLE_COUNT]; typeTableEntries[(int)TypeTableType.Procedures] = CreateTypeTableEntry(TypeTableType.Procedures, procsLength, DATA_START); typeTableEntries[(int)TypeTableType.JumpLabels] = CreateTypeTableEntry(TypeTableType.JumpLabels, jumpsLength, DATA_START + procsLength); typeTableEntries[(int)TypeTableType.Opcodes] = CreateTypeTableEntry(TypeTableType.Opcodes, opCodeDataEnd - opCodeDataStart, opCodeDataStart); typeTableEntries[(int)TypeTableType.Messages] = CreateTypeTableEntry(TypeTableType.Messages, 0, opCodeDataEnd); typeTableEntries[(int)TypeTableType.Strings] = CreateTypeTableEntry(TypeTableType.Strings, 0xF0, opCodeDataEnd); if (_messageFile != null) { _messageFile.InternalWrite(writer); int messageDataEnd = (int)writer.BaseStream.Position - posFileStart; int messageDataSize = messageDataEnd - opCodeDataEnd; typeTableEntries[(int)TypeTableType.Strings].dataOffset += messageDataSize; typeTableEntries[(int)TypeTableType.Messages] = CreateTypeTableEntry(TypeTableType.Messages, messageDataSize, opCodeDataEnd); } // TODO: add code for the 'string' table here? haven't really seen it being used though. // fixed size zero bytes for the strings table when unused writer.Write(0, 0xF0); long posFileEnd = writer.BaseStream.Position; int length = (int)posFileEnd - posFileStart; writer.BaseStream.Seek(posFileStart, SeekOrigin.Begin); // write standard header writer.Write(TYPE); writer.Write((short)0); // userID writer.Write(length); writer.WriteCString(TAG, 4); writer.Write(0); // unused // write bf header writer.Write(typeTableEntries.Length); writer.Write(0); // some unknown value here, not sure what its for writer.AlignPosition(16); // write type table entries foreach (TypeTableEntry entry in typeTableEntries) { entry.InternalWrite(writer); } // lastly, write the code labels foreach (BFCodeLabel label in _procedures) { label.InternalWrite(writer); } foreach (BFCodeLabel label in _jumpLabels) { label.InternalWrite(writer); } }
private void InternalRead(BinaryReader reader) { long posFileStart = reader.GetPosition(); short flag = reader.ReadInt16(); short userID = reader.ReadInt16(); int length = reader.ReadInt32(); string tag = reader.ReadCString(4); int unused = reader.ReadInt32(); if (tag != TAG) { throw new InvalidDataException("Identifier mismatch."); } int numTypeTableEntries = reader.ReadInt32(); int numUnknown = reader.ReadInt32(); reader.AlignPosition(16); TypeTableEntry[] typeTable = new TypeTableEntry[numTypeTableEntries]; for (int i = 0; i < numTypeTableEntries; i++) { typeTable[i] = new TypeTableEntry(reader); } System.Diagnostics.Debug.Assert(typeTable[(int)TypeTableType.Strings].elementCount == 0xF0); for (int i = 0; i < numTypeTableEntries; i++) { reader.Seek(posFileStart + typeTable[i].dataOffset, SeekOrigin.Begin); switch ((TypeTableType)typeTable[i].type) { case TypeTableType.Procedures: ReadCodeLabels(reader, ref _procedures, typeTable[i].elementCount, out _requireSortProcedures); break; case TypeTableType.JumpLabels: ReadCodeLabels(reader, ref _jumpLabels, typeTable[i].elementCount, out _requireSortJumps); break; case TypeTableType.Opcodes: { bool hasExtendedOpcodes; _opcodes = BFDisassembler.ParseCodeblock(reader.ReadUInt32Array(typeTable[i].elementCount), out hasExtendedOpcodes); if (hasExtendedOpcodes) // only fix up the opcode indices if they have to be FixupOpcodeIndices(); // this function is kinda 2*O(n^2) } break; case TypeTableType.Messages: if (typeTable[i].elementCount > 0) _messageFile = new BMDFile(StreamHelper.ReadStream(reader, typeTable[i].elementCount), false); break; case TypeTableType.Strings: // TODO: Implement this break; } } }
private static TypeTableEntry CreateTypeTableEntry(TypeTableType entryType, int entriesTotalSize, int dataOffset = 0) { int elementLength = 0; switch (entryType) { case TypeTableType.Procedures: case TypeTableType.JumpLabels: elementLength = 32; break; case TypeTableType.Opcodes: elementLength = 4; break; case TypeTableType.Messages: case TypeTableType.Strings: elementLength = 1; break; } TypeTableEntry entry = new TypeTableEntry() { type = (int)entryType, dataOffset = dataOffset, elementCount = entriesTotalSize / elementLength, elementLength = elementLength, }; return entry; }