//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

/***************************************************************************
 **   C O N F I D E N T I A L --- W E S T W O O D   A S S O C I A T E S   **
 ***************************************************************************
 *                                                                         *
 *                 Project Name : Westwood Library                         *
 *                                                                         *
 *                    File Name : IFF.C                                    *
 *                                                                         *
 *                   Programmer : Joe L. Bostic                            *
 *                                                                         *
 *                   Start Date : May 16, 1991                             *
 *                                                                         *
 *                  Last Update : April 19, 1994   [SKB]                   *
 *                                                                         *
 *                                                                         *
 * IFF reader code designed for loading pictures (ILBM or PBM).            *
 *                                                                         *
 *-------------------------------------------------------------------------*
 * Functions:                                                              *
 *   Close_Iff_File -- Closes an IFF file handle.                          *
 *   Get_Iff_Chunk_Size -- Get the size of the given IFF chunk.            *
 *   Open_Iff_File -- Opens an IFF file for reading.                       *
 *   Read_Iff_Chunk -- Reads a chunk from an IFF file.                     *
 *   Write_Iff_Chunk -- Writes an IFF chuck out.                           *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "iff.h"
#include "file.h"

#define ID_FORM			MAKE_ID('F','O','R','M')

#ifdef MIN
#undef MIN
#endif

/***************************************************************************
 * OPEN_IFF_FILE -- Opens an IFF file for reading.                         *
 *                                                                         *
 *    This function will open an IFF file for reading.  It will perform    *
 *    a the simple validity test of checking the first four bytes to make  *
 *    sure they are "FORM".  The value returned is the filehandle of the   *
 *    opened file.                                                         *
 *                                                                         *
 * INPUT:   filename - ASCII name of the IFF file to be opened.            *
 *                                                                         *
 * OUTPUT:  Returns the filehandle.  If there is an error or the file      *
 *          is not an IFF FORM then -1 will be returned.                   *
 *                                                                         *
 * WARNINGS:   You are responsible for error handling if this function     *
 *             returns -1 (not an IFF file).                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   05/16/1991 JLB : Created.                                             *
 *   04/19/1994 SKB : Update to 32 bit library.                            *
 *=========================================================================*/
int __cdecl Open_Iff_File(char const *filename)
{
	int	fh;		// File handle.
	long	type;		// IFF file type.


	/* We want to be able to open the file for READ | WRITE, but we do not
	   want the Open_File to create it.  So check to see if it exists before
		the Open_File */

//	fh = Open_File(filename, READ);		// Open the source file for READ
//	Close_File(fh);

	//fh = Open_File(filename, READ | WRITE);	// Open the source file again
	fh = Open_File(filename, READ);	// Open the source file again

	//	Validate that it is a FORM type.

	Read_File(fh, &type, 4L);

	if (type == ID_FORM) {

		//	The file is valid (so far).  Position the read so that the actual
		//	IFF file type code can be read.

		Seek_File(fh, 4L, SEEK_CUR);		// Skip the filesize bytes.

	} else {

		// This is NOT an IFF file.  Close the source file and return with
		//	the error code.
		Close_File(fh);
		fh = WW_ERROR;
	}
	return fh;
}


/***************************************************************************
 * CLOSE_IFF_FILE -- Closes an IFF file handle.                            *
 *                                                                         *
 *    The routine will close the file that was opened with the             *
 *    Open_Iff_File() function.                                            *
 *                                                                         *
 * INPUT:   fh - File handle that was returned from Open_Iff_File().       *
 *                                                                         *
 * OUTPUT:  none                                                           *
 *                                                                         *
 * WARNINGS:   none                                                        *
 *                                                                         *
 * HISTORY:                                                                *
 *   05/16/1991 JLB : Created.                                             *
 *   04/19/1994 SKB : Update to 32 bit library.                            *
 *=========================================================================*/
void __cdecl Close_Iff_File(int fh)
{
	if (fh != WW_ERROR) Close_File(fh);
}


/***************************************************************************
 * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk.              *
 *                                                                         *
 * INPUT:      int file handle to open IFF file, long id to get size of   *
 *                                                                         *
 * OUTPUT:     long size of the chunk or 0L if it was not found            *
 *                                                                         *
 * WARNINGS:   none                                                        *
 *                                                                         *
 * HISTORY:                                                                *
 *   06/03/1991  CY : Created.                                             *
 *   04/19/1994 SKB : Update to 32 bit library.                            *
 *=========================================================================*/
unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id)
{
	long	form;					// Chunk iff form name.
	long	chunksize;			// Size of the chunk.
	char	first_iteration;	// Check once the current chunk name


	first_iteration = TRUE;

	for (;;) {
		if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break;


		if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break;

#if(IBM)
		chunksize = Reverse_Long(chunksize);
#endif

		if (id == form) {
			Seek_File(fh, -8L, SEEK_CUR);			// Seek back to the start of
			return(chunksize);						// the chunk & return size
		} else {

			if (first_iteration) {
				Seek_File(fh, 12L, SEEK_SET);		// Start at beginning of file.
				first_iteration = FALSE;			// Don't do this again
			} else {

				/* Otherwise, go to the next chunk in the file */

				chunksize = (chunksize + 1) & 0xFFFFFFFEL;
				Seek_File(fh, chunksize, SEEK_CUR);
			}
		}
	}

	return(0L);
}


/***************************************************************************
 * READ_IFF_CHUNK -- Reads a chunk from an IFF file.                       *
 *                                                                         *
 *    Once an IFF file is opened, various chunks must be read from it.     *
 *    This routine will search through the IFF file and load in the        *
 *    specified chunk.  It will scan through the entire file when          *
 *    searching for the chunk.  It will load the FIRST chunk of the given  *
 *    type.                                                                *
 *                                                                         *
 * INPUT:   fh       - File handle of IFF file.                            *
 *                                                                         *
 *          id       - Chunk ID code.                                      *
 *                                                                         *
 *          buffer   - Pointer to buffer to load the chunk.                *
 *                                                                         *
 *          maxsize  - Maximum data bytes to read.                         *
 *                                                                         *
 * OUTPUT:     Returns with the number of bytes read from the chunk.       *
 *             If 0 is returned, this indicates that the chunk wasn't      *
 *             found.                                                      *
 *                                                                         *
 * WARNINGS:   none                                                        *
 *                                                                         *
 * HISTORY:                                                                *
 *   05/16/1991 JLB : Created.                                             *
 *   04/19/1994 SKB : Update to 32 bit library.                            *
 *=========================================================================*/
unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize)
{
	long	form;					// Chunk iff form name.
	unsigned long	chunksize;			// Size of the chunk.
	char	first_iteration;	// Check once the current chunk name

	first_iteration = TRUE;

	for (;;) {
		if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break;

		if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break;

#if(IBM)
		chunksize = Reverse_Long(chunksize);
#endif

		if (id == form) {

			maxsize = MIN(maxsize, chunksize);
			Read_File(fh, buffer, maxsize);		// Read the buffer.

			chunksize = (chunksize + 1) & 0xFFFFFFFEL;
			if (maxsize < chunksize) {
				Seek_File(fh, chunksize - maxsize, SEEK_CUR);
			}
			return(maxsize);
		} else {

			if (first_iteration) {
				Seek_File(fh, 12L, SEEK_SET);		// Start at beginning of file.
				first_iteration = FALSE;			// Don't do this again

			} else {

				/* Otherwise, go to the next chunk in the file */

				chunksize = (chunksize + 1) & 0xFFFFFFFEL;
				Seek_File(fh, chunksize, SEEK_CUR);
			}
		}
	}

	return(0L);
}



/***************************************************************************
 * WRITE_IFF_CHUNK -- Writes an IFF chuck out.                             *
 *                                                                         *
 * INPUT:                                                                  *
 *                                                                         *
 * OUTPUT:                                                                 *
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   04/19/1994 SKB : Created.                                             *
 *=========================================================================*/
void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length)
{
	long	pos;		// Current position in the IFF file.
	long	oldpos;	// Record of start of chunk offset.
	long	endpos;	// end of file offset before we write our data
	long	value;
	BOOL	odd;		// Is length odd?
	char	pad = 0;	// Optional padding byte for even sized chunks.

	/*
	** Get the current end of file (before we write more data to the file)
	*/
	pos 	 = Seek_File (file, 0L, SEEK_CUR);
	endpos = Seek_File (file, 0L, SEEK_END);
	Seek_File (file, pos, SEEK_SET);

	if (length) {
		value = id;
		odd = (short)length & 0x01;

		Write_File(file, &value, 4L);
		oldpos = Seek_File(file, 0L, SEEK_CUR);
		Write_File(file, &value, 4L);
		Write_File(file, buffer, length);
		pos = Seek_File(file, 0L, SEEK_CUR);
		if (odd) {
			Write_File(file, &pad, 1L);
		}

		/*
		**	Update the chunk size long.
		*/
		Seek_File(file, oldpos, SEEK_SET);
		value = IFFize_LONG((pos - oldpos)-4);
		Write_File(file, &value, 4L);

		/*
		**	Update the file size LONG. if we are not just overwriting existing data
		*/
		// (MCC)
		if ( endpos < pos ) {
			Seek_File(file, 4L, SEEK_SET);
			value = IFFize_LONG((pos+odd) - 8);
			Write_File(file, &value, 4L);
		}

		/*
		**	Return to end of file.
		*/
		Seek_File(file, 0L, SEEK_END);
	}
}