unit FirmwareData; //*************************************************************************************** // Description: Class for managing and manipulating firmware data. // File Name: FirmwareData.pas // //--------------------------------------------------------------------------------------- // C O P Y R I G H T //--------------------------------------------------------------------------------------- // Copyright (c) 2016 by Feaser http://www.feaser.com All rights reserved // // This software has been carefully tested, but is not guaranteed for any particular // purpose. The author does not offer any warranties and does not guarantee the accuracy, // adequacy, or completeness of the software and is not responsible for any errors or // omissions or the results obtained from use of the software. // //--------------------------------------------------------------------------------------- // L I C E N S E //--------------------------------------------------------------------------------------- // This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or // modify it under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) any later // version. // // OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the GNU General Public License for more details. // // You have received a copy of the GNU General Public License along with OpenBLT. It // should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. // //*************************************************************************************** {$IFDEF FPC} {$mode objfpc} {$ENDIF} interface //*************************************************************************************** // Includes //*************************************************************************************** uses SysUtils, Classes; //*************************************************************************************** // Type Definitions //*************************************************************************************** type //---------------------------------- TDataSegment ------------------------------------- TDataSegment = class(TObject) private // array with actual data bytes of the segment. FDataBytes: array of Byte; // base memory address for the data of this segment. FBaseAddress: Longword; // number of data bytes in this segment. FDataSize: Integer; procedure SetBaseAddress(value: Longword); function GetLastAddress: Longword; function GetData(index: Integer): Byte; procedure GrowDataArray(numOfBytesToAdd: Integer); public constructor Create; destructor Destroy; override; procedure Clear; function Add(data: array of Byte; length: Integer; address: Longword): Boolean; function Remove(length: Integer; address: Longword): Boolean; procedure Dump; property Data[index: Integer]: Byte read GetData; property Size: Integer read FDataSize; property BaseAddress: Longword read FBaseAddress write SetBaseAddress; property LastAddress: Longword read GetLastAddress; end; //---------------------------------- TDataSegmentList --------------------------------- TDataSegmentList=class(TList) private function Get(Index: Integer): TDataSegment; protected { Protected declarations } public { Public declarations } constructor Create; destructor Destroy; override; function Add(segment: TDataSegment): Integer; procedure Delete(Index: Integer); property Items[Index: Integer]: TDataSegment read Get; default; end; //---------------------------------- TFirmwareFileType -------------------------------- TFirmwareFileType = ( FFT_UNKNOWN, FFT_SRECORD, FFT_BINARY ); //---------------------------------- TFirmwareFileHandler ----------------------------- TFirmwareFileHandler = class(TObject) type TFirmwareFileDataReadEvent = procedure(sender: TObject; data: array of Byte; length: Integer; address: Longword) of object; protected // event handler for when a chunk of data was read from the firmware file FOnDataRead: TFirmwareFileDataReadEvent; public constructor Create; virtual; function Load(firmwareFile: String): Boolean; virtual; abstract; function Save(firmwareFile: String; segments: TDataSegmentList): Boolean; virtual; abstract; property OnDataRead: TFirmwareFileDataReadEvent read FOnDataRead write FOnDataRead; end; //---------------------------------- TSRecordFileHandler ------------------------------ TSRecordFileHandler = class(TFirmwareFileHandler) type TSRecordLineType = (ltInvalid, ltS0, ltS1, ltS2, ltS3, ltS7, ltS8, ltS9); private FDataBytesPerLineOnSave: Integer; class function GetLineType(line: String): TSRecordLineType; static; function GetLineData(line: String; var data: array of Byte; var length: Integer; var address: Longword): Boolean; function ConstructLine(data: array of Byte; length: Integer; address: Longword): String; public constructor Create; override; function Load(firmwareFile: String): Boolean; override; function Save(firmwareFile: String; segments: TDataSegmentList): Boolean; override; class function IsSRecordFile(firmwareFile: String): Boolean; static; property DataBytesPerLineOnSave: Integer read FDataBytesPerLineOnSave write FDataBytesPerLineOnSave; end; //---------------------------------- TBinaryFileHandler ------------------------------- TBinaryFileHandler = class(TFirmwareFileHandler) private public constructor Create; override; function Load(firmwareFile: String): Boolean; override; function Save(firmwareFile: String; segments: TDataSegmentList): Boolean; override; end; //---------------------------------- TFirmwareData ------------------------------------ TFirmwareData = class(TObject) private // list with data segments of the firmware FSegmentList: TDataSegmentList; function GetSegmentCount: Integer; function GetSegment(index: Integer): TDataSegment; procedure SortSegments; function FindSegmentIdx(address: Longword): Integer; function FindPrevSegmentIdx(address: Longword): Integer; function FindNextSegmentIdx(address: Longword): Integer; function GetFirmwareFileType(firmwareFile: String): TFirmwareFileType; procedure FirmwareFileDataRead(sender: TObject; data: array of Byte; length: Integer; address: Longword); public constructor Create; destructor Destroy; override; function AddData(data: array of Byte; length: Integer; address: Longword): Boolean; function RemoveData(length: Integer; address: Longword): Boolean; procedure ClearData; function LoadFromFile(firmwareFile: String; append: Boolean): Boolean; function SaveToFile(firmwareFile: String; firmwareFileType: TFirmwareFileType): Boolean; procedure Dump; property SegmentCount: Integer read GetSegmentCount; property Segment[index: Integer]: TDataSegment read GetSegment; end; implementation //--------------------------------------------------------------------------------------- //-------------------------------- TDataSegment ----------------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructor // //*************************************************************************************** constructor TDataSegment.Create; begin // call inherited constructor inherited Create; // clear segment contents Clear; end; //*** end of Create *** //*************************************************************************************** // NAME: Destroy // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class destructor // //*************************************************************************************** destructor TDataSegment.Destroy; begin // release allocated array memory SetLength(FDataBytes, 0); // call inherited destructor inherited; end; //*** end of Destroy *** //*************************************************************************************** // NAME: SetBaseAddress // PARAMETER: value New base address. // RETURN VALUE: none // DESCRIPTION: Setter for base address. // //*************************************************************************************** procedure TDataSegment.SetBaseAddress(value: Longword); begin FBaseAddress := value; end; //*** end of SetBaseAddress *** //*************************************************************************************** // NAME: GetLastAddress // PARAMETER: none // RETURN VALUE: Last address. // DESCRIPTION: Getter for last address in the segment. // //*************************************************************************************** function TDataSegment.GetLastAddress: Longword; begin Result := 0; if FDataSize > 0 then Result := (FBaseAddress + LongWord(FDataSize)) - 1; end; //*** end of GetLastAddress *** //*************************************************************************************** // NAME: GetData // PARAMETER: index Index into the data byte array. // RETURN VALUE: Byte value. // DESCRIPTION: Getter for a byte value from the array at the specified index. // //*************************************************************************************** function TDataSegment.GetData(index: Integer): Byte; begin Result := 0; if (index < FDataSize) and (index >= 0) then Result := FDataBytes[index]; end; //*** end of GetData *** //*************************************************************************************** // NAME: Clear // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Allocates more space to the data array if necessary. Allocation is // done in chunks of DATA_ARRAY_GROWTH_STEP, because this is more // run-time efficient. // //*************************************************************************************** procedure TDataSegment.GrowDataArray(numOfBytesToAdd: Integer); const DATA_ARRAY_GROWTH_STEP: Integer = 1024; var numOfBytesToGrow: Integer; numOfStepsToGrow: Integer; desiredArrayLength: Integer; begin if numOfBytesToAdd > 0 then begin // check if more space needs to be allocated if Length(FDataBytes) < (FDataSize + numOfBytesToAdd) then begin // determine how many bytes the array needs to grow numOfBytesToGrow := (FDataSize + numOfBytesToAdd) - Length(FDataBytes); if numOfBytesToGrow > 0 then begin // determine how many growth steps to add numOfStepsToGrow := numOfBytesToGrow div DATA_ARRAY_GROWTH_STEP; if (numOfBytesToGrow mod DATA_ARRAY_GROWTH_STEP) > 0 then numOfStepsToGrow := numOfStepsToGrow + 1; // determine desired new array length desiredArrayLength := Length(FDataBytes) + (numOfStepsToGrow * DATA_ARRAY_GROWTH_STEP); // grow the array SetLength(FDataBytes, desiredArrayLength); end; end; end; end; //*** end of GrowDataArray *** //*************************************************************************************** // NAME: Clear // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Clears all databytes from the segment and resets its base address. // //*************************************************************************************** procedure TDataSegment.Clear; begin FBaseAddress := 0; FDataSize := 0; SetLength(FDataBytes, 0); end; //*** end of Clear //*************************************************************************************** // NAME: Add // PARAMETER: data Array with bytes to add to the segment. // length Number of bytes in the array. // address Address where to start adding bytes at in the segment. // RETURN VALUE: True if the data was added to the segment, False if it couldn't be // added. This latter situation happens if the data is not aligned to // the data the is already present in the segment. // DESCRIPTION: Adds data bytes to the segment starting at the specified address. This // function allows a new chunk of data to be added at the front or the // rear of the segment, as well as overwriting existing data. // //*************************************************************************************** function TDataSegment.Add(data: array of Byte; length: Integer; address: Longword): Boolean; var byteIdx: Integer; numBytesToAppend: Integer; begin // init result Result := False; // check if there is something to add if length <= 0 then Exit; // the following checks assume there is already data in the segment if FDataSize > 0 then begin // check if the new data does not fit at the end if address > (GetLastAddress + 1) then Exit; // check if new data does not fit at the start if (address + Longword(length)) < FBaseAddress then Exit; end; // still here some there is something to add. check if the segment is currently empty if (FDataSize = 0) then begin // make sure enough elements are allocated in the data array GrowDataArray(length); // set the base address FBaseAddress := address; // add the data for byteIdx := 0 to (length - 1) do FDataBytes[byteIdx] := data[byteIdx]; // set the new size FDataSize := length; // success Result := True; end // check if all data is for overwriting existing data else if (address >= FBaseAddress) and ((address + Longword(length - 1)) <= GetLastAddress) then begin // overwrite the data for byteIdx := 0 to (length - 1) do FDataBytes[(address - FBaseAddress) + Longword(byteIdx)] := data[byteIdx]; // success Result := True; end // check if data should be appended at the end including partial overwrite at the end else if (address >= FBaseAddress) and ((address + Longword(length - 1)) > GetLastAddress) then begin // determine minimal required growth of the array numBytesToAppend := (address + Longword(length)) - (FBaseAddress + Longword(FDataSize)); // make sure enough elements are allocated in the data array GrowDataArray(numBytesToAppend); // add the data for byteIdx := 0 to (length - 1) do FDataBytes[(address - FBaseAddress) + Longword(byteIdx)] := data[byteIdx]; // set the new size FDataSize := FDataSize + numBytesToAppend; // success Result := True; end // check if data should be appended at the start including partial overwrite at the start else if (address < FBaseAddress) and ((address + Longword(length - 1)) <= GetLastAddress) then begin // determine minimal required growth of the array numBytesToAppend := FBaseAddress - address; // make sure enough elements are allocated in the data array GrowDataArray(numBytesToAppend); // set the base address FBaseAddress := address; // move current contents {for byteIdx := 0 to (FDataSize - 1) do FDataBytes[numBytesToAppend + byteIdx] := FDataBytes[byteIdx];} for byteIdx := (FDataSize - 1) downto 0 do FDataBytes[numbytesToAppend + byteIdx] := FDataBytes[byteIdx]; // add the new data for byteIdx := 0 to (length - 1) do FDataBytes[byteIdx] := data[byteIdx]; // set the new size FDataSize := FDataSize + numBytesToAppend; // success Result := True; end // check if data should be both appended at the start and the end. this is the case when // the to be added data is larger then the current segment and overlaps the entire current // segment else if (address < FBaseAddress) and ((address + Longword(length - 1)) > GetLastAddress) then begin // set the base address FBaseAddress := address; // make sure enough elements are allocated in the data array GrowDataArray(length); // add the new data. no need to first move current contents because they will be // fully overwritten anyways for byteIdx := 0 to (length - 1) do FDataBytes[byteIdx] := data[byteIdx]; // set the new size FDataSize := length; // success Result := True; end; end; //*** end of Add *** //*************************************************************************************** // NAME: Remove // PARAMETER: length Number of bytes to remove // address Address where to start removing data from. // RETURN VALUE: True if the data was removed, False if the data could not be removed // because this class cannot split a segment. // DESCRIPTION: Removes data from the segment. Note that the to be removed data // must be aligned to the start or the end of the segment, because this // class cannot split a segment. // //*************************************************************************************** function TDataSegment.Remove(length: Integer; address: Longword): Boolean; var numOfBytesToRemove: Integer; byteIdx: Integer; begin Result := True; // if there is nothing to remove then we are done already if (length <= 0) or (FDataSize = 0) then begin Exit; end; // if the data is not in this segment the we are also done already if (address > GetLastAddress) or ((address + Longword(length - 1)) < FBaseAddress) then begin Exit; end; // check if the to be removed data overlaps with either the end or the start of the // segment. if not, then we cannot remove the data because this class cannot split the // segment if (address > FBaseAddress) and ((address + Longword(length - 1)) < GetLastAddress) then begin Result := False; Exit; end; // check if the entire segment should be removed if (address <= FBaseAddress) and ((address + Longword(length - 1)) >= GetLastAddress) then begin Clear; end // check if the to be removed data is at the start of the segment else if (address <= FBaseAddress) then begin numOfBytesToRemove := (address + Longword(length)) - FBaseAddress; // move remaining data to the start of the array for byteIdx := 0 to (FDataSize - numOfBytesToRemove - 1) do FDataBytes[byteIdx] := FDataBytes[byteIdx + numOfBytesToRemove]; // adjust size and base address FDataSize := FDataSize - numOfBytesToRemove; FBaseAddress := FBaseAddress + Longword(numOfBytesToRemove); end // check if the to be removed data is at the end of the segment else if (address > FBaseAddress) and ((address + Longword(length - 1)) >= GetLastAddress) then begin numOfBytesToRemove := GetLastAddress - address + 1; FDataSize := FDataSize - numOfBytesToRemove; end; end; //*** end of Remove *** //*************************************************************************************** // NAME: Dump // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Dumps the segment contents to the standard output for debugging // purposes. // //*************************************************************************************** procedure TDataSegment.Dump; {$IFDEF DEBUG} var line: String; byteCnt: Integer; {$ENDIF} begin {$IFDEF DEBUG} // output address and size Writeln('Segment base address = $' + Format('%.8X', [BaseAddress])); Writeln('Segment data size = ' + IntToStr(Size)); // output raw data Writeln('Segment data contents = ' + sLineBreak); line := ' '; for byteCnt := 1 to Size do begin line := line + Format('%.2X ', [Data[byteCnt - 1]]); if (byteCnt mod 16) = 0 then begin Writeln(line); line := ' '; end; end; Writeln(line); {$ENDIF} end; //*** end of Dump //--------------------------------------------------------------------------------------- //-------------------------------- TDataSegmentList ------------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Object constructor. Calls TObject's constructor and initializes // the private property variables to their default values. // //*************************************************************************************** constructor TDataSegmentList.Create; begin // call inherited constructor inherited Create; end; //*** end of Create *** //*************************************************************************************** // NAME: Destroy // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Component destructor. // //*************************************************************************************** destructor TDataSegmentList.Destroy; var idx: Integer; begin // release allocated heap memory for idx := 0 to Count - 1 do TDataSegment(Items[idx]).Free; inherited; end; //*** end of Destroy *** //*************************************************************************************** // NAME: Get // PARAMETER: Index Index in the list // RETURN VALUE: List item. // DESCRIPTION: Obtains an element from the list. // //*************************************************************************************** function TDataSegmentList.Get(Index: Integer): TDataSegment; begin Result := TDataSegment(inherited Get(Index)); end; //*** end of Get *** //*************************************************************************************** // NAME: Add // PARAMETER: segment The data segment to add. // RETURN VALUE: Index of the newly added segment in the list if successful, -1 // otherwise. // DESCRIPTION: Adds an element to the list. // //*************************************************************************************** function TDataSegmentList.Add(segment: TDataSegment): Integer; begin // add the entry to the list Result := inherited Add(segment); // set correct value for error situation if Result < 0 then Result := -1; end; //*** end of Add *** //*************************************************************************************** // NAME: Delete // PARAMETER: Index Index in the list. // RETURN VALUE: none // DESCRIPTION: Remove an element to the list as the specified index. It is automa- // tically freed as well. // //*************************************************************************************** procedure TDataSegmentList.Delete(Index: Integer); var segment: TDataSegment; begin // only continue if the index is valid if (Index >= 0) and (Index < Count) then begin // obtain object first so we can free it afterwards segment := Get(Index); // delete it from the list inherited Delete(Index); // now free it segment.Free end; end; //*** end of Delete *** //--------------------------------------------------------------------------------------- //-------------------------------- TFirmwareFileHandler --------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructor // //*************************************************************************************** constructor TFirmwareFileHandler.Create; begin // call inherited constructor inherited Create; // init fields FOnDataRead := nil; end; //*** end of Create *** //--------------------------------------------------------------------------------------- //-------------------------------- TSRecordFileHandler ---------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructor // //*************************************************************************************** constructor TSRecordFileHandler.Create; begin // call inherited constructor inherited Create; // set default number of data bytes to add to a line when saving an s-record FDataBytesPerLineOnSave := 16; end; //*** end of Create *** //*************************************************************************************** // NAME: Load // PARAMETER: firmwareFile Filename with path of the file to load. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Loads the data in the specified firmware file. The OnDataRead event // handler is called each time a chunk of data was read from the file. // //*************************************************************************************** function TSRecordFileHandler.Load(firmwareFile: String): Boolean; var srecordFile: TextFile; line: String; lineData: array of Byte; lineLength: Integer; lineAddr: Longword; begin // init result value and locals Result := True; // first check if the file actually exists if not FileExists(firmwareFile) then begin Result := False; Exit; end; // check if the event handler is configured, otherwise it is pointless to go through // the file if not Assigned(FOnDataRead) then begin Result := False; Exit; end; // create array with sufficient length SetLength(lineData, 1024); // go through the lines in the file to try and detect a line that is formatted as an // S-record. start by getting the file handle and going to the start of the file AssignFile(srecordFile, firmwareFile); Reset(srecordFile); // loop through the lines while not Eof(srecordFile) do begin // read the next line from the file ReadLn(srecordFile, line); // parse the line to extract the data bytes and address info if GetLineData(line, lineData, lineLength, lineAddr) then begin // invoke the event handler to inform about the new data FOnDataRead(Self, lineData, lineLength, lineAddr); end; end; // close the file CloseFile(srecordFile); // release array SetLength(lineData, 0); end; //*** end of Load *** //*************************************************************************************** // NAME: Save // PARAMETER: firmwareFile Filename with path of the file to save. // segments List with data segments that need to be saved. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Saves the firmware data to the specified firmware file. // //*************************************************************************************** function TSRecordFileHandler.Save(firmwareFile: String; segments: TDataSegmentList): Boolean; var srecordFile: TextFile; segmentIdx: Integer; byteIdx: Integer; line: String; programData: array of Byte; currentAddress: Longword; currentByteCnt: Integer; firmwareFileBytes: TBytes; headerByteCount: Integer; checksumCalc: Byte; addrByteCnt: Integer; charIdx: Integer; begin // init result Result := True; // check if there is actually something to write if segments.Count <= 0 then begin // no program data to write Result := False; Exit; end; // open the firmware file for writing AssignFile(srecordFile, firmwareFile); ReWrite(srecordFile); // ---- add the S0 header line that contains the filename ---- SetLength(firmwareFileBytes, Length(firmwareFile)); for charIdx := 1 to Length(firmwareFile) do firmwareFileBytes[charIdx - 1] := Ord(firmwareFile[charIdx]); headerByteCount := 3 + Length(firmwareFileBytes); line := 'S0' + Format('%.2X', [headerByteCount]) + '0000'; for byteIdx := 0 to (Length(firmwareFileBytes) - 1) do begin line := line + Format('%.2X', [firmwareFileBytes[byteIdx]]); end; // compute checksum checksumCalc := 0; for byteIdx := 0 to (headerByteCount - 1) do begin checksumCalc := checksumCalc + StrToInt('$' + Copy(line, 3+(byteIdx*2), 2)); end; // convert to one's complement and add it checksumCalc := not checksumCalc; line := line + Format('%.2X', [checksumCalc]); // add it to the file WriteLn(srecordFile, line); // ---- add the program data lines ---- // init program data array SetLength(programData, DataBytesPerLineOnSave); // loop through all segments for segmentIdx := 0 to (segments.Count - 1) do begin // set current address and byte count currentAddress := segments[segmentIdx].BaseAddress; currentByteCnt := 0; // progress the data for byteIdx := 0 to (segments[segmentIdx].Size - 1) do begin // add the program data byte programData[currentByteCnt] := segments[segmentIdx].Data[byteIdx]; currentByteCnt := currentByteCnt + 1; // check if desired program data bytes per line is reached if currentByteCnt = DataBytesPerLineOnSave then begin // construct the s-record line and add it to the file line := ConstructLine(programData, currentByteCnt, currentAddress); WriteLn(srecordFile, line); // refresh loop variables currentAddress := currentAddress + Longword(currentByteCnt); currentByteCnt := 0; end; end; // check if there are still bytes left to write to the file if currentByteCnt > 0 then begin // construct the s-record line and add it to the file line := ConstructLine(programData, currentByteCnt, currentAddress); WriteLn(srecordFile, line); end; end; // ---- add the termination line ---- // determine the line type to use if segments[0].BaseAddress >= $FFFFFF then begin addrByteCnt := 4; line := 'S705' + Format('%.8X', [segments[0].BaseAddress]); end else if segments[0].BaseAddress >= $FFFF then begin addrByteCnt := 3; line := 'S804' + Format('%.6X', [segments[0].BaseAddress]); end else begin addrByteCnt := 2; line := 'S903' + Format('%.4X', [segments[0].BaseAddress]); end; // compute checksum checksumCalc := 0; for byteIdx := 0 to addrByteCnt do begin checksumCalc := checksumCalc + StrToInt('$' + Copy(line, 3+(byteIdx*2), 2)); end; // convert to one's complement and add it checksumCalc := not checksumCalc; line := line + Format('%.2X', [checksumCalc]); WriteLn(srecordFile, line); // close the file CloseFile(srecordFile); end; //*** end of Save *** //*************************************************************************************** // NAME: IsSRecordFile // PARAMETER: firmwareFile Filename with path of the file to check. // RETURN VALUE: True is the file has the S-Record format, False otherwise. // DESCRIPTION: Checks if the file contains data formatted as an S-Record. // //*************************************************************************************** class function TSRecordFileHandler.IsSRecordFile(firmwareFile: String): Boolean; var srecordFile: TextFile; line: String; begin // init result value and locals Result := False; // first check if the file actually exists if not FileExists(firmwareFile) then Exit; // go through the lines in the file to try and detect a line that is formatted as an // S-record. start by getting the file handle and going to the start of the file AssignFile(srecordFile, firmwareFile); Reset(srecordFile); // loop through the lines while not Eof(srecordFile) do begin ReadLn(srecordFile, line); // read line from file if (TSRecordFileHandler.GetLineType(line) = ltS1) or (TSRecordFileHandler.GetLineType(line) = ltS2) or (TSRecordFileHandler.GetLineType(line) = ltS3) then begin // valid S-Record Result := true; // no need to continue looping Break; end; end; // close the file CloseFile(srecordFile); end; //*** end of IsSRecordFile *** //*************************************************************************************** // NAME: GetLineType // PARAMETER: Line from S-Record // RETURN VALUE: line type // DESCRIPTION: Determines what type of S-Record line we're dealing with. // //*************************************************************************************** class function TSRecordFileHandler.GetLineType(line: String): TSRecordLineType; begin Result := ltInvalid; if Pos('S0', UpperCase(line)) > 0 then begin Result := ltS0; Exit; end; if Pos('S1', UpperCase(line)) > 0 then begin Result := ltS1; Exit; end; if Pos('S2', UpperCase(line)) > 0 then begin Result := ltS2; Exit; end; if Pos('S3', UpperCase(line)) > 0 then begin Result := ltS3; Exit; end; if Pos('S7', UpperCase(line)) > 0 then begin Result := ltS7; Exit; end; if Pos('S8', UpperCase(line)) > 0 then begin Result := ltS8; Exit; end; if Pos('S9', UpperCase(line)) > 0 then begin Result := ltS9; Exit; end; end; //*** end of GetLineType *** //*************************************************************************************** // NAME: GetLineData // PARAMETER: line Line from S-Record. // data Array where the data bytes are to be stored. // length Storage for number of bytes that were read. // address Storage for the address found on the S-Record line. // RETURN VALUE: True is successful, False otherwise // DESCRIPTION: Extracts the data bytes and address from the S-Record line. // //*************************************************************************************** function TSRecordFileHandler.GetLineData(line: String; var data: array of Byte; var length: Integer; var address: Longword): Boolean; var lineType: TSRecordLineType; byteCount: Integer; byteIdx: Integer; checksumRead: Byte; checksumCalc: Byte; addrByteCnt: Integer; begin // init result Result := True; // read out the line type lineType := TSRecordFileHandler.GetLineType(line); // set line type specific settings case lineType of ltS1: begin addrByteCnt := 2; end; ltS2: begin addrByteCnt := 3; end; ltS3: begin addrByteCnt := 4; end; else // line does not contain program data Result := False; Exit; end; // extract count value from the line byteCount := StrToInt('$' + Copy(line, 3, 2)); // extract address address := StrToInt('$' + Copy(line, 5, addrByteCnt*2)); // determine number of data bytes = total bytes - address - checksum length := byteCount - addrByteCnt - 1; // read the checksum checksumRead := StrToInt('$' + Copy(line, (5+(addrByteCnt*2))+(length*2), 2)); // compute checksum checksumCalc := 0; for byteIdx := 0 to (byteCount - 1) do begin checksumCalc := checksumCalc + StrToInt('$' + Copy(line, 3+(byteIdx*2), 2)); end; // convert to one's complement checksumCalc := not checksumCalc; // validate checksum if checksumCalc <> checksumRead then begin // line contains an invalid checksum Result := False; Exit; end; // read all the data bytes for byteIdx := 0 to (length - 1) do begin data[byteIdx] := StrToInt('$' + Copy(line, (5+(addrByteCnt*2))+(byteIdx*2), 2)); end; end; //*** end of GetLineData *** //*************************************************************************************** // NAME: ConstructLine // PARAMETER: data Array with data bytes. // length Number of bytes in the array. // address Base address of the data. // RETURN VALUE: The constructed line if successful, '' otherwise. // DESCRIPTION: Constructs an S-record line with program data. // //*************************************************************************************** function TSRecordFileHandler.ConstructLine(data: array of Byte; length: Integer; address: Longword): String; var addrByteCnt: Integer; byteCount: Integer; addressStr: String; byteIdx: Integer; checksumCalc: Byte; begin // determine the line type to use if address >= $FFFFFF then begin addrByteCnt := 4; addressStr := Format('%.8X', [address]); Result := 'S3'; end else if address >= $FFFF then begin addrByteCnt := 3; addressStr := Format('%.6X', [address]); Result := 'S2'; end else begin addrByteCnt := 2; addressStr := Format('%.4X', [address]); Result := 'S1'; end; // determine number of bytes after the Sx, excluding checksum byteCount := addrByteCnt + length + 1; // add the count and address Result := Result + Format('%.2X', [byteCount]) + addressStr; // add all the data bytes for byteIdx := 0 to (length - 1) do begin Result := Result + Format('%.2X', [data[byteIdx]]); end; // compute checksum checksumCalc := 0; for byteIdx := 0 to (byteCount - 1) do begin checksumCalc := checksumCalc + StrToInt('$' + Copy(Result, 3+(byteIdx*2), 2)); end; // convert to one's complement checksumCalc := not checksumCalc; // add the checksum Result := Result + Format('%.2X', [checksumCalc]); end; //*** end of ConstructLine ***/ //--------------------------------------------------------------------------------------- //-------------------------------- TBinaryFileHandler ----------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructor // //*************************************************************************************** constructor TBinaryFileHandler.Create; begin // call inherited constructor inherited Create; end; //*** end of Create *** //*************************************************************************************** // NAME: Load // PARAMETER: firmwareFile Filename with path of the file to load. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Loads the data in the specified firmware file. The OnDataRead event // handler is called each time a chunk of data was read from the file. // //*************************************************************************************** function TBinaryFileHandler.Load(firmwareFile: String): Boolean; begin // loading from a binary file is not yet supported Result := False; end; //*** end of Load *** //*************************************************************************************** // NAME: Save // PARAMETER: firmwareFile Filename with path of the file to save. // segments List with data segments that need to be saved. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Saves the firmware data to the specified firmware file. // //*************************************************************************************** function TBinaryFileHandler.Save(firmwareFile: String; segments: TDataSegmentList): Boolean; var startAddr: Longword; endAddr: Longword; segmentIdx: Integer; progData: array of Byte; progLen: Longword; byteIdx: Longword; binaryFile: File; begin // init result and locals Result := False; startAddr := $FFFFFFFF; endAddr := 0; // first need to determine the start and end addresses for the firmware data for segmentIdx := 0 to (segments.Count - 1) do begin if segments[segmentIdx].BaseAddress < startAddr then startAddr := segments[segmentIdx].BaseAddress; if segments[segmentIdx].LastAddress > endAddr then endAddr := segments[segmentIdx].LastAddress; end; // plausibility check if startAddr > endAddr then Exit; // calculate program length progLen := endAddr - startAddr + 1; // init array size such that it can hold all program data, including filler bytes // for possible SetLength(progData, progLen); // fill it completely with filler bytes for byteIdx := 0 to (progLen - 1) do progData[byteIdx] := $FF; // add the segment data to the program data array for segmentIdx := 0 to (segments.Count - 1) do begin // loop through segment data bytes one-by-one for byteIdx := 0 to (segments[segmentIdx].Size - 1) do begin // at the byte at the correct index progData[(segments[segmentIdx].BaseAddress - startAddr) + byteIdx] := segments[segmentIdx].Data[byteIdx]; end; end; // open the firmware file for writing AssignFile(binaryFile, firmwareFile); // define a record to be of size 1 byte. ReWrite(binaryFile, 1); // write all program bytes one-by-one to the file for byteIdx := 0 to (progLen - 1) do begin BlockWrite(binaryFile, progData[byteIdx], 1); end; // clean up CloseFile(binaryFile); Result := True; end; //*** end of Save *** //--------------------------------------------------------------------------------------- //-------------------------------- TFirmwareData ---------------------------------------- //--------------------------------------------------------------------------------------- //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructor // //*************************************************************************************** constructor TFirmwareData.Create; begin // call inherited constructor inherited Create; // create empty data segments list FSegmentList := TDataSegmentList.Create(); end; //*** end of Create *** //*************************************************************************************** // NAME: Destroy // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class destructor // //*************************************************************************************** destructor TFirmwareData.Destroy; begin // release the data segments list FSegmentList.Free; // call inherited destructor inherited; end; //*** end of Destroy *** //*************************************************************************************** // NAME: GetSegmentCount // PARAMETER: none // RETURN VALUE: Count of data segments. // DESCRIPTION: Getter for the count of data segments with firmware data. // //*************************************************************************************** function TFirmwareData.GetSegmentCount: Integer; begin Result := FSegmentList.Count; end; //*** end of GetSegmentCount *** //*************************************************************************************** // NAME: GetSegment // PARAMETER: index Index of the data segment to get. // RETURN VALUE: Data segment if successful, nil otherwise. // DESCRIPTION: Getter for a data segment at the specified index. // //*************************************************************************************** function TFirmwareData.GetSegment(index: Integer): TDataSegment; begin Result := nil; if (index >= 0) and (index < FSegmentList.Count) then Result := FSegmentList[index]; end; //*** end of GetSegment *** //*************************************************************************************** // NAME: FirmwareDataCompareSegments // PARAMETER: Item1 First item for the comparison. // Item2 Second item for the comparison. // RETURN VALUE: 1 if Item1's identifier is larger, -1 if Item1's identifier is // smaller, 0 if the identifiers are equal. // DESCRIPTION: Custom sorting routine for the entries in filter. // //*************************************************************************************** function FirmwareDataCompareSegments(Item1, Item2: Pointer): Integer; begin Result := TDataSegment(Item1).BaseAddress - TDataSegment(Item2).BaseAddress; end; //*** end of FirmwareDataCompareSegments *** //*************************************************************************************** // NAME: SortSegments // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Sorts the segments based on the base address of the segment. // //*************************************************************************************** procedure TFirmwareData.SortSegments; begin FSegmentList.Sort(@FirmwareDataCompareSegments); end; //*** end of SortSegments *** //*************************************************************************************** // NAME: FindSegmentIdx // PARAMETER: address Address to match // RETURN VALUE: Segment index if found, -1 otherwise. // DESCRIPTION: Searches for a segment that contains the specified address. // //*************************************************************************************** function TFirmwareData.FindSegmentIdx(address: Longword): Integer; var segmentIdx: Integer; begin Result := -1; // loop through segments for segmentIdx := 0 to (GetSegmentCount - 1) do begin // does this address fall into this segment? if (address >= FSegmentList[segmentIdx].BaseAddress) and (address <= FSegmentList[segmentIdx].LastAddress) then begin // match found Result := segmentIdx; // no need to continue loop Break; end; end; end; //*** end of FindSegmentIdx *** //*************************************************************************************** // NAME: FindPrevSegmentIdx // PARAMETER: address Address to match // RETURN VALUE: Segment index if found, -1 otherwise. // DESCRIPTION: Searches for the previous segment. So a segment who's lastaddress is // closest to the specified address. // //*************************************************************************************** function TFirmwareData.FindPrevSegmentIdx(address: Longword): Integer; var segmentIdx: Integer; begin Result := -1; // loop through segments and keep in mind that they are ordered by increasing memory // addresses for segmentIdx := (GetSegmentCount - 1) downto 0 do begin if FSegmentList[segmentIdx].LastAddress < address then begin // match found Result := segmentIdx; Break; end; end; end; //*** end of FindPrevSegmentIdx *** //*************************************************************************************** // NAME: FindNextSegmentIdx // PARAMETER: address Address to match // RETURN VALUE: Segment index if found, -1 otherwise. // DESCRIPTION: Searches for the next segment. So a segment who's baseaddress is // closest to the specified address. // //*************************************************************************************** function TFirmwareData.FindNextSegmentIdx(address: Longword): Integer; var segmentIdx: Integer; begin Result := -1; // loop through segments and keep in mind that they are ordered by increasing memory // addresses for segmentIdx := 0 to (GetSegmentCount - 1) do begin if FSegmentList[segmentIdx].BaseAddress > address then begin // match found Result := segmentIdx; Break; end; end; end; //*** end of FindNextSegmentIdx *** //*************************************************************************************** // NAME: GetFirmwareFileType // PARAMETER: firmwareFile Filename with path of the file to check. // RETURN VALUE: The type of the firmware file. // DESCRIPTION: Determines the type of the firmware file. // //*************************************************************************************** function TFirmwareData.GetFirmwareFileType(firmwareFile: String): TFirmwareFileType; begin // init result to unknown file type Result := FFT_UNKNOWN; // check if the file is formatted as an S-Record if TSRecordFileHandler.IsSRecordFile(firmwareFile) then Result := FFT_SRECORD; end; //*** end of GetFirmwareFileType *** //*************************************************************************************** // NAME: FirmwareFileDataRead // PARAMETER: sender Object that triggered the event // data Array with data bytes that were read. // length Number of data bytes that were read. // address Start memory address that the bytes belong to. // RETURN VALUE: none // DESCRIPTION: Callback for when data was read from a firmware file during loading. // //*************************************************************************************** procedure TFirmwareData.FirmwareFileDataRead(sender: TObject; data: array of Byte; length: Integer; address: Longword); begin // add the newly read firmware data AddData(data, length, address); end; //*** end of FirmwareFileDataRead *** //*************************************************************************************** // NAME: AddData // PARAMETER: data Array with bytes to add. // length Number of bytes in the array. // address Address where to start adding bytes. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Adds firmware data to the data segments. Segments are automatically // created and joined where needed. // //*************************************************************************************** function TFirmwareData.AddData(data: array of Byte; length: Integer; address: Longword): Boolean; var firstSegmentIdx: Integer; lastSegmentIdx: Integer; segmentIdx: Integer; joinedData: array of Byte; joinedSize: Integer; byteIdx: Integer; begin Result := True; // find the starting and ending segment index firstSegmentIdx := FindSegmentIdx(address); lastSegmentIdx := FindSegmentIdx(address + Longword(length) - 1); // try to snap segments if they are directly next to another one if firstSegmentIdx = -1 then begin segmentIdx := FindPrevSegmentIdx(address); if segmentIdx <> - 1 then begin if address = (FSegmentList[segmentIdx].LastAddress + 1) then firstSegmentIdx := segmentIdx; end; end; if lastSegmentIdx = -1 then begin segmentIdx := FindNextSegmentIdx(address + Longword(length) - 1); if segmentIdx <> - 1 then begin if (address + Longword(length)) = FSegmentList[segmentIdx].BaseAddress then lastSegmentIdx := segmentIdx; end; end; // begin and end belongs to existing segments? if (firstSegmentIdx <> -1) and (lastSegmentIdx <> -1) then begin // create new data array with a copy of the first segment at the start and a copy // of the last segment at the end. joinedSize := (FSegmentList[lastSegmentIdx].LastAddress + 1) - FSegmentList[firstSegmentIdx].BaseAddress; SetLength(joinedData, joinedSize); for byteIdx := 0 to (FSegmentList[firstSegmentIdx].Size - 1) do joinedData[byteIdx] := FSegmentList[firstSegmentIdx].Data[byteIdx]; for byteIdx := 0 to (FSegmentList[lastSegmentIdx].Size - 1) do joinedData[(joinedSize - FSegmentList[lastSegmentIdx].Size) + byteIdx] := FSegmentList[lastSegmentIdx].Data[byteIdx]; // now remove the affected segments in preparation to replace them with 1 big new one // but not the first one, because this one will be resized to be a big one that holds // all the data. keep in mind that the indexes change after deleting a segment, so // the to be deleted segment is always at index firstSegmentIdx + 1 for segmentIdx := (firstSegmentIdx + 1) to lastSegmentIdx do begin FSegmentList.Delete(firstSegmentIdx + 1); end; // add the backed up data to the first segment, which will automatically be expanded Result := FSegmentList[firstSegmentIdx].Add(joinedData, joinedSize, FSegmentList[firstSegmentIdx].BaseAddress); // now add the actual data if Result then Result := FSegmentList[firstSegmentIdx].Add(data, length, address); // release array SetLength(joinedData, 0); // make sure segments are properly sorted SortSegments; // all done Exit; end; // begin and end do not belong to existing segments if (firstSegmentIdx = -1) and (lastSegmentIdx = -1) then begin // it could be there there are existing segments between the range that should be // removed. try to match the first and last segment index to snap to these. firstSegmentIdx := FindNextSegmentIdx(address); lastSegmentIdx := FindPrevSegmentIdx(address + Longword(length) - 1); // if these are both valid values, then there are segments in between that should // be removed if (firstSegmentIdx <> -1) and (lastSegmentIdx <> -1) then begin // remove the segments. keep in mind that the indexes change after deleting a // segment, so the to be deleted segment is always at index firstSegmentIdx for segmentIdx := firstSegmentIdx to lastSegmentIdx do begin FSegmentList.Delete(firstSegmentIdx); end; end; // now add the data as a new segment segmentIdx := FSegmentList.Add(TDataSegment.Create); if segmentIdx >= 0 then Result := FSegmentList[segmentIdx].Add(data, length, address) else Result := False; // make sure segments are properly sorted SortSegments; // all done Exit; end; // begin belongs to existing segments but the end does not? if (firstSegmentIdx <> -1) and (lastSegmentIdx = -1) then begin // snap last segment to the closest known one lastSegmentIdx := FindPrevSegmentIdx(address + Longword(length) - 1); // remove the overlapping segments, excluding the first one. keep in mind that the // indexes change after deleting a segment, so the to be deleted segment is always at // index firstSegmentIdx + 1 for segmentIdx := (firstSegmentIdx + 1) to lastSegmentIdx do begin FSegmentList.Delete(firstSegmentIdx + 1); end; // now add the data to the first segment, which will automatically expand it Result := FSegmentList[firstSegmentIdx].Add(data, length, address); // make sure segments are properly sorted SortSegments; // all done Exit; end; // begin does not belong to an existing segment but the end does if (firstSegmentIdx = -1) and (lastSegmentIdx <> -1) then begin // snap first segment to the closest known one firstSegmentIdx := FindNextSegmentIdx(address); // remove the overlapping segments, excluding the last one. keep in mind that the // indexes change after deleting a segment, so the to be deleted segment is always at // index firstSegmentIdx for segmentIdx := firstSegmentIdx to (lastSegmentIdx - 1) do begin FSegmentList.Delete(firstSegmentIdx); end; // note that last segment index changed because we deleted segments so refresh it lastSegmentIdx := FindSegmentIdx(address + Longword(length) - 1); // try to snap it if lastSegmentIdx = -1 then begin segmentIdx := FindNextSegmentIdx(address + Longword(length) - 1); if segmentIdx <> - 1 then begin if (address + Longword(length)) = FSegmentList[segmentIdx].BaseAddress then lastSegmentIdx := segmentIdx; end; end; // now add the data to the first last, which will automatically expand it if lastSegmentIdx <> -1 then begin Result := FSegmentList[lastSegmentIdx].Add(data, length, address); end else begin Result := False; end; // make sure segments are properly sorted SortSegments; // all done Exit; end; end; //*** end of AddData *** //*************************************************************************************** // NAME: RemoveData // PARAMETER: length Number of bytes to remove. // address Address where to start removing from. // RETURN VALUE: True is successful, False otherwise. // DESCRIPTION: Removes firmware data from the data segments. // //*************************************************************************************** function TFirmwareData.RemoveData(length: Integer; address: Longword): Boolean; var firstSegmentIdx: Integer; lastSegmentIdx: Integer; segmentDelCnt: Integer; segmentIdx: Integer; byteIdx: Integer; remainderData: array of Byte; remainderLen: Integer; remainderAddr: Longword; begin Result := True; // find the starting and ending segment index firstSegmentIdx := FindSegmentIdx(address); lastSegmentIdx := FindSegmentIdx(address + Longword(length) - 1); // in case the start and end is not in a segment, try to align it to the closest one if firstSegmentIdx = -1 then firstSegmentIdx := FindNextSegmentIdx(address); if lastSegmentIdx = -1 then lastSegmentIdx := FindPrevSegmentIdx(address + Longword(length) - 1); // after the align operation both indexes must be valid, otherwise there are no // segments to remove, which means we are done already if (firstSegmentIdx = -1) or (lastSegmentIdx = -1) then Exit; // check if a segment split is needed, which is a special case and should be done first if (firstSegmentIdx = lastSegmentIdx) and (address > FSegmentList[firstSegmentIdx].BaseAddress) and ((address + Longword(length) - 1) < FSegmentList[lastSegmentIdx].LastAddress) then begin // copy remainder data after the split to a temporary buffer remainderAddr := address + Longword(length); remainderLen := (FSegmentList[firstSegmentIdx].LastAddress + 1) - remainderAddr; SetLength(remainderData, remainderLen); for byteIdx := 0 to (remainderLen - 1) do remainderData[byteIdx] := FSegmentList[firstSegmentIdx].Data[(FSegmentList[firstSegmentIdx].Size - remainderLen) + byteIdx]; // create a new segment where the remainder data will be copied to segmentIdx := FSegmentList.Add(TDataSegment.Create); if segmentIdx >= 0 then Result := FSegmentList[segmentIdx].Add(remainderData, remainderLen, remainderAddr) else begin // this should not happen and indicates a severe error Result := False; end; // the part after the split can be safely removed no. by removing the length of the // segment, it is guaranteerd that the remainder after the split is also removed if Result then begin Result := FSegmentList[firstSegmentIdx].Remove(FSegmentList[firstSegmentIdx].Size, address); end; // a segment was added so perform sorting SortSegments; // all done Exit; end; // begin and end belongs to existing segments? note that this should always be the // case because of the segment alignment that is performed at the start. if (firstSegmentIdx <> -1) and (lastSegmentIdx <> -1) then begin // remove bytes from the end of the first segment. note that the remove will only // operates on the specified segment so no need to worry that it removes too many Result := FSegmentList[firstSegmentIdx].Remove(length, address); // remove bytes from the end of the last segment. note that the remove will only // operates on the specified segment so no need to worry that it removes too many if Result then Result := FSegmentList[lastSegmentIdx].Remove(length, address); // remove overlapping segments if any, but not the first and the last one. keep in // mind that the indexes change after deleting a segment so the to be deleted segment // is always firstSegmentIdx + 1 if Result then begin segmentDelCnt := 0; for segmentIdx := (firstSegmentIdx + 1) to (lastSegmentIdx - 1) do begin FSegmentList.Delete(firstSegmentIdx + 1); segmentDelCnt := segmentDelCnt + 1; end; // refresh last segment index lastSegmentIdx := lastSegmentIdx - segmentDelCnt; // check if last segment is now empty and delete it if so if FSegmentList[lastSegmentIdx].Size = 0 then FSegmentList.Delete(lastSegmentIdx); // check if first segment is now empty and delete it if so if FSegmentList[firstSegmentIdx].Size = 0 then FSegmentList.Delete(firstSegmentIdx); end; // no need to sort again so all done Exit; end; end; //*** end of RemoveData *** //*************************************************************************************** // NAME: ClearData // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Clear all segments with firmware data. // //*************************************************************************************** procedure TFirmwareData.ClearData; begin FSegmentList.Clear; end; //*** end of ClearData *** //*************************************************************************************** // NAME: LoadFromFile // PARAMETER: firmwareFile Filename with full path of the firmware file to load. // append True to append the firmware data to what is currently loaded, // False to clear the current firmware data first. // RETURN VALUE: True if successful, False otherwise. // DESCRIPTION: Loads firmware data from a firmware file. // //*************************************************************************************** function TFirmwareData.LoadFromFile(firmwareFile: String; append: Boolean): Boolean; var firmwareFileType: TFirmwareFileType; firmwareFileHandler: TFirmwareFileHandler; begin // init locals and the result Result := False; firmwareFileHandler := nil; // determine firmware file type firmwareFileType := GetFirmwareFileType(firmwareFile); // check if the file type is an S-record and if so, load it if firmwareFileType = FFT_SRECORD then begin // create instance of the firmware file handler firmwareFileHandler := TSRecordFileHandler.Create; end; // check if the firmware file handler object was instantiated, which flags that a // firmware file can be loaded through it if Assigned(firmwareFileHandler) then begin // clear the current firmware data if we should not append the new data from the file if not append then begin ClearData; end; // set onload handler which does the actual data processing {$IFDEF FPC} firmwareFileHandler.OnDataRead := @FirmwareFileDataRead; {$ELSE} firmwareFileHandler.OnDataRead := FirmwareFileDataRead; {$ENDIF} // load data from the file Result := firmwareFileHandler.Load(firmwareFile); // release instance of the firmware file handler firmwareFileHandler.Free end; end; //*** end of LoadFromFile *** //*************************************************************************************** // NAME: SaveToFile // PARAMETER: firmwareFile Filename with full path of the firmware file to save. // firwareFileType Firmware file type to use when saving. // RETURN VALUE: True if successful, False otherwise. // DESCRIPTION: Saves firmware data to a firmware file of the specified format. // //*************************************************************************************** function TFirmwareData.SaveToFile(firmwareFile: String; firmwareFileType: TFirmwareFileType): Boolean; var firmwareFileHandler: TFirmwareFileHandler; begin // init result Result := False; // check if the file type is an S-record and if so, save it if firmwareFileType = FFT_SRECORD then begin // create instance of the firmware file handler firmwareFileHandler := TSRecordFileHandler.Create; // perform firmware file save operation Result := firmwareFileHandler.Save(firmwareFile, FSegmentList); // release the firmware file handler firmwareFileHandler.Free; end // check if the file type is a binary file and if so, save it else if firmwareFileType = FFT_BINARY then begin // create instance of the firmware file handler firmwareFileHandler := TBinaryFileHandler.Create; // perform firmware file save operation Result := firmwareFileHandler.Save(firmwareFile, FSegmentList); // release the firmware file handler firmwareFileHandler.Free; end; end; //*** end of SaveToFile *** //*************************************************************************************** // NAME: Dump // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Dumps the segment contents to the standard output for debugging // purposes. // //*************************************************************************************** procedure TFirmwareData.Dump; {$IFDEF DEBUG} var segmentIdx: Integer; {$ENDIF} begin {$IFDEF DEBUG} for segmentIdx := 0 to (SegmentCount - 1) do begin Writeln('Segment index = ' + IntToStr(segmentIdx)); Segment[segmentIdx].Dump; end; {$ENDIF} end; //*** end of DumpFirmwareData *** end. //******************************** end of FirmwareData.pas ******************************