Coverage for pyilper/lifutils.py: 77%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

227 statements  

1#!/usr/bin/python3 

2# -*- coding: utf-8 -*- 

3# 

4# LIF utilities 

5# 

6# Python classes to handle LIF image files  

7# derived from the LIF utilities of Tony Duell 

8# Copyright (c) 2008 A. R. Duell 

9# Python Version (c) 2015 Joachim Siebold 

10# 

11# This program is free software; you can redistribute it and/or 

12# modify it under the terms of the GNU General Public License 

13# as published by the Free Software Foundation; either version 2 

14# of the License, or (at your option) any later version. 

15# 

16# This program is distributed in the hope that it will be useful, 

17# but WITHOUT ANY WARRANTY; without even the implied warranty of 

18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19# GNU General Public License for more details. 

20# 

21# You should have received a copy of the GNU General Public License 

22# along with this program; if not, write to the Free Software 

23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

24# 

25# LIF image file classes --------------------------------------------------- 

26# 

27# Changelog 

28# 01.10.2015 cg 

29# - fixed wrong file type for "ROM75" 

30# 05.10.2015 jsi: 

31# - class statement syntax update 

32# 29.11.2015 jsi: 

33# - do not check for lif medium type 

34# 30.11.2015 jsi: 

35# - raise error in lifopen if not a valid lif image file 

36# 08.01.2016 jsi: 

37# - introduced lifcore.py, refactoring 

38# 09.01.2016 jsi: 

39# - added filetypes 

40# 08.02.2016 jsi: 

41# - changed os detection to platform.system() 

42# 11.07.2016 jsi: 

43# - use functions from pilcore.py for platform detection 

44# 04.12.2016 jsi 

45# - allow directory not starting at record 2 

46# 

47import os 

48from .lifcore import * 

49from .pilcore import isWINDOWS 

50 

51class LifError(Exception): 

52 def __init__(self,msg,add_msg=None): 

53 self.msg=msg 

54 self.add_msg= add_msg 

55 

56class cls_LifDir: 

57 

58 def __init__(self,liffile): 

59 self.liffile=liffile 

60 self.lifdir= { } 

61 self.lastblock=0 

62 self.num_entries=0 

63 self.cur_entry= None 

64 self.isOpen= False 

65 

66 def open(self): 

67 recno= self.liffile.dir_start 

68 lastdirrec= recno+self.liffile.dir_length-1 

69 i=0 

70 while True: 

71 if recno > lastdirrec: 

72 break 

73 if i == 0: 

74 self.liffile.rrec(recno) 

75 entry= bytearray(32) 

76 for j in range(32): 

77 entry[j]= self.liffile.buffer[j+i*32] 

78 ft= getLifInt(entry,10,2) 

79 if ft == 0xFFFF: 

80 break 

81 if self.cur_entry is None: 

82 self.cur_entry=0 

83 else: 

84 self.cur_entry+=1 

85 self.lifdir[self.cur_entry]= entry 

86 if ft != 0x0000: 

87 self.num_entries+=1 

88 start_block= getLifInt(self.lifdir[self.cur_entry],12,4) 

89 alloc_blocks= getLifInt(self.lifdir[self.cur_entry],16,4) 

90 endblock= start_block+alloc_blocks 

91 if endblock > self.lastblock: 

92 self.lastblock= endblock 

93 i+=1 

94 if i==8: 

95 i=0 

96 recno+=1 

97 

98 if self.lastblock == 0: 

99 self.lastblock= self.liffile.dir_start+self.liffile.dir_length-1 

100 self.isOpen=True 

101 

102 def rewind(self): 

103 if not self.isOpen: 

104 raise LifError("Directory not open","") 

105 self.cur_entry=0 

106 

107 def getNextEntry(self): 

108 if self.num_entries==0: 

109 return [] 

110 if self.cur_entry == len(self.lifdir): 

111 return [] 

112 while True: 

113 ft= getLifInt(self.lifdir[self.cur_entry],10,2) 

114 if ft != 0x0000: 

115 break 

116 self.cur_entry+=1 

117 if self.cur_entry == len(self.lifdir): 

118 return[] 

119 name=getLifString(self.lifdir[self.cur_entry],0,10) 

120 start_block= getLifInt(self.lifdir[self.cur_entry],12,4) 

121 alloc_blocks= getLifInt(self.lifdir[self.cur_entry],16,4) 

122 datetime=getLifDateTime(self.lifdir[self.cur_entry],20) 

123 tl= self.getTypeLen() 

124 self.cur_entry+=1 

125 return [name, ft,start_block, alloc_blocks, datetime, tl[0], tl[1]] 

126 

127 def getTypeLen(self): 

128 e=self.lifdir[self.cur_entry] 

129 ft=getLifInt(e,10,2) 

130# LIF1 (Text) 

131 if ft == 1 or ft== 0xE0D1: 

132 t=get_finfo_type(ft)[0] 

133 l=getLifInt(e,16,4)* 256 

134# D-LEX 

135 elif ft== 0x00FF: 

136 t=get_finfo_type(ft)[0] 

137 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

138# SDATA 

139 elif ft == 0xE0D0: 

140 t=get_finfo_type(ft)[0] 

141 l= getLifInt(e,28,2)*8 

142# HP-71 Data file 

143 elif ft== 0xE0F0 or ft== 0xE0F1: 

144 t=get_finfo_type(ft)[0] 

145 l= (e[28] + (e[29] <<8)) * (e[30] + (e[31]<<8)) 

146# HP-71 BIN 

147 elif ft >= 0xE204 and ft<= 0xE207: 

148 t=get_finfo_type(ft)[0] 

149 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

150# HP-71 LEX 

151 elif ft>= 0xE208 and ft <= 0xE20B: 

152 t=get_finfo_type(ft)[0] 

153 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

154# HP-71 KEY 

155 elif ft==0xE20C or ft== 0xE20D: 

156 t=get_finfo_type(ft)[0] 

157 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

158# HP-71 Basic 

159 elif ft>= 0xE214 and ft<=0xE217: 

160 t=get_finfo_type(ft)[0] 

161 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

162# HP-71 Forth 

163 elif ft>= 0xE218 and ft<=0xE21B: 

164 t=get_finfo_type(ft)[0] 

165 l=getLifInt(e,16,4)* 256 

166# HP-71 ROM 

167 elif ft == 0xE21C: 

168 t=get_finfo_type(ft)[0] 

169 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

170# HP-71 Graphics 

171 elif ft == 0xE222: 

172 t=get_finfo_type(ft)[0] 

173 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

174# HP-71 Address 

175 elif ft == 0xE224: 

176 t=get_finfo_type(ft)[0] 

177 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

178# HP-71 Symbol file?? 

179 elif ft == 0xE22E: 

180 t=get_finfo_type(ft)[0] 

181 l= int((( e[28] + (e[29] <<8) + (e[30]<<16))+1)/2) 

182# HP-41 WALL with X-MEM 

183 elif ft== 0xE020: 

184 t=get_finfo_type(ft)[0] 

185 l= (getLifInt(e,28,2)*8)+1 

186# HP-41 X-MEM 

187 elif ft== 0xE030: 

188 t=get_finfo_type(ft)[0] 

189 l= (getLifInt(e,28,2)*8)+1 

190# HP-41 WALL 

191 elif ft== 0xE040: 

192 t=get_finfo_type(ft)[0] 

193 l= (getLifInt(e,28,2)*8)+1 

194# HP-41 KEYS 

195 elif ft== 0xE050: 

196 t=get_finfo_type(ft)[0] 

197 l= (getLifInt(e,28,2)*8)+1 

198# HP-41 Status 

199 elif ft== 0xE060: 

200 t=get_finfo_type(ft)[0] 

201 l= (getLifInt(e,28,2)*8)+1 

202# HP-41 ROM/MLDL dump 

203 elif ft== 0xE070: 

204 t=get_finfo_type(ft)[0] 

205 l= (getLifInt(e,28,2)*8)+1 

206# HP-41 FOCAL program 

207 elif ft== 0xE080: 

208 t=get_finfo_type(ft)[0] 

209 l= getLifInt(e,28,2)+1 

210# HP-75 text 

211 elif ft== 0xE052: 

212 t=get_finfo_type(ft)[0] 

213 l= getLifInt(e,16,4)*256 

214# HP-75 Appointments 

215 elif ft== 0xE053: 

216 t=get_finfo_type(ft)[0] 

217 l= getLifInt(e,16,4)*256 

218# HP-75 Data 

219 elif ft== 0xE058: 

220 t=get_finfo_type(ft)[0] 

221 l= getLifInt(e,16,4)*256 

222# HP-75 LEX 

223 elif ft== 0xE089: 

224 t=get_finfo_type(ft)[0] 

225 l= getLifInt(e,16,4)*256 

226# HP-75 Visicalc 

227 elif ft== 0xE08A: 

228 t=get_finfo_type(ft)[0] 

229 l= getLifInt(e,16,4)*256 

230# HP-75 Basic 

231 elif ft== 0xE0FE or ft==0xE088: 

232 t=get_finfo_type(ft)[0] 

233 l= getLifInt(e,16,4)*256 

234# HP-75 ROM 

235 elif ft== 0xE08B: 

236 t=get_finfo_type(ft)[0] 

237 l= getLifInt(e,16,4)*256 

238# other ... 

239 else: 

240 t= "0x{:4X}".format(ft) 

241 l= getLifInt(e,16,4)* 256 

242 return [t , l] 

243 

244 

245 

246class cls_LifFile: 

247 

248 def __init__(self): 

249 self.filename= None # Name of LIF File 

250 self.filehd= None # File descriptor 

251 self.isWindows=False # platform idicator for i/o 

252 self.isLifFile= False # Valid lif file 

253 self.buffer= bytearray(256) # read write buffer 

254 self.header= bytearray(256) # lif header 

255 self.label = None 

256 self.dir_start=0 

257 self.dir_length=0 

258 self.no_tracks=0 

259 self.no_surfaces=0 

260 self.no_blocks=0 

261 if isWINDOWS(): 

262 self.isWindows= True 

263 

264 def set_filename(self,name): 

265 self.filename= name 

266 

267 def __open__(self): 

268 if self.filename is None: 

269 raise LifError("No file specified","") 

270 try: 

271 if self.isWindows: 

272 self.filefd= os.open(self.filename,os.O_RDONLY | os.O_BINARY) 

273 else: 

274 self.filefd= os.open(self.filename,os.O_RDONLY) 

275 except OSError as e: 

276 raise LifError("Cannot open file",e.strerror) 

277 

278 def __close__(self): 

279 if self.filefd is None: 

280 raise LifError("File not open","") 

281 try: 

282 os.close(self.filefd) 

283 except OSError as e: 

284 raise LifError("Cannot close file",e.strerror) 

285 

286 

287 def rrec(self,recno): 

288 try: 

289 os.lseek(self.filefd,recno * 256, os.SEEK_SET) 

290 b=os.read(self.filefd,256) 

291 if len(b) < 256: 

292 raise LifError("Cannot read from file","") 

293 for i in range (256): 

294 self.buffer[i]= b[i] 

295 except OSError as e: 

296 raise LifError("Cannot read from file",e.strerror) 

297 

298 

299 def lifopen(self): 

300 self.__open__() 

301 try: 

302 self.rrec(0) 

303 except LifError: 

304 return 

305 for i in range(256): 

306 self.header[i]= self.buffer[i] 

307 lifmagic= getLifInt(self.header,0,2) 

308 liftype= getLifInt(self.header,20,2) 

309 dirstart=getLifInt(self.header,8,4) 

310# if lifmagic == 0x8000 and liftype == 1 and dirstart == 2: 

311# if lifmagic == 0x8000 and dirstart == 2: 

312 if lifmagic == 0x8000: 

313 self.isLifFile= True 

314 self.dir_start= dirstart 

315 self.dir_length= getLifInt(self.header,16,4) 

316 self.no_tracks= getLifInt(self.header,24,4) 

317 self.no_surfaces= getLifInt(self.header,28,4) 

318 self.no_blocks= getLifInt(self.header,32,4) 

319 self.label= getLifString(self.header,2,6) 

320 self.initdatetime= getLifDateTime(self.header,36) 

321 else: 

322 raise LifError("No valid LIF image file") 

323 

324 def lifclose(self): 

325 self.__close__() 

326 

327 def getLifHeader(self): 

328 if self.isLifFile: 

329 return([self.dir_start, self.dir_length,self.no_tracks, self.no_surfaces,self.no_blocks,self.label,self.initdatetime]) 

330 else: 

331 return None 

332