/** GameBoy Cartridge Lister/Tester **************************/   
/**                                                         **/
/**                         gblist.c                        **/
/**                                                         **/
/** This program will list and [optionally] test a group of **/
/** cartridges finding those which fail CRC or complement   **/
/** check, or have invalid size.                            **/
/**                                                         **/
/** Copyright (C) Marat Fayzullin 1996                      **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/
/**     changes to this file.                               **/
/*************************************************************/

#include <stdio.h>
#include <string.h>

typedef unsigned char byte;
typedef unsigned short word;

int CheckCRC=0,UseANSI=0,FixCRC=0;

void Error(char *File,char *Text)
{
  printf
  (
    "|%-20s|%s %-48s %s|\n",
    File,UseANSI? "\033[31m":"",Text,UseANSI? "\033[0m":""
  );
}

int main(int argc,char *argv[])
{
  static char *Types[] =
  { "R...","R..1","RW.1","RWB1","????","R..2","R.B2" };
  static char *RAMSizes[] =
  { " 0kB"," 2kB"," 8kB","32kB","    " };

  static byte Buf[0x4000];
  char S[15];
  FILE *F;
  int J,I,K;

  char Name[50],*Type,Comment[1024];
  word Checksum,Producer,RealCRC;
  byte Version,Complement,RealComp;
  int RAMSize,ROMBanks;

  if(argc<2)
  {
    fprintf(stderr,"GBLIST .GB File Processor by Marat Fayzullin\n");
    fprintf(stderr,"Usage: %s [-caf] files...\n", argv[0]);
    fprintf(stderr,"    -a - Use ANSI escape sequences for colors\n");
    fprintf(stderr,"    -c - Check CRC\n");
    fprintf(stderr,"    -f - Fix CRC\n");
    return(1);
  }

  CheckCRC=UseANSI=FixCRC=0;

  for(I=1;I<argc;I++)
    if(*argv[I]=='-')
      for(J=1;argv[I][J];J++)
        switch(argv[I][J])
        {
          case 'c': CheckCRC=1;break;
          case 'a': UseANSI=1;break;
          case 'f': FixCRC=1;break;
          default:
            fprintf(stderr,"%s: Unknown option -%c\n",argv[0],argv[I][J]);
        }

  puts("+--------------------+------------------+----+-----+----+----+--+-------+");
  puts("| File               | Name             |Type| ROM |RAM |Make|VE|CRC-Cmp|");
  puts("+--------------------+------------------+----+-----+----+----+--+-------+");

  for(J=(*argv[1]=='-')? 2:1;J<argc;J++)
    if(*argv[J]!='-')
      if(!(F=fopen(argv[J],"rb"))) Error(argv[J],"Couldn't open file");
      else
      {
        if(fread(Buf,1,0x4000,F)!=0x4000)
          Error(argv[J],"Couldn't read header");
        else
        {
          Comment[0]='\0';

          for(I=0x134;(I<0x144)&&Buf[I];I++)
            Name[I-0x134]=(Buf[I]>' ')? Buf[I]:' ';
          Name[I-0x134]='\0';

          if((Buf[0x0147]<=7)&&(Buf[0x0147]!=4))
          {
            Type=Types[Buf[0x0147]];
            ROMBanks=2<<Buf[0x148];
            RAMSize=((Buf[0x0147]!=2)&&(Buf[0x0147]!=3))? 4:Buf[0x149]&0x03;
          }
          else
          {
            Type="????";ROMBanks=1;RAMSize=4;
            strcat(Comment,"[Invalid Type]");
          }
          strcpy(S,RAMSizes[RAMSize]);
          Checksum=((word)Buf[0x14E]<<8)+Buf[0x14F];
          Complement=Buf[0x14D];
          Producer=((word)Buf[0x14B]<<8)+Buf[0x14A];
          Version=Buf[0x14C];

          /*** Checking the cartridge complement, CRC, size: ***/
          if(CheckCRC)
          {
            for(I=0x134,RealComp=25;I<0x14D;I++) RealComp+=Buf[I];
            RealComp=0x100-RealComp;
            if(RealComp!=Complement)
              sprintf
              (
                Comment+strlen(Comment),
                "Wrong Complement: %02X!=%02X.",
                RealComp,Complement
              );

            RealCRC=-Buf[0x14E]-Buf[0x14F];
            for(K=0;K<0x4000;K++) RealCRC+=Buf[K];
            for(I=ROMBanks-1;I>0;I--)
              if(fread(Buf,1,0x4000,F)!=0x4000) break;
              else for(K=0;K<0x4000;K++) RealCRC+=Buf[K];

            if(I) strcat(Comment,"[File Too Short]");
            else
              if(fgetc(F)!=EOF) strcat(Comment,"File is too long. ");
              else
                if(Checksum!=RealCRC)
                  sprintf
                  (
                    Comment+strlen(Comment),
                    "Wrong CRC: %04X!=%04X.",
                    RealCRC,Checksum
                  );
          }

          /*** Printing out the information line ***/
          if(*Comment)
            printf
            (
              "|%-20s|%s %-48s %s|\n",
              argv[J],UseANSI? "\033[35m":"",Comment,UseANSI? "\033[0m":""
            );
          else       
            printf
            (
              "|%-20s| %-16s |%s|%3dkB|%4s|%04X|%2d|%04X-%02X|\n",
              argv[J],Name,Type,ROMBanks*16,S,Producer,Version,
              Checksum,Complement
            );
        }
        fclose(F);
      }

  puts("+--------------------+------------------+----+-----+----+----+--+-------+");
}
