/* 
    TiMidity++ -- MIDI to WAVE converter and player
    Copyright (C) 1999 Masanao Izumo <mo@goice.co.jp>
    Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	Macintosh interface for TiMidity
	by T.Nogami	<t-nogami@happy.email.ne.jp>
		
    mac_mag.c
    Macintosh mag loader
*/

/*
 *X mag loader By Takanori Watanabe. Based on Mac mag loader.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "timidity.h"
#include "common.h"
#include "x_mag.h"
#include "controls.h"
#include "wrd.h"
#define BUFSIZE 512
static const char *MAGSIG="MAKI02  ";

#define READSHORT(p) ((int)*(uint8*)((p)+1)*256+(int)*(uint8 *)(p))
#define READLONG(p) (\
		     (int)*(uint8*)((p)+3)*256*256*256+\
		     (int)*(uint8*)((p)+2)*256*256+\
		     (int)*(uint8*)((p)+1)*256+\
		     (int)*(uint8 *)(p)\
		     )
#define MAGHDRLEN 31

static void mag_delete(magdata *mg);

static magdata *top=NULL;
struct magfilehdr{
  char machine;
  char mflag;
  char scrmode;
  char xorig[2];
  char yorig[2];
  char xend[2];
  char yend[2];
  char flagaoff[4];
  char flagboff[4];
  char flagbsize[4];
  char pxloff[4];
  char pxlsize[4];
};
static int read_flag_1line(magdata * mh,uint8 *flag )
{
  int		x, flagA,flagB,j;
  j=0;
  for( x=0; x<=mh->xend-(mh->xorig/8*8); ){
    flagA= mh->flagadata[mh->flagapos/8] & (0x80 >> (mh->flagapos & 0x07) );
    mh->flagapos++;
    if( flagA ){
      flagB= mh->flagbdata[mh->flagbpos++];
    }else{
      flagB= 0;
    }
    flag[x] ^= flagB>>4;	 x+=4;j++;
    flag[x] ^= flagB & 0x0F; x+=4;j++;
  }
  return j;
}

/*This Function Requires Blank XImage*/
Bool pho_load_pixel(XImage *image,XColor *pallet,char *filename){
  int i,j,k,l;
  static const int shift[4]={0,2,1,3};/*B,R,G,E*/
  struct timidity_file *fp;
  char buffer[640/8];

  if((fp=wrd_open_file(filename))==NULL)
    return False;
  for(l=0;l<4;l++){
    for(i=0;i<400;i++){
      if(tf_read(buffer,sizeof(buffer),1,fp)==0){
	goto close;
      }
      for(j=0;j<sizeof(buffer);j++){
	for(k=0;k<8;k++)
	  if((buffer[j]<<k)&0x80)
	    XPutPixel(image,j*8+k,i,XGetPixel(image,j*8+k,i)
		      |pallet[1<<shift[l]].pixel);
	  else
	    XPutPixel(image,j*8+k,i,XGetPixel(image,j*8+k,i)
		      |pallet[0].pixel);
      }
    }
  }
 close:
  close_file(fp);
  return True;
}

void mag_load_pixel(XImage *image,XColor *pallet,magdata *mh)
{
  int 		x,y,i, dx,dy,pxlpos,w,j,repl;
  uint8 flag[640];
  long pixels;
  const int	DX[]={0,-4,-8,-16,  0,-4,  0,-4,-8,  0,-4,-8,  0,-4,-8, 0},
  DY[]={0, 0, 0,  0, -1,-1, -2,-2,-2, -4,-4,-4, -8,-8,-8, -16};
  memset(flag,0,640);
  mh->flagapos=mh->flagbpos=pxlpos=0;
  w=image->width;
  for( y=0; y<=mh->yend-mh->yorig; y++ ){
    repl=read_flag_1line( mh,flag );
    x=0;
    for( j=0; j<repl; j++ ){
      if( flag[x]==0 ){
	pixels=mh->pxldata[pxlpos]*256+mh->pxldata[pxlpos+1];
	pxlpos+=2;
	for( i=3; i>=0; i-- ){
	  if(x+i < w)
	    XPutPixel(image,x+i, y, pallet[pixels & 0x000F].pixel);
	  pixels >>= 4;
	}
      } else {
	dx=DX[flag[x]];
	dy=DY[flag[x]];
	for(i=0;i<4;i++) {
	  if(x+i < w)
	    XPutPixel(image,x+i,y,XGetPixel(image,x+dx+i,y+dy));
	}
      }
      x+=4;
    }
  }
}

void mag_deletetab(void)
{
  if(top!=NULL){
    mag_delete(top);
  }
  top=NULL;
}
static void mag_delete(magdata *mg){
  if(mg->next!=NULL)
    mag_delete(mg->next);
  free(mg->filename);
  free(mg->flagadata);
  free(mg->flagbdata);
  free(mg->pxldata);
  free(mg);
}
magdata *mag_search(char *filename)
{
  magdata *m;
  for(m=top;m!=NULL;m=m->next)
    if(strcmp(filename,m->filename)==0)
      return m;
  return NULL;
}
magdata *mag_create(char *file)
{

  struct timidity_file *fp=NULL;
  int i;
  uint8 buffer[BUFSIZE];
  int *pal;
  magdata mg,*res;
  struct magfilehdr mfh;
  int len,c,header_pos;
  char *flaga=NULL,*flagb=NULL,*pixels=NULL;
  if((res=mag_search(file))){
    return res;
  }
  mg.filename=safe_malloc(strlen(file)+2);
  strcpy(mg.filename,file);
  fp=wrd_open_file(file);
  if(fp==NULL){
    goto error;
  }
  header_pos=0;
  while((c=tf_getc(fp))!=0){
    len=strlen(MAGSIG);
    if(header_pos<len)
      buffer[header_pos]=c;
    else if(header_pos==len){
      if(memcmp(MAGSIG,buffer,len)!=0){
	ctl->cmsg(CMSG_INFO,VERB_VERBOSE,"BAD SIGNATURE\n");
	goto error;
      }
    }
    header_pos++;
  }
  /*READ FHDR*/
  {
    if(tf_read(&mfh,sizeof(mfh),1,fp)==0){
      fprintf(stderr,"FOO!\n");
      goto error;
    }
    mg.machine=mfh.machine;
    mg.mflag=mfh.mflag;
    mg.scrmode=mfh.scrmode;
    mg.xorig=READSHORT(mfh.xorig);
    mg.yorig=READSHORT(mfh.yorig);
    mg.xend=READSHORT(mfh.xend);
    mg.yend=READSHORT(mfh.yend);
    mg.flagaoff=READLONG(mfh.flagaoff);
    mg.flagboff=READLONG(mfh.flagboff);
    mg.flagbsize=READLONG(mfh.flagbsize);
    mg.pxloff=READLONG(mfh.pxloff);
    mg.pxlsize=READLONG(mfh.pxlsize);
  }
  /*READ PALLET*/
  pal=mg.pal+1;
  for(i=0;i<PALSIZE;i++){
    if(tf_read(buffer,1,3,fp)<3){
      exit(-1);
    }
#if 0
    pal[i]=buffer[0]/16+buffer[1]/16*256+buffer[2]/16*16;
#else
    pal[i]=buffer[0]/16*16+buffer[1]/16*256+buffer[2]/16;
#endif
  } 
  
  {
    int flaga_size,res;
    flaga_size=mg.flagboff-mg.flagaoff;
    if(tf_seek(fp,mg.flagaoff+header_pos,SEEK_SET)==-1)
      goto error;
    flaga=safe_malloc(flaga_size+100);
    if((res=tf_read(flaga,1,flaga_size,fp))<flaga_size)
      goto error;
    mg.flagadata=flaga;
  }
  {
    flagb=safe_malloc(mg.flagbsize+100);
    if(tf_seek(fp,mg.flagboff+header_pos,SEEK_SET)==-1)
      goto error;
    if(tf_read(flagb,1,mg.flagbsize,fp)<mg.flagbsize)
      goto error;
    mg.flagbdata=flagb;
  }
  {
    pixels=safe_malloc(mg.pxlsize+100);
    if(tf_seek(fp,mg.pxloff+header_pos,SEEK_SET)==-1)
      goto error;
    if(tf_read(pixels,1,mg.pxlsize,fp)<mg.pxlsize)
      goto error;
    mg.pxldata=pixels;
  }
  res=safe_malloc(sizeof(magdata));
  
  *res=mg;
  close_file(fp);
  res->next=top;
  top=res;
  return res;
 error:
  if(fp!=NULL)
  {
    close_file(fp);
    ctl->cmsg(CMSG_INFO,VERB_VERBOSE,"Mag error: %s\n", file);
  }
  free(mg.filename);
  if(flaga != NULL)
      free(flaga);
  if(flagb != NULL)
      free(flagb);
  if(pixels != NULL)
      free(pixels);
  return NULL;
}
