/*
 * datafile.c:テクスチャーデータなど
 *
 * deftbl.c:シンボル名定義
 *   echo "DEFTBL(texture1)," >> /tmp/deftbl.c
 *
 * このファイルをコンパイルする
 * 実行する test filename.c
 *
 */

#include <stdio.h>
#include <stdlib.h>
#if 0
#include "/home/hayakawa/prj/PR/libbg/dmamgr.h"
#else
#include "dmamgr.h"
#endif

#ifndef DATA_INCLUDE
#error DATA_INCLUDE が定義されていないため、コンパイルテストのみです
#endif

#ifndef SLIENC_CMD
#define SLIENC_CMD "../TOOLS/slienc"
#endif

typedef enum flag_e {
    FLAG_BETA = 0,			/* 圧縮しない */
    FLAG_COMPRESS = 1,		/* 圧縮する */
    FLAG_REDUCE = 2			/* 小さくなるなら、圧縮する */
} flag_t;

typedef struct tbl_s {
    char *symbol;
    void *ptr;
    size_t siz;
    flag_t flag;
} tbl_t;

#define DEFTBL(symbol) {#symbol, symbol, sizeof(symbol), FLAG_COMPRESS}
#define DEFTBLX(symbol, flag) {#symbol, symbol, sizeof(symbol), flag}
#define CALC_COMP_SIZE(siz) ((siz) * 2 + 32)
#ifndef UPBOUND
#define UPBOUND(n, b) (((n) + (b) - 1) & ~((b) - 1))
#endif

#ifdef YOSHI_MODE
#include "actor_yoshiPattern.h"
#endif

#ifdef DATA_INCLUDE
#include <datafile.c>
#endif

#ifndef TMPDIR
#define TMPDIR "/tmp/"
#endif

static tbl_t tbl[] = {
#ifdef DATA_INCLUDE
#include <deftbl.c>
#endif
    {NULL, NULL, 0, 0}		/* ターミネータ */
};

/*
 * 配列ソースの書き出し
 */
static void
write_array(
    FILE *fp,
    char *symbol,
    void *comp,
    size_t comp_siz,
    size_t orig_siz
)
{
    size_t left = comp_siz;
    int i;
    unsigned short *u16 = (unsigned short *)comp;
    float comp_rate = 100.0f * (float)comp_siz / (float)orig_siz;
    
    fprintf(fp, "\n");
    fprintf(fp,"/" "*\n");
    fprintf(fp," * 圧縮ツール: $Id: srcpress.c,v 1.1.1.1 2003/05/19 20:33:17 tong Exp $\n");
    fprintf(fp," * シンボル名: %s\n", symbol);
    fprintf(fp," * 原本サイズ: %u\n", orig_siz);
    fprintf(fp," * 圧縮サイズ: %u\n", comp_siz);
    fprintf(fp," * 圧縮率    : %6.2f%%\n", comp_rate);
    fprintf(fp," *" "/\n");

    fprintf(fp, "unsigned short %s[] = {\n", symbol);

    u16 = (unsigned short *)comp;
    left = comp_siz;
    while (left > 16) {
	fprintf(fp, "    ");
	for (i = 0; i < 8; ++i) {
	    fprintf(fp, "0x%04x,%c", *u16++, (i != 7) ? ' ' : '\n');
	}
	left -= 16;
    }
    {
	fprintf(fp, "    ");
	while (left > 2) {
	    fprintf(fp, "0x%04x, ", *u16++);
	    left -= 2;
	}
	fprintf(fp, "0x%04x\n", *u16++);
	left -= 2;
    }

    fprintf(fp, "};\n\n");
    fprintf(fp, "unsigned short %s_end_of_data = 0;\n", symbol);
}

/*
 * ヘッダソースの書き出し
 */
static void
write_header(
    FILE *fp,
    char *symbol,
    size_t comp_siz,
    size_t orig_siz
)
{
    
    fprintf(fp,"#define %s_SYMBOL %s /""* シンボル名 *""/\n", symbol, symbol);
    fprintf(fp,"#define %s_ORGSIZ %u /""* 原本サイズ *""/\n", symbol, orig_siz);
    fprintf(fp,"#define %s_CMPSIZ %u /""* 圧縮サイズ *""/\n", symbol, comp_siz);

    fprintf(fp, "extern unsigned short %s[];\n", symbol);
    
}

/*
 * 圧縮プログラム呼び出し
 */
static void
slienc(
    void *ptr,
    size_t siz,
    void *comp,
    size_t *comp_siz
)
{
    FILE *fp;
    char filename[FILENAME_MAX+1];
    char *symbol = "tmpfile";
    char command[10+FILENAME_MAX];
    int ret;
    
    /*
     * テーブルをファイルに書き出す
     */
    sprintf(filename, TMPDIR "%s.raw", symbol);
    fp = fopen(filename, "w");
    if (fp == NULL) {
	fprintf(stderr, "%s ファイルオープンエラー\n", filename);
	exit(1);
    }
    if (fwrite(ptr, siz, 1, fp) == 0) {
	fprintf(stderr, "%s ファイル書き込みエラー\n", filename);
	fclose(fp);
	exit(1);
    }
    fclose(fp);
    
    /*
     * ファイルを圧縮する
     */
    sprintf(command, SLIENC_CMD " " TMPDIR "%s.raw", symbol);
    ret = system(command);
    if (ret != 0) {
	if (ret == 127) {
	    fprintf(stderr, "シェルが実行できなかった\n");
	} else {
	    fprintf(stderr, "slienc エラー %d\n", ret);
	}
	exit(1);
    }
    
    /*
     * ファイルをテーブルに読み込む
     */
    sprintf(filename, TMPDIR "%s.szp", symbol);
    fp = fopen(filename, "r");
    if (fp == NULL) {
	fprintf(stderr, "%s ファイルオープンエラー\n", filename);
	exit(1);
    }
    if ((*comp_siz = fread(comp, 1, *comp_siz, fp)) == 0) {
	fprintf(stderr, "%s ファイル読み込みエラー\n", filename);
	fclose(fp);
	exit(1);
    }
    fclose(fp);
}

/*
 * 圧縮データを伸長して元に戻るかをチェックする
 */
void slidec(unsigned char *a,unsigned char *b);
static void
slichk(
    void *ptr,
    size_t siz,
    void *comp
)
{
    char *orig = (char *)ptr;
    char *work;
    int i;

    work = (char *)malloc(siz);

    slidec((unsigned char *)comp, (unsigned char *)work);

    for (i = 0; i < (int)siz; ++i) {
	if (*work++ != *orig++) {
	    printf("圧縮データを展開しても元に戻らないよー\n");
	    return;
	}
    }
    //printf("圧縮チェックＯＫ\n");
}

static void
rawsave(
    FILE *cfp,
    FILE *hfp,
    tbl_t *tblp
)
{
    char *comp;
    size_t comp_siz;
    
    //fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
    printf("%s:\n", tblp->symbol);

    if (tblp->flag == FLAG_COMPRESS || tblp->flag == FLAG_REDUCE) {
	comp_siz = CALC_COMP_SIZE(tblp->siz) + DMAMGR_HEADER_SIZE;
	comp = (char *)calloc(sizeof(*comp), comp_siz);
	if (comp == NULL) {
	    fprintf(stderr, "メモリアロケーションエラー (%u)\n", comp_siz);
	    exit(1);
	}
	
	//fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
	slienc(tblp->ptr, tblp->siz, comp + DMAMGR_HEADER_SIZE, &comp_siz);
	//fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
	slichk(tblp->ptr, tblp->siz, comp + DMAMGR_HEADER_SIZE);
	//fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
	comp_siz = DMAMGR_HEADER_SIZE + UPBOUND(comp_siz, 2);
	 /* ２バイトバウンダリに切り上げ */
	{
	    head_t *head = (head_t *)comp;
	    head->magic = DMAMGR_MAGIC;
	    head->compsize = (u32)comp_siz;
	    head->origsize = (u32)tblp->siz;
	    head->dummy = 0;
	}
    }
    
    //fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
    if (tblp->flag == FLAG_BETA ||
	tblp->flag == FLAG_REDUCE && tblp->siz <= comp_siz) {
	printf("ソースファイル書き出し中 %s %u\n", tblp->symbol, tblp->siz);
	/*
	 * Ｃのソースを吐き出す
	 */
	write_array(cfp, tblp->symbol, tblp->ptr, tblp->siz, tblp->siz);
	write_header(hfp, tblp->symbol, tblp->siz, tblp->siz);
    } else {
	printf("ソースファイル書き出し中 %s %u %u\n",
	       tblp->symbol, comp_siz, tblp->siz);
	/*
	 * Ｃのソースを吐き出す
	 */
	write_array(cfp, tblp->symbol, comp, comp_siz, tblp->siz);
	write_header(hfp, tblp->symbol, comp_siz, tblp->siz);
    }
    //fprintf(stderr, "now %s %d\n", __FILE__, __LINE__);
    
    free(comp);
}

int
main(int argc, char **argv)
{
    tbl_t *tblp;
    FILE *cfp;
    FILE *hfp;
    char cfile[FILENAME_MAX];
    char hfile[FILENAME_MAX];

    if (argc != 2) {
	printf("usage: %s filename \n", argv[0]);
	exit(1);
    }

    sprintf(cfile, "%s.c", argv[1]);
    sprintf(hfile, "%s.h", argv[1]);

    
    cfp = fopen(cfile, "w");
    if (cfp == NULL) {
	fprintf(stderr, "%s ファイルオープンエラー\n", cfile);
	exit(1);
    }
    hfp = fopen(hfile, "w");
    if (hfp == NULL) {
	fclose(cfp);
	fprintf(stderr, "%s ファイルオープンエラー\n", hfile);
	exit(1);
    }
    
    tblp = tbl;
    while (tblp->symbol != NULL) {
	rawsave(cfp, hfp, tblp);
	++tblp;
    }
    
    fclose(cfp);
    fclose(hfp);

    return 0;

}
