/******************************************************************************
 * vtga.c - Chapter 7 sample code                                             *
 *                                                                            *
 * To be used with mach64 sample code.                                        *
 * This module contains primitive for bringing in TGA files to video          *
 * memory for the sample code animation examples.                             *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

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

#include "atim64.h"
#include "defines.h"
#include "main.h"
#include "vtga.h"


/******************************************************************************
 * get_targa_header                                                           *
 *  Function: Loads targa file header information                             *
 *    Inputs: filename - name and location of file                            *
 *            header - pointer to header structure                            *
 *   Outputs: SUCCESS    - success                                            *
 *            OPEN_ERROR - can't open file                                    *
 ******************************************************************************/

short get_targa_header (char *filename, TARGA_HEADER *header)
{
    FILE *TargaFile;

    // Open targa file.

    TargaFile = fopen (filename, "rb");
    if (TargaFile == NULL)
    {
        return (OPEN_ERROR);
    } // if

    // Check for supportability (type 1, 8 bpp, colour map).

    fread (header, sizeof (TARGA_HEADER), 1, TargaFile);

    fclose (TargaFile);

    return (SUCCESS);

} // get_targa_header


/******************************************************************************
 * set_targa_palette                                                          *
 *  Function: Loads a targa image colour table and sets the palette           *
 *    Inputs: filename - name and location of file                            *
 *   Outputs: SUCCESS - success                                               *
 *            OPEN_ERROR - can't open file                                    *
 *            IMAGE_TYPE_ERROR - targa file does not have colour table        *
 *     Notes: - a valid set accelerator mode is assumed                       *
 *            - the image size is intended to be fairly small                 *
 *            - accelerator mode is assumed to be 8 bpp                       *
 ******************************************************************************/

short set_targa_palette (char *filename)
{
    FILE *TargaFile;
    TARGA_HEADER header;
    palette entry;
    short i;

    // Open targa file.

    TargaFile = fopen (filename, "rb");
    if (TargaFile == NULL)
    {
        return (OPEN_ERROR);
    } // if

    // Check for supportability (type 1, 8 bpp, colour map).

    fread (&header, sizeof (TARGA_HEADER), 1, TargaFile);

    // Support check for type 1, 8 bpp, colour map.

    if ((header.cm_type != 1) ||
        (header.image_type != 1) ||
        (header.pixel_depth != 8) ||
        (header.image_coord == 1) ||
        (header.image_coord == 3))
    {
        // Return error if image type is not supported (no colour table).

        fclose (TargaFile);
        return (IMAGE_TYPE_ERROR);
    } // if

    // Read colour map and setup palette for 8 bpp.

    for (i = header.cm_origin; i < header.cm_length; i++)
    {
        entry.blue = fgetc (TargaFile) >> 2;
        entry.green = fgetc (TargaFile) >> 2;
        entry.red = fgetc (TargaFile) >> 2;
        set_palette (i, entry);
    } // for

    fclose (TargaFile);

    // Return to caller.

    return (SUCCESS);

} // set_targa_palette


/******************************************************************************
 * load_targa                                                                 *
 *  Function: Loads targa image into video memory                             *
 *    Inputs: filename - name and location of file                            *
 *            x - starting x-coordinate (top-left corner)                     *
 *            y - starting y-coordinate (top-left corner)                     *
 *   Outputs: SUCCESS - success                                               *
 *            OPEN_ERROR - can't open file                                    *
 *            MEMORY_ERROR - not enough memory for input buffer               *
 *            IMAGE_TYPE_ERROR - targa file is not supported                  *
 *     Notes: - a valid set accelerator mode is assumed                       *
 *            - the image size is intended to be fairly small                 *
 *            - accelerator mode is assumed to be 8 bpp                       *
 ******************************************************************************/

short load_targa (char *filename, short x, short y)
{
    FILE *TargaFile;
    TARGA_HEADER header;
    unsigned char *inbuffer;
    unsigned long temp1, temp2, temp3, temp4, data;
    unsigned long totalbytes, hostwrts;
    short drawx, drawy, j;
    short shifter, remainder;
    short adder, error;
    unsigned char r, g, b;
    unsigned char value;

    // Open targa file.
    TargaFile = fopen (filename, "rb");
    if (TargaFile == NULL)
    {
        return (OPEN_ERROR);
    } // if

    // Check for support ability (type 1, 8 bpp, colour map).
    fread (&header, sizeof (TARGA_HEADER), 1, TargaFile);

    // Support check for type 1, 8 bpp, colour map.
    error = 0;
    if ((header.cm_type != 1) ||
        (header.image_type != 1) ||
        (header.pixel_depth != 8) ||
        (header.image_coord == 1) ||
        (header.image_coord == 3))
    {
        error++;
    } // if

    // Support check for type 2, 24 bpp, no colour map.
    if ((header.cm_type != 0) ||
        (header.image_type != 2) ||
        (header.pixel_depth != 24) ||
        (header.image_coord == 1) ||
        (header.image_coord == 3))
    {
        error++;
    } // if

    // Return error if image type is not supported
    if (error > 1)
    {
        fclose (TargaFile);
        return (IMAGE_TYPE_ERROR);
    } // if

    // Skip over colour table if 8 bpp image type.
    if (header.pixel_depth == 8)
    {
        fseek (TargaFile, (unsigned long) (header.cm_length * 3), SEEK_CUR);
    } // if

    // Open input buffer (image width * image pixel depth in bytes).
    inbuffer = (unsigned char *) malloc ((header.width + 1) *
                                         (header.pixel_depth / 8));
    if (inbuffer == NULL)
    {
        fclose (TargaFile);
        return (MEMORY_ERROR);
    } // if

    // Determine if an additional host write is needed at the end of the loop.

    remainder = 0;
    totalbytes = (unsigned long) (header.width) *
                 (unsigned long) (header.height);
    hostwrts = totalbytes / 4;
    if ((hostwrts * 4) != totalbytes)
    {
        remainder++;
    } // if

    // Setup engine for buffer to screen host data transfer.

    wait_for_idle ();
    temp1 = regr (DP_SRC);
    temp2 = regr (DP_MIX);
    temp3 = regr (SRC_CNTL);
    temp4 = regr (DST_CNTL);

    regw (DP_SRC, FRGD_SRC_HOST);
    regw (DP_MIX, FRGD_MIX_S | BKGD_MIX_S);
    regw (SRC_CNTL, 0);

    if (header.image_coord == 0)
    {
        // Bottom left.

        regw (DST_CNTL, DST_Y_BOTTOM_TO_TOP | DST_X_LEFT_TO_RIGHT);
        regw (DST_X, x);
        regw (DST_Y, y + header.height - 1);
        regw (DST_HEIGHT, header.height);
        regw (DST_WIDTH, header.width);
    }
    else
    {
        // Top left.
        regw (DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_RIGHT_TO_LEFT);
        regw (DST_X, x + header.width - 1);
        regw (DST_Y, y);
        regw (DST_HEIGHT, header.height);
        regw (DST_WIDTH, header.width);
    } // if

    // Main draw loop.

    if (header.pixel_depth == 24)
    {
        adder = 3;
    }
    else
    {
        adder = 1;
    } // if
    j = 0;
    shifter = 0;
    data = 0;
    for (drawy = 0; drawy < header.height; drawy++)
    {
        // Get next image scanline.
        fread ((unsigned char *) inbuffer,
               header.width * (header.pixel_depth / 8), 1, TargaFile);

        // Get next line of pixels from input buffer.
        for (drawx = 0; drawx < (header.width * (header.pixel_depth / 8));
             drawx = drawx + adder)
        {
            if (header.pixel_depth == 24)
            {
                // Get 24 bpp data and convert to 8 bpp.

                g = (unsigned char) (*(inbuffer + drawx) & 0xFF);
                b = (unsigned char) (*(inbuffer + drawx + 1) & 0xFF);
                r = (unsigned char) (*(inbuffer + drawx + 2) & 0xFF);
                value = (unsigned char) ((r & 0xE0) | ((g >> 3) & 0x1C) |
                                         ((b >> 6) & 0x03));
            }
            else
            {
                // Get 8 bit data.

                value = (unsigned char) (*(inbuffer + drawx) & 0xFF);
            } // if

            data = data | ((unsigned long) (value) << shifter);
            shifter = shifter + 8;

            // Accumulate 32 bits at a time before writing.
            j++;
            if (j > 3)
            {
                wait_for_fifo (1);
                regw (HOST_DATA0, data);
                j = 0;
                shifter = 0;
                data = 0;
            } // if
        } // for
    } // for

    // Write remaining data if needed.
    if (remainder != 0)
    {
        wait_for_fifo (1);
        regw (HOST_DATA0, data);
    } // if

    // Restore main engine context.

    wait_for_idle ();
    regw (DP_SRC, temp1);
    regw (DP_MIX, temp2);
    regw (SRC_CNTL, temp3);
    regw (DST_CNTL, temp4);

    // Close buffers and targa file.
    free (inbuffer);
    fclose (TargaFile);

    // Return to caller.
    return (SUCCESS);

} // load_targa
