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
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
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
51class LifError(Exception):
52 def __init__(self,msg,add_msg=None):
53 self.msg=msg
54 self.add_msg= add_msg
56class cls_LifDir:
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
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
98 if self.lastblock == 0:
99 self.lastblock= self.liffile.dir_start+self.liffile.dir_length-1
100 self.isOpen=True
102 def rewind(self):
103 if not self.isOpen:
104 raise LifError("Directory not open","")
105 self.cur_entry=0
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]]
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]
246class cls_LifFile:
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
264 def set_filename(self,name):
265 self.filename= name
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)
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)
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)
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")
324 def lifclose(self):
325 self.__close__()
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