Logo Search packages:      
Sourcecode: vertex version File versions

editorfio.c

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/fio.h"

#include "msglist.h"
#include "guiutils.h"
#include "cdialog.h"

#include "v3dmp.h"
#include "v3dmodel.h"
#include "v3dfio.h"

#include "editor.h"
#include "editorcb.h"
#include "editorviewcb.h"
#include "editorp.h"
#include "editortexture.h"
#include "editorhf.h"
#include "editorlist.h"
#include "editortdialog.h"
#include "editorfio.h"

#include "view.h"
#include "viewcb.h"

#include "texbrowser.h"
#include "texbrowsercb.h"

#include "backup.h"
#include "vmastatusbar.h"
#include "vmacfg.h"
#include "vmacfglist.h"
#include "vma.h"

#include "messages.h"

#ifdef MEMWATCH
# include "memwatch.h"
#endif


#include "images/icon_save_20x20.xpm"
#include "images/icon_cancel_20x20.xpm"


/* Comment line parser, handles special token comment strings read by
 * this program. Called from EditorLoadModelFile() and EditorLoadModelData()
 */
static void PARSE_COMMENT_COLOR(vma_color_struct *c, const gchar *s);
static gint EditorV3DHandleComment(
      ma_editor_struct *editor, const gchar *s,
      v3d_model_struct *model
);

/* Save options prompt and callbacks. */
static gint EditorSavePromptCancelCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static void EditorSavePromptCancelBCB(
        GtkWidget *widget, gpointer data
);
static void EditorSavePromptSaveCB(
        GtkWidget *widget, gpointer data
);
static gbool EditorSaveDoPromptOptions(
      ma_editor_struct *editor, const char *filename
);

/* Load/save progress callbacks. */
static gint EditorLoadProgressCB(gpointer data, gint c, gint m);
static gint EditorSaveProgressCB(gpointer data, gint c, gint m);

/* Load V3D models from file and data functions. */
void EditorLoadModelFile(
      ma_editor_struct *editor, const gchar *filename
);
void EditorLoadModelData(
        ma_editor_struct *editor, const gchar **buf
);

/* Save V3D models to file functions. */
void EditorSaveModelFile(ma_editor_struct *editor, const gchar *filename);
void EditorSaveModelFileAs(ma_editor_struct *editor, const gchar *filename);


/* Callback results used in EditorSavePrompt*() callbacks. */
static gint save_prompt_block_level = 0;
static gint save_prompt_status = 0;


#define ISCR(c)         (((c) == '\n') || ((c) == '\r'))
#define ISCOMMENT(c)    ((c) == (VMA_CFG_COMMENT_CHAR))

#define MAX(a,b)  (((a) > (b)) ? (a) : (b))
#define MIN(a,b)  (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))

#define RADTODEG(r)     ((r) * 180 / PI)
#define DEGTORAD(d)     ((d) * PI / 180)


/*
 *    Loads argument s as a color statement for the given color
 *    structure.
 *
 *    This function should be called from EditorV3DHandleComment().
 */
static void PARSE_COMMENT_COLOR(vma_color_struct *c, const gchar *s)
{
      if((c == NULL) || (s == NULL))
          return;

      /* Argument s is assumed to be a slash separated list of
       * floating point values in the format r/g/b/a.
       */
      c->r = CLIP(atof(s), 0.0, 1.0);

      while(((*s) != '/') && ((*s) != '\0'))
          s++;
      while((*s) == '/')
          s++;
        c->g = CLIP(atof(s), 0.0, 1.0);

        while(((*s) != '/') && ((*s) != '\0'))
            s++;
        while((*s) == '/')
            s++;
        c->b = CLIP(atof(s), 0.0, 1.0);

        while(((*s) != '/') && ((*s) != '\0'))                
            s++;
        while((*s) == '/')
            s++;
        c->a = CLIP(atof(s), 0.0, 1.0);

      return;
}

/*
 *      Handles comment string s, with respect to the given inputs.
 *      This is to handle embeded special token parameters within comments
 *    that are program specific (ie setting up view and camera
 *    positions).
 *
 *      If the comment is not handled (ie it is a regular comment)
 *      then 0 will be returned, otherwise non-zero.
 *
 *      Input s assumed valid.
 */
gint EditorV3DHandleComment(
      ma_editor_struct *editor, const gchar *s,
      v3d_model_struct *model
)
{
      gchar **strv;
      gint strc;


      if(s == NULL)
          return(0);

      /* Seek past spaces. */
      while(ISBLANK(*s))
          s++;

      /* First non-blank a comment? */
      if(ISCOMMENT(*s))
          s++;
      else
          return(0);

        /* Its a comment, now seek past more spaces if any. */
        while(ISBLANK(*s))
            s++;

      /* Is a special token? */
      if((*s) == '$')
          s++;
      else
          return(0);

      /* Pointer s now positioned to parse special commented token
       * parameter.
       */
      /* Version. */
      if(strcasepfx(s, "version"))
      {


      }
#define DO_SET_2D_VIEW  \
{ \
 /* Seek to start of first argument. */ \
 while(!ISBLANK(*s) && ((*s) != '\0')) \
  s++; \
 while(ISBLANK(*s)) \
  s++; \
 \
 /* Parse arguments. */ \
 strv = strexp(s, &strc); \
 \
 /* Arguments (5 total): \
  * offset_i offset_j scale_i, scale_j path \
  */ \
 if(strc > 0) \
  v->bgimage_offset_i = atof(strv[0]); \
 if(strc > 1) \
  v->bgimage_offset_j = atof(strv[1]); \
 if(strc > 2) \
  v->bgimage_scale_i = atof(strv[2]); \
 if(strc > 3) \
  v->bgimage_scale_j = atof(strv[3]); \
 if(strc > 4) \
 { \
  g_free(v->bgimage_filename); \
  v->bgimage_filename = ((strv[4] != NULL) ? g_strdup(strv[4]) : NULL); \
 } \
 \
 /* Free parsed argument strings. */ \
 StringFreeArray(strv, strc); \
}

        /* View 1: background image */
        else if(strcasepfx(s, "view1_bgimage") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 0)
        )
      {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[0]
            );
            if(v != NULL)
            {
                DO_SET_2D_VIEW
            }
      }
        /* View 2: background image */
        else if(strcasepfx(s, "view2_bgimage") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 1)
        )
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[1]
            );
            if(v != NULL)
            {
                DO_SET_2D_VIEW
            }
        }
        /* View 3: background image */
        else if(strcasepfx(s, "view3_bgimage") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 2)
        )
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[2]
            );
            if(v != NULL)
            {
                DO_SET_2D_VIEW
            }
        }
#undef DO_SET_2D_VIEW
#define DO_SET_2D_VIEW  \
{ \
 /* Seek to start of first argument. */ \
 while(!ISBLANK(*s) && ((*s) != '\0')) \
  s++; \
 while(ISBLANK(*s)) \
  s++; \
 \
 /* Parse arguments. */ \
 strv = strexp(s, &strc); \
 \
 /* Arguments (6 total): \
  * vti vtj viewable_dim v_cur_i v_cur_j grid_spacing \
  */ \
 if(strc > 0) \
  v->v_ti = atof(strv[0]); \
 if(strc > 1) \
  v->v_tj = atof(strv[1]); \
 if(strc > 2) \
  v->viewable_dim = atof(strv[2]); \
 if(strc > 3) \
 { \
  v->v_cur_i = atof(strv[3]); \
  cursor_i = v->v_cur_i; \
 } \
 if(strc > 4) \
 { \
  v->v_cur_j = atof(strv[4]); \
  cursor_j = v->v_cur_j; \
 } \
 if(strc > 5) \
  v->grid_spacing = atof(strv[5]); \
 \
 View2DTranslateCB(v); \
 View2DZoomCB(v); \
 View2DGridCB(v); \
 View2DUpdateMenus(v); \
 \
 /* Free parsed argument strings. */ \
 StringFreeArray(strv, strc); \
}
      /* View 1: last positions and states. */
      else if(strcasepfx(s, "view1") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 0)
      )
      {
          vma_view2d_struct *v = ((editor == NULL) ?
            NULL : editor->view2d[0]
          );
          if(v != NULL)
          {
            gdouble cursor_i = 0.0, cursor_j = 0.0;

            DO_SET_2D_VIEW

            editor->vcursor_x = cursor_i;
            editor->vcursor_z = cursor_j;

                VMAStatusBarCursorPosition(
                editor->sb,
                editor->vcursor_x, editor->vcursor_y,
                editor->vcursor_z
            );
          }
      }
        /* View 2: last positions and states. */
        else if(strcasepfx(s, "view2") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 1)
        )
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[1]
            );
            if(v != NULL)
            {
                gdouble cursor_i = 0.0, cursor_j = 0.0;

            DO_SET_2D_VIEW

            editor->vcursor_y = cursor_i;
                editor->vcursor_z = cursor_j;

                VMAStatusBarCursorPosition(
                    editor->sb, 
                    editor->vcursor_x, editor->vcursor_y,
                    editor->vcursor_z
                );
            }
        }
        /* View 3: last positions and states. */
        else if(strcasepfx(s, "view3") &&
                (VMA_MAX_2D_VIEWS_PER_EDITOR > 2)
        )
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[2]
            );
            if(v != NULL)
            {
                gdouble cursor_i = 0.0, cursor_j = 0.0;

            DO_SET_2D_VIEW

                editor->vcursor_x = cursor_i;
                editor->vcursor_y = cursor_j;

                VMAStatusBarCursorPosition(
                    editor->sb, 
                    editor->vcursor_x, editor->vcursor_y,
                    editor->vcursor_z
                );
          }
        }
#undef DO_SET_2D_VIEW
        /* View 4: (the 3D view) last positions and states. */
        else if(strcasepfx(s, "view4") &&
                (VMA_MAX_3D_VIEWS_PER_EDITOR > 0)
      )
        {
            vma_view3d_struct *v = ((editor == NULL) ?
                NULL : editor->view3d[0]
            );
            if(v != NULL)
            {
                /* Seek to start of first argument. */
                while(!ISBLANK(*s) &&
                      !((*s) == '\0')   
                )
                    s++; 
                while(ISBLANK(*s))
                    s++;

                /* Parse arguments. */
                strv = strexp(s, &strc);

            /* Arguments (16 total):
             * x y z heading pitch bank clip_near clip_far fov
             * grid_spacing move_rate render_state cull_state
             * cull_counter_clockwise translations alpha_channel
             */
            if(strc > 0)
                v->cam_x = atof(strv[0]);
                if(strc > 1)
                    v->cam_y = atof(strv[1]);
                if(strc > 2)
                    v->cam_z = atof(strv[2]);

                if(strc > 3)
                    v->cam_h = DEGTORAD(atof(strv[3]));
                if(strc > 4)
                    v->cam_p = DEGTORAD(atof(strv[4]));
                if(strc > 5)
                    v->cam_b = DEGTORAD(atof(strv[5]));

                if(strc > 6)
                    v->cam_clip_near = atof(strv[6]);
                if(strc > 7)
                    v->cam_clip_far = atof(strv[7]);
                if(strc > 8)
            {
                    v->cam_fov = DEGTORAD(atof(strv[8]));
                if(v->cam_fov > PI)
                  v->cam_fov = PI;
                    if(v->cam_fov < 0.0)
                        v->cam_fov = DEGTORAD(40.0);
            }

                if(strc > 9)
                    v->grid_spacing = atof(strv[9]);
                if(strc > 10)
                    v->move_rate = atof(strv[10]);

                if(strc > 11)
                    v->render_state = atoi(strv[11]);
                if(strc > 12)
                v->cull_state = atoi(strv[12]);
            /* True if counter clockwise. */
                if(strc > 13)
                    v->cull_direction = atoi(strv[13]);
                if(strc > 14)
                    v->translations_state = atoi(strv[14]);
                if(strc > 15)
                    v->enable_alpha_channel = atoi(strv[15]);

            /* Call nessesary values change callbacks to ensure
             * changed values get applied to all related resources.
             * This may cause the view to be redrawn.
             */
            View3DMoveStrafeCB(v);
                View3DTurnCB(v);
                View3DGridCB(v);
            View3DMoveRateCB(v);
            View3DUpdateMenus(v);

                /* Free parsed argument strings. */
                StringFreeArray(strv, strc);
          }
        }
        /* Light. */
        else if(strcasepfx(s, "light") &&
                (VMA_MAX_3D_VIEWS_PER_EDITOR > 0)
        )
        {
          gint light_num;
          vma_light_struct *light_ptr;

          /* Seek to start of first argument. */
          while(!ISBLANK(*s) && ((*s) != '\0'))
            s++;
          while(ISBLANK(*s))
            s++;
          /* Parse arguments. */
          strv = strexp(s, &strc);

          /* First argument is light number. */
          if(strc > 0)
            light_num = atoi(strv[0]);
          else
            light_num = -1;

          if((light_num >= 0) && (light_num < VMA_LIGHTS_MAX))
            light_ptr = &(editor->light[light_num]);
          else
            light_ptr = NULL;
          if(light_ptr != NULL)
          {
            /* Second argument specifies if this light is enabled
             * or disabled.
             */
            if(strc > 1)
            {
                if(atoi(strv[1]))
                  light_ptr->flags |= VMA_LIGHT_FLAG_ENABLED;
                else
                  light_ptr->flags &= ~VMA_LIGHT_FLAG_ENABLED;
            }

            /* Position. */
                if(strc > 2)
                light_ptr->x = atof(strv[2]);
                if(strc > 3)
                    light_ptr->y = atof(strv[3]); 
                if(strc > 4)
                    light_ptr->z = atof(strv[4]); 

            /* Direction (in radians). */
                if(strc > 5)
                    light_ptr->heading = DEGTORAD(atof(strv[5]));
                if(strc > 6)
                    light_ptr->pitch = DEGTORAD(atof(strv[6]));
                if(strc > 7)
                    light_ptr->bank = DEGTORAD(atof(strv[7]));

            /* Ambient. */
            if(strc > 8)
                PARSE_COMMENT_COLOR(&light_ptr->ambient, strv[8]);
                /* Diffuse. */
                if(strc > 9)
                    PARSE_COMMENT_COLOR(&light_ptr->diffuse, strv[9]);
                /* Specular. */
                if(strc > 10)
                    PARSE_COMMENT_COLOR(&light_ptr->specular, strv[10]);

            /* Spot expoent. */
            if(strc > 11)
                light_ptr->spot_exponent = atof(strv[11]);
            /* Spot cutoff in radians. */
                if(strc > 12)
                    light_ptr->spot_cutoff = DEGTORAD(atof(strv[12]));

            /* Attenuation values. */
                if(strc > 13)
                    light_ptr->attenuation_constant = atof(strv[13]);
                if(strc > 14)
                    light_ptr->attenuation_linear = atof(strv[14]);
                if(strc > 15)
                    light_ptr->attenuation_quadratic = atof(strv[15]);
          }

          /* Free parsed argument strings. */
          StringFreeArray(strv, strc);
      }
      else
      {
          /* Some other special token this program doesn't support,
           * so just skip it and return 0 that way it looks like we
           * never parsed it (so calling function won't remove it).
           */
          return(0);
      }

      return(1);
}


/*
 *      V3D save options prompt "delete_event" callback.
 */
static gint EditorSavePromptCancelCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      EditorSavePromptCancelBCB(widget, data);
      return(TRUE);
}

/*
 *      V3D save options prompt cancel button callback.
 */
static void EditorSavePromptCancelBCB(
        GtkWidget *widget, gpointer data
)
{
      save_prompt_status = 0;       /* User canceled. */
      while(save_prompt_block_level > 0)
      {
          save_prompt_block_level--;
          gtk_main_quit();
      }
      return;
}

/*
 *    V3D save options prompt save callback.
 */
static void EditorSavePromptSaveCB( 
        GtkWidget *widget, gpointer data
)
{
      save_prompt_status = 1;       /* User clicked on save. */
        while(save_prompt_block_level > 0)
        {
            save_prompt_block_level--;
            gtk_main_quit();
        }
      return;
}

/*
 *    Blocks execution and prompts user for some V3D options.
 *
 *    Creates a V3D save prompt window and prompts the user for input.
 *
 *    Will update the global cfg items list and may return FALSE if
 *    the user canceled.
 */
static gbool EditorSaveDoPromptOptions(
        ma_editor_struct *editor, const char *filename
)
{
      const gchar *msglist[] = VMA_MSGLIST_V3DPROMPT_TOOLTIPS;
      gint  border_major = 5,
            border_minor = 2;
      GdkWindow *window;
      GtkWidget *w, *parent, *parent2;
      GtkWidget   *toplevel, *main_vbox,
                  *prompt_on_save_check,
                  *optimization_scale,
                  *strip_extras_check,
                  *save_btn, *cancel_btn;
      GtkAdjustment *adj;
      GtkAccelGroup *accelgrp;
      gpointer client_data = (gpointer)editor;

#define BTN_WIDTH (100 + (2 * 3))
#define BTN_HEIGHT      (30 + (2 * 3))


      if(editor == NULL)
          return(FALSE);

        /* Keyboard accelerator group. */
      accelgrp = gtk_accel_group_new();

        /* Create save prompt toplevel. */
        toplevel = parent = w = gtk_window_new(GTK_WINDOW_DIALOG);
        gtk_widget_realize(w);
        window = w->window;
        if(window != NULL)
        {
            gdk_window_set_decorations(
                window,
                GDK_DECOR_TITLE | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE
            );
            gdk_window_set_functions(
                window,
                GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | GDK_FUNC_CLOSE
            );
        }
        gtk_signal_connect(
            GTK_OBJECT(w), "delete_event",
            GTK_SIGNAL_FUNC(EditorSavePromptCancelCB),
            (gpointer)client_data
        );
      gtk_window_set_title(
          GTK_WINDOW(w), "V3D Format Options"
      );
        gtk_container_border_width(GTK_CONTAINER(w), 0);
      gtk_accel_group_attach(accelgrp, GTK_OBJECT(w));


        /* Main vbox. */
      main_vbox = w = gtk_vbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(parent), w);
        gtk_widget_show(w);
        parent = w;

      w = gtk_vbox_new(FALSE, border_major);
      gtk_container_border_width(GTK_CONTAINER(w), border_major);
        gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
        gtk_widget_show(w);
        parent = w;

      /* Optimization scale. */
        w = gtk_hbox_new(FALSE, border_minor);
        gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent2 = w;
      /* Label. */
      w = gtk_label_new("Optimization Level:");
      gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
      gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      /* Adjustment. */
        adj = (GtkAdjustment *)gtk_adjustment_new(
            (gfloat)VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_V3DFMT_OPTIMIZATION
          ),
            0.0,                                        /* Minimum. */
            V3D_SAVE_FILE_OPTIMIZATION_MAX + 1.0,     /* Maximum. */
            1.0,                /* Step inc. */
            1.0,                /* Page inc. */
            1.0                 /* Page size. */
        );
      /* Scale. */
      optimization_scale = w = gtk_hscale_new(adj);
      gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      gtk_widget_set_usize(w, 100, -1);
        GUISetWidgetTip(
            w,
            MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_V3DPROMPT_OPTIMIZATION
            )
        );
      gtk_widget_show(w);
      gtk_scale_set_draw_value(GTK_SCALE(w), TRUE);
      gtk_scale_set_value_pos(GTK_SCALE(w), GTK_POS_RIGHT);
      gtk_scale_set_digits(GTK_SCALE(w), 0);

        /* Strip extraneous data check. */
        w = gtk_hbox_new(FALSE, border_minor);
        gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent2 = w;
        /* Check button. */
      strip_extras_check = w = gtk_check_button_new_with_label(
            "Strip Extranous Data"
        );
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        GTK_TOGGLE_BUTTON(w)->active = (VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_V3DFMT_STRIP_EXTRAS
        ) ? TRUE : FALSE);
        GUISetWidgetTip(
            w,
            MsgListMatchCaseMessage(
                msglist, VMA_MSGNAME_V3DPROMPT_STRIP_EXTRAS
            )
        );
        gtk_widget_show(w);


        /* Horizontal rule. */
        w = gtk_hseparator_new();
        gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
 

      /* Prompt on save check. */
      w = gtk_hbox_new(FALSE, border_minor);
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent2 = w;
      /* Check button. */
      prompt_on_save_check = w = gtk_check_button_new_with_label(
          "Always Prompt On Save"
      );
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
      GTK_TOGGLE_BUTTON(w)->active = (VMACFGItemListGetValueI(
          option, VMA_CFG_PARM_V3DFMT_PROMPT_ON_SAVE
      ) ? TRUE : FALSE);
        gtk_widget_show(w);



      /* Horizontal rule. */
      w = gtk_hseparator_new();
        gtk_box_pack_start(GTK_BOX(main_vbox), w, FALSE, FALSE, 0);
        gtk_widget_show(w);

      /* Homogeneous hbox for buttons. */
      w = gtk_hbox_new(TRUE, 0);
      gtk_box_pack_start(GTK_BOX(main_vbox), w, FALSE, FALSE, border_major);
      gtk_widget_show(w);
      parent2 = w;

        /* Save button. */
      save_btn = w = GUIButtonPixmapLabelH(
            (u_int8_t **)icon_save_20x20_xpm, "Save", NULL
        );
        gtk_widget_set_usize(w, BTN_WIDTH, BTN_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
        gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(EditorSavePromptSaveCB), client_data
        );
        gtk_accel_group_add(
            accelgrp, GDK_space, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_Return, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_3270_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_KP_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_ISO_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, 's', 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
      gtk_widget_show(w);

        /* Cancel button. */
      cancel_btn = w = GUIButtonPixmapLabelH(
            (u_int8_t **)icon_cancel_20x20_xpm, "Cancel", NULL
        );
        gtk_widget_set_usize(w, BTN_WIDTH, BTN_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
        gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(EditorSavePromptCancelBCB),
          (gpointer)client_data
        );
        gtk_accel_group_add(
            accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, 'c', 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_widget_show(w);

      /* Set modal for editor's toplevel. */
      gtk_window_set_modal(GTK_WINDOW(toplevel), TRUE);
      gtk_window_set_transient_for(
          GTK_WINDOW(toplevel), GTK_WINDOW(editor->toplevel)
      );

      /* Set save button as the default button. */
      w = save_btn;
      if(w != NULL)
      {
          gtk_widget_grab_focus(w);
          gtk_widget_grab_default(w);
      }

      /* Map the whole thing. */
      gtk_widget_show(toplevel);

      /* Reset save prompt status. */
      save_prompt_status = 0;


      /* Enter GTK+ main block loop. */
      if(save_prompt_block_level < 0)
          save_prompt_block_level = 0;
      save_prompt_block_level++;
      gtk_main();

      if(save_prompt_block_level > 0)
      {
          fprintf(
            stderr,
"EditorSaveDoPromptOptions(): save_prompt_block_level %i.\n",
            save_prompt_block_level
          );
      }

      /* Unset modal on our toplevel. */
      gtk_window_set_modal(
          GTK_WINDOW(toplevel), FALSE
      );
      gtk_window_set_transient_for(
          GTK_WINDOW(toplevel), NULL
      );

      /* Check status. */
      switch(save_prompt_status)
      {
        case 1:
          /* Clicked on save, begin updating global configuration 
           * values.
           */
          /* Optimization level. */
          w = optimization_scale;
          if(w != NULL)
          {
            adj = gtk_range_get_adjustment(GTK_RANGE(w));
            if(adj != NULL)
            {
                u_int32_t val_u32 = (u_int32_t)adj->value;
                VMACFGItemListMatchSetValue(
                  option, VMA_CFG_PARM_V3DFMT_OPTIMIZATION,
                  (void *)&val_u32, FALSE
                );
                }
          }

          /* Strip extraneous data. */
          w = strip_extras_check;
          if(w != NULL)
          {
            u_int8_t val_u8 = (u_int8_t)GTK_TOGGLE_BUTTON(w)->active;
            VMACFGItemListMatchSetValue(
                option, VMA_CFG_PARM_V3DFMT_STRIP_EXTRAS,
                (void *)&val_u8, FALSE
            );
          }

            /* Prompt for options on save. */
            w = prompt_on_save_check;
            if(w != NULL)
            {
                u_int8_t val_u8 = (u_int8_t)GTK_TOGGLE_BUTTON(w)->active;
                VMACFGItemListMatchSetValue(
                    option, VMA_CFG_PARM_V3DFMT_PROMPT_ON_SAVE,
                    (void *)&val_u8, FALSE
                );
            }

          /* Fall through to case cancel. */

        default:
          /* All else assume clicked on cancel. */

          /* Destroy all widgets we created for this prompt. */
          gtk_widget_destroy(optimization_scale);
          gtk_widget_destroy(strip_extras_check);
          gtk_widget_destroy(prompt_on_save_check);
          gtk_widget_destroy(save_btn);
          gtk_widget_destroy(cancel_btn);
          gtk_widget_destroy(toplevel);
          gtk_accel_group_unref(accelgrp);
          break;
      }

      return(save_prompt_status ? TRUE : FALSE);
}



/*
 *    Load progress callback.
 */
static gint EditorLoadProgressCB(gpointer data, gint c, gint m)
{
        ma_editor_struct *editor = (ma_editor_struct *)data;
        if(editor == NULL)
            return(1);

        if(!editor->initialized)
            return(1);

        if(m > 0)
            VMAStatusBarProgress(
                editor->sb,
                (double)c / (double)m
            );
        else
            VMAStatusBarProgress(
                editor->sb,
                -1.0
            );

        return(0);
}

/*
 *    Save progress callback.
 */
static gint EditorSaveProgressCB(gpointer data, gint c, gint m)
{
      ma_editor_struct *editor = (ma_editor_struct *)data;
      if(editor == NULL)
          return(1);

      if(!editor->initialized)
          return(1);

      if(m > 0)
          VMAStatusBarProgress(
            editor->sb,
            (double)c / (double)m
          );
      else
          VMAStatusBarProgress(
                editor->sb,
                -1.0
            );

      return(0);
}


/*
 *    Loads the contents from the model file into the editor.
 *
 *    If VMA_CFG_PARM_BACKUP_ON_OPEN is true then the given filename
 *    will be backed up before opening.
 *
 *    Current loaded filename will be updated on the editor.
 */
void EditorLoadModelFile(
      ma_editor_struct *editor, const gchar *filename
)
{
      gint i, n, j, status;
      FILE *fp;
      struct stat stat_buf;
      ma_texture_browser_struct *tb;

      gchar *strptr;
      v3d_model_struct *model;
      gpointer p;
      mp_comment_struct *mp_comment;


      if((editor == NULL) || (filename == NULL))
          return;

      /* Check if editor is initailized. */
      if(!editor->initialized)
      {
          fprintf(
            stderr,
 "EditorLoadModelFile(): Cannot load to uninitialized editor.\n"
          );
          return;
      }

        /* Backup existing file if possible? */
        if(VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_BACKUP_ON_OPEN
        ))
        {
            gint min, max, new_index_highest;

            min = 1;
            max = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_BACKUP_MAX
            );
            new_index_highest = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_BACKUP_NEW_INDEX_HIGHEST
            );
            if(VMABackupFile(
                filename,
                min, max,
                (new_index_highest) ?
                    VMA_BACKUP_ORDER_HL : VMA_BACKUP_ORDER_LH,
                1               /* Can remove overflow. */
            ) != VMA_BACKUP_SUCCESS)
            {
                gint len = 256 + strlen(filename);
                gchar *buf;

                buf = (gchar *)g_malloc((len + 1) * sizeof(gchar));
                if(buf != NULL)
                    sprintf(buf,
"Unable to make backup of file:\n\n    %s",
                        filename
                    );

                CDialogSetTransientFor(editor->toplevel);
                CDialogGetResponse(
"Backup Failed",
                buf,
"The attempt to make a backup of the specified file\n\
has failed. This may be caused by issues related to\n\
permissions, ownership, insufficient disk space, or\n\
an invalid backup setting (see Edit->Preferances->Backup).",
                    CDIALOG_ICON_ERROR,
                    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                    CDIALOG_BTNFLAG_OK
                );
                CDialogSetTransientFor(NULL);

                g_free(buf);
                buf = NULL;

                return;
            }
      }

        if(editor->processing)
            return;


      /* Mark as processing. */
      editor->processing = TRUE;
      EditorSetBusy(editor);
      VMAStatusBarMessage(editor->sb, "Loading model...");
      VMAStatusBarProgress(editor->sb, 0.0);

      /* Get pointer to texture browser structure. */
      tb = &editor->texture_browser;

        /* Clear texture browser listing. */
        TexBrowserListDeleteAll(tb);

        /* Delete all undos and redos. */
        VMAUndoListDeleteAll(&editor->undo, &editor->total_undos);
        VMAUndoListDeleteAll(&editor->redo, &editor->total_redos);

      /* Clear all lists and user loaded data from editor. */
      EditorListDeleteAllLists(editor);


      /* Check if file exists. */
      if(stat(filename, &stat_buf))
      {
          gint len = strlen(filename) + 256;
          gchar *buf;

          buf = (gchar *)g_malloc(len);
          if(buf != NULL)
            sprintf(
                buf,
"No such file:\n\
    %s",
                filename
            );

          CDialogSetTransientFor(editor->toplevel);
          CDialogGetResponse(
"Open Failed",
buf,
"The specified file does not exist, please verify that\n\
the location is specified correctly and permissions\n\
allow reading of the file.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);

          g_free(buf);

          EditorSetReady(editor);
          editor->processing = FALSE;

          return;
      }

      /* Open file. */
        fp = FOpen(filename, "rb");
        if(fp == NULL)
        {     
            char text[PATH_MAX + NAME_MAX + 256];

            (*text) = '\0';
            strcat(text, "Cannot open file:\n    ");
            strncat(text, filename, PATH_MAX + NAME_MAX);

          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Open Failed",
text,
"The specified file cannot be opened, verify that it\n\
has read permissions.",  
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
          EditorSetReady(editor);
          editor->processing = FALSE;
            return;
        }


      /* Update loaded filename. */
      g_free(editor->loaded_filename);
      editor->loaded_filename = StringCopyAlloc(filename);

        /* Begin reading model file. */
      status = V3DLoadModel(
          NULL, fp,
          &editor->mh_item, &editor->total_mh_items,
          &editor->model, &editor->total_models,
          (void *)editor,
          EditorLoadProgressCB
      );

      /* Close file. */
        FClose(fp);
      fp = NULL;

      if(status)
      {
          /* Load error. */
            gchar text[PATH_MAX + NAME_MAX + 256];


          /* Delete all models and textures just loaded if any. */
          EditorListDeleteAllLists(editor);

            (*text) = '\0';
            strcat(text, "Error occured while loading:\n\n    ");
            strncat(text, filename, PATH_MAX + NAME_MAX);
          strcat(text, "\n\nFile may be corrupt.");

          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Model file loading error",
text,
"The specified file could not be loaded due to an\n\
error that was encountered during the load. Check if\n\
the file may be corrupt.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
            EditorSetReady(editor);
          editor->processing = FALSE;
          return;
      }

      /* Parse through each other data line and comment primitive
       * on each model for commented special token parameters used
       * by this program.
       */
      for(i = 0; i < editor->total_models; i++)
      {
          model = editor->model[i];
          if(model == NULL)
            continue;

          /* Go through each primitive. */
          for(n = 0; n < model->total_primitives; n++)
          {
            p = model->primitive[n];
            if(p == NULL)
                continue;

            /* Is it a primitive of type comment? */
            if((*(gint *)p) == V3DMP_TYPE_COMMENT)
            {
                gint line_num;

                mp_comment = (mp_comment_struct *)p;

                for(line_num = 0; line_num < mp_comment->total_lines; line_num++)
                {
                    if(EditorV3DHandleComment(
                      editor, mp_comment->line[line_num],
                      model
                    ))
                    {
                      /* Was parsed, so remove line from comment
                       * primitive.
                       */
                      mp_comment->total_lines--;
                      g_free(mp_comment->line[line_num]);
                      for(j = line_num; j < mp_comment->total_lines; j++)
                        mp_comment->line[j] = mp_comment->line[j + 1];

                            /* Deincrement line count so current line gets
                             * parsed again since it was shifted.
                             */
                      line_num--;
                  }
                }

                /* If all lines gone from this comment primitive then
                 * destroy this comment primitive.
                 */
                if(mp_comment->total_lines <= 0)
                {
                  V3DMPDestroy(p);
                  p = NULL;
                  model->total_primitives--;
                  for(j = n; j < model->total_primitives; j++)
                      model->primitive[j] = model->primitive[j + 1];

                  if(model->total_primitives > 0)
                  {
                      model->primitive = (gpointer *)g_realloc(
                          model->primitive,
                          model->total_primitives * sizeof(gpointer)
                      );
                      if(model->primitive == NULL)
                      {
                        model->total_primitives = 0;
                      }
                  }
                  else
                  {
                      g_free(model->primitive);
                      model->primitive = NULL;
                      model->total_primitives = 0;
                  }

                        /* Deincrement primitive count so current
                   * primitive gets parsed again since it was shifted.
                   */
                  n--;
                  continue;
                }
            }     /* Is it a primitive of type comment? */
            if(p == NULL)
                continue;

          } /* Go through each primitive. */


          /* Go through each other data line. */
            for(n = 0; n < model->total_other_data_lines; n++)
            {
                strptr = model->other_data_line[n];
                if(strptr == NULL)
                    continue;

            /* Handle this line as a comment. */
                if(EditorV3DHandleComment(
                    editor, strptr,
                    model
                ))
            {
                /* Delete this line. */
                g_free(strptr);
                model->total_other_data_lines--;

                    for(j = n; j < model->total_other_data_lines; j++)
                        model->other_data_line[j] =
                      model->other_data_line[j + 1];

                    if(model->total_other_data_lines > 0)
                    {
                        model->other_data_line = (gchar **)g_realloc(
                            model->other_data_line,
                            model->total_other_data_lines * sizeof(gchar *)
                        );
                    }
                    else
                    {
                        g_free(model->other_data_line);
                        model->other_data_line = NULL;
                    }
                    if(model->other_data_line == NULL)
                        model->total_other_data_lines = 0;

                /* Deincrement line count so current line gets
                 * parsed again since it was shifted.
                 */
                n--;
                continue;
            }
          } /* Go through each other data line. */
      }     /* Go through each loaded model. */


      /* Load textures from model header to texture browser and
       * editor.
       */
      TexBrowserListFetch(tb, editor);
      EditorTextureLoadAll(editor);

      /* Realize all loaded primitives on all models. */
        for(i = 0; i < editor->total_models; i++)
          EditorPrimitiveRealizeAll(editor, i, TRUE);

      /* Add models to gui models list. */
      EditorListAddModelsRG(editor);


      /* Mark as done loading. */
        VMAStatusBarMessage(editor->sb, "Loading done");
      VMAStatusBarProgress(editor->sb, 0.0);
        EditorSetReady(editor);
        editor->processing = FALSE;

      /* Select the first model with primitives if any. */
      for(i = 0; i < editor->total_models; i++)
      {
          if(editor->model[i] == NULL)
            continue;

          if(editor->model[i]->type == V3D_MODEL_TYPE_STANDARD)
            break;
      }
      if(i < editor->total_models)
      {
          GtkWidget *w = editor->models_list;
          if(w != NULL)
          {
            gtk_clist_select_row(
                GTK_CLIST(w),
                i,      /* Row. */
                0 /* Colum. */
            );
          }
      }

        /* Reset has changes. */
        editor->has_changes = FALSE;

      /* Redraw. */
        EditorRedrawAllViews(editor);

      return;
}

/*
 *      Loads the contents from the model data into the editor, same
 *    as EditorLoadModelFile() except it loads from memory.
 *
 *    The given buf must point to an array of null terminated strings
 *    where the last pointer to a string is actually a NULL pointer.
 *    The strings should be of V3D format data.
 *
 *      Current loaded filename will NOT be modified on the editor.
 */
void EditorLoadModelData(
      ma_editor_struct *editor, const gchar **buf
)
{
      gint i, n, j, status;
      gchar *strptr;
        ma_texture_browser_struct *tb;
      v3d_model_struct *model;
      gpointer p;
      mp_comment_struct *mp_comment;

        if((editor == NULL) || (buf == NULL))
            return;

        /* Check if editor is initailized. */
        if(!editor->initialized)
        {
            fprintf(
                stderr,
 "EditorLoadModelData(): Cannot load to uninitialized editor.\n"
            );
            return;
        }   

        if(editor->processing)
            return;


      /* Mark as processing. */
      editor->processing = TRUE;
        EditorSetBusy(editor);
        VMAStatusBarMessage(editor->sb, "Loading model...");
      VMAStatusBarProgress(editor->sb, 0.0);

        /* Get pointer to texture browser structure. */
        tb = &editor->texture_browser;

        /* Clear texture browser listing. */
        TexBrowserListDeleteAll(tb);

        /* Delete all undos and redos. */
        VMAUndoListDeleteAll(&editor->undo, &editor->total_undos);
        VMAUndoListDeleteAll(&editor->redo, &editor->total_redos);

        /* Clear all lists and user loaded data from editor. */
        EditorListDeleteAllLists(editor);


        /* Leave loaded filename value as is. */


        /* Begin reading model data. */
        status = V3DLoadModel(
            buf, NULL,
            &editor->mh_item, &editor->total_mh_items,
            &editor->model, &editor->total_models,
            (void *)editor,
            EditorLoadProgressCB
        );
        if(status)
        {
            /* Load error. */

            /* Delete all models and textures just loaded if any. */
            EditorListDeleteAllLists(editor);

          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Model data loading error",
"Could not load model from internal data.",
"An internal error has occured while attempting to load\n\
a model from internal data which was (apparently)\n\
defined at the time this program was compiled.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
            EditorSetReady(editor);
          editor->processing = FALSE;
            return;
        }


        /* Parse through each other data line and comment primitive
         * on each model for commented special token parameters used
         * by this program.
         */
        for(i = 0; i < editor->total_models; i++)
        {
            model = editor->model[i];
            if(model == NULL)
                continue;

            /* Go through each primitive. */
            for(n = 0; n < model->total_primitives; n++)
            {
                p = model->primitive[n];
                if(p == NULL)
                    continue;

                /* Is it a primitive of type comment? */
                if((*(gint *)p) == V3DMP_TYPE_COMMENT)
                {
                gint line_num;

                    mp_comment = (mp_comment_struct *)p;

                    for(line_num = 0; line_num < mp_comment->total_lines; line_num++)
                    {
                        if(EditorV3DHandleComment(
                            editor, mp_comment->line[line_num],
                            model
                        ))
                        {
                            /* Was parsed, so remove line from comment
                             * primitive.
                             */
                            mp_comment->total_lines--;
                            g_free(mp_comment->line[line_num]);
                            for(j = line_num; j < mp_comment->total_lines; j++)
                                mp_comment->line[j] = mp_comment->line[j + 1];

                            /* Deincrement line count so current line gets
                             * parsed again since it was shifted.
                             */
                            line_num--;
                        }
                    }

                    /* If all lines gone from this comment primitive then
                     * destroy this comment primitive.
                     */
                    if(mp_comment->total_lines <= 0)
                    {
                        V3DMPDestroy(p);
                        p = NULL;
                        model->total_primitives--;  
                        for(j = n; j < model->total_primitives; j++)
                            model->primitive[j] = model->primitive[j + 1];

                        if(model->total_primitives > 0)
                        {
                            model->primitive = (gpointer *)g_realloc(
                                model->primitive,
                                model->total_primitives * sizeof(gpointer)
                            );
                            if(model->primitive == NULL)
                            {
                                model->total_primitives = 0;
                            }
                        }
                        else
                        {
                            g_free(model->primitive);
                            model->primitive = NULL;
                            model->total_primitives = 0;
                        }

                        /* Deincrement primitive count so current
                         * primitive gets parsed again since it was shifted.
                         */
                        n--;
                        continue;
                    }
                }       /* Is it a primitive of type comment? */
                if(p == NULL)
                    continue;

            }     /* Go through each primitive. */

            /* Go through each other data line. */
            for(n = 0; n < model->total_other_data_lines; n++)
            {
                strptr = model->other_data_line[n];
                if(strptr == NULL)
                    continue;

                /* Handle this line as a comment. */  
                if(EditorV3DHandleComment(
                    editor, strptr,
                    model
                ))
                {
                /* Delete this line. */
                    g_free(strptr);
                    model->total_other_data_lines--;
             
                    for(j = n; j < model->total_other_data_lines; j++)
                        model->other_data_line[j] =
                            model->other_data_line[j + 1];
                
                    if(model->total_other_data_lines > 0)
                    {
                        model->other_data_line = (gchar **)g_realloc(
                            model->other_data_line,
                            model->total_other_data_lines * sizeof(gchar *)
                        );
                    }
                    else
                    {
                        g_free(model->other_data_line);
                        model->other_data_line = NULL;
                    }
                    if(model->other_data_line == NULL)
                        model->total_other_data_lines = 0;

                    /* Deincrement line count so current line gets
                     * parsed again since it was shifted.
                     */
                    n--;
                continue;
                }
            }     /* Go through each other data line. */
        }   /* Go through each loaded model. */


        /* Load textures from model header to texture browser and  
         * editor.
         */
        TexBrowserListFetch(tb, editor);
        EditorTextureLoadAll(editor);

        /* Realize all loaded primitives on all models. */
        for(i = 0; i < editor->total_models; i++)
            EditorPrimitiveRealizeAll(editor, i, TRUE);

        /* Add models to gui models list. */
        EditorListAddModelsRG(editor);


      /* Mark as done loading. */
        VMAStatusBarMessage(editor->sb, "Loading done");
      VMAStatusBarProgress(editor->sb, 0.0);
        EditorSetReady(editor);
        editor->processing = FALSE;

        /* Select the first model with primitives if any. */
        for(i = 0; i < editor->total_models; i++)
        {
            if(editor->model[i] == NULL)
                continue;
        
            if(editor->model[i]->type == V3D_MODEL_TYPE_STANDARD)
                break;
        }
        if(i < editor->total_models)
        {
            GtkWidget *w = editor->models_list;
            if(w != NULL)
            {
                gtk_clist_select_row(
                    GTK_CLIST(w),
                    i,  /* Row. */
                    0   /* Colum. */  
                );
            }
        }

        /* Reset has changes. */
        editor->has_changes = FALSE;

      /* Redraw. */
      EditorRedrawAllViews(editor);

      return;
}


/*
 *    Saves the model to the specified file.
 *
 *    Before any operation is performed, the data on the given editor
 *    will be synced, this is to ensure that the most up to date data
 *    is saved.
 *
 *    If global VMA_CFG_PARM_V3DFMT_PROMPT_ON_SAVE is true then
 *    the user will be prompted for V3D format options before saving
 *    or the user may cancel the save.
 *
 *    Just before the actual save begins, if the 
 *    VMA_CFG_PARM_BACKUP_ON_SAVE is true then the model file will be
 *    duplicated if it exists. So the backed up model file will contain
 *    the data of the previous file.
 *
 *    On successful save, the editor's has_changes member will be reset
 *    back to FALSE.
 *
 *    Menus on editor will be updated.
 */
void EditorSaveModelFile(ma_editor_struct *editor, const gchar *filename)
{
      gint status;
      FILE *fp;

      gbool prompt_on_save;
      gint optimization_level;
      gbool strip_extras;

      gchar out_text[2048];
      const gint out_text_len = 2048;


        if((editor == NULL) ||
           (filename == NULL)
      )
            return;

        /* Check if editor is initailized. */
        if(!editor->initialized)
        {
            fprintf(
                stderr,
 "EditorSaveModelFile(): Cannot save from uninitialized editor.\n"
            );
            return;
        }

      if(editor->processing)
          return;

      /* Get prompt on save cfg item value. */
      prompt_on_save = VMACFGItemListGetValueI(
          option, VMA_CFG_PARM_V3DFMT_PROMPT_ON_SAVE
      ) ? TRUE : FALSE;

      /* Need to query user for options before saving? */
      if(prompt_on_save)
      {
          /* Prompt for options. */
          if(!EditorSaveDoPromptOptions(
            editor, filename
          ))
          {
            /* User clicked on cancel. */
            return;
          }
      }

      /* Get newly update cfg item values. */
      /* Optimization level. */
        optimization_level = VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_V3DFMT_OPTIMIZATION
        );
      /* Strip extraneous data. */
        strip_extras = VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_V3DFMT_STRIP_EXTRAS
        );


      /* Backup existing file if possible? */
      if(VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_BACKUP_ON_SAVE
      ))
      {
          gint min, max, new_index_highest;

          min = 1;
          max = VMACFGItemListGetValueI(
                option, VMA_CFG_PARM_BACKUP_MAX
          );
          new_index_highest = VMACFGItemListGetValueI(
            option, VMA_CFG_PARM_BACKUP_NEW_INDEX_HIGHEST
          );
          if(VMABackupFile(
            filename,
            min, max,
            (new_index_highest) ?
                VMA_BACKUP_ORDER_HL : VMA_BACKUP_ORDER_LH,
            1           /* Can remove overflow. */
          ) != VMA_BACKUP_SUCCESS)
          {
                gint len = 256 + strlen(filename);
                gchar *buf;

                buf = (gchar *)g_malloc((len + 1) * sizeof(gchar));
                if(buf != NULL)
                    sprintf(buf,
"Unable to make backup of file:\n\n    %s",
                        filename
                    );

                CDialogSetTransientFor(editor->toplevel);
                CDialogGetResponse(
"Backup Failed",
                buf,
"The attempt to make a backup of the specified file\n\
has failed. This may be caused by issues related to\n\
permissions, ownership, insufficient disk space, or\n\
an invalid backup setting (see Edit->Preferances->Backup).",
                    CDIALOG_ICON_ERROR,
                    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                    CDIALOG_BTNFLAG_OK
                );
                CDialogSetTransientFor(NULL);

                g_free(buf);
                buf = NULL;

            return;
          }
      }

        /* Open file for writing. */
        fp = FOpen(filename, "wb");
        if(fp == NULL)
        {
            char text[PATH_MAX + NAME_MAX + 256];

            (*text) = '\0';
            strcat(text, "Cannot open file:\n\n    ");
            strncat(text, filename, PATH_MAX + NAME_MAX);   

          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Cannot open file for writing",
text,
"The specified file cannot be opened for writing, verify\n\
that it has write permissions.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
            EditorSetReady(editor);
            return;
        }

        /* Do not update loaded filename. */


      /* Apply values from related resources. */
      EditorSyncData(editor);


      /* Mark as processing. */
      editor->processing = TRUE;
      EditorSetBusy(editor);
      VMAStatusBarMessage(editor->sb, "Saving model...");
      VMAStatusBarProgress(editor->sb, 0.0);


      /* Begin writing file. */

      /* Save program specific comment tokens. */
#define DO_WRITE_OUT_TEXT     \
{ \
 out_text[out_text_len - 1] = '\0'; \
 fputs(out_text, fp); \
 fputc('\n', fp); \
}
      /* 2d views. */
      if(VMA_MAX_2D_VIEWS_PER_EDITOR > 0)
      {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[0]
            );
          if(v != NULL)
          {
            /* Positions (6 arguments total):
             * v_ti v_tj viewable_dim v_cur_i v_cur_j grid_spacing
             */
            sprintf(out_text, "#$view1 %f %f %f %f %f %f",
                v->v_ti, v->v_tj, v->viewable_dim,
                v->v_cur_i, v->v_cur_j, v->grid_spacing
            );
            DO_WRITE_OUT_TEXT

                /* Background image (5 arguments total):
                 * offset_i offset_j scale_i scale_j path
                 */
                sprintf(out_text, "#$view1_bgimage %f %f %f %f %s",
                    v->bgimage_offset_i, v->bgimage_offset_j, 
                v->bgimage_scale_i, v->bgimage_scale_j,
                ((v->bgimage_filename != NULL) ?
                  v->bgimage_filename : "none"
                )
                );
                DO_WRITE_OUT_TEXT
          }
      }
        if(VMA_MAX_2D_VIEWS_PER_EDITOR > 1)
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[1]
            );
            if(v != NULL)
            {
                /* Positions (6 arguments total):
                 * v_ti v_tj viewable_dim v_cur_i v_cur_j grid_spacing
                 */
                sprintf(out_text, "#$view2 %f %f %f %f %f %f",
                    v->v_ti, v->v_tj, v->viewable_dim,
                    v->v_cur_i, v->v_cur_j, v->grid_spacing
                );
                DO_WRITE_OUT_TEXT

               /* Background image (5 arguments total):
                 * offset_i offset_j scale_i scale_j path
                 */
                sprintf(out_text, "#$view2_bgimage %f %f %f %f %s",
                    v->bgimage_offset_i, v->bgimage_offset_j,
                    v->bgimage_scale_i, v->bgimage_scale_j,
                    ((v->bgimage_filename != NULL) ?
                        v->bgimage_filename : "none"
                    )
                );
                DO_WRITE_OUT_TEXT
            }
        }
        if(VMA_MAX_2D_VIEWS_PER_EDITOR > 2)
        {
            vma_view2d_struct *v = ((editor == NULL) ?
                NULL : editor->view2d[2]
            );
            if(v != NULL)
            {
                /* Positions (6 arguments total):
                 * v_ti v_tj viewable_dim v_cur_i v_cur_j grid_spacing
                 */
                sprintf(out_text, "#$view3 %f %f %f %f %f %f",
                    v->v_ti, v->v_tj, v->viewable_dim,
                    v->v_cur_i, v->v_cur_j, v->grid_spacing
                );
                DO_WRITE_OUT_TEXT

               /* Background image (5 arguments total):
                 * offset_i offset_j scale_i scale_j path
                 */
                sprintf(out_text, "#$view3_bgimage %f %f %f %f %s",
                    v->bgimage_offset_i, v->bgimage_offset_j,
                    v->bgimage_scale_i, v->bgimage_scale_j,
                    ((v->bgimage_filename != NULL) ?
                        v->bgimage_filename : "none"
                    )
                );
                DO_WRITE_OUT_TEXT
            }
        }
        if(VMA_MAX_3D_VIEWS_PER_EDITOR > 0)
        {
            vma_view3d_struct *v = ((editor == NULL) ?
                NULL : editor->view3d[0]
            );
            if(v != NULL)
            {
                /* Arguments (16 total):
                 * x y z heading pitch bank clip_near clip_far fov
                 * grid_spacing move_rate render_state cull_state
                 * cull_counter_clockwise translations alpha_channel
                 */
                sprintf(out_text,
 "#$view4\
 %f %f %f %f %f %f %f %f\
 %f %f %f %i %i %i %i %i",
                v->cam_x, v->cam_y, v->cam_z, 
                RADTODEG(v->cam_h), RADTODEG(v->cam_p), RADTODEG(v->cam_b),
                    v->cam_clip_near, v->cam_clip_far,
                RADTODEG(v->cam_fov),
                    v->grid_spacing, v->move_rate,
                    v->render_state, v->cull_state,
                    v->cull_direction, v->translations_state,
                    v->enable_alpha_channel
            );
            DO_WRITE_OUT_TEXT
            }
      }
      /* Lights. */
      if(1)
      {
          gint i;
          vma_light_struct *light_ptr;

          for(i = 0; i < VMA_LIGHTS_MAX; i++)
          {
            light_ptr = &(editor->light[i]);

                sprintf(out_text,
 "#$light\
 %i %i\
 %f %f %f\
 %f %f %f\
 %f/%f/%f/%f\
 %f/%f/%f/%f\
 %f/%f/%f/%f\
 %f %f\
 %f %f %f",
                    i, (gint)(light_ptr->flags & VMA_LIGHT_FLAG_ENABLED),
                light_ptr->x, light_ptr->y, light_ptr->z,
                RADTODEG(light_ptr->heading), RADTODEG(light_ptr->pitch), RADTODEG(light_ptr->bank),
                light_ptr->ambient.r, light_ptr->ambient.g, light_ptr->ambient.b, light_ptr->ambient.a,
                    light_ptr->diffuse.r, light_ptr->diffuse.g, light_ptr->diffuse.b, light_ptr->diffuse.a,
                light_ptr->specular.r, light_ptr->specular.g, light_ptr->specular.g, light_ptr->specular.a,
                light_ptr->spot_exponent, RADTODEG(light_ptr->spot_cutoff),
                light_ptr->attenuation_constant, light_ptr->attenuation_linear, light_ptr->attenuation_quadratic
                );
                DO_WRITE_OUT_TEXT
          }
      }

#undef DO_WRITE_OUT_TEXT

      /* Save rest by standard file format procedure. */
      status = V3DSaveModel(
          NULL, fp,
          editor->mh_item, editor->total_mh_items,
          editor->model, editor->total_models,
          optimization_level,
          strip_extras,
            (gpointer)editor,
            EditorSaveProgressCB
      );

        /* Close the file. */
        FClose(fp);
      fp = NULL;

      /* Mark as done saving. */
        VMAStatusBarMessage(editor->sb, "Saving done");
        VMAStatusBarProgress(editor->sb, 0.0);
        EditorSetReady(editor);
      editor->processing = FALSE;

      /* Check if there was an error saving the model. */
      if(status < 0)
      {
            /* Save error. */

          CDialogSetTransientFor(editor->toplevel);
            CDialogGetResponse(
"Model data saving error",
"An error occured while saving the model data to file.",
"An error has occured while saving the model data to a\n\
file. Verify that the model data in memory is not\n\
not corrupt, and do not trust the saved model data\n\
on the file in question.",
                CDIALOG_ICON_ERROR,
                CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_OK
            );
          CDialogSetTransientFor(NULL);
          return;
      }


      /* Reset has changes. */
      editor->has_changes = FALSE;

      /* Need to update menus on editor. */
      EditorUpdateMenus(editor);
      EditorUpdateAllViewMenus(editor);

      return;
}

/*
 *    Save the model to a new file specified by filename.
 *
 *    If the given file filename exists, then the user will be prompted
 *    to overwrite the existing file or not.
 *
 *    If after the above check, saving is to commence then the
 *    editor's loaded_filename member will be updated and the function
 *    EditorSaveModelFile() will be called.
 */
void EditorSaveModelFileAs(
      ma_editor_struct *editor, const gchar *filename
)
{
      gint status;
      struct stat stat_buf;

      if(editor == NULL)
          return;

        /* Check if editor is initailized. */
        if(!editor->initialized)
        {
            fprintf(
                stderr,
 "EditorSaveModelFileAs(): Cannot save from uninitialized editor.\n"
            );
            return;
        }

      if(editor->processing)
          return;

      /* Check if filename exists. */
      if(!stat(filename, &stat_buf))
      {
          char text[256 + PATH_MAX + NAME_MAX];

          strcpy(text, "Overwrite existing file:\n    ");
          strncat(text, filename, PATH_MAX + NAME_MAX);
          text[256 + PATH_MAX + NAME_MAX - 1] = '\0';

          CDialogSetTransientFor(editor->toplevel);
            status = CDialogGetResponse(
"Confirm Overwrite",
text,
"You are being asked if you want to overwrite an existing\n\
file. If you say yes then any existing data on the file\n\
will be replaced with the data you are intending to save.\n\
This new data may not correspond to the data in the existing\n\
file, if you are unsure say cancel.",
                CDIALOG_ICON_WARNING,
                CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_CANCEL |
                CDIALOG_BTNFLAG_HELP,
                CDIALOG_BTNFLAG_CANCEL
            );
          CDialogSetTransientFor(NULL);
          switch(status)
          {
            case CDIALOG_RESPONSE_YES:
              case CDIALOG_RESPONSE_YES_TO_ALL:
            case CDIALOG_RESPONSE_OK:
            break;

            default:
            return;
            break;
          }
      }

        /* Update loaded filename. */
      g_free(editor->loaded_filename);
        editor->loaded_filename = strdup(filename);

      /* Call regular save file function. */
      EditorSaveModelFile(editor, filename);
}

Generated by  Doxygen 1.6.0   Back to index