1 /* $Header: d:/cvsroot/tads/tads3/VMFILE.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */
2 
3 /*
4  *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5  *
6  *   Please see the accompanying license file, LICENSE.TXT, for information
7  *   on using and copying this software.
8  */
9 /*
10 Name
11   vmfile.h - VM external file interface
12 Function
13 
14 Notes
15 
16 Modified
17   10/28/98 MJRoberts  - Creation
18 */
19 
20 #ifndef VMFILE_H
21 #define VMFILE_H
22 
23 #include "t3std.h"
24 #include "vmerr.h"
25 
26 /* ------------------------------------------------------------------------ */
27 /*
28  *   VM external file interface.  The VM uses this interface for
29  *   manipulating files that contain program images and saved state.
30  */
31 
32 class CVmFile
33 {
34 public:
35     /* create a new file object */
CVmFile()36     CVmFile()
37     {
38         /* no file yet */
39         fp_ = 0;
40 
41         /* presume the base seek position is at the start of the file */
42         seek_base_ = 0;
43     }
44 
45     /* delete the file */
46     ~CVmFile();
47 
48     /*
49      *   Set the file to use an existing underlying OS file handle.  The
50      *   seek_base value gives the byte offset within the OS file handle
51      *   of our virtual byte stream; this is useful if the file object is
52      *   handling a byte stream embedded within a larger OS file (such as
53      *   a resource within a file containing multiple resources).  If
54      *   we're simply providing access to the entire underlying file, the
55      *   seek_base value should be zero.
56      */
set_file(osfildef * fp,long seek_base)57     void set_file(osfildef *fp, long seek_base)
58     {
59         /* remember the file handle and seek position */
60         fp_ = fp;
61         seek_base_ = seek_base;
62     }
63 
64     /*
65      *   Detach from the underlying OS file handle.  Normally, when we are
66      *   deleted, we'll close our underlying OS file handle.  If the
67      *   caller has a separate reference to the OS file handle and wants
68      *   to keep the handle open after deleting us, the caller should use
69      *   this function to detach us from the OS file handle before
70      *   deleting us.
71      *
72      *   After calling this routine, no operations can be performed on the
73      *   underlying file through this object, since we will have forgotten
74      *   the file handle.
75      */
detach_file()76     void detach_file()
77     {
78         /* forget our file handle */
79         fp_ = 0;
80     }
81 
82     /*
83      *   Open an existing file for reading.  Throws an error if the file
84      *   does not exist.
85      */
86     void open_read(const char *fname, os_filetype_t typ);
87 
88     /*
89      *   Create a file for writing, replacing any existing file.  Throws
90      *   an error if the file cannot be created.
91      */
92     void open_write(const char *fname, os_filetype_t typ);
93 
94     /* close the underlying file */
close()95     void close()
96     {
97         osfcls(fp_);
98         fp_ = 0;
99     }
100 
101     /*
102      *   Read various types from the file.  These routines throw an error
103      *   if the data cannot be read.
104      */
read_byte()105     uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; }
read_uint2()106     uint read_uint2() { char b[2]; read_bytes(b, 2); return osrp2(b); }
read_int2()107     int  read_int2()  { char b[2]; read_bytes(b, 2); return osrp2s(b); }
read_int4()108     long read_int4()  { char b[4]; read_bytes(b, 4); return osrp4(b); }
109 
110     /* read bytes - throws an error if the bytes cannot be read */
read_bytes(char * buf,size_t buflen)111     void read_bytes(char *buf, size_t buflen)
112     {
113         if (buflen != 0 && osfrb(fp_, buf, buflen))
114             err_throw(VMERR_READ_FILE);
115     }
116 
117     /*
118      *   Write various types to the file.  These routines throw an error
119      *   if the data cannot be written.
120      */
write_int2(uint v)121     void write_int2(uint v) { char b[2]; oswp2(b, v); write_bytes(b, 2); }
write_int4(uint v)122     void write_int4(uint v) { char b[4]; oswp4(b, v); write_bytes(b, 4); }
123 
124     /* write bytes - throws an error if the bytes cannot be written */
write_bytes(const char * buf,size_t buflen)125     void write_bytes(const char *buf, size_t buflen)
126     {
127         if (buflen != 0 && osfwb(fp_, buf, buflen))
128             err_throw(VMERR_WRITE_FILE);
129     }
130 
131     /* get the current seek position */
get_pos()132     long get_pos() const { return osfpos(fp_) - seek_base_; }
133 
134     /* seek to a new position */
set_pos(long seekpos)135     void set_pos(long seekpos)
136     {
137         /* seek relative to the base seek position */
138         osfseek(fp_, seekpos + seek_base_, OSFSK_SET);
139     }
140 
141     /* seek to a position relative to the end of the file */
set_pos_from_eof(long pos)142     void set_pos_from_eof(long pos) { osfseek(fp_, pos, OSFSK_END); }
143 
144     /* seek to a position relative to the current file position */
set_pos_from_cur(long pos)145     void set_pos_from_cur(long pos) { osfseek(fp_, pos, OSFSK_CUR); }
146 
147 protected:
148     /* our underlying OS file handle */
149     osfildef *fp_;
150 
151     /*
152      *   Base seek position - this is useful for virtual byte streams that
153      *   are embedded in larger files (resource files, for example).
154      *   set_pos() is always relative to this position.  Note that
155      *   set_pos_from_eof() is *not* relative to this position, so
156      *   embedded byte streams must not use set_pos_from_eof() unless the
157      *   embedded stream ends at the end of the enclosing file.
158      */
159     long seek_base_;
160 };
161 
162 /* ------------------------------------------------------------------------ */
163 /*
164  *   Generic stream interface.  This is a higher-level type of file interface
165  *   than CVmFile: this interface can be implemented on different kinds of
166  *   underlying storage formats to provide generic stream access independent
167  *   of the actual storage implementation.
168  *
169  *   We provide default implementations of the type readers in terms of the
170  *   low-level byte reader virtual method, which must be implemented by each
171  *   concrete subclass.  However, all of the type readers are virtual, so
172  *   subclasses can override these if they can be implemented more
173  *   efficiently on the underlying stream (for example, some implementations
174  *   might have ready access to the data in a buffer already, in which case
175  *   it might be faster to avoid the extra buffer copy of the default
176  *   implementations of the type readers).
177  */
178 class CVmStream
179 {
180 public:
181     /* read/write a byte */
read_byte()182     virtual uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; }
write_byte(uchar b)183     virtual void write_byte(uchar b) { write_bytes((char *)&b, 1); }
184 
185     /* read an integer value (2-byte, unsigned 2-byte, 4-byte) */
read_int2()186     virtual int read_int2() { char b[2]; read_bytes(b, 2); return osrp2s(b); }
read_uint2()187     virtual int read_uint2() { char b[2]; read_bytes(b, 2); return osrp2(b); }
read_int4()188     virtual long read_int4() { char b[4]; read_bytes(b, 4); return osrp4(b); }
189 
190     /* write an integer value */
write_int2(int i)191     virtual void write_int2(int i)
192         { char b[2]; oswp2(b, i); write_bytes(b, 2); }
write_int4(long l)193     virtual void write_int4(long l)
194         { char b[4]; oswp4(b, l); write_bytes(b, 4); }
195 
196     /* read bytes - this must be provided by the implementation */
197     virtual void read_bytes(char *buf, size_t len) = 0;
198 
199     /* write byte */
200     virtual void write_bytes(const char *buf, size_t len) = 0;
201 
202     /* get the current seek offset */
203     virtual long get_seek_pos() const = 0;
204 
205     /*
206      *   set the seek offset - this is only valid with offsets previously
207      *   obtained with get_seek_pos()
208      */
209     virtual void set_seek_pos(long pos) = 0;
210 };
211 
212 /*
213  *   Implementation of the generic stream with an underlying CVmFile object
214  */
215 class CVmFileStream: public CVmStream
216 {
217 public:
218     /* create based on the underlying file object */
CVmFileStream(CVmFile * fp)219     CVmFileStream(CVmFile *fp) { fp_ = fp; }
220 
221     /* read bytes */
read_bytes(char * buf,size_t len)222     void read_bytes(char *buf, size_t len) { fp_->read_bytes(buf, len); }
223 
224     /* write bytes */
write_bytes(const char * buf,size_t len)225     void write_bytes(const char *buf, size_t len)
226         { fp_->write_bytes(buf, len); }
227 
228     /* get/set the seek position */
get_seek_pos()229     long get_seek_pos() const { return fp_->get_pos(); }
set_seek_pos(long pos)230     void set_seek_pos(long pos) { fp_->set_pos(pos); }
231 
232 private:
233     /* our underlying file stream */
234     CVmFile *fp_;
235 };
236 
237 /*
238  *   Implementation of the generic stream reader for reading from memory
239  */
240 class CVmMemoryStream: public CVmStream
241 {
242 public:
243     /* create given the underlying memory buffer */
CVmMemoryStream(char * buf,size_t len)244     CVmMemoryStream(char *buf, size_t len)
245     {
246         /* remember the buffer */
247         buf_ = buf;
248         buflen_ = len;
249 
250         /* start at the start of the buffer */
251         p_ = buf;
252     }
253 
254     /* read bytes */
read_bytes(char * buf,size_t len)255     void read_bytes(char *buf, size_t len)
256     {
257         /* if we don't have enough bytes left, throw an error */
258         if (len > (size_t)((buf_ + buflen_) - p_))
259             err_throw(VMERR_READ_FILE);
260 
261         /* copy the data */
262         memcpy(buf, p_, len);
263 
264         /* advance past the data we just read */
265         p_ += len;
266     }
267 
268     /* write bytes */
write_bytes(const char * buf,size_t len)269     void write_bytes(const char *buf, size_t len)
270     {
271         /* if we don't have space, throw an error */
272         if (len > (size_t)((buf_ + buflen_) - p_))
273             err_throw(VMERR_WRITE_FILE);
274 
275         /* copy the data */
276         memcpy(p_, buf, len);
277 
278         /* advance past the data */
279         p_ += len;
280     }
281 
282     /* get the position - return an offset from the start of our buffer */
get_seek_pos()283     long get_seek_pos() const { return p_ - buf_; }
284 
285     /* set the position, as an offset from the start of our buffer */
set_seek_pos(long pos)286     void set_seek_pos(long pos)
287     {
288         /* limit it to the available space */
289         if (pos < 0)
290             pos = 0;
291         else if (pos > (long)buflen_)
292             pos = buflen_;
293 
294         /* set the position */
295         p_ = buf_ + pos;
296     }
297 
298 protected:
299     /* our buffer */
300     char *buf_;
301 
302     /* size of our buffer */
303     size_t buflen_;
304 
305     /* current read/write pointer */
306     char *p_;
307 };
308 
309 /*
310  *   Read-only memory stream
311  */
312 class CVmReadOnlyMemoryStream: public CVmMemoryStream
313 {
314 public:
CVmReadOnlyMemoryStream(const char * buf,size_t len)315     CVmReadOnlyMemoryStream(const char *buf, size_t len)
316         : CVmMemoryStream((char *)buf, len)
317     {
318     }
319 
320     /* disallow writing */
write_bytes(const char *,size_t)321     void write_bytes(const char *, size_t) { err_throw(VMERR_WRITE_FILE); }
322 };
323 
324 #endif /* VMFILE_H */
325 
326