/*
 *	dtinylz - TinyLZ compression tool
 *	Copyright (C) 2008 Naoyuki Sawa
 *
 *	* Sun Feb 17 14:57:30 JST 2008 Naoyuki Sawa
 *	- 1st リリース。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define DIE() abort()
#define __CLIP_H__
#include "../../cliptlz.h"
#include "../../cliptlz.c"

#define VERSION "20080217"

/////////////////////////////////////////////////////////////////////////////

void
usage()
{
	printf("dtinylz - TinyLZ compression tool (%s) Naoyuki Sawa\n", VERSION);
	printf("Usage: [-d/-e] <source file name> [<destination file name>]\n");
	printf("Options:\n");
	printf("  -e : encode source file(default)\n");
	printf("  -d : decode source file\n");
	exit(1);
}

int mode; /* 'd' or 'e' */
char srcname[_MAX_PATH];
char dstname[_MAX_PATH];

int srclen;
unsigned char* srcbuf;
int dstlen;
unsigned char* dstbuf;
int verlen;
unsigned char* verbuf;

int
main(int argc, char* argv[])
{
	int i;
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];
	FILE* fp;

	/* コマンドライン解析。 */
	for(i = 1; i < argc; i++) {
		switch(argv[i][0]) {
		case '-':
		case '/':
			switch(tolower(argv[i][1])) {
			case 'd':
			case 'e':
				if(!mode) {
					mode = tolower(argv[i][1]);
					break;
				}
				usage();
				break;
			default:
				usage();
				break;
			}
			break;
		default:
			if(!srcname[0]) {
				strcpy(srcname, argv[i]);
				break;
			}
			if(!dstname[0]) {
				strcpy(dstname, argv[i]);
				break;
			}
			usage();
			break;
		}
	}
	/* "-d"も"-e"も指定されていなければ、"-e"とします。 */
	if(!mode) {
		mode = 'e';
	}
	/* 入力ファイル名は必須です。 */
	if(!srcname[0]) {
		usage();
	}
	if(mode == 'd') {
		/* 入力ファイル名の拡張子が省略されていたら、".tlz"とします。 */
		_splitpath(srcname, drive, dir, fname, ext);
		if(!ext[0]) {
			_makepath(srcname, drive, dir, fname, ".tlz");
		}
		/* 出力ファイル名が省略されていたら、入力ファイル名から拡張子を取り除いたものとします。 */
		if(!dstname[0]) {
			_splitpath(srcname, drive, dir, fname, ext);
			 _makepath(dstname, drive, dir, fname, "");
		}
	}
	if(mode == 'e') {
		/* 出力ファイル名が省略されていたら、入力ファイル名の拡張子を".tlz"にしたものとします。 */
		if(!dstname[0]) {
			_splitpath(srcname, drive, dir, fname, ext);
			 _makepath(dstname, drive, dir, fname, ".tlz");
		}
		/* 出力ファイル名の拡張子が省略されていたら、".tlz"とします。 */
		_splitpath(dstname, drive, dir, fname, ext);
		if(!ext[0]) {
			_makepath(dstname, drive, dir, fname, ".tlz");
		}
	}
	/* 入力ファイルと出力ファイルが同じであってはいけません。 */
	if(!stricmp(srcname, dstname)) {
		fprintf(stderr, "Error: Source file shall not be same as destination file.\n");
		exit(1);
	}

	/* 入力ファイルを読み込みます。 */
	fp = fopen(srcname, "rb");
	if(!fp) {
		fprintf(stderr, "Error: Failed to open file %s.\n", srcname);
		exit(1);
	}
	fseek(fp, 0, SEEK_END);
	srclen = ftell(fp);
	if(!srclen) {
		fprintf(stderr, "Error: Source file shall not be empty.");
		exit(1);
	}
	rewind(fp);
	srcbuf = malloc(srclen);
	if(!srcbuf) {
		fprintf(stderr, "Error: Failed to allocate source buffer.\n");
		exit(1);
	}
	fread(srcbuf, 1, srclen, fp);
	fclose(fp);

	if(mode == 'e') {
		/* 圧縮。 */
		dstlen = TinyLZ_compress(NULL, 0, srcbuf, srclen);
		dstbuf = malloc(dstlen);
		if(!dstbuf) {
			fprintf(stderr, "Error: Failed to allocate destination buffer.\n");
			exit(1);
		}
		TinyLZ_compress(dstbuf, dstlen, srcbuf, srclen);
		/* ベリファイ。 */
		verbuf = malloc(srclen);
		if(!verbuf) {
			fprintf(stderr, "Error: Failed to allocate verify buffer.\n");
			exit(1);
		}
		verlen = TinyLZ_uncompress(verbuf, srclen, dstbuf, dstlen);
		if(verlen != srclen) {
			fprintf(stderr, "Internal error: Verify failed.n");
			exit(1);
		}
	}
	if(mode == 'd') {
		/* 展開。 */
		dstlen = TinyLZ_uncompress(NULL, 0, srcbuf, srclen);
		dstbuf = malloc(dstlen);
		if(!dstbuf) {
			fprintf(stderr, "Error: Failed to allocate destination buffer.\n");
			exit(1);
		}
		TinyLZ_uncompress(dstbuf, dstlen, srcbuf, srclen);
	}

	/* 出力ファイルを書き込みます。 */
	fp = fopen(dstname, "wb");
	if(!fp) {
		fprintf(stderr, "Error: Failed to create file %s.\n", dstname);
		exit(1);
	}
	fwrite(dstbuf, 1, dstlen, fp);
	fclose(fp);

	/* レポート表示。 */
	printf("%s (%d) => %s (%d) : %d%%\n",
		srcname, srclen,
		dstname, dstlen,
		(dstlen * 100 + srclen - 1) / srclen);

	return 0;
}
