1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00

Prevent data loss if the app crashes during save - Fix #763

When saving a pxo and a file of the same name already exists, the new file gets a temporary name (.pxo1 extension), and it gets renamed to its proper name if it's saved successfully (Pixelorama doesn't crash)
This commit is contained in:
Emmanouil Papadeas 2022-10-07 02:16:15 +03:00
parent 9f443f3ab0
commit fbe6ea9a97

View file

@ -319,7 +319,7 @@ func save_pxo_file(
) -> void: ) -> void:
if !autosave: if !autosave:
project.name = path.get_file() project.name = path.get_file()
var serialized_data = project.serialize() var serialized_data := project.serialize()
if !serialized_data: if !serialized_data:
Global.error_dialog.set_text( Global.error_dialog.set_text(
tr("File failed to save. Converting project data to dictionary failed.") tr("File failed to save. Converting project data to dictionary failed.")
@ -327,7 +327,7 @@ func save_pxo_file(
Global.error_dialog.popup_centered() Global.error_dialog.popup_centered()
Global.dialog_open(true) Global.dialog_open(true)
return return
var to_save = JSON.print(serialized_data) var to_save := JSON.print(serialized_data)
if !to_save: if !to_save:
Global.error_dialog.set_text( Global.error_dialog.set_text(
tr("File failed to save. Converting dictionary to JSON failed.") tr("File failed to save. Converting dictionary to JSON failed.")
@ -336,12 +336,19 @@ func save_pxo_file(
Global.dialog_open(true) Global.dialog_open(true)
return return
var file: File = File.new() # Check if a file with the same name exists. If it does, rename the new file temporarily.
var err # Needed in case of a crash, so that the old file won't be replaced with an empty one.
var temp_path := path
var dir := Directory.new()
if dir.file_exists(path):
temp_path = path + "1"
var file := File.new()
var err: int
if use_zstd_compression: if use_zstd_compression:
err = file.open_compressed(path, File.WRITE, File.COMPRESSION_ZSTD) err = file.open_compressed(temp_path, File.WRITE, File.COMPRESSION_ZSTD)
else: else:
err = file.open(path, File.WRITE) err = file.open(temp_path, File.WRITE)
if err != OK: if err != OK:
Global.error_dialog.set_text(tr("File failed to save. Error code %s") % err) Global.error_dialog.set_text(tr("File failed to save. Error code %s") % err)
@ -357,24 +364,26 @@ func save_pxo_file(
for frame in project.frames: for frame in project.frames:
for cel in frame.cels: for cel in frame.cels:
cel.save_image_data_to_pxo(file) cel.save_image_data_to_pxo(file)
for brush in project.brushes: for brush in project.brushes:
file.store_buffer(brush.get_data()) file.store_buffer(brush.get_data())
if project.tiles.has_mask: if project.tiles.has_mask:
file.store_buffer(project.tiles.tile_mask.get_data()) file.store_buffer(project.tiles.tile_mask.get_data())
file.close() file.close()
if temp_path != path:
# Rename the new file to its proper name and remove the old file, if it exists.
dir.rename(temp_path, path)
if OS.get_name() == "HTML5" and OS.has_feature("JavaScript") and !autosave: if OS.get_name() == "HTML5" and OS.has_feature("JavaScript") and !autosave:
err = file.open(path, File.READ) err = file.open(path, File.READ)
if !err: if err == OK:
var file_data = Array(file.get_buffer(file.get_len())) var file_data := Array(file.get_buffer(file.get_len()))
JavaScript.download_buffer(file_data, path.get_file()) JavaScript.download_buffer(file_data, path.get_file())
file.close() file.close()
# Remove the .pxo file from memory, as we don't need it anymore # Remove the .pxo file from memory, as we don't need it anymore
var dir = Directory.new() var browser_dir := Directory.new()
dir.remove(path) browser_dir.remove(path)
if autosave: if autosave:
Global.notification_label("File autosaved") Global.notification_label("File autosaved")
@ -398,7 +407,7 @@ func save_pxo_file(
func open_image_as_new_tab(path: String, image: Image) -> void: func open_image_as_new_tab(path: String, image: Image) -> void:
var project = Project.new([], path.get_file(), image.get_size()) var project := Project.new([], path.get_file(), image.get_size())
project.layers.append(PixelLayer.new(project)) project.layers.append(PixelLayer.new(project))
Global.projects.append(project) Global.projects.append(project)
@ -411,7 +420,7 @@ func open_image_as_new_tab(path: String, image: Image) -> void:
func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert: int) -> void: func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert: int) -> void:
var project = Project.new([], path.get_file()) var project := Project.new([], path.get_file())
project.layers.append(PixelLayer.new(project)) project.layers.append(PixelLayer.new(project))
Global.projects.append(project) Global.projects.append(project)
horiz = min(horiz, image.get_size().x) horiz = min(horiz, image.get_size().x)
@ -509,7 +518,7 @@ func open_image_as_spritesheet_layer(
func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void: func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void:
var project = Global.current_project var project: Project = Global.current_project
project.undos += 1 project.undos += 1
project.undo_redo.create_action("Replaced Cel") project.undo_redo.create_action("Replaced Cel")
@ -534,7 +543,7 @@ func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void
func open_image_as_new_frame(image: Image, layer_index := 0) -> void: func open_image_as_new_frame(image: Image, layer_index := 0) -> void:
var project = Global.current_project var project: Project = Global.current_project
image.crop(project.size.x, project.size.y) image.crop(project.size.x, project.size.y)
var frame := Frame.new() var frame := Frame.new()
@ -560,7 +569,7 @@ func open_image_as_new_frame(image: Image, layer_index := 0) -> void:
func open_image_as_new_layer(image: Image, file_name: String, frame_index := 0) -> void: func open_image_as_new_layer(image: Image, file_name: String, frame_index := 0) -> void:
var project = Global.current_project var project: Project = Global.current_project
image.crop(project.size.x, project.size.y) image.crop(project.size.x, project.size.y)
var layer := PixelLayer.new(project, file_name) var layer := PixelLayer.new(project, file_name)
var cels := [] var cels := []
@ -721,5 +730,5 @@ func save_project_to_recent_list(path: String) -> void:
Global.config_cache.set_value("data", "recent_projects", top_menu_container.recent_projects) Global.config_cache.set_value("data", "recent_projects", top_menu_container.recent_projects)
Global.top_menu_container.recent_projects_submenu.clear() top_menu_container.recent_projects_submenu.clear()
top_menu_container.update_recent_projects_submenu() top_menu_container.update_recent_projects_submenu()