/*
**	Command & Conquer Generals(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;
}

