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