/*
    Ninja Sample

    COPYRIGHT (C) SEGA ENTERSRISES,LTD.

    TexLow Sample for SET5

    1999.3.4
*/
#include <Shinobi.h>
#include <string.h>

#define LOAD_END			0
#define READ_START			1
#define FILE_READ			2
#define FILE_READ_WAIT		3
#define GET_SURFACE			4
#define LOAD_TEX			5
#define LOAD_DMA_WAIT		6
#define DMA_WAIT_ERR_COUNT	10

static GDFS tex_loadreq_fp;
static Int load_mode = LOAD_END;
static NJS_TEXLIST *load_list = NULL;
static Uint32 load_n = 0;
static Uint32 globalIndex_S = NJD_START_GLOBALINEX;


void EntryTexMemList(NJS_TEXMEMLIST *texmemlist);
void FreeTexMemList(NJS_TEXMEMLIST *texmemlist);
void CheckFileName(char* namedst,const char* namesrc);
void InitTextureErrStat(void);

/************************************************
	LoadTexture
	テクスチャのロードを要求する関数
	※実際にロードするのはLoadTextureServer関数
	　ロードが終了するまで次のロードは要求できない
	　LoadMode関数で確認すること
	※キャッシュテクスチャには対応していません
	
	引数
		NJS_TEXLIST *texlist　	テクスチャリスト
		Sint32 n				テクスチャ番号
	
	戻り値
		ロード要求成功　１
		ロード要求失敗　−１
*************************************************/
/************************************************
	LoadTexture
	A function to demand a road of a texture
	※Actually, it is LoadTextureServer function that load
      The next road cannot demand it till a road is finished
	　Confirm it by LoadMode function
	※Is not equivalent to cache texture
	
	Argument
		NJS_TEXLIST *texlist    texture list
		Sint32 n				texture number
	
	Return value
		Load demand success 1
		Load demand failure -1
*************************************************/
Sint32 LoadTexture(NJS_TEXLIST *texlist,Uint32 n)
{
	if(load_mode == LOAD_END){
		/* エラーの初期化　*/
		/* Initialization of error */
		InitTextureErrStat();
		load_mode = READ_START;
		load_list = texlist;
		load_n = n;
		return 1;
	}
	else{
		/* ロード中で登録できない　*/
		/* Load a texture, and cannot register itself.*/
		return -1;
	}
	
}

/************************************************
	LoadTextureServer
	LoadTexture関数でロード要求された関数をロード
	※キャッシュテクスチャには対応していません
	
	引数
		なし
	戻り値
		正常	1
		エラー	-1
*************************************************/
/************************************************
	LoadTextureServer
	Load a function demanded that it was loaded by LoadTexture function.
	※Is not equivalent to cache texture
	
	Argument
		
	Return value
		Normality	1
		error		-1
*************************************************/
Sint32 LoadTextureServer(void)
{
	Sint8 *filename;
	static Sint32 file_sect_size;
	static Sint32 buffer_sect_size;
	static Sint32 r_sect_size;
	static Sint32 offset=0;
	static Sint32 dma_count;
	static Sint32 read_size;
	static Uint32 *buffer_start;
	Uint32 *pTexture;
	Sint32 ret;
	static Uint32 globalIndex;
	static NJS_TEXMEMLIST *texmemlist;
	NJS_PVRHEADERDATA pvrh;
	Int i,no;
	Sint8 filename2[128];/* 128文字まで*//* To 128 characters */
	NJS_TEXINFO *info;
	
	/* ロードエンドの時はすぐにリターンする */
	/* Make a return at the time of LOAD_END immediately. */
	if(load_mode == LOAD_END) return;

	if(load_mode == READ_START){
				/* テクスチャがメモリからの場合　*/
				/* Load of a texture is a case from memory. */
		if(load_list->textures[load_n].attr & NJD_TEXATTR_TYPE_MEMORY){
			buffer_start = ((NJS_TEXINFO*)load_list->textures[load_n].filename)->texaddr;
			/* ヘッダデータを取得する pTextureにはデータの先頭アドレスが返る　*/
			/* Acquire header data. The top address of data returns in pTexture.*/
			if((pTexture = njLoadPvrHeader(&pvrh,&globalIndex,buffer_start)) != NULL){
				buffer_start = pTexture;
			}
			else{
				/* ヘッダデータがない場合　*/
				/* When there are not header data */
				info = (NJS_TEXINFO*)load_list->textures[load_n].filename;
				pvrh.nWidth = info->texsurface.nWidth;
				pvrh.nHeight = info->texsurface.nHeight;
				pvrh.nTextureType = (info->texsurface.Type >> 16);
				globalIndex = NJD_GLOBAXINDEX_FIRST_VAL;
			}

			/* グローバルインデックスがユーザ指定の場合　*/
			/* Global index is a case user setting. */
			if(load_list->textures[load_n].attr & NJD_TEXATTR_GLOBALINDEX){
					globalIndex = load_list->textures[load_n].texaddr;
			}
			else{
				/* ファイル内にグローバルインデックスがない場合　*/
				/* When there is not a global index in a file. */
				if(globalIndex == NJD_GLOBAXINDEX_FIRST_VAL){
					/* グローバルインデックスを付ける　*/
					/* Establish a global index. */
					globalIndex = (globalIndex_S--);
				}
			}
			
			no = -1;
			/* テクスチャメモリリストの空いている場所を探す　*/
			/* Look for a place becoming vacant of texture memory list. */
		    for(i = 0;i < _nj_texmemsize;i++){
		        if(_nj_texmemlist[i].globalIndex == globalIndex){
			        	/* すでにロードしてある　*/
			        	/* Is loaded already. */
						load_mode = LOAD_END;
						_nj_texmemlist[i].count++;
						return 1;
		        }
				else{
					if(no < 0){
						if(_nj_texmemlist[i].globalIndex == NJD_GLOBAXINDEX_FIRST_VAL){
					        	no = i;
				        }
				    }
			    }
		    }
	    	/* テクスチャメモリリストは取得できなかった*/
	    	/* Texture memory list was not able to acquire it. */
		    if(no < 0){
				load_mode = LOAD_END;
				texmemlist = NULL;
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_OTHER;
				return -1;
		    }
			texmemlist = &_nj_texmemlist[no];
			texmemlist->globalIndex = globalIndex;
			/* テクスチャサーフェスを作成する　*/
			/* Make texture SurfaceDesc. */
			if(njGetTexSurface(texmemlist,&pvrh) == -1){
				/* サーフェス取得に失敗の時　*/
				/* In failing in the SurfaceDesc acquisition. */
				load_mode = LOAD_END;
				FreeTexMemList(texmemlist);
				texmemlist = NULL;
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_SURFACE;
				return -1;
			}
			njSetTextureParam(texmemlist);
			load_mode = LOAD_TEX;
			read_size = texmemlist->texinfo.texsurface.TextureSize;
			offset = 0;
			r_sect_size = 0;
		}
		else{
			/* ロードファイル名の取得　*/
			/* The acquisition of load file name. */
			filename = load_list->textures[load_n].filename;
			/* .PVRが付いているか確認　*/
			/* Confirm whether ".PVR" is on. */
			CheckFileName(filename2,filename);
			if((tex_loadreq_fp = gdFsOpen(filename2,NULL)) != NULL){
				/* ファイルのセクタサイズの計算 */
				/* A calculation of sector size of a file. */
				gdFsGetFileSctSize(tex_loadreq_fp,&file_sect_size);
				/* ファイルバッファのセクタサイズの計算 */
				/* A calculation of sector size of file buffer. */
				buffer_sect_size = _nj_texture_buffer_max_size/NJD_GD_SECT_SIZE;
				/* 残りのセクタサイズ */
				/* Remaining sector size. */
				r_sect_size = file_sect_size;
				offset = 0;
				/* 次のモード設定 */
				/* The next mode setting. */
				load_mode = FILE_READ;
			}
			else{
				/* ファイルがオープンができない場合 */
				/* When it is not possible for a file to open it.*/
				load_mode = LOAD_END;
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_FILEOPEN;
				return -1;
			}
		}
	}
	
	if(load_mode == FILE_READ){
		if(buffer_sect_size >= r_sect_size){
			/* バッファの方が大きい場合　*/
			/* When a sector of a buffer is bigger than a sector loading it. */
			if((ret = gdFsReqRd32(tex_loadreq_fp,r_sect_size,_nj_texture_buffer_st)) < 0){
				/* エラー　*/
				/* error */
				load_mode = LOAD_END;
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_FILELOAD;
				return -1;
			}
			read_size = r_sect_size*NJD_GD_SECT_SIZE;
			r_sect_size = 0;
		}
		else{
			/* バッファの方が小さい場合　*/
			/* When a sector of a buffer is smaller than a sector loading it.*/
			if((ret = gdFsReqRd32(tex_loadreq_fp,buffer_sect_size,_nj_texture_buffer_st)) < 0){
				/* エラー　*/
				/* error */
				load_mode = LOAD_END;
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_FILELOAD;
				return -1;
			}
			r_sect_size -= buffer_sect_size;
			read_size = buffer_sect_size*NJD_GD_SECT_SIZE;
		}
		load_mode = FILE_READ_WAIT;
		buffer_start = _nj_texture_buffer_st;
	}

	if(load_mode == FILE_READ_WAIT){
		ret = gdFsGetStat(tex_loadreq_fp);
		switch(ret){
		case GDD_STAT_COMPLETE:
			if(offset == 0){
				load_mode = GET_SURFACE;
			}
			else{
				load_mode = LOAD_TEX;
			}
			break;
		case GDD_STAT_IDLE:
		case GDD_STAT_READ:
		case GDD_STAT_SEEK:
		case GDD_STAT_BUSY:
			load_mode = FILE_READ_WAIT;
			return;
			break;
		case GDD_STAT_ERR:
			/* エラーの場合 */
			/* error */
			load_mode = LOAD_END;
			gdFsClose(tex_loadreq_fp);
			/*********** エラーコードを返す ************/
			/********* Turn over error code ************/
			_nj_texerr_.n = load_n;
			_nj_texerr_.texerr = NJD_TEXERR_FILELOAD;
			return -1;
			break;
		}
	}

	if(load_mode == GET_SURFACE){
		/* ヘッダデータを取得する pTextureにはデータの先頭アドレスが返る　*/
		/* Acquire header data. The top address of data returns in pTexture. */
		if((pTexture = njLoadPvrHeader(&pvrh,&globalIndex,buffer_start)) != NULL){
			read_size -= (Sint32)((Uint32)pTexture - (Uint32)buffer_start);
			/* グローバルインデックスがユーザ指定の場合　*/
			/*  Global index is a case user setting. */
			if(load_list->textures[load_n].attr & NJD_TEXATTR_GLOBALINDEX){
					globalIndex = load_list->textures[load_n].texaddr;
			}
			else{
				/* ファイル内にグローバルインデックスがない場合　*/
				/* When there is not a global index in a file. */
				if(globalIndex == NJD_GLOBAXINDEX_FIRST_VAL){
					/* グローバルインデックスを付ける　*/
					/* Establish a global index. */
					globalIndex = (globalIndex_S--);
				}
			}
			no = -1;
			/* テクスチャメモリリストの空いている場所を探す　*/
			/* Look for a place becoming vacant of texture memory list. */
		    for(i = 0;i < _nj_texmemsize;i++){
		        if(_nj_texmemlist[i].globalIndex == globalIndex){
			        	/* すでにロードしてある　*/
			        	/* Is loaded already. */
						gdFsClose(tex_loadreq_fp);
						load_mode = LOAD_END;
						_nj_texmemlist[i].count++;
						return 1;
		        }
		        else{
					if(no < 0){
						if(_nj_texmemlist[i].globalIndex == NJD_GLOBAXINDEX_FIRST_VAL){
					        	no = i;
				        }
				    }
			    }
		    }
	    	/* テクスチャメモリリストは取得できなかった */
	    	/* Texture memory list was not able to acquire it. */
		    if(no < 0){
				load_mode = LOAD_END;
				texmemlist = NULL;
				gdFsClose(tex_loadreq_fp);
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_OTHER;
				return -1;
		    }
			texmemlist = &_nj_texmemlist[no];
			texmemlist->globalIndex = globalIndex;
			/* テクスチャサーフェスを作成する　*/
			/* Make texture SurfaceDesc. */
			if(njGetTexSurface(texmemlist,&pvrh) < 0){
				/* サーフェス取得に失敗の時　*/
				/* In failing in the SurfaceDesc acquisition. */
				load_mode = LOAD_END;
				FreeTexMemList(texmemlist);
				texmemlist = NULL;
				gdFsClose(tex_loadreq_fp);
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_SURFACE;
				return -1;
			}
			njSetTextureParam(texmemlist);
			buffer_start = pTexture;
			load_mode = LOAD_TEX;
			offset = 0;
		}
	}

	if(load_mode == LOAD_TEX){
		if(njLoadTexturePartLow(texmemlist,buffer_start,offset,read_size) == -1){
				/* テクスチャロードエラー　*/
				/* Texture load error. */
				load_mode = LOAD_END;
				/* 取得していたサーフェスのリリース　*/
				/* Release of the SurfaceDesc which acquired it. */
				njReleaseTextureLow(texmemlist);
				/* 取得していたメモリリストをリリースする　*/
				/* Release of the texture memory list which acquired it.*/
				FreeTexMemList(texmemlist);
				texmemlist = NULL;
				/* ファイルのクローズ　*/
				/* Closing of a file. */
				gdFsClose(tex_loadreq_fp);
				/*********** エラーコードを返す ************/
				/********* Turn over error code ************/
				_nj_texerr_.n = load_n;
				_nj_texerr_.texerr = NJD_TEXERR_TEXMEMLOAD;
				return -1;
		}
		offset += read_size;
		load_mode = LOAD_DMA_WAIT;
		dma_count = 0;
	}


	if(load_mode == LOAD_DMA_WAIT){
		/* DMAが終了したかの確認　*/
		/* The confirmation that DMA finished. */
		if(njLoadTextureFinishDMA() == 1){
			if(r_sect_size <= 0){
				/* すべてのロードが終了　*/
				/* All texture load is finished. */
				gdFsClose(tex_loadreq_fp);
				load_mode = LOAD_END;
				/* テクスチャメモリリストに追加情報 */
				/* Is addition information in texture memory list. */
				EntryTexMemList(texmemlist);
				/* テクスチャリストとテクスチャメモリリストのリンクをはる */
				/* Set up a link of texture list and texture memory list. */ 
				load_list->textures[load_n].texaddr = (Uint32)texmemlist;
				/* ロードしたテクスチャの数をカウントする
				　この数分NINDOWSのテクスチャビューアで見ることができる
				*/
				/* Count number of the texture which loaded it.
				   Can watch it with texture viewer of NINDOWS for these several minutes.
				*/
				_nj_tex_count++;
			}
			else{
				/* 次のファイルのリード　*/
				/* A lead of the next file. */
				load_mode = FILE_READ;
			}
			dma_count = 0;
		}
		else{
			/* */
			dma_count++;
		}
		if(dma_count > DMA_WAIT_ERR_COUNT){
			/* DMA_WAIT_ERR_COUNT(10)回数来てもロードが完了しない場合はエラーとする*/
			/* When load is not completed even if comes, do DMA_WAIT_ERR_COUNT(10) frequency with an error. */
			load_mode = LOAD_END;
			/* サーフェスをリリースする　*/
			/* Release a SurfaceDesc. */
			njReleaseTextureLow(texmemlist);
			FreeTexMemList(texmemlist);
			texmemlist = NULL;
			gdFsClose(tex_loadreq_fp);
			/*********** エラーコードを返す ************/
			/********* Turn over error code ************/
			_nj_texerr_.n = load_n;
			_nj_texerr_.texerr = NJD_TEXERR_OTHER;
			return -1;
		}
	}


	return 1;
}


/************************************************
	LoadMode
	LoadTextureServer関数の現在のモードを取得する
	
	引数
		なし
	戻り値
		モード

		LOAD_END			0	テクスチャロード終了
		READ_START			1	ファイルリードスタート
		FILE_READ			2	ファイルリード要求中
		FILE_READ_WAIT		3	ファイルリードDMA待ち
		GET_SURFACE			4	サーフェス取得中
		LOAD_TEX			5	テクスチャロード要求中
		LOAD_DMA_WAIT		6	テクスチャロードDMA待ち

*************************************************/
/************************************************
	LoadMode
	Acquire a current mode of LoadTextureServer function.
	
	Argument
		
	Return value
		Mode

		LOAD_END			0	Texture load end
		READ_START			1	File read start
		FILE_READ			2	During file read demand
		FILE_READ_WAIT		3	Wait for file read DMA
		GET_SURFACE			4	During the SurfaceDesc acquisition
		LOAD_TEX			5	During texture load demand
		LOAD_DMA_WAIT		6	Wait for texture load DMA

*************************************************/
Sint32 LoadMode(void)
{
	return load_mode;
}

/************************************************
	EntryTexMemList
	テクスチャリストにロードしたフラグを立てる
	
	引数
		NJS_TEXMEMLIST *texmemlist
	戻り値
		なし
*************************************************/
/************************************************
	EntryTexMemList
	Make the flag which loaded it in texture list.
	
	Argument
		NJS_TEXMEMLIST *texmemlist
	Return value
		
*************************************************/
void EntryTexMemList(NJS_TEXMEMLIST *texmemlist)
{
	texmemlist->count = 1;
	texmemlist->texaddr = NJD_TEXMEMFLAG_TEXMEM;
	texmemlist->dummy = 0;
}


/************************************************
	FreeTexMemList
	テクスチャメモリリストをフリーする
	
	引数
		NJS_TEXMEMLIST *texmemlist
	戻り値
		なし
*************************************************/
/************************************************
	FreeTexMemList
	Release texture memory list.
	
	Argument
		NJS_TEXMEMLIST *texmemlist
	Return value
		
*************************************************/
void FreeTexMemList(NJS_TEXMEMLIST *texmemlist)
{
	texmemlist->globalIndex 					= 	0xFFFFFFFF;
	texmemlist->bank 							= 	0xFFFFFFFF;
    texmemlist->tspparambuffer					= 	0;
    texmemlist->texparambuffer					= 	0;
    texmemlist->texinfo.texaddr 				= 	0;
	texmemlist->texinfo.texsurface.Type 		= 	0;
	texmemlist->texinfo.texsurface.BitDepth 	= 	0;
	texmemlist->texinfo.texsurface.PixelFormat 	= 	0;
	texmemlist->texinfo.texsurface.nWidth 		= 	0;
	texmemlist->texinfo.texsurface.nHeight 		= 	0;
	texmemlist->texinfo.texsurface.TextureSize 	= 	0;
	texmemlist->texinfo.texsurface.fSurfaceFlags = 	0;
	texmemlist->texinfo.texsurface.pSurface 	= 	0;
	texmemlist->texaddr 						= 	0;
    texmemlist->count							=	0;
}

/************************************************
	CheckFileName
	PVRファイル名をチェックする。
	PVR拡張子がファイル名にない場合はPVRをつける
	
	引数
		char* namedst		チェック後のファイル名
		const char* namesrc　入力ファイル名
	戻り値
		なし
*************************************************/
/************************************************
	CheckFileName
	Check PVR file name.
	When there is not it, PVR expansion child adds PVR to a file name
	
	Argument
		char* namedst		A file name after a check
		const char* namesrc The input file name
	Return value
		
*************************************************/
void CheckFileName(char* namedst,const char* namesrc)
{
	Sint32 strl;
	char ext[5];
	Sint32 i;
	Sint32 ret;

	strl = strlen(namesrc);
	/* 拡張子は後ろ3文字に決めうち　*/
	/* Back three characters consider it to be expansion. */
	strcpy(ext,&namesrc[strl-4]);	/* 拡張子のコピー *//* A copy of expansion */
	strupr(ext);					/* 大文字に変換する *//* Convert it into large characters */

	if(ext[0] == '.'){/* 拡張子がある *//* There is expansion. */
		if(ret = strcmp(ext,".PVR") != 0){
			/* PVR以外の時　*/
			/* When is not PVR. */
			strcpy(namedst,namesrc);
			strcat(namedst,".PVR");
			return ;
		}
		else{
			strcat(namedst,namesrc);
			return ;
		}
	}
	else{/* 拡張子がないときはPVRをつける */
		 /* When there is not expansion, add PVR.*/
			strcpy(namedst,namesrc);
			strcat(namedst,".PVR");
			return ;
	}
}

/************************************************
	ReleaseTexture
	テクスチャのリリース
	
	引数
		NJS_TEXLIST *texlist	テクスチャリスト
		Sint32 n				テクスチャ番号
	戻り値
		なし
*************************************************/
/************************************************
	ReleaseTexture
	Release of a texture.
	
	Argument
		NJS_TEXLIST *texlist	texture list
		Sint32 n				texture number
	Return value
		
*************************************************/
void ReleaseTexture(NJS_TEXLIST *texlist,Uint32 n)
{
	NJS_TEXMEMLIST *texmemlist;
	
	texmemlist = (NJS_TEXMEMLIST*)texlist->textures[n].texaddr;
	if(texmemlist->texaddr & NJD_TEXMEMFLAG_TEXMEM){
		/* テクスチャをリリースする　*/
		/* Release a texture. */
		njReleaseTextureLow(texmemlist);
		_nj_tex_count--;
		/* 取得していたメモリリストをリリースする　*/
		/* Release the memory list which acquired it. */
		FreeTexMemList(texmemlist);
	}
}

/************************************************
	LoadTextureStop
	テクスチャロード中の中止
	
	引数
		なし
	戻り値
		成功　１
		失敗　−１
*************************************************/
/************************************************
	LoadTextureStop
	Stop load texture.
	
	Argument
		
	Return value
	Success  1
	Failure -1
*************************************************/
Sint32 LoadTextureStop(void)
{
	Sint32 ret;
	
	InitTextureErrStat();

	if(load_mode == FILE_READ_WAIT){
		ret = gdFsStopRd(tex_loadreq_fp);
		gdFsClose(tex_loadreq_fp);
		if(ret != GDD_ERR_OK){
			_nj_texerr_.n = load_n;
			_nj_texerr_.gderr = ret;
			_nj_texerr_.texerr = NJD_TEXERR_OTHER;
			load_mode = LOAD_END;
			return -1;
		}
	}

	load_mode = LOAD_END;

	return 1;
	
}

/************************************************
	InitTextureErrStat
	テクスチャエラーの初期化
	
	引数
		なし
	戻り値
		なし
*************************************************/
/************************************************
	InitTextureErrStat
	Initialization of texture error.
	Argument
	
	Return value

*************************************************/
void InitTextureErrStat(void)
{
	_nj_texerr_.n = NJD_GLOBAXINDEX_FIRST_VAL;
	_nj_texerr_.globalIndex = NJD_GLOBAXINDEX_FIRST_VAL;
	_nj_texerr_.texerr = 0;
	_nj_texerr_.gdstat = 0;
	_nj_texerr_.gderr = 0;

}
