source: FileSystem/UIntelHexFile.pas

Last change on this file was 93, checked in by george, 13 years ago
  • Moved: File handling related units moved to FileSystem directory.
File size: 6.9 KB
Line 
1unit UIntelHexFile;
2
3interface
4
5uses
6 Classes, UTextFileStream, SysUtils, Contnrs, UCommon, UMemoryStreamEx,
7 DateUtils;
8
9type
10 EFileCorrupted = class(Exception);
11 EChecksumError = class(Exception);
12
13 TIntelHexRecordType = (rtData, rtEndOfFile, rtSegmentAddress, rtExtendedAddress);
14
15 TMappedMemory = class(TMemoryStreamEx)
16 BaseAddress: Integer;
17 end;
18
19 { TIntelHexFile }
20
21 TIntelHexFile = class
22 private
23 function GetSize:Integer;
24 procedure WriteBlock(Block: TMappedMemory; StringList:TStringList);
25 function TwoComplement(Value: Byte): Byte;
26 public
27 Blocks: TObjectList; // of TMappedMemory
28 BytePerLine: Byte;
29 procedure LoadFromFile(FileName: string);
30 procedure SaveToFile(FileName: string);
31 procedure LoadFromStringList(StringList:TStringList);
32 procedure SaveToStringList(StringList:TStringList);
33 procedure LoadFromBinFile(FileName: string);
34 procedure SaveToBinFile(FileName: string);
35 constructor Create;
36 destructor Destroy; override;
37 property Size: Integer read GetSize;
38 end;
39
40
41implementation
42
43{ TIntelHexFile }
44
45function TIntelHexFile.GetSize:Integer;
46var
47 I: Integer;
48begin
49 Result := 0;
50 for I := 0 to Blocks.Count - 1 do
51 Result := Result + TMappedMemory(Blocks[I]).Size;
52end;
53
54procedure TIntelHexFile.WriteBlock(Block:TMappedMemory; StringList:TStringList)
55 ;
56var
57 I: Integer;
58 L: Integer;
59 CheckSum: Byte;
60 OutputLine: string;
61 Count: Integer;
62 Line: TMemoryStreamEx;
63begin
64 Line := TMemoryStreamEx.Create;
65 Block.Position := 0;
66 for L := 0 to (Block.Size div BytePerLine) do begin
67 Count := BytePerLine;
68 if Count > (Block.Size - Block.Position) then
69 Count := Block.Size - Block.Position;
70 if Count > 0 then begin
71 Line.Size := 4 + Count;
72 Line.Position := 0;
73 Line.WriteByte(Count);
74 Line.WriteByte(((Block.Position + Block.BaseAddress) shr 8) and $ff);
75 Line.WriteByte((Block.Position + Block.BaseAddress) and $ff);
76 Line.WriteByte(Integer(rtData));
77 Line.WriteStreamPart(TStream(Block), Count);
78
79 CheckSum := TwoComplement(Line.Sum);
80 Line.WriteByte(CheckSum);
81
82 OutputLine := ':';
83 Line.Position := 0;
84 for I := 0 to Line.Size - 1 do
85 OutputLine := OutputLine + IntToHex(Line.ReadByte, 2);
86 StringList.Add(OutputLine);
87 end;
88 end;
89 Line.Destroy;
90end;
91
92function TIntelHexFile.TwoComplement(Value:Byte):Byte;
93begin
94 Result := (not Value + 1) and $ff;
95end;
96
97procedure TIntelHexFile.LoadFromFile(FileName: string);
98var
99 StringList: TStringList;
100begin
101 StringList := TStringList.Create;
102 StringList.LoadFromFile(UTF8Decode(FileName));
103 LoadFromStringList(StringList);
104 StringList.Destroy;
105end;
106
107procedure TIntelHexFile.LoadFromStringList(StringList: TStringList);
108var
109 Row: string;
110 DataCount: Byte;
111 RecordType: TIntelHexRecordType;
112 I: Integer;
113 CheckSum: Byte;
114 LastAddress: Integer;
115 Address: Integer;
116 SegmentAddress: Word;
117 ExtendedAddress: Word;
118 Block: TMappedMemory;
119 Line: TMemoryStreamEx;
120begin
121 Line := TMemoryStreamEx.Create;
122 Blocks.Clear;
123 Block := nil;
124 LastAddress := -1;
125 ExtendedAddress := 0;
126 SegmentAddress := 0;
127 for I := 0 to StringList.Count - 1 do begin
128 Row := Trim(StringList[I]);
129 if SplitString(Row, 1) <> ':' then
130 raise EFileCorrupted.Create('File corrupted');
131
132 Line.Position := 0;
133 Line.Size := Length(Row) div 2;
134 while Length(Row) > 0 do
135 Line.WriteByte(StrToInt('$' + SplitString(Row, 2)));
136
137 // Read CheckSum
138 Line.Position := Line.Size - 1;
139 CheckSum := Line.ReadByte;
140 Line.Size := Line.Size - 1;
141 if CheckSum <> TwoComplement(Line.Sum) then
142 raise EChecksumError.Create('Checksum error ' + Row);
143
144 Line.Position := 0;
145 DataCount := Line.ReadByte;
146 Address := ((Line.ReadByte shl 8) or Line.ReadByte) +
147 + (SegmentAddress shl 4) + (ExtendedAddress shl 16);
148
149 if LastAddress <> Address then begin
150 Block := TMappedMemory.Create;
151 Block.BaseAddress := Address;
152 Blocks.Add(Block);
153 end;
154 RecordType := TIntelHexRecordType(Line.ReadByte);
155 case RecordType of
156 rtData: begin
157 Block.WriteStreamPart(Line, Line.Size - Line.Position);
158 end;
159 rtEndOfFile: begin
160 Break;
161 end;
162 rtSegmentAddress: begin
163 SegmentAddress := (Line.ReadByte shl 8) or Line.ReadByte;
164 ExtendedAddress := 0;
165 end;
166 rtExtendedAddress: begin
167 ExtendedAddress := (Line.ReadByte shl 8) or Line.ReadByte;
168 SegmentAddress := 0;
169 end;
170 end;
171 LastAddress := Address + DataCount;
172 end;
173 Line.Destroy;
174end;
175
176procedure TIntelHexFile.SaveToFile(FileName: string);
177var
178 StringList: TStringList;
179begin
180 StringList := TStringList.Create;
181 SaveToStringList(StringList);
182 StringList.SaveToFile(UTF8Decode(FileName));
183 StringList.Destroy;
184end;
185
186procedure TIntelHexFile.SaveToStringList(StringList: TStringList);
187var
188 I: Integer;
189 Line: TMemoryStreamEx;
190 OutputLine: string;
191 CheckSum: Byte;
192begin
193 StringList.Clear;
194
195 for I := 0 to Blocks.Count - 1 do
196 WriteBlock(TMappedMemory(Blocks[I]), StringList);
197
198 Line := TMemoryStreamEx.Create;
199
200 // Write EndOfFile
201 Line.Size := 4;
202 Line.WriteByte(0);
203 Line.WriteByte(0);
204 Line.WriteByte(0);
205 Line.WriteByte(Integer(rtEndOfFile));
206
207 CheckSum := TwoComplement(Line.Sum);
208
209 Line.Position := 0;
210 OutputLine := ':';
211 for I := 0 to Line.Size - 1 do
212 OutputLine := OutputLine + IntToHex(Line.ReadByte, 2);
213 OutputLine := OutputLine + IntToHex(CheckSum, 2);
214 StringList.Add(OutputLine);
215
216 Line.Destroy;
217end;
218
219procedure TIntelHexFile.LoadFromBinFile(FileName:string);
220var
221 NewBlock: TMappedMemory;
222begin
223 // TODO: analyze empty areas with FF bytes and split them to blocks
224 Blocks.Clear;
225 NewBlock := TMappedMemory.Create;
226 NewBlock.LoadFromFile(UTF8Decode(FileName));
227end;
228
229procedure TIntelHexFile.SaveToBinFile(FileName:string);
230var
231 MergedMemory: TMemoryStreamEx;
232 I: Integer;
233begin
234 MergedMemory := TMemoryStreamEx.Create;
235
236 // Fill unused space by unused data (FF)
237 for I := 0 to Blocks.Count - 1 do
238 if MergedMemory.Size < (TMappedMemory(Blocks[I]).BaseAddress + TMappedMemory(Blocks[I]).Size) then
239 MergedMemory.Size := (TMappedMemory(Blocks[I]).BaseAddress + TMappedMemory(Blocks[I]).Size);
240 MergedMemory.FillByte($ff, MergedMemory.Size);
241
242 // Write all memory blocks
243 for I := 0 to Blocks.Count - 1 do begin
244 MergedMemory.Position := TMappedMemory(Blocks[I]).BaseAddress;
245 MergedMemory.WriteStream(TMappedMemory(Blocks[I]), TMappedMemory(Blocks[I]).Size);
246 end;
247 MergedMemory.SaveToFile(UTF8Decode(FileName));
248 MergedMemory.Destroy;
249end;
250
251constructor TIntelHexFile.Create;
252begin
253 Blocks := TObjectList.Create;
254 BytePerLine := 16;
255end;
256
257destructor TIntelHexFile.Destroy;
258begin
259 Blocks.Destroy;
260 inherited Destroy;
261end;
262
263end.
Note: See TracBrowser for help on using the repository browser.