/* writefile.c * * .blend file writing * * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ /* FILEFORMAT: IFF-style structure (but not IFF compatible!) start file: BLENDER_V100 12 bytes (versie 1.00) V = big endian, v = little endian _ = 4 byte pointer, - = 8 byte pointer datablocks: also see struct BHead 4 chars int, len data after BHead void, old pointer int int, in case of array: amount of structs data ... ... Almost all data in Blender are structures. Each struct saved gets a BHead header. With BHead the struct can be linked again and compared with StructDNA . WRITE Preferred writing order: (not really a must, but why would you do it random?) Any case: direct data is ALWAYS after the lib block (Local file data) - for each LibBlock - write LibBlock - write associated direct data (External file data) - per library - write library block - per LibBlock - write the ID of LibBlock - write FileGlobal (some global vars) - write SDNA - write USER if filename is ~/.B.blend */ /* for version 2.2+ Important to know is that 'streaming' has been added to files, for Blender Publisher */ #ifdef HAVE_CONFIG_H #include #endif #ifndef WIN32 #include #else #include "winsock2.h" #include "BLI_winstuff.h" #include #include // for getpid #endif #include #include #include #include #include #include "nla.h" // __NLA is defined #include "DNA_packedFile_types.h" #include "DNA_sdna_types.h" #include "DNA_property_types.h" #include "DNA_sensor_types.h" #include "DNA_controller_types.h" #include "DNA_actuator_types.h" #include "DNA_effect_types.h" #include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "DNA_vfont_types.h" #include "DNA_ipo_types.h" #include "DNA_curve_types.h" #include "DNA_camera_types.h" #include "DNA_meta_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_material_types.h" #include "DNA_lattice_types.h" #include "DNA_armature_types.h" #include "DNA_sequence_types.h" #include "DNA_ika_types.h" #include "DNA_group_types.h" #include "DNA_oops_types.h" #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "DNA_lamp_types.h" #include "DNA_fileglobal_types.h" #include "DNA_sound_types.h" #include "DNA_texture_types.h" #include "DNA_text_types.h" #include "DNA_image_types.h" #include "DNA_key_types.h" #include "DNA_scene_types.h" #include "DNA_constraint_types.h" #include "DNA_listBase.h" /* for Listbase, the type of samples, ...*/ #include "DNA_action_types.h" #include "DNA_nla_types.h" #include "MEM_guardedalloc.h" // MEM_freeN #include "BLI_blenlib.h" #include "BLI_linklist.h" #include "BKE_action.h" #include "BKE_bad_level_calls.h" // build_seqar (from WHILE_SEQ) free_oops error #include "BKE_curve.h" #include "BKE_constraint.h" #include "BKE_global.h" // for G #include "BKE_library.h" // for set_listbasepointers #include "BKE_main.h" // G.main #include "BKE_packedFile.h" // for packAll #include "BKE_screen.h" // for waitcursor #include "BKE_scene.h" // for do_seq #include "BKE_sound.h" /* ... and for samples */ #include "BKE_utildefines.h" // for defines #include "GEN_messaging.h" #include "BLO_writefile.h" #include "BLO_readfile.h" #include "BLO_undofile.h" #include "readfile.h" #include "genfile.h" /* ********* my write, buffered writing with minimum 50k chunks ************ */ typedef struct { struct SDNA *sdna; int file; unsigned char *buf; MemFile *compare, *current; int tot, count, error, memsize; } WriteData; static WriteData *writedata_new(int file) { extern char DNAstr[]; /* DNA.c */ extern int DNAlen; WriteData *wd= MEM_callocN(sizeof(*wd), "writedata"); /* XXX, see note about this in readfile.c, remove * once we have an xp lock - zr */ wd->sdna= dna_sdna_from_data(DNAstr, DNAlen, 0); wd->file= file; wd->buf= MEM_mallocN(100000, "wd->buf"); return wd; } static void writedata_do_write(WriteData *wd, void *mem, int memlen) { if (wd->error) return; /* memory based save */ if(wd->current) { add_memfilechunk(NULL, wd->current, mem, memlen); } else { if (write(wd->file, mem, memlen) != memlen) wd->error= 1; } } static void writedata_free(WriteData *wd) { dna_freestructDNA(wd->sdna); MEM_freeN(wd->buf); MEM_freeN(wd); } /***/ int mywfile; /** * Low level WRITE(2) wrapper that buffers data * @param adr Pointer to new chunk of data * @param len Length of new chunk of data * @warning Talks to other functions with global parameters */ #define MYWRITE_FLUSH NULL static void mywrite( WriteData *wd, void *adr, int len) { if (wd->error) return; if(adr==MYWRITE_FLUSH) { if(wd->count) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } return; } wd->tot+= len; if(len>50000) { if(wd->count) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } writedata_do_write(wd, adr, len); return; } if(len+wd->count>99999) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } memcpy(&wd->buf[wd->count], adr, len); wd->count+= len; } /** * BeGiN initializer for mywrite * @param file File descriptor * @param write_flags Write parameters * @warning Talks to other functions with global parameters */ static WriteData *bgnwrite(int file, MemFile *compare, MemFile *current, int write_flags) { WriteData *wd= writedata_new(file); wd->compare= compare; wd->current= current; /* this inits comparing */ add_memfilechunk(compare, NULL, NULL, 0); return wd; } /** * END the mywrite wrapper * @return 1 if write failed * @return unknown global variable otherwise * @warning Talks to other functions with global parameters */ static int endwrite(WriteData *wd) { int err; if (wd->count) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } err= wd->error; writedata_free(wd); /* blender gods may live forever but this parent pointer died in the statement above if(wd->current) printf("undo size %d\n", wd->current->size); */ return err; } /* ********** WRITE FILE ****************** */ static void writestruct(WriteData *wd, int filecode, char *structname, int nr, void *adr) { BHead bh; short *sp; if(adr==0 || nr==0) return; /* init BHead */ bh.code= filecode; bh.old= adr; bh.nr= nr; bh.SDNAnr= dna_findstruct_nr(wd->sdna, structname); if(bh.SDNAnr== -1) { printf("error: can't find SDNA code %s\n", structname); return; } sp= wd->sdna->structs[bh.SDNAnr]; bh.len= nr*wd->sdna->typelens[sp[0]]; if(bh.len==0) return; mywrite(wd, &bh, sizeof(BHead)); mywrite(wd, adr, bh.len); } static void writedata(WriteData *wd, int filecode, int len, void *adr) /* do not use for structs */ { BHead bh; if(adr==0) return; if(len==0) return; len+= 3; len-= ( len % 4); /* init BHead */ bh.code= filecode; bh.old= adr; bh.nr= 1; bh.SDNAnr= 0; bh.len= len; mywrite(wd, &bh, sizeof(BHead)); if(len) mywrite(wd, adr, len); } static void write_scriptlink(WriteData *wd, ScriptLink *slink) { writedata(wd, DATA, sizeof(void *)*slink->totscript, slink->scripts); writedata(wd, DATA, sizeof(short)*slink->totscript, slink->flag); } static void write_renderinfo(WriteData *wd) /* for renderdaemon */ { Scene *sce; int data[8]; sce= G.main->scene.first; while(sce) { if(sce->id.lib==0 && ( sce==G.scene || (sce->r.scemode & R_BG_RENDER)) ) { data[0]= sce->r.sfra; data[1]= sce->r.efra; strncpy((char *)(data+2), sce->id.name+2, 23); writedata(wd, REND, 32, data); } sce= sce->id.next; } } static void write_userdef(WriteData *wd) { bTheme *btheme; writestruct(wd, USER, "UserDef", 1, &U); btheme= U.themes.first; while(btheme) { writestruct(wd, DATA, "bTheme", 1, btheme); btheme= btheme->next; } } static void write_effects(WriteData *wd, ListBase *lb) { Effect *eff; eff= lb->first; while(eff) { switch(eff->type) { case EFF_BUILD: writestruct(wd, DATA, "BuildEff", 1, eff); break; case EFF_PARTICLE: writestruct(wd, DATA, "PartEff", 1, eff); break; case EFF_WAVE: writestruct(wd, DATA, "WaveEff", 1, eff); break; default: writedata(wd, DATA, MEM_allocN_len(eff), eff); } eff= eff->next; } } static void write_properties(WriteData *wd, ListBase *lb) { bProperty *prop; prop= lb->first; while(prop) { writestruct(wd, DATA, "bProperty", 1, prop); if(prop->poin && prop->poin != &prop->data) writedata(wd, DATA, MEM_allocN_len(prop->poin), prop->poin); prop= prop->next; } } static void write_sensors(WriteData *wd, ListBase *lb) { bSensor *sens; sens= lb->first; while(sens) { writestruct(wd, DATA, "bSensor", 1, sens); writedata(wd, DATA, sizeof(void *)*sens->totlinks, sens->links); switch(sens->type) { case SENS_NEAR: writestruct(wd, DATA, "bNearSensor", 1, sens->data); break; case SENS_MOUSE: writestruct(wd, DATA, "bMouseSensor", 1, sens->data); break; case SENS_TOUCH: writestruct(wd, DATA, "bTouchSensor", 1, sens->data); break; case SENS_KEYBOARD: writestruct(wd, DATA, "bKeyboardSensor", 1, sens->data); break; case SENS_PROPERTY: writestruct(wd, DATA, "bPropertySensor", 1, sens->data); break; case SENS_COLLISION: writestruct(wd, DATA, "bCollisionSensor", 1, sens->data); break; case SENS_RADAR: writestruct(wd, DATA, "bRadarSensor", 1, sens->data); break; case SENS_RANDOM: writestruct(wd, DATA, "bRandomSensor", 1, sens->data); break; case SENS_RAY: writestruct(wd, DATA, "bRaySensor", 1, sens->data); break; case SENS_MESSAGE: writestruct(wd, DATA, "bMessageSensor", 1, sens->data); break; default: ; /* error: don't know how to write this file */ } sens= sens->next; } } static void write_controllers(WriteData *wd, ListBase *lb) { bController *cont; cont= lb->first; while(cont) { writestruct(wd, DATA, "bController", 1, cont); writedata(wd, DATA, sizeof(void *)*cont->totlinks, cont->links); switch(cont->type) { case CONT_EXPRESSION: writestruct(wd, DATA, "bExpressionCont", 1, cont->data); break; case CONT_PYTHON: writestruct(wd, DATA, "bPythonCont", 1, cont->data); break; default: ; /* error: don't know how to write this file */ } cont= cont->next; } } static void write_actuators(WriteData *wd, ListBase *lb) { bActuator *act; act= lb->first; while(act) { writestruct(wd, DATA, "bActuator", 1, act); switch(act->type) { case ACT_ACTION: writestruct(wd, DATA, "bActionActuator", 1, act->data); break; case ACT_SOUND: writestruct(wd, DATA, "bSoundActuator", 1, act->data); break; case ACT_CD: writestruct(wd, DATA, "bCDActuator", 1, act->data); break; case ACT_OBJECT: writestruct(wd, DATA, "bObjectActuator", 1, act->data); break; case ACT_IPO: writestruct(wd, DATA, "bIpoActuator", 1, act->data); break; case ACT_PROPERTY: writestruct(wd, DATA, "bPropertyActuator", 1, act->data); break; case ACT_CAMERA: writestruct(wd, DATA, "bCameraActuator", 1, act->data); break; case ACT_CONSTRAINT: writestruct(wd, DATA, "bConstraintActuator", 1, act->data); break; case ACT_EDIT_OBJECT: writestruct(wd, DATA, "bEditObjectActuator", 1, act->data); break; case ACT_SCENE: writestruct(wd, DATA, "bSceneActuator", 1, act->data); break; case ACT_GROUP: writestruct(wd, DATA, "bGroupActuator", 1, act->data); break; case ACT_RANDOM: writestruct(wd, DATA, "bRandomActuator", 1, act->data); break; case ACT_MESSAGE: writestruct(wd, DATA, "bMessageActuator", 1, act->data); break; case ACT_GAME: writestruct(wd, DATA, "bGameActuator", 1, act->data); break; case ACT_VISIBILITY: writestruct(wd, DATA, "bVisibilityActuator", 1, act->data); break; default: ; /* error: don't know how to write this file */ } act= act->next; } } static void write_nlastrips(WriteData *wd, ListBase *nlabase) { bActionStrip *strip; for (strip=nlabase->first; strip; strip=strip->next) writestruct(wd, DATA, "bActionStrip", 1, strip); } static void write_constraints(WriteData *wd, ListBase *conlist) { bConstraint *con; for (con=conlist->first; con; con=con->next) { /* Write the specific data */ switch (con->type) { case CONSTRAINT_TYPE_NULL: break; case CONSTRAINT_TYPE_TRACKTO: writestruct(wd, DATA, "bTrackToConstraint", 1, con->data); break; case CONSTRAINT_TYPE_KINEMATIC: writestruct(wd, DATA, "bKinematicConstraint", 1, con->data); break; case CONSTRAINT_TYPE_ROTLIKE: writestruct(wd, DATA, "bRotateLikeConstraint", 1, con->data); break; case CONSTRAINT_TYPE_LOCLIKE: writestruct(wd, DATA, "bLocateLikeConstraint", 1, con->data); break; case CONSTRAINT_TYPE_ACTION: writestruct(wd, DATA, "bActionConstraint", 1, con->data); break; case CONSTRAINT_TYPE_LOCKTRACK: writestruct(wd, DATA, "bLockTrackConstraint", 1, con->data); break; case CONSTRAINT_TYPE_FOLLOWPATH: writestruct(wd, DATA, "bFollowPathConstraint", 1, con->data); break; case CONSTRAINT_TYPE_STRETCHTO: writestruct(wd, DATA, "bStretchToConstraint", 1, con->data); break; default: break; } /* Write the constraint */ writestruct(wd, DATA, "bConstraint", 1, con); } } static void write_pose(WriteData *wd, bPose *pose) { bPoseChannel *chan; /* Write each channel */ if (!pose) return; // Write channels for (chan=pose->chanbase.first; chan; chan=chan->next) { write_constraints(wd, &chan->constraints); writestruct(wd, DATA, "bPoseChannel", 1, chan); } // Write this pose writestruct(wd, DATA, "bPose", 1, pose); } static void write_defgroups(WriteData *wd, ListBase *defbase) { bDeformGroup *defgroup; for (defgroup=defbase->first; defgroup; defgroup=defgroup->next) writestruct(wd, DATA, "bDeformGroup", 1, defgroup); } static void write_constraint_channels(WriteData *wd, ListBase *chanbase) { bConstraintChannel *chan; for (chan = chanbase->first; chan; chan=chan->next) writestruct(wd, DATA, "bConstraintChannel", 1, chan); } static void write_objects(WriteData *wd, ListBase *idbase) { Object *ob; ObHook *hook; ob= idbase->first; while(ob) { if(ob->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_OB, "Object", 1, ob); /* direct data */ writedata(wd, DATA, sizeof(void *)*ob->totcol, ob->mat); write_effects(wd, &ob->effect); write_properties(wd, &ob->prop); write_sensors(wd, &ob->sensors); write_controllers(wd, &ob->controllers); write_actuators(wd, &ob->actuators); write_scriptlink(wd, &ob->scriptlink); write_pose(wd, ob->pose); write_defgroups(wd, &ob->defbase); write_constraints(wd, &ob->constraints); write_constraint_channels(wd, &ob->constraintChannels); write_nlastrips(wd, &ob->nlastrips); writestruct(wd, DATA, "PartDeflect", 1, ob->pd); for(hook= ob->hooks.first; hook; hook= hook->next) { writestruct(wd, DATA, "ObHook", 1, hook); writedata(wd, DATA, sizeof(int)*hook->totindex, hook->indexar); } } ob= ob->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_vfonts(WriteData *wd, ListBase *idbase) { VFont *vf; PackedFile * pf; vf= idbase->first; while(vf) { if(vf->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_VF, "VFont", 1, vf); /* direct data */ if (vf->packedfile) { pf = vf->packedfile; writestruct(wd, DATA, "PackedFile", 1, pf); writedata(wd, DATA, pf->size, pf->data); } } vf= vf->id.next; } } static void write_ipos(WriteData *wd, ListBase *idbase) { Ipo *ipo; IpoCurve *icu; ipo= idbase->first; while(ipo) { if(ipo->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_IP, "Ipo", 1, ipo); /* direct data */ icu= ipo->curve.first; while(icu) { writestruct(wd, DATA, "IpoCurve", 1, icu); icu= icu->next; } icu= ipo->curve.first; while(icu) { if(icu->bezt) writestruct(wd, DATA, "BezTriple", icu->totvert, icu->bezt); if(icu->bp) writestruct(wd, DATA, "BPoint", icu->totvert, icu->bp); icu= icu->next; } } ipo= ipo->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_keys(WriteData *wd, ListBase *idbase) { Key *key; KeyBlock *kb; key= idbase->first; while(key) { if(key->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_KE, "Key", 1, key); /* direct data */ kb= key->block.first; while(kb) { writestruct(wd, DATA, "KeyBlock", 1, kb); if(kb->data) writedata(wd, DATA, kb->totelem*key->elemsize, kb->data); kb= kb->next; } } key= key->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_cameras(WriteData *wd, ListBase *idbase) { Camera *cam; cam= idbase->first; while(cam) { if(cam->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_CA, "Camera", 1, cam); /* direct data */ write_scriptlink(wd, &cam->scriptlink); } cam= cam->id.next; } } static void write_mballs(WriteData *wd, ListBase *idbase) { MetaBall *mb; MetaElem *ml; mb= idbase->first; while(mb) { if(mb->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_MB, "MetaBall", 1, mb); /* direct data */ writedata(wd, DATA, sizeof(void *)*mb->totcol, mb->mat); ml= mb->elems.first; while(ml) { writestruct(wd, DATA, "MetaElem", 1, ml); ml= ml->next; } } mb= mb->id.next; } } static void write_curves(WriteData *wd, ListBase *idbase) { Curve *cu; Nurb *nu; cu= idbase->first; while(cu) { if(cu->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_CU, "Curve", 1, cu); /* direct data */ writedata(wd, DATA, sizeof(void *)*cu->totcol, cu->mat); if(cu->vfont) { writedata(wd, DATA, cu->len+1, cu->str); } else { /* is also the order of reading */ nu= cu->nurb.first; while(nu) { writestruct(wd, DATA, "Nurb", 1, nu); nu= nu->next; } nu= cu->nurb.first; while(nu) { if( (nu->type & 7)==CU_BEZIER) writestruct(wd, DATA, "BezTriple", nu->pntsu, nu->bezt); else { writestruct(wd, DATA, "BPoint", nu->pntsu*nu->pntsv, nu->bp); if(nu->knotsu) writedata(wd, DATA, KNOTSU(nu)*sizeof(float), nu->knotsu); if(nu->knotsv) writedata(wd, DATA, KNOTSV(nu)*sizeof(float), nu->knotsv); } nu= nu->next; } } } cu= cu->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_dverts(WriteData *wd, int count, MDeformVert *dvlist) { int i; /* Write the dvert list */ writestruct(wd, DATA, "MDeformVert", count, dvlist); /* Write deformation data for each dvert */ if (dvlist) { for (i=0; ifirst; while(mesh) { if(mesh->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_ME, "Mesh", 1, mesh); /* direct data */ writedata(wd, DATA, sizeof(void *)*mesh->totcol, mesh->mat); writestruct(wd, DATA, "MVert", mesh->totvert, mesh->mvert); writestruct(wd, DATA, "MEdge", mesh->totedge, mesh->medge); writestruct(wd, DATA, "MFace", mesh->totface, mesh->mface); writestruct(wd, DATA, "TFace", mesh->totface, mesh->tface); writestruct(wd, DATA, "MCol", 4*mesh->totface, mesh->mcol); writestruct(wd, DATA, "MSticky", mesh->totvert, mesh->msticky); write_dverts(wd, mesh->totvert, mesh->dvert); } mesh= mesh->id.next; } } static void write_images(WriteData *wd, ListBase *idbase) { Image *ima; PackedFile * pf; ima= idbase->first; while(ima) { if(ima->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_IM, "Image", 1, ima); if (ima->packedfile) { pf = ima->packedfile; writestruct(wd, DATA, "PackedFile", 1, pf); writedata(wd, DATA, pf->size, pf->data); } } ima= ima->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_textures(WriteData *wd, ListBase *idbase) { Tex *tex; tex= idbase->first; while(tex) { if(tex->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_TE, "Tex", 1, tex); /* direct data */ if(tex->plugin) writestruct(wd, DATA, "PluginTex", 1, tex->plugin); if(tex->coba) writestruct(wd, DATA, "ColorBand", 1, tex->coba); if(tex->env) writestruct(wd, DATA, "EnvMap", 1, tex->env); } tex= tex->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_materials(WriteData *wd, ListBase *idbase) { Material *ma; int a; ma= idbase->first; while(ma) { if(ma->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_MA, "Material", 1, ma); for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, ma->mtex[a]); } if(ma->ramp_col) writestruct(wd, DATA, "ColorBand", 1, ma->ramp_col); if(ma->ramp_spec) writestruct(wd, DATA, "ColorBand", 1, ma->ramp_spec); write_scriptlink(wd, &ma->scriptlink); } ma= ma->id.next; } } static void write_worlds(WriteData *wd, ListBase *idbase) { World *wrld; int a; wrld= idbase->first; while(wrld) { if(wrld->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_WO, "World", 1, wrld); for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, wrld->mtex[a]); } write_scriptlink(wd, &wrld->scriptlink); } wrld= wrld->id.next; } } static void write_lamps(WriteData *wd, ListBase *idbase) { Lamp *la; int a; la= idbase->first; while(la) { if(la->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_LA, "Lamp", 1, la); /* direct data */ for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, la->mtex[a]); } write_scriptlink(wd, &la->scriptlink); } la= la->id.next; } } static void write_lattices(WriteData *wd, ListBase *idbase) { Lattice *lt; lt= idbase->first; while(lt) { if(lt->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_LT, "Lattice", 1, lt); /* direct data */ writestruct(wd, DATA, "BPoint", lt->pntsu*lt->pntsv*lt->pntsw, lt->def); } lt= lt->id.next; } } static void write_ikas(WriteData *wd, ListBase *idbase) { Ika *ika; Limb *li; ika= idbase->first; while(ika) { if(ika->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_IK, "Ika", 1, ika); /* direct data */ li= ika->limbbase.first; while(li) { writestruct(wd, DATA, "Limb", 1, li); li= li->next; } writestruct(wd, DATA, "Deform", ika->totdef, ika->def); } ika= ika->id.next; } } static void write_scenes(WriteData *wd, ListBase *scebase) { Scene *sce; Base *base; Editing *ed; Sequence *seq; MetaStack *ms; Strip *strip; sce= scebase->first; while(sce) { /* write LibData */ writestruct(wd, ID_SCE, "Scene", 1, sce); /* direct data */ base= sce->base.first; while(base) { writestruct(wd, DATA, "Base", 1, base); base= base->next; } writestruct(wd, DATA, "Radio", 1, sce->radio); ed= sce->ed; if(ed) { writestruct(wd, DATA, "Editing", 1, ed); /* reset write flags too */ WHILE_SEQ(&ed->seqbase) { if(seq->strip) seq->strip->done= 0; writestruct(wd, DATA, "Sequence", 1, seq); } END_SEQ WHILE_SEQ(&ed->seqbase) { if(seq->strip && seq->strip->done==0) { /* write strip with 'done' at 0 because readfile */ if(seq->plugin) writestruct(wd, DATA, "PluginSeq", 1, seq->plugin); if(seq->effectdata) { switch(seq->type){ case SEQ_WIPE: writestruct(wd, DATA, "WipeVars", 1, seq->effectdata); break; case SEQ_GLOW: writestruct(wd, DATA, "GlowVars", 1, seq->effectdata); break; } } strip= seq->strip; writestruct(wd, DATA, "Strip", 1, strip); if(seq->type==SEQ_IMAGE) writestruct(wd, DATA, "StripElem", strip->len, strip->stripdata); else if(seq->type==SEQ_MOVIE || seq->type==SEQ_SOUND) writestruct(wd, DATA, "StripElem", 1, strip->stripdata); strip->done= 1; } } END_SEQ /* new; meta stack too, even when its nasty restore code */ for(ms= ed->metastack.first; ms; ms= ms->next) { writestruct(wd, DATA, "MetaStack", 1, ms); } } write_scriptlink(wd, &sce->scriptlink); if (sce->r.avicodecdata) { writestruct(wd, DATA, "AviCodecData", 1, sce->r.avicodecdata); if (sce->r.avicodecdata->lpFormat) writedata(wd, DATA, sce->r.avicodecdata->cbFormat, sce->r.avicodecdata->lpFormat); if (sce->r.avicodecdata->lpParms) writedata(wd, DATA, sce->r.avicodecdata->cbParms, sce->r.avicodecdata->lpParms); } if (sce->r.qtcodecdata) { writestruct(wd, DATA, "QuicktimeCodecData", 1, sce->r.qtcodecdata); if (sce->r.qtcodecdata->cdParms) writedata(wd, DATA, sce->r.qtcodecdata->cdSize, sce->r.qtcodecdata->cdParms); } sce= sce->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_screens(WriteData *wd, ListBase *scrbase) { bScreen *sc; ScrArea *sa; ScrVert *sv; ScrEdge *se; sc= scrbase->first; while(sc) { /* write LibData */ writestruct(wd, ID_SCR, "Screen", 1, sc); /* direct data */ sv= sc->vertbase.first; while(sv) { writestruct(wd, DATA, "ScrVert", 1, sv); sv= sv->next; } se= sc->edgebase.first; while(se) { writestruct(wd, DATA, "ScrEdge", 1, se); se= se->next; } sa= sc->areabase.first; while(sa) { SpaceLink *sl; Panel *pa; writestruct(wd, DATA, "ScrArea", 1, sa); pa= sa->panels.first; while(pa) { writestruct(wd, DATA, "Panel", 1, pa); pa= pa->next; } sl= sa->spacedata.first; while(sl) { if(sl->spacetype==SPACE_VIEW3D) { View3D *v3d= (View3D*) sl; writestruct(wd, DATA, "View3D", 1, v3d); if(v3d->bgpic) writestruct(wd, DATA, "BGpic", 1, v3d->bgpic); if(v3d->localvd) writestruct(wd, DATA, "View3D", 1, v3d->localvd); } else if(sl->spacetype==SPACE_IPO) { writestruct(wd, DATA, "SpaceIpo", 1, sl); } else if(sl->spacetype==SPACE_BUTS) { writestruct(wd, DATA, "SpaceButs", 1, sl); } else if(sl->spacetype==SPACE_FILE) { writestruct(wd, DATA, "SpaceFile", 1, sl); } else if(sl->spacetype==SPACE_SEQ) { writestruct(wd, DATA, "SpaceSeq", 1, sl); } else if(sl->spacetype==SPACE_OOPS) { SpaceOops *so= (SpaceOops *)sl; Oops *oops; /* cleanup */ oops= so->oops.first; while(oops) { Oops *oopsn= oops->next; if(oops->id==0) { BLI_remlink(&so->oops, oops); free_oops(oops); } oops= oopsn; } /* ater cleanup, because of listbase! */ writestruct(wd, DATA, "SpaceOops", 1, so); oops= so->oops.first; while(oops) { writestruct(wd, DATA, "Oops", 1, oops); oops= oops->next; } /* outliner */ if(so->treestore) { writestruct(wd, DATA, "TreeStore", 1, so->treestore); if(so->treestore->data) writestruct(wd, DATA, "TreeStoreElem", so->treestore->usedelem, so->treestore->data); } } else if(sl->spacetype==SPACE_IMAGE) { writestruct(wd, DATA, "SpaceImage", 1, sl); } else if(sl->spacetype==SPACE_IMASEL) { writestruct(wd, DATA, "SpaceImaSel", 1, sl); } else if(sl->spacetype==SPACE_TEXT) { writestruct(wd, DATA, "SpaceText", 1, sl); } else if(sl->spacetype==SPACE_SCRIPT) { writestruct(wd, DATA, "SpaceScript", 1, sl); } else if(sl->spacetype==SPACE_ACTION) { writestruct(wd, DATA, "SpaceAction", 1, sl); } else if(sl->spacetype==SPACE_SOUND) { writestruct(wd, DATA, "SpaceSound", 1, sl); } else if(sl->spacetype==SPACE_NLA){ writestruct(wd, DATA, "SpaceNla", 1, sl); } sl= sl->next; } sa= sa->next; } sc= sc->id.next; } } static void write_libraries(WriteData *wd, Main *main) { ListBase *lbarray[30]; ID *id; int a, tot, foundone; while(main) { a=tot= set_listbasepointers(main, lbarray); /* test: is lib being used */ foundone= 0; while(tot--) { id= lbarray[tot]->first; while(id) { if(id->us>0 && (id->flag & LIB_EXTERN)) { foundone= 1; break; } id= id->next; } if(foundone) break; } if(foundone) { writestruct(wd, ID_LI, "Library", 1, main->curlib); while(a--) { id= lbarray[a]->first; while(id) { if(id->us>0 && (id->flag & LIB_EXTERN)) { writestruct(wd, ID_ID, "ID", 1, id); } id= id->next; } } } main= main->next; } } static void write_bone(WriteData *wd, Bone* bone) { Bone* cbone; // write_constraints(wd, &bone->constraints); // Write this bone writestruct(wd, DATA, "Bone", 1, bone); // Write Children cbone= bone->childbase.first; while(cbone) { write_bone(wd, cbone); cbone= cbone->next; } } static void write_armatures(WriteData *wd, ListBase *idbase) { bArmature *arm; Bone *bone; arm=idbase->first; while (arm) { if (arm->id.us>0 || wd->current) { writestruct(wd, ID_AR, "bArmature", 1, arm); /* Direct data */ bone= arm->bonebase.first; while(bone) { write_bone(wd, bone); bone=bone->next; } } arm=arm->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_actions(WriteData *wd, ListBase *idbase) { bAction *act; bActionChannel *chan; act=idbase->first; while (act) { if (act->id.us>0 || wd->current) { writestruct(wd, ID_AC, "bAction", 1, act); for (chan=act->chanbase.first; chan; chan=chan->next) { writestruct(wd, DATA, "bActionChannel", 1, chan); write_constraint_channels(wd, &chan->constraintChannels); } } act=act->id.next; } } static void write_texts(WriteData *wd, ListBase *idbase) { Text *text; TextLine *tmp; text= idbase->first; while(text) { if ( (text->flags & TXT_ISMEM) && (text->flags & TXT_ISEXT)) text->flags &= ~TXT_ISEXT; /* write LibData */ writestruct(wd, ID_TXT, "Text", 1, text); if(text->name) writedata(wd, DATA, strlen(text->name)+1, text->name); if(!(text->flags & TXT_ISEXT)) { /* now write the text data, in two steps for optimization in the readfunction */ tmp= text->lines.first; while (tmp) { writestruct(wd, DATA, "TextLine", 1, tmp); tmp= tmp->next; } tmp= text->lines.first; while (tmp) { writedata(wd, DATA, tmp->len+1, tmp->line); tmp= tmp->next; } } text= text->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_sounds(WriteData *wd, ListBase *idbase) { bSound *sound; bSample *sample; PackedFile * pf; // set all samples to unsaved status sample = samples->first; while (sample) { sample->flags |= SAMPLE_NEEDS_SAVE; sample = sample->id.next; } sound= idbase->first; while(sound) { if(sound->id.us>0 || wd->current) { // do we need to save the packedfile as well ? sample = sound->sample; if (sample) { if (sample->flags & SAMPLE_NEEDS_SAVE) { sound->newpackedfile = sample->packedfile; sample->flags &= ~SAMPLE_NEEDS_SAVE; } else { sound->newpackedfile = NULL; } } /* write LibData */ writestruct(wd, ID_SO, "bSound", 1, sound); if (sound->newpackedfile) { pf = sound->newpackedfile; writestruct(wd, DATA, "PackedFile", 1, pf); writedata(wd, DATA, pf->size, pf->data); } if (sample) { sound->newpackedfile = sample->packedfile; } } sound= sound->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_groups(WriteData *wd, ListBase *idbase) { Group *group; GroupKey *gk; GroupObject *go; ObjectKey *ok; group= idbase->first; while(group) { if(group->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_GR, "Group", 1, group); gk= group->gkey.first; while(gk) { writestruct(wd, DATA, "GroupKey", 1, gk); gk= gk->next; } go= group->gobject.first; while(go) { writestruct(wd, DATA, "GroupObject", 1, go); go= go->next; } go= group->gobject.first; while(go) { ok= go->okey.first; while(ok) { writestruct(wd, DATA, "ObjectKey", 1, ok); ok= ok->next; } go= go->next; } } group= group->id.next; } } static void write_global(WriteData *wd) { FileGlobal fg; fg.curscreen= G.curscreen; fg.curscene= G.scene; fg.displaymode= G.displaymode; fg.winpos= G.winpos; fg.fileflags= (G.fileflags & ~G_FILE_NO_UI); // prevent to save this, is not good convention, and feature with concerns... fg.globalf= G.f; writestruct(wd, GLOB, "FileGlobal", 1, &fg); } /* if *mem there's filesave to memory */ static int write_file_handle(int handle, MemFile *compare, MemFile *current, int write_user_block, int write_flags) { BHead bhead; ListBase mainlist; char buf[13]; WriteData *wd; int data; mainlist.first= mainlist.last= G.main; G.main->next= NULL; blo_split_main(&mainlist); wd= bgnwrite(handle, compare, current, write_flags); sprintf(buf, "BLENDER%c%c%.3d", (sizeof(void*)==8)?'-':'_', (G.order==B_ENDIAN)?'V':'v', G.version); mywrite(wd, buf, 12); write_renderinfo(wd); if(current==NULL) write_screens (wd, &G.main->screen); // no UI save write_scenes (wd, &G.main->scene); write_curves (wd, &G.main->curve); write_mballs (wd, &G.main->mball); write_images (wd, &G.main->image); write_cameras (wd, &G.main->camera); write_lamps (wd, &G.main->lamp); write_lattices (wd, &G.main->latt); write_ikas (wd, &G.main->ika); write_vfonts (wd, &G.main->vfont); write_ipos (wd, &G.main->ipo); write_keys (wd, &G.main->key); write_worlds (wd, &G.main->world); write_texts (wd, &G.main->text); write_sounds (wd, &G.main->sound); write_groups (wd, &G.main->group); write_armatures(wd, &G.main->armature); write_actions (wd, &G.main->action); write_objects (wd, &G.main->object); write_materials(wd, &G.main->mat); write_textures (wd, &G.main->tex); write_meshs (wd, &G.main->mesh); write_libraries(wd, G.main->next); write_global(wd); if (write_user_block) { write_userdef(wd); } /* dna as last, because (to be implemented) test for which structs are written */ writedata(wd, DNA1, wd->sdna->datalen, wd->sdna->data); /* end of file */ memset(&bhead, 0, sizeof(BHead)); bhead.code= ENDB; mywrite(wd, &bhead, sizeof(BHead)); blo_join_main(&mainlist); G.main= mainlist.first; return endwrite(wd); } /* return: success (1) */ int BLO_write_file(char *dir, int write_flags, char **error_r) { char userfilename[FILE_MAXDIR+FILE_MAXFILE]; char tempname[FILE_MAXDIR+FILE_MAXFILE]; int file, fout, write_user_block; #ifdef WIN32 char tmpdir[FILE_MAXDIR+FILE_MAXFILE]; #endif sprintf(tempname, "%s@", dir); file = open(tempname,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666); if(file == -1) { *error_r= "Unable to open"; return 0; } BLI_make_file_string(G.sce, userfilename, BLI_gethome(), ".B.blend"); write_user_block= BLI_streq(dir, userfilename); fout= write_file_handle(file, NULL,NULL, write_user_block, write_flags); close(file); if(!fout) { if(BLI_rename(tempname, dir) < 0) { *error_r= "Can't change old file. File saved with @"; return 0; } } else { remove(tempname); *error_r= "Not enough diskspace"; return 0; } return 1; } /* return: success (1) */ int BLO_write_file_mem(MemFile *compare, MemFile *current, int write_flags, char **error_r) { int err; err= write_file_handle(0, compare, current, 0, write_flags); if(err==0) return 1; return 0; } /* Runtime writing */ #ifdef WIN32 #define PATHSEPERATOR "\\" #else #define PATHSEPERATOR "/" #endif static char *get_install_dir(void) { extern char bprogname[]; char *tmpname = BLI_strdup(bprogname); char *cut; #ifdef __APPLE__ cut = strstr(tmpname, ".app"); if (cut) cut[0] = 0; #endif cut = BLI_last_slash(tmpname); if (cut) { cut[0] = 0; return tmpname; } else { MEM_freeN(tmpname); return NULL; } } static char *get_runtime_path(char *exename) { char *installpath= get_install_dir(); if (!installpath) { return NULL; } else { char *path= MEM_mallocN(strlen(installpath)+strlen(PATHSEPERATOR)+strlen(exename)+1, "runtimepath"); strcpy(path, installpath); strcat(path, PATHSEPERATOR); strcat(path, exename); MEM_freeN(installpath); return path; } } #ifdef __APPLE__ static int recursive_copy_runtime(char *outname, char *exename, char **cause_r) { char *cause = NULL, *runtime = get_runtime_path(exename); char command[2 * (FILE_MAXDIR+FILE_MAXFILE) + 32]; int progfd = -1; if (!runtime) { cause= "Unable to find runtime"; goto cleanup; } //printf("runtimepath %s\n", runtime); progfd= open(runtime, O_BINARY|O_RDONLY, 0); if (progfd==-1) { cause= "Unable to find runtime"; goto cleanup; } sprintf(command, "/bin/cp -R \"%s\" \"%s\"", runtime, outname); //printf("command %s\n", command); if (system(command) == -1) { cause = "Couldn't copy runtime"; } cleanup: if (progfd!=-1) close(progfd); if (runtime) MEM_freeN(runtime); if (cause) { *cause_r= cause; return 0; } else return 1; } void BLO_write_runtime(char *file, char *exename) { char gamename[FILE_MAXDIR+FILE_MAXFILE]; int outfd = -1; char *cause= NULL; // remove existing file / bundle //printf("Delete file %s\n", file); BLI_delete(file, NULL, TRUE); if (!recursive_copy_runtime(file, exename, &cause)) goto cleanup; strcpy(gamename, file); strcat(gamename, "/Contents/Resources/game.blend"); //printf("gamename %s\n", gamename); outfd= open(gamename, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0777); if (outfd != -1) { write_file_handle(outfd, NULL,NULL, 0, G.fileflags); if (write(outfd, " ", 1) != 1) { cause= "Unable to write to output file"; goto cleanup; } } else { cause = "Unable to open blenderfile"; } cleanup: if (outfd!=-1) close(outfd); if (cause) error("Unable to make runtime: %s", cause); } #else /* !__APPLE__ */ static int handle_append_runtime(int handle, char *exename, char **cause_r) { char *cause= NULL, *runtime= get_runtime_path(exename); unsigned char buf[1024]; int count, progfd= -1; if (!runtime) { cause= "Unable to find runtime"; goto cleanup; } progfd= open(runtime, O_BINARY|O_RDONLY, 0); if (progfd==-1) { cause= "Unable to find runtime"; goto cleanup; } while ((count= read(progfd, buf, sizeof(buf)))>0) { if (write(handle, buf, count)!=count) { cause= "Unable to write to output file"; goto cleanup; } } cleanup: if (progfd!=-1) close(progfd); if (runtime) MEM_freeN(runtime); if (cause) { *cause_r= cause; return 0; } else return 1; } static int handle_write_msb_int(int handle, int i) { unsigned char buf[4]; buf[0]= (i>>24)&0xFF; buf[1]= (i>>16)&0xFF; buf[2]= (i>>8)&0xFF; buf[3]= (i>>0)&0xFF; return (write(handle, buf, 4)==4); } void BLO_write_runtime(char *file, char *exename) { int outfd= open(file, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0777); char *cause= NULL; int datastart; if (!outfd) { cause= "Unable to open output file"; goto cleanup; } if (!handle_append_runtime(outfd, exename, &cause)) goto cleanup; datastart= lseek(outfd, 0, SEEK_CUR); write_file_handle(outfd, NULL,NULL, 0, G.fileflags); if (!handle_write_msb_int(outfd, datastart) || (write(outfd, "BRUNTIME", 8)!=8)) { cause= "Unable to write to output file"; goto cleanup; } cleanup: if (outfd!=-1) close(outfd); if (cause) error("Unable to make runtime: %s", cause); } #endif /* !__APPLE__ */