fix [#33108] Running save_as_mainfile breaks relative texture paths

save-as with path remapping left the paths relate to the file written.
This commit is contained in:
Campbell Barton
2012-11-07 04:13:03 +00:00
parent cdce3e0949
commit 8740b6cd88
8 changed files with 116 additions and 22 deletions

View 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

View File

@@ -32,7 +32,11 @@
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
}

View 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 */

View 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);
}

View 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);

View 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 */

View 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 */

View 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);