554 lines
12 KiB
C++
554 lines
12 KiB
C++
/*
|
|
** Command & Conquer Generals Zero Hour(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/>.
|
|
*/
|
|
|
|
|
|
#include "stdAfx.h"
|
|
#include "sys/stat.h"
|
|
#include "iff.h"
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#define IFF_RAWREAD(iff,data,size,label) {if ( IFF_rawread ( (iff), (data), (size)) != (size)) goto label;}
|
|
|
|
|
|
int IFF_rawread ( IFF_FILE *iff, void *buffer, int bytes )
|
|
{
|
|
if ( ! (iff->flags & mIFF_FILE_LOADED ) )
|
|
{
|
|
return _read ( iff->fp, buffer, bytes );
|
|
}
|
|
|
|
if ( iff->file_size < (iff->file_pos + bytes) )
|
|
{
|
|
bytes = iff->file_size - iff->file_pos;
|
|
}
|
|
memcpy ( buffer, &iff->mem_file[iff->file_pos], bytes );
|
|
iff->file_pos += bytes;
|
|
return bytes;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_seek ( IFF_FILE *iff, int pos, int mode )
|
|
{
|
|
|
|
if ( ! (iff->flags & mIFF_FILE_LOADED ))
|
|
{
|
|
return lseek ( iff->fp, pos, mode );
|
|
}
|
|
|
|
switch ( mode )
|
|
{
|
|
case SEEK_CUR:
|
|
iff->file_pos += pos;
|
|
break;
|
|
case SEEK_END:
|
|
iff->file_pos = iff->file_size - pos;
|
|
break;
|
|
case SEEK_SET:
|
|
iff->file_pos = pos;
|
|
break;
|
|
}
|
|
|
|
if ( iff->file_pos < 0 )
|
|
{
|
|
iff->file_pos = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( iff->file_pos > iff->file_size )
|
|
{
|
|
iff->file_pos = iff->file_size;
|
|
}
|
|
}
|
|
|
|
return iff->file_pos;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
IFF_FILE *IFF_Open ( const char *name )
|
|
{
|
|
IFF_FILE *iff = NULL;
|
|
|
|
|
|
if ( ! (iff = (IFF_FILE *) malloc ( sizeof (IFF_FILE))))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
iff->fp = -1;
|
|
memset ( iff, 0, sizeof ( IFF_FILE ));
|
|
|
|
if ((iff->fp = open ( name, _O_BINARY | _O_RDONLY )) == -1 )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
|
|
return iff;
|
|
|
|
error:
|
|
|
|
if (iff)
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
IFF_FILE *IFF_Load ( const char *name )
|
|
{
|
|
IFF_FILE *iff = NULL;
|
|
|
|
if ( ! (iff = (IFF_FILE *) malloc ( sizeof (IFF_FILE))))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
memset ( iff, 0, sizeof ( IFF_FILE ));
|
|
iff->fp = -1;
|
|
|
|
if ((iff->fp = open ( name, _O_BINARY | _O_RDONLY )) == -1 )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
iff->file_size = lseek ( iff->fp, 0, SEEK_END );
|
|
lseek ( iff->fp, 0, SEEK_SET );
|
|
|
|
if ( !(iff->mem_file = ( char *) malloc ( iff->file_size) ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
DO_READ ( iff->fp, iff->mem_file, iff->file_size, error );
|
|
|
|
iff->flags |= mIFF_FILE_LOADED;
|
|
|
|
return iff;
|
|
|
|
error:
|
|
|
|
if (iff)
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void IFF_Reset ( IFF_FILE *iff )
|
|
{
|
|
IFF_seek ( iff, 0, SEEK_SET );
|
|
iff->pad_form = 0;
|
|
iff->pad_chunk = 0;
|
|
iff->FormSize = 0;
|
|
iff->ChunkSize = 0;
|
|
iff->next_byte = 0;
|
|
iff->chunk_size_pos = 0;
|
|
iff->form_size_pos = 0;
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void IFF_goto_form_end ( IFF_FILE *iff )
|
|
{
|
|
|
|
iff->FormSize += iff->pad_form;
|
|
iff->pad_form = 0;
|
|
if (iff->FormSize)
|
|
{
|
|
IFF_seek ( iff, iff->FormSize, SEEK_CUR );
|
|
iff->next_byte += iff->FormSize;
|
|
iff->FormSize = 0;
|
|
iff->ChunkSize = 0;
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void IFF_goto_chunk_end ( IFF_FILE *iff )
|
|
{
|
|
|
|
iff->ChunkSize += iff->pad_chunk;
|
|
iff->pad_chunk = 0;
|
|
if (iff->ChunkSize)
|
|
{
|
|
IFF_seek ( iff, iff->ChunkSize, SEEK_CUR );
|
|
iff->next_byte += iff->ChunkSize;
|
|
iff->FormSize -= iff->ChunkSize;
|
|
iff->ChunkSize = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_NextForm ( IFF_FILE *iff )
|
|
{
|
|
IFF_CHUNK chunk;
|
|
int form;
|
|
|
|
IFF_goto_form_end ( iff );
|
|
|
|
IFF_RAWREAD (iff, &chunk, sizeof( IFF_CHUNK ), error );
|
|
|
|
chunk.Size = BgEn32 (chunk.Size);
|
|
chunk.ID = BgEn32 (chunk.ID);
|
|
|
|
iff->pad_form = (int) (chunk.Size & 0x0001);
|
|
|
|
if (chunk.ID != vIFF_ID_FORM )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_RAWREAD (iff, &form, sizeof( int ), error);
|
|
|
|
iff->FormID = (int) BgEn32 (form);
|
|
|
|
iff->flags |= mIFF_FILE_FORMOPEN;
|
|
iff->next_byte += sizeof( int ) + sizeof ( IFF_CHUNK );
|
|
iff->FormSize = (int) chunk.Size - sizeof ( int );
|
|
iff->ChunkSize = 0;
|
|
iff->pad_chunk = 0;
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_NextChunk ( IFF_FILE *iff )
|
|
{
|
|
IFF_CHUNK chunk;
|
|
|
|
IFF_goto_chunk_end ( iff );
|
|
|
|
if (iff->FormSize==0)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_RAWREAD ( iff, &chunk, sizeof( IFF_CHUNK ), error );
|
|
|
|
chunk.Size = BgEn32 (chunk.Size);
|
|
chunk.ID = BgEn32 (chunk.ID);
|
|
|
|
iff->pad_chunk = (int) (chunk.Size & 0x0001);
|
|
|
|
iff->flags |= mIFF_FILE_CHUNKOPEN;
|
|
iff->ChunkID = (int) chunk.ID;
|
|
iff->ChunkSize = (int) chunk.Size;
|
|
iff->next_byte += sizeof ( IFF_CHUNK );
|
|
iff->FormSize -= sizeof ( IFF_CHUNK );
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void IFF_Close ( IFF_FILE *iff )
|
|
{
|
|
if (iff->fp != -1)
|
|
{
|
|
_close (iff->fp);
|
|
}
|
|
|
|
if ( iff->mem_file )
|
|
{
|
|
free ( iff->mem_file );
|
|
}
|
|
|
|
free ( iff );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_Read ( IFF_FILE *iff, void *buff, int size )
|
|
{
|
|
int read =0;
|
|
|
|
if ( size>iff->ChunkSize )
|
|
{
|
|
size = iff->ChunkSize;
|
|
}
|
|
|
|
read = IFF_rawread ( iff, buff, size);
|
|
|
|
if (read==-1)
|
|
{
|
|
read = 0;
|
|
}
|
|
|
|
iff->ChunkSize -= read;
|
|
iff->FormSize -= read;
|
|
iff->next_byte += read;
|
|
|
|
|
|
return read;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
IFF_FILE *IFF_New ( const char *name )
|
|
{
|
|
IFF_FILE *iff = NULL;
|
|
|
|
|
|
if ( ! (iff = (IFF_FILE *) malloc ( sizeof (IFF_FILE))))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
memset ( iff, 0, sizeof ( IFF_FILE ));
|
|
iff->fp = -1;
|
|
|
|
if ((iff->fp = _open ( name, _O_BINARY | _O_RDWR | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE )) == -1 )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
return iff;
|
|
|
|
error:
|
|
|
|
if (iff)
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_NewForm ( IFF_FILE *iff, int id )
|
|
{
|
|
IFF_CHUNK chunk;
|
|
|
|
chunk.ID = BgEn32 (vIFF_ID_FORM);
|
|
chunk.Size = BgEn32 (90000);
|
|
|
|
iff->FormSize = 0;
|
|
iff->ChunkSize = 0;
|
|
iff->FormID = id;
|
|
|
|
DO_WRITE ( iff->fp, &chunk, sizeof (IFF_CHUNK), error);
|
|
|
|
chunk.ID = BgEn32 ( (int) iff->FormID);
|
|
DO_WRITE ( iff->fp, &chunk.ID, sizeof (int), error );
|
|
|
|
iff->flags |= mIFF_FILE_FORMOPEN;
|
|
iff->form_size_pos = iff->next_byte + sizeof (int);
|
|
iff->next_byte += sizeof ( IFF_CHUNK ) + sizeof (int);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_NewChunk ( IFF_FILE *iff, int id )
|
|
{
|
|
IFF_CHUNK chunk;
|
|
|
|
chunk.Size = BgEn32 (90000);
|
|
chunk.ID = BgEn32 ( (int) id);
|
|
|
|
|
|
DO_WRITE ( iff->fp, &chunk, sizeof ( IFF_CHUNK ), error );
|
|
|
|
iff->flags |= mIFF_FILE_CHUNKOPEN;
|
|
iff->chunk_size_pos = iff->next_byte + sizeof (int);
|
|
iff->next_byte += sizeof ( IFF_CHUNK ) ;
|
|
iff->FormSize += sizeof ( IFF_CHUNK ) ;
|
|
iff->ChunkSize = 0;
|
|
iff->ChunkID = id;
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_Write ( IFF_FILE *iff, void *buff, int size )
|
|
{
|
|
int val =0;
|
|
|
|
val = _write ( iff->fp, buff, size);
|
|
|
|
if (val==-1)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
iff->ChunkSize += val;
|
|
iff->FormSize += val;
|
|
iff->next_byte += val;
|
|
|
|
|
|
return val;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_CloseForm ( IFF_FILE *iff )
|
|
{
|
|
int fp;
|
|
int off;
|
|
int size;
|
|
int pad = 0;
|
|
|
|
if (iff && ((fp = iff->fp) != -1))
|
|
{
|
|
|
|
if (iff->FormSize&0x0001)
|
|
{
|
|
DO_WRITE (fp, &pad, 1, error );
|
|
iff->next_byte++;
|
|
}
|
|
|
|
off = iff->next_byte - iff->form_size_pos;
|
|
|
|
size = BgEn32 ( (int) (iff->FormSize+sizeof ( int )));
|
|
|
|
if (lseek (fp, -off, SEEK_CUR)==iff->form_size_pos)
|
|
{
|
|
DO_WRITE ( fp, &size, sizeof (int), error );
|
|
lseek ( fp, 0, SEEK_END);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int IFF_CloseChunk ( IFF_FILE *iff )
|
|
{
|
|
int fp;
|
|
int off;
|
|
int size;
|
|
int pad = 0;
|
|
|
|
if (iff && ((fp = iff->fp) != -1 ))
|
|
{
|
|
|
|
if (iff->ChunkSize&0x0001)
|
|
{
|
|
DO_WRITE (fp, &pad, 1, error );
|
|
iff->next_byte++;
|
|
iff->FormSize++;
|
|
}
|
|
|
|
off = iff->next_byte - iff->chunk_size_pos;
|
|
|
|
size = BgEn32 ((int) iff->ChunkSize);
|
|
|
|
if (lseek (fp, -off, SEEK_CUR)==iff->chunk_size_pos)
|
|
{
|
|
DO_WRITE ( fp, &size, sizeof (int), error );
|
|
lseek ( fp, 0, SEEK_END);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
error:
|
|
|
|
return TRUE;
|
|
}
|
|
|