/* ----------------------------------------------------- */
/*  ADPCM DATA to ADPCM DATA for PMD ver 4.4 converter   */
/* ===================================================== */
/*  2021/08/17 coder g.zaffini                           */
/* ----------------------------------------------------- */

/* ----------------------------------------------------- */
/* Include                                               */
/* ----------------------------------------------------- */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* ----------------------------------------------------- */
/* Define                                                */
/* ----------------------------------------------------- */

/* ----------------------------------------------------- */
/* Prototypes                                            */
/* ----------------------------------------------------- */

 
/* ----------------------------------------------------- */
static void usage( const char *p_name ) {
	fprintf( stderr, "Usage: %s <ADPCMfile>\n", p_name );
}

/* ----------------------------------------------------- */
int main (int argc, char *argv[]) {

	unsigned char adpcm4_4 [30 + 2 + ( 256 * 1024)] = \
	/* DATA/FILE only         1    2    3    4    5    6    7    8    9    0    1    2    3    4    5    6    7    8    9    0    1    2    3    4    5    6    7    8    9    0 */ \
				{'A', 'D', 'P', 'C', 'M', 32 , 'D', 'A', 'T', 'A', 32 , 'f', 'o', 'r', 32 , 32 , 'P', 'M', 'D', 32 , 'v', 'e', 'r', '.', '4', '.', '4', '-', 32 , 32 ,}; /* 30 bytes */
	char fileTbl [128];
	size_t i, i0, adpcm4_4_tablearea;

	FILE * restrict interface_inpAdpcm;
	FILE * restrict interface_inpTable;
	FILE * restrict interface_outAdpcm4_4;

	printf( "%s\n", argv[0] );
	printf( "===========================================\n" );
	printf( "2021/08/17 coder g.zaffini\n" );

	if( argc < 2 ) {
		usage( argv[0] );
		return 1;
	}

	printf( "%s\n", argv[1] );
	interface_inpAdpcm = fopen ( argv[1], "rb" );
	if (NULL != interface_inpAdpcm) {
		i0 = 0;
		i = argv [1][i0];
		while ('\0' != i ) {
			fileTbl [i0] = i;
			i0++;
			i = argv [1][i0];
		}
		i0 = sizeof( fileTbl ) - (size_t)(1);
		for ( i = 0; i < sizeof( fileTbl ); i++ ) {
#if defined(_WIN32)
			if ( '\\' == fileTbl [i] ) {
#else
			if ( '/' == fileTbl [i] ) {
#endif
				i0 = ( i + 1 );
			}
			if ( '\0' == fileTbl [i] ) {
				break;
			}
		}
		if ( i0 == (size_t)( sizeof( fileTbl ) - (size_t)(1) ) ) {
			fileTbl [0] = 'P';
			fileTbl [1] = 'C';
			fileTbl [2] = 'M';
			fileTbl [3] = 'T';
			fileTbl [4] = 'B';
			fileTbl [5] = 'L';
			fileTbl [6] = '\0';

		} else {
			if ( ( i0 + 7 ) < 128 ) { 
				fileTbl [i0 + 0] = 'P';
				fileTbl [i0 + 1] = 'C';
				fileTbl [i0 + 2] = 'M';
				fileTbl [i0 + 3] = 'T';
				fileTbl [i0 + 3] = 'T';
				fileTbl [i0 + 4] = 'B';
				fileTbl [i0 + 5] = 'L';
				fileTbl [i0 + 6] = '\0';
			} else {
				printf ( "input file name error!\n" );
				fclose ( interface_inpAdpcm ) ;
				return -1;
			}
		}

		printf ( "%s \n", fileTbl );
		interface_inpTable = fopen ( fileTbl, "rb" );
		if (NULL != interface_inpTable) {
			unsigned char table_buffer [256 * 4];
			size_t i4_4 = 0;
			size_t i4_4_tablearea;
			unsigned long int next_table = 0x10000;

			/* clear table_buffer */
			for ( i = 0; i < sizeof ( table_buffer ); i++ ) {
				table_buffer [i] = 0u;
			}

			/* clear adpcm4_4 */
			for ( i4_4 = 30; i4_4 < sizeof ( adpcm4_4 ); i4_4++ ) {
				adpcm4_4 [i4_4] = 0u;
			}

			/* set i4_4 */
			i4_4 = ( 0x21 * 0x20 );

			/* set adpcm4_4_tablearea */
			adpcm4_4_tablearea = 32;

			/* do fill adpcm4_4 */
			rewind ( interface_inpTable );
			i0 = fread ( table_buffer, 1, sizeof ( table_buffer ), interface_inpTable );
			if ( ( 0 == ( i0 % 256 ) ) || ( i0 == sizeof ( table_buffer ) ) ) {
				/* set next_table */
				next_table = (unsigned short int)table_buffer [0] + (unsigned short int)( 0x100 * table_buffer [1] );
			} else {
				printf ( "error reading PCMTBL!\n" );
				fclose ( interface_inpTable );
				fclose ( interface_inpAdpcm ) ;
				return -1;
			}
			while ( i0 > 0 ) {
				unsigned long int start, stop, curr_start, curr_stop;
				size_t pcmtbl_pos;
				curr_start = 0u;
				do {
					curr_stop = next_table + 1lu;
					pcmtbl_pos = 256;
					for ( i = 2; i < i0 - 2; i += 4 ) {
						start = (unsigned short int)table_buffer [i + 0] + (unsigned short int)( 0x100 * table_buffer [i + 1] );
						stop = (unsigned short int)table_buffer [i + 2] + (unsigned short int)( 0x100 * table_buffer [i + 3] );
						if ( ( start == curr_start ) && ( stop > 0lu ) && ( stop <= curr_stop ) ) {
							curr_stop = stop;
							pcmtbl_pos = i - 2;
						}
					}
					if ( curr_stop <= next_table ) {
						rewind ( interface_inpAdpcm );
						fseek ( interface_inpAdpcm, curr_start * 4, SEEK_SET );

						i = fread ( &adpcm4_4 [i4_4], 1, (curr_stop + 1lu - curr_start) * 4lu, interface_inpAdpcm );
						if ( i == ( (curr_stop + 1lu - curr_start) * 4lu ) ) {
							while ( ( i % 32 ) != 0 ) {
								adpcm4_4 [i4_4 + i++] = 0x88;
							}
							i4_4_tablearea = i4_4 + (0x05 * 0x20);
							adpcm4_4 [adpcm4_4_tablearea + pcmtbl_pos + 0] = ( i4_4_tablearea / 32 ) & 0xff;
							adpcm4_4 [adpcm4_4_tablearea + pcmtbl_pos + 1] = ( ( i4_4_tablearea / 32 ) >> 8 ) & 0xff;
							i4_4 += i;
							i4_4_tablearea = i4_4 + (0x05 * 0x20);
							adpcm4_4 [adpcm4_4_tablearea + pcmtbl_pos + 2] = ( ( i4_4_tablearea - 1 ) / 32 ) & 0xff;
							adpcm4_4 [adpcm4_4_tablearea + pcmtbl_pos + 3] = ( ( ( i4_4_tablearea - 1 ) / 32 ) >> 8 ) & 0xff;
						} else {
							printf ( "error reading ADPCM!\n" );
							fclose ( interface_inpTable ) ;
							fclose ( interface_inpAdpcm ) ;
							return -1;
						}

					}
					/* set curr_start */
					curr_start = curr_stop + 1lu;

				} while ( curr_stop < next_table );

				/* read another chunk of PCMTBL */
				i0 = fread ( table_buffer, 1, sizeof ( table_buffer ), interface_inpTable );
				if ( ( i0 > 0 ) && \
					( ( 0 == ( i0 % 256 ) ) || ( i0 == sizeof ( table_buffer ) ) ) ) {
					/* set next_table */
					next_table = (unsigned short int)table_buffer [0] + (unsigned short int)( 0x100 * table_buffer [1] );
				} else if ( i0 > 0 ) {
					printf ( "error reading PCMTBL!\n" );
					fclose ( interface_inpTable ) ;
					fclose ( interface_inpAdpcm ) ;
					return -1;
				}
			}
			if ( !feof( interface_inpTable ) ) {
				printf ( "error reading PCMTBL!\n" );
				fclose ( interface_inpTable ) ;
				fclose ( interface_inpAdpcm ) ;
				return -1;
			}

			/* do complete adpcm4_4 DATA/FILE header */
			i = 30;
			adpcm4_4 [ i++ ] = ( i4_4_tablearea / 32 ) & 0xff;
			adpcm4_4 [ i ] = ( ( i4_4_tablearea / 32 ) >> 8 ) & 0xff;

			/* do close file fileTbl */
			fclose ( interface_inpAdpcm ) ;

			/* do close file fileTbl */
			fclose ( interface_inpTable ) ;

			/* do open file interface_outAdpcm4_4 */
			i0 = sizeof( fileTbl ) - (size_t)(1);
			for ( i = 0; i < sizeof( fileTbl ); i++ ) {
#if defined(_WIN32)
				if ( '\\' == fileTbl [i] ) {
#else
				if ( '/' == fileTbl [i] ) {
#endif
					i0 = ( i + 1 );
				}
				if ( '\0' == fileTbl [i] ) {
					break;
				}
			}
			if ( i0 == (size_t)( sizeof( fileTbl ) - (size_t)(1) ) ) {
				fileTbl [0] = 'A';
				fileTbl [1] = 'D';
				fileTbl [2] = 'P';
				fileTbl [3] = 'C';
				fileTbl [4] = 'M';
				fileTbl [5] = '4';
				fileTbl [6] = '_';
				fileTbl [7] = '4';
				fileTbl [8] = '\0';

			} else {
				if ( i0 + 9 < 128 ) { 
					fileTbl [i0 + 0] = 'A';
					fileTbl [i0 + 1] = 'D';
					fileTbl [i0 + 2] = 'P';
					fileTbl [i0 + 3] = 'C';
					fileTbl [i0 + 4] = 'M';
					fileTbl [i0 + 5] = '4';
					fileTbl [i0 + 6] = '_';
					fileTbl [i0 + 7] = '4';
					fileTbl [i0 + 8] = '\0';
				} else {
					printf ( "input file name error!\n" );
					return -1;
				}
			}

			/* do write into file interface_outAdpcm4_4 */
			printf ( "%s \n", fileTbl );
			interface_outAdpcm4_4 = fopen ( fileTbl, "wb" );
			if (NULL != interface_outAdpcm4_4) {
				rewind ( interface_outAdpcm4_4 );
				i0 = fwrite ( adpcm4_4, 1, i4_4, interface_outAdpcm4_4);
				if ( i0 < i4_4 ) {
					printf ( "output file creation error!\n" );
				}
			} else {
				printf ( "output file creation error!\n" );
				return -1;
			}

			/* do close file interface_outAdpcm4_4 */
			fclose ( interface_outAdpcm4_4 ) ;

		} else {
			printf ( "input file %s error!\n", fileTbl );
			fclose ( interface_inpAdpcm ) ;
			return -1;
		}
	} else {
		printf ( "input file %s not found!", argv [1] );
		return -1;
	}
}

