1 /* $Id: file_read.c,v 1.13 2006/09/16 10:18:38 toad32767 Exp $ */
2 /**
3  ** 2005, 2006 by Marco Trillo
4  ** This file is part of UModPlayer, and is released by
5  ** its autors to the Public Domain.
6  ** In case it's not legally possible, its autors grant
7  ** anyone the right to use, redistribute and modify
8  ** this software for any purpose, without any conditions,
9  ** unless such conditions are required by law.
10  **
11  ** THIS FILE COMES WITHOUT ANY WARRANTY. THE AUTHORS
12  ** SHALL NOT BE LIABLE FOR ANY DAMAGE RESULTING BY THE
13  ** USE OR MISUSE OF THIS SOFTWARE.
14  **/
15 
16 /*
17  * =====================
18  *  FILE-RELATED STUFF
19  * =====================
20  */
21 
22 #include <sys/time.h>
23 #include <termios.h>
24 
25 #include <messages.h>
26 #include <umodplayer.h>
27 #include <text.h>
28 #include <file_read.h>
29 #include <file_write.h>
30 #include <coresound.h>
31 #include <playlist.h>
32 #include <modtypes.h>
33 
34 #include <sys/stat.h>
35 
36 /*
37  * Safe variants of malloc(), realloc(),
38  * strcopyof(), and ReadString().
39  */
40 LOCAL void *
safe_alloc(size_t size)41 safe_alloc(size_t size)
42 {
43 	void *ptr = malloc(size);
44 	if (ptr == NULL) {
45 		error("%s", MESSAGE_NO_MEMORY);
46 		exit(UM_ERR_MEMORY);
47 	}
48 	return ptr;
49 }
50 LOCAL char *
safecopyof(char * s)51 safecopyof(char *s)
52 {
53 	char *ptr = strcopyof(s);
54 	if (ptr == NULL) {
55 		error("%s", MESSAGE_NO_MEMORY);
56 		exit(UM_ERR_MEMORY);
57 	}
58 	return ptr;
59 }
60 LOCAL char *
SafeReadString()61 SafeReadString()
62 {
63 	char *s = ReadString();
64 	if (s == NULL) {
65 		error("%s", MESSAGE_NO_MEMORY);
66 		exit(UM_ERR_MEMORY);
67 	}
68 	return s;
69 }
70 
71 /*
72  * `file.name' is the global variable specifying the module name.
73  * Set `file.name' to NULL if failed.
74  *
75  * If `file.malloc' is TRUE, then `file.name' must be free()'d
76  * before being set to NULL.
77  */
78 EXPORT void
ULoadFile()79 ULoadFile()
80 {
81 	int fd;
82 	unsigned long remaining, buflen = 0, bufpos;
83 	uint8_t *buffer;
84 	struct stat buf;
85 
86 	if (file.name == NULL) {
87 		return;
88 	}
89 
90 	TrimString(file.name);
91 
92 	if (stat(file.name, &buf) == -1) {
93 		error("CAN'T stat(\"%s\"): %s\n", file.name, strerror(errno));
94 		goto cleanup;
95 	}
96 
97 	buflen = (unsigned long) (buf.st_size);
98 	if (buflen == 0) {
99 		error("size of \"%s\" is zero!\n", file.name);
100 		goto cleanup;
101 	}
102 
103 	fd = open(file.name, O_RDONLY);
104 	if (fd == -1) {
105 		error("CAN'T open(\"%s\"): %s\n", file.name, strerror(errno));
106 		goto cleanup;
107 	}
108 
109 	buffer = (uint8_t *) safe_alloc((size_t) buflen);
110 
111 	remaining = buflen;
112 	bufpos = 0;
113 	while (remaining > 0) {
114 		long bytes_read;
115 
116 		if ((bytes_read = (long) read(fd, buffer + bufpos, (size_t) remaining)) < 1) {
117 			error("CAN'T read(): %s\n", strerror(errno));
118 			free(buffer);
119 			close(fd);
120 			goto cleanup;
121 		}
122 
123 		remaining -= bytes_read;
124 		bufpos += bytes_read;
125 	}
126 
127 	close(fd);
128 
129 	file.mod = ModPlug_Load(buffer, buflen);
130 	free(buffer);
131 	if (file.mod == NULL) {
132 		goto cleanup;
133 	}
134 
135 	DrawInfoTable();
136 
137 	return;
138 
139 cleanup:
140 	if (file.malloc == TRUE) {
141 		free(file.name);
142 	}
143 	file.malloc = FALSE;
144 	file.name = NULL;
145 
146 	return;
147 }
148 
149 EXPORT void
UFreeFile()150 UFreeFile()
151 {
152 	if (file.name != NULL) {
153 		ModPlug_Unload(file.mod);
154 		if (file.malloc == TRUE)
155 			free(file.name);
156 		file.malloc = FALSE;
157 		file.name = NULL;
158 	}
159 	return;
160 }
161 
162 EXPORT void
DisplayCurPos()163 DisplayCurPos()
164 {
165 	notice("Order: %d - Pattern: %d - Row: %d\n",
166 	    ModPlug_GetCurrentOrder(file.mod),
167 	    ModPlug_GetCurrentPattern(file.mod),
168 	    ModPlug_GetCurrentRow(file.mod)
169 	);
170 	return;
171 }
172 
173 EXPORT void
DrawInfoTable()174 DrawInfoTable()
175 {
176 	WTable table;
177 	int bytes_out;
178 
179 	if (sets.verbosity == 0)
180 		return;
181 
182 	rr = (char **) safe_alloc(7 * sizeof(char *));
183 	bytes_out = ModPlug_GetLength(file.mod);
184 	bytes_out /= 1000;
185 	rr[0] = (char *) safe_alloc(10);
186 	sprintf(rr[0], "%d:%02d", bytes_out / 60, bytes_out % 60);
187 	rr[1] = safecopyof(DetermineType());
188 	rr[2] = (char *) safe_alloc(20);
189 	sprintf(rr[2], "%d instruments", ModPlug_NumInstruments(file.mod));
190 	rr[3] = (char *) safe_alloc(16);
191 	sprintf(rr[3], "%d samples", ModPlug_NumSamples(file.mod));
192 	rr[4] = (char *) safe_alloc(18);
193 	sprintf(rr[4], "%d channels", ModPlug_NumChannels(file.mod));
194 	rr[5] = (char *) safe_alloc(18);
195 	sprintf(rr[5], "%d patterns", ModPlug_NumPatterns(file.mod));
196 	rr[6] = (char *) ModPlug_GetName(file.mod);
197 
198 	bytes_out = CalcLen(rr, 7) + 1;
199 	TableSetOptions(&table, 1, 6, 2, MAXVAL(bytes_out, 13), 0, TABLE_LEFT_ALIGN);
200 	TableSetCaption(&table, rr[6]);
201 	TableUseTheme(&table, sets.appareance);
202 	TableInitCallback(&table, MyTextCallback);
203 
204 	DrawTable(table);
205 
206 	for (bytes_out = 0; bytes_out < 6; ++bytes_out)
207 		free(rr[bytes_out]);
208 	free(rr);
209 
210 	return;
211 }
212 
213 EXPORT void
ExportSong()214 ExportSong()
215 {
216 	char *p, *q;
217 	unsigned long len;
218 	char y;
219 
220 	printf(MESSAGE_EXPORT);
221 	fflush(stdout);
222 
223 	p = SafeReadString();
224 	TrimString(p);
225 
226 	if (*p == 0) {
227 		free(p);
228 		return;
229 	}
230 	for (;;) {
231 		startpos = endpos = 0;
232 		puts(MESSAGE_FORMATS);
233 		puts(MESSAGE_PRESS_ENTER);
234 		puts("================ Audio export options =================");
235 		puts("a) Export audio as WAVE (WAV), 16-bit");
236 		puts("b) Export audio as Audio IFF (AIFF), 16-bit");
237 		puts("c) Export audio as Audio IFF (AIFF), 24-bit");
238 		puts("d) Export audio as Audio IFF (AIFF), 32-bit");
239 		puts("e) Export audio as raw system-endian 16-bit PCM");
240 		puts("f) Export audio as raw little-endian 16-bit PCM");
241 		puts("g) Export audio as raw big-endian 16-bit PCM\n");
242 		puts("=============== Module export options =================");
243 		puts("h) Convert to Impulse Tracker (IT)\n");
244 		puts("=======================================================");
245 		puts("(audio will be exported using current sound options)");
246 		fputs(MESSAGE_FORMAT, stdout);
247 		fflush(stdout);
248 		q = SafeReadString();
249 		if (*q == '\0') {
250 			free(p);
251 			free(q);
252 			return;
253 		}
254 		if (*q == 'a') {
255 			len = (unsigned long) WriteWAV(p);
256 			if (len < 1) {
257 				error("%s\n", MESSAGE_EXPORT_ERROR);
258 			}
259 			free(p);
260 			free(q);
261 			return;
262 		}
263 		if (*q == 'b' || *q == 'c' || *q == 'd') {
264 			int ret = 0;
265 
266 			if (*q == 'b')
267 				ret = WriteAIFF(p, 16);
268 			else if (*q == 'c')
269 				ret = WriteAIFF(p, 24);
270 			else if (*q == 'd')
271 				ret = WriteAIFF(p, 32);
272 			if (ret < 1) {
273 				puts(MESSAGE_EXPORT_ERROR);
274 			}
275 			free(p);
276 			free(q);
277 			return;
278 		}
279 		if (*q == 'e') {
280 			len = WritePCM(p, EXPORT_SYS_ENDIAN);
281 			if (len < 1) {
282 				error("%s\n", MESSAGE_EXPORT_ERROR);
283 			}
284 			free(p);
285 			free(q);
286 			return;
287 		}
288 		if (*q == 'f') {
289 			len = WritePCM(p, EXPORT_LTE_ENDIAN);
290 			if (len < 1) {
291 				error("%s\n", MESSAGE_EXPORT_ERROR);
292 			}
293 			free(p);
294 			free(q);
295 			return;
296 		}
297 		if (*q == 'g') {
298 			len = WritePCM(p, EXPORT_BIG_ENDIAN);
299 			if (len < 1) {
300 				error("%s\n", MESSAGE_EXPORT_ERROR);
301 			}
302 			free(p);
303 			free(q);
304 			return;
305 		}
306 		if (*q == 'h') {
307 #ifdef MODPLUG_CAN_SAVE
308 			y = ModPlug_ExportIT(file.mod, p);
309 #else
310 			error("ModPlug compiled without 'save' support\n");
311 			y = 0;
312 #endif
313 			if (y != 1) {
314 				error("%s\n", MESSAGE_EXPORT_ERROR);
315 			} else {
316 				notice("%s\n", MESSAGE_DONE);
317 			}
318 			free(p);
319 			free(q);
320 			return;
321 		}
322 		free(q);
323 	}
324 
325 
326 	return;
327 }
328 
329 LOCAL void
ErrorDispatch(int ret,char * data)330 ErrorDispatch(int ret, char *data)
331 {
332 	switch (ret) {
333 	case UM_ERR_NOITEM:
334 		error("%s\n", MESSAGE_CANT_FIND_ITEM);
335 		break;
336 	case UM_ERR_IO:
337 		error("I/O error: %s: %s\n", data, strerror(errno));
338 		break;
339 	case UM_ERR_MEMORY:
340 		error("Memory error: %s\n", strerror(errno));
341 		break;
342 	case UM_ERR_INTERNAL:
343 		error("Internal error (%s)\n", data);
344 		break;
345 	case UM_OK:
346 		return;
347 	default:
348 		error("Unknown error: %d. Please report this bug.\n", ret);
349 	}
350 }
351 
352 LOCAL void
PlayListLoadFolder()353 PlayListLoadFolder()
354 {
355 	char path[1024];
356 
357 	getcwd(path, 1024);
358 	ErrorDispatch(list_from_dir(path), path);
359 }
360 
361 LOCAL void
PlayListSaveFile(char * finame)362 PlayListSaveFile(char *finame)
363 {
364 	ErrorDispatch(save_list(finame), finame);
365 }
366 
367 LOCAL void
PlayListLoadFile(char * finame)368 PlayListLoadFile(char *finame)
369 {
370 	ErrorDispatch(load_list(finame), finame);
371 }
372 
373 /*
374  * wait 2 seconds for a key, to interrupt the playlist course
375  */
376 LOCAL int
PlayListWaitForKey()377 PlayListWaitForKey()
378 {
379 	int keyHit;
380 	fd_set fds;
381 	struct timeval tv;
382 	struct termios term, _term;
383 
384 	tcgetattr(0, &_term);
385 	memcpy(&term, &_term, sizeof(term));
386 	cfmakeraw(&term);
387 	tcsetattr(0, TCSANOW, &term);
388 	FD_ZERO(&fds);
389 	FD_SET(0, &fds);
390 	tv.tv_sec = 2;
391 	tv.tv_usec = 0;
392 	keyHit = select(1, &fds, NULL, NULL, &tv);
393 	tcsetattr(0, TCSANOW, &_term);
394 
395 	return keyHit;
396 }
397 
398 EXPORT void
PlayList()399 PlayList()
400 {
401 	char *s, pathbuf[512];
402 
403 	getcwd(pathbuf, 512);
404 	puts(MESSAGE_PRESS_ENTER);
405 	puts("a) Import playlist from current dir.");
406 	puts("b) Load a saved playlist");
407 	puts("c) New Playlist");
408 	fputs(MESSAGE_OPTION, stdout);
409 	fflush(stdout);
410 	s = SafeReadString();
411 	if (*s == '\0') {
412 		free(s);
413 		return;
414 	}
415 
416 	switch (*s) {
417 	case 'a':
418 		PlayListLoadFolder();
419 		break;
420 
421 	case 'b':
422 		printf(MESSAGE_FILENAME);
423 		fflush(stdout);
424 		free(s);
425 		s = SafeReadString();
426 		if (*s == '\0') {
427 			free(s);
428 			return;
429 		}
430 		PlayListLoadFile(s);
431 		break;
432 
433 	case 'c':
434 		break;
435 
436 	default:
437 		free(s);
438 		return;
439 	}
440 
441 	free(s);
442 	print_list();
443 	for (;;) {
444 		puts(MESSAGE_PRESS_ENTER);
445 		puts("p) PLAY");
446 		puts("s) SAVE");
447 		puts("n) NEW ITEM");
448 		puts("d) DELETE ITEM");
449 		puts("m) MOVE ITEM");
450 		puts("v) VIEW ITEMS");
451 		puts("q) QUIT");
452 		printf(MESSAGE_OPTION);
453 		fflush(stdout);
454 		s = SafeReadString();
455 		if (*s == '\0') {
456 			free(s);
457 			continue;
458 		}
459 
460 		switch (*s) {
461 		case 'q':
462 			if (s != NULL)
463 				free(s);
464 
465 			warning("playlist will be deleted. are you sure [y/n]? ");
466 			fflush(stderr);
467 			s = SafeReadString();
468 
469 			if (*s != 'y') {
470 				free(s);
471 				break;
472 			}
473 			free(s);
474 			delete_list();
475 			return;
476 
477 		/* PLAY */
478 		case 'p':
479 		{
480 			struct playlist *p = next_playlist(NULL);
481 
482 			while (p != NULL) {
483 				UFreeFile();	/* free current loaded file */
484 				file.name = p->path;
485 				file.malloc = FALSE;
486 				if (file.name != NULL)
487 					ULoadFile();	/* load the new file */
488 				if (file.name != NULL) {
489 					(void) CoreSound_StartMonitor(); /* play */
490 
491 					/* play finished */
492 					puts(MESSAGE_STOP_PLAYLIST);
493 					usleep(50000);
494 					if (PlayListWaitForKey())
495 						break;
496 				}
497 				p = next_playlist(p);
498 			}
499 			free(s);
500 			break;
501 		}
502 		/* SAVE */
503 		case 's':
504 			free(s);
505 			printf(MESSAGE_FILENAME);
506 			fflush(stdout);
507 			s = ReadString();
508 			if (s == NULL) {
509 				error("%s", MESSAGE_NO_MEMORY);
510 				break;
511 			}
512 			if (*s == 0) {
513 				free(s);
514 				break;
515 			}
516 			PlayListSaveFile(s);
517 			free(s);
518 			break;
519 		/* NEW ITEM */
520 		case 'n':
521 		{
522 			struct playlist *p;
523 			char *name, buf[1024];
524 			int ret;
525 
526 			free(s);
527 			printf(MESSAGE_FILENAME);
528 			fflush(stdout);
529 			s = ReadString();
530 			if (s == NULL) {
531 				error("%s", MESSAGE_NO_MEMORY);
532 				break;
533 			}
534 			if (*s == '\0') {
535 				free(s);
536 				break;
537 			}
538 			/* convert relative paths into absolute paths */
539 			TrimString(s);
540 			if (s[0] != '/') {
541 				snprintf(buf, 1024, "%s/%s", pathbuf, s);
542 				free(s);
543 				s = safecopyof(buf);
544 			}
545 			p = new_playlist();
546 			if (p == NULL) {
547 				error("%s", MESSAGE_NO_MEMORY);
548 				exit(UM_ERR_MEMORY);
549 			}
550 			p->path = s;
551 			name = safecopyof(s);
552 			ret = item_get_info(p, name, buf, 1024);
553 			free(name);
554 			break;
555 		}
556 		/* DELETE ITEM */
557 		case 'd':
558 		{
559 			int no;
560 			struct playlist *p;
561 
562 asknum_d:
563 			free(s);
564 			fputs("delete item number [first item is 0; press ENTER to cancel]? ", stdout);
565 			fflush(stdout);
566 			s = ReadString();
567 			if (s == NULL) {
568 				error("%s", MESSAGE_NO_MEMORY);
569 				break;
570 			}
571 			if (*s == '\0') {
572 				free(s);
573 				break;
574 			}
575 
576 			no = atoi(s);
577 			p = find_playlist(no);
578 			if (p == NULL) {
579 				puts(MESSAGE_CANT_FIND_ITEM);
580 				goto asknum_d; /* ask again */
581 			}
582 
583 			delete_playlist(p);
584 			free(s);
585 			break;
586 		}
587 		/* MOVE ITEM */
588 		case 'm':
589 		{
590 			int from, to;
591 
592 			free(s);
593 			fputs("move item number [first item is 0; press ENTER to cancel]? ", stdout);
594 			fflush(stdout);
595 			s = ReadString();
596 			if (s == NULL) {
597 				error("%s", MESSAGE_NO_MEMORY);
598 				break;
599 			}
600 			if (*s == '\0') {
601 				free(s);
602 				break;
603 			}
604 			from = atoi(s);
605 			free(s);
606 
607 			fputs("new position for the item [start at 0; ENTER to cancel]? ", stdout);
608 			fflush(stdout);
609 			s = ReadString();
610 			if (s == NULL) {
611 				error("%s", MESSAGE_NO_MEMORY);
612 				continue;
613 			}
614 			if (*s == '\0') {
615 				free(s);
616 				continue;
617 			}
618 
619 			to = atoi(s);
620 			ErrorDispatch(move_playlist(from, to), s);
621 			free(s);
622 			break;
623 		}
624 		/* PRINT PLAYLIST */
625 		case 'v':
626 			free(s);
627 			print_list();
628 			break;
629 		}
630 	}
631 	return;
632 }
633 
634 EXPORT char *
DetermineType()635 DetermineType()
636 {
637 	int type;
638 
639 	type = ModPlug_GetModuleType(file.mod);
640 	switch (type) {
641 	case MOD_TYPE_MOD:
642 		return "Amiga Module (MOD)";
643 		break;
644 	case MOD_TYPE_S3M:
645 		return "Scream Tracker 3 (S3M)";
646 		break;
647 	case MOD_TYPE_XM:
648 		return "Extended Module (XM)";
649 		break;
650 	case MOD_TYPE_MED:
651 		return "OctaMED (MED)";
652 		break;
653 	case MOD_TYPE_MTM:
654 		return "MultiTracker (MTM)";
655 		break;
656 	case MOD_TYPE_IT:
657 		return "Impulse Tracker (IT)";
658 		break;
659 	case MOD_TYPE_669:
660 		return "Composer 669 (669)";
661 		break;
662 	case MOD_TYPE_ULT:
663 		return "ULT";
664 		break;
665 	case MOD_TYPE_STM:
666 		return "Scream Tracker (STM)";
667 		break;
668 	case MOD_TYPE_FAR:
669 		return "Farandole (FAR)";
670 		break;
671 	case MOD_TYPE_WAV:
672 		return "Wave Audio (WAV)";
673 		break;
674 	case MOD_TYPE_AMF:
675 		return "AMF";
676 		break;
677 	case MOD_TYPE_AMS:
678 		return "AMS";
679 		break;
680 	case MOD_TYPE_DSM:
681 		return "DSM";
682 		break;
683 	case MOD_TYPE_MDL:
684 		return "MDL";
685 		break;
686 	case MOD_TYPE_OKT:
687 		return "OKT";
688 		break;
689 	case MOD_TYPE_MID:
690 		return "MIDI (MID)";
691 		break;
692 	case MOD_TYPE_DMF:
693 		return "DMF";
694 		break;
695 	case MOD_TYPE_PTM:
696 		return "PTM";
697 		break;
698 	case MOD_TYPE_DBM:
699 		return "DBM";
700 		break;
701 	case MOD_TYPE_MT2:
702 		return "MT2";
703 		break;
704 	case MOD_TYPE_AMF0:
705 		return "AMF";
706 		break;
707 	case MOD_TYPE_PSM:
708 		return "PSM";
709 		break;
710 	case MOD_TYPE_J2B:
711 		return "J2B";
712 		break;
713 	case MOD_TYPE_UMX:
714 		return "Unreal Pack (UMX)";
715 		break;
716 	default:
717 		return "Unknown";
718 	}
719 }
720 
721 
722