1 // Copyright (c) 2015 Sergio Gonzalez. All rights reserved.
2 // License: https://github.com/serge-rgb/milton#license
3 
4 #include "persist.h"
5 
6 #include <stb_image_write.h>
7 
8 #include "common.h"
9 #include "gui.h"
10 #include "memory.h"
11 #include "milton.h"
12 #include "platform.h"
13 #include "tiny_jpeg.h"
14 
15 
16 #define MILTON_MAGIC_NUMBER 0X11DECAF3
17 
18 static u64 g_bytes_written = 0;
19 
save_debug_log(char * message,...)20 void save_debug_log(char* message, ...)
21 {
22     va_list args;
23     va_start(args, message);
24     milton_log_args(message, args);
25     va_end(args);
26 }
27 
28 #pragma pack(push, 1)
29 struct PersistStrokePoint
30 {
31     v2l point;
32     f32 pressure;
33 };
34 #pragma pack(pop)
35 
36 static b32
fread_checked(void * dst,size_t sz,size_t count,FILE * fd)37 fread_checked(void* dst, size_t sz, size_t count, FILE* fd)
38 {
39     b32 ok = false;
40 
41     size_t read = fread(dst, sz, count, fd);
42     if ( read == count && !ferror(fd)) {
43         ok = true;
44     }
45 
46     return ok;
47 }
48 
49 void
milton_unset_last_canvas_fname()50 milton_unset_last_canvas_fname()
51 {
52     b32 del = platform_delete_file_at_config(TO_PATH_STR("saved_path"), DeleteErrorTolerance_OK_NOT_EXIST);
53     if ( del == false ) {
54         platform_dialog("The default canvas could not be set to open the next time you run Milton. Please contact the developers.", "Important");
55     }
56 }
57 
58 static b32
read_brushes(Brush * brushes,i32 num_brushes,FILE * fd)59 read_brushes(Brush* brushes, i32 num_brushes, FILE* fd)
60 {
61     i32 size = 0;
62     b32 ok = fread_checked(&size, sizeof(size), 1, fd);
63 
64     if (size == 0 || size > sizeof(Brush)) {
65         ok = false;
66     }
67 
68     for (i32 i = 0; i < num_brushes; ++i) {
69         brushes[i] = default_brush();
70         if (ok) { ok = fread_checked(brushes + i, size, 1, fd); }
71     }
72 
73     return ok;
74 }
75 
76 void
milton_load(Milton * milton)77 milton_load(Milton* milton)
78 {
79     // Declare variables here to silence compiler warnings about using GOTO.
80     i32 history_count = 0;
81     i32 num_layers = 0;
82     i32 saved_working_layer_id = 0;
83     int err = 0;
84 
85     i32 layer_guid = 0;
86     ColorButton* btn = NULL;
87     MiltonGui* gui = NULL;
88     auto saved_size = milton->view->screen_size;
89 
90     milton_log("Loading file %s\n", milton->persist->mlt_file_path);
91     // Reset the canvas.
92     milton_reset_canvas(milton);
93 
94     CanvasState* canvas = milton->canvas;
95 #define READ(address, size, num, fd) do { ok = fread_checked(address,size,num,fd); if (!ok){ goto END; } } while(0)
96 
97     // Unload gpu data if the strokes have been cooked.
98     gpu_free_strokes(milton->renderer, milton->canvas);
99     mlt_assert(milton->persist->mlt_file_path);
100     FILE* fd = platform_fopen(milton->persist->mlt_file_path, TO_PATH_STR("rb"));
101     b32 ok = true;  // fread check
102     b32 handled = false;  // when ok==false but we don't need to prompt a scary message.
103 
104     if ( fd ) {
105         u32 milton_binary_version = (u32)-1;
106         u32 milton_magic = (u32)-1;
107         READ(&milton_magic, sizeof(u32), 1, fd);
108         READ(&milton_binary_version, sizeof(u32), 1, fd);
109 
110         if (ok) {
111             if ( milton_binary_version < MILTON_MINOR_VERSION ) {
112                 if ( platform_dialog_yesno ("This file will be updated to the new version of Milton. Older versions won't be able to open it. Is this OK?", "File format change") ) {
113                     milton->persist->mlt_binary_version = MILTON_MINOR_VERSION;
114                     milton_log("Updating this file to latest mlt version.\n");
115                 } else {
116                     ok = false;
117                     handled = true;
118                     goto END;
119                 }
120             } else {
121                 milton->persist->mlt_binary_version = milton_binary_version;
122             }
123         }
124 
125         if ( milton_binary_version > MILTON_MINOR_VERSION ) {
126             platform_dialog("This file was created with a newer version of Milton.", "Could not open.");
127 
128             // Stop loading, but exit without prompting.
129             ok = false;
130             handled = true;
131             goto END;
132         }
133 
134         if ( milton_binary_version >= 9 ) {
135             // Defaults
136             *milton->view = {};
137 
138             READ(&milton->view->size, sizeof(u32), 1, fd); // Read size.
139             if (milton->view->size > sizeof(CanvasView)) {
140                 ok = false;
141                 handled = true;
142                 goto END;
143             }
144             READ((u8*)milton->view + offsetof(CanvasView, screen_size),
145                 milton->view->size - sizeof(u32),
146                 1,
147                 fd);  // Rest of the struct.
148 
149             milton->view->size = sizeof(CanvasView);
150         }
151         else if ( milton_binary_version >= 4 && milton_binary_version < 9) {
152             init_view(milton->view, milton->settings->background_color, milton->view->screen_size.x, milton->view->screen_size.y); // defaults
153 
154             size_t bytes_offset = offsetof(CanvasView, screen_size);
155 
156             READ((u8*)milton->view + bytes_offset, sizeof(CanvasViewPreV9), 1, fd);
157 
158             // Patch angle, which was stomped by the old num_layers member, which we don't use anymore.
159             milton->view->angle = 0.0f;
160         } else {
161             CanvasViewPreV4 legacy_view = {};
162             READ(&legacy_view, sizeof(CanvasViewPreV4), 1, fd);
163             milton->view->screen_size = legacy_view.screen_size;
164             milton->view->scale = legacy_view.scale;
165             milton->view->zoom_center = legacy_view.zoom_center;
166             milton->view->pan_center = VEC2L(legacy_view.pan_center * -1);
167             milton->view->background_color = legacy_view.background_color;
168             milton->view->working_layer_id = legacy_view.working_layer_id;
169             milton->view->angle = 0.0f;
170         }
171 
172         // The screen size might hurt us.
173         milton->view->screen_size = saved_size;
174 
175         // The process of loading changes state. working_layer_id changes when creating layers.
176         saved_working_layer_id = milton->view->working_layer_id;
177 
178         if ( milton_magic != MILTON_MAGIC_NUMBER ) {
179             platform_dialog("MLT file could not be loaded. Magic number mismatch.", "Problem");
180             milton_unset_last_canvas_fname();
181             ok = false;
182             goto END;
183         }
184 
185         num_layers = 0;
186         READ(&num_layers, sizeof(i32), 1, fd);
187         READ(&layer_guid, sizeof(i32), 1, fd);
188 
189         for ( int layer_i = 0; ok && layer_i < num_layers; ++layer_i ) {
190             i32 len = 0;
191             READ(&len, sizeof(i32), 1, fd);
192 
193             if ( len > MAX_LAYER_NAME_LEN ) {
194                 milton_log("Corrupt file. Layer name is too long.\n");
195                 ok = false;
196                 goto END;
197             }
198 
199             if (ok) { milton_new_layer(milton); }
200 
201             Layer* layer = milton->canvas->working_layer;
202 
203             READ(layer->name, sizeof(char), (size_t)len, fd);
204 
205             READ(&layer->id, sizeof(i32), 1, fd);
206             READ(&layer->flags, sizeof(layer->flags), 1, fd);
207 
208             if ( ok ) {
209                 i32 num_strokes = 0;
210                 READ(&num_strokes, sizeof(i32), 1, fd);
211 
212                 for ( i32 stroke_i = 0; ok && stroke_i < num_strokes; ++stroke_i ) {
213                     Stroke stroke = {};
214 
215                     stroke.id = milton->canvas->stroke_id_count++;
216 
217                     if ( milton_binary_version < 7 ) {
218                         READ(&stroke.brush, sizeof(BrushPreV7), 1, fd);
219 
220                         // Previous versions used a magic value for the eraser.
221                         v4f k_eraser_color = {23,34,45,56};
222 
223                         if (stroke.brush.color == k_eraser_color) {
224                             stroke.flags |= StrokeFlag_ERASER;
225                         }
226                         stroke.brush.hardness = 10.0f;
227                     }
228                     else if ( milton_binary_version < 8 ) {
229                         READ(&stroke.brush, sizeof(BrushPreV8), 1, fd);
230                         READ(&stroke.flags, sizeof(stroke.flags), 1, fd);
231                         stroke.brush.hardness = 2.0f;
232                     }
233                     else {
234                         if (!read_brushes(&stroke.brush, 1, fd)) {
235                             ok = false;
236                             goto END;
237                         }
238                         READ(&stroke.flags, sizeof(stroke.flags), 1, fd);
239                     }
240 
241                     READ(&stroke.num_points, sizeof(i32), 1, fd);
242 
243                     if ( stroke.num_points > STROKE_MAX_POINTS || stroke.num_points <= 0 ) {
244                         milton_log("ERROR: File has a stroke with %d points\n",
245                                    stroke.num_points);
246                         // Older versions have a possible off-by-one bug here.
247                         if (stroke.num_points == STROKE_MAX_POINTS)  {
248                             stroke.points = arena_alloc_array(&canvas->arena, stroke.num_points, v2l);
249                             READ(stroke.points, sizeof(v2l), (size_t)stroke.num_points, fd);
250                             stroke.pressures = arena_alloc_array(&canvas->arena, stroke.num_points, f32);
251                             READ(stroke.pressures, sizeof(f32), (size_t)stroke.num_points, fd);
252                             READ(&stroke.layer_id, sizeof(i32), 1, fd);
253 #if STROKE_DEBUG_VIZ
254                             stroke.debug_flags = arena_alloc_array(&canvas->arena, stroke.num_points, int);
255 #endif
256 
257                             stroke.bounding_rect = bounding_box_for_stroke(&stroke);
258 
259                             layer::layer_push_stroke(layer, stroke);
260                         } else {
261                             ok = false;
262                             goto END;
263                         }
264                     } else {
265                         if ( milton_binary_version >= 4 ) {
266                             stroke.points = arena_alloc_array(&canvas->arena, stroke.num_points, v2l);
267                             READ(stroke.points, sizeof(v2l), (size_t)stroke.num_points, fd);
268                         } else {
269                             stroke.points = arena_alloc_array(&canvas->arena, stroke.num_points, v2l);
270                             v2i* points_32bit = (v2i*)mlt_calloc((size_t)stroke.num_points, sizeof(v2i), "Persist");
271 
272                             READ(points_32bit, sizeof(v2i), (size_t)stroke.num_points, fd);
273                             for (int i = 0; i < stroke.num_points; ++i) {
274                                 stroke.points[i] = VEC2L(points_32bit[i]);
275                             }
276                         }
277 #if STROKE_DEBUG_VIZ
278                         stroke.debug_flags = arena_alloc_array(&canvas->arena, stroke.num_points, int);
279 #endif
280                         stroke.pressures = arena_alloc_array(&canvas->arena, stroke.num_points, f32);
281                         READ(stroke.pressures, sizeof(f32), (size_t)stroke.num_points, fd);
282                         READ(&stroke.layer_id, sizeof(i32), 1, fd);
283                         stroke.bounding_rect = bounding_box_for_stroke(&stroke);
284                         layer::layer_push_stroke(layer, stroke);
285                     }
286                 }
287 
288                 // Set the flags of the working layer to the last stroke of the working layer.
289                 if (layer->id == saved_working_layer_id) {
290                     i64 stroke_count = count(&layer->strokes);
291                     if (stroke_count > 0) {
292                         milton->working_stroke.flags = layer->strokes[ stroke_count - 1]->flags;
293                     }
294                 }
295             }
296 
297             if ( milton_binary_version >= 4 ) {
298                 i64 num_effects = 0;
299                 READ(&num_effects, sizeof(num_effects), 1, fd);
300                 if ( num_effects > 0 ) {
301                     LayerEffect** e = &layer->effects;
302                     for ( i64 i = 0; i < num_effects; ++i ) {
303                         mlt_assert(*e == NULL);
304                         *e = arena_alloc_elem(&canvas->arena, LayerEffect);
305                         READ(&(*e)->type, sizeof((*e)->type), 1, fd);
306                         READ(&(*e)->enabled, sizeof((*e)->enabled), 1, fd);
307                         switch ((*e)->type) {
308                             case LayerEffectType_BLUR: {
309                                 READ(&(*e)->blur.original_scale, sizeof((*e)->blur.original_scale), 1, fd);
310                                 READ(&(*e)->blur.kernel_size, sizeof((*e)->blur.kernel_size), 1, fd);
311                             } break;
312                         }
313                         e = &(*e)->next;
314                     }
315                 }
316             }
317         }
318         milton->view->working_layer_id = saved_working_layer_id;
319 
320         if ( milton_binary_version >= 5 ) {
321             v3f rgb;
322             READ(&rgb, sizeof(v3f), 1, fd);
323             gui_picker_from_rgb(&milton->gui->picker, rgb);
324         } else {
325             READ(&milton->gui->picker.data, sizeof(PickerData), 1, fd);
326         }
327 
328 
329         // Buttons
330         {
331             i32 button_count = 0;
332             gui = milton->gui;
333             btn = gui->picker.color_buttons;
334 
335             READ(&button_count, sizeof(i32), 1, fd);
336             for ( i32 i = 0;
337                   btn!=NULL && i < button_count;
338                   ++i, btn=btn->next ) {
339                 READ(&btn->rgba, sizeof(v4f), 1, fd);
340             }
341         }
342 
343         // Brush
344         if ( milton_binary_version >= 2 && milton_binary_version <= 5  ) {
345             // PEN, ERASER
346             for (int i = 0; i < 2; ++i) {
347                 READ(&milton->brushes[i], sizeof(BrushPreV7), 1, fd);
348             }
349             // Sizes
350             READ(&milton->brush_sizes, sizeof(i32), 2, fd);
351         }
352         else if ( milton_binary_version > 5 ) {
353             u16 num_brushes = 0;
354             READ(&num_brushes, sizeof(u16), 1, fd);
355             if ( num_brushes > BrushEnum_COUNT ) {
356                 milton_log("Error loading file: too many brushes: %d\n", num_brushes);
357             }
358             if ( milton_binary_version < 7 ) {
359                 for (int i = 0; i < num_brushes; ++i) {
360                     milton->brushes[i] = default_brush();
361                     READ(milton->brushes + i, sizeof(BrushPreV7), 1, fd);
362                 }
363             }
364             else if (milton_binary_version < 8) {
365                 for (int i = 0; i < num_brushes; ++i) {
366                     milton->brushes[i] = default_brush();
367                     READ(milton->brushes + i, sizeof(BrushPreV8), 1, fd);
368                 }
369             }
370             else {
371                 if (!read_brushes(milton->brushes, num_brushes, fd)) {
372                     ok = false;
373                     goto END;
374                 }
375 
376             }
377 
378             READ(&milton->brush_sizes, sizeof(i32), num_brushes, fd);
379         }
380 
381         history_count = 0;
382         READ(&history_count, sizeof(history_count), 1, fd);
383         reset(&milton->canvas->history);
384         reserve(&milton->canvas->history, history_count);
385         READ(milton->canvas->history.data, sizeof(*milton->canvas->history.data), (size_t)history_count, fd);
386         milton->canvas->history.count = history_count;
387 
388         // MLT 3
389         // Layer alpha
390         if ( milton_binary_version >= 3 ) {
391             Layer* l = milton->canvas->root_layer;
392             for ( i64 i = 0; ok && i < num_layers; ++i ) {
393                 mlt_assert(l != NULL);
394                 READ(&l->alpha, sizeof(l->alpha), 1, fd);
395                 l = l->next;
396             }
397         } else {
398             for ( Layer* l = milton->canvas->root_layer; l != NULL; l = l->next ) {
399                 l->alpha = 1.0f;
400             }
401         }
402 
403         err = fclose(fd);
404         if ( err != 0 ) {
405             ok = false;
406         }
407 
408 END:
409         // Finished loading
410         if ( !ok ) {
411             if ( !handled ) {
412                 platform_dialog("Tried to load a corrupt Milton file or there was an error reading from disk.", "Error");
413             }
414             milton_reset_canvas_and_set_default(milton);
415         } else {
416             i32 id = milton->view->working_layer_id;
417             {  // Use working_layer_id to make working_layer point to the correct thing
418                 Layer* layer = milton->canvas->root_layer;
419                 while ( layer ) {
420                     if ( layer->id == id ) {
421                         milton->canvas->working_layer = layer;
422                         break;
423                     }
424                     layer = layer->next;
425                 }
426             }
427             milton->canvas->layer_guid = layer_guid;
428 
429             // Update GPU
430             milton->flags |= MiltonStateFlags_JUST_SAVED;
431         }
432     } else {
433         milton_log("milton_load: Could not open file!\n");
434         milton_reset_canvas_and_set_default(milton);
435     }
436 #undef READ
437 }
438 
439 static bool
write_data(void * address,size_t size,size_t count,FILE * fd)440 write_data(void* address, size_t size, size_t count, FILE* fd)
441 {
442     bool ok = true;
443     size_t written = fwrite(address, size, count, fd);
444     if ( written != count ) {
445         ok = false;
446     }
447     else {
448         g_bytes_written += written * size;
449     }
450     return ok;
451 }
452 
453 void
begin_data_tracking()454 begin_data_tracking()
455 {
456     g_bytes_written = 0;
457 }
458 
459 u64
end_data_tracking()460 end_data_tracking()
461 {
462     return g_bytes_written;
463 }
464 
465 u64
milton_save(Milton * milton)466 milton_save(Milton* milton)
467 {
468     begin_data_tracking();
469     // Declaring variables here to silence compiler warnings about GOTO jumping declarations.
470     i32 history_count = 0;
471     u32 milton_binary_version = 0;
472     milton->flags |= MiltonStateFlags_LAST_SAVE_FAILED;  // Assume failure. Remove flag on success.
473 
474     int pid = (int)getpid();
475     PATH_CHAR tmp_fname[MAX_PATH] = {};
476     PATH_SNPRINTF(tmp_fname, MAX_PATH, TO_PATH_STR("%s.mlt_tmp_%d"), milton->persist->mlt_file_path, pid);
477 
478     FILE* fd = platform_fopen(tmp_fname, TO_PATH_STR("wb"));
479 
480     b32 could_write_milton_state = false;
481 
482     if ( fd ) {
483         u32 milton_magic = MILTON_MAGIC_NUMBER;
484 
485         if ( write_data(&milton_magic, sizeof(u32), 1, fd) ) {
486             milton_binary_version = milton->persist->mlt_binary_version;
487             i32 num_layers = layer::number_of_layers(milton->canvas->root_layer);
488 
489             mlt_assert(sizeof(CanvasView) == milton->view->size);
490 
491             if ( write_data(&milton_binary_version, sizeof(u32), 1, fd) &&
492                  write_data(milton->view, sizeof(CanvasView), 1, fd) &&
493                  write_data(&num_layers, sizeof(i32), 1, fd) &&
494                  write_data(&milton->canvas->layer_guid, sizeof(i32), 1, fd) ) {
495 
496                 //
497                 // Layer contents
498                 //
499 
500                 bool could_write_layer_contents = true;
501 
502                 for ( Layer* layer = milton->canvas->root_layer;
503                       could_write_layer_contents && layer;
504                       layer=layer->next  ) {
505                     if ( layer->strokes.count > INT_MAX ) {
506                         milton_die_gracefully("FATAL. Number of strokes in layer greater than can be stored in file format. ");
507                     }
508                     i32 num_strokes = (i32)layer->strokes.count;
509                     char* name = layer->name;
510                     i32 len = (i32)(strlen(name) + 1);
511 
512                     bool could_write_strokes = true;
513                     bool could_write_effects = true;
514 
515                     if ( write_data(&len, sizeof(i32), 1, fd) &&
516                          write_data(name, sizeof(char), (size_t)len, fd) &&
517                          write_data(&layer->id, sizeof(i32), 1, fd) &&
518                          write_data(&layer->flags, sizeof(layer->flags), 1, fd) &&
519                          write_data(&num_strokes, sizeof(i32), 1, fd) ) {
520                         for ( i32 stroke_i = 0;
521                               could_write_strokes && stroke_i < num_strokes;
522                               ++stroke_i ) {
523                             Stroke* stroke = get(&layer->strokes, stroke_i);
524                             mlt_assert(stroke->num_points > 0);
525                             if ( stroke->num_points > 0 && stroke->num_points <= STROKE_MAX_POINTS ) {
526                                 i32 size_of_brush = sizeof(Brush);
527                                 if ( !write_data(&size_of_brush, sizeof(i32), 1, fd ) ||
528                                      !write_data(&stroke->brush, sizeof(Brush), 1, fd) ||
529                                      !write_data(&stroke->flags, sizeof(stroke->flags), 1, fd) ||
530                                      !write_data(&stroke->num_points, sizeof(i32), 1, fd) ||
531                                      !write_data(stroke->points, sizeof(v2l), (size_t)stroke->num_points, fd) ||
532                                      !write_data(stroke->pressures, sizeof(f32), (size_t)stroke->num_points, fd) ||
533                                      !write_data(&stroke->layer_id, sizeof(i32), 1, fd) ) {
534                                     could_write_strokes = false;
535                                     break;
536                                 }
537                             } else {
538                                 milton_log("WARNING: Trying to write a stroke of size %d\n", stroke->num_points);
539                             }
540                         }
541                     } else {
542                         could_write_strokes = false;
543                     }
544 
545                     if ( !could_write_strokes ) {
546                         could_write_effects = false;
547                     }
548                     else {
549                         i64 num_effects = 0;
550                         for ( LayerEffect* e = layer->effects; e != NULL; e = e->next ) {
551                             ++num_effects;
552                         }
553                         if ( write_data(&num_effects, sizeof(num_effects), 1, fd) ) {
554                             for ( LayerEffect* e = layer->effects; e != NULL; e = e->next ) {
555                                 if ( write_data(&e->type, sizeof(e->type), 1, fd) &&
556                                      write_data(&e->enabled, sizeof(e->enabled), 1, fd) ) {
557                                     switch (e->type) {
558                                         case LayerEffectType_BLUR: {
559                                             if ( !write_data(&e->blur.original_scale, sizeof(e->blur.original_scale), 1, fd) ||
560                                                  !write_data(&e->blur.kernel_size, sizeof(e->blur.kernel_size), 1, fd) ) {
561                                                 could_write_effects = false;
562                                             }
563                                         } break;
564                                     }
565                                 }
566                                 else {
567                                     could_write_effects = false;
568                                 }
569                             }
570                         }
571                     }
572                     if (!could_write_strokes || !could_write_effects) {
573                         could_write_layer_contents = false;
574                     }
575                 }
576 
577                 if ( could_write_layer_contents ) {
578                     b32 could_write_picker = true;
579                     if ( milton_binary_version >= 5 ) {
580                         v3f rgb = gui_get_picker_rgb(milton->gui);
581                         could_write_picker = write_data(&rgb, sizeof(rgb), 1, fd);
582                     }
583                     else {
584                         could_write_picker = write_data(&milton->gui->picker.data, sizeof(PickerData), 1, fd);
585                     }
586 
587                     //
588                     // Buttons
589                     //
590                     b32 could_write_buttons = true;
591 
592                     if ( could_write_picker ) {
593                         i32 button_count = 0;
594                         MiltonGui* gui = milton->gui;
595                         // Count buttons
596                         for (ColorButton* b = gui->picker.color_buttons; b!= NULL; b = b->next, button_count++) { }
597                         // Write
598                         could_write_buttons = write_data(&button_count, sizeof(i32), 1, fd);
599                         if ( could_write_buttons ) {
600                             for ( ColorButton* b = gui->picker.color_buttons;
601                                   could_write_buttons && b!= NULL;
602                                   b = b->next ) {
603                                 could_write_buttons = write_data(&b->rgba, sizeof(v4f), 1, fd);
604                             }
605                         }
606                     }
607                     else {
608                         could_write_buttons = false;
609                     }
610 
611                     if ( could_write_buttons ) {
612 
613                         //
614                         // Brush
615                         //
616                         b32 could_write_brushes = true;
617 
618                         i32 size_of_brush = sizeof(Brush);
619 
620                         u16 num_brushes = 3;  // Brush, eraser, primitive.
621                         if ( !write_data(&num_brushes, sizeof(num_brushes), 1, fd) ||
622                              !write_data(&size_of_brush, sizeof(i32), 1, fd) ||
623                              !write_data(&milton->brushes, sizeof(Brush), num_brushes, fd) ||
624                              !write_data(&milton->brush_sizes, sizeof(i32), num_brushes, fd) ) {
625                             could_write_brushes = false;
626                         }
627 
628                         if ( could_write_brushes ) {
629                             history_count = (i32)milton->canvas->history.count;
630                             if ( milton->canvas->history.count > INT_MAX ) {
631                                 history_count = 0;
632                             }
633 
634                             //
635                             // Undo history
636                             //
637 
638                             if ( write_data(&history_count, sizeof(history_count), 1, fd) &&
639                                  write_data(milton->canvas->history.data, sizeof(*milton->canvas->history.data), (size_t)history_count, fd) ) {
640 
641                                 //
642                                 // Layer alpha
643                                 //
644                                 b32 could_write_layer_alpha = true;
645 
646                                 if ( milton_binary_version >= 3 ) {
647                                     Layer* l = milton->canvas->root_layer;
648                                     for ( i64 i = 0;
649                                           could_write_layer_alpha && i < num_layers;
650                                           ++i ) {
651                                         mlt_assert(l);
652                                         if ( !write_data(&l->alpha, sizeof(l->alpha), 1, fd) ) {
653                                             could_write_layer_alpha = false;
654                                         }
655                                         l = l->next;
656                                     }
657                                 }
658 
659                                 //
660                                 // Done.
661                                 //
662                                 if ( could_write_layer_alpha ) {
663                                     could_write_milton_state = true;
664                                 }
665                             }
666                         }
667                     }
668                 }
669             }
670         }
671 
672         int file_error = ferror(fd);
673         if ( file_error == 0 ) {
674             int close_ret = fclose(fd);
675             if ( close_ret == 0 ) {
676                 if ( !could_write_milton_state ) {
677                     platform_dialog("Milton failed to write to the file!", "Save error.");
678                 }
679                 else {
680                     if ( platform_move_file(tmp_fname, milton->persist->mlt_file_path) ) {
681                         //  \o/
682                         milton_save_postlude(milton);
683                     }
684                     else {
685                         milton_log("Could not move file. Moving on. Avoiding this save.\n");
686                         milton->flags |= MiltonStateFlags_MOVE_FILE_FAILED;
687                     }
688                 }
689             }
690             else {
691                 milton_log("File error when closing handle. Error code %d. \n", close_ret);
692             }
693         }
694         else {
695             milton_log("File IO error. Error code %d. \n", file_error);
696         }
697 
698 
699     }
700     else {
701         milton_die_gracefully("Could not create file for saving! ");
702     }
703     u64 bytes_written = end_data_tracking();
704     return bytes_written;
705 }
706 
707 PATH_CHAR*
milton_get_last_canvas_fname()708 milton_get_last_canvas_fname()
709 {
710     PATH_CHAR* last_fname = (PATH_CHAR*)mlt_calloc(MAX_PATH, sizeof(PATH_CHAR), "Strings");
711 
712     PATH_CHAR full[MAX_PATH] = {};
713 
714     PATH_STRCPY(full, TO_PATH_STR("saved_path"));
715     platform_fname_at_config(full, MAX_PATH);
716     FILE* fd = platform_fopen(full, TO_PATH_STR("rb+"));
717 
718     if ( fd ) {
719         u64 len = 0;
720         fread(&len, sizeof(len), 1, fd);
721         if ( len < MAX_PATH ) {
722             fread(last_fname, sizeof(PATH_CHAR), len, fd);
723             // If the read fails, or if the file doesn't exist, milton_load
724             // will fail gracefully and load a default canvas.
725             fclose(fd);
726         }
727     } else {
728         mlt_free(last_fname, "Strings");
729     }
730 
731     return last_fname;
732 
733 }
734 
735 void
milton_set_last_canvas_fname(PATH_CHAR * last_fname)736 milton_set_last_canvas_fname(PATH_CHAR* last_fname)
737 {
738     //PATH_CHAR* full = (PATH_CHAR*)mlt_calloc(MAX_PATH, sizeof(char));
739     //wcscpy(full, "last_canvas_fname");
740     PATH_CHAR full[MAX_PATH] = { TO_PATH_STR("saved_path") };
741     platform_fname_at_config(full, MAX_PATH);
742     FILE* fd = platform_fopen(full, TO_PATH_STR("wb"));
743     if ( fd ) {
744         u64 len = PATH_STRLEN(last_fname)+1;
745         fwrite(&len, sizeof(len), 1, fd);
746         fwrite(last_fname, sizeof(*last_fname), len, fd);
747         fclose(fd);
748     }
749 }
750 
751 // Called by stb_image
752 static void
write_func(void * context,void * data,int size)753 write_func(void* context, void* data, int size)
754 {
755     FILE* fd = *(FILE**)context;
756 
757     if ( fd ) {
758         size_t written = fwrite(data, (size_t)size, 1, fd);
759         if ( written != 1 ) {
760             fclose(fd);
761             *(FILE**)context = NULL;
762         }
763     }
764 }
765 
766 void
milton_save_buffer_to_file(PATH_CHAR * fname,u8 * buffer,i32 w,i32 h)767 milton_save_buffer_to_file(PATH_CHAR* fname, u8* buffer, i32 w, i32 h)
768 {
769     int len = 0;
770     {
771         size_t sz = PATH_STRLEN(fname);
772         if ( sz > ((1u << 31) -1) ) {
773             milton_die_gracefully("A really, really long file name. This shouldn't happen.");
774         }
775         len = (int)sz;
776     }
777     size_t ext_sz = ( len+1 ) * sizeof(PATH_CHAR);
778     PATH_CHAR* fname_copy = (PATH_CHAR*)mlt_calloc(ext_sz, 1, "Strings");
779     fname_copy[0] = '\0';
780     PATH_STRCPY(fname_copy, fname);
781 
782     // NOTE: This should work with unicode.
783     int ext_len = 0;
784     PATH_CHAR* ext = fname_copy + len;
785     b32 found = false;
786     {
787         int safety = len;
788         while ( *--ext != '.' ) {
789             if( safety-- == 0 ) {
790                 break;
791             }
792         }
793         if ( safety > 0 ) {
794             found = true;
795             ext_len = len - safety;
796             ++ext;
797         }
798     }
799 
800     if ( found ) {
801         for ( int i = 0; i < ext_len; ++i ) {
802             PATH_CHAR c = ext[i];
803             ext[i] = PATH_TOLOWER(c);
804         }
805 
806         FILE* fd = NULL;
807 
808         fd = platform_fopen(fname, TO_PATH_STR("wb"));
809 
810         if ( fd ) {
811             if ( !PATH_STRCMP(ext, TO_PATH_STR("png")) ) {
812                 stbi_write_png_to_func(write_func, &fd, w, h, 4, buffer, 0);
813             }
814             else if ( !PATH_STRCMP(ext, TO_PATH_STR("jpg")) || !PATH_STRCMP(ext, TO_PATH_STR("jpeg")) ) {
815                 tje_encode_with_func(write_func, &fd, 3, w, h, 4, buffer);
816             }
817             else {
818                 platform_dialog("File extension not handled by Milton\n", "Info");
819             }
820 
821             // !! fd might have been set to NULL if write_func failed.
822             if ( fd ) {
823                 if ( ferror(fd) ) {
824                     platform_dialog("Unknown error when writing to file :(", "Unknown error");
825                 }
826                 else {
827                     platform_dialog("Image exported successfully!", "Success");
828                 }
829                 fclose(fd);
830             }
831             else {
832                 platform_dialog("File created, but there was an error writing to it.", "Error");
833             }
834         }
835         else {
836             platform_dialog ( "Could not open file", "Error" );
837         }
838     }
839     else {
840         platform_dialog("File name missing extension!\n", "Error");
841     }
842     mlt_free(fname_copy, "Strings");
843 }
844 
845 b32
platform_settings_load(PlatformSettings * prefs)846 platform_settings_load(PlatformSettings* prefs)
847 {
848     b32 loaded = false;
849     PATH_CHAR fname[MAX_PATH] = TO_PATH_STR("platform_settings.bin");
850     platform_fname_at_config(fname, MAX_PATH);
851 
852     milton_log("Prefs file: %s\n", fname);
853 
854     *prefs = {};
855 
856     if ( FILE* fd = platform_fopen(fname, TO_PATH_STR("rb")) ) {
857         if ( !ferror(fd) ) {
858             u16 prefs_size = 0;
859             fread(&prefs_size, sizeof(u16), 1, fd);
860 
861             if (prefs_size <= sizeof(*prefs)) {
862                 loaded = fread(prefs, prefs_size, 1, fd);
863             }
864         }
865         else {
866             milton_log("Error writing to prefs file...\n");
867         }
868         fclose(fd);
869     }
870     else {
871         milton_log("Could not open file for writing prefs\n");
872     }
873 
874     return loaded;
875 }
876 
877 void
platform_settings_save(PlatformSettings * prefs)878 platform_settings_save(PlatformSettings* prefs)
879 {
880     PATH_CHAR fname[MAX_PATH] = TO_PATH_STR("platform_settings.bin");
881     platform_fname_at_config(fname, MAX_PATH);
882     FILE* fd = platform_fopen(fname, TO_PATH_STR("wb"));
883     if ( fd && !ferror(fd) ) {
884         u16 prefs_size = sizeof(PlatformSettings);
885         fwrite(&prefs_size, sizeof(u16), 1, fd);
886         fwrite(prefs, sizeof(*prefs), 1, fd);
887         fclose(fd);
888     }
889     else {
890         milton_log("Could not open file for writing prefs :(\n");
891     }
892 }
893 
894 b32
milton_settings_load(MiltonSettings * settings)895 milton_settings_load(MiltonSettings* settings)
896 {
897     PATH_CHAR settings_fname[MAX_PATH] = TO_PATH_STR("user_settings.bin"); {
898         platform_fname_at_config(settings_fname, MAX_PATH);
899     }
900 
901     b32 ok = false;
902     auto* fd = platform_fopen(settings_fname, TO_PATH_STR("rb"));
903     if ( fd ) {
904         u16 struct_size = 0;
905         if ( fread(&struct_size, sizeof(u16), 1, fd) ) {
906             if (struct_size <= sizeof(*settings)) {
907                 if ( fread(settings, sizeof(*settings), 1, fd) ) {
908                     ok = true;
909                 }
910             }
911         }
912     }
913     if ( !ok ) {
914         milton_log("Warning: Failed to read settings file\n");
915     }
916 
917     return ok;
918 }
919 
milton_settings_save(MiltonSettings * settings)920 void milton_settings_save(MiltonSettings* settings)
921 {
922     PATH_CHAR settings_fname[MAX_PATH] = TO_PATH_STR("user_settings.bin"); {
923         platform_fname_at_config(settings_fname, MAX_PATH);
924     }
925 
926     b32 ok = false;
927     auto* fd = platform_fopen(settings_fname, TO_PATH_STR("wb"));
928     if ( fd ) {
929         mlt_assert( sizeof(*settings) < 1<<16 );
930         u16 sz = sizeof(*settings);
931         if ( fwrite(&sz, sizeof(sz), 1, fd) ) {
932             if ( fwrite(settings, sizeof(*settings), 1, fd) ) {
933                 ok = true;
934             }
935         }
936     }
937     if ( !ok ) {
938         milton_log("Warning: could not correctly save settings file\n");
939     }
940 }
941