/* 
    lzmacat.c

    LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)

    Copyright (C) 2005 Ming-Ching Tiew mctiew@yahoo.com 
    lzmacat with adaptive memory usage
    Code derived from LzmaTest.c and LzmaStateTest.c from Igor Pavlov 

    GPL Source

*/

#include <stdio.h>

// hey guys sorry, I know include c files is strange ! 
#define _LZMA_IN_CB
#define _LZMA_OUT_READ
#include "LzmaDecode.h"
#include "LzmaDecode.c"
#include "LzmaDecode2.c"

#define INPUT_BUFFER_SIZE  0x10000
#define OUTPUT_BUFFER_SIZE 0x10000

typedef struct _CBuffer
{
  ILzmaInCallback InCallback;
  FILE *File;
  unsigned char *Buffer;
} CBuffer;

int LzmaReadCompressed(void *object, const unsigned char **buffer, SizeT *size)
{
  CBuffer *b = (CBuffer *)object;
  *buffer = b->Buffer;
  *size = fread(b->Buffer, 1, INPUT_BUFFER_SIZE, b->File);
  return LZMA_RESULT_OK;
}

int main(int argc, char **argv)
{
  FILE *infp;
  unsigned int outSize, outSizeProcessed;
  unsigned char *out_buffer;
  int i, res, zero;
  CBuffer cb;
  UInt32 nowPos;

  CLzmaDecoderState state;
  unsigned char properties[LZMA_PROPERTIES_SIZE];

  if (argc != 2) {
	fprintf(stderr, "Usage:  lzmacat file.lzma\n");
	return 1;
  }
	
  infp = fopen(argv[1], "rb");
  if (infp == 0) {
	fprintf(stderr, "Open input file error!\n");
	return 1;
  }

  if (!fread(properties, 1, sizeof(properties), infp)) {
	fprintf(stderr, "Can't read lzma properties!\n");
	return 1;
  }

  if( !fread(&outSize, 1, sizeof(outSize), infp)) {
	fprintf(stderr, "Can't read uncompressed size!\n");
  }
	
  if (outSize == 0xFFFFFFFF) {
	fprintf(stderr, "Stream version is not supported!\n");
	return 1;
  }
	
  if( !fread(&zero, 1, sizeof(zero), infp)) {
	fprintf(stderr, "Cannot read hi-byte uncompressed size!\n");
	return 1;
  }

  if (zero != 0) {
	fprintf(stderr, "File too big (bigger than 4 GB)!\n");
	return 1;
  }

  if( LzmaDecodeProperties( &state.Properties, properties, LZMA_PROPERTIES_SIZE ) != LZMA_RESULT_OK ) 
  {
	fprintf(stderr, "Lzma properties error!\n");
	return 1;
  }
  state.Probs = (CProb*) malloc(LzmaGetNumProbs(&state.Properties) * sizeof( CProb ));

  if( state.Probs  == 0 )
  {
	fprintf(stderr, "Cannot allocate memory for state.Probs!\n");
	return 1;
  }
	
  cb.File = infp;
  cb.InCallback.Read = LzmaReadCompressed;
  cb.Buffer = malloc(INPUT_BUFFER_SIZE);
  if (cb.Buffer == 0) 
  {
	fprintf(stderr, "Cannot allocate memory for input buffer!\n");
	free(state.Probs);
	return 1;
  }

  if ( outSize < state.Properties.DictionarySize )
  {
	out_buffer = malloc ( outSize );
	if ( out_buffer == 0 )
	{
	   fprintf(stderr, "Cannot allocated memory for output buffer!\n");
	   free(state.Probs);
	   free(cb.Buffer);
	   return 1;
	}
        res = LzmaDecodeSmall(  &state, &cb.InCallback,  
				out_buffer, outSize, &outSizeProcessed);
	if ( res == LZMA_RESULT_OK )
	   fwrite( out_buffer,1, outSizeProcessed, stdout);
  }
  else
  {
  	state.Dictionary=(unsigned char *)malloc(state.Properties.DictionarySize);
  	if ( state.Dictionary == 0) 
	{	
		fprintf(stderr, "Cannot allocate memory for dictionary!\n");
		free(state.Probs);
		free(cb.Buffer);
		return 1;
	}
	LzmaDecoderInit( &state );

	out_buffer = malloc (OUTPUT_BUFFER_SIZE);
	if(out_buffer==0) {
		fprintf(stderr, "Cannot allocate memory for output buffer!\n");
		free(state.Probs);
		free(cb.Buffer);
		free(state.Dictionary);
		return 1;
	}
	for (nowPos = 0; nowPos < outSize;) {
		UInt32 blockSize = outSize - nowPos;
		if (blockSize > OUTPUT_BUFFER_SIZE )
			blockSize = OUTPUT_BUFFER_SIZE;
		res = LzmaDecode( &state, &cb.InCallback,  
			out_buffer, blockSize, &outSizeProcessed);
		if (res != 0)
			break;
		if (outSizeProcessed == 0) {
			outSize = nowPos;
			break;
		}
		nowPos += outSizeProcessed;
		fwrite(out_buffer,1, outSizeProcessed, stdout);
	}
  	free(state.Dictionary);
  }
  free(state.Probs);
  free(cb.Buffer);
  free(out_buffer);
  if (res != 0) {
	fprintf(stderr, "lzma decode has failed!\n");
	return 1;
  }
  return 0;
}

