/*	
 *	clipym.c
 *
 *	P/ECE YM (ST-Sound) Driver
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Mon Apr 19 19:00:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/****************************************************************************
 *	YMドライバ内部関数
 ****************************************************************************/

int
ym_init(YMDRIVER* ym, const void* data, int len)
{
	YMINFO* info = &ym->info;
	AY38910* ay38910 = &ym->ay38910;

	/* まずクリア。 */
	memset(ym, 0, sizeof(YMDRIVER));

	/* YM情報を取得します。 */
	if(ym_get_info(info, data, len) != 0) return -1;

	/* AY38910リセット。 */
	ay38910_reset(ay38910, info->ym2149_external_frequency_in_hz, NULL, NULL);

	/* VBL間隔サイクル数調整用DDAセットアップ。 */
	ym->vbl_interval.u = SPEAKER_FREQUENCY / info->player_frequency_in_hz;
	ym->vbl_interval.n = SPEAKER_FREQUENCY % info->player_frequency_in_hz;
	ym->vbl_interval.d =                    -info->player_frequency_in_hz;

	return 0;
}

int
ym_stream_callback(short* wbuff, int param)
{
	return ym_mix((YMDRIVER*)param, wbuff);
}

int
ym_get_info_ym3(YMINFO* info, const YM3HEADER* header, int len)
{
	const unsigned char* p = (const unsigned char*)header;

	memset(info, 0, sizeof(YMINFO));

	if((len - sizeof *header) % 14 != 0) return -1;
	info->nb_of_valid_vbl_of_the_file = (len - sizeof *header) / 14;
	info->vbl_number_to_loop_the_song = 0;
	info->nb_of_registers_in_each_vbl = 14;
	info->vbl_stride = 1;
	info->register_stride = info->nb_of_valid_vbl_of_the_file;
	info->ym2149_external_frequency_in_hz = 2000000;
	info->player_frequency_in_hz = 50;
	memcpy(info->id_of_format, header->id_of_ym3_format, 4);
	info->name_of_the_song = "";
	info->name_of_the_author = "";
	info->comments = "";

	p += sizeof *header;
	info->all_ym2149_registers = p;

	return 0;
}

int
ym_get_info_ym3b(YMINFO* info, const YM3BHEADER* header, int len)
{
	const unsigned char* p = (const unsigned char*)header;

	memset(info, 0, sizeof(YMINFO));

	if((len - sizeof *header - 4/*vbl_number_to_loop_the_song*/) % 14 != 0) return -1;
	info->nb_of_valid_vbl_of_the_file = (len - sizeof *header - 4/*vbl_number_to_loop_the_song*/) / 14;
	info->vbl_number_to_loop_the_song = LEWORD(p + len - 4/*vbl_number_to_loop_the_song*/); /* なぜかここだけLittleEndian */
	info->nb_of_registers_in_each_vbl = 14;
	info->vbl_stride = 1;
	info->register_stride = info->nb_of_valid_vbl_of_the_file;
	info->ym2149_external_frequency_in_hz = 2000000;
	info->player_frequency_in_hz = 50;
	memcpy(info->id_of_format, header->id_of_ym3b_format, 4);
	info->name_of_the_song = "";
	info->name_of_the_author = "";
	info->comments = "";

	p += sizeof *header;
	info->all_ym2149_registers = p;

	return 0;
}

int
ym_get_info_ym4(YMINFO* info, const YM4HEADER* header, int len)
{
	const unsigned char* p = (const unsigned char*)header;
	int i;
	int nb_of_digidrum_sample;
	int sample_size;

	memset(info, 0, sizeof(YMINFO));

	if(memcmp(header->check_string, "LeOnArD!", 8) != 0) return -1;
	if(memcmp(p + len - 4/*end_file_check*/, "End!", 4) != 0) return -1;
	info->nb_of_valid_vbl_of_the_file = BEWORD(header->nb_of_valid_vbl_of_the_file);
	info->vbl_number_to_loop_the_song = BEWORD(header->vbl_number_to_loop_the_song);
	info->nb_of_registers_in_each_vbl = 16;
	if(BEWORD(header->song_attributes) & YM_INTERLEAVED_DATA_BLOCK) {
		info->vbl_stride = 1;
		info->register_stride = info->nb_of_valid_vbl_of_the_file;
	} else {
		info->vbl_stride = 16;
		info->register_stride = 1;
	}
	info->ym2149_external_frequency_in_hz = 2000000;
	info->player_frequency_in_hz = 50;
	memcpy(info->id_of_format, header->id_of_ym4_format, 4);

	p += sizeof *header;
	nb_of_digidrum_sample = BEWORD(header->nb_of_digidrum_sample);
	for(i = 0; i < nb_of_digidrum_sample; i++) {
		sample_size = BEWORD(p);
		p += 4/*sample_size*/ + sample_size;
	}
	info->name_of_the_song = p;
	while(*p++) { /** no job **/ }
	info->name_of_the_author = p;
	while(*p++) { /** no job **/ }
	info->comments = p;
	while(*p++) { /** no job **/ }
	info->all_ym2149_registers = p;

	return -1;
}

int
ym_get_info_ym5(YMINFO* info, const YM5HEADER* header, int len)
{
	const unsigned char* p = (const unsigned char*)header;
	int i;
	int size_of_future_additional_data;
	int nb_of_digidrum_sample;
	int sample_size;

	memset(info, 0, sizeof(YMINFO));

	if(memcmp(header->check_string, "LeOnArD!", 8) != 0) return -1;
	if(memcmp(p + len - 4/*end_file_check*/, "End!", 4) != 0) return -1;
	info->nb_of_valid_vbl_of_the_file = BEWORD(header->nb_of_valid_vbl_of_the_file);
	info->vbl_number_to_loop_the_song = BEWORD(header->vbl_number_to_loop_the_song);
	info->nb_of_registers_in_each_vbl = 16;
	if(BEWORD(header->song_attributes) & YM_INTERLEAVED_DATA_BLOCK) {
		info->vbl_stride = 1;
		info->register_stride = info->nb_of_valid_vbl_of_the_file;
	} else {
		info->vbl_stride = 16;
		info->register_stride = 1;
	}
	info->ym2149_external_frequency_in_hz = BEWORD(header->ym2149_external_frequency_in_hz);
	info->player_frequency_in_hz = BEHALF(header->player_frequency_in_hz);
	memcpy(info->id_of_format, header->id_of_ym5_format, 4);

	p += sizeof *header;
	size_of_future_additional_data = BEHALF(header->size_of_future_additional_data);
	p += size_of_future_additional_data;
	nb_of_digidrum_sample = BEHALF(header->nb_of_digidrum_sample);
	for(i = 0; i < nb_of_digidrum_sample; i++) {
		sample_size = BEWORD(p);
		p += 4/*sample_size*/ + sample_size;
	}
	info->name_of_the_song = p;
	while(*p++) { /** no job **/ }
	info->name_of_the_author = p;
	while(*p++) { /** no job **/ }
	info->comments = p;
	while(*p++) { /** no job **/ }
	info->all_ym2149_registers = p;

	return 0;
}

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

int
ym_get_info(YMINFO* info, const void* data, int len)
{
	int retval;
	if(len < 4) {
		retval = -1;
	} else if(memcmp(data, "YM3!", 4) == 0) {
		retval = ym_get_info_ym3(info, (const YM3HEADER*)data, len);
	} else if(memcmp(data, "YM3b", 4) == 0) {
		retval = ym_get_info_ym3b(info, (const YM3BHEADER*)data, len);
	} else if(memcmp(data, "YM4!", 4) == 0) {
		retval = ym_get_info_ym4(info, (const YM4HEADER*)data, len);
	} else if(memcmp(data, "YM5!", 4) == 0 ||
	          memcmp(data, "YM6!", 4) == 0) {
		retval = ym_get_info_ym5(info, (const YM5HEADER*)data, len);
	} else {
		return -1;
	}
	return retval;
}

int
ym_play(const void* data, int len)
{
	static YMDRIVER ym; /* STATICです! */

	/* まず確実に停止します。 */
	ym_stop();

	/* YMドライバを初期化します。 */
	if(ym_init(&ym, data, len) != 0) return -1;

	/* ストリーム再生を開始します。 */
	stream_play(YMBUFLEN, ym_stream_callback, (int)&ym, 1/*スタック切換使用*/);

	return 0;
}

void
ym_stop()
{
	/* ストリーム再生を停止します。 */
	stream_stop();

	/* YMドライバのクリーンアップは不要です。 */
}

