~ubuntu-branches/debian/sid/mame/sid

« back to all changes in this revision

Viewing changes to mess/src/tools/regrep.c

  • Committer: Package Import Robot
  • Author(s): Jordi Mallach, Jordi Mallach, Emmanuel Kasper
  • Date: 2011-12-19 22:56:27 UTC
  • mfrom: (0.1.2)
  • Revision ID: package-import@ubuntu.com-20111219225627-ub5oga1oys4ogqzm
Tags: 0.144-1
[ Jordi Mallach ]
* Fix syntax errors in DEP5 copyright file (lintian).
* Use a versioned copyright Format specification field.
* Update Vcs-* URLs.
* Move transitional packages to the new metapackages section, and make
  them priority extra.
* Remove references to GNU/Linux and MESS sources from copyright.
* Add build variables for s390x.
* Use .xz tarballs as it cuts 4MB for the upstream sources.
* Add nplayers.ini as a patch. Update copyright file to add CC-BY-SA-3.0.

[ Emmanuel Kasper ]
* New upstream release. Closes: #651538.
* Add Free Desktop compliant png icons of various sizes taken from
  the hydroxygen iconset
* Mess is now built from a new source package, to avoid possible source
  incompatibilities between mame and the mess overlay.
* Mame-tools are not built from the mame source package anymore, but
  from the mess source package

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************
2
 
 
3
 
    Regression test report generator
4
 
 
5
 
****************************************************************************
6
 
 
7
 
    Copyright Aaron Giles
8
 
    All rights reserved.
9
 
 
10
 
    Redistribution and use in source and binary forms, with or without
11
 
    modification, are permitted provided that the following conditions are
12
 
    met:
13
 
 
14
 
        * Redistributions of source code must retain the above copyright
15
 
          notice, this list of conditions and the following disclaimer.
16
 
        * Redistributions in binary form must reproduce the above copyright
17
 
          notice, this list of conditions and the following disclaimer in
18
 
          the documentation and/or other materials provided with the
19
 
          distribution.
20
 
        * Neither the name 'MAME' nor the names of its contributors may be
21
 
          used to endorse or promote products derived from this software
22
 
          without specific prior written permission.
23
 
 
24
 
    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
25
 
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26
 
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
 
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
28
 
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29
 
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
 
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31
 
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
 
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33
 
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 
    POSSIBILITY OF SUCH DAMAGE.
35
 
 
36
 
****************************************************************************/
37
 
 
38
 
#include <stdio.h>
39
 
#include <stdlib.h>
40
 
#include <string.h>
41
 
#include <ctype.h>
42
 
#include "osdcore.h"
43
 
#include "png.h"
44
 
 
45
 
 
46
 
/***************************************************************************
47
 
    CONSTANTS & DEFINES
48
 
***************************************************************************/
49
 
 
50
 
#define MAX_COMPARES            16
51
 
#define BITMAP_SPACE                    4
52
 
 
53
 
enum
54
 
{
55
 
        STATUS_NOT_PRESENT = 0,
56
 
        STATUS_SUCCESS,
57
 
        STATUS_SUCCESS_DIFFERENT,
58
 
        STATUS_MISSING_FILES,
59
 
        STATUS_EXCEPTION,
60
 
        STATUS_FATAL_ERROR,
61
 
        STATUS_FAILED_VALIDITY,
62
 
        STATUS_OTHER,
63
 
        STATUS_COUNT
64
 
};
65
 
 
66
 
enum
67
 
{
68
 
        BUCKET_UNKNOWN = 0,
69
 
        BUCKET_IMPROVED,
70
 
        BUCKET_REGRESSED,
71
 
        BUCKET_CHANGED,
72
 
        BUCKET_MULTI_ERROR,
73
 
        BUCKET_CONSISTENT_ERROR,
74
 
        BUCKET_GOOD,
75
 
        BUCKET_GOOD_BUT_CHANGED,
76
 
        BUCKET_GOOD_BUT_CHANGED_SCREENSHOTS,
77
 
        BUCKET_COUNT
78
 
};
79
 
 
80
 
 
81
 
 
82
 
/***************************************************************************
83
 
    TYPE DEFINITIONS
84
 
***************************************************************************/
85
 
 
86
 
typedef struct _summary_file summary_file;
87
 
struct _summary_file
88
 
{
89
 
    summary_file *  next;
90
 
    char            name[20];
91
 
    char            source[100];
92
 
    UINT8           status[MAX_COMPARES];
93
 
    UINT8                       matchbitmap[MAX_COMPARES];
94
 
    char *          text[MAX_COMPARES];
95
 
    UINT32          textsize[MAX_COMPARES];
96
 
    UINT32          textalloc[MAX_COMPARES];
97
 
};
98
 
 
99
 
 
100
 
typedef struct _summary_list summary_list;
101
 
struct _summary_list
102
 
{
103
 
    summary_list *  next;
104
 
    summary_file *  files;
105
 
    char *                      dir;
106
 
    char                        version[40];
107
 
};
108
 
 
109
 
 
110
 
 
111
 
/***************************************************************************
112
 
    GLOBAL VARIABLES
113
 
***************************************************************************/
114
 
 
115
 
static summary_file *filehash[128][128];
116
 
static summary_list lists[MAX_COMPARES];
117
 
static int list_count;
118
 
 
119
 
static const char *const bucket_name[] =
120
 
{
121
 
        "Unknown",
122
 
        "Games That Have Improved",
123
 
        "Games That Have Regressed",
124
 
        "Games With Changed Screenshots",
125
 
        "Games With Multiple Errors",
126
 
        "Games With Consistent Errors",
127
 
        "Games That Are Consistently Good",
128
 
        "Games That Regressed But Improved",
129
 
        "Games With Changed Screenshots",
130
 
};
131
 
 
132
 
static const int bucket_output_order[] =
133
 
{
134
 
        BUCKET_REGRESSED,
135
 
        BUCKET_IMPROVED,
136
 
        BUCKET_CHANGED,
137
 
        BUCKET_GOOD_BUT_CHANGED_SCREENSHOTS,
138
 
        BUCKET_GOOD_BUT_CHANGED,
139
 
        BUCKET_MULTI_ERROR,
140
 
        BUCKET_CONSISTENT_ERROR
141
 
};
142
 
 
143
 
static const char *const status_text[] =
144
 
{
145
 
        "",
146
 
        "Success",
147
 
        "Changed",
148
 
        "Missing Files",
149
 
        "Exception",
150
 
        "Fatal Error",
151
 
        "Failed Validity Check",
152
 
        "Other Unknown Error"
153
 
};
154
 
 
155
 
static const char *const status_color[] =
156
 
{
157
 
        "",
158
 
        "background:#00A000",
159
 
        "background:#E0E000",
160
 
        "background:#8000C0",
161
 
        "background:#C00000",
162
 
        "background:#C00000",
163
 
        "background:#C06000",
164
 
        "background:#C00000",
165
 
        "background:#C00000",
166
 
};
167
 
 
168
 
 
169
 
 
170
 
/***************************************************************************
171
 
    PROTOTYPES
172
 
***************************************************************************/
173
 
 
174
 
/* summary parsing */
175
 
static int read_summary_log(const char *filename, int index);
176
 
static summary_file *parse_driver_tag(char *linestart, int index);
177
 
static summary_file *get_file(const char *filename);
178
 
static int CLIB_DECL compare_file(const void *file0ptr, const void *file1ptr);
179
 
static summary_file *sort_file_list(void);
180
 
 
181
 
/* HTML helpers */
182
 
static core_file *create_file_and_output_header(const astring *filename, const astring *templatefile, const astring *title);
183
 
static void output_footer_and_close_file(core_file *file, const astring *templatefile, const astring *title);
184
 
 
185
 
/* report generators */
186
 
static void output_report(const astring *dirname, const astring *tempheader, const astring *tempfooter, summary_file *filelist);
187
 
static int compare_screenshots(summary_file *curfile);
188
 
static int generate_png_diff(const summary_file *curfile, const astring *destdir, const char *destname);
189
 
static void create_linked_file(const astring *dirname, const summary_file *curfile, const summary_file *prevfile, const summary_file *nextfile, const char *pngfile, const astring *tempheader, const astring *tempfooter);
190
 
static void append_driver_list_table(const char *header, const astring *dirname, core_file *indexfile, const summary_file *listhead, const astring *tempheader, const astring *tempfooter);
191
 
 
192
 
 
193
 
 
194
 
/***************************************************************************
195
 
    INLINE FUNCTIONS
196
 
***************************************************************************/
197
 
 
198
 
/*-------------------------------------------------
199
 
    trim_string - trim leading/trailing spaces
200
 
    from a string
201
 
-------------------------------------------------*/
202
 
 
203
 
INLINE char *trim_string(char *string)
204
 
{
205
 
        int length;
206
 
 
207
 
        /* trim leading spaces */
208
 
        while (*string != 0 && isspace((UINT8)*string))
209
 
                string++;
210
 
 
211
 
        /* trim trailing spaces */
212
 
        length = strlen(string);
213
 
        while (length > 0 && isspace((UINT8)string[length - 1]))
214
 
                string[--length] = 0;
215
 
 
216
 
        return string;
217
 
}
218
 
 
219
 
 
220
 
/*-------------------------------------------------
221
 
    get_unique_index - get the unique bitmap
222
 
    index for a given entry
223
 
-------------------------------------------------*/
224
 
 
225
 
INLINE int get_unique_index(const summary_file *curfile, int index)
226
 
{
227
 
        int listnum, curindex = 0;
228
 
 
229
 
        /* if we're invalid, just return that */
230
 
        if (curfile->matchbitmap[index] == 0xff)
231
 
                return -1;
232
 
 
233
 
        /* count unique elements up to us */
234
 
        for (listnum = 0; listnum < curfile->matchbitmap[index]; listnum++)
235
 
                if (curfile->matchbitmap[listnum] == listnum)
236
 
                        curindex++;
237
 
        return curindex;
238
 
}
239
 
 
240
 
 
241
 
 
242
 
/***************************************************************************
243
 
    MAIN
244
 
***************************************************************************/
245
 
 
246
 
/*-------------------------------------------------
247
 
    main - main entry point
248
 
-------------------------------------------------*/
249
 
 
250
 
int main(int argc, char *argv[])
251
 
{
252
 
        astring *dirname = NULL, *tempfilename = NULL, *tempheader = NULL, *tempfooter = NULL;
253
 
        UINT32 bufsize;
254
 
        void *buffer;
255
 
        int listnum;
256
 
    int result;
257
 
 
258
 
    /* first argument is the directory */
259
 
    if (argc < 4)
260
 
    {
261
 
        fprintf(stderr, "Usage:\nregrep <template> <outputdir> <summary1> [<summary2> [<summary3> ...]]\n");
262
 
        return 1;
263
 
    }
264
 
    tempfilename = astring_dupc(argv[1]);
265
 
    dirname = astring_dupc(argv[2]);
266
 
    list_count = argc - 3;
267
 
 
268
 
        /* read the template file into an astring */
269
 
        if (core_fload(astring_c(tempfilename), &buffer, &bufsize) == FILERR_NONE)
270
 
        {
271
 
                tempheader = astring_dupch((const char *)buffer, bufsize);
272
 
                free(buffer);
273
 
        }
274
 
 
275
 
        /* verify the template */
276
 
        if (tempheader == NULL)
277
 
        {
278
 
                fprintf(stderr, "Unable to read template file\n");
279
 
                return 1;
280
 
        }
281
 
        result = astring_findc(tempheader, 0, "<!--CONTENT-->");
282
 
        if (result == -1)
283
 
        {
284
 
                fprintf(stderr, "Template is missing a <!--CONTENT--> marker\n");
285
 
                return 1;
286
 
        }
287
 
        tempfooter = astring_substr(astring_dup(tempheader), result + 14, -1);
288
 
        tempheader = astring_substr(tempheader, 0, result);
289
 
 
290
 
    /* loop over arguments and read the files */
291
 
    for (listnum = 0; listnum < list_count; listnum++)
292
 
    {
293
 
        result = read_summary_log(argv[listnum + 3], listnum);
294
 
        if (result != 0)
295
 
            return result;
296
 
    }
297
 
 
298
 
    /* output the summary */
299
 
    output_report(dirname, tempheader, tempfooter, sort_file_list());
300
 
 
301
 
        astring_free(dirname);
302
 
        astring_free(tempfilename);
303
 
        astring_free(tempheader);
304
 
        astring_free(tempfooter);
305
 
    return 0;
306
 
}
307
 
 
308
 
 
309
 
 
310
 
/***************************************************************************
311
 
    SUMMARY PARSING
312
 
***************************************************************************/
313
 
 
314
 
/*-------------------------------------------------
315
 
    get_file - lookup a driver name in the hash
316
 
    table and return a pointer to it; if none
317
 
    found, allocate a new entry
318
 
-------------------------------------------------*/
319
 
 
320
 
static summary_file *get_file(const char *filename)
321
 
{
322
 
    summary_file *file;
323
 
 
324
 
    /* use the first two characters as a lookup */
325
 
    for (file = filehash[filename[0] & 0x7f][filename[1] & 0x7f]; file != NULL; file = file->next)
326
 
        if (strcmp(filename, file->name) == 0)
327
 
            return file;
328
 
 
329
 
    /* didn't find one -- allocate */
330
 
    file = (summary_file *)malloc(sizeof(*file));
331
 
    if (file == NULL)
332
 
        return NULL;
333
 
    memset(file, 0, sizeof(*file));
334
 
 
335
 
    /* set the name so we find it in the future */
336
 
    strcpy(file->name, filename);
337
 
 
338
 
    /* add to the head of the list */
339
 
    file->next = filehash[filename[0] & 0x7f][filename[1] & 0x7f];
340
 
    filehash[filename[0] & 0x7f][filename[1] & 0x7f] = file;
341
 
    return file;
342
 
}
343
 
 
344
 
 
345
 
/*-------------------------------------------------
346
 
    read_summary_log - read a summary.log file
347
 
    and build entries for its data
348
 
-------------------------------------------------*/
349
 
 
350
 
static int read_summary_log(const char *filename, int index)
351
 
{
352
 
    summary_file *curfile = NULL;
353
 
    char linebuffer[1024];
354
 
    char *linestart;
355
 
    int drivers = 0;
356
 
    FILE *file;
357
 
 
358
 
    /* open the logfile */
359
 
    file = fopen(filename, "r");
360
 
    if (file == NULL)
361
 
    {
362
 
        fprintf(stderr, "Error: file '%s' not found\n", filename);
363
 
        return 1;
364
 
    }
365
 
 
366
 
    /* parse it */
367
 
    while (fgets(linebuffer, sizeof(linebuffer), file) != NULL)
368
 
    {
369
 
        /* trim the leading/trailing spaces */
370
 
        linestart = trim_string(linebuffer);
371
 
 
372
 
        /* is this one of our specials? */
373
 
        if (strncmp(linestart, "@@@@@", 5) == 0)
374
 
        {
375
 
                /* advance past the signature */
376
 
                linestart += 5;
377
 
 
378
 
                        /* look for the driver= tag */
379
 
                        if (strncmp(linestart, "driver=", 7) == 0)
380
 
                        {
381
 
                                curfile = parse_driver_tag(linestart + 7, index);
382
 
                                if (curfile == NULL)
383
 
                                        goto error;
384
 
                                drivers++;
385
 
                        }
386
 
 
387
 
                        /* look for the source= tag */
388
 
                        else if (strncmp(linestart, "source=", 7) == 0)
389
 
                        {
390
 
                                /* error if no driver yet */
391
 
                                if (curfile == NULL)
392
 
                                {
393
 
                                        fprintf(stderr, "Unexpected @@@@@source= tag\n");
394
 
                                        goto error;
395
 
                                }
396
 
 
397
 
                                /* copy the string */
398
 
                                strcpy(curfile->source, trim_string(linestart + 7));
399
 
                        }
400
 
 
401
 
                        /* look for the dir= tag */
402
 
                        else if (strncmp(linestart, "dir=", 4) == 0)
403
 
                        {
404
 
                                char *dirname = trim_string(linestart + 4);
405
 
 
406
 
                                /* allocate a copy of the string */
407
 
                                lists[index].dir = (char *)malloc(strlen(dirname) + 1);
408
 
                                if (lists[index].dir == NULL)
409
 
                                        goto error;
410
 
                                strcpy(lists[index].dir, dirname);
411
 
                                fprintf(stderr, "Directory %s\n", lists[index].dir);
412
 
                        }
413
 
                }
414
 
 
415
 
        /* if not, consider other options */
416
 
        else if (curfile != NULL)
417
 
        {
418
 
            int foundchars = 0;
419
 
            char *curptr;
420
 
 
421
 
            /* look for the pngcrc= tag */
422
 
                        if (strncmp(linestart, "pngcrc: ", 7) == 0)
423
 
            {
424
 
            }
425
 
 
426
 
                        /* otherwise, accumulate the text */
427
 
                        else
428
 
                        {
429
 
                                /* find the end of the line and normalize it with a CR */
430
 
                                for (curptr = linestart; *curptr != 0 && *curptr != '\n' && *curptr != '\r'; curptr++)
431
 
                                        if (!isspace((UINT8)*curptr))
432
 
                                                foundchars = 1;
433
 
                                *curptr++ = '\n';
434
 
                                *curptr = 0;
435
 
 
436
 
                                /* ignore blank lines */
437
 
                                if (!foundchars)
438
 
                                        continue;
439
 
 
440
 
                                /* see if we have enough room */
441
 
                                if (curfile->textsize[index] + (curptr - linestart) + 1 >= curfile->textalloc[index])
442
 
                                {
443
 
                                        curfile->textalloc[index] = curfile->textsize[index] + (curptr - linestart) + 256;
444
 
                                        curfile->text[index] = (char *)realloc(curfile->text[index], curfile->textalloc[index]);
445
 
                                        if (curfile->text[index] == NULL)
446
 
                                        {
447
 
                                                fprintf(stderr, "Unable to allocate memory for text\n");
448
 
                                                goto error;
449
 
                                        }
450
 
                                }
451
 
 
452
 
                                /* append our text */
453
 
                                strcpy(curfile->text[index] + curfile->textsize[index], linestart);
454
 
                                curfile->textsize[index] += curptr - linestart;
455
 
                        }
456
 
                }
457
 
 
458
 
                /* look for the M.A.M.E. header */
459
 
                else if (strncmp(linestart, "M.A.M.E. v", 10) == 0)
460
 
                {
461
 
                        char *start = linestart + 10;
462
 
                        char *end;
463
 
 
464
 
                        /* find the end */
465
 
                        for (end = start; !isspace((UINT8)*end); end++) ;
466
 
                        *end = 0;
467
 
                        strcpy(lists[index].version, start);
468
 
                        fprintf(stderr, "Parsing results from version %s\n", lists[index].version);
469
 
                }
470
 
    }
471
 
 
472
 
    fclose(file);
473
 
    fprintf(stderr, "Parsed %d drivers\n", drivers);
474
 
    return 0;
475
 
 
476
 
error:
477
 
    fclose(file);
478
 
    return 1;
479
 
}
480
 
 
481
 
 
482
 
/*-------------------------------------------------
483
 
    parse_driver_tag - parse the status info
484
 
    from a driver tag
485
 
-------------------------------------------------*/
486
 
 
487
 
static summary_file *parse_driver_tag(char *linestart, int index)
488
 
{
489
 
        summary_file *curfile;
490
 
        char *colon;
491
 
 
492
 
        /* find the colon separating name from status */
493
 
        colon = strchr(linestart, ':');
494
 
        if (colon == NULL)
495
 
        {
496
 
                fprintf(stderr, "Unexpected text after @@@@@driver=\n");
497
 
                return NULL;
498
 
        }
499
 
 
500
 
        /* NULL terminate at the colon and look up the file */
501
 
        *colon = 0;
502
 
        curfile = get_file(trim_string(linestart));
503
 
        if (curfile == NULL)
504
 
        {
505
 
                fprintf(stderr, "Unable to allocate memory for driver\n");
506
 
                return NULL;
507
 
        }
508
 
 
509
 
        /* clear out any old status for this file */
510
 
        curfile->status[index] = STATUS_NOT_PRESENT;
511
 
        if (curfile->text[index] != NULL)
512
 
                free(curfile->text[index]);
513
 
        curfile->text[index] = NULL;
514
 
        curfile->textsize[index] = 0;
515
 
        curfile->textalloc[index] = 0;
516
 
 
517
 
        /* strip leading/trailing spaces from the status */
518
 
        colon = trim_string(colon + 1);
519
 
 
520
 
        /* convert status into statistics */
521
 
        if (strcmp(colon, "Success") == 0)
522
 
                curfile->status[index] = STATUS_SUCCESS;
523
 
        else if (strcmp(colon, "Missing files") == 0)
524
 
                curfile->status[index] = STATUS_MISSING_FILES;
525
 
        else if (strcmp(colon, "Exception") == 0)
526
 
                curfile->status[index] = STATUS_EXCEPTION;
527
 
        else if (strcmp(colon, "Fatal error") == 0)
528
 
                curfile->status[index] = STATUS_FATAL_ERROR;
529
 
        else if (strcmp(colon, "Failed validity check") == 0)
530
 
                curfile->status[index] = STATUS_FAILED_VALIDITY;
531
 
        else
532
 
                curfile->status[index] = STATUS_OTHER;
533
 
 
534
 
        return curfile;
535
 
}
536
 
 
537
 
 
538
 
/*-------------------------------------------------
539
 
    compare_file - compare two files, sorting
540
 
    first by source filename, then by driver name
541
 
-------------------------------------------------*/
542
 
 
543
 
static int CLIB_DECL compare_file(const void *file0ptr, const void *file1ptr)
544
 
{
545
 
        summary_file *file0 = *(summary_file **)file0ptr;
546
 
        summary_file *file1 = *(summary_file **)file1ptr;
547
 
        int result = strcmp(file0->source, file1->source);
548
 
        if (result == 0)
549
 
                result = strcmp(file0->name, file1->name);
550
 
        return result;
551
 
}
552
 
 
553
 
 
554
 
/*-------------------------------------------------
555
 
    sort_file_list - convert the hashed lists
556
 
    into a single, sorted list
557
 
-------------------------------------------------*/
558
 
 
559
 
static summary_file *sort_file_list(void)
560
 
{
561
 
        summary_file *listhead, **tailptr, *curfile, **filearray;
562
 
        int numfiles, filenum;
563
 
    int c0, c1;
564
 
 
565
 
    /* count the total number of files */
566
 
    numfiles = 0;
567
 
    for (c0 = 0; c0 < 128; c0++)
568
 
            for (c1 = 0; c1 < 128; c1++)
569
 
                for (curfile = filehash[c0][c1]; curfile != NULL; curfile = curfile->next)
570
 
                        numfiles++;
571
 
 
572
 
        /* allocate an array of files */
573
 
        filearray = (summary_file **)malloc(numfiles * sizeof(*filearray));
574
 
        if (filearray == NULL)
575
 
        {
576
 
                fprintf(stderr, "Out of memory!\n");
577
 
                return NULL;
578
 
        }
579
 
 
580
 
        /* populate the array */
581
 
    numfiles = 0;
582
 
    for (c0 = 0; c0 < 128; c0++)
583
 
            for (c1 = 0; c1 < 128; c1++)
584
 
                for (curfile = filehash[c0][c1]; curfile != NULL; curfile = curfile->next)
585
 
                        filearray[numfiles++] = curfile;
586
 
 
587
 
        /* sort the array */
588
 
        qsort(filearray, numfiles, sizeof(filearray[0]), compare_file);
589
 
 
590
 
        /* now regenerate a single list */
591
 
    listhead = NULL;
592
 
    tailptr = &listhead;
593
 
    for (filenum = 0; filenum < numfiles; filenum++)
594
 
    {
595
 
        *tailptr = filearray[filenum];
596
 
        tailptr = &(*tailptr)->next;
597
 
    }
598
 
    *tailptr = NULL;
599
 
    free(filearray);
600
 
 
601
 
    return listhead;
602
 
}
603
 
 
604
 
 
605
 
 
606
 
/***************************************************************************
607
 
    HTML OUTPUT HELPERS
608
 
***************************************************************************/
609
 
 
610
 
/*-------------------------------------------------
611
 
    create_file_and_output_header - create a new
612
 
    HTML file with a standard header
613
 
-------------------------------------------------*/
614
 
 
615
 
static core_file *create_file_and_output_header(const astring *filename, const astring *templatefile, const astring *title)
616
 
{
617
 
        astring *modified;
618
 
        core_file *file;
619
 
 
620
 
        /* create the indexfile */
621
 
        if (core_fopen(astring_c(filename), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS | OPEN_FLAG_NO_BOM, &file) != FILERR_NONE)
622
 
                return NULL;
623
 
 
624
 
        /* print a header */
625
 
        modified = astring_dup(templatefile);
626
 
        astring_replacec(modified, 0, "<!--TITLE-->", astring_c(title));
627
 
        core_fwrite(file, astring_c(modified), astring_len(modified));
628
 
 
629
 
        /* return the file */
630
 
        astring_free(modified);
631
 
        return file;
632
 
}
633
 
 
634
 
 
635
 
/*-------------------------------------------------
636
 
    output_footer_and_close_file - write a
637
 
    standard footer to an HTML file and close it
638
 
-------------------------------------------------*/
639
 
 
640
 
static void output_footer_and_close_file(core_file *file, const astring *templatefile, const astring *title)
641
 
{
642
 
        astring *modified;
643
 
 
644
 
        modified = astring_dup(templatefile);
645
 
        astring_replacec(modified, 0, "<!--TITLE-->", astring_c(title));
646
 
        core_fwrite(file, astring_c(modified), astring_len(modified));
647
 
        astring_free(modified);
648
 
        core_fclose(file);
649
 
}
650
 
 
651
 
 
652
 
 
653
 
/***************************************************************************
654
 
    REPORT GENERATORS
655
 
***************************************************************************/
656
 
 
657
 
/*-------------------------------------------------
658
 
    output_report - generate the summary
659
 
    report HTML files
660
 
-------------------------------------------------*/
661
 
 
662
 
static void output_report(const astring *dirname, const astring *tempheader, const astring *tempfooter, summary_file *filelist)
663
 
{
664
 
        summary_file *buckethead[BUCKET_COUNT], **buckettailptr[BUCKET_COUNT];
665
 
        summary_file *curfile;
666
 
        astring *title = astring_dupc("MAME Regressions");
667
 
        astring *tempname = astring_alloc();
668
 
        int listnum, bucknum;
669
 
        core_file *indexfile;
670
 
        int count = 0, total;
671
 
 
672
 
        /* initialize the lists */
673
 
        for (bucknum = 0; bucknum < BUCKET_COUNT; bucknum++)
674
 
        {
675
 
                buckethead[bucknum] = NULL;
676
 
                buckettailptr[bucknum] = &buckethead[bucknum];
677
 
        }
678
 
 
679
 
        /* compute the total number of files */
680
 
        total = 0;
681
 
        for (curfile = filelist; curfile != NULL; curfile = curfile->next)
682
 
                total++;
683
 
 
684
 
        /* first bucketize the games */
685
 
        for (curfile = filelist; curfile != NULL; curfile = curfile->next)
686
 
        {
687
 
                int statcount[STATUS_COUNT] = { 0 };
688
 
                int bucket = BUCKET_UNKNOWN;
689
 
                int unique_codes = 0;
690
 
                int first_valid;
691
 
 
692
 
                /* print status */
693
 
                if (++count % 100 == 0)
694
 
                        fprintf(stderr, "Processing file %d/%d\n", count, total);
695
 
 
696
 
                /* find the first valid entry */
697
 
                for (first_valid = 0; curfile->status[first_valid] == STATUS_NOT_PRESENT; first_valid++) ;
698
 
 
699
 
                /* do we need to output anything? */
700
 
                for (listnum = first_valid; listnum < list_count; listnum++)
701
 
                        if (statcount[curfile->status[listnum]]++ == 0)
702
 
                                unique_codes++;
703
 
 
704
 
                /* were we consistent? */
705
 
                if (unique_codes == 1)
706
 
                {
707
 
                        /* were we consistently ok? */
708
 
                        if (curfile->status[first_valid] == STATUS_SUCCESS)
709
 
                                bucket = compare_screenshots(curfile);
710
 
 
711
 
                        /* must have been consistently erroring */
712
 
                        else
713
 
                                bucket = BUCKET_CONSISTENT_ERROR;
714
 
                }
715
 
 
716
 
                /* ok, we're not consistent; could be a number of things */
717
 
                else
718
 
                {
719
 
                        /* were we ok at the start and end but not in the middle? */
720
 
                        if (curfile->status[first_valid] == STATUS_SUCCESS && curfile->status[list_count - 1] == STATUS_SUCCESS)
721
 
                                bucket = BUCKET_GOOD_BUT_CHANGED;
722
 
 
723
 
                        /* did we go from good to bad? */
724
 
                        else if (curfile->status[first_valid] == STATUS_SUCCESS)
725
 
                                bucket = BUCKET_REGRESSED;
726
 
 
727
 
                        /* did we go from bad to good? */
728
 
                        else if (curfile->status[list_count - 1] == STATUS_SUCCESS)
729
 
                                bucket = BUCKET_IMPROVED;
730
 
 
731
 
                        /* must have had multiple errors */
732
 
                        else
733
 
                                bucket = BUCKET_MULTI_ERROR;
734
 
                }
735
 
 
736
 
                /* add us to the appropriate list */
737
 
                *buckettailptr[bucket] = curfile;
738
 
                buckettailptr[bucket] = &curfile->next;
739
 
        }
740
 
 
741
 
        /* terminate all the lists */
742
 
        for (bucknum = 0; bucknum < BUCKET_COUNT; bucknum++)
743
 
                *buckettailptr[bucknum] = NULL;
744
 
 
745
 
        /* output header */
746
 
        astring_printf(tempname, "%s" PATH_SEPARATOR "%s", astring_c(dirname), "index.html");
747
 
        indexfile = create_file_and_output_header(tempname, tempheader, title);
748
 
        if (indexfile == NULL)
749
 
        {
750
 
                fprintf(stderr, "Error creating file '%s'\n", astring_c(tempname));
751
 
                astring_free(tempname);
752
 
                astring_free(title);
753
 
                return;
754
 
        }
755
 
 
756
 
        /* iterate over buckets and output them */
757
 
        for (bucknum = 0; bucknum < ARRAY_LENGTH(bucket_output_order); bucknum++)
758
 
        {
759
 
                int curbucket = bucket_output_order[bucknum];
760
 
 
761
 
                if (buckethead[curbucket] != NULL)
762
 
                {
763
 
                        fprintf(stderr, "Outputting bucket: %s\n", bucket_name[curbucket]);
764
 
                        append_driver_list_table(bucket_name[curbucket], dirname, indexfile, buckethead[curbucket], tempheader, tempfooter);
765
 
                }
766
 
        }
767
 
 
768
 
        /* output footer */
769
 
        output_footer_and_close_file(indexfile, tempfooter, title);
770
 
        astring_free(tempname);
771
 
        astring_free(title);
772
 
}
773
 
 
774
 
 
775
 
/*-------------------------------------------------
776
 
    compare_screenshots - compare the screenshots
777
 
    for all the games in a file
778
 
-------------------------------------------------*/
779
 
 
780
 
static int compare_screenshots(summary_file *curfile)
781
 
{
782
 
        bitmap_t *bitmaps[MAX_COMPARES];
783
 
        int unique[MAX_COMPARES];
784
 
        int numunique = 0;
785
 
        int listnum;
786
 
 
787
 
        /* iterate over all files and load their bitmaps */
788
 
        for (listnum = 0; listnum < list_count; listnum++)
789
 
        {
790
 
                bitmaps[listnum] = NULL;
791
 
                if (curfile->status[listnum] == STATUS_SUCCESS)
792
 
                {
793
 
                        astring *fullname = astring_alloc();
794
 
                        file_error filerr;
795
 
                        core_file *file;
796
 
 
797
 
                        /* get the filename for the image */
798
 
                        astring_printf(fullname, "%s" PATH_SEPARATOR "snap" PATH_SEPARATOR "%s" PATH_SEPARATOR "final.png", lists[listnum].dir, curfile->name);
799
 
 
800
 
                        /* open the file */
801
 
                        filerr = core_fopen(astring_c(fullname), OPEN_FLAG_READ, &file);
802
 
 
803
 
                        /* if that failed, look in the old location */
804
 
                        if (filerr != FILERR_NONE)
805
 
                        {
806
 
                                /* get the filename for the image */
807
 
                                astring_printf(fullname, "%s" PATH_SEPARATOR "snap" PATH_SEPARATOR "_%s.png", lists[listnum].dir, curfile->name);
808
 
 
809
 
                                /* open the file */
810
 
                                filerr = core_fopen(astring_c(fullname), OPEN_FLAG_READ, &file);
811
 
                        }
812
 
 
813
 
                        /* if that worked, load the file */
814
 
                        if (filerr == FILERR_NONE)
815
 
                        {
816
 
                                png_read_bitmap(file, &bitmaps[listnum]);
817
 
                                core_fclose(file);
818
 
                        }
819
 
                        astring_free(fullname);
820
 
                }
821
 
        }
822
 
 
823
 
        /* now find all the different bitmap types */
824
 
        for (listnum = 0; listnum < list_count; listnum++)
825
 
        {
826
 
                curfile->matchbitmap[listnum] = 0xff;
827
 
                if (bitmaps[listnum] != NULL)
828
 
                {
829
 
                        bitmap_t *this_bitmap = bitmaps[listnum];
830
 
                        int compnum;
831
 
 
832
 
                        /* compare against all unique bitmaps */
833
 
                        for (compnum = 0; compnum < numunique; compnum++)
834
 
                        {
835
 
                                bitmap_t *base_bitmap = bitmaps[unique[compnum]];
836
 
                                int bitmaps_differ;
837
 
                                int x, y;
838
 
 
839
 
                                /* if the sizes are different, we differ; otherwise start off assuming we are the same */
840
 
                                bitmaps_differ = (this_bitmap->width != base_bitmap->width || this_bitmap->height != base_bitmap->height);
841
 
 
842
 
                                /* compare scanline by scanline */
843
 
                                for (y = 0; y < this_bitmap->height && !bitmaps_differ; y++)
844
 
                                {
845
 
                                        UINT32 *base = BITMAP_ADDR32(base_bitmap, y, 0);
846
 
                                        UINT32 *curr = BITMAP_ADDR32(this_bitmap, y, 0);
847
 
 
848
 
                                        /* scan the scanline */
849
 
                                        for (x = 0; x < this_bitmap->width; x++)
850
 
                                                if (*base++ != *curr++)
851
 
                                                        break;
852
 
                                        bitmaps_differ = (x != this_bitmap->width);
853
 
                                }
854
 
 
855
 
                                /* if we matched, remember which listnum index we matched, and stop */
856
 
                                if (!bitmaps_differ)
857
 
                                {
858
 
                                        curfile->matchbitmap[listnum] = unique[compnum];
859
 
                                        break;
860
 
                                }
861
 
 
862
 
                                /* if different from the first unique entry, adjust the status */
863
 
                                if (bitmaps_differ && compnum == 0)
864
 
                                        curfile->status[listnum] = STATUS_SUCCESS_DIFFERENT;
865
 
                        }
866
 
 
867
 
                        /* if we're unique, add ourselves to the list */
868
 
                        if (compnum >= numunique)
869
 
                        {
870
 
                                unique[numunique++] = listnum;
871
 
                                curfile->matchbitmap[listnum] = listnum;
872
 
                                continue;
873
 
                        }
874
 
                }
875
 
        }
876
 
 
877
 
        /* free the bitmaps */
878
 
        for (listnum = 0; listnum < list_count; listnum++)
879
 
                if (bitmaps[listnum] != NULL)
880
 
                        bitmap_free(bitmaps[listnum]);
881
 
 
882
 
        /* if all screenshots matched, we're good */
883
 
        if (numunique == 1)
884
 
                return BUCKET_GOOD;
885
 
 
886
 
        /* if the last screenshot matched the first unique one, we're good but changed */
887
 
        if (curfile->matchbitmap[listnum - 1] == unique[0])
888
 
                return BUCKET_GOOD_BUT_CHANGED_SCREENSHOTS;
889
 
 
890
 
        /* otherwise we're just changed */
891
 
        return BUCKET_CHANGED;
892
 
}
893
 
 
894
 
 
895
 
/*-------------------------------------------------
896
 
    generate_png_diff - create a new PNG file
897
 
    that shows multiple differing PNGs side by
898
 
    side with a third set of differences
899
 
-------------------------------------------------*/
900
 
 
901
 
static int generate_png_diff(const summary_file *curfile, const astring *destdir, const char *destname)
902
 
{
903
 
        bitmap_t *bitmaps[MAX_COMPARES] = { NULL };
904
 
        astring *srcimgname = astring_alloc();
905
 
        astring *dstfilename = astring_alloc();
906
 
        astring *tempname = astring_alloc();
907
 
        bitmap_t *finalbitmap = NULL;
908
 
        int width, height, maxwidth;
909
 
        int bitmapcount = 0;
910
 
        int listnum, bmnum;
911
 
        core_file *file = NULL;
912
 
        file_error filerr;
913
 
        png_error pngerr;
914
 
        int error = -1;
915
 
        int starty;
916
 
 
917
 
        /* generate the common source filename */
918
 
        astring_printf(dstfilename, "%s" PATH_SEPARATOR "%s", astring_c(destdir), destname);
919
 
        astring_printf(srcimgname, "snap" PATH_SEPARATOR "%s" PATH_SEPARATOR "final.png", curfile->name);
920
 
 
921
 
        /* open and load all unique bitmaps */
922
 
        for (listnum = 0; listnum < list_count; listnum++)
923
 
                if (curfile->matchbitmap[listnum] == listnum)
924
 
                {
925
 
                        astring_printf(tempname, "%s" PATH_SEPARATOR "%s", lists[listnum].dir, astring_c(srcimgname));
926
 
 
927
 
                        /* open the source image */
928
 
                        filerr = core_fopen(astring_c(tempname), OPEN_FLAG_READ, &file);
929
 
                        if (filerr != FILERR_NONE)
930
 
                                goto error;
931
 
 
932
 
                        /* load the source image */
933
 
                        pngerr = png_read_bitmap(file, &bitmaps[bitmapcount++]);
934
 
                        core_fclose(file);
935
 
                        if (pngerr != PNGERR_NONE)
936
 
                                goto error;
937
 
                }
938
 
 
939
 
        /* if there's only one unique bitmap, skip it */
940
 
        if (bitmapcount <= 1)
941
 
                goto error;
942
 
 
943
 
        /* determine the size of the final bitmap */
944
 
        height = width = 0;
945
 
        maxwidth = bitmaps[0]->width;
946
 
        for (bmnum = 1; bmnum < bitmapcount; bmnum++)
947
 
        {
948
 
                int curwidth;
949
 
 
950
 
                /* determine the maximal width */
951
 
                maxwidth = MAX(maxwidth, bitmaps[bmnum]->width);
952
 
                curwidth = bitmaps[0]->width + BITMAP_SPACE + maxwidth + BITMAP_SPACE + maxwidth;
953
 
                width = MAX(width, curwidth);
954
 
 
955
 
                /* add to the height */
956
 
                height += MAX(bitmaps[0]->height, bitmaps[bmnum]->height);
957
 
                if (bmnum != 1)
958
 
                        height += BITMAP_SPACE;
959
 
        }
960
 
 
961
 
        /* allocate the final bitmap */
962
 
        finalbitmap = bitmap_alloc(width, height, BITMAP_FORMAT_ARGB32);
963
 
        if (finalbitmap == NULL)
964
 
                goto error;
965
 
 
966
 
        /* now copy and compare each set of bitmaps */
967
 
        starty = 0;
968
 
        for (bmnum = 1; bmnum < bitmapcount; bmnum++)
969
 
        {
970
 
                bitmap_t *bitmap1 = bitmaps[0];
971
 
                bitmap_t *bitmap2 = bitmaps[bmnum];
972
 
                int curheight = MAX(bitmap1->height, bitmap2->height);
973
 
                int x, y;
974
 
 
975
 
                /* iterate over rows in these bitmaps */
976
 
                for (y = 0; y < curheight; y++)
977
 
                {
978
 
                        UINT32 *src1 = (y < bitmap1->height) ? BITMAP_ADDR32(bitmap1, y, 0) : NULL;
979
 
                        UINT32 *src2 = (y < bitmap2->height) ? BITMAP_ADDR32(bitmap2, y, 0) : NULL;
980
 
                        UINT32 *dst1 = BITMAP_ADDR32(finalbitmap, starty + y, 0);
981
 
                        UINT32 *dst2 = BITMAP_ADDR32(finalbitmap, starty + y, bitmap1->width + BITMAP_SPACE);
982
 
                        UINT32 *dstdiff = BITMAP_ADDR32(finalbitmap, starty + y, bitmap1->width + BITMAP_SPACE + maxwidth + BITMAP_SPACE);
983
 
 
984
 
                        /* now iterate over columns */
985
 
                        for (x = 0; x < maxwidth; x++)
986
 
                        {
987
 
                                int pix1 = -1, pix2 = -2;
988
 
 
989
 
                                if (src1 != NULL && x < bitmap1->width)
990
 
                                        pix1 = dst1[x] = src1[x];
991
 
                                if (src2 != NULL && x < bitmap2->width)
992
 
                                        pix2 = dst2[x] = src2[x];
993
 
                                dstdiff[x] = (pix1 != pix2) ? 0xffffffff : 0xff000000;
994
 
                        }
995
 
                }
996
 
 
997
 
                /* update the starting Y position */
998
 
                starty += BITMAP_SPACE + MAX(bitmap1->height, bitmap2->height);
999
 
        }
1000
 
 
1001
 
        /* write the final PNG */
1002
 
        filerr = core_fopen(astring_c(dstfilename), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file);
1003
 
        if (filerr != FILERR_NONE)
1004
 
                goto error;
1005
 
        pngerr = png_write_bitmap(file, NULL, finalbitmap, 0, NULL);
1006
 
        core_fclose(file);
1007
 
        if (pngerr != PNGERR_NONE)
1008
 
                goto error;
1009
 
 
1010
 
        /* if we get here, we are error free */
1011
 
        error = 0;
1012
 
 
1013
 
error:
1014
 
        if (finalbitmap != NULL)
1015
 
                bitmap_free(finalbitmap);
1016
 
        for (bmnum = 0; bmnum < bitmapcount; bmnum++)
1017
 
                if (bitmaps[bmnum] != NULL)
1018
 
                        bitmap_free(bitmaps[bmnum]);
1019
 
        if (error)
1020
 
                osd_rmfile(astring_c(dstfilename));
1021
 
        astring_free(dstfilename);
1022
 
        astring_free(srcimgname);
1023
 
        astring_free(tempname);
1024
 
        return error;
1025
 
}
1026
 
 
1027
 
 
1028
 
/*-------------------------------------------------
1029
 
    create_linked_file - create a comparison
1030
 
    file between differing versions
1031
 
-------------------------------------------------*/
1032
 
 
1033
 
static void create_linked_file(const astring *dirname, const summary_file *curfile, const summary_file *prevfile, const summary_file *nextfile, const char *pngfile, const astring *tempheader, const astring *tempfooter)
1034
 
{
1035
 
        astring *linkname = astring_alloc();
1036
 
        astring *filename = astring_alloc();
1037
 
        astring *title = astring_alloc();
1038
 
        core_file *linkfile;
1039
 
        int listnum;
1040
 
 
1041
 
        /* create the filename */
1042
 
        astring_printf(filename, "%s.html", curfile->name);
1043
 
 
1044
 
        /* output header */
1045
 
        astring_printf(title, "%s Regressions (%s)", curfile->name, curfile->source);
1046
 
        astring_printf(linkname, "%s" PATH_SEPARATOR "%s", astring_c(dirname), astring_c(filename));
1047
 
        linkfile = create_file_and_output_header(linkname, tempheader, title);
1048
 
        if (linkfile == NULL)
1049
 
        {
1050
 
                fprintf(stderr, "Error creating file '%s'\n", astring_c(filename));
1051
 
                astring_free(title);
1052
 
                astring_free(filename);
1053
 
                astring_free(linkname);
1054
 
                return;
1055
 
        }
1056
 
 
1057
 
        /* link to the previous/next entries */
1058
 
        core_fprintf(linkfile, "\t<p>\n");
1059
 
        core_fprintf(linkfile, "\t<table width=\"100%%\">\n");
1060
 
        core_fprintf(linkfile, "\t\t<td align=\"left\" width=\"40%%\" style=\"border:none\">");
1061
 
        if (prevfile != NULL)
1062
 
                core_fprintf(linkfile, "<a href=\"%s.html\"><< %s (%s)</a>", prevfile->name, prevfile->name, prevfile->source);
1063
 
        core_fprintf(linkfile, "</td>\n");
1064
 
        core_fprintf(linkfile, "\t\t<td align=\"center\" width=\"20%%\" style=\"border:none\"><a href=\"index.html\">Home</a></td>\n");
1065
 
        core_fprintf(linkfile, "\t\t<td align=\"right\" width=\"40%%\" style=\"border:none\">");
1066
 
        if (nextfile != NULL)
1067
 
                core_fprintf(linkfile, "<a href=\"%s.html\">%s (%s) >></a>", nextfile->name, nextfile->name, nextfile->source);
1068
 
        core_fprintf(linkfile, "</td>\n");
1069
 
        core_fprintf(linkfile, "\t</table>\n");
1070
 
        core_fprintf(linkfile, "\t</p>\n");
1071
 
 
1072
 
        /* output data for each one */
1073
 
        for (listnum = 0; listnum < list_count; listnum++)
1074
 
        {
1075
 
                int imageindex = -1;
1076
 
 
1077
 
                /* generate the HTML */
1078
 
                core_fprintf(linkfile, "\n\t<h2>%s</h2>\n", lists[listnum].version);
1079
 
                core_fprintf(linkfile, "\t<p>\n");
1080
 
                core_fprintf(linkfile, "\t<b>Status:</b> %s\n", status_text[curfile->status[listnum]]);
1081
 
                if (pngfile != NULL)
1082
 
                        imageindex = get_unique_index(curfile, listnum);
1083
 
                if (imageindex != -1)
1084
 
                        core_fprintf(linkfile, " [%d]", imageindex);
1085
 
                core_fprintf(linkfile, "\t</p>\n");
1086
 
                if (curfile->text[listnum] != NULL)
1087
 
                {
1088
 
                        core_fprintf(linkfile, "\t<p>\n");
1089
 
                        core_fprintf(linkfile, "\t<b>Errors:</b>\n");
1090
 
                        core_fprintf(linkfile, "\t<pre>%s</pre>\n", curfile->text[listnum]);
1091
 
                        core_fprintf(linkfile, "\t</p>\n");
1092
 
                }
1093
 
        }
1094
 
 
1095
 
        /* output link to the image */
1096
 
        if (pngfile != NULL)
1097
 
        {
1098
 
                core_fprintf(linkfile, "\n\t<h2>Screenshot Comparisons</h2>\n");
1099
 
                core_fprintf(linkfile, "\t<p>\n");
1100
 
                core_fprintf(linkfile, "\t<img src=\"%s\" />\n", pngfile);
1101
 
                core_fprintf(linkfile, "\t</p>\n");
1102
 
        }
1103
 
 
1104
 
        /* output footer */
1105
 
        output_footer_and_close_file(linkfile, tempfooter, title);
1106
 
        astring_free(title);
1107
 
        astring_free(filename);
1108
 
        astring_free(linkname);
1109
 
}
1110
 
 
1111
 
 
1112
 
/*-------------------------------------------------
1113
 
    append_driver_list_table - append a table
1114
 
    of drivers from a list to an HTML file
1115
 
-------------------------------------------------*/
1116
 
 
1117
 
static void append_driver_list_table(const char *header, const astring *dirname, core_file *indexfile, const summary_file *listhead, const astring *tempheader, const astring *tempfooter)
1118
 
{
1119
 
        const summary_file *curfile, *prevfile;
1120
 
        int width = 100 / (2 + list_count);
1121
 
        int listnum;
1122
 
 
1123
 
        /* output a header */
1124
 
        core_fprintf(indexfile, "\t<h2>%s</h2>\n", header);
1125
 
 
1126
 
        /* start the table */
1127
 
        core_fprintf(indexfile, "\t<p><table width=\"90%%\">\n");
1128
 
        core_fprintf(indexfile, "\t\t<tr>\n\t\t\t<th width=\"%d%%\">Source</th><th width=\"%d%%\">Driver</th>", width, width);
1129
 
        for (listnum = 0; listnum < list_count; listnum++)
1130
 
                core_fprintf(indexfile, "<th width=\"%d%%\">%s</th>", width, lists[listnum].version);
1131
 
        core_fprintf(indexfile, "\n\t\t</tr>\n");
1132
 
 
1133
 
        /* if nothing, print a default message */
1134
 
        if (listhead == NULL)
1135
 
        {
1136
 
                core_fprintf(indexfile, "\t\t<tr>\n\t\t\t");
1137
 
                core_fprintf(indexfile, "<td colspan=\"%d\" align=\"center\">(No regressions detected)</td>", list_count + 2);
1138
 
                core_fprintf(indexfile, "\n\t\t</tr>\n");
1139
 
        }
1140
 
 
1141
 
        /* iterate over files */
1142
 
        for (prevfile = NULL, curfile = listhead; curfile != NULL; prevfile = curfile, curfile = curfile->next)
1143
 
        {
1144
 
                int rowspan = 0, uniqueshots = 0;
1145
 
                char pngdiffname[40];
1146
 
 
1147
 
                /* if this is the first entry in this source file, count how many rows we need to span */
1148
 
                if (prevfile == NULL || strcmp(prevfile->source, curfile->source) != 0)
1149
 
                {
1150
 
                        const summary_file *cur;
1151
 
                        for (cur = curfile; cur != NULL; cur = cur->next)
1152
 
                                if (strcmp(cur->source, curfile->source) == 0)
1153
 
                                        rowspan++;
1154
 
                                else
1155
 
                                        break;
1156
 
                }
1157
 
 
1158
 
                /* create screenshots if necessary */
1159
 
                pngdiffname[0] = 0;
1160
 
                for (listnum = 0; listnum < list_count; listnum++)
1161
 
                        if (curfile->matchbitmap[listnum] == listnum)
1162
 
                                uniqueshots++;
1163
 
                if (uniqueshots > 1)
1164
 
                {
1165
 
                        sprintf(pngdiffname, "compare_%s.png", curfile->name);
1166
 
                        if (generate_png_diff(curfile, dirname, pngdiffname) != 0)
1167
 
                                pngdiffname[0] = 0;
1168
 
                }
1169
 
 
1170
 
                /* create a linked file */
1171
 
                create_linked_file(dirname, curfile, prevfile, curfile->next, (pngdiffname[0] == 0) ? NULL : pngdiffname, tempheader, tempfooter);
1172
 
 
1173
 
                /* create a row */
1174
 
                core_fprintf(indexfile, "\t\t<tr>\n\t\t\t");
1175
 
                if (rowspan > 0)
1176
 
                        core_fprintf(indexfile, "<td rowspan=\"%d\">%s</td>", rowspan, curfile->source);
1177
 
                core_fprintf(indexfile, "<td><a href=\"%s.html\">%s</a></td>", curfile->name, curfile->name);
1178
 
                for (listnum = 0; listnum < list_count; listnum++)
1179
 
                {
1180
 
                        int unique_index = -1;
1181
 
 
1182
 
                        if (pngdiffname[0] != 0)
1183
 
                                unique_index = get_unique_index(curfile, listnum);
1184
 
                        if (unique_index != -1)
1185
 
                                core_fprintf(indexfile, "<td><span style=\"%s\">&nbsp;&nbsp;&nbsp;</span> %s [<a href=\"%s\" target=\"blank\">%d</a>]</td>", status_color[curfile->status[listnum]], status_text[curfile->status[listnum]], pngdiffname, unique_index);
1186
 
                        else
1187
 
                                core_fprintf(indexfile, "<td><span style=\"%s\">&nbsp;&nbsp;&nbsp;</span> %s</td>", status_color[curfile->status[listnum]], status_text[curfile->status[listnum]]);
1188
 
                }
1189
 
                core_fprintf(indexfile, "\n\t\t</tr>\n");
1190
 
 
1191
 
                /* also print the name and source file */
1192
 
                printf("%s %s\n", curfile->name, curfile->source);
1193
 
        }
1194
 
 
1195
 
        /* end of table */
1196
 
        core_fprintf(indexfile, "</table></p>\n");
1197
 
}