1 /*
2     NIBREAD - part of the NIBTOOLS package for 1541/1571 disk image nibbling
3    	by Peter Rittwage <peter(at)rittwage(dot)com>
4     based on code from MNIB, by Dr. Markus Brenner
5 */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <signal.h>
11 #include <time.h>
12 #include <ctype.h>
13 
14 #include "mnibarch.h"
15 #include "gcr.h"
16 #include "nibtools.h"
17 #include "lz.h"
18 
19 int _dowildcard = 1;
20 
21 char bitrate_range[4] = { 43 * 2, 31 * 2, 25 * 2, 18 * 2 };
22 char bitrate_value[4] = { 0x00, 0x20, 0x40, 0x60 };
23 char density_branch[4] = { 0xb1, 0xb5, 0xb7, 0xb9 };
24 
25 BYTE compressed_buffer[(MAX_HALFTRACKS_1541 + 2) * NIB_TRACK_LENGTH];
26 BYTE file_buffer[(MAX_HALFTRACKS_1541 + 2) * NIB_TRACK_LENGTH];
27 BYTE track_buffer[(MAX_HALFTRACKS_1541 + 2) * NIB_TRACK_LENGTH];
28 BYTE track_density[MAX_HALFTRACKS_1541 + 2];
29 BYTE track_alignment[MAX_HALFTRACKS_1541 + 2];
30 size_t track_length[MAX_HALFTRACKS_1541 + 2];
31 
32 size_t error_retries;
33 int file_buffer_size;
34 int reduce_sync, reduce_badgcr, reduce_gap;
35 int fix_gcr;
36 int start_track, end_track, track_inc;
37 int read_killer;
38 int align;
39 int drivetype;
40 int imagetype;
41 int mode;
42 int force_density;
43 int track_match;
44 int gap_match_length;
45 int cap_min_ignore;
46 int interactive_mode;
47 int verbose;
48 int extended_parallel_test;
49 int force_nosync;
50 int ihs;
51 int auto_capacity_adjust;
52 int align_disk;
53 int skew;
54 int rawmode;
55 int rpm_real;
56 int unformat_passes;
57 int capacity_margin;
58 int align_delay;
59 int align_report;
60 int increase_sync = 0;
61 int presync = 0;
62 BYTE fillbyte = 0x55;
63 BYTE drive = 8;
64 char * cbm_adapter = "";
65 int use_floppycode_srq = 0;
66 int override_srq = 0;
67 int extra_capacity_margin=5;
68 int sync_align_buffer=0;
69 int fattrack=0;
70 int old_g64=0;
71 
72 BYTE density_map;
73 float motor_speed;
74 
75 CBM_FILE fd;
76 FILE *fplog;
77 
78 int ARCH_MAINDECL
main(int argc,char * argv[])79 main(int argc, char *argv[])
80 {
81 	int bump, reset, i;
82 	char filename[256], logfilename[256], *dotpos;
83 	char argcache[256];
84 	FILE *fp;
85 
86 	fprintf(stdout,
87 		"\nnibread - Commodore 1541/1571 disk image nibbler\n"
88 		AUTHOR "Revision %d - " VERSION "\n\n", SVN);
89 
90 	/* we can do nothing with no switches */
91 	if (argc < 2)
92 		usage();
93 
94 	/* clear heap buffers */
95 	memset(compressed_buffer, 0x00, sizeof(compressed_buffer));
96 	memset(file_buffer, 0x00, sizeof(file_buffer));
97 	memset(track_buffer, 0x00, sizeof(track_buffer));
98 
99 #ifdef DJGPP
100 	fd = 1;
101 #endif
102 
103 	bump = 1;  /* failing to bump sometimes give us wrong tracks on heavily protected disks */
104 	reset = 1;
105 
106 	start_track = 1 * 2;
107 	end_track = 41 * 2;
108 	track_inc = 2;
109 
110 	reduce_sync = 4;
111 	reduce_badgcr = 0;
112 	reduce_gap = 0;
113 	fix_gcr = 0;
114 	read_killer = 1;
115 	error_retries = 10;
116 	force_density = 0;
117 	track_match = 0;
118 	interactive_mode = 0;
119 	verbose = 1;
120 	extended_parallel_test = 0;
121 	force_nosync = 0;
122 	align = ALIGN_NONE;
123 	gap_match_length = 7;
124 	cap_min_ignore = 0;
125 	ihs = 0;
126 	mode = MODE_READ_DISK;
127 	rpm_real = 296;
128 	align_report = 0;
129 
130 	// cache our arguments for logfile generation
131 	strcpy(argcache, "");
132 	for (i = 0; i < argc; i++)
133 	{
134 		strcat(argcache, argv[i]);
135 		strcat(argcache," ");
136 	}
137 
138 	// parse arguments
139 	while (--argc && (*(++argv)[0] == '-'))
140 	{
141 		switch ((*argv)[1])
142 		{
143 		case '@':
144 			cbm_adapter = &(*argv)[2];
145 			printf("* Using OpenCBM adapter %s\n", cbm_adapter);
146 			break;
147 
148 		case 'P':
149 			printf("* Skip 1571 SRQ Support (Use parallel)\n");
150 			override_srq = 1;
151 			break;
152 
153 		case 's':
154 			break;
155 
156 		case 'j':
157 			printf("* 1541/1571 Index Hole Sensor (SC+ compatible)\n");
158 			Use_SCPlus_IHS = 1;
159 			use_floppycode_ihs = 1; // ihs floppy code!
160 			break;
161 
162 		case 'x':
163 			printf("* Track Alignment Report (1541/1571 SC+ compatible IHS)\n");
164 			track_align_report = 1;
165 			use_floppycode_ihs = 1; // ihs floppy code!
166 			break;
167 
168 		case 'y':
169 			printf("* Deep Bitrate Scan (1541/1571 SC+ compatible IHS)\n");
170 			Deep_Bitrate_SCPlus_IHS = 1;
171 			use_floppycode_ihs = 1; // ihs floppy code!
172 			break;
173 
174 		case 'z':
175 			printf("* Testing 1541/1571 Index Hole Sensor (SC+ compatible)\n");
176 			Test_SCPlus_IHS = 1;
177 			bump = 0; // Don't bump for simple IHS check
178 			use_floppycode_ihs = 1; // ihs floppy code!
179 			break;
180 
181 		case 'A':
182 			align_report = 1;
183 			printf("* Track Alignment Report\n");
184 			break;
185 
186 		case 'h':
187 			track_inc = 1;
188 			end_track = 83;
189 			printf("* Using halftracks\n");
190 			break;
191 
192 		case 'i':
193 			printf("* 1571 or SuperCard-compatible index hole sensor (use only for side 1)\n");
194 			ihs = 1;
195 			break;
196 
197 		case 'V':
198 			track_match = 1;
199 			printf("* Enable track match (low-level verify)\n");
200 			break;
201 
202 		case 'n':
203 			force_nosync = 0;
204 			printf("* Allowing track reads to wait for sync\n");
205 			break;
206 
207 		case 't':
208 			extended_parallel_test = atoi(&(*argv)[2]);
209 			if(!extended_parallel_test)
210 				extended_parallel_test = 100;
211 			printf("* Extended parallel port test loops = %d\n", extended_parallel_test);
212 			break;
213 
214 		case 'I':
215 			interactive_mode = 1;
216 			printf("* Interactive mode\n");
217 			break;
218 
219 		case 'd':
220 			force_density = 1;
221 			printf("* Forcing default density\n");
222 			break;
223 
224 		case 'k':
225 			read_killer = 0;
226 			printf("* Disabling read of 'killer' tracks\n");
227 			break;
228 
229 		case 'S':
230 			if (!(*argv)[2]) usage();
231 			start_track = (BYTE) (2*(atoi(&(*argv)[2])));
232 			printf("* Start track set to %d\n", start_track/2);
233 			break;
234 
235 		case 'E':
236 			if (!(*argv)[2]) usage();
237 			end_track =  (BYTE) (2*(atoi(&(*argv)[2])));
238 			printf("* End track set to %d\n", end_track/2);
239 			break;
240 
241 		case 'D':
242 			if (!(*argv)[2]) usage();
243 			drive = (BYTE) (atoi(&(*argv)[2]));
244 			printf("* Use Device %d\n", drive);
245 			break;
246 
247 		case 'G':
248 			if (!(*argv)[2]) usage();
249 			gap_match_length = atoi(&(*argv)[2]);
250 			printf("* Gap match length set to %d\n", gap_match_length);
251 			break;
252 
253 		case 'v':
254 			verbose++;
255 			printf("* Verbose mode on\n");
256 			break;
257 
258 		case 'e':	// change read retries
259 			if (!(*argv)[2]) usage();
260 			error_retries = atoi(&(*argv)[2]);
261 			printf("* Read retries set to %d\n", error_retries);
262 			break;
263 
264 		case 'm':
265 			printf("* Minimum capacity ignore on\n");
266 			cap_min_ignore = 1;
267 			break;
268 
269 		default:
270 			usage();
271 			break;
272 		}
273 	}
274 	printf("\n");
275 
276 	if(argc < 1) usage();
277 	strcpy(filename, argv[0]);
278 
279 	if( (fp=fopen(filename,"r")) )
280 	{
281 		fclose(fp);
282 		printf("File exists - Overwrite? (y/N)");
283 		if(getchar() != 'y') exit(0);
284 	}
285 
286 #ifdef DJGPP
287 	calibrate();
288 	if (!detect_ports(reset))
289 		return 0;
290 #elif defined(OPENCBM_42)
291 	/* remain compatible with OpenCBM < 0.4.99 */
292 	if (cbm_driver_open(&fd, 0) != 0)
293 	{
294 		printf("Is your X-cable properly configured?\n");
295 		exit(0);
296 	}
297 #else /* assume > 0.4.99 */
298 	if (cbm_driver_open_ex(&fd, cbm_adapter) != 0)
299 	{
300 		printf("Is your X-cable properly configured?\n");
301 		exit(0);
302 	}
303 #endif
304 
305 	/* Once the drive is accessed, we need to close out state when exiting */
306 	atexit(handle_exit);
307 	signal(SIGINT, handle_signals);
308 
309 	if(!init_floppy(fd, drive, bump))
310 	{
311 		printf("Floppy drive initialization failed\n");
312 		exit(0);
313 	}
314 
315 	if(extended_parallel_test)
316 		parallel_test(extended_parallel_test);
317 
318 	if (Test_SCPlus_IHS) // "-z"
319 	{
320 		IHSresult = Check_SCPlus_IHS(fd,0); // 0=TurnIHSoffAfterwards
321 		OutputIHSResult(TRUE,FALSE,IHSresult,NULL);
322 		exit(0);
323 	}
324 
325 	if(align_report)
326 		TrackAlignmentReport(fd);
327 
328 	/* create log file */
329 	strcpy(logfilename, filename);
330 	dotpos = strrchr(logfilename, '.');
331 	if (dotpos != NULL)
332 		*dotpos = '\0';
333 	strcat(logfilename, ".log");
334 
335 	if ((fplog = fopen(logfilename, "wb")) == NULL)
336 	{
337 		fprintf(stderr, "Couldn't create log file %s!\n", logfilename);
338 		exit(2);
339 	}
340 
341 	fprintf(fplog, "%s\n", VERSION);
342 	fprintf(fplog, "'%s'\n", argcache);
343 
344 	if(strrchr(filename, '.') == NULL)  strcat(filename, ".nbz");
345 
346 	if((compare_extension(filename, "D64")) || (compare_extension(filename, "G64")))
347 	{
348 		printf("\nDisk imaging only directly supports NIB, NB2, and NBZ formats.\n");
349 		printf("Use nibconv after imaging to convert to desired file type.\n");
350 		exit(0);
351 	}
352 
353 	if (Use_SCPlus_IHS) // "-j"
354 	{
355 		IHSresult = Check_SCPlus_IHS(fd,1); // 1=KeepOn
356 		OutputIHSResult(TRUE,TRUE,IHSresult,fplog);
357 		if (IHSresult != 0)
358 		{
359 			// turn SCPlus IHS off
360 			send_mnib_cmd(fd, FL_IHS_OFF, NULL, 0);
361 			burst_read(fd);
362 
363 			if (fplog) fclose(fplog);
364 			exit(0);
365 		}
366 	}
367 
368 
369 	if (Deep_Bitrate_SCPlus_IHS) // "-y"
370 	{
371 		// We need some memory for the Deep Bitrate Scan
372 		if(!(logline = malloc(0x10000)))
373 		{
374 			printf("Error: Could not allocate memory for Deep Bitrate Scan buffer.\n");
375 			exit(0);
376 		}
377 		DeepBitrateAnalysis(fd,filename,track_buffer,logline);
378 	}
379 	else if (track_align_report) // "-x"
380 		TrackAlignmentReport2(fd,track_buffer);
381 	else
382 	{
383 		if(!(disk2file(fd, filename)))
384 			printf("Operation failed!\n");
385 	}
386 
387 
388 	if (Use_SCPlus_IHS) // "-j"
389 	{
390 		// turn SCPlus IHS off
391 		send_mnib_cmd(fd, FL_IHS_OFF, NULL, 0);
392 		burst_read(fd);
393 	}
394 
395 	motor_on(fd);
396 	step_to_halftrack(fd, 18*2);
397 
398 	if(fplog) fclose(fplog);
399 
400 	exit(0);
401 }
402 
parallel_test(int iterations)403 void parallel_test(int iterations)
404 {
405 	int i;
406 
407 	printf("Performing extended parallel port test\n");
408 	for(i=0; i<iterations; i++)
409 	{
410 		if(!verify_floppy(fd))
411 		{
412 			printf("Parallel port failed extended testing.  Check wiring and sheilding.\n");
413 			exit(0);
414 		}
415 		printf(".");
416 	}
417 	printf("\nPassed advanced parallel port test\n");
418 	exit(0);
419 }
420 
disk2file(CBM_FILE fd,char * filename)421 int disk2file(CBM_FILE fd, char *filename)
422 {
423 	int count = 0;
424 	char newfilename[256];
425 	char filenum[4], *dotpos;
426 
427 	/* read data from drive to file */
428 	motor_on(fd);
429 
430 	if(compare_extension(filename, "NB2"))
431 	{
432 		track_inc = 1;
433 		if(!(write_nb2(fd, filename))) return 0;
434 	}
435 	else if(compare_extension(filename, "NIB"))
436 	{
437 		if(!(read_floppy(fd, track_buffer, track_density, track_length))) return 0;
438 		if(!(file_buffer_size = write_nib(file_buffer, track_buffer, track_density, track_length))) return 0;
439 		if(!(save_file(filename, file_buffer, file_buffer_size))) return 0;
440 
441 		if(interactive_mode)
442 		{
443 			for(;;)
444 			{
445 				motor_off(fd);
446 				printf("Swap disk and press a key for next image, or CTRL-C to quit.\n");
447 				getchar();
448 				motor_on(fd);
449 
450 				/* create new filename */
451 				sprintf(filenum, "%d", ++count);
452 				strcpy(newfilename, filename);
453 				dotpos = strrchr(newfilename, '.');
454 				if (dotpos != NULL) *dotpos = '\0';
455 				strcat(newfilename, filenum);
456 				strcat(newfilename, ".nib");
457 
458 				if(!(read_floppy(fd, track_buffer, track_density, track_length))) return 0;
459 				if(!(file_buffer_size = write_nib(file_buffer, track_buffer, track_density, track_length))) return 0;
460 				if(!(save_file(newfilename, file_buffer, file_buffer_size))) return 0;
461 			}
462 		}
463 	}
464 	else
465 	{
466 		if(!(read_floppy(fd, track_buffer, track_density, track_length))) return 0;
467 		if(!(file_buffer_size = write_nib(file_buffer, track_buffer, track_density, track_length))) return 0;
468 		if(!(file_buffer_size = LZ_CompressFast(file_buffer, compressed_buffer, file_buffer_size))) return 0;
469 		if(!(save_file(filename, compressed_buffer, file_buffer_size))) return 0;
470 
471 		if(interactive_mode)
472 		{
473 			for(;;)
474 			{
475 				motor_off(fd);
476 				printf("Swap disk and press a key for next image, or CTRL-C to quit.\n");
477 				getchar();
478 				motor_on(fd);
479 
480 				/* create new filename */
481 				sprintf(filenum, "%d", ++count);
482 				strcpy(newfilename, filename);
483 				dotpos = strrchr(newfilename, '.');
484 				if (dotpos != NULL) *dotpos = '\0';
485 				strcat(newfilename, filenum);
486 				strcat(newfilename, ".nbz");
487 
488 				if(!(read_floppy(fd, track_buffer, track_density, track_length))) return 0;
489 				if(!(file_buffer_size = write_nib(file_buffer, track_buffer, track_density, track_length))) return 0;
490 				if(!(file_buffer_size = LZ_CompressFast(file_buffer, compressed_buffer, file_buffer_size))) return 0;
491 				if(!(save_file(newfilename, compressed_buffer, file_buffer_size))) return 0;
492 			}
493 		}
494 	}
495 
496 	return 1;
497 }
498 
499 void
usage(void)500 usage(void)
501 {
502 	fprintf(stderr, "usage: nibread [options] <filename>\n\n"
503 		 " -@x: Use OpenCBM device 'x' (xa1541, xum1541:0, xum1541:1, etc.)\n"
504 	     " -D[n]: Use drive #[n]\n"
505 	     " -e[n]: Retry reading tracks with errors [n] times\n"
506 	     " -S[n]: Override starting track\n"
507 	     " -E[n]: Override ending track\n"
508 	     " -G[n]: Match track gap by [n] number of bytes (advanced users only)\n"
509 	     " -s: Use SRQ transfer code instead of parallel (1571 only)\n"
510 	     " -k: Disable reading of 'killer' tracks\n"
511 	     " -d: Force default densities\n"
512 	     " -v: Enable track matching (crude read verify)\n"
513 	     " -I: Interactive imaging mode\n"
514 //	     " -m: Disable minimum capacity check\n"
515 	     " -V: Verbose (output more detailed track data)\n"
516 	     " -h: Read halftracks\n"
517 	     " -t: Extended parallel port tests\n"
518 	     " -j: Use Index Hole Sensor  (1541/1571 SC+ compatible IHS)\n"
519 	     " -x: Track Alignment Report (1541/1571 SC+ compatible IHS)\n"
520 	     " -y: Deep Bitrate Analysis  (1541/1571 SC+ compatible IHS)\n"
521 	     " -z: Test Index Hole Sensor (1541/1571 SC+ compatible IHS)\n"
522 	     );
523 	exit(1);
524 }
525