CnC_Red_Alert/WINVQ/VQM32/IFF.CPP

700 lines
16 KiB
C++
Raw Permalink 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
* iff.c
*
* DESCRIPTION
* IFF manager. (32-Bit protected mode)
*
* PROGRAMMER
* Denzil E. Long, Jr.
*
* DATE
* January 26, 1995
*
*----------------------------------------------------------------------------
*
* PUBLIC
* OpenIFF - Open an IFF for reading or writting.
* CloseIFF - Close an IFF.
* ReadForm - Read the IFF FORM, size and type of the file.
* WriteForm - Write IFF form ID, size and type fields.
* ReadChunkHeader - Read the IFF chunk identification header.
* WriteChunkHeader - Write an IFF chunk identification header.
* WriteChunk - Write an IFF chunk with data from a buffer.
* WriteChunkBytes - Write data from a buffer to the IFF stream.
* SkipChunkBytes - Skip bytes in a chunk.
* FindChunk - Scan for a specific chunk name.
* IDtoStr - Convert a longword identifier to a NULL-terminated
* string.
* CurrentFilePos - Get the current file position.
*
****************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <mem.h>
#include "iff.h"
/****************************************************************************
*
* NAME
* OpenIFF - Open an IFF for reading or writting.
*
* SYNOPSIS
* IFFHandle = OpenIFF(Name, Mode)
*
* IFFHandle *OpenIFF(char *, long);
*
* FUNCTION
* Opens an IFF for a new read or write. The direction of the I/O is
* given by the value of Mode, which can be either IFF_READ or IFF_WRITE.
*
* INPUTS
* Name - Pointer to name of file to open.
* Mode - IFF_READ or IFF_WRITE.
*
* RESULT
* IFFHandle - Pointer to IFFHandle structure or NULL if error.
*
****************************************************************************/
IFFHandle *OpenIFF(char *name, long mode)
{
IFFHandle *iff;
/* Allocate IFFHandle structure. */
if ((iff = (IFFHandle *)malloc(sizeof(IFFHandle))) != NULL) {
/* Initialize handle.*/
memset(iff, 0, sizeof(IFFHandle));
iff->flags = mode;
switch (mode) {
case IFFF_READ:
iff->fh = open(name, O_RDONLY|O_BINARY);
break;
case IFFF_WRITE:
iff->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY),
(S_IREAD|S_IWRITE));
printf("\r");
break;
case (IFFF_READ|IFFF_WRITE):
iff->fh = open(name, (O_RDWR|O_BINARY), (S_IREAD|S_IWRITE));
break;
default:
iff->fh = -1;
break;
}
/* If something went wrong we must free up any resources
* that we have opened.
*/
if (iff->fh == -1) {
free(iff);
iff = NULL;
}
}
return (iff);
}
/****************************************************************************
*
* NAME
* CloseIFF - Close an IFF.
*
* SYNOPSIS
* CloseIFF(IFFHandle)
*
* void CloseIFF(IFFHandle *);
*
* FUNCTION
* Completes an IFF read or write operation.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
*
* RESULT
* NONE
*
****************************************************************************/
void CloseIFF(IFFHandle *iff)
{
long length;
/* Write the length of the FORM */
if ((iff->flags & IFFF_WRITE) && ((iff->form.size == 0)
|| (iff->scan > iff->form.size))) {
lseek(iff->fh, 4, SEEK_SET);
length = REVERSE_LONG(iff->scan);
write(iff->fh, &length, 4);
}
close(iff->fh);
free(iff);
}
/****************************************************************************
*
* NAME
* ReadForm - Read the IFF FORM, size and type of the file.
*
* SYNOPSIS
* Error = ReadForm(IFFHandle, FormHeader)
*
* long ReadForm(IFFHandle *, FormHeader *);
*
* FUNCTION
* Read in the IFF form, size, type information. If the FormHeader
* structure pointer is NULL then the FORM will be read into the
* IFFHandles form structure.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* FormHeader - Pointer to FormHeader structure.
*
* RESULT
* Error - 0 if successful or IFFERR_??? error code.
*
****************************************************************************/
long ReadForm(IFFHandle *iff, FormHeader *form)
{
FormHeader *ptr;
long error;
/* Read the FORM into the IFFHandle or the provided FormHeader. */
if (form == NULL) {
ptr = &iff->form;
} else {
ptr = form;
}
/* Read in IFF FORM from the file stream.. */
if ((error = read(iff->fh, ptr, 12)) == 12) {
ptr->size = REVERSE_LONG(ptr->size);
iff->scan = 4;
error = 0;
} else {
if (error == -1)
error = IFFERR_READ;
else if (error == 0)
error = IFFERR_EOF;
}
return (error);
}
/****************************************************************************
*
* NAME
* WriteForm - Write IFF form ID, size and type fields.
*
* SYNOPSIS
* Error = WriteForm(IFFHandle)
*
* long WriteForm(IFFHandle, FormHeader *);
*
* FUNCTION
* Write out the IFF form, size, type information. If the size field
* is zero then the IFF form size will be calculated and written by
* the CloseIFF() function. If the FormHeader structure pointer is NULL
* the the form from the IFFHandle will be written.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* FormHeader - Pointer to FormHeader structure.
*
* RESULT
* Error - 0 if successful or IFFERR_??? error code.
*
****************************************************************************/
long WriteForm(IFFHandle *iff, FormHeader *form)
{
FormHeader *ptr;
long error = 0;
/* Use the FORM from the IFFHandle or the provided FormHeader. */
if (form == NULL) {
ptr = &iff->form;
} else {
ptr = form;
}
/* Write the IFF form to the file stream. */
if (iff->flags & IFFF_WRITE) {
ptr->size = REVERSE_LONG(ptr->size);
if (write(iff->fh, ptr, 12) == 12) {
iff->scan = 4;
} else {
error = IFFERR_WRITE;
}
} else {
error = IFFERR_WRITE;
}
return (error);
}
/****************************************************************************
*
* NAME
* ReadChunkHeader - Read the IFF chunk identification header.
*
* SYNOPSIS
* Error = ReadChunkHeader(IFFHandle)
*
* long ReadChunkHeader(IFFHandle *);
*
* FUNCTION
* Read the IFF identification header from the files data stream.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
*
* RESULT
* Error - 0 if successful or IFFERR_??? error code.
*
****************************************************************************/
long ReadChunkHeader(IFFHandle *iff)
{
long error = 0;
/* Skip any part of the previous chunk that hasn't been processed. */
if ((iff->cn.size != 0) && (iff->cn.scan < PADSIZE(iff->cn.size))) {
error = lseek(iff->fh, (PADSIZE(iff->cn.size) - iff->cn.scan), SEEK_CUR);
if (error == -1) {
error = IFFERR_READ;
} else {
error = 0;
}
}
/* Read in the next chunk header context. */
if (!error) {
if ((error = read(iff->fh, &iff->cn, 8)) == 8) {
error = 0;
iff->scan += 8;
iff->cn.size = REVERSE_LONG(iff->cn.size);
iff->cn.scan = 0;
} else {
if (error == -1) {
error = IFFERR_READ;
} else if (error == 0) {
error = IFFERR_EOF;
}
}
}
return (error);
}
/****************************************************************************
*
* NAME
* WriteChunkHeader - Write an IFF chunk identification header.
*
* SYNOPSIS
* Error = WriteChunkHeader(IFFHandle, ID, Size)
*
* long WriteChunkHeader(IFFHandle *, long, long);
*
* FUNCTION
* Write an IFF identification header to the files data stream.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* ID - ID code of chunk.
* Size - Size of chunk in bytes (WORD aligned).
*
* RESULT
* Error - 0 if successful or IFFERR_??? error code.
*
****************************************************************************/
long WriteChunkHeader(IFFHandle *iff, long id, long size)
{
long error = 0;
/* Make sure it is okay to write. */
if (iff->flags & IFFF_WRITE) {
iff->cn.id = id;
iff->cn.size = REVERSE_LONG(size);
iff->cn.scan = 0;
if (write(iff->fh, &iff->cn, 8) == 8) {
iff->scan += 8;
} else {
error = IFFERR_WRITE;
}
}
return (error);
}
/****************************************************************************
*
* NAME
* WriteChunk - Write an IFF chunk with data from a buffer.
*
* SYNOPSIS
* Actual = WriteChunk(IFFHandle, ID, Buffer, Size)
*
* long WriteChunk(IFFHandle *, long, char *, long);
*
* FUNCTION
* Write a IFF chunk at the current file position.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* ID - ID code of chunk.
* Buffer - Pointer to buffer area with bytes to be written.
* Size - Number of bytes to write.
*
* RESULT
* Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
*
****************************************************************************/
long WriteChunk(IFFHandle *iff, long id, char *buffer, long size)
{
Context cn;
long actual;
/* Make sure we can write to this file. */
if (iff->flags & IFFF_WRITE) {
cn.id = id;
cn.size = REVERSE_LONG(size);
/* Write chunk header. */
if (write(iff->fh, &cn, 8) == 8) {
iff->scan += 8;
iff->cn.scan += 8;
/* Write chunk data. */
actual = write(iff->fh, buffer, size);
if (actual == size) {
iff->scan += actual;
iff->cn.scan += actual;
/* Write chunk padding if necessary. */
if (PADSIZE(size) > size) {
id = 0;
if (write(iff->fh, &id, 1) == 1) {
iff->scan++;
iff->cn.scan++;
} else {
actual = IFFERR_WRITE;
}
}
} else {
actual = IFFERR_WRITE;
}
} else {
actual = IFFERR_WRITE;
}
} else {
actual = IFFERR_WRITE;
}
return (actual);
}
/****************************************************************************
*
* NAME
* WriteChunkBytes - Write data from a buffer to the IFF stream.
*
* SYNOPSIS
* Actual = WriteChunkBytes(IFFHandle, Buffer, Size)
*
* long WriteChunk(IFFHandle *, char *, long);
*
* FUNCTION
* Write a IFF chunk at the current file position.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* Buffer - Pointer to buffer area with bytes to be written.
* Size - Number of bytes to write.
*
* RESULT
* Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
*
****************************************************************************/
long WriteChunkBytes(IFFHandle *iff, char *buffer, long size)
{
long actual;
/* Make sure we can write to this file. */
if (iff->flags & IFFF_WRITE) {
/* Write data. */
if ((actual = (unsigned short)write(iff->fh, buffer, size)) == size) {
iff->scan += actual;
iff->cn.scan += actual;
} else {
actual = IFFERR_WRITE;
}
} else {
actual = IFFERR_WRITE;
}
return (actual);
}
/****************************************************************************
*
* NAME
* ReadChunkBytes - Read data from a chunk into a buffer.
*
* SYNOPSIS
* Actual = ReadChunkBytes(IFFHandle, Buffer, Length)
*
* long ReadChunkBytes(IFFHandle *, char *, long);
*
* FUNCTION
* Read in 'Length' number of bytes from the current chunk context.
* If the specified length exceeds the number of bytes remaining in the
* chunk ReadChunkBytes() will read in only the number of remaining
* bytes. ReadChunkBytes() will never read beyond the scope of the
* current chunk.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* Buffer - Pointer to buffer to read data into.
* Length - Number of bytes to read.
*
* RESULT
* Actual - (positive) Bytes written or (negative) IFFERR_??? error code.
*
****************************************************************************/
long ReadChunkBytes(IFFHandle *iff, char *buffer, long size)
{
long actual;
/* If the actual bytes remaining in the current chunk is less than
* the requested bytes to read then adjust the read request size
* to only read in the bytes that remain in the chunk.
*/
actual = (iff->cn.size - iff->cn.scan);
if (size > actual) {
size = actual;
}
/* Read in the requested number of bytes. */
if ((actual = read(iff->fh, buffer, size)) != size) {
actual = IFFERR_READ;
} else {
iff->scan += actual;
iff->cn.scan += actual;
}
return (actual);
}
/****************************************************************************
*
* NAME
* SkipChunkBytes - Skip bytes in a chunk.
*
* SYNOPSIS
* Error = SkipChunkBytes(IFFHandle, Skip)
*
* long SkipChunkBytes(IFFHandle *, long);
*
* FUNCTION
* Skip the specified number of bytes of the chunk.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* Skip - Number of bytes to skip.
*
* RESULT
* Error - 0 if successful or FAIL_??? error code.
*
****************************************************************************/
long SkipChunkBytes(IFFHandle *iff, long skip)
{
long error = 0;
if (lseek(iff->fh, skip, SEEK_CUR) == -1) {
error = IFFERR_READ;
} else {
iff->scan += skip;
iff->cn.scan += skip;
}
return (error);
}
/****************************************************************************
*
* NAME
* FindChunk - Scan for a specific chunk name.
*
* SYNOPSIS
* Error = FindChunk(IFFHandle, ID)
*
* long FindChunk(IFFHandle *, long);
*
* FUNCTION
* Scan from the current file position for the next occurance of the
* specified chunk ID. When a match is found the function will return
* with the matching chunk as the current context.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
* ID - ID code of chunk.
*
* RESULT
* Error - 0 if successful or FAIL_??? error code.
*
****************************************************************************/
long FindChunk(IFFHandle *iff, long id)
{
long found = 0;
long error = 0;
/* Invalid handle check. */
if (iff != NULL) {
/* Scan until we have a match or an error. */
while ((found == 0) && !(error = ReadChunkHeader(iff))) {
/* If we found a match the terminate scan, otherwise skip this
* chunk and process the next.
*/
if (iff->cn.id == id) {
found = 1;
} else {
error = SkipChunkBytes(iff, PADSIZE(iff->cn.size));
}
}
}
return (error);
}
/****************************************************************************
*
* NAME
* IDtoStr - Convert a longword identifier to a NULL-terminated string.
*
* SYNOPSIS
* String = IDtoStr(ID, Buffer)
*
* char *IDtoStr(long, char *);
*
* FUNCTION
* Writes the ASCII equivalent of the given longword ID into buffer as a
* NULL-terminated string.
*
* INPUTS
* ID - Longword ID.
* Buffer - Character buffer to accept string (at least 5 characters).
*
* RESULT
* String - The value of "Buffer".
*
****************************************************************************/
char *IDtoStr(long id, char *buf)
{
memcpy(buf, &id, 4);
*(buf + 4) = 0;
return (buf);
}
/****************************************************************************
*
* NAME
* CurrentFilePos - Get the current file position.
*
* SYNOPSIS
* Position = CurrentFilePos(IFFHandle)
*
* long CurrentFilePos(IFFHandle *);
*
* FUNCTION
* This function returns the offset in bytes of the current file position
* from the beginning of the IFF.
*
* INPUTS
* IFFHandle - Pointer to IFFHandle structure.
*
* RESULT
* Position - Offset in bytes from the beginning of the file to the
* current position.
*
****************************************************************************/
long CurrentFilePos(IFFHandle *iff)
{
long offset;
if ((offset = lseek(iff->fh, 0, SEEK_CUR)) == -1) {
offset = IFFERR_READ;
}
return (offset);
}