cfad47cfa3/tads3/vmfile.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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 */
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
     */
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
     */
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 */
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
     */
105
    uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; }
106
    uint read_uint2() { char b[2]; read_bytes(b, 2); return osrp2(b); }
107
    int  read_int2()  { char b[2]; read_bytes(b, 2); return osrp2s(b); }
108
    long read_int4()  { char b[4]; read_bytes(b, 4); return osrp4(b); }
109
    ulong read_uint4() { char b[4]; read_bytes(b, 4); return t3rp4u(b); }
110
111
    /* read bytes - throws an error if the bytes cannot be read */
112
    void read_bytes(char *buf, size_t buflen)
113
    {
114
        if (buflen != 0 && osfrb(fp_, buf, buflen))
115
            err_throw(VMERR_READ_FILE);
116
    }
117
118
    /*
119
     *   Write various types to the file.  These routines throw an error
120
     *   if the data cannot be written. 
121
     */
122
    void write_int2(uint v) { char b[2]; oswp2(b, v); write_bytes(b, 2); }
123
    void write_int4(uint v) { char b[4]; oswp4(b, v); write_bytes(b, 4); }
124
125
    /* write bytes - throws an error if the bytes cannot be written */
126
    void write_bytes(const char *buf, size_t buflen)
127
    {
128
        if (buflen != 0 && osfwb(fp_, buf, buflen))
129
            err_throw(VMERR_WRITE_FILE);
130
    }
131
132
    /* get the current seek position */
133
    long get_pos() const { return osfpos(fp_) - seek_base_; }
134
135
    /* seek to a new position */
136
    void set_pos(long seekpos)
137
    {
138
        /* seek relative to the base seek position */
139
        osfseek(fp_, seekpos + seek_base_, OSFSK_SET);
140
    }
141
142
    /* seek to a position relative to the end of the file */
143
    void set_pos_from_eof(long pos) { osfseek(fp_, pos, OSFSK_END); }
144
145
    /* seek to a position relative to the current file position */
146
    void set_pos_from_cur(long pos) { osfseek(fp_, pos, OSFSK_CUR); }
147
148
protected:
149
    /* our underlying OS file handle */
150
    osfildef *fp_;
151
152
    /* 
153
     *   Base seek position - this is useful for virtual byte streams that
154
     *   are embedded in larger files (resource files, for example).
155
     *   set_pos() is always relative to this position.  Note that
156
     *   set_pos_from_eof() is *not* relative to this position, so
157
     *   embedded byte streams must not use set_pos_from_eof() unless the
158
     *   embedded stream ends at the end of the enclosing file. 
159
     */
160
    long seek_base_;
161
};
162
163
/* ------------------------------------------------------------------------ */
164
/*
165
 *   Generic stream interface.  This is a higher-level type of file interface
166
 *   than CVmFile: this interface can be implemented on different kinds of
167
 *   underlying storage formats to provide generic stream access independent
168
 *   of the actual storage implementation.
169
 *   
170
 *   We provide default implementations of the type readers in terms of the
171
 *   low-level byte reader virtual method, which must be implemented by each
172
 *   concrete subclass.  However, all of the type readers are virtual, so
173
 *   subclasses can override these if they can be implemented more
174
 *   efficiently on the underlying stream (for example, some implementations
175
 *   might have ready access to the data in a buffer already, in which case
176
 *   it might be faster to avoid the extra buffer copy of the default
177
 *   implementations of the type readers).  
178
 */
179
class CVmStream
180
{
181
public:
182
    /* read/write a byte */
183
    virtual uchar read_byte() { char b; read_bytes(&b, 1); return (uchar)b; }
184
    virtual void write_byte(uchar b) { write_bytes((char *)&b, 1); }
185
186
    /* 
187
     *   read various integer types (signed 2-byte, unsigned 2-byte, signed
188
     *   4-byte, unsigned 4-byte) 
189
     */
190
    virtual int read_int2() { char b[2]; read_bytes(b, 2); return osrp2s(b); }
191
    virtual uint read_uint2()
192
        { char b[2]; read_bytes(b, 2); return osrp2(b); }
193
    virtual long read_int4() { char b[4]; read_bytes(b, 4); return osrp4(b); }
194
    virtual ulong read_uint4()
195
        { char b[4]; read_bytes(b, 4); return t3rp4u(b); }
196
197
    /* write an integer value */
198
    virtual void write_int2(int i)
199
        { char b[2]; oswp2(b, i); write_bytes(b, 2); }
200
    virtual void write_int4(long l)
201
        { char b[4]; oswp4(b, l); write_bytes(b, 4); }
202
203
    /* read bytes - this must be provided by the implementation */
204
    virtual void read_bytes(char *buf, size_t len) = 0;
205
206
    /* write byte */
207
    virtual void write_bytes(const char *buf, size_t len) = 0;
208
209
    /* get the current seek offset */
210
    virtual long get_seek_pos() const = 0;
211
212
    /* 
213
     *   set the seek offset - this is only valid with offsets previously
214
     *   obtained with get_seek_pos() 
215
     */
216
    virtual void set_seek_pos(long pos) = 0;
217
};
218
219
/*
220
 *   Implementation of the generic stream with an underlying CVmFile object 
221
 */
222
class CVmFileStream: public CVmStream
223
{
224
public:
225
    /* create based on the underlying file object */
226
    CVmFileStream(CVmFile *fp) { fp_ = fp; }
227
228
    /* read bytes */
229
    void read_bytes(char *buf, size_t len) { fp_->read_bytes(buf, len); }
230
231
    /* write bytes */
232
    void write_bytes(const char *buf, size_t len)
233
        { fp_->write_bytes(buf, len); }
234
235
    /* get/set the seek position */
236
    long get_seek_pos() const { return fp_->get_pos(); }
237
    void set_seek_pos(long pos) { fp_->set_pos(pos); }
238
239
private:
240
    /* our underlying file stream */
241
    CVmFile *fp_;
242
};
243
244
/*
245
 *   Implementation of the generic stream reader for reading from memory 
246
 */
247
class CVmMemoryStream: public CVmStream
248
{
249
public:
250
    /* create given the underlying memory buffer */
251
    CVmMemoryStream(char *buf, size_t len)
252
    {
253
        /* remember the buffer */
254
        buf_ = buf;
255
        buflen_ = len;
256
257
        /* start at the start of the buffer */
258
        p_ = buf;
259
    }
260
261
    /* read bytes */
262
    void read_bytes(char *buf, size_t len)
263
    {
264
        /* if we don't have enough bytes left, throw an error */
265
        if (len > (size_t)((buf_ + buflen_) - p_))
266
            err_throw(VMERR_READ_FILE);
267
268
        /* copy the data */
269
        memcpy(buf, p_, len);
270
271
        /* advance past the data we just read */
272
        p_ += len;
273
    }
274
275
    /* write bytes */
276
    void write_bytes(const char *buf, size_t len)
277
    {
278
        /* if we don't have space, throw an error */
279
        if (len > (size_t)((buf_ + buflen_) - p_))
280
            err_throw(VMERR_WRITE_FILE);
281
282
        /* copy the data */
283
        memcpy(p_, buf, len);
284
285
        /* advance past the data */
286
        p_ += len;
287
    }
288
289
    /* get the position - return an offset from the start of our buffer */
290
    long get_seek_pos() const { return p_ - buf_; }
291
292
    /* set the position, as an offset from the start of our buffer */
293
    void set_seek_pos(long pos)
294
    {
295
        /* limit it to the available space */
296
        if (pos < 0)
297
            pos = 0;
298
        else if (pos > (long)buflen_)
299
            pos = buflen_;
300
301
        /* set the position */
302
        p_ = buf_ + pos;
303
    }
304
305
protected:
306
    /* our buffer */
307
    char *buf_;
308
309
    /* size of our buffer */
310
    size_t buflen_;
311
312
    /* current read/write pointer */
313
    char *p_;
314
};
315
316
/*
317
 *   Read-only memory stream 
318
 */
319
class CVmReadOnlyMemoryStream: public CVmMemoryStream
320
{
321
public:
322
    CVmReadOnlyMemoryStream(const char *buf, size_t len)
323
        : CVmMemoryStream((char *)buf, len)
324
    {
325
    }
326
327
    /* disallow writing */
328
    void write_bytes(const char *, size_t) { err_throw(VMERR_WRITE_FILE); }
329
};
330
331
#endif /* VMFILE_H */
332