1 /***************************************************************************
2 * *
3 * LIBDSK: General floppy and diskimage access library *
4 * Copyright (C) 2001,2005 John Elliott <seasip.webmaster@gmail.com> *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Library General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Library General Public *
17 * License along with this library; if not, write to the Free *
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
19 * MA 02111-1307, USA *
20 * *
21 ***************************************************************************/
22
23 /* DSKDUMP is meant to image a diskette 'blind' - something like the dump mode
24 * of ANADISK. */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "config.h"
30 #ifdef HAVE_LIBGEN_H
31 # include <libgen.h>
32 #endif
33 #include "libdsk.h"
34 #include "utilopts.h"
35 #include "formname.h"
36 #include <errno.h>
37
38 #ifdef __PACIFIC__
39 # define AV0 "DSKDUMP"
40 #else
41 # ifdef HAVE_BASENAME
42 # define AV0 (basename(argv[0]))
43 # else
44 # define AV0 argv[0]
45 # endif
46 #endif
47
48 static dsk_format_t format = -1;
49 static char *intyp = NULL, *outtyp = NULL;
50 static char *incomp = NULL, *outcomp = NULL;
51 static int inside = -1, outside = -1;
52 static int idstep = 0, odstep = 0;
53 static int retries = 1;
54 static dsk_pcyl_t maxcyl;
55 static dsk_phead_t maxhead = 2;
56 static int first = -1, last = -1;
57
58 int do_copy(char *infile, char *outfile);
59 int dump_cyl(DSK_PDRIVER indr, DSK_GEOMETRY *dg,
60 DSK_PDRIVER outdr, dsk_pcyl_t cyl, dsk_phead_t head);
61
check_numeric(char * arg,int * argc,char ** argv)62 int check_numeric(char *arg, int *argc, char **argv)
63 {
64 int n = find_arg(arg, *argc, argv);
65 unsigned nr;
66
67 if (n < 0) return -1;
68 excise_arg(n, argc, argv);
69 if (n >= *argc || atoi(argv[n]) == 0)
70 {
71 fprintf(stderr, "Syntax error: use '%s nnn' where nnn is nonzero\n", arg);
72 exit(1);
73 }
74 nr = atoi(argv[n]);
75 excise_arg(n, argc, argv);
76
77 return nr;
78 }
79
report(const char * s)80 static void report(const char *s)
81 {
82 fprintf(stderr, "%s\r", s);
83 fflush(stderr);
84 }
85
report_end(void)86 static void report_end(void)
87 {
88 fprintf(stderr, "\r%-79.79s\r", "");
89 fflush(stderr);
90 }
91
92
help(int argc,char ** argv)93 int help(int argc, char **argv)
94 {
95 fprintf(stderr, "Syntax: \n"
96 " %s {options} in-image { out-image }\n",
97 AV0);
98 fprintf(stderr,"\nOptions are:\n"
99 "-itype <type> type of input disc image\n"
100 "-otype <type> type of output disc image\n"
101 " '%s -types' lists valid types.\n"
102 "-iside <side> Force side 0 or side 1 of input\n"
103 "-oside <side> Force side 0 or side 1 of output\n"
104 "-retry <count> Set number of retries on error\n"
105 "-idstep Double-step when reading\n"
106 "-odstep Double-step when writing\n"
107 "-format Force a specified format name\n"
108 " '%s -formats' lists valid formats.\n",
109 AV0, AV0);
110 fprintf(stderr,"\nDefault in-image type is autodetect."
111 "\nDefault out-image type is DSK.\n\n");
112
113 fprintf(stderr, "eg: %s /dev/fd0 myfile1.DSK\n"
114 " %s -side 1 /dev/fd0 myfile2.DSK\n"
115 " %s -md3 /dev/fd0 md3boot.dsk\n"
116 " %s -otype floppy myfile.DSK /dev/fd0\n",
117 AV0, AV0, AV0, AV0);
118 return 1;
119 }
120
121
main(int argc,char ** argv)122 int main(int argc, char **argv)
123 {
124 int stdret;
125
126 stdret = standard_args(argc, argv); if (!stdret) return 0;
127 if (argc < 2) return help(argc, argv);
128 if (find_arg("--help", argc, argv) > 0) return help(argc, argv);
129
130 ignore_arg("-type", 2, &argc, argv);
131 ignore_arg("-side", 2, &argc, argv);
132 ignore_arg("-dstep", 2, &argc, argv);
133 ignore_arg("-comp", 2, &argc, argv);
134
135 intyp = check_type("-itype", &argc, argv);
136 outtyp = check_type("-otype", &argc, argv);
137 incomp = check_type("-icomp", &argc, argv);
138 outcomp = check_type("-ocomp", &argc, argv);
139 inside = check_forcehead("-iside", &argc, argv);
140 outside = check_forcehead("-oside", &argc, argv);
141 retries = check_retry("-retry", &argc, argv);
142 if (present_arg("-idstep", &argc, argv)) idstep = 1;
143 if (present_arg("-odstep", &argc, argv)) odstep = 1;
144 if (!outtyp) outtyp = "dsk";
145 format = check_format("-format", &argc, argv);
146 first = check_numeric("-first", &argc, argv);
147 last = check_numeric("-last", &argc, argv);
148 args_complete(&argc, argv);
149 if (argc < 3) return help(argc, argv);
150 return do_copy(argv[1], argv[2]);
151 }
152
153
154
155 static char *op = "Opening";
156
157
do_copy(char * infile,char * outfile)158 int do_copy(char *infile, char *outfile)
159 {
160 DSK_PDRIVER indr = NULL, outdr = NULL;
161 dsk_err_t e;
162 dsk_pcyl_t cyl;
163 dsk_phead_t head;
164 char *cmt = NULL;
165 DSK_GEOMETRY dg;
166
167 dsk_reportfunc_set(report, report_end);
168
169 e = dsk_open (&indr, infile, intyp, incomp);
170 if (!e) e = dsk_set_retry(indr, retries);
171 if (!e && inside >= 0) e = dsk_set_option(indr, "HEAD", inside);
172 if (!e && idstep) e = dsk_set_option(indr, "DOUBLESTEP", 1);
173
174 if (!e) e = dsk_creat(&outdr, outfile, outtyp, outcomp);
175 if (!e && outside >= 0) e = dsk_set_option(outdr, "HEAD", outside);
176 if (!e && odstep) e = dsk_set_option(outdr, "DOUBLESTEP", 1);
177 if (!e) e = dsk_set_retry(outdr, retries);
178
179 /* Try to guess geometry. If this fails the user has to specify an approximate
180 * geometry manually. We need to do this to get a feel for the number of
181 * cylinders to scan; dsk_psecid() will fail if (eg) it tries to select a
182 * 1.4Mb geometry on a 360k drive */
183 if (format == -1)
184 {
185 op = "Identifying disc";
186 if (!e) e = dsk_getgeom(indr, &dg);
187 }
188 else if (!e) e = dg_stdformat(&dg, format, NULL, NULL);
189
190 op = "Scanning disc";
191 if (!e)
192 {
193 maxcyl = dg.dg_cylinders + 4;
194 /* Head is being forced. Do only one side. */
195 if (inside >= 0) maxhead = 1;
196 dsk_get_comment(indr, &cmt);
197 if (outdr) dsk_set_comment(outdr, cmt);
198 if (first < 0) first = 0;
199 if (last < 0) last = maxcyl - 1;
200 for (cyl = first; cyl <= (dsk_pcyl_t)last ; cyl++)
201 {
202 for (head = 0; head < maxhead; head++)
203 {
204 e = dump_cyl(indr, &dg, outdr, cyl, head);
205 if (e) goto abort;
206 }
207 }
208 abort: ;
209 }
210 if (indr) dsk_close(&indr);
211 if (outdr) dsk_close(&outdr);
212 printf("\r%-70.70s\n", "");
213 if (e)
214 {
215 fprintf(stderr, "\n%s: %s\n", op, dsk_strerror(e));
216 return 1;
217 }
218 return 0;
219 }
220
221
222
dump_cyl(DSK_PDRIVER indr,DSK_GEOMETRY * dg,DSK_PDRIVER outdr,dsk_pcyl_t cyl,dsk_phead_t head)223 int dump_cyl(DSK_PDRIVER indr, DSK_GEOMETRY *dg,
224 DSK_PDRIVER outdr, dsk_pcyl_t cyl, dsk_phead_t head)
225 {
226 dsk_err_t err = DSK_ERR_OK;
227 DSK_FORMAT sector_id;
228 DSK_FORMAT *fmt_track;
229 dsk_psect_t osec;
230 dsk_psect_t sector_count;
231 dsk_psect_t secnum;
232 unsigned buflen = 128;
233
234 dg->dg_noskip = 1;
235 /* Guess data rate and recording mode */
236 for (dg->dg_datarate = RATE_HD; dg->dg_datarate <= RATE_ED; ++dg->dg_datarate)
237 {
238 for (dg->dg_fm = RECMODE_MFM;
239 dg->dg_fm <= RECMODE_FM; ++dg->dg_fm)
240 {
241 err = dsk_psecid(indr, dg, cyl, head, §or_id);
242 if (!err) break;
243 }
244 if (!err) break;
245 }
246 if (err)
247 {
248 return DSK_ERR_OK;
249 }
250 /* Now scour the track and print any sector headers we find */
251 err = dsk_ptrackids(indr, dg, cyl, head, §or_count, &fmt_track);
252 if (!err)
253 {
254 for (secnum = 0; secnum < sector_count; secnum++)
255 {
256 sector_id = fmt_track[secnum];
257 if (sector_id.fmt_secsize > buflen)
258 buflen = sector_id.fmt_secsize;
259 }
260 }
261 if (outdr && sector_count)
262 {
263 unsigned char *buf = dsk_malloc(buflen);
264 if (!buf) return DSK_ERR_NOMEM;
265
266 /* Format a track with the correct number and size of sectors */
267 osec = dg->dg_sectors;
268 dg->dg_sectors = sector_count;
269 op = "Formatting";
270 err = dsk_pformat(outdr, dg, cyl, head, fmt_track, 0xE5);
271 dg->dg_sectors = osec;
272 if (!err) for (secnum = 0; secnum < sector_count; secnum++)
273 {
274 int deleted = 0;
275 op = "Reading";
276 printf("Cylinder %02d/%2d Head %d/%d "
277 "Sector (%02d,%d,%03d)/%03d size %04d\r",
278 cyl, last,
279 head, maxhead-1,
280 fmt_track[secnum].fmt_cylinder,
281 fmt_track[secnum].fmt_head,
282 fmt_track[secnum].fmt_sector,
283 sector_count,
284 (int)fmt_track[secnum].fmt_secsize);
285 fflush(stdout);
286 err = dsk_xread(indr, dg, buf, cyl, head,
287 fmt_track[secnum].fmt_cylinder,
288 fmt_track[secnum].fmt_head,
289 fmt_track[secnum].fmt_sector,
290 fmt_track[secnum].fmt_secsize, &deleted);
291 /* XXX Need to be able to write sectors with errors,
292 * at least to CPCEMU DSK */
293 if (err == DSK_ERR_DATAERR) err = DSK_ERR_OK;
294 if (err) break;
295 op = "Writing";
296 err = dsk_xwrite(outdr, dg, buf, cyl, head,
297 fmt_track[secnum].fmt_cylinder,
298 fmt_track[secnum].fmt_head,
299 fmt_track[secnum].fmt_sector,
300 fmt_track[secnum].fmt_secsize, deleted);
301 if (err) break;
302 }
303 dsk_free(buf);
304 if (err) return err;
305 }
306 return DSK_ERR_OK;
307 }
308