// in_hunex: unpack hunex archive (only ProjectWitch? unconfirmed).
// Usage: in_hunex outdir
// 0.01.090817 prototype. without checking extention.
// 0.02.090817 completed.
// 0.03.090817 fixed ahx checking.

//config
#define Marray 10
#define Mname  2000
#define bufsize 0x100000 //1M

typedef unsigned int  uint;
typedef unsigned char byte;

#define fourcc(a,b,c,d) ((uint)(byte)(a) | ((uint)(byte)(b)<<8) | ((uint)(byte)(c)<<16) | ((uint)(byte)(d)<<24))

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

typedef struct{
  char name[Mname];
  uint size;
  FILE *f;
  int count;
}farray;

farray ff[Marray];
char out[Mname];

//routines
const uint read32(FILE *f){
  char s[4];
  fread(s,1,4,f);
  return fourcc(s[0],s[1],s[2],s[3]);
}

const unsigned char *memstr(const unsigned char *s1, const char *s2, const int c){
  const int len=strlen(s2),size=c-len;
  int i=0,j;
  for(;i<=size;i++){
    for(j=0;j<len;j++)
      if(s1[i+j]!=s2[j])break;
    if(j==len)return s1+i;
  }
  return NULL;
}

const unsigned char *memmem(const unsigned char *s1, const unsigned char *s2, const int c, const int len){
  const int size=c-len;
  int i=0,j;
  for(;i<=size;i++){
    for(j=0;j<len;j++)
      if(s1[i+j]!=s2[j])break;
    if(j==len)return s1+i;
  }
  return NULL;
}

const char *findext(const byte *s, const int len){ //tenuki
  if(!memcmp(s,"sce",3))return "sce";
  if(!memcmp(s,"\x89PNG",4))return "png";
  if(s[3]+(s[2]<<8)-2<len-6&&!memcmp(s+s[3]+(s[2]<<8)-2,"(c)CRI",6))
    return (s[4]==0x10||s[4]==0x11)?"ahx":"adx";
  if(s[4]+(s[5]<<8)<len-6&&!memcmp(s+s[4]+(s[5]<<8),"VAGp",4))return "vag";
  if(!memcmp(s,"AIXF",4))return "aix";
  if(!memcmp(s,"RIFF",4))return "at3";
  if(!memcmp(s,"IKA ",4))return "ika";
  if(!memcmp(s,"LIT\0",4))return "lit";
  if(memmem(s,(const byte*)"TTH\0",len,4))return "tth";
  if(memmem(s,(const byte*)"HAP\0",len,4))return "hap";
  if(memmem(s,(const byte*)"EFF\0",len,4))return "eff";
  return "bin";
}

void output(FILE* f, const uint begin, const uint end, const char *arc, const uint count){
  byte buf[bufsize];
  uint size=end-begin,_size=size;
  int x;
  FILE *fout;

  mkdir(arc);
  fseek(f,begin,SEEK_SET);
  x=fread(buf,1,10000,f);
  sprintf(out,"%s/%s_%05u.%s",arc,arc,count,findext(buf,x)); //todo: check ext.
  fout=fopen(out,"wb");
  fseek(f,begin,SEEK_SET);
  for(;size>bufsize;size-=bufsize){
    fread(buf,1,bufsize,f);fwrite(buf,1,bufsize,fout);
    printf("Expanding %s %d/%d\r",out,_size-size,_size);
  }
  fread(buf,1,size,f);fwrite(buf,1,size,fout);
  printf("Expanded %s %d/%d \n",out,_size,_size);
  fclose(fout);
}

int main(const int argc, const char **argv){
  FILE *dir,*cmn;
  uint n=0,i=0,count=0,icount;
  uint *aindex;

  printf("<<< in_hunex by II3zjBzeS2 >>>\n");

  //input
  if(!(dir=fopen("0flist.dir","rb"))){printf("Err: cannot open 0flist.dir\n");return 1;}
  if(!(cmn=fopen("cmn.bin","rb"))){fclose(dir);printf("Err: cannot open cmn.bin\n");return 1;}
  for(;n<Marray;n++){
    int len;
redo:
    if(!fgets(ff[n].name,Mname,dir))break;
    len=strlen(ff[n].name);
    if(ff[n].name[len-1]=='\n'){ff[n].name[len-1]=0;len--;}
    if(ff[n].name[len-1]=='\r'){ff[n].name[len-1]=0;len--;}
    if(!(ff[n].f=fopen(ff[n].name,"rb"))){printf("Warn: cannot open %s\n",ff[n].name);goto redo;}
    fseek(ff[n].f,0,SEEK_END);
    ff[n].size=ftell(ff[n].f);
    fseek(ff[n].f,0,SEEK_SET);
    ff[n].count=0;
    for(i=0;i<len;i++){if(ff[n].name[i]=='.')ff[n].name[i]=0;}
  }
  fclose(dir);

  if(argc>1)
    if(_chdir(argv[1])<0)printf("Warn: cannot chdir\n");
  icount=read32(cmn);
  aindex=(uint*)malloc(icount*4+4);
  for(i=0;i<=icount;i++)aindex[i]=read32(cmn);

  //output
  for(i=0;i<icount;i++){
    fseek(cmn,aindex[i],SEEK_SET);
    if(read32(cmn)==fourcc('H','E','D',0)){
      uint files,j=0,x;
      uint *bindex;
      read32(cmn);files=read32(cmn);read32(cmn);
      bindex=(uint*)malloc(files*4+4);
      for(j=0;j<=files;j++)bindex[j]=read32(cmn);
      for(j=0;j<n;j++)if(ff[j].size==bindex[files]){x=j;break;}
      if(j==n){printf("Warn: unknown filesize %u: skip\n",bindex[files]);free(bindex);continue;}
      for(j=0;j<files;j++)output(ff[x].f,bindex[j],bindex[j+1],ff[x].name,j);
      free(bindex);
    }else{
      output(cmn,aindex[i],aindex[i+1],"cmn",count);
      count++;
    }
  }
  free(aindex);
  printf("Everything is Ok\n");return 0;
}
