diff mbox series

[1/2] alsa-tools/hdspmixer: enhance saving of presets

Message ID 20210207145048.14580-2-superfassl@gmail.com (mailing list archive)
State New, archived
Headers show
Series [1/2] alsa-tools/hdspmixer: enhance saving of presets | expand

Commit Message

Jasmin Fazlic Feb. 7, 2021, 2:50 p.m. UTC
Changing the version in the file header would make
a preset file not readable by older versions of the
tool. If we just append new data always at the end
of the save procedure we should have no problems
reading them with different versions, as they all
just read to a certain point and ignore the rest
of the file.

This patch implements the logic to save the presets
first to a file called file_name.tmp and appends any
extra data that would come after in a possibly present
file_name file.

Any data written by newer versions would remain in
the preset file and from now on no old version should
remove data written by newer versions.

Also since we write to a temporary file and rename
afterwards an extra feature is gained of not corrupting
the preset should we crash.

Signed-off-by: Jasmin Fazlic <superfassl@gmail.com>
---
 hdspmixer/src/HDSPMixerWindow.cxx | 98 ++++++++++++++++++++-----------
 1 file changed, 64 insertions(+), 34 deletions(-)
diff mbox series

Patch

diff --git a/hdspmixer/src/HDSPMixerWindow.cxx b/hdspmixer/src/HDSPMixerWindow.cxx
index 9efc25d..3b3d668 100644
--- a/hdspmixer/src/HDSPMixerWindow.cxx
+++ b/hdspmixer/src/HDSPMixerWindow.cxx
@@ -353,18 +353,25 @@  void HDSPMixerWindow::save()
             sizeof(inputs->strips[0]->data[0][0][0]->fader_pos) /
             sizeof(inputs->strips[0]->data[0][0][0]->fader_pos[0]));
 
-
-    FILE *file;
-
-    if ((file = fopen(file_name, "w")) == NULL) {
-	fl_alert("Error opening file %s for saving", file_name);
+	FILE *in,*out;
+
+	/* We want to append any existing extra data that might got written by a
+	 * newer version to this file, therefore write our data to file_name.tmp
+	 * and append the old data. Also this way we would not corrupt the file
+	 * should we crash.
+	 */
+	std::string const tmp = file_name + std::string(".tmp");
+	char const * const tmpc = tmp.c_str();
+
+    if ((out = fopen(tmpc, "w")) == NULL) {
+	fl_alert("Error opening file %s for saving", tmpc);
     }
     if (dirty) {
 	inputs->buttons->presets->save_preset(current_preset+1);
     }
     /* since hdspmixer 1.11, we also store the meter level settings. Indicate
      * the new on-disk structure via a small header */
-    if (fwrite((void *)&header, sizeof(char), sizeof(header), file) !=
+    if (fwrite((void *)&header, sizeof(char), sizeof(header), out) !=
             sizeof(header)) {
         goto save_error;
     }
@@ -374,99 +381,122 @@  void HDSPMixerWindow::save()
 	    for (int preset = 0; preset < 8; ++preset) {
 		for (int channel = 0; channel < HDSP_MAX_CHANNELS; ++channel) {
 		    /* inputs pans and volumes */
-		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->pan_pos[0]), sizeof(int), pan_array_size, file) != pan_array_size) {
+		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->pan_pos[0]), sizeof(int), pan_array_size, out) != pan_array_size) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->fader_pos[0]), sizeof(int), pan_array_size, file) != pan_array_size) {
+		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->fader_pos[0]), sizeof(int), pan_array_size, out) != pan_array_size) {
 			goto save_error;
 		    }
 		    /* playbacks pans and volumes */
-		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->pan_pos[0]), sizeof(int), pan_array_size, file) != pan_array_size) {
+		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->pan_pos[0]), sizeof(int), pan_array_size, out) != pan_array_size) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->fader_pos[0]), sizeof(int), pan_array_size, file) != pan_array_size) {
+		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->fader_pos[0]), sizeof(int), pan_array_size, out) != pan_array_size) {
 			goto save_error;
 		    }
 		    /* inputs mute/solo/dest */
-		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->mute), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->mute), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->solo), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->solo), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->dest), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(inputs->strips[channel]->data[card][speed][preset]->dest), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
 		    /* playbacks mute/solo/dest */
-		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->mute), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->mute), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->solo), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->solo), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
-		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->dest), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(playbacks->strips[channel]->data[card][speed][preset]->dest), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
 		    /* outputs volumes */
-		    if (fwrite((void *)&(outputs->strips[channel]->data[card][speed][preset]->fader_pos), sizeof(int), 1, file) != 1) {
+		    if (fwrite((void *)&(outputs->strips[channel]->data[card][speed][preset]->fader_pos), sizeof(int), 1, out) != 1) {
 			goto save_error;
 		    }
 		    
  		}
 		/* Lineouts */		    
-		if (fwrite((void *)&(outputs->strips[HDSP_MAX_CHANNELS]->data[card][speed][preset]->fader_pos), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(outputs->strips[HDSP_MAX_CHANNELS]->data[card][speed][preset]->fader_pos), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(outputs->strips[HDSP_MAX_CHANNELS+1]->data[card][speed][preset]->fader_pos), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(outputs->strips[HDSP_MAX_CHANNELS+1]->data[card][speed][preset]->fader_pos), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
 		/* Global settings */
-		if (fwrite((void *)&(data[card][speed][preset]->input), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->input), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->output), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->output), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->playback), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->playback), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->submix), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->submix), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->submix_value), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->submix_value), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->solo), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->solo), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->mute), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->mute), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}		
-		if (fwrite((void *)&(data[card][speed][preset]->last_destination), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->last_destination), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->rmsplus3), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->rmsplus3), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->numbers), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->numbers), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->over), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->over), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->level), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->level), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
-		if (fwrite((void *)&(data[card][speed][preset]->rate), sizeof(int), 1, file) != 1) {
+		if (fwrite((void *)&(data[card][speed][preset]->rate), sizeof(int), 1, out) != 1) {
 		    goto save_error;
 		}
 	    }
 	}
     }
-    fclose(file);
+
+	/* If the file we want to write already exists it could be possible that it
+	* was saved with a newer version. If that is the case we just append its
+	* content to the new output file and that way ensure that we don't lose any
+	* data the new version wrote.
+	*/
+	if ((in = fopen(file_name, "r")) != NULL) {
+		if (!fseek(in, ftell(out), SEEK_SET)) {
+			char buff[512];
+			size_t read;
+			while ((read = fread(&buff, sizeof(char), sizeof(buff), in)) != 0)
+				fwrite(buff, sizeof(char), read, out);
+			if (ferror(in) || ferror(out))
+				fl_alert("Error appending %s to %s", file_name, tmpc);
+		}
+		fclose(in);
+	}
+
+	fclose(out);
+
+	if (rename(tmpc, file_name))
+		fl_alert("Error renaming %s to %s", tmpc, file_name);
+	::remove(tmpc);
+
     return;
 save_error:
-    fclose(file);
+    fclose(out);
     fl_alert("Error saving presets to file %s", file_name);
     return;
 }