/* 
LzmaTest.c
Test application for LZMA Decoder
LZMA SDK 4.02 Copyright (c) 1999-2004 Igor Pavlov (2004-06-10)
*/

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

#include "LzmaDecode.h"

size_t MyReadFile(FILE *file, void *data, size_t size)
{
  return (fread(data, 1, size, file) == size);
}

#ifdef _LZMA_IN_CB
typedef struct _CBuffer
{
  ILzmaInCallback InCallback;
  unsigned char *Buffer;
  unsigned int Size;
} CBuffer;

int LzmaReadCompressed(void *object, unsigned char **buffer, unsigned int *size)
{
  CBuffer *bo = (CBuffer *)object;
  *size = bo->Size; /* You can specify any available size here */
  *buffer = bo->Buffer;
  bo->Buffer += *size; 
  bo->Size -= *size;
  return LZMA_RESULT_OK;
}
#endif

int main2(int numargs, const char *args[], char *rs)
{
  FILE *inputHandle, *outputHandle;
  unsigned int length, processedSize;
  unsigned int compressedSize, outSize, outSizeProcessed, lzmaInternalSize;
  void *inStream, *outStream, *lzmaInternalData;
  unsigned char properties[5];
  unsigned char prop0;
  int ii;
  int lc, lp, pb;
  int res;
  #ifdef _LZMA_IN_CB
  CBuffer bo;
  #endif

  sprintf(rs + strlen(rs), "\nLZMA Decoder 4.02 Copyright (c) 1999-2004 Igor Pavlov  2004-06-10\n");
  if (numargs < 2 || numargs > 3)
  {
    sprintf(rs + strlen(rs), "\nUsage:  lzmaDec file.lzma [outFile]\n");
    return 1;
  }

  inputHandle = fopen(args[1], "rb");
  if (inputHandle == 0)
  {
    sprintf(rs + strlen(rs), "\n Open input file error");
    return 1;
  }

  fseek(inputHandle, 0, SEEK_END);
  length = ftell(inputHandle);
  fseek(inputHandle, 0, SEEK_SET);

  if (!MyReadFile(inputHandle, properties, sizeof(properties)))
    return 1;
  
  outSize = 0;
  for (ii = 0; ii < 4; ii++)
  {
    unsigned char b;
    if (!MyReadFile(inputHandle, &b, sizeof(b)))
      return 1;
    outSize += (unsigned int)(b) << (ii * 8);
  }

  if (outSize == 0xFFFFFFFF)
  {
    sprintf(rs + strlen(rs), "\nstream version is not supported");
    return 1;
  }

  for (ii = 0; ii < 4; ii++)
  {
    unsigned char b;
    if (!MyReadFile(inputHandle, &b, sizeof(b)))
      return 1;
    if (b != 0)
    {
      sprintf(rs + strlen(rs), "\n too long file");
      return 1;
    }
  }

  compressedSize = length - 13;
  inStream = malloc(compressedSize);
  if (inStream == 0)
  {
    sprintf(rs + strlen(rs), "\n can't allocate");
    return 1;
  }
  if (!MyReadFile(inputHandle, inStream, compressedSize))
  {
    sprintf(rs + strlen(rs), "\n can't read");
    return 1;
  }

  fclose(inputHandle);

  prop0 = properties[0];
  if (prop0 >= (9*5*5))
  {
    sprintf(rs + strlen(rs), "\n Properties error");
    return 1;
  }
  for (pb = 0; prop0 >= (9 * 5); 
    pb++, prop0 -= (9 * 5));
  for (lp = 0; prop0 >= 9; 
    lp++, prop0 -= 9);
  lc = prop0;

  lzmaInternalSize = 
    (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb);

  #ifdef _LZMA_OUT_READ
  lzmaInternalSize += 100;
  #endif

  outStream = malloc(outSize);
  lzmaInternalData = malloc(lzmaInternalSize);
  if (outStream == 0 || lzmaInternalData == 0)
  {
    sprintf(rs + strlen(rs), "\n can't allocate");
    return 1;
  }

  #ifdef _LZMA_IN_CB
  bo.InCallback.Read = LzmaReadCompressed;
  bo.Buffer = (unsigned char *)inStream;
  bo.Size = compressedSize;
  #endif

  #ifdef _LZMA_OUT_READ
  {
    UInt32 nowPos;
    unsigned char *dictionary;
    UInt32 dictionarySize = 0;
    int i;
    for (i = 0; i < 4; i++)
      dictionarySize += (UInt32)(properties[1 + i]) << (i * 8);
    dictionary = malloc(dictionarySize);
    if (dictionary == 0)
    {
      sprintf(rs + strlen(rs), "\n can't allocate");
      return 1;
    }
    LzmaDecoderInit((unsigned char *)lzmaInternalData, lzmaInternalSize,
        lc, lp, pb,
        dictionary, dictionarySize,
        #ifdef _LZMA_IN_CB
        &bo.InCallback
        #else
        (unsigned char *)inStream, compressedSize
        #endif
        );
    for (nowPos = 0; nowPos < outSize;)
    {
      UInt32 blockSize = outSize - nowPos;
      UInt32 kBlockSize = 0x10000;
      if (blockSize > kBlockSize)
        blockSize = kBlockSize;
      res = LzmaDecode((unsigned char *)lzmaInternalData, 
      ((unsigned char *)outStream) + nowPos, blockSize, &outSizeProcessed);
      if (res != 0)
      {
        sprintf(rs + strlen(rs), "\nerror = %d\n", res);
        return 1;
      }
      if (outSizeProcessed == 0)
      {
        outSize = nowPos;
        break;
      }
      nowPos += outSizeProcessed;
    }
    free(dictionary);
  }

  #else
  res = LzmaDecode((unsigned char *)lzmaInternalData, lzmaInternalSize,
      lc, lp, pb,
      #ifdef _LZMA_IN_CB
      &bo.InCallback,
      #else
      (unsigned char *)inStream, compressedSize,
      #endif
      (unsigned char *)outStream, outSize, &outSizeProcessed);
  outSize = outSizeProcessed;
  #endif

  if (res != 0)
  {
    sprintf(rs + strlen(rs), "\nerror = %d\n", res);
    return 1;
  }

  if (numargs > 2)
  {
    outputHandle = fopen(args[2], "wb+");
    if (outputHandle == 0)
    {
      sprintf(rs + strlen(rs), "\n Open output file error");
      return 1;
    }
    processedSize = fwrite(outStream, 1, outSize, outputHandle);
    if (processedSize != outSize)
    {
      sprintf(rs + strlen(rs), "\n can't write");
      return 1;
    }
    fclose(outputHandle);
  }
  free(lzmaInternalData);
  free(outStream);
  free(inStream);
  return 0;
}

int main(int numargs, const char *args[])
{
  char sz[800] = { 0 };
  int code = main2(numargs, args, sz);
  printf(sz);
  return code;
}
