Huh! that file was added... anyhoo, here again!
[blender.git] / source / blender / blenloader / intern / undofile.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2004 Blender Foundation
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * .blend file reading entry point
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifdef WIN32
39 #include "BLI_winstuff.h"
40 #endif
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <math.h>
46
47 #include "MEM_guardedalloc.h"
48
49 #include "DNA_ListBase.h"
50 #include "DNA_userdef_types.h"
51
52 #include "BKE_utildefines.h"
53 #include "BKE_global.h"
54
55 #include "BLO_undofile.h"
56
57 #include "BLI_blenlib.h"
58 #include "BLI_linklist.h"
59
60
61
62 /* **************** support for memory-write, for undo buffers *************** */
63
64 /* not memfile itself */
65 void BLO_free_memfile(MemFile *memfile)
66 {
67         MemFileChunk *chunk;
68         
69         while(chunk = (memfile->chunks.first) ) {
70                 if(chunk->ident==0) MEM_freeN(chunk->buf);
71                 BLI_remlink(&memfile->chunks, chunk);
72                 MEM_freeN(chunk);
73         }
74         memfile->size= 0;
75 }
76
77 /* to keep list of memfiles consistant, 'first' is always first in list */
78 /* result is that 'first' is being freed */
79 void BLO_merge_memfile(MemFile *first, MemFile *second)
80 {
81         MemFileChunk *fc, *sc;
82         
83         fc= first->chunks.first;
84         sc= second->chunks.first;
85         while (fc || sc) {
86                 if(fc && sc) {
87                         if(sc->ident) {
88                                 sc->ident= 0;
89                                 fc->ident= 1;
90                         }
91                 }
92                 if(fc) fc= fc->next;
93                 if(sc) sc= sc->next;
94         }
95         
96         BLO_free_memfile(first);
97 }
98
99 static int my_memcmp(int *mem1, int *mem2, int len)
100 {
101         register int a= len, *mema= mem1, *memb= mem2;
102         
103         while(a--) {
104                 if( *mema != *memb) return 1;
105                 mema++;
106                 memb++;
107         }
108         return 0;
109 }
110
111 void add_memfilechunk(MemFile *compare, MemFile *current, char *buf, unsigned int size)
112 {
113         static MemFileChunk *compchunk=NULL;
114         MemFileChunk *curchunk;
115         
116         /* this function inits when compare != NULL or when current==NULL */
117         if(compare) {
118                 compchunk= compare->chunks.first;
119                 return;
120         }
121         if(current==NULL) {
122                 compchunk= NULL;
123                 return;
124         }
125         
126         curchunk= MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk");
127         curchunk->size= size;
128         curchunk->buf= NULL;
129         curchunk->ident= 0;
130         BLI_addtail(&current->chunks, curchunk);
131         
132         /* we compare compchunk with buf */
133         if(compchunk) {
134                 if(compchunk->size == curchunk->size) {
135                         if( my_memcmp((int *)compchunk->buf, (int *)buf, size/4)==0) {
136                                 curchunk->buf= compchunk->buf;
137                                 curchunk->ident= 1;
138                         }
139                 }
140                 compchunk= compchunk->next;
141         }
142         
143         /* not equal... */
144         if(curchunk->buf==NULL) {
145                 curchunk->buf= MEM_mallocN(size, "Chunk buffer");
146                 memcpy(curchunk->buf, buf, size);
147                 current->size += size;
148         }
149 }
150
151 /* ***************** GLOBAL UNDO *************** */
152
153 #define UNDO_DISK       0
154
155 #define MAXUNDONAME     64
156 typedef struct UndoElem {
157         struct UndoElem *next, *prev;
158         char str[FILE_MAXDIR+FILE_MAXFILE];
159         char name[MAXUNDONAME];
160         MemFile memfile;
161 } UndoElem;
162
163 #define MAXUNDO  32
164 static ListBase undobase={NULL, NULL};
165 static UndoElem *curundo= NULL;
166
167
168 static int read_undosave(UndoElem *uel)
169 {
170         char scestr[FILE_MAXDIR+FILE_MAXFILE];
171         int success=0, fileflags;
172         
173         strcpy(scestr, G.sce);  /* temporal store */
174         fileflags= G.fileflags;
175         G.fileflags |= G_FILE_NO_UI;
176
177         if(UNDO_DISK) 
178                 success= BKE_read_file(uel->str, NULL);
179         else
180                 success= BKE_read_file_from_memfile(&uel->memfile);
181         
182         /* restore */
183         strcpy(G.sce, scestr);
184         G.fileflags= fileflags;
185
186         return success;
187 }
188
189 /* name can be a dynamic string */
190 void BIF_write_undo(char *name)
191 {
192         int nr, success;
193         UndoElem *uel;
194         
195         if( (U.uiflag & USER_GLOBALUNDO)==0) return;
196
197         /* remove all undos after (also when curundo==NULL) */
198         while(undobase.last != curundo) {
199                 uel= undobase.last;
200                 BLI_remlink(&undobase, uel);
201                 BLO_free_memfile(&uel->memfile);
202                 MEM_freeN(uel);
203         }
204         
205         /* make new */
206         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
207         strncpy(uel->name, name, MAXUNDONAME-1);
208         BLI_addtail(&undobase, uel);
209         
210         /* and limit amount to the maximum */
211         nr= 0;
212         uel= undobase.last;
213         while(uel) {
214                 nr++;
215                 if(nr==MAXUNDO) break;
216                 uel= uel->prev;
217         }
218         if(uel) {
219                 while(undobase.first!=uel) {
220                         UndoElem *first= undobase.first;
221                         BLI_remlink(&undobase, first);
222                         /* the merge is because of compression */
223                         BLO_merge_memfile(&first->memfile, &first->next->memfile);
224                         MEM_freeN(first);
225                 }
226         }
227
228
229         /* disk save version */
230         if(UNDO_DISK) {
231                 static int counter= 0;
232                 char *err, tstr[FILE_MAXDIR+FILE_MAXFILE];
233                 char numstr[32];
234                 
235                 /* calculate current filename */
236                 counter++;
237                 counter= counter % MAXUNDO;     
238         
239                 sprintf(numstr, "%d.blend", counter);
240                 BLI_make_file_string("/", tstr, U.tempdir, numstr);
241         
242                 success= BLO_write_file(tstr, G.fileflags, &err);
243                 
244                 strcpy(curundo->str, tstr);
245         }
246         else {
247                 MemFile *prevfile=NULL;
248                 char *err;
249                 
250                 if(curundo->prev) prevfile= &(curundo->prev->memfile);
251                 
252                 success= BLO_write_file_mem(prevfile, &curundo->memfile, G.fileflags, &err);
253                 
254         }
255 }
256
257 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
258 void BIF_undo_step(int step)
259 {
260         
261         if(step==1) {
262                 /* curundo should never be NULL, after restart or load file it should call undo_save */
263                 if(curundo==NULL || curundo->prev==NULL) error("No undo available");
264                 else {
265                         printf("undo %s\n", curundo->name);
266                         curundo= curundo->prev;
267                         read_undosave(curundo);
268                 }
269         }
270         else {
271                 
272                 /* curundo has to remain current situation! */
273                 
274                 if(curundo==NULL || curundo->next==NULL) error("No redo available");
275                 else {
276                         read_undosave(curundo->next);
277                         curundo= curundo->next;
278                         printf("redo %s\n", curundo->name);
279                 }
280         }
281 }
282
283 void BIF_reset_undo(void)
284 {
285         UndoElem *uel;
286         
287         uel= undobase.first;
288         while(uel) {
289                 BLO_free_memfile(&uel->memfile);
290                 uel= uel->next;
291         }
292         
293         BLI_freelistN(&undobase);
294         curundo= NULL;
295 }
296
297
298