2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/editor/brundo.c,v 1.14 2000/02/19 12:27:48 toml Exp $
7 // brush level undo code
9 #include <stdlib.h> // all the S's
29 #include <dbmem.h> // must be last header!
31 static BOOL editUndoInProgress
=FALSE
;
32 static BOOL editUndoSuspended
=FALSE
;
33 static editBrush start_brush
;
35 extern bool set_brush_to_time_n(editBrush
*br
, int n
);
37 //#define UNDO_STACK_TRACE
38 #ifdef UNDO_STACK_TRACE
39 #define editUndoStoreAndTraceAction(action) do { undoStoreAction(action); editUndoStackPrint(); } while (0)
41 #define editUndoStoreAndTraceAction(action) undoStoreAction(action)
44 #define noUndoRedoActions (editUndoInProgress||editUndoSuspended)
54 editBrush total
; // has the brush id inside it, of course
65 #define MAX_UNDO_BRUSH_OP (UNDO_CREATE)
67 #define UNDO_BLOCK_ST 4
68 #define UNDO_BLOCK_END 5
71 #ifdef FANCY_DELTA_STUFF
73 // a delta structure looks like
81 // the deltalist_header starts with the current total length
83 #define DELTA_HEADER_SIZE (sizeof(uchar)*2)
84 #define DELTALIST_HEADER_SIZE (sizeof(short)*1)
85 #define delta_header(len,offset)
86 #define delta_size(len) (DELTA_HEADER_SIZE+sizeof(uchar)*len)
87 #define deltalist_get_len(dl) (*((short *)dl))
89 static void *_storeDelta(void **dlist
, uchar
*o
, uchar
*n
, int offset
, int len
)
94 *dlist
=(void *)Malloc(DELTALIST_HEADER_SIZE
+delta_size(len
));
97 deltalist_len
=(int)deltalist_get_len(dlist
);
98 *dlist
=(void *)Realloc(dlist
,deltalist_len
+delta_size(len
));
100 ptr
=((uchar
*)*dlist
)+deltalist_len
;
102 *ptr
++=(uchar
)offset
;
103 memcpy(ptr
,o
+offset
,len
);
104 memcpy(ptr
+len
,n
+offset
,len
);
105 *((short *)*dlist
)=(short)deltalist_len
+delta_size(len
);
109 // returns a deltalist that it has malloced
110 static void *_buildBrushDelta(editBrush
*old_br
, editBrush
*new_br
)
112 uchar
*o
=(uchar
*)old_br
, *n
=(uchar
*)new_br
;
116 for (i
=0; i
<sizeof(editBrush
); i
++)
118 bool diff
=(*(o
+i
)!=*(n
+i
));
119 if (diff
&&(missmatch
==-1))
121 if (!diff
&&(missmatch
!=-1))
123 delta
=_storeDelta(&delta
,o
,n
,missmatch
,i
-missmatch
);
131 // this is to be called before performing an action which will modify a brush
132 // for now it saves the whole brush
133 // TODO: eventually, it should check to see if top of undo stack is this
134 // brush, and know not to bother if it is. unless your last actions was an
135 // undo, in which case it should. of course, really really it should scan
136 // the brush and record just the delta. hmmm, maybe ill do that now.
137 // so for now, full is ignored, since we always do full
138 static void *_buildBrushState(int type
, editBrush
*us
, bool full
)
140 undoBrushState
*newU
;
141 newU
=(undoBrushState
*)Malloc(sizeof(undoBrushState
));
143 newU
->time
=blistCheck(us
);
148 static void *_buildData(int type
, int data1
, int data2
)
151 newU
=(undoData
*)Malloc(sizeof(undoData
));
158 static void *_restoreBrushState(undoGeneric
*undoRec
, bool create
)
160 undoBrushState
*lastU
=(undoBrushState
*)undoRec
;
166 us
=brushCopy(&lastU
->total
);
167 us
->br_id
=lastU
->total
.br_id
; // preseve brush id
169 // Undelete objects properly
171 if (brushGetType(us
) == brType_OBJECT
)
173 editObjCreateObjFromBrush(OBJ_NULL
, us
);
175 blistInsert(us
); // and insert, so if we reverse it brush id will still be valid
176 blistSetPostoBrush(us
);
177 set_brush_to_time_n(us
, lastU
->time
);
178 we_switched_brush_focus();
181 { // for now, this gross hack to try and make undo/redo for texture changes more useful
183 if (lastU
->total
.br_id
==VIRTUAL_BRUSH_ID
)
185 if ((us
=vBrush_UndoVBrush(&lastU
->total
))!=NULL
)
187 rv
=_buildBrushState(UNDO_BRUSH
,us
,FALSE
);
191 Warning(("MultiBrush Undo failed\n"));
193 else if ((us
=brFind(lastU
->total
.br_id
))!=NULL
)
195 BOOL tdelta
=gedundo_check_texture_delta(&lastU
->total
,us
);
196 rv
=_buildBrushState(UNDO_BRUSH
,us
,FALSE
);
199 gedundo_do_texture_delta(us
);
202 Warning(("Cant find undo brush %d!!\n",lastU
->total
.br_id
));
207 // for now, just saves off the current brush, for use or not
208 void editUndoStoreStart(editBrush
*us
)
210 if (!noUndoRedoActions
&& (us
!=NULL
))
214 // this is the guts of the undo thinking - which will someday be smart and sensible...
215 // till then, this hack
217 // ok, lastU->total is the brush in the current undo (ie. state we will currently undo to)
218 // start_brush is where we started this frame (often != lastU)
219 // us is the brush as it currently exists
220 // we hand them to gedit, it tells us existing thing is ok, or to save it
221 void editUndoStoreBrush(editBrush
*us
)
223 undoBrushState
*lastU
=(undoBrushState
*)undoPeek();
224 editBrush
*base
=NULL
;
226 if (noUndoRedoActions
) return; // well, the easy case
227 if (lastU
&&lastU
->type
==UNDO_BRUSH
) base
=&lastU
->total
;
228 if (gedundo_check_brush_delta(us
,&start_brush
,base
))
230 void *newAction
=_buildBrushState(UNDO_BRUSH
,&start_brush
,FALSE
);
231 editUndoStoreAndTraceAction(newAction
);
232 // mprintf("new action for brush %d record (tx %d - old %d)\n",us->br_id,us->tx_id,start_brush.tx_id);
235 // mprintf("punt store of brush, we think it is the same\n");
238 // these both store full, so that neither needs to change for redo or anything
239 // ie. the del and create records are just backwards versions of eachother
240 // which have the same packet, and direction+type indicates which to do
241 void editUndoStoreDelete(editBrush
*us
)
243 if (!noUndoRedoActions
)
244 editUndoStoreAndTraceAction(_buildBrushState(UNDO_DELETE
,us
,TRUE
));
247 void editUndoStoreCreate(editBrush
*us
)
249 if (!noUndoRedoActions
)
250 editUndoStoreAndTraceAction(_buildBrushState(UNDO_CREATE
,us
,TRUE
));
253 // this is used to store off block starts and stops
254 // so that you can treat a set of undo's as a block
255 // and a single call to undo/redo will do all of them
256 void editUndoStoreBlock(BOOL start_of_block
)
258 undoBrushState
*lastU
=(undoBrushState
*)undoPeek();
260 if (noUndoRedoActions
) return;
261 if (!start_of_block
&& (lastU
!=NULL
) && (lastU
->type
==UNDO_BLOCK_ST
))
264 editUndoStoreAndTraceAction(_buildData(start_of_block
?UNDO_BLOCK_ST
:UNDO_BLOCK_END
,-1,-1));
267 void editUndoStoreGroup(int old_group
, int new_group
)
269 if (!noUndoRedoActions
)
270 editUndoStoreAndTraceAction(_buildData(UNDO_GROUP
,old_group
,new_group
));
273 void editUndoDoGroupOp(undoData
*groupOp
, BOOL is_undo
)
276 vBrush_GoToGroup(groupOp
->data1
);
278 vBrush_GoToGroup(groupOp
->data2
);
281 void editUndoSuspend(BOOL suspend
)
283 editUndoSuspended
=suspend
;
286 static int getBrushIdfromUndo(undoGeneric
*und
)
288 undoBrushState
*lastU
=(undoBrushState
*)und
;
289 return lastU
->total
.br_id
;
292 bool editUndoDoUndo(void)
298 editUndoInProgress
=TRUE
;
300 undoGeneric
*lastUndo
=(undoGeneric
*)undoDoUndo();
304 switch (lastUndo
->type
)
307 editUndoDoGroupOp((undoData
*)lastUndo
,TRUE
);
316 undoUndoReplace(_restoreBrushState(lastUndo
,FALSE
));
319 us
=brFind(getBrushIdfromUndo(lastUndo
));
320 vBrush_DeletePtr(us
);
323 _restoreBrushState(lastUndo
,TRUE
);
326 } while (in_block
&&rv
);
327 editUndoInProgress
=FALSE
;
331 bool editUndoDoRedo(void)
337 editUndoInProgress
=TRUE
;
339 undoGeneric
*lastUndo
=(undoGeneric
*)undoDoRedo();
343 switch (lastUndo
->type
)
346 editUndoDoGroupOp((undoData
*)lastUndo
,FALSE
);
355 undoRedoReplace(_restoreBrushState(lastUndo
,FALSE
));
358 _restoreBrushState(lastUndo
,TRUE
);
361 us
=brFind(getBrushIdfromUndo(lastUndo
));
362 vBrush_DeletePtr(us
);
365 } while (in_block
&&rv
);
366 editUndoInProgress
=FALSE
;
371 void _undostackwalkcallback(void *action
, bool next
)
373 undoGeneric
*tmpUndo
=(undoGeneric
*)action
;
374 mprintf("%s",next
?"[":"(");
377 else if (tmpUndo
->type
<=MAX_UNDO_BRUSH_OP
)
379 ((undoGeneric
*)action
)->type
,
380 ((undoBrushState
*)action
)->total
.br_id
);
382 mprintf("%d",((undoGeneric
*)action
)->type
);
383 mprintf("%s",next
?"]":")");
386 void editUndoStackPrint(void)
388 undoDumpStack(_undostackwalkcallback
);