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

#include "efihack.h"


EFI_STATUS
EFIAPI
EfiGetInfo (
            IN      VOID                    *Source,
            IN      UINT32                  SrcSize,
            OUT     UINT32                  *DstSize,
            OUT     UINT32                  *ScratchSize
            );

EFI_STATUS
EFIAPI
EfiDecompress (
               IN      VOID                    *Source,
               IN      UINT32                  SrcSize,
               IN OUT  VOID                    *Destination,
               IN      UINT32                  DstSize,
               IN OUT  VOID                    *Scratch,
               IN      UINT32                  ScratchSize
               );

int main(int argc, char **argv)
{
    char *buffer;
    long buflen, fill;
    ssize_t got;
    char *dstbuf, *scratchbuf;
    UINT32 DstSize;
    UINT32 ScratchSize;
    EFI_STATUS Status;

    // read all data from stdin
    buflen = 32768;
    fill = 0;
    buffer = malloc(buflen);
    if (buffer == NULL) {
        fprintf(stderr, "Out of memory!\n");
        return 1;
    }
    for (;;) {
        if (fill == buflen) {
            long newbuflen;

            newbuflen = buflen << 1;
            buffer = realloc(buffer, newbuflen);
            if (buffer == NULL) {
                fprintf(stderr, "Out of memory!\n");
                return 1;
            }
            buflen = newbuflen;
        }

        got = read(0, buffer + fill, buflen - fill);
        if (got < 0) {
            fprintf(stderr, "Error during read: %d\n", errno);
            return 1;
        } else if (got == 0) {
            break;  // EOF
        } else {
            fill += got;
        }
    }

    //fprintf(stderr, "got %d bytes\n", fill);

    // inspect data
    Status = EfiGetInfo(buffer, fill, &DstSize, &ScratchSize);
    if (Status != EFI_SUCCESS) {
        fprintf(stderr, "EFI ERROR (get info)\n");
        return 1;
    }
    dstbuf = malloc(DstSize);
    scratchbuf = malloc(ScratchSize);
    if (dstbuf == NULL || scratchbuf == NULL) {
        fprintf(stderr, "Out of memory!\n");
        return 1;
    }

    // decompress data
    Status = EfiDecompress(buffer, fill, dstbuf, DstSize, scratchbuf, ScratchSize);
    if (Status != EFI_SUCCESS) {
        fprintf(stderr, "EFI ERROR (decompress)\n");
        return 1;
    }

    // write to stdout
    while (DstSize > 0) {
        got = write(1, dstbuf, DstSize);
        if (got < 0) {
            fprintf(stderr, "Error during write: %d\n", errno);
            return 1;
        } else {
            dstbuf += got;
            DstSize -= got;
        }
    }

    return 0;
}
