fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/utils/pti/main.c *
7 * Created: 2020-04-25 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2020-2023 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include "main.h"
24#include "comment.h"
25#include "ops.h"
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30
31#include <lib/getopt.h>
32
33#include <drivers/pti/pti.h>
34#include <drivers/pti/pti-io.h>
35
36
37int pti_info (pti_img_t *img);
38
39
40const char *arg0 = NULL;
41
42int par_verbose = 0;
43
44int par_print_info = 0;
45
46unsigned par_fmt_inp = PTI_FORMAT_NONE;
47unsigned par_fmt_out = PTI_FORMAT_NONE;
48
49unsigned long par_default_clock = PTI_CLOCK_C64_PAL;
50
51
52static pce_option_t opts[] = {
53 { '?', 0, "help", NULL, "Print usage information" },
54 { 'c', 1, "default-clock","clock", "Set the default clock [c64-pal]" },
55 { 'f', 0, "info", NULL, "Print image information [no]" },
56 { 'i', 1, "input", "filename", "Load an input file" },
57 { 'I', 1, "input-format", "format", "Set the input format [auto]" },
58 { 'm', 1, "cat", "filename", "Concatenate an image" },
59 { 'o', 1, "output", "filename", "Set the output file name [none]" },
60 { 'O', 1, "output-format", "format", "Set the output format [auto]" },
61 { 'p', 1, "operation", "name [...]", "Perform an operation" },
62 { 's', 2, "set", "par val", "Set a parameter value" },
63 { 'v', 0, "verbose", NULL, "Verbose operation [no]" },
64 { 'V', 0, "version", NULL, "Print version information" },
65 { -1, 0, NULL, NULL, NULL }
66};
67
68
69static
70void print_help (void)
71{
72 pce_getopt_help (
73 "pti: convert and modify PCE Tape Image files",
74 "usage: pti [options] [input] [options] [output]",
75 opts
76 );
77
78 fputs (
79 "\n"
80 "operations are:\n"
81 " cat <file> Add an image to the end of the current image\n"
82 " comment-add text Add to the image comment\n"
83 " comment-load filename Load the image comment from a file\n"
84 " comment-print Print the image comment\n"
85 " comment-save filename Save the image comment to a file\n"
86 " comment-set text Set the image comment\n"
87 " duty-cycle <low> <high> Adjust the duty cycles\n"
88 " decode <type> <file> Decode tracks\n"
89 " encode <type> <file> Encode a file\n"
90 " fix-clock <clock> Set the sample clock without modifying the image\n"
91 " info Print image information\n"
92 " invert Invert the amplitude\n"
93 " new Create a new image\n"
94 " scale <factor> Scale the image by a factor\n"
95 " set-clock <clock> Set the sample clock rate\n"
96 " space-add-left <time> Add space at the beginning\n"
97 " space-add-right <time> Add space at the end\n"
98 " space-add <time> Add space at the beginning and end\n"
99 " space-auto <mintime> Convert constant levels to silence\n"
100 " space-max <time> Limit the length of silence periods\n"
101 " space-trim-left Remove space from the beginning\n"
102 " space-trim-right Remove space from the end\n"
103 " space-trim Remove space from the beginning and end\n"
104 " trim-left <time> Cut time from the beginning\n"
105 " trim-right <time> Cut time from the end\n"
106 "\n"
107 "sample clocks are:\n"
108 " c16-ntsc, c16-pal, c64-ntsc, c64-pal, pc-pic, pc-cpu, pet,\n"
109 " vic20-ntsc, vic20-pal, <int>\n"
110 "\n"
111 "parameters are:\n"
112 " default-clock, pcm-srate,\n"
113 " wav-bits, wav-lowpass, wav-lowpass-order, wav-srate\n"
114 "\n"
115 "decode types are:\n"
116 " cbm-t64, cbm-raw\n"
117 "\n"
118 "encode types are:\n"
119 " cbm-bas, cbm-prg\n"
120 "\n"
121 "file formats are:\n"
122 " cas, pcm, pti, t64(r), tap, txt, wav\n",
123 stdout
124 );
125
126 fflush (stdout);
127}
128
129static
130void print_version (void)
131{
132 fputs (
133 "pti version " PCE_VERSION_STR
134 "\n\n"
135 "Copyright (C) 2020-" PCE_YEAR " Hampa Hug <hampa@hampa.ch>\n",
136 stdout
137 );
138
139 fflush (stdout);
140}
141
142int pti_parse_double (const char *str, double *val)
143{
144 char *end;
145
146 if ((str == NULL) || (*str == 0)) {
147 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
148 return (1);
149 }
150
151 *val = strtod (str, &end);
152
153 if ((end != NULL) && (*end == 0)) {
154 return (0);
155 }
156
157 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
158
159 return (1);
160}
161
162int pti_parse_long (const char *str, long *val)
163{
164 char *end;
165
166 if ((str == NULL) || (*str == 0)) {
167 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
168 return (1);
169 }
170
171 *val = strtol (str, &end, 0);
172
173 if ((end != NULL) && (*end == 0)) {
174 return (0);
175 }
176
177 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
178
179 return (1);
180}
181
182int pti_parse_ulong (const char *str, unsigned long *val)
183{
184 char *end;
185
186 if ((str == NULL) || (*str == 0)) {
187 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
188 return (1);
189 }
190
191 *val = strtoul (str, &end, 0);
192
193 if ((end != NULL) && (*end == 0)) {
194 return (0);
195 }
196
197 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
198
199 return (1);
200}
201
202int pti_parse_uint (const char *str, unsigned *val)
203{
204 unsigned long tmp;
205
206 if (pti_parse_ulong (str, &tmp)) {
207 return (1);
208 }
209
210 *val = tmp;
211
212 return (0);
213}
214
215int pti_parse_bool (const char *str, int *val)
216{
217 unsigned long tmp;
218
219 if (pti_parse_ulong (str, &tmp)) {
220 return (1);
221 }
222
223 *val = tmp != 0;
224
225 return (0);
226}
227
228static
229int pti_parse_clock (const char *str, unsigned long *val)
230{
231 if (strcmp (str, "pet") == 0) {
232 *val = PTI_CLOCK_PET;
233 }
234 else if (strcmp (str, "vic20-ntsc") == 0) {
235 *val = PTI_CLOCK_VIC20_NTSC;
236 }
237 else if (strcmp (str, "vic20-pal") == 0) {
238 *val = PTI_CLOCK_VIC20_PAL;
239 }
240 else if (strcmp (str, "c64-ntsc") == 0) {
241 *val = PTI_CLOCK_C64_NTSC;
242 }
243 else if (strcmp (str, "c64-pal") == 0) {
244 *val = PTI_CLOCK_C64_PAL;
245 }
246 else if (strcmp (str, "c16-ntsc") == 0) {
247 *val = PTI_CLOCK_C16_NTSC;
248 }
249 else if (strcmp (str, "c16-pal") == 0) {
250 *val = PTI_CLOCK_C16_PAL;
251 }
252 else if ((strcmp (str, "pc-pit") == 0) || (strcmp (str, "pc") == 0)) {
253 *val = PTI_CLOCK_PC_PIT;
254 }
255 else if (strcmp (str, "pc-cpu") == 0) {
256 *val = PTI_CLOCK_PC_CPU;
257 }
258 else {
259 return (1);
260 }
261
262 return (0);
263}
264
265int pti_parse_freq (const char *str, unsigned long *val)
266{
267 char *end;
268
269 if ((str == NULL) || (*str == 0)) {
270 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
271 return (1);
272 }
273
274 if (pti_parse_clock (str, val) == 0) {
275 return (0);
276 }
277
278 *val = strtoul (str, &end, 0);
279
280 if ((end == NULL) || (*end == 0)) {
281 return (0);
282 }
283
284 if ((*end == 'k') || (*end == 'K')) {
285 *val *= 1000;
286 end += 1;
287 }
288 else if ((*end == 'm') || (*end == 'M')) {
289 *val *= 1000000;
290 end += 1;
291 }
292
293 if (*end != 0) {
294 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
295 return (1);
296 }
297
298 return (0);
299}
300
301int pti_parse_time_sec (const char *str, double *sec)
302{
303 char *end;
304
305 if ((str == NULL) || (*str == 0)) {
306 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
307 return (1);
308 }
309
310 *sec = strtod (str, &end);
311
312 if ((end == NULL) || (*end == 0)) {
313 return (0);
314 }
315
316 if (strcmp (end, "s") == 0) {
317 ;
318 }
319 else if (strcmp (end, "ms") == 0) {
320 *sec /= 1000.0;
321 }
322 else if (strcmp (end, "us") == 0) {
323 *sec /= 1000000.0;
324 }
325 else {
326 fprintf (stderr, "%s: bad value (%s)\n", arg0, str);
327 return (1);
328 }
329
330 return (0);
331}
332
333int pti_parse_time_clk (const char *str, unsigned long *sec, unsigned long *clk, unsigned long clock)
334{
335 double tmp;
336
337 if (pti_parse_time_sec (str, &tmp)) {
338 return (1);
339 }
340
341 pti_time_set (sec, clk, tmp, clock);
342
343 return (0);
344}
345
346static
347int pti_set_format (const char *name, unsigned *val)
348{
349 if (strcmp (name, "cas") == 0) {
350 *val = PTI_FORMAT_CAS;
351 }
352 else if (strcmp (name, "pcm") == 0) {
353 *val = PTI_FORMAT_PCM;
354 }
355 else if (strcmp (name, "pti") == 0) {
356 *val = PTI_FORMAT_PTI;
357 }
358 else if (strcmp (name, "tap") == 0) {
359 *val = PTI_FORMAT_TAP;
360 }
361 else if (strcmp (name, "txt") == 0) {
362 *val = PTI_FORMAT_TXT;
363 }
364 else if (strcmp (name, "wav") == 0) {
365 *val = PTI_FORMAT_WAV;
366 }
367 else {
368 fprintf (stderr, "%s: unknown format (%s)\n", arg0, name);
369 *val = PTI_FORMAT_NONE;
370 return (1);
371 }
372
373 return (0);
374}
375
376pti_img_t *pti_load_image (const char *fname)
377{
378 pti_img_t *img;
379
380 if (par_verbose) {
381 fprintf (stderr, "%s: load image from %s\n", arg0, fname);
382 }
383
384 if (strcmp (fname, "-") == 0) {
385 img = pti_img_load_fp (stdin, par_fmt_inp);
386 }
387 else {
388 img = pti_img_load (fname, par_fmt_inp);
389 }
390
391 if (img == NULL) {
392 fprintf (stderr, "%s: loading failed (%s)\n", arg0, fname);
393 return (NULL);
394 }
395
396 if (par_print_info) {
397 par_print_info = 0;
398 pti_info (img);
399 }
400
401 return (img);
402}
403
404int pti_save_image (const char *fname, pti_img_t **img)
405{
406 int r;
407
408 if (*img == NULL) {
409 *img = pti_img_new();
410 }
411
412 if (*img == NULL) {
413 return (1);
414 }
415
416 if (par_verbose) {
417 fprintf (stderr, "%s: save image to %s\n", arg0, fname);
418 }
419
420 if (strcmp (fname, "-") == 0) {
421 r = pti_img_save_fp (stdout, *img, par_fmt_out);
422 }
423 else {
424 r = pti_img_save (fname, *img, par_fmt_out);
425 }
426
427 if (r) {
428 fprintf (stderr, "%s: saving failed (%s)\n",
429 arg0, fname
430 );
431
432 return (1);
433 }
434
435 return (0);
436}
437
438static
439int pti_set_parameter (const char *name, const char *val)
440{
441 if (strcmp (name, "default-clock") == 0) {
442 if (pti_parse_freq (val, &par_default_clock)) {
443 return (1);
444 }
445
446 pti_set_default_clock (par_default_clock);
447 }
448 else if (strcmp (name, "pcm-srate") == 0) {
449 unsigned long srate;
450
451 if (pti_parse_freq (val, &srate)) {
452 return (1);
453 }
454
455 pti_pcm_set_default_srate (srate);
456 }
457 else if (strcmp (name, "wav-bits") == 0) {
458 unsigned bits;
459
460 if (pti_parse_uint (val, &bits)) {
461 return (1);
462 }
463
464 pti_wav_set_bits (bits);
465 }
466 else if (strcmp (name, "wav-lowpass") == 0) {
467 unsigned long cutoff;
468
469 if (pti_parse_ulong (val, &cutoff)) {
470 return (1);
471 }
472
473 pti_wav_set_lowpass (cutoff);
474 }
475 else if (strcmp (name, "wav-lowpass-order") == 0) {
476 unsigned order;
477
478 if (pti_parse_uint (val, &order)) {
479 return (1);
480 }
481
482 pti_wav_set_lowpass_order (order);
483 }
484 else if (strcmp (name, "wav-srate") == 0) {
485 unsigned long srate;
486
487 if (pti_parse_freq (val, &srate)) {
488 return (1);
489 }
490
491 pti_wav_set_srate (srate);
492 }
493 else if ((strcmp (name, "wav-minimum-volume") == 0) || (strcmp (name, "wav-vol") == 0)) {
494 unsigned vol;
495
496 if (pti_parse_uint (val, &vol)) {
497 return (1);
498 }
499
500 pti_wav_set_min_volume (vol);
501 }
502 else {
503 return (1);
504 }
505
506 return (0);
507}
508
509int main (int argc, char **argv)
510{
511 int r;
512 char **optarg;
513 pti_img_t *img;
514 const char *out;
515
516 arg0 = argv[0];
517
518 img = NULL;
519 out = NULL;
520
521 while (1) {
522 r = pce_getopt (argc, argv, &optarg, opts);
523
524 if (r == GETOPT_DONE) {
525 break;
526 }
527
528 if (r < 0) {
529 return (1);
530 }
531
532 switch (r) {
533 case '?':
534 print_help();
535 return (0);
536
537 case 'V':
538 print_version();
539 return (0);
540
541 case 'c':
542 if (pti_set_parameter ("default-clock", optarg[0])) {
543 return (1);
544 }
545 break;
546
547 case 'f':
548 if (img != NULL) {
549 pti_info (img);
550 }
551 else {
552 par_print_info = 1;
553 }
554 break;
555
556 case 'i':
557 if (img != NULL) {
558 pti_img_del (img);
559 }
560
561 if ((img = pti_load_image (optarg[0])) == NULL) {
562 return (1);
563 }
564 break;
565
566 case 'I':
567 if (pti_set_format (optarg[0], &par_fmt_inp)) {
568 return (1);
569 }
570 break;
571
572 case 'm':
573 if (pti_cat (&img, optarg[0])) {
574 return (1);
575 }
576 break;
577
578 case 'o':
579 if (pti_save_image (optarg[0], &img)) {
580 return (1);
581 }
582 break;
583
584 case 'O':
585 if (pti_set_format (optarg[0], &par_fmt_out)) {
586 return (1);
587 }
588 break;
589
590 case 'p':
591 if (pti_operation (&img, optarg[0], argc, argv)) {
592 return (1);
593 }
594 break;
595
596 case 's':
597 if (pti_set_parameter (optarg[0], optarg[1])) {
598 fprintf (stderr, "%s: bad parameter (%s=%s)\n",
599 arg0, optarg[0], optarg[1]
600 );
601 return (1);
602 }
603 break;
604
605 case 'v':
606 par_verbose = 1;
607 break;
608
609 case 0:
610 if (img == NULL) {
611 if ((img = pti_load_image (optarg[0])) == NULL) {
612 return (1);
613 }
614 }
615 else {
616 if (pti_save_image (optarg[0], &img)) {
617 return (1);
618 }
619 }
620 break;
621
622 default:
623 return (1);
624 }
625 }
626
627 if (out != NULL) {
628 if (img == NULL) {
629 img = pti_img_new();
630 }
631
632 if (img == NULL) {
633 return (1);
634 }
635
636 if (par_verbose) {
637 fprintf (stderr, "%s: save image to %s\n", arg0, out);
638 }
639
640 r = pti_img_save (out, img, par_fmt_out);
641
642 if (r) {
643 fprintf (stderr, "%s: saving failed (%s)\n",
644 argv[0], out
645 );
646 return (1);
647 }
648 }
649
650 return (0);
651}