Essential cleanup for mess involved with reading files, initializing UI and
[blender.git] / source / blender / blenkernel / intern / blender.c
1
2 /*  blender.c   jan 94     MIXED MODEL
3  * 
4  * common help functions and data
5  * 
6  * $Id$
7  *
8  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version. The Blender
14  * Foundation also sells licenses for use in proprietary software under
15  * the Blender License.  See http://www.blender.org/BL/ for information
16  * about this.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
28  * All rights reserved.
29  *
30  * The Original Code is: all of this file.
31  *
32  * Contributor(s): none yet.
33  *
34  * ***** END GPL/BL DUAL LICENSE BLOCK *****
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifndef WIN32 
42     #include <unistd.h> // for read close
43     #include <sys/param.h> // for MAXPATHLEN
44 #else
45     #include <io.h> // for open close read
46 #endif
47
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <fcntl.h> // for open
52
53 #include "MEM_guardedalloc.h"
54 #include "DNA_listBase.h"
55 #include "DNA_sdna_types.h"
56 #include "DNA_userdef_types.h"
57 #include "DNA_object_types.h"
58 #include "DNA_curve_types.h"
59
60 #include "BLI_blenlib.h"
61 #include "BLI_dynstr.h"
62
63 #include "IMB_imbuf_types.h"
64 #include "IMB_imbuf.h"
65
66 #ifdef WIN32
67 #include "BLI_winstuff.h"
68 #endif
69
70 #include "DNA_mesh_types.h"
71 #include "DNA_screen_types.h"
72
73 #include "BKE_library.h"
74 #include "BKE_blender.h"
75 #include "BKE_displist.h"
76 #include "BKE_global.h"
77 #include "BKE_main.h"
78 #include "BKE_object.h"
79 #include "BKE_scene.h"
80 #include "BKE_curve.h"
81 #include "BKE_font.h"
82
83 #include "BLI_editVert.h"
84
85 #include "BLO_undofile.h"
86 #include "BLO_readfile.h" 
87 #include "BLO_writefile.h" 
88
89 #include "BKE_bad_level_calls.h" // for freeAllRad editNurb free_editMesh free_editText free_editArmature
90 #include "BKE_utildefines.h" // O_BINARY FALSE
91 #include "BIF_mainqueue.h" // mainqenter for onload script
92 #include "mydevice.h"
93 #include "nla.h"
94
95 Global G;
96 UserDef U;
97
98 char versionstr[48]= "";
99
100 /* ************************************************ */
101 /* pushpop facility: to store data temporally, FIFO! */
102
103 ListBase ppmain={0, 0};
104
105 typedef struct PushPop {
106         struct PushPop *next, *prev;
107         void *data;
108         int len;
109 } PushPop;
110
111 void pushdata(void *data, int len)
112 {
113         PushPop *pp;
114         
115         pp= MEM_mallocN(sizeof(PushPop), "pushpop");
116         BLI_addtail(&ppmain, pp);
117         pp->data= MEM_mallocN(len, "pushpop");
118         pp->len= len;
119         memcpy(pp->data, data, len);
120 }
121
122 void popfirst(void *data)
123 {
124         PushPop *pp;
125         
126         pp= ppmain.first;
127         if(pp) {
128                 memcpy(data, pp->data, pp->len);
129                 BLI_remlink(&ppmain, pp);
130                 MEM_freeN(pp->data);
131                 MEM_freeN(pp);
132         }
133         else printf("error in popfirst\n");
134 }
135
136 void poplast(void *data)
137 {
138         PushPop *pp;
139         
140         pp= ppmain.last;
141         if(pp) {
142                 memcpy(data, pp->data, pp->len);
143                 BLI_remlink(&ppmain, pp);
144                 MEM_freeN(pp->data);
145                 MEM_freeN(pp);
146         }
147         else printf("error in poplast\n");
148 }
149
150 void free_pushpop()
151 {
152         PushPop *pp;
153
154         pp= ppmain.first;
155         while(pp) {
156                 BLI_remlink(&ppmain, pp);
157                 MEM_freeN(pp->data);
158                 MEM_freeN(pp);
159         }       
160 }
161
162 void pushpop_test()
163 {
164         if(ppmain.first) printf("pushpop not empty\n");
165         free_pushpop();
166 }
167
168
169
170 /* ********** free ********** */
171
172 void free_blender(void)
173 {
174         free_main(G.main);
175         G.main= NULL;
176
177         IMB_freeImBufdata();            /* imbuf lib */
178 }
179
180 void duplicatelist(ListBase *list1, ListBase *list2)  /* copy from 2 to 1 */
181 {
182         struct Link *link1, *link2;
183         
184         list1->first= list1->last= 0;
185         
186         link2= list2->first;
187         while(link2) {
188
189                 link1= MEM_dupallocN(link2);
190                 BLI_addtail(list1, link1);
191                 
192                 link2= link2->next;
193         }       
194 }
195
196 static EditMesh theEditMesh;
197
198 void initglobals(void)
199 {
200         memset(&G, 0, sizeof(Global));
201         
202         G.editMesh = &theEditMesh;
203         memset(G.editMesh, 0, sizeof(G.editMesh));
204
205         U.savetime= 1;
206
207         G.animspeed= 4;
208
209         G.main= MEM_callocN(sizeof(Main), "initglobals");
210
211         strcpy(G.ima, "//");
212
213         G.version= BLENDER_VERSION;
214
215         G.order= 1;
216         G.order= (((char*)&G.order)[0])?L_ENDIAN:B_ENDIAN;
217
218         sprintf(versionstr, "www.blender.org %d", G.version);
219
220 #ifdef _WIN32   // FULLSCREEN
221         G.windowstate = G_WINDOWSTATE_USERDEF;
222 #endif
223
224         clear_workob(); /* object.c */
225 }
226
227 /***/
228
229 static void clear_global(void) 
230 {
231         extern short winqueue_break;    /* screen.c */
232
233         freeAllRad();
234         free_main(G.main); /* free all lib data */
235         freefastshade();        /* othwerwise old lamp settings stay active */
236
237
238         /* prevent hanging vars */      
239         R.backbuf= 0;
240         
241         /* force all queues to be left */
242         winqueue_break= 1;
243         
244         if (G.obedit) {
245                 freeNurblist(&editNurb);
246                 free_editMesh(G.editMesh);
247                 free_editText();
248                 free_editArmature();
249         }
250
251         G.curscreen= NULL;
252         G.scene= NULL;
253         G.main= NULL;
254         
255         G.obedit= NULL;
256         G.obpose= NULL;
257         G.saction= NULL;
258         G.buts= NULL;
259         G.v2d= NULL;
260         G.vd= NULL;
261         G.soops= NULL;
262         G.sima= NULL;
263         G.sipo= NULL;
264         
265         G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT);
266 }
267
268 static void setup_app_data(BlendFileData *bfd, char *filename) 
269 {
270         Object *ob;
271         Base *base;
272         bScreen *curscreen= NULL;
273         Scene *curscene= NULL;
274         char mode;
275         
276         /* 'u' = undo save, 'n' = no UI load */
277         if(bfd->main->screen.first==NULL) mode= 'u';
278         else if(G.fileflags & G_FILE_NO_UI) mode= 'n';
279         else mode= 0;
280         
281         /* no load screens? */
282         if(mode) {
283                 /* comes from readfile.c */
284                 extern void lib_link_screen_restore(Main *, char, Scene *);
285                 
286                 SWAP(ListBase, G.main->screen, bfd->main->screen);
287                 
288                 /* we re-use current screen */
289                 curscreen= G.curscreen;
290                 /* but use new Scene pointer */
291                 curscene= bfd->curscene;
292                 if(curscene==NULL) curscene= bfd->main->scene.first;
293                 /* and we enforce curscene to be in current screen */
294                 curscreen->scene= curscene;
295
296                 /* clear_global will free G.main, here we can still restore pointers */
297                 lib_link_screen_restore(bfd->main, mode, curscene);
298         }
299         
300         clear_global();
301         
302         if(mode!='u') G.save_over = 1;
303         
304         G.main= bfd->main;
305         if (bfd->user) {
306                 
307                 /* only here free userdef themes... */
308                 BLI_freelistN(&U.themes);
309
310                 U= *bfd->user;
311                 MEM_freeN(bfd->user);
312                 
313         }
314         
315         /* case G_FILE_NO_UI or no screens in file */
316         if(mode) {
317                 G.curscreen= curscreen;
318                 G.scene= curscene;
319         }
320         else {
321                 R.winpos= bfd->winpos;
322                 R.displaymode= bfd->displaymode;
323                 G.fileflags= bfd->fileflags;
324                 G.curscreen= bfd->curscreen;
325                 G.scene= G.curscreen->scene;
326         }
327         
328         /* special cases, override loaded flags: */
329         if (G.f & G_DEBUG) bfd->globalf |= G_DEBUG;
330         else bfd->globalf &= ~G_DEBUG;
331         if (G.f & G_SCENESCRIPT) bfd->globalf |= G_SCENESCRIPT;
332         else bfd->globalf &= ~G_SCENESCRIPT;
333
334         G.f= bfd->globalf;
335         
336         /* check posemode */
337         for(base= G.scene->base.first; base; base=base->next) {
338                 ob= base->object;
339                 if(ob->flag & OB_POSEMODE) {
340                         if(ob->type==OB_ARMATURE) G.obpose= ob;
341                 }
342         }
343         
344         /* few DispLists, but do text_to_curve */
345         // this should be removed!!! But first a better displist system (ton)
346         for (ob= G.main->object.first; ob; ob= ob->id.next) {
347                 if(ob->type==OB_FONT) {
348                         Curve *cu= ob->data;
349                         if(cu->nurb.first==0) text_to_curve(ob, 0);
350                 }
351         }
352         
353         if (!G.background) {
354                 setscreen(G.curscreen);
355         }
356                 /* baseflags */
357         set_scene_bg(G.scene);
358
359         if (G.f & G_SCENESCRIPT) {
360                 /* there's an onload scriptlink to execute in screenmain */
361                 mainqenter(ONLOAD_SCRIPT, 1);
362         }
363
364         strcpy(G.sce, filename);
365         strcpy(G.main->name, filename); /* is guaranteed current file */
366         
367         MEM_freeN(bfd);
368 }
369
370 /* returns:
371    0: no load file
372    1: OK
373    2: OK, and with new user settings
374 */
375
376 int BKE_read_file(char *dir, void *type_r) 
377 {
378         BlendReadError bre;
379         BlendFileData *bfd;
380         int retval= 1;
381         
382         if (!G.background)
383                 waitcursor(1);
384                 
385         bfd= BLO_read_from_file(dir, &bre);
386         if (bfd) {
387                 if(bfd->user) retval= 2;
388                 if (type_r)
389                         *((BlenFileType*)type_r)= bfd->type;
390                 
391                 setup_app_data(bfd, dir);
392         } else {
393                 error("Loading %s failed: %s", dir, BLO_bre_as_string(bre));
394         }
395         
396         if (!G.background)
397                 waitcursor(0);
398         
399         return (bfd?retval:0);
400 }
401
402 int BKE_read_file_from_memory(char* filebuf, int filelength, void *type_r)
403 {
404         BlendReadError bre;
405         BlendFileData *bfd;
406         
407         if (!G.background)
408                 waitcursor(1);
409                 
410         bfd= BLO_read_from_memory(filebuf, filelength, &bre);
411         if (bfd) {
412                 if (type_r)
413                         *((BlenFileType*)type_r)= bfd->type;
414                 
415                 setup_app_data(bfd, "<memory>");
416         } else {
417                 error("Loading failed: %s", BLO_bre_as_string(bre));
418         }
419         
420         if (!G.background)
421                 waitcursor(0);
422         
423         return (bfd?1:0);
424 }
425
426 int BKE_read_file_from_memfile(MemFile *memfile)
427 {
428         BlendReadError bre;
429         BlendFileData *bfd;
430         
431         if (!G.background)
432                 waitcursor(1);
433                 
434         bfd= BLO_read_from_memfile(memfile, &bre);
435         if (bfd) {
436                 setup_app_data(bfd, "<memory>");
437         } else {
438                 error("Loading failed: %s", BLO_bre_as_string(bre));
439         }
440         
441         if (!G.background)
442                 waitcursor(0);
443         
444         return (bfd?1:0);
445 }
446
447
448 /* ***************** GLOBAL UNDO *************** */
449
450 #define UNDO_DISK       0
451
452 #define MAXUNDONAME     64
453 typedef struct UndoElem {
454         struct UndoElem *next, *prev;
455         char str[FILE_MAXDIR+FILE_MAXFILE];
456         char name[MAXUNDONAME];
457         MemFile memfile;
458 } UndoElem;
459
460 #define MAXUNDO  32
461 static ListBase undobase={NULL, NULL};
462 static UndoElem *curundo= NULL;
463
464
465 static int read_undosave(UndoElem *uel)
466 {
467         char scestr[FILE_MAXDIR+FILE_MAXFILE];
468         int success=0, fileflags;
469         
470         strcpy(scestr, G.sce);  /* temporal store */
471         fileflags= G.fileflags;
472         G.fileflags |= G_FILE_NO_UI;
473
474         if(UNDO_DISK) 
475                 success= BKE_read_file(uel->str, NULL);
476         else
477                 success= BKE_read_file_from_memfile(&uel->memfile);
478         
479         /* restore */
480         strcpy(G.sce, scestr);
481         G.fileflags= fileflags;
482
483         return success;
484 }
485
486 /* name can be a dynamic string */
487 void BKE_write_undo(char *name)
488 {
489         int nr, success;
490         UndoElem *uel;
491         
492         if( (U.uiflag & USER_GLOBALUNDO)==0) return;
493
494         /* remove all undos after (also when curundo==NULL) */
495         while(undobase.last != curundo) {
496                 uel= undobase.last;
497                 BLI_remlink(&undobase, uel);
498                 BLO_free_memfile(&uel->memfile);
499                 MEM_freeN(uel);
500         }
501         
502         /* make new */
503         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
504         strncpy(uel->name, name, MAXUNDONAME-1);
505         BLI_addtail(&undobase, uel);
506         
507         /* and limit amount to the maximum */
508         nr= 0;
509         uel= undobase.last;
510         while(uel) {
511                 nr++;
512                 if(nr==MAXUNDO) break;
513                 uel= uel->prev;
514         }
515         if(uel) {
516                 while(undobase.first!=uel) {
517                         UndoElem *first= undobase.first;
518                         BLI_remlink(&undobase, first);
519                         /* the merge is because of compression */
520                         BLO_merge_memfile(&first->memfile, &first->next->memfile);
521                         MEM_freeN(first);
522                 }
523         }
524
525
526         /* disk save version */
527         if(UNDO_DISK) {
528                 static int counter= 0;
529                 char *err, tstr[FILE_MAXDIR+FILE_MAXFILE];
530                 char numstr[32];
531                 
532                 /* calculate current filename */
533                 counter++;
534                 counter= counter % MAXUNDO;     
535         
536                 sprintf(numstr, "%d.blend", counter);
537                 BLI_make_file_string("/", tstr, U.tempdir, numstr);
538         
539                 success= BLO_write_file(tstr, G.fileflags, &err);
540                 
541                 strcpy(curundo->str, tstr);
542         }
543         else {
544                 MemFile *prevfile=NULL;
545                 char *err;
546                 
547                 if(curundo->prev) prevfile= &(curundo->prev->memfile);
548                 
549                 success= BLO_write_file_mem(prevfile, &curundo->memfile, G.fileflags, &err);
550                 
551         }
552 }
553
554 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
555 void BKE_undo_step(int step)
556 {
557         
558         if(step==0) {
559                 read_undosave(curundo);
560         }
561         else if(step==1) {
562                 /* curundo should never be NULL, after restart or load file it should call undo_save */
563                 if(curundo==NULL || curundo->prev==NULL) error("No undo available");
564                 else {
565                         if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
566                         curundo= curundo->prev;
567                         read_undosave(curundo);
568                 }
569         }
570         else {
571                 
572                 /* curundo has to remain current situation! */
573                 
574                 if(curundo==NULL || curundo->next==NULL) error("No redo available");
575                 else {
576                         read_undosave(curundo->next);
577                         curundo= curundo->next;
578                         if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
579                 }
580         }
581 }
582
583 void BKE_reset_undo(void)
584 {
585         UndoElem *uel;
586         
587         uel= undobase.first;
588         while(uel) {
589                 BLO_free_memfile(&uel->memfile);
590                 uel= uel->next;
591         }
592         
593         BLI_freelistN(&undobase);
594         curundo= NULL;
595 }
596
597 /* based on index nr it does a restore */
598 void BKE_undo_number(int nr)
599 {
600         UndoElem *uel;
601         int a=1;
602         
603         for(uel= undobase.first; uel; uel= uel->next, a++) {
604                 if(a==nr) break;
605         }
606         curundo= uel;
607         BKE_undo_step(0);
608 }
609
610 char *BKE_undo_menu_string(void)
611 {
612         UndoElem *uel;
613         DynStr *ds= BLI_dynstr_new();
614         char *menu;
615         
616         BLI_dynstr_append(ds, "Global Undo History %t");
617         
618         for(uel= undobase.first; uel; uel= uel->next) {
619                 BLI_dynstr_append(ds, "|");
620                 BLI_dynstr_append(ds, uel->name);
621         }
622         
623         menu= BLI_dynstr_get_cstring(ds);
624         BLI_dynstr_free(ds);
625
626         return menu;
627 }
628
629         /* saves quit.blend */
630 void BKE_undo_save_quit(void)
631 {
632         UndoElem *uel;
633         MemFileChunk *chunk;
634         int file;
635         char str[FILE_MAXDIR+FILE_MAXFILE];
636         
637         if( (U.uiflag & USER_GLOBALUNDO)==0) return;
638         
639         uel= curundo;
640         if(uel==NULL) {
641                 printf("No undo buffer to save recovery file\n");
642                 return;
643         }
644         
645         /* no undo state to save */
646         if(undobase.first==undobase.last) return;
647                 
648         BLI_make_file_string("/", str, U.tempdir, "quit.blend");
649
650         file = open(str,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666);
651         if(file == -1) {
652                 printf("Unable to save %s\n", str);
653                 return;
654         }
655
656         chunk= uel->memfile.chunks.first;
657         while(chunk) {
658                 if( write(file, chunk->buf, chunk->size) != chunk->size) break;
659                 chunk= chunk->next;
660         }
661         
662         close(file);
663         
664         if(chunk) printf("Unable to save %s\n", str);
665         else printf("Saved session recovery to %s\n", str);
666 }
667