#include <stdio.h>
#include <stdlib.h>
//#include <mem.h>
#include <string.h>

#define _STRMAX 79

#define BYTE unsigned char
#define MAXDEFB 8

#define MASKCOLOR 0x00

BYTE filebuf[65536];
BYTE scrbuf[65536]; //6144
BYTE attrbuf[65536]; //6144
char labelbuf[_STRMAX+1];
char formatlabelbuf[_STRMAX+1];
BYTE sizeword[4];
BYTE pic[1024][1024];
BYTE pixrow[1024/8][1024+1024];
#define PIXROWSHIFT 0/*1024*/
BYTE maskrow[1024/8][1024];
//BYTE pixrowshift[1024/8][1024]; //>>4
BYTE attrrow[1024/8][1024];

BYTE ink;
BYTE paper;
BYTE curink;
BYTE curpaper;
BYTE defaultcolor;

char sprformat;

int hgt;
int wid;
int bpp;

int readnum(FILE * fin)
{
char c;
int num;
  num = 0;
  do{
    if (!fread(&c,1,1,fin)) break;
    if ((c<'0')||(c>'9')) break; //в том числе 0x0a
    num = num*10 + (int)(c-'0');
  }while(1);
return num;
}

unsigned int readlabel(FILE * fin, char * s)
{
char c;
unsigned int i;
int iscomment;
  do{
    i = 0;
    iscomment = 0;
    do{
      if (!fread(&c,1,1,fin)) break;
      if (c == ';') iscomment = -1;
      if ((c=='=')||(c==',')) {
        if (iscomment) continue; //в комментах можно =
        break;
      };
      if (c == 0x0d) continue;
      if (c == 0x0a) {
        if (iscomment) break; //комменты кончаются по концу строки
        continue;
      };
      if (i==_STRMAX) break;
      s[i] = c;
      i++;
    }while(1);
    s[i] = '\0';
  }while(iscomment);
return i;
}


void emitdb(BYTE b, FILE * fout)
{
  fputs("\tdb ", fout);
  fprintf(fout, "0x%x%x", b>>4, b&0x0f);
  fputs("\n", fout);
}

void emitdw(char* str, BYTE b, FILE * fout)
{
  fputs("\tdw ", fout);
  fputs(str, fout);
  fprintf(fout, "%d", b);
  fputs("\n", fout);
}

void emitnops(BYTE count, FILE * fout)
{
  fputs("\tds ", fout);
  fprintf(fout, "0x%x%x", count>>4, count&0x0f);
  fputs("\n", fout);
}

void emitbyte(BYTE b, FILE * fout)
{
  fprintf(fout, "0x%x%x", b>>4, b&0x0f);
}

void emitspr(int xchr, int y, int sprwid8, int sprhgt, FILE * fout)
//0 1 2(3mask) <--- для высот 18,14... начинаем отсюда
//7 6 5(4mask)
//8 9 (attr)
//11 10 (attr) <--- для высот 16,12... начинаем отсюда
//12 13 14(15mask)
//19 18 16(17mask)
{
BYTE b;
int i;
int j;
  j = y;
  while (1) {
    if ((j!=y)||((sprhgt&2)!=0)) {
      fputs("\tdb ", fout);
        emitbyte(pixrow[xchr][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+1][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+2][j], fout);
        fputs(",", fout);
        emitbyte(maskrow[xchr+2][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+2][j+1], fout);
        fputs(",", fout);
        emitbyte(maskrow[xchr+2][j+1], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+1][j+1], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr][j+1], fout);
      fputs("\n", fout);
      fputs("\tdb ", fout);
        emitbyte(attrrow[xchr][j], fout);
        fputs(",", fout);
        emitbyte(attrrow[xchr+1][j], fout);
      fputs("\n", fout);
      j+=2;
    };
    if (j >= (y+sprhgt)) break;
    fputs("\tdb ", fout);
      emitbyte(attrrow[xchr+1][j], fout);
      fputs(",", fout);
      emitbyte(attrrow[xchr][j], fout);
    fputs("\n", fout);
    fputs("\tdb ", fout);
      emitbyte(pixrow[xchr][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+1][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+2][j], fout);
      fputs(",", fout);
      emitbyte(maskrow[xchr+2][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+2][j+1], fout);
      fputs(",", fout);
      emitbyte(maskrow[xchr+2][j+1], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+1][j+1], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr][j+1], fout);
    fputs("\n", fout);
    j+=2;
    if (j >= (y+sprhgt)) break;
  };
}

void emitspr_x4(int xchr, int y, int sprwid8, int sprhgt, FILE * fout)
//зеркально по сравнению с этой схемой:
//0 1 2(3mask) <--- для высот 18,14... начинаем отсюда
//7 6 5(4mask)
//8 9 (attr)
//11 10 (attr) <--- для высот 16,12... начинаем отсюда
//12 13 14(15mask)
//19 18 16(17mask)
{
BYTE b;
int i;
int j;
  j = y;
  while (1) {
    if ((j!=y)||((sprhgt&2)!=0)) {
      fputs("\tdb ", fout);
        emitbyte(pixrow[xchr+2][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+1][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr][j], fout);
        fputs(",", fout);
        emitbyte(maskrow[xchr][j], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr][j+1], fout);
        fputs(",", fout);
        emitbyte(maskrow[xchr][j+1], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+1][j+1], fout);
        fputs(",", fout);
        emitbyte(pixrow[xchr+2][j+1], fout);
      fputs("\n", fout);
      fputs("\tdb ", fout);
        emitbyte(attrrow[xchr+2][j], fout);
        fputs(",", fout);
        emitbyte(attrrow[xchr+1][j], fout);
      fputs("\n", fout);
      j+=2;
    };
    if (j >= (y+sprhgt)) break;
    fputs("\tdb ", fout);
      emitbyte(attrrow[xchr+1][j], fout);
      fputs(",", fout);
      emitbyte(attrrow[xchr+2][j], fout);
    fputs("\n", fout);
    fputs("\tdb ", fout);
      emitbyte(pixrow[xchr+2][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+1][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr][j], fout);
      fputs(",", fout);
      emitbyte(maskrow[xchr][j], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr][j+1], fout);
      fputs(",", fout);
      emitbyte(maskrow[xchr][j+1], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+1][j+1], fout);
      fputs(",", fout);
      emitbyte(pixrow[xchr+2][j+1], fout);
    fputs("\n", fout);
    j+=2;
    if (j >= (y+sprhgt)) break;
  };
}

//сдвигаем ряд знакомест >>shiftbits, результат в pixrow[sprx][y+PIXROWSHIFT]
void shiftrow(int sprx, int y, int sprwid, int rowhgt, int pixrowshift, BYTE shiftbits)
{
int j;
int x;
BYTE b;
BYTE b0;
BYTE bm;
BYTE bm0;
BYTE shiftmask;
  shiftmask = (BYTE)(0xff>>(0x08-shiftbits)); //если shiftbits==0x01, то shiftmask==0x01
  j = y;
  while (j < (y+rowhgt)) {
    b = 0x00;
    bm = 0xff;
    x = sprx;
    while (x < (sprx+sprwid+8)) {
      b0 = pixrow[x/8][j];
      pixrow[x/8][j+pixrowshift] = (BYTE)((b<<(0x08-shiftbits)) + (b0>>shiftbits));
      b = (BYTE)(b0&shiftmask/**0x0f*/); //если shiftbits==0x01, то shiftmask==0x01
      bm0 = maskrow[x/8][j];
      maskrow[x/8][j+pixrowshift] = (BYTE)((bm<<(0x08-shiftbits)) + (bm0>>shiftbits));
      bm = (BYTE)(bm0&shiftmask/**0x0f*/); //если shiftbits==0x01, то shiftmask==0x01
      x = x+8;
    };
    j++;
  };
}

//сдвигаем ряд знакомест <<shiftbits, результат в pixrow[sprx][y+PIXROWSHIFT]
void shiftrowleft(int sprx, int y, int sprwid, int rowhgt, int pixrowshift, BYTE shiftbits)
{
int j;
int x;
BYTE b;
BYTE b0;
BYTE bm;
BYTE bm0;
BYTE shiftmask;
  shiftmask = (BYTE)(0xff<<(0x08-shiftbits)); //если shiftbits==0x01, то shiftmask==0x80
  j = y;
  while (j < (y+rowhgt)) {
    b = 0x00;
    bm = 0xff;
    x = (sprx+sprwid);
    do {
      x = x-8;
      b0 = pixrow[x/8][j];
      pixrow[x/8][j+pixrowshift] = (BYTE)((b>>(0x08-shiftbits)) + (b0<<shiftbits));
      b = (BYTE)(b0&shiftmask); //если shiftbits==0x01, то shiftmask==0x80
      bm0 = maskrow[x/8][j];
      bm0 = (BYTE)((bm>>(0x08-shiftbits)) + (bm0<<shiftbits));
      maskrow[x/8][j+pixrowshift] = bm0;//(BYTE)((bm>>(0x08-shiftbits)) + (bm0<<shiftbits));
      bm = (BYTE)(bm0&shiftmask); //если shiftbits==0x01, то shiftmask==0x80
    }while (x != sprx);
    j++;
  };
}

int scrindex(int x, int y)
{
  return (x/8) + ((y&0xc0)/64*0x800) + ((y&0x38)/8*0x20) + ((y&0x07)*0x100);
}

int attrindex(int x, int y)
{
  return (x/8) + ((y&0xc0)/64*0x800) + ((y&0x38)/8*0x100) + ((y&0x07)*0x20);
}

void decodeline(int sprx, int y, int rowhgt) //перекодируем ряд столбцов высотой rowhgt
{
int x;
int j;
BYTE b;
BYTE bmask;
BYTE b0;

  j = y;
  while (j < (y+rowhgt)) {
    x = sprx;
    //while (x < (sprx+sprwid)) {
      b = (BYTE)(scrbuf[scrindex(x,j)])&0x0f;
      bmask = (BYTE)(scrbuf[scrindex(x,j)])&0xf0;
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = bmask;
      attrrow[x/8][j] = (BYTE)(attrbuf[attrindex(x,j)]);
      x = x+8;
      b = (BYTE)(scrbuf[scrindex(x,j)]);
      bmask = 0xff;
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = bmask;
      attrrow[x/8][j] = (BYTE)(attrbuf[attrindex(x,j)]);
      x = x+8;
      b = (BYTE)(scrbuf[scrindex(x,j)])&0xc0;
      bmask = (BYTE)(scrbuf[scrindex(x,j)])&0x3f;
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = bmask;
      attrrow[x/8][j] = attrrow[x/8-1][j]; //повторяем правый атрибут (чтобы по мастер-спрайту X=2 получить все сдвиги)
    //};
    j++;
  };
};

void decodeline_noxshift(int sprx, int y, int rowhgt) //перекодируем ряд столбцов высотой rowhgt
{
int x;
int j;
BYTE b;
BYTE bmask;
BYTE b0;

  j = y;
  while (j < (y+rowhgt)) {
    x = sprx;
    //while (x < (sprx+sprwid)) {
      b = (BYTE)(scrbuf[scrindex(x,j)]);
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = 0;
      attrrow[x/8][j] = (BYTE)(attrbuf[attrindex(x,j)]);
      x = x+8;
      b = (BYTE)(scrbuf[scrindex(x,j)]);
      bmask = 0xff;
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = 0;
      attrrow[x/8][j] = (BYTE)(attrbuf[attrindex(x,j)]);
      x = x+8;
      b = (BYTE)(scrbuf[scrindex(x,j)]);
      pixrow[x/8][j] = b;
      maskrow[x/8][j] = 0;
      attrrow[x/8][j] = attrrow[x/8-1][j]; //повторяем правый атрибут (чтобы по мастер-спрайту X=2 получить все сдвиги)
    //};
    j++;
  };
};

void resfile(char * finname, char * fintxtname, char * foutname)
{
FILE* fin;
FILE* fintxt;
FILE* fout;
int i;
int j;
int size;
int y;
int x;
int addr;
BYTE b;

int sprx;
int spry;
int sprwid;
int sprhgt;
int rowhgt; //8 for tiles, sprhgt for sprites

  fin = fopen(finname, "rb");
  if (fin) {
    //fread(filebuf, 10, 1, fin); //skip to 10 (header size)
    //size = read4b(fin); //10 (header size)
    //fread(filebuf, 4, 1, fin); //skip to 18
    wid = 32;//read4b(fin); //18
    hgt = 192; //read4b(fin); //22
    //fread(filebuf, 2, 1, fin); //skip to 28
    //fread(&bpp, 1, 1, fin); //28
    //fread(filebuf, 1, size-29, fin); //skip to pic

    if (strstr(finname,".btile")) {
      fread(filebuf, 1, 48*16*16, fin);
      addr = 0;
      j = 0;
      while (j < 256) {
        i = 0;
        while (i < 256) {
          y = 0;
          while (y < 16) { //pixels in tile
            b = filebuf[addr + (y*2)];
            scrbuf[scrindex(i, j+y)] = b;
            b = filebuf[addr + (y*2) + 1];
            scrbuf[scrindex(i+8, j+y)] = b;
            y++;
          };
          y = 0;
          while (y < 16) { //attrs in tile //реально такая загрузка переворачивает атрибуты вверх ногами, так не надо! TODO
            b = filebuf[addr + 32 + 7-(y/2)];
            attrbuf[attrindex(i, j+y)] = b;
            b = filebuf[addr + 32 + 8 + 7-(y/2)];
            attrbuf[attrindex(i+8, j+y)] = b;
            y++;
          };
          addr = addr+48; //next tile
          i = i+16;
        };
        j = j+16;
      };
    }else if (strstr(finname,".mlt")) {
      fread(scrbuf, 1, 6144, fin);
      fread(attrbuf, 1, 6144, fin);
    }else { //.scr to .btile
      wid = 32;
      hgt = 192;
      fread(scrbuf, 1, 6144, fin);
      fread(filebuf, 1, 768, fin);
      j = 0;
      while (j < hgt) { //lines
        //addr = ((j&0x40) + ((j&0x07)<<8) + ((j&0x38)>>8))<<5;
        i = 0;
        while (i < wid) {
          b = filebuf[((j>>3)<<5) + i];
          attrbuf[attrindex(i<<3, j)] = b;
          i = i+1;
        };
        j = j+1;
      };
      fout = fopen(foutname, "wb");
      if (fout) {
        addr = 0;
        j = 0;
        while (j < 256) {
          i = 0;
          while (i < 256) {
            y = 0;
            while (y < 16) { //pixels in tile
              b = scrbuf[scrindex(i, j+y)];
              filebuf[addr + (y*2)] = b;
              b = scrbuf[scrindex(i+8, j+y)];
              filebuf[addr + (y*2) + 1] = b;
              y++;
            };
            y = 0;
            while (y < 16) { //attrs in tile
              b = attrbuf[attrindex(i, j+y)];
              filebuf[addr + 32 + (y/2)] = b;
              b = attrbuf[attrindex(i+8, j+y)];
              filebuf[addr + 32 + 8 + (y/2)] = b;
              y = y+2;
            };
            addr = addr+48; //next tile
            i = i+16;
          };
          j = j+16;
        };
        fwrite(filebuf, 1, 12288, fout);
        fclose(fout);
      }else {printf("can't open %s",foutname);};
      goto closefile;
    };

    if ((wid>0)&&(wid<=1024)&&(hgt>0)&&(hgt<=1024)&&((wid&7)==0)&&((hgt&7)==0)) {
/*      y = hgt;
      while (y>0) {
        y--;
        x = 0;
        while (x<wid) {
          fread(&b, 1, 1, fin);
          if (bpp == 8) {
            pic[x][y] = b;
            x++;
          }else {
            pic[x][y] = (BYTE)((b&0xf0)>>4);
            x++;
            pic[x][y] = (BYTE)(b&0x0f);
            x++;
          };
        };
      };*/

      fintxt = fopen(fintxtname, "rb");
      if (fintxt) {
        fout = fopen(foutname, "wb");
        if (fout) {
/**          if (labelname[0]!='\0') {
            fputs(labelname, fout);
            fputs("\n", fout);
          };*/
          while (1) {
            size = readlabel(fintxt, labelbuf); //fread(filebuf, 1, MAXDEFB, fin);
            if (size == 0) break;
            readlabel(fintxt, formatlabelbuf); //format
            sprformat = *formatlabelbuf;
            sprx = readnum(fintxt);
            spry = readnum(fintxt);
            sprwid = readnum(fintxt);
            sprhgt = readnum(fintxt);
            defaultcolor = (BYTE)readnum(fintxt);

            if ((sprformat == 'h')||(sprformat == 'H')||(sprformat == 'I')||(sprformat == 'm')) {
              rowhgt = sprhgt;
            };

            if ((sprformat == 't')||(sprformat == 'a')) {
              rowhgt = 16;
            };

            if (sprformat == 'm') {
              y = spry;
              while (y < (spry+sprhgt)) {
                x = sprx;
                while (x < (sprx+sprwid)) {
                  fwrite(&scrbuf[scrindex(x,y)],1,1,fout);
                  x = x+8;
                };
                y = y+1;
              };
              y = spry;
              while (y < (spry+sprhgt)) {
                x = sprx;
                while (x < (sprx+sprwid)) {
                  fwrite(&attrbuf[attrindex(x,y)],1,1,fout);
                  x = x+8;
                };
                y = y+1;
              };
            }; //if


            y = spry;
            while (y < (spry+sprhgt)) {

              //выводим в асм
              if (sprformat == 'h') {
                decodeline(sprx,y,rowhgt); //перекодируем ряд столбцов высотой rowhgt
//пусть мастер-спрайтом будет X=2
//мы прочитали спрайт в следующем виде:
//MMMMssSS SSSSSSSS ssMMMM.. (TODO реально битами маски являются те, которые продолжают единичку (если она есть) с левого/правого края, в графику они тогда не идут)
//надо из него сгенерировать 4 сдвига:
//00ssSSSS SSSSSSss 00000000 (MMMM1111 маска)
//0000ssSS SSSSSSSS ss000000 (00MMMM11 маска)
//000000ss SSSSSSSS SSss0000 (11MMMM00 маска слева)
//00000000 ssSSSSSS SSSSss00 (1111MMMM маска слева)
                shiftrowleft(sprx, y, sprwid, rowhgt, PIXROWSHIFT, 0x02); //сдвигаем ряд знакомест <<2, результат в pixrow[sprx][y+PIXROWSHIFT]
                fputs(labelbuf, fout);
                fputs("_x0\n", fout);
                emitdw("prspr_y0_x0_hgt",(BYTE)(sprhgt), fout);
                emitspr(sprx/8,y,sprwid/8,sprhgt,fout);
                decodeline(sprx,y,rowhgt); //перекодируем ряд столбцов высотой rowhgt
                //shiftrow(sprx, y, sprwid, rowhgt, PIXROWSHIFT, 0x02); //сдвигаем ряд знакомест >>2, результат в pixrow[sprx][y+PIXROWSHIFT]
                fputs(labelbuf, fout);
                fputs("_x2\n", fout);
                emitdw("prspr_y0_x0_hgt",(BYTE)(sprhgt), fout);
                emitspr(sprx/8,y,sprwid/8,sprhgt,fout);
                shiftrow(sprx, y, sprwid, rowhgt, PIXROWSHIFT, 0x02); //сдвигаем ряд знакомест >>2, результат в pixrow[sprx][y+PIXROWSHIFT]
                fputs(labelbuf, fout);
                fputs("_x4\n", fout);
                emitdw("prspr_y0_x4_hgt",(BYTE)(sprhgt), fout);
                emitspr_x4(sprx/8,y,sprwid/8,sprhgt,fout);
                shiftrow(sprx, y, sprwid, rowhgt, PIXROWSHIFT, 0x02); //сдвигаем ряд знакомест >>2, результат в pixrow[sprx][y+PIXROWSHIFT]
                fputs(labelbuf, fout);
                fputs("_x6\n", fout);
                emitdw("prspr_y0_x4_hgt",(BYTE)(sprhgt), fout);
                emitspr_x4(sprx/8,y,sprwid/8,sprhgt,fout);
              }; //if

              if (sprformat == 'H') {
                decodeline_noxshift(sprx,y,rowhgt); //перекодируем ряд столбцов высотой rowhgt
                fputs(labelbuf, fout);
                fputs("_x0\n", fout);
                fputs(labelbuf, fout);
                fputs("_x2\n", fout);
                fputs(labelbuf, fout);
                fputs("_x4\n", fout);
                fputs(labelbuf, fout);
                fputs("_x6\n", fout);
                emitdw("prspr_y0_x0_hgt",(BYTE)(sprhgt), fout);
                emitspr(sprx/8,y,sprwid/8,sprhgt,fout);
              }; //if

              if (sprformat == 'I') {
                decodeline(sprx,y,rowhgt); //перекодируем ряд столбцов высотой rowhgt
                fputs(labelbuf, fout);
                fputs("_x0\n", fout);
                fputs(labelbuf, fout);
                fputs("_x2\n", fout);
                fputs(labelbuf, fout);
                fputs("_x4\n", fout);
                fputs(labelbuf, fout);
                fputs("_x6\n", fout);
                emitdw("prspr_y0_x0_hgt",(BYTE)(sprhgt), fout);
                emitspr(sprx/8,y,sprwid/8,sprhgt,fout);
              }; //if

              if (sprformat == 't') {
                x = sprx;
                while (x < (sprx+sprwid)) {
                  fputs("\tdb ", fout);
                  j = y;
                  while (1) {
                    emitbyte((BYTE)(scrbuf[scrindex(x,j)]), fout);
                    j++;
                    if (j == (y+rowhgt)) break;
                    fputs(",", fout);
                  };
                  fputs("\n", fout);
                  x = x+8;
                };
              }; //if

              if (sprformat == 'a') {
                x = sprx;
                while (x < (sprx+sprwid)) {
                  fputs("\tdb ", fout);
                  j = y;
                  while (1) {
                    emitbyte((BYTE)(attrbuf[attrindex(x,j)]), fout);
                    j = j+2;
                    if (j == (y+rowhgt)) break;
                    fputs(",", fout);
                  };
                  fputs("\n", fout);
                  x = x+8;
                };
              }; //if

              y = y+rowhgt;
            }; //while (y < (spry+sprhgt))

          }; //while(1) //перебираем все спрайты
          fclose(fout);
        }else {printf("can't open %s",foutname);};
        fclose(fintxt);
      }else {printf("can't open %s",fintxtname);};
    };
closefile:
    fclose(fin);
  }else {printf("can't open %s",finname);};
}

int main(int argc,char* argv[])
{
//  int i;
  char *finname;
  char *fintxtname;
  char *foutname;
  finname = "hardymc.mlt";
  fintxtname = "hardyspr.dat";
  foutname = "hardyspr.ast";

  if (argc<4) {
    printf(
      "NedoRes for Hardy\n"
      "\thardycnv.exe file.bmp file.dat file.ast\n"
    );
  }else {
    finname = argv[1];
    fintxtname = argv[2];
    foutname = argv[3];
  };

  resfile(finname, fintxtname, foutname);

  return 0;
}