1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles,Nicola Salmoria
3 /***************************************************************************
4 
5     romcmp.c
6 
7     ROM comparison utility program.
8 
9 ***************************************************************************/
10 
11 #include "unzip.h"
12 #include "osdcore.h"
13 #include "osdcomm.h"
14 #include "hash.h"
15 
16 #include <cstdarg>
17 #include <cstdlib>
18 
19 
20 #define MAX_FILES 1000
21 
22 #ifndef MAX_FILENAME_LEN
23 #define MAX_FILENAME_LEN 255
24 #endif
25 
26 #ifndef PATH_DELIM
27 #define PATH_DELIM '/'
28 #endif
29 
30 
31 
32 /* compare modes when one file is twice as long as the other */
33 /* A = All file */
34 /* 12 = 1st half */
35 /* 22 = 2nd half */
36 /* E = Even bytes */
37 /* O = Odd bytes */
38 /* E1 = Even bytes 1st half */
39 /* O1 = Odd bytes 1st half */
40 /* E2 = Even bytes 2nd half */
41 /* O2 = Odd bytes 2nd half */
42 enum
43 {
44 	MODE_A,
45 	MODE_NIB1,MODE_NIB2,
46 	MODE_12, MODE_22,
47 	MODE_14, MODE_24, MODE_34, MODE_44,
48 	MODE_E, MODE_O,
49 	MODE_E12, MODE_O12, MODE_E22, MODE_O22,
50 	TOTAL_MODES
51 };
52 
53 static const char *const modenames[] =
54 {
55 	"          ",
56 	"[bits 0-3]",
57 	"[bits 4-7]",
58 	"[1/2]     ",
59 	"[2/2]     ",
60 	"[1/4]     ",
61 	"[2/4]     ",
62 	"[3/4]     ",
63 	"[4/4]     ",
64 	"[even]    ",
65 	"[odd]     ",
66 	"[even 1/2]",
67 	"[odd 1/2] ",
68 	"[even 2/2]",
69 	"[odd 2/2] ",
70 };
71 
compatiblemodes(int mode,int * start,int * end)72 static void compatiblemodes(int mode,int *start,int *end)
73 {
74 	if (mode == MODE_A)
75 	{
76 		*start = MODE_A;
77 		*end = MODE_A;
78 	}
79 	if (mode >= MODE_NIB1 && mode <= MODE_NIB2)
80 	{
81 		*start = MODE_NIB1;
82 		*end = MODE_NIB2;
83 	}
84 	if (mode >= MODE_12 && mode <= MODE_22)
85 	{
86 		*start = MODE_12;
87 		*end = MODE_22;
88 	}
89 	if (mode >= MODE_14 && mode <= MODE_44)
90 	{
91 		*start = MODE_14;
92 		*end = MODE_44;
93 	}
94 	if (mode >= MODE_E && mode <= MODE_O)
95 	{
96 		*start = MODE_E;
97 		*end = MODE_O;
98 	}
99 	if (mode >= MODE_E12 && mode <= MODE_O22)
100 	{
101 		*start = MODE_E12;
102 		*end = MODE_O22;
103 	}
104 }
105 
106 struct fileinfo
107 {
108 	char name[MAX_FILENAME_LEN+1];
109 	int size;
110 	unsigned char *buf; /* file is read in here */
111 	int listed;
112 };
113 
114 static fileinfo files[2][MAX_FILES];
115 static float matchscore[MAX_FILES][MAX_FILES][TOTAL_MODES][TOTAL_MODES];
116 
117 
is_ascii_char(int ch)118 static bool is_ascii_char(int ch)
119 {
120 	return (ch >= 0x20 && ch < 0x7f) || (ch == '\n') || (ch == '\r') || (ch == '\t');
121 }
122 
checkintegrity(const fileinfo * file,int side)123 static void checkintegrity(const fileinfo *file, int side)
124 {
125 	if (file->buf == nullptr) return;
126 
127 	/* check for bad data lines */
128 	unsigned mask0 = 0x0000;
129 	unsigned mask1 = 0xffff;
130 
131 	bool is_ascii = true;
132 	for (unsigned i = 0; i < file->size; i += 2)
133 	{
134 		is_ascii = is_ascii && is_ascii_char(file->buf[i]);
135 		mask0 |= file->buf[i] << 8;
136 		mask1 &= (file->buf[i] << 8) | 0x00ff;
137 		if (i < file->size - 1)
138 		{
139 			is_ascii = is_ascii && is_ascii_char(file->buf[i+1]);
140 			mask0 |= file->buf[i+1];
141 			mask1 &= file->buf[i+1] | 0xff00;
142 		}
143 		if (mask0 == 0xffff && mask1 == 0x0000) break;
144 	}
145 
146 	if (is_ascii && mask0 == 0x7f7f && mask1 == 0)
147 	{
148 		printf("%-23s %-23s ASCII TEXT FILE\n", side ? "" : file->name, side ? file->name : "");
149 		return;
150 	}
151 
152 	if (mask0 != 0xffff || mask1 != 0x0000)
153 	{
154 		int fixedmask;
155 		int bits;
156 
157 		fixedmask = (~mask0 | mask1) & 0xffff;
158 
159 		if (((mask0 >> 8) & 0xff) == (mask0 & 0xff) && ((mask1 >> 8) & 0xff) == (mask1 & 0xff))
160 			bits = 8;
161 		else bits = 16;
162 
163 		printf("%-23s %-23s FIXED BITS (",side ? "" : file->name,side ? file->name : "");
164 		for (int i = 0; i < bits; i++)
165 		{
166 			if (~mask0 & 0x8000) printf("0");
167 			else if (mask1 & 0x8000) printf("1");
168 			else printf("x");
169 
170 			mask0 <<= 1;
171 			mask1 <<= 1;
172 		}
173 		printf(")\n");
174 
175 		/* if the file contains a fixed value, we don't need to do the other */
176 		/* validity checks */
177 		if (fixedmask == 0xffff || fixedmask == 0x00ff || fixedmask == 0xff00)
178 			return;
179 	}
180 
181 	unsigned addrbit = 1;
182 	unsigned addrmirror = 0;
183 	while (addrbit <= file->size/2)
184 	{
185 		unsigned i = 0;
186 		for (i = 0; i < file->size; i++)
187 		{
188 			if ((i ^ addrbit) < file->size && file->buf[i] != file->buf[i ^ addrbit]) break;
189 		}
190 
191 		if (i == file->size)
192 			addrmirror |= addrbit;
193 
194 		addrbit <<= 1;
195 	}
196 
197 	if (addrmirror != 0)
198 	{
199 		if (addrmirror == file->size/2)
200 		{
201 			printf("%-23s %-23s 1ST AND 2ND HALF IDENTICAL\n", side ? "" : file->name, side ? file->name : "");
202 			util::hash_collection hash;
203 			hash.begin();
204 			hash.buffer(file->buf, file->size / 2);
205 			hash.end();
206 			printf("%-23s %-23s                  %s\n", side ? "" : file->name, side ? file->name : "", hash.attribute_string().c_str());
207 		}
208 		else
209 		{
210 			printf("%-23s %-23s BADADDR",side ? "" : file->name,side ? file->name : "");
211 			for (int i = 0; i < 24; i++)
212 			{
213 				if (file->size <= (1<<(23-i))) printf(" ");
214 				else if (addrmirror & 0x800000) printf("-");
215 				else printf("x");
216 				addrmirror <<= 1;
217 			}
218 			printf("\n");
219 		}
220 		return;
221 	}
222 
223 	unsigned sizemask = 1;
224 	while (sizemask < file->size - 1)
225 		sizemask = (sizemask << 1) | 1;
226 
227 	mask0 = 0x000000;
228 	mask1 = sizemask;
229 	for (unsigned i = 0; i < file->size; i++)
230 	{
231 		if (file->buf[i] != 0xff)
232 		{
233 			mask0 |= i;
234 			mask1 &= i;
235 			if (mask0 == sizemask && mask1 == 0x00) break;
236 		}
237 	}
238 
239 	if (mask0 != sizemask || mask1 != 0x00)
240 	{
241 		printf("%-23s %-23s ",side ? "" : file->name,side ? file->name : "");
242 		for (int i = 0; i < 24; i++)
243 		{
244 			if (file->size <= (1<<(23-i))) printf(" ");
245 			else if (~mask0 & 0x800000) printf("1");
246 			else if (mask1 & 0x800000) printf("0");
247 			else printf("x");
248 			mask0 <<= 1;
249 			mask1 <<= 1;
250 		}
251 		printf(" = 0xFF\n");
252 
253 		return;
254 	}
255 
256 
257 	mask0 = 0x000000;
258 	mask1 = sizemask;
259 	for (unsigned i = 0; i < file->size; i++)
260 	{
261 		if (file->buf[i] != 0x00)
262 		{
263 			mask0 |= i;
264 			mask1 &= i;
265 			if (mask0 == sizemask && mask1 == 0x00) break;
266 		}
267 	}
268 
269 	if (mask0 != sizemask || mask1 != 0x00)
270 	{
271 		printf("%-23s %-23s ",side ? "" : file->name,side ? file->name : "");
272 		for (int i = 0; i < 24; i++)
273 		{
274 			if (file->size <= (1<<(23-i))) printf(" ");
275 			else if ((mask0 & 0x800000) == 0) printf("1");
276 			else if (mask1 & 0x800000) printf("0");
277 			else printf("x");
278 			mask0 <<= 1;
279 			mask1 <<= 1;
280 		}
281 		printf(" = 0x00\n");
282 
283 		return;
284 	}
285 
286 	mask0 = 0xff;
287 	for (unsigned i = 0; i < file->size/4 && mask0 != 0x00; i++)
288 	{
289 		if (file->buf[               2*i  ] != 0x00) mask0 &= ~0x01;
290 		if (file->buf[               2*i  ] != 0xff) mask0 &= ~0x02;
291 		if (file->buf[               2*i+1] != 0x00) mask0 &= ~0x04;
292 		if (file->buf[               2*i+1] != 0xff) mask0 &= ~0x08;
293 		if (file->buf[file->size/2 + 2*i  ] != 0x00) mask0 &= ~0x10;
294 		if (file->buf[file->size/2 + 2*i  ] != 0xff) mask0 &= ~0x20;
295 		if (file->buf[file->size/2 + 2*i+1] != 0x00) mask0 &= ~0x40;
296 		if (file->buf[file->size/2 + 2*i+1] != 0xff) mask0 &= ~0x80;
297 	}
298 
299 	if (mask0 & 0x01) printf("%-23s %-23s 1ST HALF = 00xx\n",side ? "" : file->name,side ? file->name : "");
300 	if (mask0 & 0x02) printf("%-23s %-23s 1ST HALF = FFxx\n",side ? "" : file->name,side ? file->name : "");
301 	if (mask0 & 0x04) printf("%-23s %-23s 1ST HALF = xx00\n",side ? "" : file->name,side ? file->name : "");
302 	if (mask0 & 0x08) printf("%-23s %-23s 1ST HALF = xxFF\n",side ? "" : file->name,side ? file->name : "");
303 	if (mask0 & 0x10) printf("%-23s %-23s 2ND HALF = 00xx\n",side ? "" : file->name,side ? file->name : "");
304 	if (mask0 & 0x20) printf("%-23s %-23s 2ND HALF = FFxx\n",side ? "" : file->name,side ? file->name : "");
305 	if (mask0 & 0x40) printf("%-23s %-23s 2ND HALF = xx00\n",side ? "" : file->name,side ? file->name : "");
306 	if (mask0 & 0x80) printf("%-23s %-23s 2ND HALF = xxFF\n",side ? "" : file->name,side ? file->name : "");
307 }
308 
309 
usedbytes(const fileinfo * file,int mode)310 static int usedbytes(const fileinfo *file,int mode)
311 {
312 	switch (mode)
313 	{
314 		case MODE_A:
315 		case MODE_NIB1:
316 		case MODE_NIB2:
317 			return file->size;
318 		case MODE_12:
319 		case MODE_22:
320 		case MODE_E:
321 		case MODE_O:
322 			return file->size / 2;
323 		case MODE_14:
324 		case MODE_24:
325 		case MODE_34:
326 		case MODE_44:
327 		case MODE_E12:
328 		case MODE_O12:
329 		case MODE_E22:
330 		case MODE_O22:
331 			return file->size / 4;
332 		default:
333 			return 0;
334 	}
335 }
336 
basemultmask(const fileinfo * file,int mode,int * base,int * mult,int * mask)337 static void basemultmask(const fileinfo *file,int mode,int *base,int *mult,int *mask)
338 {
339 	*mult = 1;
340 	if (mode >= MODE_E) *mult = 2;
341 
342 	switch (mode)
343 	{
344 		case MODE_A:
345 		case MODE_12:
346 		case MODE_14:
347 		case MODE_E:
348 		case MODE_E12:
349 			*base = 0; *mask = 0xff; break;
350 		case MODE_NIB1:
351 			*base = 0; *mask = 0x0f; break;
352 		case MODE_NIB2:
353 			*base = 0; *mask = 0xf0; break;
354 		case MODE_O:
355 		case MODE_O12:
356 			*base = 1; *mask = 0xff; break;
357 		case MODE_22:
358 		case MODE_E22:
359 			*base = file->size / 2; *mask = 0xff; break;
360 		case MODE_O22:
361 			*base = 1 + file->size / 2; *mask = 0xff; break;
362 		case MODE_24:
363 			*base = file->size / 4; *mask = 0xff; break;
364 		case MODE_34:
365 			*base = 2*file->size / 4; *mask = 0xff; break;
366 		case MODE_44:
367 			*base = 3*file->size / 4; *mask = 0xff; break;
368 	}
369 }
370 
filecompare(const fileinfo * file1,const fileinfo * file2,int mode1,int mode2)371 static float filecompare(const fileinfo *file1,const fileinfo *file2,int mode1,int mode2)
372 {
373 	int i;
374 	int match = 0;
375 	int size1,size2;
376 	int base1=0,base2=0,mult1=0,mult2=0,mask1=0,mask2=0;
377 
378 
379 	if (file1->buf == nullptr || file2->buf == nullptr) return 0.0;
380 
381 	size1 = usedbytes(file1,mode1);
382 	size2 = usedbytes(file2,mode2);
383 
384 	if (size1 != size2) return 0.0;
385 
386 	basemultmask(file1,mode1,&base1,&mult1,&mask1);
387 	basemultmask(file2,mode2,&base2,&mult2,&mask2);
388 
389 	if (mask1 == mask2)
390 	{
391 		if (mask1 == 0xff)
392 		{
393 			/* normal compare */
394 			for (i = 0;i < size1;i++)
395 				if (file1->buf[base1 + mult1 * i] == file2->buf[base2 + mult2 * i]) match++;
396 		}
397 		else
398 		{
399 			/* nibble compare, abort if other half is not empty */
400 			for (i = 0;i < size1;i++)
401 			{
402 				if (((file1->buf[base1 + mult1 * i] & ~mask1) != (0x00 & ~mask1) &&
403 						(file1->buf[base1 + mult1 * i] & ~mask1) != (0xff & ~mask1)) ||
404 					((file2->buf[base1 + mult1 * i] & ~mask2) != (0x00 & ~mask2) &&
405 						(file2->buf[base1 + mult1 * i] & ~mask2) != (0xff & ~mask2)))
406 				{
407 					match = 0;
408 					break;
409 				}
410 				if ((file1->buf[base1 + mult1 * i] & mask1) == (file2->buf[base2 + mult2 * i] & mask2)) match++;
411 			}
412 		}
413 	}
414 
415 	return (float)match / size1;
416 }
417 
418 
readfile(const char * path,fileinfo * file)419 static void readfile(const char *path,fileinfo *file)
420 {
421 	osd_file::error filerr;
422 	uint64_t filesize;
423 	uint32_t actual;
424 	char fullname[256];
425 	osd_file::ptr f;
426 
427 	if (path)
428 	{
429 		char delim[2] = { PATH_DELIM, '\0' };
430 		strcpy(fullname,path);
431 		strcat(fullname,delim);
432 	}
433 	else fullname[0] = 0;
434 	strcat(fullname,file->name);
435 
436 	if ((file->buf = (unsigned char *)malloc(file->size)) == nullptr)
437 	{
438 		printf("%s: out of memory!\n",file->name);
439 		return;
440 	}
441 
442 	filerr = osd_file::open(fullname, OPEN_FLAG_READ, f, filesize);
443 	if (filerr != osd_file::error::NONE)
444 	{
445 		printf("%s: error %d\n", fullname, int(filerr));
446 		return;
447 	}
448 
449 	filerr = f->read(file->buf, 0, file->size, actual);
450 	if (filerr != osd_file::error::NONE)
451 	{
452 		printf("%s: error %d\n", fullname, int(filerr));
453 		return;
454 	}
455 }
456 
457 
freefile(fileinfo * file)458 static void freefile(fileinfo *file)
459 {
460 	free(file->buf);
461 	file->buf = nullptr;
462 }
463 
464 
printname(const fileinfo * file1,const fileinfo * file2,float score,int mode1,int mode2)465 static void printname(const fileinfo *file1,const fileinfo *file2,float score,int mode1,int mode2)
466 {
467 	printf("%-12s %s %-12s %s ",file1 ? file1->name : "",modenames[mode1],file2 ? file2->name : "",modenames[mode2]);
468 	if (score == 0.0f) printf("NO MATCH\n");
469 	else if (score == 1.0f) printf("IDENTICAL\n");
470 	else printf("%3.6f%%\n",(double) (score*100));
471 }
472 
473 
load_files(int i,int * found,const char * path)474 static int load_files(int i, int *found, const char *path)
475 {
476 	/* attempt to open as a directory first */
477 	auto dir = osd::directory::open(path);
478 	if (dir)
479 	{
480 		const osd::directory::entry *d;
481 
482 		/* load all files in directory */
483 		while ((d = dir->read()) != nullptr)
484 		{
485 			const char *d_name = d->name;
486 			char buf[255+1];
487 
488 			sprintf(buf, "%s%c%s", path, PATH_DELIM, d_name);
489 			if (d->type == osd::directory::entry::entry_type::FILE)
490 			{
491 				uint64_t size = d->size;
492 				while (size && (size & 1) == 0) size >>= 1;
493 				//if (size & ~1)
494 				//  printf("%-23s %-23s ignored (not a ROM)\n",i ? "" : d_name,i ? d_name : "");
495 				//else
496 				{
497 					strcpy(files[i][found[i]].name,d_name);
498 					files[i][found[i]].size = d->size;
499 					readfile(path,&files[i][found[i]]);
500 					files[i][found[i]].listed = 0;
501 					if (found[i] >= MAX_FILES)
502 					{
503 						printf("%s: max of %d files exceeded\n",path,MAX_FILES);
504 						break;
505 					}
506 					found[i]++;
507 				}
508 			}
509 		}
510 		dir.reset();
511 	}
512 
513 	/* if not, try to open as a ZIP file */
514 	else
515 	{
516 		util::archive_file::ptr zip;
517 
518 		/* wasn't a directory, so try to open it as a zip file */
519 		if ((util::archive_file::open_zip(path, zip) != util::archive_file::error::NONE) &&
520 			(util::archive_file::open_7z(path, zip) != util::archive_file::error::NONE))
521 		{
522 			printf("Error, cannot open zip file '%s' !\n", path);
523 			return 1;
524 		}
525 
526 		/* load all files in zip file */
527 		for (int zipent = zip->first_file(); zipent >= 0; zipent = zip->next_file())
528 		{
529 			if (zip->current_is_directory()) continue;
530 
531 			int size;
532 
533 			size = zip->current_uncompressed_length();
534 			while (size && (size & 1) == 0) size >>= 1;
535 			if (zip->current_uncompressed_length() == 0) // || (size & ~1))
536 			{
537 				printf("%-23s %-23s ignored (not a ROM)\n",
538 					i ? "" : zip->current_name().c_str(), i ? zip->current_name().c_str() : "");
539 			}
540 			else
541 			{
542 				fileinfo *file = &files[i][found[i]];
543 				const char *delim = strrchr(zip->current_name().c_str(), '/');
544 
545 				if (delim)
546 					strcpy (file->name,delim+1);
547 				else
548 					strcpy(file->name,zip->current_name().c_str());
549 				file->size = zip->current_uncompressed_length();
550 				if ((file->buf = (unsigned char *)malloc(file->size)) == nullptr)
551 					printf("%s: out of memory!\n",file->name);
552 				else
553 				{
554 					if (zip->decompress(file->buf, file->size) != util::archive_file::error::NONE)
555 					{
556 						free(file->buf);
557 						file->buf = nullptr;
558 					}
559 				}
560 
561 				file->listed = 0;
562 				if (found[i] >= MAX_FILES)
563 				{
564 					printf("%s: max of %d files exceeded\n",path,MAX_FILES);
565 					break;
566 				}
567 				found[i]++;
568 			}
569 		}
570 	}
571 	return 0;
572 }
573 
574 
main(int argc,char * argv[])575 int CLIB_DECL main(int argc,char *argv[])
576 {
577 	int err;
578 	int total_modes = MODE_NIB2;    /* by default, use only MODE_A, MODE_NIB1 and MODE_NIB2 */
579 
580 	if (argc >= 2 && strcmp(argv[1],"-d") == 0)
581 	{
582 		argc--;
583 		argv++;
584 		total_modes = TOTAL_MODES;
585 	}
586 
587 	if (argc < 2)
588 	{
589 		printf("usage: romcmp [-d] [dir1 | zip1] [dir2 | zip2]\n");
590 		printf("-d enables a slower, more comprehensive comparison.\n");
591 		return 0;
592 	}
593 
594 	{
595 		int found[2];
596 		int i,j,mode1,mode2;
597 		int besti,bestj;
598 
599 
600 		found[0] = found[1] = 0;
601 		for (i = 0;i < 2;i++)
602 		{
603 			if (argc > i+1)
604 			{
605 				err = load_files (i, found, argv[i+1]);
606 				if (err != 0)
607 					return err;
608 			}
609 		}
610 
611 		if (argc >= 3)
612 			printf("%d and %d files\n",found[0],found[1]);
613 		else
614 			printf("%d files\n",found[0]);
615 
616 		for (i = 0;i < found[0];i++)
617 		{
618 			checkintegrity(&files[0][i],0);
619 		}
620 
621 		for (j = 0;j < found[1];j++)
622 		{
623 			checkintegrity(&files[1][j],1);
624 		}
625 
626 		if (argc < 3)
627 		{
628 			/* find duplicates in one dir */
629 			for (i = 0;i < found[0];i++)
630 			{
631 				for (j = i+1;j < found[0];j++)
632 				{
633 					for (mode1 = 0;mode1 < total_modes;mode1++)
634 					{
635 						for (mode2 = 0;mode2 < total_modes;mode2++)
636 						{
637 							if (filecompare(&files[0][i],&files[0][j],mode1,mode2) == 1.0f)
638 								printname(&files[0][i],&files[0][j],1.0,mode1,mode2);
639 						}
640 					}
641 				}
642 			}
643 		}
644 		else
645 		{
646 			/* compare two dirs */
647 			for (i = 0;i < found[0];i++)
648 			{
649 				for (j = 0;j < found[1];j++)
650 				{
651 					fprintf(stderr,"%2d%%\r",100*(i*found[1]+j)/(found[0]*found[1]));
652 					for (mode1 = 0;mode1 < total_modes;mode1++)
653 					{
654 						for (mode2 = 0;mode2 < total_modes;mode2++)
655 						{
656 							matchscore[i][j][mode1][mode2] = filecompare(&files[0][i],&files[1][j],mode1,mode2);
657 						}
658 					}
659 				}
660 			}
661 			fprintf(stderr,"   \r");
662 
663 			do
664 			{
665 				float bestscore;
666 				int bestmode1,bestmode2;
667 
668 				besti = -1;
669 				bestj = -1;
670 				bestscore = 0.0;
671 				bestmode1 = bestmode2 = -1;
672 
673 				for (mode1 = 0;mode1 < total_modes;mode1++)
674 				{
675 					for (mode2 = 0;mode2 < total_modes;mode2++)
676 					{
677 						for (i = 0;i < found[0];i++)
678 						{
679 							for (j = 0;j < found[1];j++)
680 							{
681 								if (matchscore[i][j][mode1][mode2] > bestscore
682 									|| (matchscore[i][j][mode1][mode2] == 1.0f && mode2 == 0 && bestmode2 > 0))
683 								{
684 									bestscore = matchscore[i][j][mode1][mode2];
685 									besti = i;
686 									bestj = j;
687 									bestmode1 = mode1;
688 									bestmode2 = mode2;
689 								}
690 							}
691 						}
692 					}
693 				}
694 
695 				if (besti != -1)
696 				{
697 					int start=0,end=0;
698 
699 					printname(&files[0][besti],&files[1][bestj],bestscore,bestmode1,bestmode2);
700 					files[0][besti].listed = 1;
701 					files[1][bestj].listed = 1;
702 
703 					matchscore[besti][bestj][bestmode1][bestmode2] = 0.0;
704 
705 					/* remove all matches using the same sections with a worse score */
706 					for (j = 0;j < found[1];j++)
707 					{
708 						for (mode2 = 0;mode2 < total_modes;mode2++)
709 						{
710 							if (matchscore[besti][j][bestmode1][mode2] < bestscore)
711 								matchscore[besti][j][bestmode1][mode2] = 0.0;
712 						}
713 					}
714 					for (i = 0;i < found[0];i++)
715 					{
716 						for (mode1 = 0;mode1 < total_modes;mode1++)
717 						{
718 							if (matchscore[i][bestj][mode1][bestmode2] < bestscore)
719 								matchscore[i][bestj][mode1][bestmode2] = 0.0;
720 						}
721 					}
722 
723 					/* remove all matches using incompatible sections */
724 					compatiblemodes(bestmode1,&start,&end);
725 					for (j = 0;j < found[1];j++)
726 					{
727 						for (mode2 = 0;mode2 < total_modes;mode2++)
728 						{
729 							for (mode1 = 0;mode1 < start;mode1++)
730 								matchscore[besti][j][mode1][mode2] = 0.0;
731 							for (mode1 = end+1;mode1 < total_modes;mode1++)
732 								matchscore[besti][j][mode1][mode2] = 0.0;
733 						}
734 					}
735 					compatiblemodes(bestmode2,&start,&end);
736 					for (i = 0;i < found[0];i++)
737 					{
738 						for (mode1 = 0;mode1 < total_modes;mode1++)
739 						{
740 							for (mode2 = 0;mode2 < start;mode2++)
741 								matchscore[i][bestj][mode1][mode2] = 0.0;
742 							for (mode2 = end+1;mode2 < total_modes;mode2++)
743 								matchscore[i][bestj][mode1][mode2] = 0.0;
744 						}
745 					}
746 				}
747 			} while (besti != -1);
748 
749 
750 			for (i = 0;i < found[0];i++)
751 			{
752 				if (files[0][i].listed == 0) printname(&files[0][i],nullptr,0.0,0,0);
753 			}
754 			for (i = 0;i < found[1];i++)
755 			{
756 				if (files[1][i].listed == 0) printname(nullptr,&files[1][i],0.0,0,0);
757 			}
758 		}
759 
760 
761 		for (i = 0;i < found[0];i++)
762 		{
763 			freefile(&files[0][i]);
764 		}
765 		for (i = 0;i < found[1];i++)
766 		{
767 			freefile(&files[1][i]);
768 		}
769 	}
770 
771 	util::archive_file::cache_clear();
772 	return 0;
773 }
774