CnC_Red_Alert/VQ/VQM32/TARGA.CPP

700 lines
16 KiB
C++
Raw Normal View History

/*
** Command & Conquer Red Alert(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************
*
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S
*
*----------------------------------------------------------------------------
*
* FILE
* targa.c
*
* DESCRIPTION
* Targa Image File reader. (32-Bit protected mode)
*
* PROGRAMMER
* Denzil E. Long, Jr.
*
* DATE
* January 26, 1995
*
*----------------------------------------------------------------------------
*
* PUBLIC
* OpenTarga - Open Targa image file.
* CloseTarga - Close Targa image file.
* LoadTarga - Load Targa image file.
* XFlipTarga - X flip the image.
* YFlipTarga - Y flip the image.
*
* PRIVATE
* DecodeImageData - Decompress Targa image data.
*
****************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <mem.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include "targa.h"
/* Private data declerations. */
static long DecodeImageData(TGAHandle *, char *);
static void InvertImageData(TGAHeader *, char *);
/****************************************************************************
*
* NAME
* OpenTarga - Open Targa image file.
*
* SYNOPSIS
* TGAHandle = OpenTarga(Name, Mode)
*
* TGAHandle *OpenTarga(char *, unsigned short);
*
* FUNCTION
* Open a Targa image file and read in its header. The file stream will
* positioned after the ID field (if there is one).
*
* INPUTS
* Name - Pointer to name of Targa file.
* Mode - Access mode.
*
* RESULT
* TGAHandle - Pointer to initialized TGAHandle or NULL if error.
*
****************************************************************************/
TGAHandle *OpenTarga(char *name, unsigned short mode)
{
TGAHandle *tga;
long size;
long error = 0;
/* Allocate TGAHandle */
if ((tga = (TGAHandle *)malloc(sizeof(TGAHandle))) != NULL) {
/* Initialize TGAHandle structure. */
memset((void *)tga, 0, sizeof(TGAHandle));
tga->mode = mode;
switch (mode) {
/* Open targa file for read. */
case TGA_READMODE:
if ((tga->fh = open(name, (O_RDONLY|O_BINARY))) != -1) {
/* Read in header. */
size = read(tga->fh, &tga->header, sizeof(TGAHeader));
if (size != sizeof(TGAHeader)) {
error = 1;
}
/* Skip the ID field */
if (!error && (tga->header.IDLength != 0)) {
if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) {
error = 1;
}
}
} else {
error = 1;
}
break;
/* Open targa file for write. */
case TGA_WRITEMODE:
if ((tga->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY),
(S_IREAD|S_IWRITE))) == -1) {
error = 1;
} else {
printf("\r");
}
break;
/* Open targa file for read/write.*/
case TGA_RDWRMODE:
if ((tga->fh = open(name, (O_RDWR|O_BINARY),
(S_IREAD|S_IWRITE))) != -1) {
/* Read in header. */
size = read(tga->fh, &tga->header, sizeof(TGAHeader));
if (size != sizeof(TGAHeader)) {
error = 1;
}
/* Skip the ID field */
if (!error && (tga->header.IDLength != 0)) {
if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) {
error = 1;
}
}
} else {
error = 1;
}
break;
}
/* Close on any error! */
if (error) {
CloseTarga(tga);
tga = NULL;
}
}
return (tga);
}
/****************************************************************************
*
* NAME
* CloseTarga - Close Targa image file.
*
* SYNOPSIS
* CloseTarga(TGAHandle)
*
* void CloseTarga(TGAHandle *);
*
* FUNCTION
* Close the Targa image file and free its handle.
*
* INPUTS
* TGAHandle - Pointer to TGAHandle returned by OpenTarga().
*
* RESULT
* NONE
*
****************************************************************************/
void CloseTarga(TGAHandle *tga)
{
/* Ensure valid handle. */
if (tga) {
/* Close the file if it is open. */
if (tga->fh != -1) close(tga->fh);
/* Free TGAHandle */
free(tga);
}
}
/****************************************************************************
*
* NAME
* LoadTarga - Load Targa Image File.
*
* SYNOPSIS
* Error = LoadTarga(Name, Palette, ImageBuffer)
*
* long LoadTarga(char *, char *, char *);
*
* FUNCTION
* Open and load the Targa into the specified buffers. If either buffer
* pointer is NULL then that field will not be processed.
*
* INPUTS
* Name - Name of Targa image file to load.
* Palette - Pointer to buffer to load the palette into.
* ImageBuffer - Pointer to buffer to load the image data into.
*
* RESULT
* Error - 0 if successful, or TGAERR_??? error code.
*
****************************************************************************/
long LoadTarga(char *name, char *palette, char *image)
{
TGAHandle *tga;
long size;
long depth;
long i,n;
char c;
long error = 0;
/* Open the Targa */
if ((tga = OpenTarga(name, TGA_READMODE)) != NULL) {
/* Process ColorMap (palette) */
if (tga->header.ColorMapType == 1) {
depth = (tga->header.CMapDepth >> 3);
size = (tga->header.CMapLength * depth);
/* Load the palette from the TGA if a palette buffer is provided
* otherwise we will skip it.
*/
if ((palette != NULL) && (tga->header.CMapLength > 0)) {
/* Adjust palette to the starting color entry. */
palette += (tga->header.CMapStart * depth);
/* Read in the palette. */
if (read(tga->fh, palette, size) == size) {
/* Swap the byte ordering of the palette entries. */
for (i = 0; i < tga->header.CMapLength; i++) {
#if(0)
for (n = 0; n < depth; n++) {
c = *(palette + n);
*(palette + n) = *(palette + ((depth - 1) - n));
*(palette + ((depth - 1) - n)) = c;
}
#else
c = *palette;
*palette = *(palette + (depth - 1));
*(palette + (depth - 1)) = c;
#endif
/* Next entry */
palette += depth;
}
} else {
error = TGAERR_READ;
}
} else {
if (lseek(tga->fh, size, SEEK_CUR) == -1) {
error = TGAERR_READ;
}
}
}
/* Load the image data from the TGA if an image buffer is provided
* otherwise we are done.
*/
if (!error && (image != NULL)) {
depth = (tga->header.PixelDepth >> 3);
size = ((tga->header.Width * tga->header.Height) * depth);
switch (tga->header.ImageType) {
case TGA_CMAPPED:
if (read(tga->fh, image, size) != size) {
error = TGAERR_READ;
}
break;
case TGA_TRUECOLOR:
if (read(tga->fh, image, size) == size) {
InvertImageData(&tga->header, image);
} else {
error = TGAERR_READ;
}
break;
case TGA_CMAPPED_ENCODED:
error = DecodeImageData(tga, image);
break;
case TGA_TRUECOLOR_ENCODED:
if ((error = DecodeImageData(tga, image)) == NULL) {
InvertImageData(&tga->header, image);
}
break;
default:
error = TGAERR_NOTSUPPORTED;
break;
}
/* Arrange the image so that the origin position (coordinate 0,0)
* is the upperleft hand corner of the image.
*/
if (!error) {
if (tga->header.ImageDescriptor & TGAF_XORIGIN) {
XFlipTarga(&tga->header, image);
}
if ((tga->header.ImageDescriptor & TGAF_YORIGIN) == 0) {
YFlipTarga(&tga->header, image);
}
}
}
/* Close the Targa */
CloseTarga(tga);
} else {
error = TGAERR_OPEN;
}
return (error);
}
/****************************************************************************
*
* NAME
* SaveTarga - Save a Targa Image File.
*
* SYNOPSIS
* Error = SaveTarga(Name, TGAHeader, Palette, ImageBuffer)
*
* long SaveTarga(char *, TGAHeader *, char *, char *);
*
* FUNCTION
*
* INPUTS
* Name - Pointer to name of file to save.
* TGAHeader - Pointer to initialized targa header structure.
* Palette - Pointer to palette.
* ImageBuffer - Pointer to raw image data.
*
* RESULT
* Error - 0 if successful, or TGAERR_??? error code.
*
****************************************************************************/
long SaveTarga(char *name, TGAHeader *tgahd, char *palette, char *image)
{
TGAHandle *tga;
long size;
long depth;
char *temppal;
char *ptr;
long i,n;
char c;
long error = 0;
/* Open the Targa for write. */
if ((tga = OpenTarga(name, TGA_WRITEMODE)) != NULL) {
/* Write the header. */
if (write(tga->fh, tgahd, sizeof(TGAHeader)) != sizeof(TGAHeader)) {
error = TGAERR_WRITE;
}
/* Write the palette. */
if (!error && (palette != NULL) && (tgahd->CMapLength > 0)) {
/* Adjust palette to the starting color entry. */
depth = (tgahd->CMapDepth >> 3);
palette += (tgahd->CMapStart * depth);
size = (tgahd->CMapLength * depth);
/* Allocate temporary buffer for palette manipulation. */
if ((temppal = (char *)malloc(size)) != NULL) {
memcpy(temppal, palette, size);
ptr = temppal;
/* Swap the byte ordering of the palette entries. */
for (i = 0; i < tga->header.CMapLength; i++) {
for (n = 0; n < (depth >> 1); n++) {
c = *(ptr + n);
*(ptr + n) = *(ptr + (depth - n));
*(ptr + (depth - n)) = c;
}
/* Next entry */
palette += depth;
}
/* Write the palette. */
if (write(tga->fh, temppal, size) != size) {
error = TGAERR_WRITE;
}
/* Free temporary palette buffer. */
free(temppal);
} else {
error = TGAERR_NOMEM;
}
}
/* Invert truecolor data. */
if (tgahd->ImageType == TGA_TRUECOLOR) {
InvertImageData(tgahd, image);
}
/* Write the image. */
if (!error && (image != NULL)) {
depth = (tgahd->PixelDepth >> 3);
size = (((tgahd->Width * tgahd->Height)) * depth);
if (write(tga->fh, image, size) != size) {
error = TGAERR_WRITE;
}
}
/* Close targa file. */
CloseTarga(tga);
} else {
error = TGAERR_OPEN;
}
return (error);
}
/****************************************************************************
*
* NAME
* XFlipTarga - X flip the image.
*
* SYNOPSIS
* XFlipTarga(TGAHeader, Image)
*
* void XFlipTarga(TGAHeader *, char *);
*
* FUNCTION
* Flip the image in memory on its X axis. (left to right)
*
* INPUTS
* TGAHeader - Pointer to initialized TGAHeader structure.
* Image - Pointer to image buffer.
*
* RESULT
* NONE
*
****************************************************************************/
void XFlipTarga(TGAHeader *tga, char *image)
{
char *ptr,*ptr1;
long x,y,d;
char v,v1;
char depth;
/* Pixel depth in bytes. */
depth = (tga->PixelDepth >> 3);
for (y = 0; y < tga->Height; y++) {
ptr = (image + ((tga->Width * depth) * y));
ptr1 = (ptr + ((tga->Width * depth) - depth));
for (x = 0; x < (tga->Width / 2); x++) {
for (d = 0; d < depth; d++) {
v = *(ptr + d);
v1 = *(ptr1 + d);
*(ptr + d) = v1;
*(ptr1 + d) = v;
}
ptr += depth;
ptr1 -= depth;
}
}
}
/****************************************************************************
*
* NAME
* YFlipTarga - Y flip the image.
*
* SYNOPSIS
* YFlipTarga(TGAHeader, Image)
*
* void YFlipTarga(TGAHeader *, char *);
*
* FUNCTION
* Flip the image in memory on its Y axis. (top to bottom)
*
* INPUTS
* TGAHeader - Pointer to initialized TGAHeader structure.
* Image - Pointer to image buffer.
*
* RESULT
* NONE
*
****************************************************************************/
void YFlipTarga(TGAHeader *tga, char *image)
{
char *ptr,*ptr1;
long x,y;
char v,v1;
char depth;
/* Pixel depth in bytes. */
depth = (tga->PixelDepth >> 3);
for (y = 0; y < (tga->Height >> 1); y++) {
/* Compute address of lines to exchange. */
ptr = (image + ((tga->Width * y) * depth));
ptr1 = (image + ((tga->Width * (tga->Height - 1)) * depth));
ptr1 -= ((tga->Width * y) * depth);
/* Exchange all the pixels on this scan line. */
for (x = 0; x < (tga->Width * depth); x++) {
v = *ptr;
v1 = *ptr1;
*ptr = v1;
*ptr1 = v;
ptr++;
ptr1++;
}
}
}
/****************************************************************************
*
* NAME
* DecodeImageData - Decompress Targa image data.
*
* SYNOPSIS
* Error = DecodeImageData(TGAHandle, ImageBuffer)
*
* long DecodeImageData(TGAHandle *, char *);
*
* FUNCTION
* Decode the RLE compressed image data into the specified buffer from
* the file I/O stream.
*
* INPUTS
* TGAHandle - Pointer to TGAHandle returned by OpenTarga().
* ImageBuffer - Pointer to buffer to decompress image into.
*
* RESULT
* Error - 0 if successful, or TGAERR_??? error code.
*
****************************************************************************/
static long DecodeImageData(TGAHandle *tga, char *image)
{
char *packet;
unsigned char count;
unsigned char depth;
unsigned long pixel_count;
unsigned long size;
unsigned long c,i;
long error = 0;
/* Compute pixel depth in bytes. */
depth = (tga->header.PixelDepth >> 3);
/* Total number of pixels compressed in this image. */
pixel_count = (tga->header.Width * tga->header.Height);
/* Allocate packet buffer to hold maximum encoded data run. */
if ((packet = (char *)malloc(128 * depth)) != NULL) {
while ((pixel_count > 0) && !error) {
/* Read count. */
if (read(tga->fh, &count, 1) == 1) {
/* If bit 8 of the count is set then we have a run of pixels,
* otherwise the data is raw pixels.
*/
if (count & 0x80) {
count &= 0x7F;
count++;
/* Read in run pixel. */
if (read(tga->fh, packet, depth) == depth) {
/* Repeat the pixel for the run count in the image buffer. */
for (c = 0; c < count; c++) {
for (i = 0; i < depth; i++) {
*image++ = *(packet + i);
}
}
} else {
error = TGAERR_READ;
}
} else {
count++;
size = (count * depth);
/* Read in raw pixels. */
if (read(tga->fh, packet, size) == size) {
/* Copy the raw pixel data into the image buffer. */
memcpy(image, packet, size);
image += size;
} else {
error = TGAERR_READ;
}
}
/* Adjust the pixel count. */
pixel_count -= count;
} else {
error = TGAERR_READ;
}
}
/* Free packet buffer. */
free(packet);
} else {
error = TGAERR_NOMEM;
}
return (error);
}
/****************************************************************************
*
* NAME
* InvertImageData - Invert TrueColor image data.
*
* SYNOPSIS
* InvertImageData(TGAHeader, ImageData)
*
* void InvertImageData(TGAHeader *, char *);
*
* FUNCTION
*
* INPUTS
* TGAHeader - Pointer to initialized TGAHeader structure.
* ImageData - Pointer to TrueColor image data.
*
* RESULT
* NONE
*
****************************************************************************/
static void InvertImageData(TGAHeader *tga, char *image)
{
long depth;
long pixel_count;
long i;
char c;
/* Compute the pixel depth in bytes. */
depth = (tga->PixelDepth >> 3);
/* Total number of pixels in this image. */
pixel_count = (tga->Width * tga->Height);
/* 16-bit pixel layout is different that 24-bit and 32-bit. */
if (depth > 2) {
while (pixel_count > 0) {
for (i = 0; i < (depth / 2); i++) {
c = *(image + i);
*(image + i) = *(image + ((depth - 1) - i));
*(image + ((depth - 1) - i)) = c;
}
/* Next pixel */
pixel_count--;
image += depth;
}
} else {
}
}