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