/*
	Ninja Library
	
	COPYRIGHT (C) SEGA ENTERPRISES,LTD.

	Create 2000.03.01

*/
#include <Ninja.h>
#include <Njutil.h>
#include <sg_gd.h>
#include <stdio.h>

#define READ_INIT		0
#define READ_WAIT		1
#define READ_WAIT_DMA	2
#define READ_END		3
#define READ_ERR		4

#define FILE_BUFFER		0
#define USER_BUFFER		1

#define NUD_FILE_BUFFER_SIZE	(2048+31)

#define CALC_SECT_BYTE(sect)	((sect) << 11)
#define CALC_BYTE_SECT(byte)	((byte) >> 11)
#define CALC_BUFFER_POS(byte)	((byte) & 0x000007FF)

Sint8   _nu_file_buffer[NUD_FILE_BUFFER_SIZE];		/* ファイルバッファ					*/
GDFS _nu_file_ptr_;									/* ファイルポインタ					*/

Sint8 *_nu_buffer_st_ptr_;							/* ファイルバッファの先頭ポインタ	*/
Sint32 _nu_buffer_sect_;							/* ファイルバッファの先頭が何セクタにいるかの位置	*/
Sint32 _nu_buffer_sect_size_= NUD_FILE_BUFFER_SIZE/NJD_GD_SECT_SIZE;
													/* バッファのセクタサイズ			*/
Sint32 _nu_buffer_size_ = CALC_BYTE_SECT(NUD_FILE_BUFFER_SIZE/NJD_GD_SECT_SIZE);
													/* バッファのバイトサイズ			*/
Sint32 _nu_buffer_r_sect_size_;						/* バッファにリードしたセクタサイズ	*/
Sint32 _nu_buffer_r_size_;							/* バッファの残っているバイトサイズ	*/
Sint32 _nu_file_max_size_ = 0;						/* ファイルのサイズ					*/
Sint32 _nu_sect_max_size_=0;						/* ファイルのセクタサイズ 			*/
Sint32 _nu_buffer_byte_ptr_ = 0;					/* バッファの何バイト目にいるか 	*/
Sint32 _nu_fread_request_flag_ = 0;					/* ファイルバッファにロードかユーザバッファにロードかのフラグ */
Sint32 _nu_fread_status_ = 0;						/* 現在のリードしたファイルの状態	*/
Sint32 _nu_file_sect_ptr_ = 0;						/* ファイルのセクタ位置 */
Sint32 _nu_file_byte_ptr_ = 0;						/* ファイルのバイト位置　ファイルの先頭からのバイト数 */
Sint32	_nu_fread_size_ = 0;							/* リードサイズ						*/
void *_nu_fread_buf_ = NULL;							/* リード先のポインタ				*/

Sint32 nuFReadRequestBuffer(Sint32 n);
void nuFReadStatus(void);

/****************************************************************/
/*  関数名：nuTexFopen											*/
/*  機  能：ファイルのオープン									*/
/*  入  力：Sint8 *filename	ファイル名							*/
/*  出  力：なし												*/
/*  戻り値：成功　１　失敗　−１								*/
/*  概　要：ファイルのオープン。クローズするまで1つのファイル	*/
/*			しかオープンできません。							*/
/*  備　考：													*/
/*                                                              */
/****************************************************************/
Sint32 nuTexFopen(const char* filename)
{

	_nu_buffer_st_ptr_ = (Sint8*)(((Uint32)_nu_file_buffer + 0x1f) & 0xffffffe0);

	/* ファイルのオープン */
	if((_nu_file_ptr_ = gdFsOpen(filename,NULL))==NULL){
		_nj_texerr_.texerr = NJD_TEXERR_FILEOPEN;
		return -1;
	}
	
	if(gdFsGetFileSize(_nu_file_ptr_,&_nu_file_max_size_) == FALSE){
		_nj_texerr_.texerr = NJD_TEXERR_FILEOPEN;
		return -1;
	}
	
	/* 最大何セクタあるか計算しておく */
	_nu_sect_max_size_ = gdFsCalcSctSize(_nu_file_max_size_);

	/* バッファの先頭位置 */
	_nu_buffer_byte_ptr_ = 0;
	/* ファイルの位置 */
	_nu_file_byte_ptr_ = 0;
	_nu_file_sect_ptr_ = 0;

	/* バッファ関連変数の初期化 */
	_nu_buffer_sect_ = -1;
	_nu_buffer_r_sect_size_ = 0;
	_nu_buffer_r_size_ = 0;

	
	if(nuFReadRequestBuffer(0) < 0){
		return -1;
	}
	_nu_fread_status_ = READ_INIT;

	return 0;
}

/****************************************************************/
/*  関数名：nuTexFclose											*/
/*  機  能：ファイルのクローズ									*/
/*  入  力：なし												*/
/*  出  力：なし												*/
/*  戻り値：なし												*/
/*  概　要：nuTexFopenでオープンしたファイルをクローズする		*/
/*  備　考：													*/
/*                                                              */
/****************************************************************/
void nuTexFclose(void)
{
	gdFsClose(_nu_file_ptr_);

}

/****************************************************************/
/*  関数名：nuFReadRequestBuffer								*/
/*  機  能：指定されたセクタをリードする						*/
/*  入  力：Sint32 n	セクタ									*/
/*  出  力：なし												*/
/*  戻り値：成功　１　失敗　−１								*/
/*  概　要：nuTexFopenでオープンしたファイルをクローズする		*/
/*  備　考：内部関数											*/
/*                                                              */
/****************************************************************/
Sint32 nuFReadRequestBuffer(Sint32 n)
{
	Sint8 *start_buffer_ptr;
	Sint32 read_sect;
	Sint32 n0,m,d;
	
	if((n >= _nu_sect_max_size_) || ( n < 0)){
		return -1;
	}


	d = n / _nu_buffer_sect_size_;
	n0 = d * _nu_buffer_sect_size_;

	if(n0 != _nu_buffer_sect_){
		_nu_buffer_sect_ = n0;
		
		if(_nu_sect_max_size_ > (n0 + _nu_buffer_sect_size_)){
			_nu_buffer_r_sect_size_ = _nu_buffer_sect_size_;
			_nu_buffer_r_size_ = CALC_SECT_BYTE(_nu_buffer_r_sect_size_);
		}
		else{
			_nu_buffer_r_sect_size_ = _nu_sect_max_size_ - n0;
			_nu_buffer_r_size_ = _nu_file_max_size_ - CALC_SECT_BYTE(n0);
		}

		gdFsSeek(_nu_file_ptr_,n0,GDD_SEEK_SET);
		_nu_file_sect_ptr_ = n;

		if(gdFsReqRd32(_nu_file_ptr_,_nu_buffer_r_sect_size_,_nu_buffer_st_ptr_) < 0){
			NUM_FREAD_ERR(_nu_file_ptr_,NJD_TEXERR_FILELOAD)
			return -1;
		}
		_nu_fread_status_ = READ_WAIT_DMA;
		_nu_fread_request_flag_ = FILE_BUFFER;

	}
	
	return 0;
}

/****************************************************************/
/*  関数名：nuFReadRequest										*/
/*  機  能：指定されたセクタからサイズ分リードする				*/
/*  入  力：Sint32 n	セクタ									*/
/*  出  力：なし												*/
/*  戻り値：成功　１　失敗　−１								*/
/*  概　要：nuTexFopenでオープンしたファイルをクローズする		*/
/*  備　考：内部関数											*/
/*                                                              */
/****************************************************************/
Sint32 nuFReadRequest(Sint32 n,Sint32 size)
{
	Sint32 read_sect;

	if((size >= NUD_FILE_BUFFER_SIZE) && (!((Uint32)_nu_fread_buf_ & 0x0000001F))){
	
		/* リードするサイズがNUD_FILE_BUFFER_SIZE以上の場合 */
		if((n >= _nu_sect_max_size_) || ( n < 0)){
			/* リード失敗 */
			return -1;
		}

		read_sect = CALC_BYTE_SECT(size);
		if(_nu_file_sect_ptr_ != n){
			gdFsSeek(_nu_file_ptr_,n,GDD_SEEK_SET);
		}
		_nu_file_sect_ptr_ = n;
		if(gdFsReqRd32(_nu_file_ptr_,read_sect,_nu_fread_buf_) < 0){
			NUM_FREAD_ERR(_nu_file_ptr_,NJD_TEXERR_FILELOAD)
			return -1;
		}
		_nu_fread_status_ = READ_WAIT_DMA;
		_nu_fread_request_flag_ = USER_BUFFER;
		
	}
	else{
		/* 小さい場合はファイルバッファへ転送する */
		return nuFReadRequestBuffer(n);
	}

	/* リード中 */
	return 0;

}

/****************************************************************/
/*  関数名：nuFReadStatus										*/
/*  機  能：リードステータスのチェック							*/
/*  入  力：なし												*/
/*  出  力：なし												*/
/*  戻り値：なし												*/
/*  概　要：現在の状態を_nu_fread_status_に返す					*/
/*			READ_INIT:ファイルをまったくリードしていない初期状態*/
/*			READ_WAIT:ファイルがリード中						*/
/*			READ_END :ファイルリード終了						*/
/*			READ_ERR :ファイルリードエラー						*/
/*  備　考：内部関数											*/
/*                                                              */
/****************************************************************/
void nuFReadStatus(void)
{
	Sint32 read_size;
	Sint32 ret;

	if(_nu_fread_status_ == READ_WAIT_DMA){
		ret = gdFsGetStat(_nu_file_ptr_);

		switch(ret){
		case GDD_STAT_COMPLETE:
			if(_nu_fread_request_flag_ == USER_BUFFER){
				/* ユーザーバッファに直接転送した場合 */
				read_size = gdFsGetNumRd(_nu_file_ptr_);
				_nu_fread_size_ -= read_size;
				_nu_file_byte_ptr_ += read_size;
				_nu_fread_buf_ = (void*)((Uint32)_nu_fread_buf_ + read_size);
				if(_nu_fread_size_ <= 0){
					_nu_fread_status_ = READ_END;
				}
				else{
					_nu_fread_status_ = READ_WAIT;
				}
			}
			else{
				/* ファイルバッファに転送した場合 */
				_nu_fread_status_ = READ_WAIT;
				_nu_buffer_byte_ptr_ = 0;
			}
			break;
		case GDD_STAT_IDLE:
		case GDD_STAT_READ:
		case GDD_STAT_SEEK:
		case GDD_STAT_BUSY:
			break;
		case GDD_STAT_ERR:
			_nu_fread_status_ = READ_ERR;
		break;
		}
		
	}
	else{
		if(_nu_fread_status_ == READ_INIT){
			ret = gdFsGetStat(_nu_file_ptr_);

			switch(ret){
			case GDD_STAT_COMPLETE:
					/* ファイルバッファに転送した場合 */
					_nu_fread_status_ = READ_END;
					_nu_buffer_byte_ptr_ = 0;
				break;
			case GDD_STAT_IDLE:
			case GDD_STAT_READ:
			case GDD_STAT_SEEK:
			case GDD_STAT_BUSY:
				break;
			case GDD_STAT_ERR:
				_nu_fread_status_ = READ_ERR;
			break;
			}
		}
	}
}


/****************************************************************/
/*  関数名：nuFReadCheckBuffer									*/
/*  機  能：セクタのチェック									*/
/*  入  力：Sint32 sect											*/
/*  出  力：なし												*/
/*  戻り値：なし												*/
/*  概　要：sectがファイルバッファ内にあるかチェックする		*/
/*  備　考：内部関数											*/
/*                                                              */
/****************************************************************/
Sint32 nuFReadCheckBuffer(Sint32 sect)
{
	Sint32 n0,n1;
	
	n0 = _nu_buffer_sect_;
	n1 = n0 + (_nu_buffer_r_sect_size_ - 1);
	if(n0 < 0){
		return -1;
	}
	if((n0 <= sect ) && (sect <= n1)){
		return 1;
	}
	else{
		return -1;
	}

}

/****************************************************************/
/*  関数名：nuTexFreadReq										*/
/*  機  能：即時復帰のバイトリード								*/
/*  入  力：Sint32 size	リードサイズ							*/
/*  出  力：void *buf	リードの結果							*/
/*  戻り値：リード終了　１	リード中　０	リードエラー　−１	*/
/*  概　要：nuTexFopenでオープンしたファイルをリードする		*/
/*  備　考：リードが終了するまで何回も呼ぶ						*/
/*                                                              */
/****************************************************************/
Sint32 nuTexFreadReq(void* buf,Sint32 size)
{
	Sint32 bsize;

	/* サイズが0の場合 */
	if(size == 0){
		return 1;
	}

	/* ファイルの状態を取得 */
	nuFReadStatus();
	
	switch(_nu_fread_status_){
	case READ_WAIT_DMA:
		return 0;
		break;
	case READ_END:
		_nu_fread_size_ = size;
		_nu_fread_buf_ = buf;
		_nu_fread_status_ = READ_WAIT;
	case READ_WAIT:
		while(_nu_fread_status_ != READ_END){
			/* ファイルのサイズより大きいサイズを指定した場合 */
			if(_nu_file_max_size_ < (_nu_file_byte_ptr_+_nu_fread_size_)){
				/* ファイルサイズ分はリードする */
				_nu_fread_size_ = _nu_file_max_size_ - _nu_file_byte_ptr_;
			}
		
			/* ファイルポインタが何セクタ目にあるか計算する */
			_nu_file_sect_ptr_ = CALC_BYTE_SECT(_nu_file_byte_ptr_);
			/* ファイルポインタがバッファの何バイト目にあるか計算する */
			_nu_buffer_byte_ptr_ = CALC_BUFFER_POS(_nu_file_byte_ptr_);

			if(_nu_file_sect_ptr_ > _nu_sect_max_size_){
				NUM_FREAD_ERR(_nu_file_ptr_,NJD_TEXERR_FILELOAD)
				return -1;
			}
			
			/* バッファにあるか調べる */
			if(nuFReadCheckBuffer(_nu_file_sect_ptr_) > 0){
				/* メインバッファにすべてある場合 */
				if(_nu_fread_size_ <= (_nu_buffer_r_size_ - _nu_buffer_byte_ptr_)){
					syMemCopy(_nu_fread_buf_,(void*)((Uint32)_nu_buffer_st_ptr_+_nu_buffer_byte_ptr_),_nu_fread_size_);
					_nu_file_byte_ptr_ += _nu_fread_size_;
					_nu_fread_size_ = 0;
					_nu_fread_buf_ = NULL;
					_nu_fread_status_ = READ_END;
					/* ファイルポインタが何セクタ目にあるか計算する */
					_nu_file_sect_ptr_ = CALC_BYTE_SECT(_nu_file_byte_ptr_);
					/* ファイルポインタがバッファの何バイト目にあるか計算する */
					_nu_buffer_byte_ptr_ = CALC_BUFFER_POS(_nu_file_byte_ptr_);
					/* 全部ロードしてしまったとき */
					if(_nu_buffer_byte_ptr_ == 0){
						/* 次のセクタをロードする*/
						if(_nu_file_sect_ptr_ < _nu_sect_max_size_){
							nuFReadRequestBuffer(_nu_file_sect_ptr_);
							_nu_fread_status_ = READ_INIT;
						}
					}
					return 1;
				}
				else{
					/* バッファにすべて存在しない場合 */
					/* バッファにあるだけコピーする */
					bsize = (_nu_buffer_r_size_ - _nu_buffer_byte_ptr_);
					syMemCopy(_nu_fread_buf_,(void*)((Uint32)_nu_buffer_st_ptr_+_nu_buffer_byte_ptr_),bsize);
					_nu_file_byte_ptr_ += bsize;
					_nu_fread_size_ -= bsize;
					_nu_fread_buf_ = (void*)((Uint32)_nu_fread_buf_ + bsize);
				}
			}
			else{
				/* どこにもない場合　ロードする */
				return nuFReadRequest(_nu_file_sect_ptr_,_nu_fread_size_);
			}
		}
		break;
		case READ_INIT:
			return 0;
		break;
		case READ_ERR:
			NUM_FREAD_ERR(_nu_file_ptr_,NJD_TEXERR_FILELOAD)
			return -1;
		break;
	}
}

/****************************************************************/
/*  関数名：nuTexFread											*/
/*  機  能：完了復帰のバイトリード								*/
/*  入  力：Sint32 size	リードサイズ							*/
/*  出  力：void *buf	リードの結果							*/
/*  戻り値：１：リード終了	−１：リードエラー					*/
/*  概　要：nuTexFopenでオープンしたファイルをリードする		*/
/*  備　考：即時復帰関数を終了するまで待つ						*/
/*                                                              */
/****************************************************************/
Sint32 nuTexFread(void* buf,Sint32 size)
{
	Sint32 ret = 0;
	
	while(ret == 0){
		ret = nuTexFreadReq(buf,size);
		if(ret < 0) return -1;
	}

	return 1;
}

/****************************************************************/
/*  関数名：nuTexFseek											*/
/*  機  能：ファイルのシーク									*/
/*  入  力：Sint32 size	リードサイズ							*/
/*			Int	type	SEEK_CUR、SEEK_SET、SEEK_END			*/
/*  出  力：なし												*/
/*  戻り値：なし												*/
/*  概　要：													*/
/*  備　考：													*/
/*                                                              */
/****************************************************************/
void nuTexFseek(Sint32 size,Int type)
{
	switch(type){
	case SEEK_CUR:
		_nu_file_byte_ptr_ += size;
		break;
	case SEEK_SET:
		_nu_file_byte_ptr_ = size;
		break;
	case SEEK_END:
		_nu_file_byte_ptr_ = (_nu_file_max_size_ - size);
		break;
	}
}

/****************************************************************/
/*  関数名：nuTexFreadAll										*/
/*  機  能：ファイルを全部リードする							*/
/*  入  力：const char *filename	リードファイル名			*/
/*			void *buf				リード先					*/
/*			Int	size				リード先のバッファサイズ	*/
/*  出  力：なし												*/
/*  戻り値：成功　１　失敗　−１								*/
/*  概　要：													*/
/*  備　考：													*/
/*                                                              */
/****************************************************************/
Sint32 nuTexFreadAll(const char *filename,void *buf,Int size)
{
	GDFS	fp;
	Sint32	file_sect_size;
	Sint32	buff_sect_size;

	njInitTextureErrStat();

	if((fp = gdFsOpen(filename,NULL)) == NULL){
		/* ファイルオープンエラー　*/
		_nj_texerr_.texerr = NJD_TEXERR_FILEOPEN;
		return -1;
	}
	
	gdFsGetFileSctSize(fp,&file_sect_size);
	buff_sect_size = CALC_BYTE_SECT(size); 
	if(buff_sect_size < file_sect_size){
		_nj_texerr_.texerr = NJD_TEXERR_MAINMEMORY;
		gdFsClose(fp);
		return -1;
	}
	if(gdFsRead(fp,file_sect_size,buf) != GDD_ERR_OK){
		_nj_texerr_.texerr = NJD_TEXERR_FILELOAD;
		gdFsClose(fp);
		return -1;
	}
	gdFsClose(fp);

	return 1;
}
