1+ # -*- coding: utf-8 -*-
2+ '''
3+ IHEX_VALIDATOR calculates the checksum of a single intel hex formatted record
4+ '''
5+
6+ from Npp import notepad , editor , NOTIFICATION , SCINTILLANOTIFICATION , ANNOTATIONVISIBLE , MARGINTYPE
7+ import struct
8+
9+ # Record Format
10+ # :LLAAAATT[DD...]CC
11+
12+ # : is the colon that starts every Intel HEX record.
13+ # LL is the record-length field that represents the number of data bytes (dd) in the record.
14+ # AAAA is the address field that represents the starting address for subsequent data in the record.
15+ # TT is the field that represents the HEX record type, which may be one of the following:
16+ # 00 - data record
17+ # 01 - end-of-file record
18+ # 02 - extended segment address record
19+ # 04 - extended linear address record
20+ # 05 - start linear address record (MDK-ARM only)
21+ # DD is a data field that represents one byte of data. A record may have multiple data bytes.
22+ # The number of data bytes in the record must match the number specified by the ll field.
23+ # CC is the checksum field that represents the checksum of the record.
24+ # The checksum is calculated by summing the values of all hexadecimal digit pairs in
25+ # the record modulo 256 and taking the two's complement.
26+ #
27+ # example = :10246200464C5549442050524F46494C4500464C33
28+ #
29+ # : 10 2462 00 464C5549442050524F46494C4500464C 33
30+ # | || |||| || || CC->Checksum
31+ # | || |||| || DD->Data
32+ # | || |||| TT->Record Type
33+ # | || AAAA->Address
34+ # | LL->Record Length
35+ # :->Colon
36+
37+
38+ # testdata
39+ # :10001300AC12AD13AE10AF1112002F8E0E8F0F2244
40+ # :10000300E50B250DF509E50A350CF5081200132259
41+ # :03000000020023D8
42+ # :0C002300787FE4F6D8FD7581130200031D
43+ # :10002F00EFF88DF0A4FFEDC5F0CEA42EFEEC88F016
44+ # :04003F00A42EFE22CB
45+ # :00000001FF
46+
47+
48+ class IHEX_VALIDATOR :
49+
50+ def __init__ (self ):
51+ self .document_is_of_interest = False
52+ self .debug_mode = False
53+ self .ANON_STYLE = 20 # (0-18 reserved by ihex lexer)
54+
55+ editor .callbackSync (self .on_updateui , [SCINTILLANOTIFICATION .UPDATEUI ])
56+ notepad .callback (self .on_langchanged , [NOTIFICATION .LANGCHANGED ])
57+ notepad .callback (self .on_bufferactivated , [NOTIFICATION .BUFFERACTIVATED ])
58+
59+
60+ def __set_annotation (self , line , text ):
61+ '''
62+ Shows an annotated line under the caret line
63+ Args:
64+ line = integer, 0-based line number of the caret line
65+ text = string, text to be shown
66+ Returns:
67+ None
68+ '''
69+
70+ editor .styleSetFore (self .ANON_STYLE , (128 ,255 ,0 ))
71+ editor .styleSetBack (self .ANON_STYLE , notepad .getEditorDefaultBackgroundColor ())
72+ editor .annotationSetVisible (ANNOTATIONVISIBLE .STANDARD )
73+ editor .annotationSetText (line , text )
74+ editor .annotationSetStyle (line , self .ANON_STYLE )
75+
76+
77+ def calculate_checksum (self ):
78+ '''
79+ Calculates the checksum of the caret line
80+ Args:
81+ None
82+ Returns:
83+ None
84+ '''
85+ line = editor .getCurLine ().strip ()
86+ line_length = len (line )
87+ record_length = line_length - 1
88+ if line .startswith (':' ) and line_length > 10 and (record_length % 2 == 0 ):
89+ if record_length == int (line [1 :3 ],16 ) * 2 + 8 :
90+ length_assumed = True
91+ x = line [1 :].decode ('hex' )
92+ else :
93+ length_assumed = False
94+ x = line [1 :- 2 ].decode ('hex' )
95+ total = sum (struct .unpack ('<' + 'B' * len (x ), x ))
96+ int_checksum = ~ total & 0xFF
97+ checksum = format (0 if int_checksum == 0xFF else int_checksum + 1 , '02X' )
98+ if self .debug_mode :
99+ print ('calculated checksum is {}' .format (checksum ))
100+ if checksum != line [- 2 :]:
101+ self .__set_annotation (editor .lineFromPosition (editor .getCurrentPos ()),
102+ '{}{}' .format (' ' * line_length if length_assumed else ' ' * (line_length - 2 ),
103+ checksum ))
104+ else :
105+ editor .annotationClearAll ()
106+
107+
108+ def check_lexer (self ):
109+ '''
110+ Checks if the current document is of interest
111+ and sets the flag accordingly
112+ Args:
113+ None
114+ Returns:
115+ None
116+ '''
117+ self .document_is_of_interest = notepad .getLanguageName (notepad .getLangType ()) == 'Intel HEX'
118+ if self .debug_mode :
119+ print ('document is of interest:{}' .format (self .document_is_of_interest ))
120+
121+
122+ def on_bufferactivated (self , args ):
123+ '''
124+ Callback which gets called every time one switches a document.
125+ Triggers the check if the document is of interest.
126+ Args:
127+ provided by notepad object but none are of interest
128+ Returns:
129+ None
130+ '''
131+ self .check_lexer ()
132+
133+
134+ def on_updateui (self , args ):
135+ '''
136+ Callback which gets called every time scintilla
137+ (aka the editor) changed something within the document.
138+ Triggers the styling function if the document is of interest.
139+ Args:
140+ provided by scintilla but none are of interest
141+ Returns:
142+ None
143+ '''
144+ if self .document_is_of_interest :
145+ self .calculate_checksum ()
146+
147+
148+ def on_langchanged (self , args ):
149+ '''
150+ Callback gets called every time one uses the Language menu to set a lexer
151+ Triggers the check if the document is of interest
152+ Args:
153+ provided by notepad object but none are of interest
154+ Returns:
155+ None
156+ '''
157+ self .check_lexer ()
158+
159+
160+ def main (self ):
161+ '''
162+ Main function entry point.
163+ Simulates two events to enforce detection of current document
164+ and potential validating.
165+ Args:
166+ None
167+ Returns:
168+ None
169+ '''
170+ self .on_bufferactivated (None )
171+ self .on_updateui (None )
172+
173+
174+ IHEX_VALIDATOR ().main ()
175+
0 commit comments