/* rezrov.c
 * Extracts resources from a blorb file
 * Written by Felix Grützmacher and placed into the public domain */

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

/* The following might need changing on your machine */
typedef unsigned int UI32;
/* Define this if you're compiling for an Intel machine */
#define _INTEL

typedef struct {
  char usage[4];
  UI32 num;
  UI32 start;
} RES_T;

#define MAX_RES (256)

void extract(RES_T res, FILE *blb);
#ifdef _INTEL
void endian_transform(UI32 *p);
#endif
unsigned num_ext = 0;

int main(int argc, char *argv[]) {
  FILE *blb;
  UI32 num_res=0;
  RES_T res[MAX_RES];
  UI32 i;
  if(argc<2) {
    fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
    exit(1);
  }

  if(!(blb=fopen(argv[1],"rb"))) {
    perror(argv[1]);
    exit(2);
  }
  fseek(blb, 20, SEEK_SET);
  if(fread(&num_res, 4, 1, blb)!=1) {
    if(feof(blb))
      fputs("End of file while reading resource index.\n", stderr);
    else perror("Error getting number of resources");
    exit(3);
  }
#ifdef _INTEL
  endian_transform(&num_res);
#endif
  if(num_res>MAX_RES)
    num_res=MAX_RES;
  if(fread(res, sizeof(RES_T), num_res, blb)!=num_res) {
    if(feof(blb))
      fputs("End of file while reading resource index.\n", stderr);
    else perror("Error reading resource index");
    exit(3);
  }

  for(i=0; i<num_res; i++)
    if(!strncmp(res[i].usage, "Snd ", 4) || !strncmp(res[i].usage, "Pict ", 4) || !strncmp(res[i].usage, "Exec", 4))
      extract(res[i], blb);

  /* Our work is done */
  fclose(blb);
  printf("Successfully captured %u resources!\n", num_ext);
  return 0;
}

void extract(RES_T res, FILE *blb) {
  char type[4];
  UI32 len;
  char fname[16]; /* max 10 digits + ".xxxx" + '\0' */
  FILE *out;
  UI32 i;
  int c;
  int tmp;
  const char *ext;
#ifdef _INTEL
  endian_transform(&(res.start));
  endian_transform(&(res.num));
#endif
  fseek(blb, res.start, SEEK_SET);
  if(fread((void *)type, 1, 4, blb)!=4) {
    if(feof(blb))
      fputs("End of file while reading resource type.\n", stderr);
    else perror("Error reading resource type");
    exit(3);
  }

  switch(*type) {
  case 'M':
    ext = ".mod";
    break;
  case 'F':
    ext = ".aiff";
    break;
  case 'P':
    ext = ".png";
    break;
  case 'Z':
    ext = ".z";
    break;
  case 'G':
    ext = ".ulx";
    break;
  default:
    return; /* skip resources of unknown type */
  }
  if(fread(&len, 4, 1, blb)!=1) {
    if(feof(blb))
      fputs("End of file while reading resource length.\n", stderr);
    else perror("Error reading resource length");
    exit(3);
  }
  tmp=len;
#ifdef _INTEL
  endian_transform(&len);
#endif
  sprintf(fname, "%u", res.num);
  strcat(fname, ext);
  if(!(out=fopen(fname, "wb"))) {
    perror(fname);
    exit(4);
  }

  /* If this is an aiff, write "FORM" and len to output */
  if(*type=='F') {
    if(fwrite(type, 1, 4, out)!=4) {
      perror("Error writing lead-in FORM tag");
      exit(4);
    }
    if(fwrite(&len, 4, 1, out)!=1) {
      perror("Error writing lead-in form length");
      exit(4);
    }
  }
  /* Copy the data */
  for(i=0; i<len; i++) {
    if((c=getc(blb))==EOF) {
      if(ferror(blb))
        perror("Error reading resource data");
      else fputs("End of file while reading resource data.\n", stderr);
      exit(3);
    }
    if(putc(c, out)==EOF) {
      perror(fname);
      exit(4);
    }
  }
  fclose(out);
  num_ext++;
}

#ifdef _INTEL
void endian_transform(UI32 *p) {
  UI32 r = *p;
  r = (r>>24) | ((r>>8)&0x0000ff00) | ((r<<8)&0x00ff0000) | (r<<24);
  *p=r;
}
#endif
