//-----------------------------------------------------------------------------
// Prevodni program mezi grafickym formatem IMG a TGA
//
// Soucast serialu "Graficke formaty" publikovaneho na http://www.root.cz
// Autor: Pavel Tisnovsky
//-----------------------------------------------------------------------------

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

#define EXT_IMG ".img"
#define EXT_TGA ".tga"



//-----------------------------------------------------------------------------
// Otevreni souboru typu IMG (jmeno neobsahuje koncovku)
//-----------------------------------------------------------------------------
FILE *openImg(char *name)
{
    char *imgName=(char *)malloc(strlen(name)+strlen(EXT_IMG)+1);
    strcpy(imgName, name);
    strcat(imgName, EXT_IMG);
    printf("Opening file %s for reading\n", imgName);
    return fopen(imgName, "rb");
}



//-----------------------------------------------------------------------------
// Otevreni souboru typu TGA (jmeno neobsahuje koncovku)
//-----------------------------------------------------------------------------
FILE *openTga(char *name)
{
    char *tgaName=(char *)malloc(strlen(name)+strlen(EXT_TGA)+1);
    strcpy(tgaName, name);
    strcat(tgaName, EXT_TGA);
    printf("Opening file %s for writing\n", tgaName);
    return fopen(tgaName, "wb");
}



//-----------------------------------------------------------------------------
// Nacteni hlavicky ze souboru typu IMG, naplnuje se i rozliseni
//-----------------------------------------------------------------------------
int readIMGHeader(FILE *fin, int *width, int *height)
{
    // makro pro prevod dvojice bytu na integer v usporadani big endian
#define twoBytes2int(b1, b2) ((int)(b1)<<8 | (b2))
    unsigned char imgHeader[10];
    int firstLine, lastLine, bpp;
    if (fread(imgHeader, 10, 1, fin)!=1) return 0;
    *width=    twoBytes2int(imgHeader[0], imgHeader[1]);
    *height=   twoBytes2int(imgHeader[2], imgHeader[3]);
    firstLine= twoBytes2int(imgHeader[4], imgHeader[5]);
    lastLine=  twoBytes2int(imgHeader[6], imgHeader[7]);
    bpp=       twoBytes2int(imgHeader[8], imgHeader[9]);
    printf("IMG header: width=     %d\n", *width);
    printf("IMG header: height=    %d\n", *height);
    printf("IMG header: 1st line=  %d\n", firstLine);
    printf("IMG header: last line= %d\n", lastLine);
    printf("IMG header: bpp=       %d\n", bpp);
    return 1;
}



//-----------------------------------------------------------------------------
// Ulozeni hlavicky souboru typu TGA
//-----------------------------------------------------------------------------
int writeTGAHeader(FILE *fout, int width, int height)
{
    unsigned char tgaHeader[18]={               // hlavicka formatu typu TGA
                        0x00,                   // typ hlavicky TGA
                        0x00,                   // nepouzivame paletu
                        0x02,                   // typ obrazku je RGB TrueColor
                        0x00, 0x00,             // delka palety je nulova
                        0x00, 0x00, 0x00,       // pozice v palete nas nezajima
                        0x00, 0x00, 0x00, 0x00, // obrazek je umisteny na pozici [0, 0]
                        0x00, 0x00, 0x00, 0x00, // sirka a vyska obrazku (dva byty na polozku)
                        0x18,                   // format je 24 bitu na pixel
                        0x20                    // orientace bitmapy v obrazku
    };
    memcpy(tgaHeader+12, &width, 2);            // do hlavicky TGA zapsat sirku obrazku
    memcpy(tgaHeader+14, &height, 2);           // do hlavicky TGA zapsat vysku obrazku
    return fwrite(tgaHeader, 18, 1, fout)==1;   // zapsat hlavicku TGA do souboru
}



//-----------------------------------------------------------------------------
// Prevod obrazovych dat z formatu IMG do formatu TGA
//-----------------------------------------------------------------------------
int convert(FILE *fin, FILE *fout)
{
    int count, r, g, b;
    // nacteme vsechny ctverice hodnot ve sledu ("runu")
    while ((count=fgetc(fin))!=EOF &&
               (r=fgetc(fin))!=EOF &&
               (g=fgetc(fin))!=EOF &&
               (b=fgetc(fin))!=EOF) {
        // rozkodovat cely sled a zapsat do vystupniho TGA
        while (count--) {
            if (fputc(r, fout)==EOF) return 0;
            if (fputc(g, fout)==EOF) return 0;
            if (fputc(b, fout)==EOF) return 0;
        }
    }
    return 1;
}



//-----------------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
#define my_assert(cond, text)\
    if ((cond)) {\
        fprintf(stderr, "error: %s\n", (text));\
        return 1;\
    }

    FILE *fin;
    FILE *fout;
    int width, height;

    puts("img2tga processing:");
    my_assert(argc<2, "file name (without extension) required!");
    my_assert(!(fin=openImg(argv[1])), "open IMG for reading failed!");
    my_assert(!(fout=openTga(argv[1])), "open TGA for writing failed!");
    my_assert(!readIMGHeader(fin, &width, &height), "read IMG header failed!");
    my_assert(!writeTGAHeader(fout, width, height), "write TGA header failed!");
    my_assert(!convert(fin, fout), "unable to convert data!");
    my_assert(fclose(fin), "close IMG failed!");
    my_assert(fclose(fout), "close TGA failed!");
    puts("img2tga done.\n");
    return 0;
}



//-----------------------------------------------------------------------------
// finito
//-----------------------------------------------------------------------------