/*  vocfile.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "cgidbg.h"
#include "remcom.h"
#include "yacintfc.h"
#include "vocfile.h"
#include "sysintfc.h"
#include "usercom.h"
#include "mkstr.h"
#include "environ.h"
#ifdef __NT_VC__
#include <io.h>
#endif

const HeaderSize = 0x1a ;

CreativeVoiceFile::CreativeVoiceFile(VoiceNode * node, const char * file_name,
	int head):
	no_header(head),
	exp_file_name(0),
	delete_file_name(0),
	TheInputFile(-1)
	
{
	TheNode = node ;
	FileName = file_name ;
	if (!FileName) FileName = node->GetName();
	strcpy(FileDescription,"NO FILE");
	MajorVersion = 0;
	MinorVersion = 0;
	Offset = 0;
	LastReadOK = 0;
	TheEndOfFile = 0;
	FileOpened = 0;
	SamplesRead = 0 ;
	NumberOfSamples = 0;
	DataBlockType = 0;
	SubBlockFormat = 0;
	int SampleRate = 0 ;

	ReadPtr = EndOfData ;
	EndOfData = 0 ;
	open(0);
}

void CreativeVoiceFile::open(int force_error)
{
	for (;;) {
        const char * new_file_name  = FileName ;
        const char * exp_name = TargetNode::open_file(&new_file_name,
			TheInputFile, "FileName parameter for `Creative Voice' file", force_error);
        if (!exp_name) return ;
        delete exp_file_name ;
        exp_file_name = 0 ;
        if (exp_name != new_file_name) exp_file_name = Concatenate(exp_name);
        if (FileName != new_file_name) {
            delete delete_file_name ;
            FileName = delete_file_name = Concatenate(new_file_name);
        }
		if (no_header) {
			FileOpened = 1 ;
			return ;
		}
        if (ReadHeader()) return ;
		if (!State.IsInteractive() && ! force_error) return ;
        const char * cannot = "cannot read `OutputNode' header in `" ;
#ifdef INTERACTIVE
        OutputType channel = OutputPrompt ;
        if (!State.IsInteractive()) channel = OutputCppHelp ;
        *Output + channel;
        if (State.IsInteractive()) OutputCurrentChannel << cannot 
            << new_file_name  << "'.\n" ;
        else
#endif
        State.Error(cannot,new_file_name,"'");
        TargetNode::expand_name(new_file_name,exp_name);
        delete delete_file_name ;
        delete exp_file_name ;
        exp_file_name = 0 ;
        delete_file_name = 0 ;
        FileName = 0 ;
        if (!State.IsInteractive()) break ;
    }

}

int CreativeVoiceFile::CheckIdentificationCode()
{
	int Version = (DataBuffer[0x17] << 8) + DataBuffer[0x16] ;
	int Check = Version + 0x1234 ;
	int Validate = (DataBuffer[0x19] << 8) + DataBuffer[0x18] ;
	if (0x2467 - Check != Validate) {
		OutputCurrentChannel << "File `" << FileName <<
			"' in VoiceNode `" << TheNode->GetName() <<
			"' does not have a valid identification code.\n" ;
/*
 *		LogOut << "Bad voice file ID code, Version = 0x" <<
 *			hex(Version) << "\n" ;
 *		LogOut << "Check = 0x" << hex(Check) << ", Validate = 0x" <<
 *			hex(Validate) << "\n" ;
 */
		return 0 ;
	}
	return 1 ;
}

int CreativeVoiceFile::ReadBlockHeader()
{
	int i;
	if (EndOfData - ReadPtr < 6) {
		State.Error("There is no data block in file`",
			FileName, "' from node `", TheNode->GetName(),
			"'.\n");
		return 0;
	}
/*
 *	LogOut << "ReadPtr-1 = 0x" << hex(ReadPtr -1) << ", BlockType = "
 *		<< DataBlockType << "\n" ;
 */

	DataBlockType = DataBuffer[ReadPtr++];
	if (DataBlockType != 1) {
		State.Error("Data sub-block is not a new voice block");
		return 0 ;
	}
	NumberOfSamples = 0 ;
	int Shift = 1 ;
	for (i = 0 ; i < 3 ; i++) {
		int Value = DataBuffer[ReadPtr++];
		// if (Value < 0) Value += 256 ;
		NumberOfSamples += Value*Shift;
/*
 *		LogOut << "DataBuffer[0x" << hex(ReadPtr-1) <<
 *			"] = 0x" << hex(DataBuffer[ReadPtr-1]) <<
 *			", NumberOfSamples = " << NumberOfSamples << "\n" ;
 */
		Shift <<= 8 ;
	}
	SampleRate = DataBuffer[ReadPtr++];
	if (SampleRate < 0) SampleRate += 256 ;
	SampleRate = 1000000 /(256 - SampleRate) ;
/*
 *	LogOut << "DataBuffer[0x" << hex(ReadPtr-1) << "] = 0x" << DataBuffer[ReadPtr-1]
 *		<< "\n" ;
 *	LogOut << "SampleRate = " << SampleRate << "\n" ;
 */
	SubBlockFormat = DataBuffer[ReadPtr++];
/*
 *	LogOut << "DataBuffer[0x" << hex(ReadPtr-1) << "] = 0x" << DataBuffer[ReadPtr-1]
 *		<< " - Format\n" ;
 */
	if (SubBlockFormat != 0) {
		State.Error(
		"Data sub-block does not contain 8 bit unpacked data");
		return 0 ;
	}
 	NumberOfSamples-=2 ;	// includes sample rate code and packing format
/*
 *	*Output + OutputCppHelp << "Skipping first 4000 samples in `" <<
 *		FileName << "'.\n" ;
 *	for (i = 0 ; i < 4000; i++) ReadWord();
 */
	return 1 ;
}

int CreativeVoiceFile::ReadBuffer()
{
	int Read = read(TheInputFile,DataBuffer,VoiceBufferSize);
	if (Read < 1) {
		ReadPtr = EndOfData=0;
		TheEndOfFile = 1;
		return Read ;
	}
	LastReadOK=1 ;
	ReadPtr = 0 ;
	EndOfData = Read ;
	return 1 ;
}

int CreativeVoiceFile::ReadHeader()
{
	int HeaderRead = ReadBuffer();
	if (HeaderRead < 0) {
		OutputCurrentChannel << "Error in reading file `" <<  FileName << 
			"' in VoiceNode `" <<  TheNode->GetName() <<  "'" ;
#ifdef INTERACTIVE
		*Output + OutputCppHelp ;
#endif
		SystemErrorMessage();
		return 0;
	}
	if (EndOfData - ReadPtr < HeaderSize) {
		OutputCurrentChannel << "File `" <<
		FileName << "' in VoiceNode `" << TheNode->GetName() <<
			"' is too small to be in voice file format.\n" ;
		return 0;
	}
	ReadPtr += HeaderSize ;
	if (!CheckIdentificationCode()) return 0 ;
	for (int i = 0 ; i < VoiceDescriptionSize ; i++)
		FileDescription[i] = DataBuffer[i] ;
	FileDescription[VoiceDescriptionSize] = '\0' ;
	MajorVersion = DataBuffer[0x17];
	MinorVersion = DataBuffer[0x16];
	Offset = (DataBuffer[0x15] << 8) + DataBuffer[0x14] ;

	if (ReadBlockHeader()) return FileOpened=1;
	return 0 ;
}


CreativeVoiceFile::~CreativeVoiceFile()
{
	delete delete_file_name ;
	delete exp_file_name ;
}


void CreativeVoiceFile::DisplayHeader()
{
/*	Header Contents
 *	Address:     Description (Variable name)
 *	0x00 - 0x13: File description (FileDescription)
 *	0x14 - 0x15: Offset to voice data (Offset)
 *	0x16 - 0x17: File format version (MajorVersion MinorVersion)
 *	0x18 - 0x19: Error code (NONE)
 *
 *	Data block header:
 *	0            Block type (DataBlockType)
 *	1-3	     Block length (NumberOfSamples (+1))
 *
 *	Sub-block header:
 *	0	     SampleRate (SampleRate)
 */
	if (no_header) {
		HelpOut << "Node `" << TheNode->GetName() <<
			"' has the `NoHeader' parameter set.\n" ;
		HelpOut << "No header was read.\n" ;
		return ;
	}
	HelpOut << "File header from `" << FileName <<
		"' in node `" << TheNode->GetName() << "'.\n" ;
	*Output << "File description: `" << FileDescription << "'.\n" ;
	char buf[32];
	sprintf(buf,"%0x",Offset);
	*Output << "Data block header starts at 0x" << buf << ".\n";
	*Output << "Format version is " << MajorVersion << "." << MinorVersion
		<< ", Data block type is " << DataBlockType << ".\n" ;

	*Output << "Block length is " << NumberOfSamples + 2 <<
		" bytes (" << NumberOfSamples + 2 << " samples).\n" ;
	*Output << "Sample rate is " << SampleRate << " hertz.\n" ;
}

MachWord CreativeVoiceFile::ReadWord()
{
    if (State.IsError()) return 0 ;
    if (TheInputFile < 1) open(1);
    if (TheInputFile < 1) return 0 ;
	if (ReadPtr >= EndOfData) {
		int Check = ReadBuffer();
		if (Check < 1) {
			LastReadOK = 0;
			return 0 ;
		}
	}
	int16 temp = DataBuffer[ReadPtr++];
	// LogOut << "temp = 0x" << hex(temp) << "\n" ;
	MachWord Ret = temp-128 ;
	// LogOut << "Ret = " << (MachWordCast) Ret << "\n" ;
	return  Ret ;
}

