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, &sector_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, &sector_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