// BlinkenSisters - Hunt for the Lost Pixels
//     Bringing back the fun of the 80s
//
// (C) 2005-07 Rene Schickbauer, Wolfgang Dautermann
//
// See License.txt for licensing information
//
// This file includes also material from other authors, see bellow
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>
#else // NO WIN32
#include <dirent.h>
#endif


/*************************************************************************
// This program shows the basic usage of the LZO library.
// We will compress a block of data and decompress again.
//
// For more information, documentation, example programs and other support
// files (like Makefiles and build scripts) please download the full LZO
// package from
//    http://www.oberhumer.com/opensource/lzo/
**************************************************************************/

/* First let's include "minizo.h". */

#include "minilzo.h"
#include "bmfconvert.h"


/* We want to compress the data block at `in' with length `IN_LEN' to
 * the block at `out'. Because the input block may be incompressible,
 * we must provide a little more output space in case that compression
 * is not possible.
 */

#define IN_LEN 10000000
#define OUT_LEN     (IN_LEN + IN_LEN / 16 + 64 + 3)

static unsigned char __LZO_MMODEL in  [ IN_LEN ];
static unsigned char __LZO_MMODEL out [ OUT_LEN ];


/* Work-memory needed for compression. Allocate memory in units
 * of `lzo_align_t' (instead of `char') to make sure it is properly aligned.
 */

#define HEAP_ALLOC(var,size) \
    lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]

//static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS);


/*************************************************************************
//
**************************************************************************/

int main(int argc, char *argv[])
{
    int r;
    lzo_uint in_len;
    lzo_uint out_len;
    lzo_uint new_len;

    if (argc < 0 && argv == NULL)   /* avoid warning about unused args */
        return 0;

    printf("\nLZO real-time data compression library (v%s, %s).\n",
            lzo_version_string(), lzo_version_date());
    printf("Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n");

	if(argc != 3) {
		printf("Usage:\n"
				"bmfdecompress inputfile.bmf outputdir\n");
		exit(3);
	}


/*
 * Step 1: initialize the LZO library
 */
    if (lzo_init() != LZO_E_OK)
    {
        printf("internal error - lzo_init() failed !!!\n");
        printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable `-DLZO_DEBUG' for diagnostics)\n");
        return 3;
    }

/*
 * Step 2: prepare the input block that will get compressed.
 *         We just fill it with zeros in this example program,
 *         but you would use your real-world data here.
 */
    in_len = IN_LEN;
	
	FILE* ifh = fopen(argv[1], "rb");
	FILE* ofh = 0;
	if(!ifh) {
		printf("Can't open file %s for input", argv[1]);
		exit(1);
	}

	char fname[1000];
	char tmp1[1000];
	unsigned int tmpnum;
	unsigned int bmftype;

	// Check magic number
	tmpnum = bmfReadInt(ifh);
	if(tmpnum != BMFTYPE_MAGIC) {
		printf("MAGIC does not match - corrupt file???\n");
		exit(1);
	}

	bool running = true;
#ifndef WIN32
    DIR *tmpdir;
#endif
	while(running) {
		bmftype = bmfReadInt(ifh);
		switch(bmftype) {
			case BMFTYPE_VERSION:
				tmpnum = bmfReadInt(ifh);
				if(tmpnum != BMF_VERSION) {
					printf("BMF Version mismatch!\n");
					if(tmpnum == 1) {
						printf("compatible (read-only) BMF Version 1 found - continuing\n");
					} else {
						exit(1);
					}
				} else {
					printf("Matching BMF Version found!\n");
				}
				break;
			case BMFTYPE_FPS:
				tmpnum = bmfReadInt(ifh);
				printf("FPS: %u\n", tmpnum);
				break;
			case BMFTYPE_FRAMECOUNT:
				tmpnum = bmfReadInt(ifh);
				printf("Framecount: %u\n", tmpnum);
				break;
			case BMFTYPE_WIDTH:
				tmpnum = bmfReadInt(ifh);
				printf("Width: %u\n", tmpnum);
				break;
			case BMFTYPE_HEIGHT:
				tmpnum = bmfReadInt(ifh);
				printf("Height: %u\n", tmpnum);
				break;
			case BMFTYPE_DIR:
				tmpnum = bmfReadInt(ifh);
				if(!fread(fname, tmpnum, 1, ifh)) {
					if(feof(ifh)) {
						printf("Premature end of file!\n");
					} else {
						if(ferror(ifh)) {
							printf("ferror says: Error on fread!\n");
						} else {
							printf("No ferror, but fread failed anyway!?!\n");
						}
					}
					exit(1);
				}
				fname[tmpnum] = 0;
				
				sprintf(tmp1, "%s/%s", argv[2], fname);
#ifdef WIN32
				_mkdir(tmp1);
#else // NO WIN32
				tmpdir = opendir(tmp1);
				if(tmpdir) {
					// Ok, dir exists, just close the habndle and ignore command
					closedir(tmpdir);
				} else {
					if(mkdir(tmp1, S_IRWXU)) {
						printf("Failed to create directory '%s'!!!!\n", tmp1);
					}
				}
#endif // NO WIN32
				fname[0] = 0;	
				
				//printf("Next file: %s\n", fname);
				break;				
			case BMFTYPE_FILENAME:
				tmpnum = bmfReadInt(ifh);
				if(!fread(fname, tmpnum, 1, ifh)) {
					if(feof(ifh)) {
						printf("Premature end of file!\n");
					} else {
						if(ferror(ifh)) {
							printf("ferror says: Error on fread!\n");
						} else {
							printf("No ferror, but fread failed anyway!?!\n");
						}
					}
					exit(1);
				}
				fname[tmpnum] = 0;
				//printf("Next file: %s\n", fname);
				break;
			case BMFTYPE_REGISTER_ADDON:
            case BMFTYPE_REGISTER_MUSIC:
			case BMFTYPE_REGISTER_POSCAP:
				tmpnum = bmfReadInt(ifh);
				if(!fread(fname, tmpnum, 1, ifh)) {
					if(feof(ifh)) {
						printf("Premature end of file!\n");
					} else {
						if(ferror(ifh)) {
							printf("ferror says: Error on fread!\n");
						} else {
							printf("No ferror, but fread failed anyway!?!\n");
						}
					}
					exit(1);
				}
				fname[tmpnum] = 0;
				if(bmftype == BMFTYPE_REGISTER_ADDON) {
                    printf("(ADDON) ");
                } else if(bmftype == BMFTYPE_REGISTER_MUSIC) {
                    printf("(MUSIC) ");
				} else if(bmftype == BMFTYPE_REGISTER_POSCAP) {
                    printf("(POSCAP) ");
				} else {
					printf("(UNKNOWN!!) ");
                }
				printf("Would Register as: '%s' %d\n", fname, tmpnum);	
				fname[0] = 0;
				break;
				
			case BMFTYPE_FRAME_COMPRESSED:
			case BMFTYPE_FRAME_UNCOMPRESSED:
			case BMFTYPE_SND_COMPRESSED:
			case BMFTYPE_SND_UNCOMPRESSED:
			case BMFTYPE_METAFILE_COMPRESSED:
			case BMFTYPE_METAFILE_UNCOMPRESSED:
				if(strlen(fname) == 0) {
					printf("Got file without filename!!\n");
					exit(1);
				}
				if(bmftype == BMFTYPE_SND_COMPRESSED ||
						bmftype == BMFTYPE_SND_UNCOMPRESSED) {
                    printf("Sound: %s\n", fname);
                }
				in_len = bmfReadInt(ifh);
				if(!fread(in, 1, in_len, ifh)) {
					if(feof(ifh)) {
						printf("Premature end of file!\n");
					} else {
						if(ferror(ifh)) {
							printf("ferror says: Error on fread!\n");
						} else {
							printf("No ferror, but fread failed anyway!?!\n");
						}
					}
					exit(1);
				}

				sprintf(tmp1, "%s/%s", argv[2], fname);

				if(bmftype == BMFTYPE_FRAME_UNCOMPRESSED ||
						bmftype == BMFTYPE_SND_UNCOMPRESSED ||
						bmftype == BMFTYPE_METAFILE_UNCOMPRESSED) {
					// No compression, just memcopy to output area
					//printf("Uncompressed file %s with len %lu\n", fname, in_len);
					memcpy(out, in, in_len);
					out_len = in_len;
				} else {

					r = lzo1x_decompress(in,in_len,out,&new_len,NULL);
					if (r == LZO_E_OK) {
						/*
						printf("decompressed %lu bytes back into %lu bytes\n",
							(unsigned long) in_len, (unsigned long) new_len);
						*/

						out_len = new_len;
						
					}
					else
					{
						/* this should NEVER happen */
						printf("internal error - decompression failed: %d\n", r);
						return 1;
					}

				}
				
				ofh = fopen(tmp1, "wb");
				if(!ofh) {
					printf("Can't open file %s\n", tmp1);
					exit(1);
				}
				fwrite(out, out_len, 1, ofh);
				fclose(ofh);
				//printf("Write to file %s\n", tmp1);
				fname[0] = 0; // Delete fname so we don't overwrite
				break;
				

			case BMFTYPE_END_OF_FILE:
				running = false;
				break;

			default:
				printf("Unknown BMFTYPE %u...\n", bmftype);
				exit(1);
		}
	}
			
	fclose(ifh);

    return 0;
}
