/* RipROMs
 * Original by Darren Salt
 * Additional OS patches by John Doe
 */

#include <stdio.h>
#include <stdlib.h>
// If your compiler can't find the following header file, you can
// safely remove the #include line.
#include <unistd.h>


typedef struct
{
  long offset, length;
} Basic;

typedef struct
{
  long offset;
  unsigned char value;
} Os;

Basic grab[] = {
  { 0x4BC8,0x0038 }, { 0x4C38,0x0008 }, { 0x4C50,0x0030 }, { 0x4CF0,0x0010 },
  { 0x4D1C,0x0024 }, { 0x4D60,0x0020 }, { 0x4DAC,0x0014 }, { 0x4DE8,0x0018 },
  { 0x4E18,0x0028 }, { 0x4E50,0x0030 }, { 0x4E88,0x0038 }, { 0x4EF8,0x0008 },
  { 0x4F2C,0x0014 }, { 0x4F70,0x0010 }, { 0x4FB0,0x0010 }, { 0x4FEC,0x0014 },
  { 0x5034,0x000C }, { 0x5070,0x0010 }, { 0x50BC,0x0004 }, { 0x50F0,0x0010 },
  { 0x5120,0x0020 }, { 0x5164,0x001C }, { 0x51B0,0x0010 }, { 0x51D4,0x002C },
  { 0x5230,0x0010 }, { 0x5254,0x002C }, { 0x52B4,0x000C }, { 0x52F8,0x0008 },
  { 0x5330,0x0010 }, { 0x5374,0x000C }, { 0x53B4,0x000C }, { 0x53EC,0x0014 },
  { 0x5438,0x0008 }, { 0x5448,0x0038 }, { 0x54EC,0x0014 }, { 0x551C,0x0024 },
  { 0x5560,0x0020 }, { 0x55A8,0x0018 }, { 0x55FC,0x0004 }, { 0x5618,0x0028 },
  { 0x5654,0x002C }, { 0x5688,0x0038 }, { 0x56FC,0x0004 }, { 0x572C,0x0014 },
  { 0x5770,0x0010 }, { 0x57B0,0x0010 }, { 0x57EC,0x0014 }, { 0x5834,0x000C },
  { 0x5870,0x0010 }, { 0x58BC,0x0004 }, { 0x58F0,0x0010 }, { 0x5920,0x0020 },
  { 0x5964,0x001C }, { 0x59B0,0x0010 }, { 0x59D4,0x002C }, { 0x5A30,0x0010 },
  { 0x5A54,0x002C }, { 0x5AB4,0x000C }, { 0x5B30,0x0010 }, { 0x5B74,0x000C },
  { 0x5BB4,0x000C }, { 0x5BFC,0x0004 }, { 0x5C38,0x0008 }, { 0x5C48,0x0038 },
  { 0x5CD0,0x0030 }, { 0x5D1C,0x0024 }, { 0x5D64,0x001C }, { 0x5DAC,0x0014 },
  { 0x5DD4,0x002C }, { 0x5E18,0x0028 }, { 0x5E58,0x0028 }, { 0x5EA0,0x0020 },
  { 0x5EEC,0x0014 }, { 0x5F2C,0x0014 }, { 0x5F74,0x000C }, { 0x5FB4,0x000C },
  { 0x5FEC,0x0014 }, { 0x6034,0x000C }, { 0x6070,0x0010 }, { 0x60D0,0x0030 },
  { 0x6120,0x0020 }, { 0x6168,0x0018 }, { 0x61B4,0x000C }, { 0x61E0,0x0020 },
  { 0x6230,0x0010 }, { 0x6254,0x002C }, { 0x62B8,0x0008 }, { 0x62D0,0x0030 },
  { 0x6330,0x0010 }, { 0x6378,0x0008 }, { 0x63B8,0x0008 }, { 0x63F0,0x0010 },
  { 0x6448,0x0038 }, { 0x648C,0x0034 }, { 0x64D4,0x002C }, { 0x6524,0x001C },
  { 0x6570,0x0010 }, { 0x65B8,0x0008 }, { 0x65D8,0x0028 }, { 0x6620,0x0020 },
  { 0x665C,0x0024 }, { 0x66A4,0x001C }, { 0x66E8,0x0018 }, { 0x6734,0x000C },
  { 0x6778,0x0008 }, { 0x67EC,0x0014 }, { 0x683C,0x0004 }, { 0x6878,0x0008 },
  { 0x688C,0x0034 }, { 0x68D8,0x0028 }, { 0x6928,0x0018 }, { 0x696C,0x0014 },
  { 0x69D0,0x0030 }, { 0x6A38,0x0008 }, { 0x6A58,0x0028 }, { 0x6AEC,0x0014 },
  { 0x6B38,0x0008 }, { 0x6B7C,0x0004 }, { 0x6BE8,0x0018 }, { 0x6C2C,0x0014 },
  { 0x6C50,0x0030 }, { 0x6CB4,0x000C }, { 0x6CD8,0x0028 }, { 0x6D18,0x0028 },
  { 0x6D58,0x0028 }, { 0x6D9C,0x0024 }, { 0x6DD4,0x002C }, { 0x6E28,0x0018 },
  { 0x6E50,0x0030 }, { 0x6E88,0x0038 }, { 0x6EE0,0x0020 }, { 0x6F20,0x0020 },
  { 0x6F60,0x0020 }, { 0x6FA4,0x001C }, { 0x6FEC,0x0014 }, { 0x7028,0x0018 },
  { 0x7064,0x001C }, { 0x70B4,0x000C }, { 0x70DC,0x0024 }, { 0x711C,0x0024 },
  { 0x715C,0x0024 }, { 0x71A0,0x0020 }, { 0x71D0,0x0030 }, { 0x7224,0x001C },
  { 0x7254,0x002C }, { 0x72B4,0x000C }, { 0x72E0,0x0020 }, { 0x7324,0x001C },
  { 0x7364,0x001C }, { 0x73AC,0x0014 }, { 0x73D8,0x0028 }, { 0x7438,0x0008 },
  { 0x7458,0x0028 }, { 0x74BC,0x0004 }, { 0x74DC,0x0024 }, { 0x751C,0x0024 },
  { 0x755C,0x0024 }, { 0x75A4,0x001C }, { 0x75D0,0x0030 }, { 0x7618,0x0028 },
  { 0x7650,0x0030 }, { 0x7688,0x0038 }, { 0x76EC,0x0014 }, { 0x772C,0x0014 },
  { 0x776C,0x0014 }, { 0x77AC,0x0014 }, { 0x77EC,0x0014 }, { 0x7834,0x000C },
  { 0x7870,0x0010 }, { 0x78B8,0x0008 }, { 0x78E0,0x0020 }, { 0x7920,0x0020 },
  { 0x7960,0x0020 }, { 0x79AC,0x0014 }, { 0x79D4,0x002C }, { 0x7A30,0x0010 },
  { 0x7A54,0x002C }, { 0x7AF0,0x0010 }, { 0x7B30,0x0010 }, { 0x7B70,0x0010 },
  { 0x7BB0,0x0010 }, { 0x7BE4,0x001C }, { 0x7C3C,0x0004 }, { 0x7C50,0x0030 },
  { 0x7C88,0x0038 }, { 0x7CE8,0x0018 }, { 0x7D28,0x0018 }, { 0x7D6C,0x0014 },
  { 0x7DB8,0x0008 }, { 0x7DD4,0x002C }, { 0x7E24,0x001C }, { 0x7E54,0x002C },
  { 0x7E88,0x0038 }, { 0x7EF8,0x0008 }, { 0x7F38,0x0008 }, { 0x7F74,0x000C },
  { 0x7FEC,0x0014 }, { 0x807C,0x0004 }, { 0x808C,0x0034 }, { 0x80D0,0x0030 },
  { 0x812C,0x0014 }, { 0x8168,0x0018 }, { 0x81D0,0x0030 }, { 0x823C,0x0004 },
  { 0x8254,0x002C }, { 0x82D0,0x0030 }, { 0x833C,0x0004 }, { 0x8378,0x0008 },
  { 0x83E4,0x001C }, { 0x8450,0x0030 }, { 0x848C,0x0034 }, { 0x84E8,0x0018 },
  { 0x8524,0x001C }, { 0x856C,0x0014 }, { 0x85B8,0x0008 }, { 0x85D4,0x002C },
  { 0x8620,0x0020 }, { 0x864C,0x0034 }, { 0x8688,0x0038 }, { 0x86F8,0x0008 },
  { 0x8734,0x000C }, { 0x8774,0x000C }, { 0x87EC,0x0014 }, { 0x883C,0x0004 },
  { 0x8878,0x0008 }, { 0x888C,0x0034 }, { 0x88D0,0x0030 }, { 0x8928,0x0018 },
  { 0x8968,0x0018 }, { 0x89D0,0x0030 }, { 0x8A38,0x0008 }, { 0x8A58,0x0028 },
  { 0x8AD0,0x0030 }, { 0x8B38,0x0008 }, { 0x8B78,0x0008 }, { 0xD644,0x2784 },
  { -1, -1 }
};

Os patch[] = {
  { 4040, 0240 },
  { 4041, 07 },
  { 4158, 012 },
  { 4159, 052 },
  { 7330, 0255 },
  { 7331, 010 },
  { 7332, 0376 },
  { 7706, 054 },
  { 7707, 0327 },
  { 7708, 02 },
  { 12505, 010 },
  { 12506, 0220 },
  { 12507, 02 },
  { 12508, 0240 },
  { 12509, 0356 },
  { 12654, 0376 },
  { 12657, 0336 },
  { 12658, 0241 },
  { 12659, 02 },
  { 12660, 020 },
  { 12661, 015 },
  { -1, 0 }
};

void Error(const char *fn, FILE *fb, int r)
{
  perror(fn);
  if (fb) fclose(fb);
  exit(r);
}


void Write(const char *fn, const char *image)
{
  FILE *out;

  out=fopen(fn, "wb");
  if (!out) {
    fprintf(stderr, "Failed to open %s file '%s'\n", "output",fn);
    exit(3);
  }
  if (fwrite(image, 0x4000, 1, out)!=1) Error(fn, out, 3);
  fclose(out);
}


int main(int argc, char *argv[])
{
  FILE *in;
  unsigned char BASIC[16384], OS[16384], *ROMptr=BASIC;
  int i=0;
  int flag=0;

  if (argc==2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H"))) {
    fprintf(stderr, "RipROMs version 1.00 (21 Jun 1996)\n\n");
    fprintf(stderr, "Usage: RipROMs <!65Host.!RunImage> <BASIC ROM file> <OS ROM file>\n");
    fprintf(stderr, "!65Host.!RunImage:\tSource file containing the ROMs. Should be from\n\t\t\t!65Host v1.61. Length 64698 bytes, dated 18 May 1992\nBASIC ROM file:\t\tTarget filename, to contain the BASIC ROM\nOS ROM file:\t\tTarget filename, to contain the OS ROM\n");
    return 0;
  }

  if (argc!=4) {
    fprintf(stderr, "Usage: RipROMs <!65Host.!RunImage> <BASIC ROM file> <OS ROM file>\n");
    return 1;
  }

  in=fopen(argv[1], "rb");
  if (!in) {
    fprintf(stderr, "Failed to open %s file '%s'\n", "input",argv[1]);
    return 2;
  }

/*
  {
    struct stat info;
    if (stat(argv[1], &info)<0) {
      perror(argv[1]);
      return 2;
    }
    flag=info.st_size!=64968;
  }
*/

  if (fseek(in, 0x3CL, SEEK_SET) || fread(BASIC, 0x31L, 1, in)!=1)
    Error(argv[1], in, 2);
  if (strcmp(BASIC, "6502 Emulator\t1.20 (18 May 1992) (BBC emulation)"))
    flag|=1;
  if (flag) {
    fclose(in);
    fprintf(stderr, "\
Input file not recognised. Either the filename was wrong, or you have an\n\
incorrect version of !65Host.\n");
    return 3;
  }

  do {
    if (fseek(in, grab[i].offset, SEEK_SET)
        || fread(ROMptr, grab[i].length, 1, in)!=1)
      Error(argv[1], in, 2);
    ROMptr+=grab[i].length;
  } while (grab[++i].offset!=-1);

  if (fseek(in, 0x9638L, SEEK_SET)
      || fread(OS, 0x4000, 1, in)!=1)
    Error(argv[1], in, 2);

  fclose(in);

  i = 0;
  do OS[patch[i].offset] = patch[i].value;
  while (patch[++i].offset!=-1);

  Write(argv[2], BASIC);
  Write(argv[3], OS);

  return 0;
}
