/* 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