OpenCBM
d64copy/main.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 1999-2001 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8  * Copyright 2004-2007 Spiro Trikaliotis
9 */
10 
11 #include "opencbm.h"
12 #include "d64copy.h"
13 
14 #include "arch.h"
15 #include "libmisc.h"
16 
17 #include <getopt.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 
24 /* setable via command line */
25 static d64copy_severity_e verbosity = sev_warning;
26 static int no_progress = 0;
27 
28 /* other globals */
29 static CBM_FILE fd_cbm;
30 
31 
32 static int is_cbm(char *name)
33 {
34  return((strcmp(name, "8" ) == 0) || (strcmp(name, "9" ) == 0) ||
35  (strcmp(name, "10") == 0) || (strcmp(name, "11") == 0) );
36 }
37 
38 
39 static void help()
40 {
41  printf(
42 "Usage: d64copy [OPTION]... [SOURCE] [TARGET]\n"
43 "Copy .d64 disk images to a CBM-1541 or compatible drive and vice versa\n"
44 "\n"
45 "Options:\n"
46 " -h, --help display this help and exit\n"
47 " -V, --version display version information and exit\n"
48 " -@, --adapter=plugin:bus tell OpenCBM which backend plugin and bus to use\n"
49 " -q, --quiet quiet output\n"
50 " -v, --verbose control verbosity (repeatedly, up to 3 times)\n"
51 " -n, --no-progress do not display progress information\n"
52 "\n"
53 " -s, --start-track=TRACK set start track\n"
54 " -e, --end-track=TRACK set end track (start <= end <= 42/70)\n"
55 "\n"
56 " -t, --transfer=TRANSFER set transfermode; valid modes:\n"
57 " auto (default)\n"
58 " original (slowest)\n"
59 " serial1 or s1\n"
60 " serial2 or s2\n"
61 " parallel (fastest)\n"
62 " (can be abbreviated, if unambiguous)\n"
63 " `original' and `serial1' should work in any case;\n"
64 " `serial2' won't work if more than one device is\n"
65 " connected to the IEC bus;\n"
66 " `parallel' needs a XP1541/XP1571 cable in addition\n"
67 " to the serial one.\n"
68 " `auto' tries to determine the best option.\n"
69 "\n"
70 " -i, --interleave=VALUE set interleave value; ignored when reading with\n"
71 " warp mode; default values are:\n"
72 "\n"
73 " original 16\n"
74 "\n"
75 " turbo r/w warp write\n"
76 " serial1 4 6\n"
77 " serial2 13 12\n"
78 " parallel 7 4\n"
79 "\n"
80 " INTERLEAVE is ignored when reading with warp mode;\n"
81 " if data transfer is very slow, increasing this\n"
82 " value may help.\n"
83 "\n"
84 " -w, --warp enable warp mode; this is not possible if\n"
85 " TRANSFER is set to `original'\n"
86 " This is the default if transfer is not `original'.\n"
87 "\n"
88 " --no-warp disable warp mode; this is the default if\n"
89 " TRANSFER is set to `original'.\n"
90 "\n"
91 " -b, --bam-only BAM-only copy; only allocated blocks are copied;\n"
92 " for extended tracks (36-40), SpeedDOS BAM format\n"
93 " is assumed. Use with caution.\n"
94 "\n"
95 " -B, --bam-save save BAM-only copy; this is like the `-b' option\n"
96 " but copies always the entire directory track.\n"
97 "\n"
98 " -d, --drive-type=TYPE specify drive type:\n"
99 " 0 or 1541 = 1541\n"
100 " 1 or 1571 = 1570/1571\n"
101 "\n"
102 " -r, --retry-count=COUNT set retry count\n"
103 "\n"
104 " -E, --error-map=WHEN control whether the error map is appended.\n"
105 " possible values for WHEN are (abbreviations\n"
106 " available):\n"
107 " always\n"
108 " on_errors (default)\n"
109 " never\n"
110 "\n"
111 " -2, --two-sided two-sided disk transfer (.d71): Requires 1571.\n"
112 " Warp mode is not available for .d71 images.\n"
113 "\n"
114 );
115 }
116 
117 static void hint(char *s)
118 {
119  fprintf(stderr, "Try `%s' --help for more information.\n", s);
120 }
121 
122 static void my_message_cb(int severity, const char *format, ...)
123 {
124  va_list args;
125 
126  static const char *severities[4] =
127  {
128  "Fatal",
129  "Warning",
130  "Info",
131  "Debug"
132  };
133 
134  if(verbosity >= severity)
135  {
136  fprintf(stderr, "[%s] ", severities[severity]);
137  va_start(args, format);
138  vfprintf(stderr, format, args);
139  va_end(args);
140  fprintf(stderr, "\n");
141  }
142 }
143 
144 static int my_status_cb(d64copy_status status)
145 {
146  static char trackmap[MAX_SECTORS+1];
147  static int last_track;
148  char *s;
149  char *d;
150 
151  static const char bs2char[] =
152  {
153  ' ', '.', '-', '?', '*'
154  };
155 
156  if(status.track == 0)
157  {
158  last_track = 0;
159  return 0;
160  }
161 
162  if(no_progress)
163  {
164  return 0;
165  }
166 
167  if(last_track != status.track)
168  {
169  if(last_track)
170  {
171  printf("\r%2d: %-24s \n", last_track, trackmap);
172  }
173 
174  for(s = status.bam[status.track-1], d = trackmap; *s; s++, d++)
175  {
176  *d = bs2char[(int)*s];
177  }
178  *d = '\0';
179  last_track = status.track;
180  }
181 
182  trackmap[status.sector] =
183  bs2char[(status.read_result ||
184  status.write_result) ? bs_error : bs_copied];
185 
186  printf("\r%2d: %-24s%3d%% %4d/%d", status.track, trackmap,
187  100 * status.sectors_processed / status.total_sectors,
188  status.sectors_processed, status.total_sectors);
189 
190  fflush(stdout);
191  return 0;
192 }
193 
194 
195 static void ARCH_SIGNALDECL reset(int dummy)
196 {
197  CBM_FILE fd_cbm_local;
198 
199  /*
200  * remember fd_cbm, and make the global one invalid
201  * so that no routine can call a cbm_...() routine
202  * once we have cancelled another one
203  */
204  fd_cbm_local = fd_cbm;
205  fd_cbm = CBM_FILE_INVALID;
206 
207  fprintf(stderr, "\nSIGINT caught X-( Resetting IEC bus...\n");
208 #ifdef LIBD64COPY_DEBUG
209  printDebugLibD64Counters(my_message_cb);
210 #endif
211  d64copy_cleanup();
212  cbm_reset(fd_cbm_local);
213  cbm_driver_close(fd_cbm_local);
214  exit(1);
215 }
216 
217 int ARCH_MAINDECL main(int argc, char *argv[])
218 {
219  d64copy_settings *settings = d64copy_get_default_settings();
220 
221  char *tm = NULL;
222  char *src_arg;
223  char *dst_arg;
224  char *adapter = NULL;
225 
226  int option;
227  int rv = 1;
228  int l;
229 
230  int src_is_cbm;
231  int dst_is_cbm;
232 
233  struct option longopts[] =
234  {
235  { "help" , no_argument , NULL, 'h' },
236  { "version" , no_argument , NULL, 'V' },
237  { "adapter" , required_argument, NULL, '@' },
238  { "warp" , no_argument , NULL, 'w' },
239  { "no-warp" , no_argument , &settings->warp, 0 },
240  { "quiet" , no_argument , NULL, 'q' },
241  { "verbose" , no_argument , NULL, 'v' },
242  { "no-progress", no_argument , NULL, 'n' },
243  { "interleave" , required_argument, NULL, 'i' },
244  { "start-track", required_argument, NULL, 's' },
245  { "end-track" , required_argument, NULL, 'e' },
246  { "transfer" , required_argument, NULL, 't' },
247  { "bam-only" , no_argument , NULL, 'b' },
248  { "bam-save" , no_argument , NULL, 'B' },
249  { "drive-type" , required_argument, NULL, 'd' },
250  { "retry-count", required_argument, NULL, 'r' },
251  { "two-sided" , no_argument , NULL, '2' },
252  { "error-map" , required_argument, NULL, 'E' },
253  { NULL , 0 , NULL, 0 }
254  };
255 
256  const char shortopts[] ="hVwqbBt:i:s:e:d:r:2vnE:@:";
257 
258  while((option = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
259  {
260  switch(option)
261  {
262  case 'h': help();
263  return 0;
264  case 'V': printf("d64copy %s\n", OPENCBM_VERSION);
265  return 0;
266  case 'w': settings->warp = 1;
267  break;
268  case 'q': if(verbosity > 0) verbosity--;
269  break;
270  case 'v': verbosity++;
271  break;
272  case 'n': no_progress = 1;
273  break;
274  case 'i': settings->interleave = arch_atoc(optarg);
275  break;
276  case 's': settings->start_track = atoi(optarg);
277  break;
278  case 'e': settings->end_track = atoi(optarg);
279  break;
280  case 't': tm = optarg;
281  break;
282  case 'b': settings->bam_mode = bm_allocated;
283  break;
284  case 'B': settings->bam_mode = bm_save;
285  break;
286  case 'd': if(strcmp(optarg, "1541") == 0)
287  {
288  settings->drive_type = cbm_dt_cbm1541;
289  }
290  else if(strcmp(optarg, "1571") == 0)
291  {
292  settings->drive_type = cbm_dt_cbm1571;
293  }
294  else if(strcmp(optarg, "1570") == 0)
295  {
296  settings->drive_type = cbm_dt_cbm1570;
297  }
298  else
299  {
300  settings->drive_type = atoi(optarg) != 0 ?
302  }
303  break;
304  case 'r': settings->retries = atoi(optarg);
305  break;
306  case '2': settings->two_sided = 1;
307  break;
308  case 'E': l = strlen(optarg);
309  if(strncmp(optarg, "always", l) == 0)
310  {
311  settings->error_mode = em_always;
312  }
313  else if(strncmp(optarg, "on_errors", l) == 0)
314  {
315  settings->error_mode = em_on_error;
316  }
317  else if(strncmp(optarg, "never", l) == 0)
318  {
319  settings->error_mode = em_never;
320  }
321  else
322  {
323  hint(argv[0]);
324  return 1;
325  }
326  break;
327  case '@': if (adapter == NULL)
328  adapter = cbmlibmisc_strdup(optarg);
329  else
330  {
331  my_message_cb(sev_fatal, "--adapter/-@ given more than once.");
332  hint(argv[0]);
333  exit(1);
334  }
335  break;
336  case 0: break; // needed for --no-warp
337  default : hint(argv[0]);
338  return 1;
339  }
340  }
341 
342  settings->transfer_mode = d64copy_get_transfer_mode_index(tm);
343  if(settings->transfer_mode < 0)
344  {
345  char *modes = d64copy_get_transfer_modes();
346  char *m;
347 
348  fprintf(stderr, "Unknown transfer mode: %s\nAvailable modes:\n", tm);
349 
350  for(m = modes; *m; m+=(strlen(m)+1))
351  {
352  fprintf(stderr, " %s\n", m);
353  }
354 
355  free(modes);
356  return 1;
357  }
358 
359  my_message_cb(3, "transfer mode is %d", settings->transfer_mode );
360 
361  if(optind + 2 != argc)
362  {
363  fprintf(stderr, "Usage: %s [OPTION]... [SOURCE] [TARGET]\n", argv[0]);
364  hint(argv[0]);
365  return 1;
366  }
367 
368  src_arg = argv[optind];
369  dst_arg = argv[optind+1];
370 
371  src_is_cbm = is_cbm(src_arg);
372  dst_is_cbm = is_cbm(dst_arg);
373 
374  if(src_is_cbm == dst_is_cbm)
375  {
376  my_message_cb(0, "either source or target must be a CBM drive");
377  return 1;
378  }
379 
380  if(cbm_driver_open_ex(&fd_cbm, adapter) == 0)
381  {
382  /*
383  * If the user specified auto transfer mode, find out
384  * which transfer mode to use.
385  */
386  settings->transfer_mode =
387  d64copy_check_auto_transfer_mode(fd_cbm,
388  settings->transfer_mode,
389  atoi(src_is_cbm ? src_arg : dst_arg));
390 
391  my_message_cb(3, "decided to use transfer mode %d", settings->transfer_mode );
392 
394 
395  if(src_is_cbm)
396  {
397  rv = d64copy_read_image(fd_cbm, settings, atoi(src_arg), dst_arg,
398  my_message_cb, my_status_cb);
399  }
400  else
401  {
402  rv = d64copy_write_image(fd_cbm, settings, src_arg, atoi(dst_arg),
403  my_message_cb, my_status_cb);
404  }
405 
406  if(!no_progress && rv >= 0)
407  {
408  printf("\n%d blocks copied.\n", rv);
409  }
410 
411  cbm_driver_close(fd_cbm);
412  rv = 0;
413  }
414  else
415  {
416  arch_error(0, arch_get_errno(), "%s", cbm_get_driver_name_ex(adapter));
417  }
418 
419  cbmlibmisc_strfree(adapter);
420  free(settings);
421 
422  return rv;
423 }
char * cbmlibmisc_strdup(const char *const OldString)
Duplicate a given string.
Definition: libstring.c:84
void cbmlibmisc_strfree(const char *String)
Free a string.
Definition: libstring.c:172
#define CBM_FILE_INVALID
Definition: opencbm.h:88
const char *CBMAPIDECL cbm_get_driver_name_ex(char *Adapter)
Get the name of the driver for a specific parallel port, extended version.
Definition: cbm.c:583
int ARCH_MAINDECL main(int argc, char **argv)
Initialize the xum1541 device This function tries to find and identify the xum1541 device...
Definition: usbcfg.c:38
int CBMAPIDECL cbm_driver_open_ex(CBM_FILE *HandleDevice, char *Adapter)
Opens the driver, extended version.
Definition: cbm.c:683
Definition: getopt.h:94
void CBMAPIDECL cbm_driver_close(CBM_FILE HandleDevice)
Closes the driver.
Definition: cbm.c:768
#define CBM_FILE
Definition: opencbm.h:87
void arch_set_ctrlbreak_handler(ARCH_CTRLBREAK_HANDLER Handler)
Set the Ctrl+C / Ctrl+Break handler.
Definition: ctrlbreak.c:62
int CBMAPIDECL cbm_reset(CBM_FILE HandleDevice)
RESET all devices.
Definition: cbm.c:1209
DLL interface for accessing the driver.
Define makros and functions which account for differences between the different architectures.
Some functions for string handling.