/* * $Id$ * * ***** BEGIN GPL 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. * * 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. * * * Contributor(s): Blender Foundation * * ***** END GPL 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 */ #ifdef HAVE_CONFIG_H #include #endif #include "zlib.h" #ifndef WIN32 #include #else #include "winsock2.h" #include "BLI_winstuff.h" #include #include // for getpid #endif #include #include #include #include #include #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_action_types.h" #include "DNA_actuator_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_color_types.h" #include "DNA_constraint_types.h" #include "DNA_controller_types.h" #include "DNA_curve_types.h" #include "DNA_customdata_types.h" #include "DNA_effect_types.h" #include "DNA_genfile.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_image_types.h" #include "DNA_ipo_types.h" // XXX depreceated - animsys #include "DNA_fileglobal_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_listBase.h" /* for Listbase, the type of samples, ...*/ #include "DNA_lamp_types.h" #include "DNA_meta_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_material_types.h" #include "DNA_modifier_types.h" #include "DNA_nla_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" #include "DNA_outliner_types.h" #include "DNA_packedFile_types.h" #include "DNA_particle_types.h" #include "DNA_property_types.h" #include "DNA_scene_types.h" #include "DNA_sdna_types.h" #include "DNA_sequence_types.h" #include "DNA_sensor_types.h" #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_sound_types.h" #include "DNA_texture_types.h" #include "DNA_text_types.h" #include "DNA_view3d_types.h" #include "DNA_vfont_types.h" #include "DNA_userdef_types.h" #include "DNA_world_types.h" #include "DNA_windowmanager_types.h" #include "MEM_guardedalloc.h" // MEM_freeN #include "BLI_blenlib.h" #include "BLI_linklist.h" #include "BKE_action.h" #include "BKE_blender.h" #include "BKE_cloth.h" #include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_constraint.h" #include "BKE_global.h" // for G #include "BKE_library.h" // for set_listbasepointers #include "BKE_main.h" #include "BKE_node.h" #include "BKE_packedFile.h" // for packAll #include "BKE_report.h" #include "BKE_screen.h" // for waitcursor #include "BKE_sequence.h" #include "BKE_sound.h" /* ... and for samples */ #include "BKE_utildefines.h" // for defines #include "BKE_modifier.h" #include "BKE_idprop.h" #include "BKE_fcurve.h" #include "BLO_writefile.h" #include "BLO_readfile.h" #include "BLO_undofile.h" #include "readfile.h" #include /* ********* my write, buffered writing with minimum size chunks ************ */ #define MYWRITE_BUFFER_SIZE 100000 #define MYWRITE_MAX_CHUNK 32768 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) { WriteData *wd= MEM_callocN(sizeof(*wd), "writedata"); /* XXX, see note about this in readfile.c, remove * once we have an xp lock - zr */ if (wd == NULL) return NULL; wd->sdna= DNA_sdna_from_data(DNAstr, DNAlen, 0); wd->file= file; wd->buf= MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf"); return wd; } static void writedata_do_write(WriteData *wd, void *mem, int memlen) { if ((wd == NULL) || wd->error || (mem == NULL) || memlen < 1) return; 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_sdna_free(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; /* flush helps compression for undo-save */ if(adr==MYWRITE_FLUSH) { if(wd->count) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } return; } wd->tot+= len; /* if we have a single big chunk, write existing data in * buffer and write out big chunk in smaller pieces */ if(len>MYWRITE_MAX_CHUNK) { if(wd->count) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } do { int writelen= MIN2(len, MYWRITE_MAX_CHUNK); writedata_do_write(wd, adr, writelen); adr = (char*)adr + writelen; len -= writelen; } while(len > 0); return; } /* if data would overflow buffer, write out the buffer */ if(len+wd->count>MYWRITE_BUFFER_SIZE-1) { writedata_do_write(wd, wd->buf, wd->count); wd->count= 0; } /* append data at end of buffer */ 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); if (wd == NULL) return NULL; 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); return err; } /* ********** WRITE FILE ****************** */ static void writestruct(WriteData *wd, int filecode, char *structname, int nr, void *adr) { BHead bh; short *sp; if(adr==NULL || nr==0) return; /* init BHead */ bh.code= filecode; bh.old= adr; bh.nr= nr; bh.SDNAnr= DNA_struct_find_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); } /* *************** writing some direct data structs used in more code parts **************** */ /*These functions are used by blender's .blend system for file saving/loading.*/ void IDP_WriteProperty_OnlyData(IDProperty *prop, void *wd); void IDP_WriteProperty(IDProperty *prop, void *wd); static void IDP_WriteArray(IDProperty *prop, void *wd) { /*REMEMBER to set totalen to len in the linking code!!*/ if (prop->data.pointer) { writedata(wd, DATA, MEM_allocN_len(prop->data.pointer), prop->data.pointer); if(prop->subtype == IDP_GROUP) { IDProperty **array= prop->data.pointer; int a; for(a=0; alen; a++) IDP_WriteProperty(array[a], wd); } } } static void IDP_WriteIDPArray(IDProperty *prop, void *wd) { /*REMEMBER to set totalen to len in the linking code!!*/ if (prop->data.pointer) { IDProperty **array = prop->data.pointer; int a; writedata(wd, DATA, MEM_allocN_len(prop->data.pointer), prop->data.pointer); for(a=0; alen; a++) IDP_WriteProperty(array[a], wd); } } static void IDP_WriteString(IDProperty *prop, void *wd) { /*REMEMBER to set totalen to len in the linking code!!*/ writedata(wd, DATA, prop->len+1, prop->data.pointer); } static void IDP_WriteGroup(IDProperty *prop, void *wd) { IDProperty *loop; for (loop=prop->data.group.first; loop; loop=loop->next) { IDP_WriteProperty(loop, wd); } } /* Functions to read/write ID Properties */ void IDP_WriteProperty_OnlyData(IDProperty *prop, void *wd) { switch (prop->type) { case IDP_GROUP: IDP_WriteGroup(prop, wd); break; case IDP_STRING: IDP_WriteString(prop, wd); break; case IDP_ARRAY: IDP_WriteArray(prop, wd); break; case IDP_IDPARRAY: IDP_WriteIDPArray(prop, wd); break; } } void IDP_WriteProperty(IDProperty *prop, void *wd) { writestruct(wd, DATA, "IDProperty", 1, prop); IDP_WriteProperty_OnlyData(prop, wd); } static void write_curvemapping(WriteData *wd, CurveMapping *cumap) { int a; writestruct(wd, DATA, "CurveMapping", 1, cumap); for(a=0; acm[a].totpoint, cumap->cm[a].curve); } /* this is only direct data, tree itself should have been written */ static void write_nodetree(WriteData *wd, bNodeTree *ntree) { bNode *node; bNodeSocket *sock; bNodeLink *link; /* for link_list() speed, we write per list */ for(node= ntree->nodes.first; node; node= node->next) writestruct(wd, DATA, "bNode", 1, node); for(node= ntree->nodes.first; node; node= node->next) { if(node->storage && node->type!=NODE_DYNAMIC) { /* could be handlerized at some point, now only 1 exception still */ if(ntree->type==NTREE_SHADER && (node->type==SH_NODE_CURVE_VEC || node->type==SH_NODE_CURVE_RGB)) write_curvemapping(wd, node->storage); else if(ntree->type==NTREE_COMPOSIT && (node->type==CMP_NODE_TIME || node->type==CMP_NODE_CURVE_VEC || node->type==CMP_NODE_CURVE_RGB)) write_curvemapping(wd, node->storage); else if(ntree->type==NTREE_TEXTURE && (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME) ) write_curvemapping(wd, node->storage); else writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage); } for(sock= node->inputs.first; sock; sock= sock->next) writestruct(wd, DATA, "bNodeSocket", 1, sock); for(sock= node->outputs.first; sock; sock= sock->next) writestruct(wd, DATA, "bNodeSocket", 1, sock); } for(link= ntree->links.first; link; link= link->next) writestruct(wd, DATA, "bNodeLink", 1, link); } 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 current_screen_compat(Main *mainvar, bScreen **screen) { wmWindowManager *wm; wmWindow *window; /* find a global current screen in the first open window, to have * a reasonable default for reading in older versions */ wm= mainvar->wm.first; window= (wm)? wm->windows.first: NULL; *screen= (window)? window->screen: NULL; } static void write_renderinfo(WriteData *wd, Main *mainvar) /* for renderdeamon */ { bScreen *curscreen; Scene *sce; int data[8]; /* XXX in future, handle multiple windows with multiple screnes? */ current_screen_compat(mainvar, &curscreen); for(sce= mainvar->scene.first; sce; sce= sce->id.next) { if(sce->id.lib==NULL && ( sce==curscreen->scene || (sce->r.scemode & R_BG_RENDER)) ) { data[0]= sce->r.sfra; data[1]= sce->r.efra; memset(data+2, 0, sizeof(int)*6); strncpy((char *)(data+2), sce->id.name+2, 21); writedata(wd, REND, 32, data); } } } 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_particlesettings(WriteData *wd, ListBase *idbase) { ParticleSettings *part; part= idbase->first; while(part) { if(part->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_PA, "ParticleSettings", 1, part); if (part->id.properties) IDP_WriteProperty(part->id.properties, wd); writestruct(wd, DATA, "PartDeflect", 1, part->pd); writestruct(wd, DATA, "PartDeflect", 1, part->pd2); } part= part->id.next; } } static void write_particlesystems(WriteData *wd, ListBase *particles) { ParticleSystem *psys= particles->first; int a; for(; psys; psys=psys->next) { writestruct(wd, DATA, "ParticleSystem", 1, psys); if(psys->particles) { writestruct(wd, DATA, "ParticleData", psys->totpart ,psys->particles); if(psys->particles->hair) { ParticleData *pa = psys->particles; for(a=0; atotpart; a++, pa++) writestruct(wd, DATA, "HairKey", pa->totkey, pa->hair); } } if(psys->child) writestruct(wd, DATA, "ChildParticle", psys->totchild ,psys->child); writestruct(wd, DATA, "SoftBody", 1, psys->soft); if(psys->soft) writestruct(wd, DATA, "PointCache", 1, psys->soft->pointcache); writestruct(wd, DATA, "PointCache", 1, psys->pointcache); } } 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_ACTUATOR: writestruct(wd, DATA, "bActuatorSensor", 1, sens->data); break; case SENS_DELAY: writestruct(wd, DATA, "bDelaySensor", 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; case SENS_JOYSTICK: writestruct(wd, DATA, "bJoystickSensor", 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: case ACT_SHAPEACTION: 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; case ACT_2DFILTER: writestruct(wd, DATA, "bTwoDFilterActuator", 1, act->data); break; case ACT_PARENT: writestruct(wd, DATA, "bParentActuator", 1, act->data); break; case ACT_STATE: writestruct(wd, DATA, "bStateActuator", 1, act->data); break; default: ; /* error: don't know how to write this file */ } act= act->next; } } static void write_fcurves(WriteData *wd, ListBase *fcurves) { FCurve *fcu; FModifier *fcm; for (fcu=fcurves->first; fcu; fcu=fcu->next) { /* F-Curve */ writestruct(wd, DATA, "FCurve", 1, fcu); /* curve data */ if (fcu->bezt) writestruct(wd, DATA, "BezTriple", fcu->totvert, fcu->bezt); if (fcu->fpt) writestruct(wd, DATA, "FPoint", fcu->totvert, fcu->fpt); if (fcu->rna_path) writedata(wd, DATA, strlen(fcu->rna_path)+1, fcu->rna_path); /* driver data */ if (fcu->driver) { ChannelDriver *driver= fcu->driver; DriverTarget *dtar; writestruct(wd, DATA, "ChannelDriver", 1, driver); /* targets */ for (dtar= driver->targets.first; dtar; dtar= dtar->next) { writestruct(wd, DATA, "DriverTarget", 1, dtar); if (dtar->rna_path) writedata(wd, DATA, strlen(dtar->rna_path)+1, dtar->rna_path); } } /* Modifiers */ for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); /* Write the specific data */ if (fmi && fcm->data) { /* firstly, just write the plain fmi->data struct */ writestruct(wd, DATA, fmi->structName, 1, fcm->data); /* do any modifier specific stuff */ switch (fcm->type) { case FMODIFIER_TYPE_GENERATOR: { FMod_Generator *data= (FMod_Generator *)fcm->data; /* write coefficients array */ if (data->coefficients) writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients); } break; case FMODIFIER_TYPE_ENVELOPE: { FMod_Envelope *data= (FMod_Envelope *)fcm->data; /* write envelope data */ if (data->data) writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data); } break; case FMODIFIER_TYPE_PYTHON: { FMod_Python *data = (FMod_Python *)fcm->data; /* Write ID Properties -- and copy this comment EXACTLY for easy finding of library blocks that implement this.*/ IDP_WriteProperty(data->prop, wd); } break; } } /* Write the modifier */ writestruct(wd, DATA, "FModifier", 1, fcm); } } } static void write_actions(WriteData *wd, ListBase *idbase) { bAction *act; bActionGroup *grp; TimeMarker *marker; for(act=idbase->first; act; act= act->id.next) { if (act->id.us>0 || wd->current) { writestruct(wd, ID_AC, "bAction", 1, act); if (act->id.properties) IDP_WriteProperty(act->id.properties, wd); write_fcurves(wd, &act->curves); for (grp=act->groups.first; grp; grp=grp->next) { writestruct(wd, DATA, "bActionGroup", 1, grp); } for (marker=act->markers.first; marker; marker=marker->next) { writestruct(wd, DATA, "TimeMarker", 1, marker); } } } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_keyingsets(WriteData *wd, ListBase *list) { KeyingSet *ks; KS_Path *ksp; for (ks= list->first; ks; ks= ks->next) { /* KeyingSet */ writestruct(wd, DATA, "KeyingSet", 1, ks); /* Paths */ for (ksp= ks->paths.first; ksp; ksp= ksp->next) { /* Path */ writestruct(wd, DATA, "KS_Path", 1, ksp); if (ksp->rna_path) writedata(wd, DATA, strlen(ksp->rna_path)+1, ksp->rna_path); } } } static void write_animdata(WriteData *wd, AnimData *adt) { AnimOverride *aor; /* firstly, just write the AnimData block */ writestruct(wd, DATA, "AnimData", 1, adt); /* write drivers */ write_fcurves(wd, &adt->drivers); /* write overrides */ for (aor= adt->overrides.first; aor; aor= aor->next) { /* overrides consist of base data + rna_path */ writestruct(wd, DATA, "AnimOverride", 1, aor); writedata(wd, DATA, strlen(aor->rna_path)+1, aor->rna_path); } /* write NLA data */ // XXX todo... } static void write_constraints(WriteData *wd, ListBase *conlist) { bConstraint *con; for (con=conlist->first; con; con=con->next) { bConstraintTypeInfo *cti= constraint_get_typeinfo(con); /* Write the specific data */ if (cti && con->data) { /* firstly, just write the plain con->data struct */ writestruct(wd, DATA, cti->structName, 1, con->data); /* do any constraint specific stuff */ switch (con->type) { case CONSTRAINT_TYPE_PYTHON: { bPythonConstraint *data = (bPythonConstraint *)con->data; bConstraintTarget *ct; /* write targets */ for (ct= data->targets.first; ct; ct= ct->next) writestruct(wd, DATA, "bConstraintTarget", 1, ct); /* Write ID Properties -- and copy this comment EXACTLY for easy finding of library blocks that implement this.*/ IDP_WriteProperty(data->prop, wd); } break; } } /* Write the constraint */ writestruct(wd, DATA, "bConstraint", 1, con); } } static void write_pose(WriteData *wd, bPose *pose) { bPoseChannel *chan; bActionGroup *grp; /* Write each channel */ if (!pose) return; /* Write channels */ for (chan=pose->chanbase.first; chan; chan=chan->next) { write_constraints(wd, &chan->constraints); /* prevent crashes with autosave, when a bone duplicated in editmode has not yet been assigned to its posechannel */ if (chan->bone) chan->selectflag= chan->bone->flag & (BONE_SELECTED|BONE_ACTIVE); /* gets restored on read, for library armatures */ writestruct(wd, DATA, "bPoseChannel", 1, chan); } /* Write groups */ for (grp=pose->agroups.first; grp; grp=grp->next) writestruct(wd, DATA, "bActionGroup", 1, grp); /* 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_modifiers(WriteData *wd, ListBase *modbase, int write_undo) { ModifierData *md; if (modbase == NULL) return; for (md=modbase->first; md; md= md->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); if (mti == NULL) return; writestruct(wd, DATA, mti->structName, 1, md); if (md->type==eModifierType_Hook) { HookModifierData *hmd = (HookModifierData*) md; writedata(wd, DATA, sizeof(int)*hmd->totindex, hmd->indexar); } else if(md->type==eModifierType_Cloth) { ClothModifierData *clmd = (ClothModifierData*) md; writestruct(wd, DATA, "ClothSimSettings", 1, clmd->sim_parms); writestruct(wd, DATA, "ClothCollSettings", 1, clmd->coll_parms); writestruct(wd, DATA, "PointCache", 1, clmd->point_cache); } else if(md->type==eModifierType_Fluidsim) { FluidsimModifierData *fluidmd = (FluidsimModifierData*) md; writestruct(wd, DATA, "FluidsimSettings", 1, fluidmd->fss); } else if (md->type==eModifierType_Collision) { /* CollisionModifierData *collmd = (CollisionModifierData*) md; // TODO: CollisionModifier should use pointcache // + have proper reset events before enabling this writestruct(wd, DATA, "MVert", collmd->numverts, collmd->x); writestruct(wd, DATA, "MVert", collmd->numverts, collmd->xnew); writestruct(wd, DATA, "MFace", collmd->numfaces, collmd->mfaces); */ } else if (md->type==eModifierType_MeshDeform) { MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; int size = mmd->dyngridsize; writedata(wd, DATA, sizeof(float)*mmd->totvert*mmd->totcagevert, mmd->bindweights); writedata(wd, DATA, sizeof(float)*3*mmd->totcagevert, mmd->bindcos); writestruct(wd, DATA, "MDefCell", size*size*size, mmd->dyngrid); writestruct(wd, DATA, "MDefInfluence", mmd->totinfluence, mmd->dyninfluences); writedata(wd, DATA, sizeof(int)*mmd->totvert, mmd->dynverts); } else if (md->type==eModifierType_Multires) { MultiresModifierData *mmd = (MultiresModifierData*) md; if(mmd->undo_verts && write_undo) writestruct(wd, DATA, "MVert", mmd->undo_verts_tot, mmd->undo_verts); } } } static void write_objects(WriteData *wd, ListBase *idbase, int write_undo) { Object *ob; ob= idbase->first; while(ob) { if(ob->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_OB, "Object", 1, ob); /*Write ID Properties -- and copy this comment EXACTLY for easy finding of library blocks that implement this.*/ if (ob->id.properties) IDP_WriteProperty(ob->id.properties, wd); if (ob->adt) write_animdata(wd, ob->adt); /* direct data */ writedata(wd, DATA, sizeof(void *)*ob->totcol, ob->mat); /* write_effects(wd, &ob->effect); */ /* not used anymore */ 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); writestruct(wd, DATA, "PartDeflect", 1, ob->pd); writestruct(wd, DATA, "SoftBody", 1, ob->soft); if(ob->soft) writestruct(wd, DATA, "PointCache", 1, ob->soft->pointcache); writestruct(wd, DATA, "BulletSoftBody", 1, ob->bsoft); write_particlesystems(wd, &ob->particlesystem); write_modifiers(wd, &ob->modifiers, write_undo); } 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); if (vf->id.properties) IDP_WriteProperty(vf->id.properties, wd); /* 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_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); if (key->id.properties) IDP_WriteProperty(key->id.properties, wd); if (key->adt) write_animdata(wd, key->adt); /* 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); if (cam->id.properties) IDP_WriteProperty(cam->id.properties, wd); if (cam->adt) write_animdata(wd, cam->adt); /* 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); if (mb->id.properties) IDP_WriteProperty(mb->id.properties, wd); /* 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 int amount_of_chars(char *str) { // Since the data is saved as UTF-8 to the cu->str // The cu->len is not same as the strlen(cu->str) return strlen(str); } 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->id.properties) IDP_WriteProperty(cu->id.properties, wd); if (cu->adt) write_animdata(wd, cu->adt); if(cu->vfont) { writedata(wd, DATA, amount_of_chars(cu->str)+1, cu->str); writestruct(wd, DATA, "CharInfo", cu->len, cu->strinfo); writestruct(wd, DATA, "TextBox", cu->totbox, cu->tb); } 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) { if (dvlist) { int i; /* Write the dvert list */ writestruct(wd, DATA, "MDeformVert", count, dvlist); /* Write deformation data for each dvert */ for (i=0; imaxlayer, data->layers); for (i=0; itotlayer; i++) { CustomDataLayer *layer= &data->layers[i]; char *structname; int structnum, datasize; if (layer->type == CD_MDEFORMVERT) { /* layer types that allocate own memory need special handling */ write_dverts(wd, count, layer->data); } else if (layer->type == CD_MDISPS) { write_mdisps(wd, count, layer->data); } else { CustomData_file_write_info(layer->type, &structname, &structnum); if (structnum) { /* when using partial visibility, the MEdge and MFace layers are smaller than the original, so their type and count is passed to make this work */ if (layer->type != partial_type) datasize= structnum*count; else datasize= structnum*partial_count; writestruct(wd, DATA, structname, datasize, layer->data); } else printf("error: this CustomDataLayer must not be written to file\n"); } } } static void write_meshs(WriteData *wd, ListBase *idbase) { Mesh *mesh; mesh= idbase->first; while(mesh) { if(mesh->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_ME, "Mesh", 1, mesh); /* direct data */ if (mesh->id.properties) IDP_WriteProperty(mesh->id.properties, wd); writedata(wd, DATA, sizeof(void *)*mesh->totcol, mesh->mat); if(mesh->pv) { write_customdata(wd, mesh->pv->totvert, &mesh->vdata, -1, 0); write_customdata(wd, mesh->pv->totedge, &mesh->edata, CD_MEDGE, mesh->totedge); write_customdata(wd, mesh->pv->totface, &mesh->fdata, CD_MFACE, mesh->totface); } else { write_customdata(wd, mesh->totvert, &mesh->vdata, -1, 0); write_customdata(wd, mesh->totedge, &mesh->edata, -1, 0); write_customdata(wd, mesh->totface, &mesh->fdata, -1, 0); } /* PMV data */ if(mesh->pv) { writestruct(wd, DATA, "PartialVisibility", 1, mesh->pv); writedata(wd, DATA, sizeof(unsigned int)*mesh->pv->totvert, mesh->pv->vert_map); writedata(wd, DATA, sizeof(int)*mesh->pv->totedge, mesh->pv->edge_map); writestruct(wd, DATA, "MFace", mesh->pv->totface, mesh->pv->old_faces); writestruct(wd, DATA, "MEdge", mesh->pv->totedge, mesh->pv->old_edges); } } mesh= mesh->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); if (lt->id.properties) IDP_WriteProperty(lt->id.properties, wd); /* direct data */ writestruct(wd, DATA, "BPoint", lt->pntsu*lt->pntsv*lt->pntsw, lt->def); write_dverts(wd, lt->pntsu*lt->pntsv*lt->pntsw, lt->dvert); } lt= lt->id.next; } } static void write_previews(WriteData *wd, PreviewImage *prv) { if (prv) { short w = prv->w[1]; short h = prv->h[1]; unsigned int *rect = prv->rect[1]; /* don't write out large previews if not requested */ if (!(U.flag & USER_SAVE_PREVIEWS) ) { prv->w[1] = 0; prv->h[1] = 0; prv->rect[1] = NULL; } writestruct(wd, DATA, "PreviewImage", 1, prv); if (prv->rect[0]) writedata(wd, DATA, prv->w[0]*prv->h[0]*sizeof(unsigned int), prv->rect[0]); if (prv->rect[1]) writedata(wd, DATA, prv->w[1]*prv->h[1]*sizeof(unsigned int), prv->rect[1]); /* restore preview, we still want to keep it in memory even if not saved to file */ if (!(U.flag & USER_SAVE_PREVIEWS) ) { prv->w[1] = w; prv->h[1] = h; prv->rect[1] = rect; } } } 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->id.properties) IDP_WriteProperty(ima->id.properties, wd); if (ima->packedfile) { pf = ima->packedfile; writestruct(wd, DATA, "PackedFile", 1, pf); writedata(wd, DATA, pf->size, pf->data); } write_previews(wd, ima->preview); } 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); if (tex->id.properties) IDP_WriteProperty(tex->id.properties, wd); if (tex->adt) write_animdata(wd, tex->adt); /* 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); /* nodetree is integral part of texture, no libdata */ if(tex->nodetree) { writestruct(wd, DATA, "bNodeTree", 1, tex->nodetree); write_nodetree(wd, tex->nodetree); } write_previews(wd, tex->preview); } 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); /*Write ID Properties -- and copy this comment EXACTLY for easy finding of library blocks that implement this.*/ /*manually set head group property to IDP_GROUP, just in case it hadn't been set yet :) */ if (ma->id.properties) IDP_WriteProperty(ma->id.properties, wd); if (ma->adt) write_animdata(wd, ma->adt); 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); /* nodetree is integral part of material, no libdata */ if(ma->nodetree) { writestruct(wd, DATA, "bNodeTree", 1, ma->nodetree); write_nodetree(wd, ma->nodetree); } write_previews(wd, ma->preview); } 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); if (wrld->id.properties) IDP_WriteProperty(wrld->id.properties, wd); if (wrld->adt) write_animdata(wd, wrld->adt); for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, wrld->mtex[a]); } write_scriptlink(wd, &wrld->scriptlink); write_previews(wd, wrld->preview); } 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); if (la->id.properties) IDP_WriteProperty(la->id.properties, wd); if (la->adt) write_animdata(wd, la->adt); /* direct data */ for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, la->mtex[a]); } if(la->curfalloff) write_curvemapping(wd, la->curfalloff); write_scriptlink(wd, &la->scriptlink); write_previews(wd, la->preview); } la= la->id.next; } } static void write_scenes(WriteData *wd, ListBase *scebase) { Scene *sce; Base *base; Editing *ed; Sequence *seq; MetaStack *ms; Strip *strip; TimeMarker *marker; TransformOrientation *ts; SceneRenderLayer *srl; sce= scebase->first; while(sce) { /* write LibData */ writestruct(wd, ID_SCE, "Scene", 1, sce); if (sce->id.properties) IDP_WriteProperty(sce->id.properties, wd); if (sce->adt) write_animdata(wd, sce->adt); write_keyingsets(wd, &sce->keyingsets); /* direct data */ base= sce->base.first; while(base) { writestruct(wd, DATA, "Base", 1, base); base= base->next; } writestruct(wd, DATA, "Radio", 1, sce->radio); writestruct(wd, DATA, "ToolSettings", 1, sce->toolsettings); if(sce->toolsettings->vpaint) writestruct(wd, DATA, "VPaint", 1, sce->toolsettings->vpaint); if(sce->toolsettings->wpaint) writestruct(wd, DATA, "VPaint", 1, sce->toolsettings->wpaint); if(sce->toolsettings->sculpt) writestruct(wd, DATA, "Sculpt", 1, sce->toolsettings->sculpt); ed= sce->ed; if(ed) { writestruct(wd, DATA, "Editing", 1, ed); /* reset write flags too */ SEQ_BEGIN(ed, seq) { if(seq->strip) seq->strip->done= 0; writestruct(wd, DATA, "Sequence", 1, seq); } SEQ_END SEQ_BEGIN(ed, seq) { 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_COLOR: writestruct(wd, DATA, "SolidColorVars", 1, seq->effectdata); break; case SEQ_SPEED: writestruct(wd, DATA, "SpeedControlVars", 1, seq->effectdata); break; case SEQ_WIPE: writestruct(wd, DATA, "WipeVars", 1, seq->effectdata); break; case SEQ_GLOW: writestruct(wd, DATA, "GlowVars", 1, seq->effectdata); break; case SEQ_TRANSFORM: writestruct(wd, DATA, "TransformVars", 1, seq->effectdata); break; } } strip= seq->strip; writestruct(wd, DATA, "Strip", 1, strip); if(seq->flag & SEQ_USE_CROP && strip->crop) { writestruct(wd, DATA, "StripCrop", 1, strip->crop); } if(seq->flag & SEQ_USE_TRANSFORM && strip->transform) { writestruct(wd, DATA, "StripTransform", 1, strip->transform); } if(seq->flag & SEQ_USE_PROXY && strip->proxy) { writestruct(wd, DATA, "StripProxy", 1, strip->proxy); } if(seq->flag & SEQ_USE_COLOR_BALANCE && strip->color_balance) { writestruct(wd, DATA, "StripColorBalance", 1, strip->color_balance); } if(seq->type==SEQ_IMAGE) writestruct(wd, DATA, "StripElem", MEM_allocN_len(strip->stripdata) / sizeof(struct StripElem), strip->stripdata); else if(seq->type==SEQ_MOVIE || seq->type==SEQ_RAM_SOUND || seq->type == SEQ_HD_SOUND) writestruct(wd, DATA, "StripElem", 1, strip->stripdata); strip->done= 1; } } SEQ_END /* 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); } if (sce->r.ffcodecdata.properties) { IDP_WriteProperty(sce->r.ffcodecdata.properties, wd); } /* writing dynamic list of TimeMarkers to the blend file */ for(marker= sce->markers.first; marker; marker= marker->next) writestruct(wd, DATA, "TimeMarker", 1, marker); /* writing dynamic list of TransformOrientations to the blend file */ for(ts = sce->transform_spaces.first; ts; ts = ts->next) writestruct(wd, DATA, "TransformOrientation", 1, ts); for(srl= sce->r.layers.first; srl; srl= srl->next) writestruct(wd, DATA, "SceneRenderLayer", 1, srl); if(sce->nodetree) { writestruct(wd, DATA, "bNodeTree", 1, sce->nodetree); write_nodetree(wd, sce->nodetree); } sce= sce->id.next; } /* flush helps the compression for undo-save */ mywrite(wd, MYWRITE_FLUSH, 0); } static void write_gpencils(WriteData *wd, ListBase *lb) { bGPdata *gpd; bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; for (gpd= lb->first; gpd; gpd= gpd->id.next) { /* write gpd data block to file */ writestruct(wd, ID_GD, "bGPdata", 1, gpd); /* write grease-pencil layers to file */ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { writestruct(wd, DATA, "bGPDlayer", 1, gpl); /* write this layer's frames to file */ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { writestruct(wd, DATA, "bGPDframe", 1, gpf); /* write strokes */ for (gps= gpf->strokes.first; gps; gps= gps->next) { writestruct(wd, DATA, "bGPDstroke", 1, gps); writestruct(wd, DATA, "bGPDspoint", gps->totpoints, gps->points); } } } } } static void write_windowmanagers(WriteData *wd, ListBase *lb) { wmWindowManager *wm; wmWindow *win; for(wm= lb->first; wm; wm= wm->id.next) { writestruct(wd, ID_WM, "wmWindowManager", 1, wm); for(win= wm->windows.first; win; win= win->next) writestruct(wd, DATA, "wmWindow", 1, win); } } static void write_region(WriteData *wd, ARegion *ar, int spacetype) { writestruct(wd, DATA, "ARegion", 1, ar); if(ar->regiondata) { switch(spacetype) { case SPACE_VIEW3D: if(ar->regiontype==RGN_TYPE_WINDOW) { RegionView3D *rv3d= ar->regiondata; writestruct(wd, DATA, "RegionView3D", 1, rv3d); if(rv3d->localvd) writestruct(wd, DATA, "RegionView3D", 1, rv3d->localvd); if(rv3d->clipbb) writestruct(wd, DATA, "BoundBox", 1, rv3d->clipbb); } else printf("regiondata write missing!\n"); break; default: printf("regiondata write missing!\n"); } } } static void write_screens(WriteData *wd, ListBase *scrbase) { bScreen *sc; ScrArea *sa; ScrVert *sv; ScrEdge *se; sc= scrbase->first; while(sc) { /* write LibData */ /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ writestruct(wd, ID_SCRN, "Screen", 1, sc); if (sc->id.properties) IDP_WriteProperty(sc->id.properties, wd); /* direct data */ for(sv= sc->vertbase.first; sv; sv= sv->next) writestruct(wd, DATA, "ScrVert", 1, sv); for(se= sc->edgebase.first; se; se= se->next) writestruct(wd, DATA, "ScrEdge", 1, se); for(sa= sc->areabase.first; sa; sa= sa->next) { SpaceLink *sl; Panel *pa; ARegion *ar; writestruct(wd, DATA, "ScrArea", 1, sa); for(ar= sa->regionbase.first; ar; ar= ar->next) { write_region(wd, ar, sa->spacetype); for(pa= ar->panels.first; pa; pa= pa->next) writestruct(wd, DATA, "Panel", 1, pa); } /* space handler scriptlinks */ write_scriptlink(wd, &sa->scriptlink); sl= sa->spacedata.first; while(sl) { for(ar= sl->regionbase.first; ar; ar= ar->next) write_region(wd, ar, sl->spacetype); 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) { SpaceIpo *sipo= (SpaceIpo *)sl; ListBase tmpGhosts = sipo->ghostCurves; /* temporarily disable ghost curves when saving */ sipo->ghostCurves.first= sipo->ghostCurves.last= NULL; writestruct(wd, DATA, "SpaceIpo", 1, sl); if(sipo->ads) writestruct(wd, DATA, "bDopeSheet", 1, sipo->ads); /* reenable ghost curves */ sipo->ghostCurves= tmpGhosts; } 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) { SpaceSeq *sseq= (SpaceSeq *)sl; writestruct(wd, DATA, "SpaceSeq", 1, sl); } else if(sl->spacetype==SPACE_OUTLINER) { SpaceOops *so= (SpaceOops *)sl; writestruct(wd, DATA, "SpaceOops", 1, so); /* 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) { SpaceImage *sima= (SpaceImage *)sl; writestruct(wd, DATA, "SpaceImage", 1, sl); if(sima->cumap) write_curvemapping(wd, sima->cumap); } 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) { SpaceScript *sc = (SpaceScript*)sl; sc->but_refs = NULL; 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); } else if(sl->spacetype==SPACE_TIME){ writestruct(wd, DATA, "SpaceTime", 1, sl); } else if(sl->spacetype==SPACE_NODE){ SpaceNode *snode= (SpaceNode *)sl; writestruct(wd, DATA, "SpaceNode", 1, sl); } sl= sl->next; } } sc= sc->id.next; } } static void write_libraries(WriteData *wd, Main *main) { ListBase *lbarray[30]; ID *id; int a, tot, foundone; for(; main; main= main->next) { a=tot= set_listbasepointers(main, lbarray); /* test: is lib being used */ foundone= 0; while(tot--) { for(id= lbarray[tot]->first; id; id= id->next) { if(id->us>0 && (id->flag & LIB_EXTERN)) { foundone= 1; break; } } if(foundone) break; } if(foundone) { writestruct(wd, ID_LI, "Library", 1, main->curlib); while(a--) { for(id= lbarray[a]->first; id; id= id->next) { if(id->us>0 && (id->flag & LIB_EXTERN)) { writestruct(wd, ID_ID, "ID", 1, id); } } } } } } static void write_bone(WriteData *wd, Bone* bone) { Bone* cbone; // PATCH for upward compatibility after 2.37+ armature recode bone->size[0]= bone->size[1]= bone->size[2]= 1.0f; // 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); if (arm->id.properties) IDP_WriteProperty(arm->id.properties, wd); /* 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_texts(WriteData *wd, ListBase *idbase) { Text *text; TextLine *tmp; TextMarker *mrk; 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->id.properties) IDP_WriteProperty(text->id.properties, wd); 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; } /* write markers */ mrk= text->markers.first; while (mrk) { writestruct(wd, DATA, "TextMarker", 1, mrk); mrk= mrk->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; // samples is a global defined in sound.c 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->id.properties) IDP_WriteProperty(sound->id.properties, wd); 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; GroupObject *go; for(group= idbase->first; group; group= group->id.next) { if(group->id.us>0 || wd->current) { /* write LibData */ writestruct(wd, ID_GR, "Group", 1, group); if (group->id.properties) IDP_WriteProperty(group->id.properties, wd); go= group->gobject.first; while(go) { writestruct(wd, DATA, "GroupObject", 1, go); go= go->next; } } } } static void write_nodetrees(WriteData *wd, ListBase *idbase) { bNodeTree *ntree; for(ntree=idbase->first; ntree; ntree= ntree->id.next) { if (ntree->id.us>0 || wd->current) { writestruct(wd, ID_NT, "bNodeTree", 1, ntree); write_nodetree(wd, ntree); if (ntree->id.properties) IDP_WriteProperty(ntree->id.properties, wd); if (ntree->adt) write_animdata(wd, ntree->adt); } } } static void write_brushes(WriteData *wd, ListBase *idbase) { Brush *brush; int a; for(brush=idbase->first; brush; brush= brush->id.next) { if(brush->id.us>0 || wd->current) { writestruct(wd, ID_BR, "Brush", 1, brush); if (brush->id.properties) IDP_WriteProperty(brush->id.properties, wd); for(a=0; amtex[a]) writestruct(wd, DATA, "MTex", 1, brush->mtex[a]); if(brush->curve) write_curvemapping(wd, brush->curve); } } } static void write_scripts(WriteData *wd, ListBase *idbase) { Script *script; for(script=idbase->first; script; script= script->id.next) { if(script->id.us>0 || wd->current) { writestruct(wd, ID_SCRIPT, "Script", 1, script); if (script->id.properties) IDP_WriteProperty(script->id.properties, wd); } } } /* context is usually defined by WM, two cases where no WM is available: * - for forward compatibility, curscreen has to be saved * - for undofile, curscene needs to be saved */ static void write_global(WriteData *wd, Main *mainvar) { FileGlobal fg; bScreen *screen; char subvstr[8]; current_screen_compat(mainvar, &screen); /* XXX still remap G */ fg.curscreen= screen; fg.curscene= screen->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; sprintf(subvstr, "%4d", BLENDER_SUBVERSION); memcpy(fg.subvstr, subvstr, 4); fg.subversion= BLENDER_SUBVERSION; fg.minversion= BLENDER_MINVERSION; fg.minsubversion= BLENDER_MINSUBVERSION; fg.pads= 0; /* prevent mem checkers from complaining */ writestruct(wd, GLOB, "FileGlobal", 1, &fg); } /* if MemFile * there's filesave to memory */ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFile *current, int write_user_block, int write_flags) { BHead bhead; ListBase mainlist; char buf[16]; WriteData *wd; blo_split_main(&mainlist, mainvar); wd= bgnwrite(handle, compare, current, write_flags); sprintf(buf, "BLENDER%c%c%.3d", (sizeof(void*)==8)?'-':'_', (ENDIAN_ORDER==B_ENDIAN)?'V':'v', BLENDER_VERSION); mywrite(wd, buf, 12); write_renderinfo(wd, mainvar); write_global(wd, mainvar); /* no UI save in undo */ if(current==NULL) { write_windowmanagers(wd, &mainvar->wm); write_screens (wd, &mainvar->screen); } write_scenes (wd, &mainvar->scene); write_curves (wd, &mainvar->curve); write_mballs (wd, &mainvar->mball); write_images (wd, &mainvar->image); write_cameras (wd, &mainvar->camera); write_lamps (wd, &mainvar->lamp); write_lattices (wd, &mainvar->latt); write_vfonts (wd, &mainvar->vfont); write_keys (wd, &mainvar->key); write_worlds (wd, &mainvar->world); write_texts (wd, &mainvar->text); write_sounds (wd, &mainvar->sound); write_groups (wd, &mainvar->group); write_armatures(wd, &mainvar->armature); write_actions (wd, &mainvar->action); write_objects (wd, &mainvar->object, (current != NULL)); write_materials(wd, &mainvar->mat); write_textures (wd, &mainvar->tex); write_meshs (wd, &mainvar->mesh); write_particlesettings(wd, &mainvar->particle); write_nodetrees(wd, &mainvar->nodetree); write_brushes (wd, &mainvar->brush); write_scripts (wd, &mainvar->script); write_gpencils (wd, &mainvar->gpencil); if(current==NULL) write_libraries(wd, mainvar->next); /* no library save in undo */ 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); return endwrite(wd); } /* return: success (1) */ int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *reports) { char userfilename[FILE_MAXDIR+FILE_MAXFILE]; char tempname[FILE_MAXDIR+FILE_MAXFILE]; int file, err, write_user_block; sprintf(tempname, "%s@", dir); file = open(tempname,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666); if(file == -1) { BKE_report(reports, RPT_ERROR, "Unable to open file for writing."); return 0; } BLI_make_file_string(G.sce, userfilename, BLI_gethome(), ".B.blend"); write_user_block= BLI_streq(dir, userfilename); err= write_file_handle(mainvar, file, NULL,NULL, write_user_block, write_flags); close(file); if(!err) { if(write_flags & G_FILE_COMPRESS) { // compressed files have the same ending as regular files... only from 2.4!!! int ret = BLI_gzip(tempname, dir); if(-1==ret) { BKE_report(reports, RPT_ERROR, "Failed opening .gz file."); return 0; } if(-2==ret) { BKE_report(reports, RPT_ERROR, "Failed opening .blend file for compression."); return 0; } } else if(BLI_rename(tempname, dir) != 0) { BKE_report(reports, RPT_ERROR, "Can't change old file. File saved with @"); return 0; } } else { BKE_report(reports, RPT_ERROR, strerror(errno)); remove(tempname); return 0; } return 1; } /* return: success (1) */ int BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags, ReportList *reports) { int err; err= write_file_handle(mainvar, 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_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"); if (path == NULL) { MEM_freeN(installpath); return NULL; } 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, ReportList *reports) { char *runtime = get_runtime_path(exename); char command[2 * (FILE_MAXDIR+FILE_MAXFILE) + 32]; int progfd = -1, error= 0; if (!runtime) { BKE_report(reports, RPT_ERROR, "Unable to find runtime"); error= 1; goto cleanup; } //printf("runtimepath %s\n", runtime); progfd= open(runtime, O_BINARY|O_RDONLY, 0); if (progfd==-1) { BKE_report(reports, RPT_ERROR, "Unable to find runtime"); error= 1; goto cleanup; } sprintf(command, "/bin/cp -R \"%s\" \"%s\"", runtime, outname); //printf("command %s\n", command); if (system(command) == -1) { BKE_report(reports, RPT_ERROR, "Couldn't copy runtime"); error= 1; } cleanup: if (progfd!=-1) close(progfd); if (runtime) MEM_freeN(runtime); return !error; } int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *reports) { char gamename[FILE_MAXDIR+FILE_MAXFILE]; int outfd = -1, error= 0; // remove existing file / bundle //printf("Delete file %s\n", file); BLI_delete(file, 0, TRUE); if (!recursive_copy_runtime(file, exename, reports)) { error= 1; 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(mainvar, outfd, NULL,NULL, 0, G.fileflags); if (write(outfd, " ", 1) != 1) { BKE_report(reports, RPT_ERROR, "Unable to write to output file."); error= 1; goto cleanup; } } else { BKE_report(reports, RPT_ERROR, "Unable to open blenderfile."); error= 1; } cleanup: if (outfd!=-1) close(outfd); BKE_reports_prepend(reports, "Unable to make runtime: "); return !error; } #else /* !__APPLE__ */ static int handle_append_runtime(int handle, char *exename, ReportList *reports) { char *runtime= get_runtime_path(exename); unsigned char buf[1024]; int count, progfd= -1, error= 0; if (!BLI_exists(runtime)) { BKE_report(reports, RPT_ERROR, "Unable to find runtime."); error= 1; goto cleanup; } progfd= open(runtime, O_BINARY|O_RDONLY, 0); if (progfd==-1) { BKE_report(reports, RPT_ERROR, "Unable to find runtime.@"); error= 1; goto cleanup; } while ((count= read(progfd, buf, sizeof(buf)))>0) { if (write(handle, buf, count)!=count) { BKE_report(reports, RPT_ERROR, "Unable to write to output file."); error= 1; goto cleanup; } } cleanup: if (progfd!=-1) close(progfd); if (runtime) MEM_freeN(runtime); return !error; } 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); } int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *reports) { int outfd= open(file, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0777); int datastart, error= 0; if (!outfd) { BKE_report(reports, RPT_ERROR, "Unable to open output file."); error= 1; goto cleanup; } if (!handle_append_runtime(outfd, exename, reports)) { error= 1; goto cleanup; } datastart= lseek(outfd, 0, SEEK_CUR); write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags); if (!handle_write_msb_int(outfd, datastart) || (write(outfd, "BRUNTIME", 8)!=8)) { BKE_report(reports, RPT_ERROR, "Unable to write to output file."); error= 1; goto cleanup; } cleanup: if (outfd!=-1) close(outfd); BKE_reports_prepend(reports, "Unable to make runtime: "); return !error; } #endif /* !__APPLE__ */