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