fix [#33108] Running save_as_mainfile breaks relative texture paths
authorCampbell Barton <ideasman42@gmail.com>
Wed, 7 Nov 2012 04:13:03 +0000 (04:13 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 7 Nov 2012 04:13:03 +0000 (04:13 +0000)
save-as with path remapping left the paths relate to the file written.

source/blender/blenkernel/BKE_global.h
source/blender/blenkernel/BKE_utildefines.h
source/blender/blenlib/BLI_bpath.h
source/blender/blenlib/intern/bpath.c
source/blender/blenloader/intern/writefile.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_operators.c

index f7af534c2c2e837d40514ced083d3989127d2b2a..f6276a69d57742c792ae51e9e1568d0dcd4ea22c 100644 (file)
@@ -167,6 +167,9 @@ enum {
 #define G_FILE_RELATIVE_REMAP    (1 << 24)
 #define G_FILE_HISTORY           (1 << 25)
 #define G_FILE_MESH_COMPAT       (1 << 26)              /* BMesh option to save as older mesh format */
+#define G_FILE_SAVE_COPY         (1 << 27)              /* restore paths after editing them */
+
+#define G_FILE_FLAGS_RUNTIME (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_MESH_COMPAT | G_FILE_SAVE_COPY)
 
 /* G.windowstate */
 #define G_WINDOWSTATE_USERDEF       0
index 06f8b89ec05287a279b10b11c367337ef34ae46b..7c1e0e97565d4396fd946f5ce0a8d4d13ab62e77 100644 (file)
 extern "C" {
 #endif
 
-/* currently unused but we may want to add macros here for BKE later */
+#define BKE_BIT_TEST_SET(value, test, flag) \
+{                                           \
+       if (test) (value) |=  flag;             \
+       else      (value) &= ~flag;             \
+} (void)0
 
 #ifdef __cplusplus
 }
index a86b362c271d699e28031fbe368a3b28881d302c..438bffb2fc5d4ce1a5a09b2fe89725debc6fe9b5 100644 (file)
@@ -48,6 +48,11 @@ void BLI_bpath_traverse_id_list(struct Main *bmain, struct ListBase *lb, BPathVi
 void BLI_bpath_traverse_main(struct Main *bmain, BPathVisitor visit_cb, const int flag, void *userdata);
 int  BLI_bpath_relocate_visitor(void *oldbasepath, char *path_dst, const char *path_src);
 
+/* Functions for temp backup/restore of paths, path count must NOT change */
+void *BLI_bpath_list_backup(struct Main *bmain, const int flag);
+void  BLI_bpath_list_restore(struct Main *bmain, const int flag, void *ls_handle);
+void  BLI_bpath_list_free(void *ls_handle);
+
 #define BLI_BPATH_TRAVERSE_ABS             (1 << 0) /* convert paths to absolute */
 #define BLI_BPATH_TRAVERSE_SKIP_LIBRARY    (1 << 2) /* skip library paths */
 #define BLI_BPATH_TRAVERSE_SKIP_PACKED     (1 << 3) /* skip packed data */
index 8ae2b941fa858d074233be5f996885e71e02ca1c..be9f5f97a6e8d56766f16aa74011719fbb6a65ab 100644 (file)
@@ -618,3 +618,73 @@ int BLI_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *pat
                return FALSE;
        }
 }
+
+
+/* -------------------------------------------------------------------- */
+/**
+ * Backup/Restore/Free functions,
+ * \note These functions assume the data won't chane order.
+ */
+
+struct PathStore {
+       struct PathStore *next, *prev;
+} PathStore;
+
+static int bpath_list_append(void *userdata, char *UNUSED(path_dst), const char *path_src)
+{
+       /* store the path and string in a single alloc */
+       ListBase *ls = userdata;
+       size_t path_size = strlen(path_src) + 1;
+       struct PathStore *path_store = MEM_mallocN(sizeof(PathStore) + path_size, __func__);
+       char *filepath = (char *)(path_store + 1);
+
+       memcpy(filepath, path_src, path_size);
+       BLI_addtail(ls, path_store);
+       return FALSE;
+}
+
+static int bpath_list_restore(void *userdata, char *path_dst, const char *path_src)
+{
+       /* assume ls->first wont be NULL because the number of paths can't change!
+        * (if they do caller is wrong) */
+       ListBase *ls = userdata;
+       struct PathStore *path_store = ls->first;
+       const char *filepath = (char *)(path_store + 1);
+       int ret;
+
+       if (strcmp(path_src, filepath) == 0) {
+               ret = FALSE;
+       }
+       else {
+               BLI_strncpy(path_dst, filepath, FILE_MAX);
+               ret = TRUE;
+       }
+
+       BLI_freelinkN(ls, path_store);
+       return ret;
+}
+
+/* return ls_handle */
+void *BLI_bpath_list_backup(Main *bmain, const int flag)
+{
+       ListBase *ls = MEM_callocN(sizeof(ListBase), __func__);
+
+       BLI_bpath_traverse_main(bmain, bpath_list_append, flag, ls);
+
+       return ls;
+}
+
+void BLI_bpath_list_restore(Main *bmain, const int flag, void *ls_handle)
+{
+       ListBase *ls = ls_handle;
+
+       BLI_bpath_traverse_main(bmain, bpath_list_restore, flag, ls);
+}
+
+void BLI_bpath_list_free(void *ls_handle)
+{
+       ListBase *ls = ls_handle;
+       BLI_assert(ls->first == NULL);  /* assumes we were used */
+       BLI_freelistN(ls);
+       MEM_freeN(ls);
+}
index 8521b43e437cbba778879fcac08a7c15b185b308..ac5366c26a390d208bd584745bf10ed561c4d2eb 100644 (file)
@@ -2890,7 +2890,7 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
        fg.winpos= G.winpos;
 
        /* prevent to save this, is not good convention, and feature with concerns... */
-       fg.fileflags= (fileflags & ~(G_FILE_NO_UI|G_FILE_RELATIVE_REMAP|G_FILE_MESH_COMPAT));
+       fg.fileflags= (fileflags & ~G_FILE_FLAGS_RUNTIME);
 
        fg.globalf= G.f;
        BLI_strncpy(fg.filename, mainvar->name, sizeof(fg.filename));
@@ -3039,6 +3039,10 @@ int BLO_write_file(Main *mainvar, const char *filepath, int write_flags, ReportL
        char tempname[FILE_MAX+1];
        int file, err, write_user_block;
 
+       /* path backup/restore */
+       void     *path_list_backup = NULL;
+       const int path_list_flag = (BLI_BPATH_TRAVERSE_SKIP_LIBRARY | BLI_BPATH_TRAVERSE_SKIP_MULTIFILE);
+
        /* open temporary file, so we preserve the original in case we crash */
        BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
 
@@ -3048,6 +3052,11 @@ int BLO_write_file(Main *mainvar, const char *filepath, int write_flags, ReportL
                return 0;
        }
 
+       /* check if we need to backup and restore paths */
+       if (UNLIKELY((write_flags & G_FILE_RELATIVE_REMAP) && (G_FILE_SAVE_COPY & write_flags))) {
+               path_list_backup = BLI_bpath_list_backup(mainvar, path_list_flag);
+       }
+
        /* remapping of relative paths to new file location */
        if (write_flags & G_FILE_RELATIVE_REMAP) {
                char dir1[FILE_MAX];
@@ -3083,6 +3092,11 @@ int BLO_write_file(Main *mainvar, const char *filepath, int write_flags, ReportL
        err= write_file_handle(mainvar, file, NULL, NULL, write_user_block, write_flags, thumb);
        close(file);
 
+       if (UNLIKELY(path_list_backup)) {
+               BLI_bpath_list_restore(mainvar, path_list_flag, path_list_backup);
+               BLI_bpath_list_free(path_list_backup);
+       }
+
        if (err) {
                BKE_report(reports, RPT_ERROR, strerror(errno));
                remove(tempname);
index 549ded6ee485e674b653ef9f709312511754cdb7..c53c4dca74c856d3933b0faf58c887a43c5d4966 100644 (file)
@@ -106,7 +106,7 @@ int                 WM_homefile_read_exec(struct bContext *C, struct wmOperator *op);
 int                    WM_homefile_read(struct bContext *C, struct ReportList *reports, short from_memory);
 int                    WM_homefile_write_exec(struct bContext *C, struct wmOperator *op);
 void           WM_file_read(struct bContext *C, const char *filepath, struct ReportList *reports);
-int                    WM_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports, int copy);
+int                    WM_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports);
 void           WM_autosave_init(struct wmWindowManager *wm);
 
                        /* mouse cursors */
index 6124b03778d0a638b283fd12980eb30c29999d1f..0e4af46d0fcb83f1f9273ff599d652fd62cb26ff 100644 (file)
@@ -762,7 +762,7 @@ int write_crash_blend(void)
        }
 }
 
-int WM_file_write(bContext *C, const char *target, int fileflags, ReportList *reports, int copy)
+int WM_file_write(bContext *C, const char *target, int fileflags, ReportList *reports)
 {
        Library *li;
        int len;
@@ -818,7 +818,7 @@ int WM_file_write(bContext *C, const char *target, int fileflags, ReportList *re
        fileflags |= G_FILE_HISTORY; /* write file history */
 
        if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
-               if (!copy) {
+               if (!(fileflags & G_FILE_SAVE_COPY)) {
                        G.relbase_valid = 1;
                        BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));  /* is guaranteed current file */
        
index c9551ee28a4679d021c3f0ef07615a872c1cf287..054e48f0bfb65d821bcf1b0a8725ef18fc60e6b5 100644 (file)
@@ -72,6 +72,7 @@
 #include "BKE_report.h"
 #include "BKE_scene.h"
 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
+#include "BKE_utildefines.h"
 
 #include "BKE_idcode.h"
 
@@ -2077,7 +2078,6 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
 {
        char path[FILE_MAX];
        int fileflags;
-       int copy = 0;
 
        save_set_compress(op);
        
@@ -2087,29 +2087,27 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
                BLI_strncpy(path, G.main->name, FILE_MAX);
                untitled(path);
        }
-
-       if (RNA_struct_property_is_set(op->ptr, "copy"))
-               copy = RNA_boolean_get(op->ptr, "copy");
        
        fileflags = G.fileflags;
 
        /* set compression flag */
-       if (RNA_boolean_get(op->ptr, "compress")) fileflags |=  G_FILE_COMPRESS;
-       else fileflags &= ~G_FILE_COMPRESS;
-       if (RNA_boolean_get(op->ptr, "relative_remap")) fileflags |=  G_FILE_RELATIVE_REMAP;
-       else fileflags &= ~G_FILE_RELATIVE_REMAP;
+       BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"),
+                        G_FILE_COMPRESS);
+       BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"),
+                        G_FILE_RELATIVE_REMAP);
+       BKE_BIT_TEST_SET(fileflags,
+                        (RNA_struct_property_is_set(op->ptr, "copy") &&
+                         RNA_boolean_get(op->ptr, "copy")),
+                        G_FILE_SAVE_COPY);
+
 #ifdef USE_BMESH_SAVE_AS_COMPAT
-       /* property only exists for 'Save As' */
-       if (RNA_struct_find_property(op->ptr, "use_mesh_compat")) {
-               if (RNA_boolean_get(op->ptr, "use_mesh_compat")) fileflags |=  G_FILE_MESH_COMPAT;
-               else fileflags &= ~G_FILE_MESH_COMPAT;
-       }
-       else {
-               fileflags &= ~G_FILE_MESH_COMPAT;
-       }
+       BKE_BIT_TEST_SET(fileflags,
+                        (RNA_struct_find_property(op->ptr, "use_mesh_compat") &&
+                         RNA_boolean_get(op->ptr, "use_mesh_compat")),
+                        G_FILE_MESH_COMPAT);
 #endif
 
-       if (WM_file_write(C, path, fileflags, op->reports, copy) != 0)
+       if (WM_file_write(C, path, fileflags, op->reports) != 0)
                return OPERATOR_CANCELLED;
 
        WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);