1 /*
2 Copyright (c) 2009-2010 Tero Lindeman (kometbomb)
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use,
8 copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following
11 conditions:
12
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "diskop.h"
27 #include "action.h"
28 #include "gui/toolutil.h"
29 #include "mused.h"
30 #include "macros.h"
31 #include "gui/msgbox.h"
32 #include <stdbool.h>
33 #include "wave.h"
34 #include "snd/freqs.h"
35 #include "snd/pack.h"
36 #include "view/wavetableview.h"
37 #include <string.h>
38 #include "memwriter.h"
39 #include <time.h>
40 #include <unistd.h>
41 #include "wavewriter.h"
42 #include "gui/menu.h"
43 #include "action.h"
44 #include <libgen.h>
45
46 extern Mused mused;
47 extern GfxDomain *domain;
48
49 #define MAX_RECENT 10
50
51 extern const Menu filemenu[];
52 Menu recentmenu[MAX_RECENT + 1];
53
54
update_recent_files_list(const char * path)55 void update_recent_files_list(const char *path)
56 {
57 debug("Adding %s to recent files list", path);
58
59 // Check if path already exists and move all items forward
60 // until the found item - or if not found, move all items
61 // forward until the last item
62
63 int found;
64
65 for (found = 0 ; found < MAX_RECENT - 1 ; ++found)
66 {
67 if (recentmenu[found].p2 != NULL ||
68 (recentmenu[found].p1 != NULL && strcmp((char*)recentmenu[found].p1, path) == 0))
69 {
70 break;
71 }
72 }
73
74 if (recentmenu[found].text != NULL)
75 {
76 free((void*)recentmenu[found].text);
77 }
78
79 if (recentmenu[found].p1 != NULL)
80 {
81 free((void*)recentmenu[found].p1);
82 }
83
84 if (found > 0)
85 {
86 memmove(recentmenu + 1, recentmenu, sizeof(recentmenu[0]) * found);
87 }
88
89 // Add new item on top
90
91 char *str = strdup(path);
92
93 Menu *menu = &recentmenu[0];
94 menu->parent = filemenu;
95 menu->text = strdup(basename(str));
96 menu->p1 = strdup(path);
97 menu->p2 = NULL;
98 menu->action = open_recent_file;
99
100 free(str);
101 }
102
103
init_recent_files_list()104 void init_recent_files_list()
105 {
106 memset(recentmenu, 0, sizeof(recentmenu));
107 // Set a dummy item in case the list is empty to
108 // circumvent a bug in the menu routine
109 recentmenu[0].parent = filemenu;
110 recentmenu[0].text = strdup("No files");
111 recentmenu[0].p2 = (void*)1; // Magic value to catch this item
112
113 int list_count = 0;
114
115 debug("Loading recent files list");
116 char *e = expand_tilde("~/.klystrackrecent");
117
118 if (e)
119 {
120 FILE *f = fopen(e, "r");
121
122 if (f)
123 {
124 for (int i = 0 ; i < MAX_RECENT ; ++i)
125 {
126 char path[500], cleaned[500];
127
128 if (!fgets(path, sizeof(path), f))
129 break;
130
131 sscanf(path, "%500[^\r\n]", cleaned);
132
133 Menu *menu = &recentmenu[i];
134 menu->parent = filemenu;
135 menu->action = open_recent_file;
136
137 // Order is important: basename might modify the input
138 menu->p1 = strdup(cleaned);
139 menu->text = strdup(basename(cleaned));
140 menu->p2 = NULL;
141
142 list_count++;
143 }
144
145 fclose(f);
146 }
147
148 free(e);
149 }
150 }
151
152
deinit_recent_files_list()153 void deinit_recent_files_list()
154 {
155 debug("Saving recent files list");
156 char *e = expand_tilde("~/.klystrackrecent");
157
158 if (e)
159 {
160 FILE *f = fopen(e, "w");
161
162 if (f)
163 {
164 for (int i = 0 ; i < MAX_RECENT ; ++i)
165 {
166 if (recentmenu[i].p1 != NULL)
167 fprintf(f,"%s\n", (char*)recentmenu[i].p1);
168 }
169
170 fclose(f);
171 }
172 else
173 {
174 warning("Could not save recent files list");
175 }
176
177 free(e);
178 }
179
180 for (int i = 0 ; i < MAX_RECENT ; ++i)
181 if (recentmenu[i].text != NULL)
182 {
183 free((void*)recentmenu[i].text);
184 free((void*)recentmenu[i].p1);
185 }
186 }
187
188
create_backup(char * filename)189 int create_backup(char *filename)
190 {
191 char new_filename[5000] = {0};
192 time_t now_time;
193 time(&now_time);
194 struct tm *now_tm = localtime(&now_time);
195
196 if (!now_tm)
197 return 0;
198
199 snprintf(new_filename, sizeof(new_filename) - 1, "%s.%04d%02d%02d-%02d%02d%02d.backup", filename,
200 now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec);
201
202 return rename(filename, new_filename) == 0;
203 }
204
205
write_wavetable_entry(SDL_RWops * f,const CydWavetableEntry * write_wave,bool write_wave_data)206 static void write_wavetable_entry(SDL_RWops *f, const CydWavetableEntry *write_wave, bool write_wave_data)
207 {
208 Uint32 flags = write_wave->flags & ~(CYD_WAVE_COMPRESSED_DELTA|CYD_WAVE_COMPRESSED_GRAY); // need to unmask bits because they're set by bitpack
209 Uint32 sample_rate = write_wave->sample_rate;
210 Uint32 samples = write_wave->samples, loop_begin = write_wave->loop_begin, loop_end = write_wave->loop_end;
211 Uint16 base_note = write_wave->base_note;
212
213 if (!write_wave_data)
214 {
215 // if the wave is not used and the data is not written, set these to zero too
216 loop_begin = 0;
217 loop_end = 0;
218 samples = 0;
219 }
220
221 Uint8 *packed = NULL;
222 Uint32 packed_size = 0;
223
224 if (write_wave->samples > 0 && write_wave_data)
225 {
226 int pack_flags;
227 packed = bitpack_best(write_wave->data, write_wave->samples, &packed_size, &pack_flags);
228
229 flags |= (Uint32)pack_flags << 3;
230 }
231
232 FIX_ENDIAN(flags);
233 FIX_ENDIAN(sample_rate);
234 FIX_ENDIAN(samples);
235 FIX_ENDIAN(loop_begin);
236 FIX_ENDIAN(loop_end);
237 FIX_ENDIAN(base_note);
238
239 SDL_RWwrite(f, &flags, sizeof(flags), 1);
240 SDL_RWwrite(f, &sample_rate, sizeof(sample_rate), 1);
241 SDL_RWwrite(f, &samples, sizeof(samples), 1);
242 SDL_RWwrite(f, &loop_begin, sizeof(loop_begin), 1);
243 SDL_RWwrite(f, &loop_end, sizeof(loop_end), 1);
244 SDL_RWwrite(f, &base_note, sizeof(base_note), 1);
245
246
247 if (packed)
248 {
249 FIX_ENDIAN(packed_size);
250 SDL_RWwrite(f, &packed_size, sizeof(packed_size), 1);
251
252 SDL_RWwrite(f, packed, sizeof(packed[0]), (packed_size + 7) / 8);
253
254 free(packed);
255 }
256 }
257
258
259 /* Write max 255 character string
260 */
261
write_string8(SDL_RWops * f,const char * string)262 static void write_string8(SDL_RWops *f, const char * string)
263 {
264 Uint8 len = strlen(string);
265 SDL_RWwrite(f, &len, sizeof(len), 1);
266 if (len)
267 SDL_RWwrite(f, string, sizeof(string[0]), len);
268 }
269
270
save_instrument_inner(SDL_RWops * f,MusInstrument * inst,const CydWavetableEntry * write_wave,const CydWavetableEntry * write_wave_fm)271 static void save_instrument_inner(SDL_RWops *f, MusInstrument *inst, const CydWavetableEntry *write_wave, const CydWavetableEntry *write_wave_fm)
272 {
273 Uint32 temp32 = inst->flags;
274 FIX_ENDIAN(temp32);
275 SDL_RWwrite(f, &temp32, sizeof(temp32), 1);
276 temp32 = inst->cydflags;
277 FIX_ENDIAN(temp32);
278 SDL_RWwrite(f, &temp32, sizeof(temp32), 1);
279 SDL_RWwrite(f, &inst->adsr, sizeof(inst->adsr), 1);
280 SDL_RWwrite(f, &inst->sync_source, sizeof(inst->sync_source), 1);
281 SDL_RWwrite(f, &inst->ring_mod, sizeof(inst->ring_mod), 1);
282 Uint16 temp16 = inst->pw;
283 FIX_ENDIAN(temp16);
284 SDL_RWwrite(f, &temp16, sizeof(temp16), 1);
285 SDL_RWwrite(f, &inst->volume, sizeof(inst->volume), 1);
286 Uint8 progsteps = 0;
287 for (int i = 0 ; i < MUS_PROG_LEN ; ++i)
288 if (inst->program[i] != MUS_FX_NOP) progsteps = i+1;
289 SDL_RWwrite(f, &progsteps, sizeof(progsteps), 1);
290 for (int i = 0 ; i < progsteps ; ++i)
291 {
292 temp16 = inst->program[i];
293 FIX_ENDIAN(temp16);
294 SDL_RWwrite(f, &temp16, sizeof(temp16), 1);
295 }
296
297 SDL_RWwrite(f, &inst->prog_period, sizeof(inst->prog_period), 1);
298 SDL_RWwrite(f, &inst->vibrato_speed, sizeof(inst->vibrato_speed), 1);
299 SDL_RWwrite(f, &inst->vibrato_depth, sizeof(inst->vibrato_depth), 1);
300 SDL_RWwrite(f, &inst->pwm_speed, sizeof(inst->pwm_speed), 1);
301 SDL_RWwrite(f, &inst->pwm_depth, sizeof(inst->pwm_depth), 1);
302 SDL_RWwrite(f, &inst->slide_speed, sizeof(inst->slide_speed), 1);
303 SDL_RWwrite(f, &inst->base_note, sizeof(inst->base_note), 1);
304 SDL_RWwrite(f, &inst->finetune, sizeof(inst->finetune), 1);
305
306 write_string8(f, inst->name);
307
308 temp16 = inst->cutoff;
309 FIX_ENDIAN(temp16);
310 SDL_RWwrite(f, &temp16, sizeof(temp16), 1);
311 SDL_RWwrite(f, &inst->resonance, sizeof(inst->resonance), 1);
312 SDL_RWwrite(f, &inst->flttype, sizeof(inst->flttype), 1);
313 SDL_RWwrite(f, &inst->ym_env_shape, sizeof(inst->ym_env_shape), 1);
314 temp16 = inst->buzz_offset;
315 FIX_ENDIAN(temp16);
316 SDL_RWwrite(f, &temp16, sizeof(temp16), 1);
317 SDL_RWwrite(f, &inst->fx_bus, sizeof(inst->fx_bus), 1);
318 SDL_RWwrite(f, &inst->vib_shape, sizeof(inst->vib_shape), 1);
319 SDL_RWwrite(f, &inst->vib_delay, sizeof(inst->vib_delay), 1);
320 SDL_RWwrite(f, &inst->pwm_shape, sizeof(inst->pwm_shape), 1);
321 SDL_RWwrite(f, &inst->lfsr_type, sizeof(inst->lfsr_type), 1);
322
323 if (write_wave)
324 {
325 Uint8 temp = 0xff;
326 SDL_RWwrite(f, &temp, sizeof(temp), 1);
327 }
328 else
329 {
330 SDL_RWwrite(f, &inst->wavetable_entry, sizeof(inst->wavetable_entry), 1);
331 }
332
333 SDL_RWwrite(f, &inst->fm_flags, sizeof(inst->fm_flags), 1);
334 SDL_RWwrite(f, &inst->fm_modulation, sizeof(inst->fm_modulation), 1);
335 SDL_RWwrite(f, &inst->fm_feedback, sizeof(inst->fm_feedback), 1);
336 SDL_RWwrite(f, &inst->fm_harmonic, sizeof(inst->fm_harmonic), 1);
337 SDL_RWwrite(f, &inst->fm_adsr, sizeof(inst->fm_adsr), 1);
338 SDL_RWwrite(f, &inst->fm_attack_start, sizeof(inst->fm_attack_start), 1);
339
340 if (write_wave_fm)
341 {
342 Uint8 temp = (inst->wavetable_entry == inst->fm_wave) ? 0xfe : 0xff;
343 SDL_RWwrite(f, &temp, sizeof(temp), 1);
344 }
345 else
346 {
347 SDL_RWwrite(f, &inst->fm_wave, sizeof(inst->fm_wave), 1);
348 }
349
350 if (write_wave)
351 {
352 write_wavetable_entry(f, write_wave, true);
353 }
354
355 if (write_wave_fm)
356 {
357 if (inst->wavetable_entry != inst->fm_wave)
358 write_wavetable_entry(f, write_wave_fm, true);
359 }
360 }
361
362
save_fx_inner(SDL_RWops * f,CydFxSerialized * fx)363 static void save_fx_inner(SDL_RWops *f, CydFxSerialized *fx)
364 {
365 CydFxSerialized temp;
366 memcpy(&temp, fx, sizeof(temp));
367
368 FIX_ENDIAN(temp.flags);
369 for (int i = 0 ; i < CYDRVB_TAPS ; ++i)
370 {
371 FIX_ENDIAN(temp.rvb.tap[i].gain);
372 FIX_ENDIAN(temp.rvb.tap[i].delay);
373 }
374
375 write_string8(f, temp.name);
376
377 SDL_RWwrite(f, &temp.flags, sizeof(temp.flags), 1);
378 SDL_RWwrite(f, &temp.crush.bit_drop, sizeof(temp.crush.bit_drop), 1);
379 SDL_RWwrite(f, &temp.chr.rate, sizeof(temp.chr.rate), 1);
380 SDL_RWwrite(f, &temp.chr.min_delay, sizeof(temp.chr.min_delay), 1);
381 SDL_RWwrite(f, &temp.chr.max_delay, sizeof(temp.chr.max_delay), 1);
382 SDL_RWwrite(f, &temp.chr.sep, sizeof(temp.chr.sep), 1);
383
384 for (int i = 0 ; i < CYDRVB_TAPS ; ++i)
385 {
386 SDL_RWwrite(f, &temp.rvb.tap[i].delay, sizeof(temp.rvb.tap[i].delay), 1);
387 SDL_RWwrite(f, &temp.rvb.tap[i].gain, sizeof(temp.rvb.tap[i].gain), 1);
388 SDL_RWwrite(f, &temp.rvb.tap[i].panning, sizeof(temp.rvb.tap[i].panning), 1);
389 SDL_RWwrite(f, &temp.rvb.tap[i].flags, sizeof(temp.rvb.tap[i].flags), 1);
390 }
391
392 SDL_RWwrite(f, &temp.crushex.downsample, sizeof(temp.crushex.downsample), 1);
393 SDL_RWwrite(f, &temp.crushex.gain, sizeof(temp.crushex.gain), 1);
394 }
395
396
397
write_packed_pattern(SDL_RWops * f,const MusPattern * pattern,bool skip)398 static void write_packed_pattern(SDL_RWops *f, const MusPattern *pattern, bool skip)
399 {
400 /*
401
402 Format:
403 00 1 * byte number of incoming steps
404 01... n_steps * nibble incoming data
405 ... n_steps * variable data described by bits in nibble
406
407 If ctrl bit 7 is set, there's also a volume column incoming
408 */
409
410 Uint16 steps = pattern->num_steps;
411
412 if (skip) steps = 0;
413
414 FIX_ENDIAN(steps);
415 SDL_RWwrite(f, &steps, 1, sizeof(steps));
416 SDL_RWwrite(f, &pattern->color, 1, sizeof(pattern->color));
417
418 Uint8 buffer = 0;
419 for (int i = 0 ; i < steps ; ++i)
420 {
421 if (pattern->step[i].note != MUS_NOTE_NONE)
422 buffer |= MUS_PAK_BIT_NOTE;
423
424 if (pattern->step[i].instrument != MUS_NOTE_NO_INSTRUMENT)
425 buffer |= MUS_PAK_BIT_INST;
426
427 if (pattern->step[i].ctrl != 0 || pattern->step[i].volume != MUS_NOTE_NO_VOLUME)
428 buffer |= MUS_PAK_BIT_CTRL;
429
430 if (pattern->step[i].command != 0)
431 buffer |= MUS_PAK_BIT_CMD;
432
433 if (i & 1 || i + 1 >= steps)
434 SDL_RWwrite(f, &buffer, 1, sizeof(buffer));
435
436 buffer <<= 4;
437 }
438
439 for (int i = 0 ; i < steps ; ++i)
440 {
441 if (pattern->step[i].note != MUS_NOTE_NONE)
442 SDL_RWwrite(f, &pattern->step[i].note, 1, sizeof(pattern->step[i].note));
443
444 if (pattern->step[i].instrument != MUS_NOTE_NO_INSTRUMENT)
445 SDL_RWwrite(f, &pattern->step[i].instrument, 1, sizeof(pattern->step[i].instrument));
446
447 if (pattern->step[i].ctrl != 0 || pattern->step[i].volume != MUS_NOTE_NO_VOLUME)
448 {
449 Uint8 ctrl = pattern->step[i].ctrl;
450 if (pattern->step[i].volume != MUS_NOTE_NO_VOLUME)
451 ctrl |= MUS_PAK_BIT_VOLUME;
452 SDL_RWwrite(f, &ctrl, 1, sizeof(pattern->step[i].ctrl));
453 }
454
455 if (pattern->step[i].command != 0)
456 {
457 Uint16 c = pattern->step[i].command;
458 FIX_ENDIAN(c);
459 SDL_RWwrite(f, &c, 1, sizeof(pattern->step[i].command));
460 }
461
462 if (pattern->step[i].volume != MUS_NOTE_NO_VOLUME)
463 SDL_RWwrite(f, &pattern->step[i].volume, 1, sizeof(pattern->step[i].volume));
464 }
465 }
466
467
open_song(FILE * f)468 int open_song(FILE *f)
469 {
470 new_song();
471
472 if (!mus_load_song_file(f, &mused.song, mused.mus.cyd->wavetable_entries)) return 0;
473
474 mused.song.num_patterns = NUM_PATTERNS;
475 mused.song.num_instruments = NUM_INSTRUMENTS;
476
477 // Use kewlkool heuristics to determine sequence spacing
478
479 mused.sequenceview_steps = mused.song.sequence_step;
480
481 if (mused.sequenceview_steps == 0)
482 {
483 mused.sequenceview_steps = 1000;
484
485 for (int c = 0 ; c < MUS_MAX_CHANNELS ; ++c)
486 for (int s = 1 ; s < mused.song.num_sequences[c] && mused.song.sequence[c][s-1].position < mused.song.song_length ; ++s)
487 if (mused.sequenceview_steps > mused.song.sequence[c][s].position - mused.song.sequence[c][s-1].position)
488 {
489 mused.sequenceview_steps = mused.song.sequence[c][s].position - mused.song.sequence[c][s-1].position;
490 }
491
492 for (int c = 0 ; c < MUS_MAX_CHANNELS ; ++c)
493 if (mused.song.num_sequences[c] > 0 && mused.song.sequence[c][mused.song.num_sequences[c]-1].position < mused.song.song_length)
494 {
495 if (mused.sequenceview_steps > mused.song.song_length - mused.song.sequence[c][mused.song.num_sequences[c]-1].position)
496 {
497 mused.sequenceview_steps = mused.song.song_length - mused.song.sequence[c][mused.song.num_sequences[c]-1].position;
498 }
499 }
500
501 if (mused.sequenceview_steps < 1) mused.sequenceview_steps = 1;
502 if (mused.sequenceview_steps == 1000) mused.sequenceview_steps = 16;
503 }
504
505 mus_set_fx(&mused.mus, &mused.song);
506 enable_callback(true);
507 mirror_flags();
508
509 if (!mused.song.time_signature) mused.song.time_signature = 0x404;
510
511 mused.time_signature = mused.song.time_signature;
512
513 mused.flags &= ~EDIT_MODE;
514
515 unmute_all_action(NULL, NULL, NULL);
516
517 for (int i = 0 ; i < mused.song.num_patterns ; ++i)
518 if(mused.song.pattern[i].num_steps == 0)
519 resize_pattern(&mused.song.pattern[i], mused.default_pattern_length);
520
521 mused.song.wavetable_names = realloc(mused.song.wavetable_names, sizeof(mused.song.wavetable_names[0]) * CYD_WAVE_MAX_ENTRIES);
522
523 for (int i = mused.song.num_wavetables ; i < CYD_WAVE_MAX_ENTRIES ; ++i)
524 {
525 mused.song.wavetable_names[i] = malloc(MUS_WAVETABLE_NAME_LEN + 1);
526 memset(mused.song.wavetable_names[i], 0, MUS_WAVETABLE_NAME_LEN + 1);
527 }
528
529 set_channels(mused.song.num_channels);
530
531 return 1;
532 }
533
534
save_instrument(SDL_RWops * f)535 int save_instrument(SDL_RWops *f)
536 {
537 const Uint8 version = MUS_VERSION;
538
539 SDL_RWwrite(f, MUS_INST_SIG, strlen(MUS_INST_SIG), sizeof(MUS_INST_SIG[0]));
540
541 SDL_RWwrite(f, &version, 1, sizeof(version));
542
543 save_instrument_inner(f, &mused.song.instrument[mused.current_instrument], &mused.mus.cyd->wavetable_entries[mused.song.instrument[mused.current_instrument].wavetable_entry], &mused.mus.cyd->wavetable_entries[mused.song.instrument[mused.current_instrument].fm_wave]);
544
545 return 1;
546 }
547
548
save_fx(SDL_RWops * f)549 int save_fx(SDL_RWops *f)
550 {
551 const Uint8 version = MUS_VERSION;
552
553 SDL_RWwrite(f, MUS_FX_SIG, strlen(MUS_FX_SIG), sizeof(MUS_FX_SIG[0]));
554
555 SDL_RWwrite(f, &version, 1, sizeof(version));
556
557 save_fx_inner(f, &mused.song.fx[mused.fx_bus]);
558
559 return 1;
560 }
561
562
save_song_inner(SDL_RWops * f,SongStats * stats)563 int save_song_inner(SDL_RWops *f, SongStats *stats)
564 {
565 bool kill_unused_things = false;
566
567 Uint8 n_inst = mused.song.num_instruments;
568 if (!confirm(domain, mused.slider_bevel, &mused.largefont, "Save unused song elements?"))
569 {
570 int maxpat = -1;
571 for (int c = 0 ; c < mused.song.num_channels ; ++c)
572 {
573 for (int i = 0 ; i < mused.song.num_sequences[c] ; ++i)
574 if (maxpat < mused.song.sequence[c][i].pattern)
575 maxpat = mused.song.sequence[c][i].pattern;
576 }
577
578 n_inst = 0;
579
580 for (int i = 0 ; i <= maxpat ; ++i)
581 for (int s = 0 ; s < mused.song.pattern[i].num_steps ; ++s)
582 if (mused.song.pattern[i].step[s].instrument != MUS_NOTE_NO_INSTRUMENT)
583 n_inst = my_max(n_inst, mused.song.pattern[i].step[s].instrument + 1);
584
585 mused.song.num_patterns = maxpat + 1;
586
587 kill_unused_things = true;
588 }
589
590 SDL_RWwrite(f, MUS_SONG_SIG, strlen(MUS_SONG_SIG), sizeof(MUS_SONG_SIG[0]));
591
592 const Uint8 version = MUS_VERSION;
593
594 mused.song.time_signature = mused.time_signature;
595 mused.song.sequence_step = mused.sequenceview_steps;
596
597 SDL_RWwrite(f, &version, 1, sizeof(version));
598
599 SDL_RWwrite(f, &mused.song.num_channels, 1, sizeof(mused.song.num_channels));
600 Uint16 temp16 = mused.song.time_signature;
601 FIX_ENDIAN(temp16);
602 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.time_signature));
603 temp16 = mused.song.sequence_step;
604 FIX_ENDIAN(temp16);
605 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.sequence_step));
606 SDL_RWwrite(f, &n_inst, 1, sizeof(mused.song.num_instruments));
607 temp16 = mused.song.num_patterns;
608 FIX_ENDIAN(temp16);
609 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.num_patterns));
610 for (int i = 0 ; i < mused.song.num_channels ; ++i)
611 {
612 temp16 = mused.song.num_sequences[i];
613 FIX_ENDIAN(temp16);
614 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.num_sequences[i]));
615 }
616 temp16 = mused.song.song_length;
617 FIX_ENDIAN(temp16);
618 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.song_length));
619 temp16 = mused.song.loop_point;
620 FIX_ENDIAN(temp16);
621 SDL_RWwrite(f, &temp16, 1, sizeof(mused.song.loop_point));
622 SDL_RWwrite(f, &mused.song.master_volume, 1, 1);
623 SDL_RWwrite(f, &mused.song.song_speed, 1, sizeof(mused.song.song_speed));
624 SDL_RWwrite(f, &mused.song.song_speed2, 1, sizeof(mused.song.song_speed2));
625 SDL_RWwrite(f, &mused.song.song_rate, 1, sizeof(mused.song.song_rate));
626 Uint32 temp32 = mused.song.flags;
627 FIX_ENDIAN(temp32);
628 SDL_RWwrite(f, &temp32, 1, sizeof(mused.song.flags));
629 SDL_RWwrite(f, &mused.song.multiplex_period, 1, sizeof(mused.song.multiplex_period));
630 SDL_RWwrite(f, &mused.song.pitch_inaccuracy, 1, sizeof(mused.song.pitch_inaccuracy));
631
632 write_string8(f, mused.song.title);
633
634 if (stats)
635 stats->size[STATS_HEADER] = SDL_RWtell(f);
636
637 Uint8 n_fx = kill_unused_things ? 0 : CYD_MAX_FX_CHANNELS;
638
639 if (kill_unused_things)
640 {
641 for (int i = 0 ; i < CYD_MAX_FX_CHANNELS ; ++i)
642 {
643 if (mused.song.fx[i].flags & (CYDFX_ENABLE_REVERB | CYDFX_ENABLE_CRUSH | CYDFX_ENABLE_CHORUS))
644 n_fx = my_max(n_fx, i + 1);
645 }
646 }
647
648 SDL_RWwrite(f, &n_fx, 1, sizeof(n_fx));
649
650 debug("Saving %d fx", n_fx);
651 for (int fx = 0 ; fx < n_fx ; ++fx)
652 {
653 save_fx_inner(f, &mused.song.fx[fx]);
654 }
655
656 if (stats)
657 stats->size[STATS_FX] = SDL_RWtell(f);
658
659 SDL_RWwrite(f, &mused.song.default_volume[0], sizeof(mused.song.default_volume[0]), mused.song.num_channels);
660 SDL_RWwrite(f, &mused.song.default_panning[0], sizeof(mused.song.default_panning[0]), mused.song.num_channels);
661
662 if (stats)
663 stats->size[STATS_DEFVOLPAN] = SDL_RWtell(f);
664
665 debug("Saving %d instruments", n_inst);
666 for (int i = 0 ; i < n_inst ; ++i)
667 {
668 save_instrument_inner(f, &mused.song.instrument[i], NULL, NULL);
669 }
670
671 if (stats)
672 stats->size[STATS_INSTRUMENTS] = SDL_RWtell(f);
673
674 bool *used_pattern = calloc(sizeof(bool), mused.song.num_patterns);
675
676 for (int i = 0 ; i < mused.song.num_channels; ++i)
677 {
678 for (int s= 0 ; s < mused.song.num_sequences[i] ; ++s)
679 {
680 temp16 = mused.song.sequence[i][s].position;
681 FIX_ENDIAN(temp16);
682 SDL_RWwrite(f, &temp16, 1, sizeof(temp16));
683
684 used_pattern[mused.song.sequence[i][s].pattern] = true;
685
686 temp16 = mused.song.sequence[i][s].pattern;
687 FIX_ENDIAN(temp16);
688 SDL_RWwrite(f, &temp16, 1, sizeof(temp16));
689 SDL_RWwrite(f, &mused.song.sequence[i][s].note_offset, 1, sizeof(mused.song.sequence[i][s].note_offset));
690 }
691 }
692
693 if (stats)
694 stats->size[STATS_SEQUENCE] = SDL_RWtell(f);
695
696 for (int i = 0 ; i < mused.song.num_patterns; ++i)
697 {
698 write_packed_pattern(f, &mused.song.pattern[i], !used_pattern[i]);
699 }
700
701 if (stats)
702 stats->size[STATS_PATTERNS] = SDL_RWtell(f);
703
704 free(used_pattern);
705
706 int max_wt = kill_unused_things ? 0 : CYD_WAVE_MAX_ENTRIES;
707
708 for (int i = 0 ; i < CYD_WAVE_MAX_ENTRIES ; ++i)
709 {
710 if (mused.mus.cyd->wavetable_entries[i].samples)
711 max_wt = my_max(max_wt, i + 1);
712 }
713
714 FIX_ENDIAN(max_wt);
715
716 SDL_RWwrite(f, &max_wt, 1, sizeof(Uint8));
717
718 debug("Saving %d wavetable items", max_wt);
719
720 for (int i = 0 ; i < max_wt ; ++i)
721 {
722 write_wavetable_entry(f, &mused.mus.cyd->wavetable_entries[i], true);
723 }
724
725 if (stats)
726 stats->size[STATS_WAVETABLE] = SDL_RWtell(f);
727
728 for (int i = 0 ; i < max_wt ; ++i)
729 {
730 write_string8(f, mused.song.wavetable_names[i]);
731 }
732
733 if (stats)
734 stats->size[STATS_WAVETABLE_NAMES] = SDL_RWtell(f);
735
736 mused.song.num_patterns = NUM_PATTERNS;
737 mused.song.num_instruments = NUM_INSTRUMENTS;
738
739 if (stats)
740 {
741 for (int i = N_STATS - 1 ; i > 0 ; --i)
742 {
743 stats->size[i] -= stats->size[i - 1];
744 }
745
746 stats->total_size = 0;
747
748 for (int i = 0 ; i < N_STATS ; ++i)
749 {
750 stats->total_size += stats->size[i];
751 }
752 }
753
754 return 1;
755 }
756
757
open_wavetable(FILE * f)758 int open_wavetable(FILE *f)
759 {
760 Wave *w = wave_load(f);
761
762 if (w)
763 {
764 // http://soundfile.sapp.org/doc/WaveFormat/ says: "8-bit samples are stored as unsigned bytes, ranging from 0 to 255.
765 // 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767."
766
767 cyd_wave_entry_init(&mused.mus.cyd->wavetable_entries[mused.selected_wavetable], w->data, w->length, w->bits_per_sample == 16 ? CYD_WAVE_TYPE_SINT16 : CYD_WAVE_TYPE_UINT8, w->channels, 1, 1);
768
769 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].flags = 0;
770 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].sample_rate = w->sample_rate;
771 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].base_note = MIDDLE_C << 8;
772
773 wave_destroy(w);
774
775 invalidate_wavetable_view();
776
777 return 1;
778 }
779
780 return 0;
781 }
782
783
open_wavetable_raw_inner(FILE * f,int t)784 static int open_wavetable_raw_inner(FILE *f, int t)
785 {
786 size_t pos = ftell(f);
787 fseek(f, 0, SEEK_END);
788 size_t s = ftell(f) - pos;
789 fseek(f, pos, SEEK_SET);
790
791 if (s > 0)
792 {
793 Sint8 *w = calloc(s, sizeof(Sint8));
794
795 if (w)
796 {
797 fread(w, 1, s, f);
798
799 cyd_wave_entry_init(&mused.mus.cyd->wavetable_entries[mused.selected_wavetable], w, s, t, 1, 1, 1);
800
801 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].flags = 0;
802 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].sample_rate = 8000;
803 mused.mus.cyd->wavetable_entries[mused.selected_wavetable].base_note = MIDDLE_C << 8;
804
805 free(w);
806
807 invalidate_wavetable_view();
808
809 return 1;
810 }
811 }
812
813 return 0;
814 }
815
816
open_wavetable_raw(FILE * f)817 int open_wavetable_raw(FILE *f)
818 {
819 return open_wavetable_raw_inner(f, CYD_WAVE_TYPE_SINT8);
820 }
821
822
open_wavetable_raw_u(FILE * f)823 int open_wavetable_raw_u(FILE *f)
824 {
825 return open_wavetable_raw_inner(f, CYD_WAVE_TYPE_UINT8);
826 }
827
828
open_instrument(FILE * f)829 int open_instrument(FILE *f)
830 {
831 if (!mus_load_instrument_file2(f, &mused.song.instrument[mused.current_instrument], mused.mus.cyd->wavetable_entries)) return 0;
832
833 mused.modified = true;
834
835 invalidate_wavetable_view();
836
837 return 1;
838 }
839
840
open_fx(FILE * f)841 int open_fx(FILE *f)
842 {
843 if (!mus_load_fx_file(f, &mused.song.fx[mused.fx_bus])) return 0;
844
845 mused.modified = true;
846
847 mus_set_fx(&mused.mus, &mused.song);
848
849 return 1;
850 }
851
852
save_song(SDL_RWops * ops)853 int save_song(SDL_RWops *ops)
854 {
855 int r = save_song_inner(ops, NULL);
856
857 mused.modified = false;
858
859 return r;
860 }
861
862
save_wavetable(FILE * ops)863 int save_wavetable(FILE *ops)
864 {
865 WaveWriter *ww = ww_create(ops, mused.mus.cyd->wavetable_entries[mused.selected_wavetable].sample_rate, 1);
866
867 ww_write(ww, mused.mus.cyd->wavetable_entries[mused.selected_wavetable].data, mused.mus.cyd->wavetable_entries[mused.selected_wavetable].samples);
868
869 ww_finish(ww);
870 return 1;
871 }
872
873
open_data(void * type,void * action,void * _ret)874 void open_data(void *type, void *action, void *_ret)
875 {
876 int t = CASTPTR(int, type);
877 int a = CASTPTR(int, action);
878 int *ret = _ret;
879
880 if (a == OD_A_OPEN && t == OD_T_SONG)
881 {
882 int r = -1;
883 if (mused.modified) r = confirm_ync(domain, mused.slider_bevel, &mused.largefont, "Save song?");
884 int ret_val;
885
886 if (r == 0)
887 {
888 if (ret) *ret = 0;
889 return;
890 }
891
892 if (r == 1)
893 {
894 stop(0,0,0); // so loop positions set by pattern loop mode will be restored
895 open_data(MAKEPTR(OD_T_SONG), MAKEPTR(OD_A_SAVE), &ret_val);
896 if (!ret_val)
897 {
898 if (ret) *ret = 0;
899 return;
900 }
901 }
902 }
903
904 const struct
905 {
906 const char *name, *ext;
907 int (*open)(FILE *);
908 int (*save)(SDL_RWops *);
909 } open_stuff[] = {
910 { "song", "kt", open_song, save_song },
911 { "instrument", "ki", open_instrument, save_instrument },
912 { "wave", "wav", open_wavetable, NULL },
913 { "raw signed", "", open_wavetable_raw, NULL },
914 { "raw unsigned", "", open_wavetable_raw_u, NULL },
915 { "FX bus", "kx", open_fx, save_fx }
916 };
917
918 const char *mode[] = { "rb", "wb" };
919 const char *modename[] = { "Open", "Save" };
920 char str[1000];
921 snprintf(str, sizeof(str), "%s %s", modename[a], open_stuff[t].name);
922
923 stop(0,0,0);
924
925 char _def[1024] = "";
926 char *def = NULL;
927
928 if (a == OD_A_SAVE)
929 {
930 switch (t)
931 {
932 case OD_T_INSTRUMENT:
933 {
934 snprintf(_def, sizeof(_def), "%s.ki", mused.song.instrument[mused.current_instrument].name);
935 }
936 break;
937
938 case OD_T_FX:
939 {
940 snprintf(_def, sizeof(_def), "%s.kx", mused.song.fx[mused.fx_bus].name);
941 }
942 break;
943
944 case OD_T_SONG:
945 {
946 if (strlen(mused.previous_song_filename) == 0)
947 {
948 snprintf(_def, sizeof(_def), "%s.kt", mused.song.title);
949 }
950 else
951 {
952 strncpy(_def, mused.previous_song_filename, sizeof(_def));
953 }
954 }
955 break;
956 }
957
958 def = _def;
959 }
960
961 char filename[5000], previous_cwd[5000];
962 FILE *f = NULL;
963 SDL_RWops *rw = NULL;
964
965 // Save current cwd
966 getcwd(previous_cwd, sizeof(previous_cwd));
967
968 if (mused.previous_filebox_path[t][0])
969 chdir(mused.previous_filebox_path[t]);
970
971 if (open_dialog_fn(mode[a], str, open_stuff[t].ext, domain, mused.slider_bevel, &mused.largefont, &mused.smallfont, def, filename, sizeof(filename)))
972 {
973 getcwd(mused.previous_filebox_path[t], sizeof(mused.previous_filebox_path[t]));
974
975 if (!(mused.flags & DISABLE_BACKUPS) && a == OD_A_SAVE && !create_backup(filename))
976 warning("Could not create backup for %s", filename);
977
978 if (a == OD_A_SAVE && t == OD_T_SONG)
979 strncpy(mused.previous_song_filename, filename, sizeof(mused.previous_song_filename) - 1);
980
981 f = fopen(filename, mode[a]);
982 if (!f)
983 {
984 msgbox(domain, mused.slider_bevel, &mused.largefont, "Could not open file", MB_OK);
985 }
986 else if (t == OD_T_SONG)
987 {
988 // Update recent files list if we are opening/saving a song
989
990 char fullpath[6001] = {0};
991 snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", mused.previous_filebox_path[t], filename);
992
993 update_recent_files_list(fullpath);
994 }
995 }
996
997 if (f)
998 {
999 int return_val = 1;
1000 void * tmp;
1001
1002 if (a == 0)
1003 tmp = open_stuff[t].open;
1004 else
1005 tmp = open_stuff[t].save;
1006
1007 if (tmp || ((t == OD_T_WAVETABLE) && (a == 1)))
1008 {
1009 cyd_lock(&mused.cyd, 1);
1010 int r;
1011 if (a == 0)
1012 r = open_stuff[t].open(f);
1013 else
1014 {
1015 if (t != OD_T_WAVETABLE)
1016 {
1017 rw = create_memwriter(f);
1018 r = open_stuff[t].save(rw);
1019 }
1020 else
1021 {
1022 r = save_wavetable(f);
1023 }
1024 }
1025
1026 cyd_lock(&mused.cyd, 0);
1027
1028 if (!r)
1029 {
1030 snprintf(str, sizeof(str), "Could not open %s!", open_stuff[t].name);
1031 msgbox(domain, mused.slider_bevel, &mused.largefont, str, MB_OK);
1032
1033 return_val = 0;
1034 }
1035 else if (a == OD_A_OPEN && t != OD_T_SONG)
1036 mused.modified = true;
1037 }
1038
1039 if (rw)
1040 SDL_RWclose(rw);
1041
1042 fclose(f);
1043
1044 if (ret) *ret = return_val;
1045 }
1046 else
1047 if (ret) *ret = 0;
1048
1049 // Restore previous cwd
1050 chdir(previous_cwd);
1051
1052 change_mode(mused.mode);
1053 }
1054