/*
 * makesfx - Makes a QDOS sfx zip file
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * not copyrighted at all by Jonathan Hudson, 04/09/95
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#define SFXFLAG "??Special Flag for unzipsfx hack  ??"

#ifdef QDOS
# include <qdos.h>
# define ZMODE (X_OK|R_OK)
# define rev_long(x) (x)
# define XFLAG 0x4afb
#else
# define ZMODE (R_OK)
# define getchid(p1) p1
# include <sys/stat.h>
  long rev_long(long l);
# define XFLAG 0xfb4a

typedef struct
{
    long id;
    long dlen;
} NTC;

struct qdirect  {
    long            d_length __attribute__ ((packed));  /* file length */
    unsigned char   d_access __attribute__ ((packed));  /* file access type */
    unsigned char   d_type __attribute__ ((packed));    /* file type */
    long            d_datalen __attribute__ ((packed)); /* data length */
    long            d_reserved __attribute__ ((packed));/* Unused */
    short           d_szname __attribute__ ((packed));  /* size of name */
    char            d_name[36] __attribute__ ((packed));/* name area */
    long            d_update __attribute__ ((packed));  /* last update */
    long            d_refdate __attribute__ ((packed));
    long            d_backup __attribute__ ((packed));   /* EOD */
} ;

int fs_headr (int fd, long t, struct qdirect *qs, short size)
{
    NTC ntc;
    int r = -1;
    struct stat s;

    fstat(fd, &s);
    qs->d_length = s.st_size;
    lseek(fd, -8, SEEK_END);
    read(fd, &ntc, 8);
    if(ntc.id == *(long *)"XTcc")
    {
        qs->d_datalen = ntc.dlen;    /* This is big endian */
        qs->d_type = 1;
        r = 0;
    }
    lseek(fd, 0, 0);
    return 42;                       /* why not ??? */
}

typedef unsigned char uch;

long rev_long (long l)
{
    uch cc[4];
    cc[0] = (uch)(l >> 24);
    cc[1] = (uch)((l >> 16) & 0xff);
    cc[2] = (uch)((l >> 8) & 0xff);
    cc[3] = (uch)(l & 0xff);
    return *(long *)cc;
}

#endif

#define RBUFSIZ 4096

void usage(void)
{
    fputs("makesfx -o outfile -z zipfile -xunzipsfx -sstubfile\n", stderr);
    exit(0);
}

int main (int ac, char **av)
{
    int fd, fo;
    static char local_sig[4] = "PK\003\004";
    char *p, tmp[4];
    short ok = 0;
    char *of = NULL;
    char *xf = NULL;
    char *zf = NULL;
    char *sf = NULL;
    int c;

    while((c = getopt(ac, av, "o:z:x:s:h")) != EOF)
    {
        switch(c)
        {
            case 'o':
                of = optarg;
                break;
            case 'z':
                zf = optarg;
                break;
            case 'x':
                xf = optarg;
                break;
            case 's':
                sf = optarg;
                break;
            case 'h':
                usage();
                break;
        }
    }


    if(zf && xf && of && sf)
    {
        if((fd = open(zf, O_RDONLY)) > 0)
        {
            if((read(fd, tmp, 4) == 4))
            {
                if(*(long *)tmp == *(long *)local_sig)
                {
                    ok = 1;
                }
            }
            close(fd);
        }
        if(!ok)
        {
            fprintf(stderr,
                    "Huum, %s doesn't look like a ZIP file to me\n", zf);
            exit(0);
        }

        if(strstr(xf, "unzipsfx"))
        {
            if(access(xf, ZMODE))
            {
                fprintf(stderr, "Sorry, don't like the look of %s\n", xf);
                exit(0);
            }
        }

        if((fo = open(of, O_CREAT|O_TRUNC|O_RDWR, 0666)) != -1)
        {
            struct qdirect sd,xd;
            int n;
            int dsoff = 0;
            int nfoff = 0;
            int zlen = 0;

            if((fd = open(sf, O_RDONLY)) != -1)
            {
                if(fs_headr(getchid(fd), -1, &sd, sizeof(sd)) > 0)
                {
                    unsigned short *q;
                    p = malloc(sd.d_length);
                    n = read(fd, p, sd.d_length);
                    for(q = (unsigned short *)p;
                        q != (unsigned short *)(p+sd.d_length); q++)
                    {
                        if(*q == XFLAG && *(q+1) == XFLAG)
                        {
                            dsoff = (int)q-(int)p;
                            break;
                        }
                    }
                    write(fo, p, n);
                    close(fd);
                }
            }

            if(dsoff == 0)
            {
                puts("Fails");

                exit(0);
            }

            if((fd = open(xf, O_RDONLY)) != -1)
            {
                char *q;
                if(fs_headr(getchid(fd), -1, &xd, sizeof(xd)) > 0)
                {
                    p = realloc(p, xd.d_length);
                    n = read(fd, p, xd.d_length);
                    {
                        for(q = p; q < p+xd.d_length ; q++)
                        {
                            if(*q == '?')
                            {
                                if(memcmp(q, SFXFLAG, sizeof(SFXFLAG)-1) == 0)
                                {
                                    nfoff = (int)(q-p);
                                    break;
                                }
                            }
                        }
                    }
                    write(fo, p, n);
                    close(fd);

                    if((fd = open(zf, O_RDONLY)) > 0)
                    {
                        p = realloc(p, RBUFSIZ);
                        while((n = read(fd, p, RBUFSIZ)) > 0)
                        {
                            write(fo, p, n);
                            zlen += n;
                        }
                        close(fd);
                    }
                    lseek(fo, dsoff+4, SEEK_SET);
                    n = rev_long((sd.d_length-dsoff));
                    write(fo, &n, sizeof(long));
                    n = rev_long(xd.d_length);
                    write(fo, &n, sizeof(long));
                    write(fo, &xd.d_datalen, sizeof(long));
                    n = rev_long(nfoff);
                    write(fo, &n, sizeof(long));
                    n = rev_long(zlen);
                    write(fo, &n, sizeof(long));
                    close(fo);
                }
                else
                {
                    close(fd);
                    fputs("Can't read unzipsfx header", stderr);
                    exit(0);
                }
            }
            free(p);
        }
    }
    else
        usage();

    return 0;
}
