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