1 /* ScummVM - Graphic Adventure Engine 2 * 3 * ScummVM is the legal property of its developers, whose names 4 * are too numerous to list here. Please refer to the COPYRIGHT 5 * file distributed with this source distribution. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #ifndef GLK_TADS_TADS2_OBJECT 24 #define GLK_TADS_TADS2_OBJECT 25 26 #include "glk/tads/tads.h" 27 #include "glk/tads/tads2/lib.h" 28 #include "glk/tads/tads2/memory_cache.h" 29 #include "glk/tads/tads2/property.h" 30 31 namespace Glk { 32 namespace TADS { 33 namespace TADS2 { 34 35 /** 36 * object number 37 */ 38 typedef ushort objnum; 39 40 /** 41 * For non-class objects, we'll leave some space free in the object so 42 * that a few properties can be added without having to resize the 43 * object. Class objects will probably never have anything added, so 44 * there's no need for extra space. 45 */ 46 #define OBJEXTRA 64 47 48 # define OBJFCLS 0x01 /* object is a class */ 49 50 /** 51 * The object structure is actually laid out portably, using unaligned 52 * 2-byte arrays, stored least significant byte first, for each ushort 53 * (including the objnum array for the superclasses). The actual 54 * entries are at these offsets on all machines: 55 * 56 * objws 0 57 * objflg 2 58 * objnsc 4 59 * objnprop 6 60 * objfree 8 61 * objrst 10 62 * objstat 12 63 * objsc[0] 14 64 * objsc[1] 16 65 * etc 66 * 67 * If the OBJFINDEX flag is set, the object has a property index. 68 * The index occurs after the last superclass (so it's where the 69 * property data would go if there were no index), and the property 70 * data follows. Each index entry consists of a pair of two-byte 71 * entries: the first is the property number, and the second is 72 * its offset within the object. For performance reasons, an index 73 * is only built on a class object -- whenever a property is changed 74 * within an object, the entire index must be rebuilt, because the 75 * locations of many properties within the object can be changed by 76 * a single property change in the object. The index is ordered by 77 * property number, so it can be searched using a binary search. 78 * Furthermore, "ignored" properties are excluded from the index; 79 * only the active instance of a particular property is stored. 80 * The index must be maintained by all routines that can change 81 * property information: setp, delp, revert, etc. 82 * 83 * Preceding the index table is a two-byte entry that gives the 84 * offset of the properties. Since the properties immediately 85 * follow the index, this can be used to deduce how large a space 86 * is available for the index itself. 87 */ 88 typedef uchar objdef; 89 #define OBJDEFSIZ 14 /* "sizeof(objdef)" - size of object header w/o sc's */ 90 91 /* object flags */ 92 #define OBJFCLASS 1 /* object is a class */ 93 #define OBJFINDEX 2 /* object has a property index */ 94 #define OBJFMOD 4 /* object has been modified by a newer definition */ 95 96 /* undo context */ 97 struct objucxdef { 98 mcmcxdef *objucxmem; /* cache manager context */ 99 errcxdef *objucxerr; /* error context */ 100 ushort objucxsiz; /* size of the undo buffer */ 101 ushort objucxhead; /* head (position of next write) */ 102 ushort objucxtail; /* tail (position of oldest record) */ 103 ushort objucxprv; /* previous head pointer */ 104 ushort objucxtop; /* highest head value written */ 105 void (*objucxcun)(void *ctx, uchar *data); 106 /* apply a client undo record */ 107 ushort (*objucxcsz)(void *ctx, uchar *data); 108 /* get size of a client undo record */ 109 void *objucxccx; /* client undo context */ 110 uchar objucxbuf[1]; /* undo buffer */ 111 }; 112 113 /* 114 * Undo records are kept in a circular buffer allocated as part of an 115 * undo context. Offsets within the buffer are kept for the head, tail, 116 * and previous head records. The head always points to the byte at 117 * which the next undo record will be written. The previous head points 118 * to the most recently written undo record; it contains a back link to 119 * the undo record before that, and so forth back through the entire 120 * chain. (These reverse links are necessary because undo records vary 121 * in size depending on the data contained within.) The tail points to 122 * the oldest undo record that's still in the buffer. Conceptually, the 123 * head is always "above" the tail in the buffer; since the buffer is 124 * circular, the tail may have a higher address, but this just means 125 * that the buffer wraps around at the top. When the head bumps into 126 * the tail (i.e., the head address is physically below or equal to the 127 * tail address, and the head is then advanced so that its address 128 * becomes higher than the tail's), the tail is advanced by discarding 129 * as many of the least recent undo records as necessary to make room 130 * for the new head position. When the head and the previous head point 131 * to the same place, we have no undo records in the buffer. 132 */ 133 /** 134 * The first byte of an undo record specifies what action is to be 135 * undone. If a property was added, it is undone merely by deleting the 136 * property. If a property was changed, it is undone by setting the 137 * property back to its old value. An additional special flag indicates 138 * a "savepoint." Normally, all changes back to a savepoint will be 139 * undone. 140 */ 141 #define OBJUADD 1 /* a property was added (undo by deleting) */ 142 #define OBJUCHG 2 /* a property was changed (change back to old value) */ 143 #define OBJUSAV 3 /* savepoint marker (no property information) */ 144 #define OBJUOVR 4 /* override original property (set orig to IGNORE) */ 145 #define OBJUCLI 5 /* client undo record (any client data) */ 146 147 /* 148 * After the control byte (OBJUxxx), the object number, property 149 * number, datatype, and data value will follow; some or all of these 150 * may be omitted, depending on the control byte. 151 */ 152 153 /* get object flags */ 154 #define objflg(o) ((ushort)osrp2(((char *)(o)) + 2)) 155 156 /* get object flags */ 157 #define objsflg(o, val) oswp2(((char *)(o)) + 2, val) 158 159 /* given an object pointer, get a pointer to the first prpdef */ 160 /* prpdef *objprp(objdef *objptr); */ 161 #define objprp(o) ((prpdef *)(objsc(o) + 2*objnsc(o))) 162 163 /* given an object pointer, get number of properties in the prpdef */ 164 /* int objnprop(objdef *objptr); */ 165 #define objnprop(o) ((ushort)osrp2(((char *)(o)) + 6)) 166 167 /* set number of properties */ 168 /* void objsnp(objdef *objptr, int newnum); */ 169 #define objsnp(o,n) oswp2(((char *)(o)) + 6, n) 170 171 /* given an object pointer, get offset of free space */ 172 /* int objfree(objdef *objptr); */ 173 #define objfree(o) ((ushort)osrp2(((char *)(o)) + 8)) 174 175 /* set free space pointer */ 176 /* void objsfree(objdef *objptr, int newfree); */ 177 #define objsfree(o,n) oswp2(((char *)(o)) + 8, n) 178 179 /* get number of static properties */ 180 /* ushort objstat(objdef *objptr); */ 181 #define objstat(o) ((ushort)osrp2(((char *)(o)) + 10)) 182 183 /* set number of static properties */ 184 /* void objsetst(objdef *objptr, int newstat); */ 185 #define objsetst(o,n) oswp2(((char *)(o)) + 10, n) 186 187 /* get reset size (size of static properties) */ 188 /* ushort objrst(objdef *objptr); */ 189 #define objrst(o) ((ushort)osrp2(((char *)(o)) + 12)) 190 191 /* set reset size */ 192 /* void objsetrst(objdef *objptr, uint newrst); */ 193 #define objsetrst(o,n) oswp2(((char *)(o)) + 12, n) 194 195 /* given an object pointer, get first superclass pointer */ 196 /* uchar *objsc(objdef *objptr); */ 197 #define objsc(o) (((uchar *)(o)) + OBJDEFSIZ) 198 199 /* given an object pointer, get number of superclasses */ 200 /* int objnsc(objdef *objptr); */ 201 #define objnsc(o) ((ushort)osrp2(((char *)(o)) + 4)) 202 203 /* set number of superclasses */ 204 /* void objsnsc(objdef *objptr, int num); */ 205 #define objsnsc(o,n) oswp2(((char *)(o)) + 4, n) 206 207 /* given a prpdef, get the next prpdef */ 208 /* prpdef *objpnxt(prpdef *p); */ 209 #define objpnxt(p) \ 210 ((prpdef *)(((uchar *)(p)) + PRPHDRSIZ + prpsize(p))) 211 212 /* get pointer to free prpdef */ 213 /* prpdef *objpfre(objdef *objptr); */ 214 #define objpfre(o) ((prpdef *)(((uchar *)(o)) + objfree(o))) 215 216 /* given a prpdef and an object pointer, compute the prpdef offset */ 217 /* uint objpofs(objdef *objptr, prpdef *propptr); */ 218 #define objpofs(o,p) ((uint)((p) ? (((uchar *)(p)) - ((uchar *)(o))) : 0)) 219 220 /* given an object pointer and a property offset, get prpdef pointer */ 221 /* prpdef *objofsp(objdef *objptr, uint propofs); */ 222 #define objofsp(o,ofs) ((prpdef *)((ofs) ? (((uchar *)(o)) + (ofs)) : 0)) 223 224 /* 225 * Get the first superclass of an object. If it doesn't have any 226 * superclasses, return invalid. 227 */ 228 objnum objget1sc(mcmcxdef *ctx, objnum objn); 229 230 /* 231 * Get an object's property WITHOUT INHERITANCE. If the object has the 232 * indicated property set, the byte OFFSET of the prpdef within the 233 * object is returned. The offset will remain valid until any type of 234 * operation that sets a property in the object (such as objdelp, 235 * objsetp, or an undo operation). An offset of zero means that the 236 * property was not set in the object. 237 */ 238 uint objgetp(mcmcxdef *ctx, objnum objn, prpnum prop, 239 dattyp *typptr); 240 241 /* 242 * Get the *ending* offset of the given property's value, without any 243 * inheritance. Returns the byte offset one past the end of the 244 * property's data. 245 */ 246 uint objgetp_end(mcmcxdef *ctx, objnum objn, prpnum prop); 247 248 /* 249 * Get a property of an object, either from the object or from a 250 * superclass (inherited). If the inh flag is TRUE, we do not look 251 * at all in the object itself, but restrict our search to inherited 252 * properties only. We return the byte OFFSET of the prpdef within 253 * the object in which the prpdef is found; the superclass object 254 * itself is NOT locked upon return, but we will NOT unlock the 255 * object passed in. If the offset is zero, the property was not 256 * found. The offset returned is valid until any operation that 257 * sets a property in the object (such as objdelp, objsetp, or an 258 * undo operation). 259 */ 260 uint objgetap(mcmcxdef *ctx, noreg objnum objn, prpnum prop, 261 objnum *orn, int inh); 262 263 /* 264 * expand an object by a requested amount, returning a pointer to the 265 * object's new location if it must be moved. The object will be 266 * unlocked and relocked by this call. On return, the actual amount 267 * of space ADDED to the object will be returned. 268 */ 269 objdef *objexp(mcmcxdef *ctx, objnum obj, ushort *siz); 270 271 /* 272 * Set an object's property, deleting the original value of the 273 * property if it existed. If an undo context is provided, write an 274 * undo record for the change; if the undo context pointer is null, no 275 * undo information is retained. 276 */ 277 void objsetp(mcmcxdef *ctx, objnum obj, prpnum prop, 278 dattyp typ, const void *val, objucxdef *undoctx); 279 280 /* 281 * Delete a property. If mark_only is true, we'll only mark the 282 * property as deleted without actually reclaiming its space; this is 283 * necessary when removing a code property (type DAT_CODE) any time 284 * other code properties may follow, because p-code is not entirely 285 * self-relative and thus can't always be relocated within an object. 286 */ 287 void objdelp(mcmcxdef *mctx, objnum objn, prpnum prop, int mark_only); 288 289 /* 290 * Set up for emitting code into an object. Writes a property header 291 * of type 'code', and returns the offset of the next free byte in the 292 * object. Call objendemt when done. The datatype argument is 293 * provided so that list generation can be done through the same 294 * mechanism, since parser lists must be converted to run-time 295 * lists via the code generator. 296 */ 297 uint objemt(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp typ); 298 299 /* done emitting code into property, finish setting object info */ 300 void objendemt(mcmcxdef *ctx, objnum objn, prpnum prop, uint endofs); 301 302 /* 303 * Determine if undo records should be kept. Undo records should be 304 * kept only if a savepoint is present in the undo log. If no savepoint 305 * is present, adding undo records would be useless, since it will not 306 * be possible to apply the undo information. 307 */ 308 int objuok(objucxdef *undoctx); 309 310 /* 311 * Reserve space in an undo buffer, deleting old records as needed. 312 * Returns a pointer to the reserved space. 313 */ 314 uchar *objures(objucxdef *undoctx, uchar cmd, ushort siz); 315 316 /* advance the tail pointer in an undo buffer over the record it points to */ 317 void objutadv(objucxdef *undoctx); 318 319 /* apply one undo record, and remove it from undo list */ 320 void obj1undo(mcmcxdef *mctx, objucxdef *undoctx); 321 322 /* 323 * Undo back to the most recent savepoint. If there is no savepoint in 324 * the undo list, NOTHING will be undone. This prevents reaching an 325 * inconsistent state in which some, but not all, of the operations 326 * between two savepoints are undone: either all operations between two 327 * savepoints will be undone, or none will. 328 */ 329 void objundo(mcmcxdef *mctx, objucxdef *undoctx); 330 331 /* set an undo savepoint */ 332 void objusav(objucxdef *undoctx); 333 334 /* initialize undo context */ 335 objucxdef *objuini(mcmcxdef *memctx, ushort undosiz, 336 void (*undocb)(void *ctx, uchar *data), 337 ushort (*sizecb)(void *ctx, uchar *data), 338 void *callctx); 339 340 /* free the undo context - releases memory allocated by objuini() */ 341 void objuterm(objucxdef *undoctx); 342 343 /* discard all undo context (for times such as restarting) */ 344 void objulose(objucxdef *undoctx); 345 346 /* 347 * Allocate and initialize a new object. The caller specifies the 348 * number of superclasses to be allocated, and the amount of space (in 349 * bytes) for the object's property data. The caller must fill in the 350 * superclass array. Upon return, the object is allocated and locked, 351 * and is initialized with no properties. A pointer to the object's 352 * memory is returned, and *objnptr receives the object number. 353 */ 354 objdef *objnew(mcmcxdef *mctx, int sccnt, ushort propspace, 355 objnum *objnptr, int classflg); 356 357 /* initialize an already allocated object */ 358 void objini(mcmcxdef *mctx, int sccnt, objnum objn, int classflg); 359 360 /* 361 * Add space for additional superclasses to an object. The object can 362 * already have some properties set (if it doesn't, it can just be 363 * reinitialized). 364 */ 365 void objaddsc(mcmcxdef *mctx, int sccnt, objnum objn); 366 367 /* 368 * Delete an object's properties and superclasses. The 'mindel' 369 * parameter specifies the minimum property number to be deleted. 370 * Properties below this are considered "system" properties that are not 371 * to be deleted. This could be used by a development environment to 372 * store the source for an object as a special system property in the 373 * object; when the object is recompiled, all of the object's properties 374 * and superclasses must be deleted except the source property, which is 375 * retained even after recompilation. 376 */ 377 void objclr(mcmcxdef *mctx, objnum objn, prpnum mindel); 378 379 /* Build or rebuild an object's property index */ 380 void objindx(mcmcxdef *mctx, objnum objn); 381 382 /* set up just-compiled object: mark static part and original properties */ 383 void objcomp(mcmcxdef *mctx, objnum objn, int for_debug); 384 385 /* revert an object to original post-compilation state */ 386 void objrevert(void *mctx, mcmon objn); 387 388 /* reset 'ignore' flags for a newly reconstructed object */ 389 void objsetign(mcmcxdef *mctx, objnum objn); 390 391 392 } // End of namespace TADS2 393 } // End of namespace TADS 394 } // End of namespace Glk 395 396 #endif 397