/******************************************************************************
Ootake
・Direct3Dでの描画にも対応した。Vista対応。
・Window表示にもDirectDrawを使うようにした。
・早回し機能を付けた。

Copyright(C)2006-2025 Kitao Nakamura.
	改造版・後継版を公開なさるときは必ずソースコードを添付してください。
	その際に事後でかまいませんので、ひとことお知らせいただけると幸いです。
	商的な利用は禁じます。
	あとは「GNU General Public License(一般公衆利用許諾契約書)」に準じます。

*******************************************************************************
	[Screen.c]

	Implement ScreenInterface.

	Copyright (C) 2004 Ki

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
******************************************************************************/
#include <stdio.h>
#include <math.h> //pow()関数で必要
#include "Screen.h"
#include "ScreenDD.h"
#include "ScreenD3D.h"
//#include "GDIScreen.h"
#include "TIMER.h"
#include "VDC.h"
#include "CDROM.h"
#include "APU.h"
#include "App.h"
#include "MainBoard.h"
#include "WinMain.h"
#include "Printf.h"

//Kitao追加。表示用ガンマテーブル
Uint32 _GammaTBL[0xFFFFFF+1];    //24bitの色コード(PCEの9bitを24(32)bit化)の色情報テーブル。BGR。v2.99
Uint32 _GammaTBLs80[0xFFFFFF+1]; //スキャンライン80%用
Uint32 _GammaTBLs90[0xFFFFFF+1]; //スキャンライン90%用
Uint32 _MonoTBL[0xFFFFFF+1];    //モノクロ変換用テーブル。BGR。グリーンディスプレイモードの時はグリーンモノクロになる。v2.99
Uint32 _MonoTBLs80[0xFFFFFF+1]; //スキャンライン80%用
Uint32 _MonoTBLs90[0xFFFFFF+1]; //スキャンライン90%用

static Sint32	_Width;
static Sint32	_Height;
static Sint32	_Magnification;	//※ここ(Screen.cpp)での_Magnificationはスクリーンショット用表示処理のときは、それと一致しないことがあるので注意。v2.28記
static Uint32	_Flags;

//Kitao追加。v2.70
static Uint32	_SBSwapBuf[40*16]; //40(横ドット)*16(縦ドット)*sizeof(Uint32)
static Uint32	_SBWorldStadium91[]   = {0x00030303,0x00070707,0x00000000,0x00000000,0x00070700,0x00070700};
static Uint32	_SBWorldStadium[]     = {0x00000006,0x00000002,0x00000002,0x00000002,0x00070700,0x00070700};
static Uint32	_SBWorldStadium2[]    = {0x00000006,0x00000002,0x00000002,0x00000500,0x00000500,0x00000500};
static Uint32	_SBPowerLeague34593[] = {0x00070701,0x00070701,0x00000000,0x00000000,0x00000000,0x00070701};
static Uint32	_SBPowerLeague2[]     = {0x00070701,0x00070701,0x00000000,0x00000000,0x00000000,0x00070701};
static Uint32	_SBPowerLeague[]      = {0x00010101,0x00060605,0x00060605,0x00060605,0x00060605,0x00010101};
static Uint32	_SBEikanKimi1[]       = {0x00070707,0x00000301,0x00000301,0x00000301,0x00070707};
static Uint32	_SBEikanKimi2[]       = {0x00060606,0x00000200,0x00000200,0x00000200,0x00060606};
static Uint32	_SBEikanKimi3[]       = {0x00050505,0x00000100,0x00000100,0x00000100,0x00050505};
static Uint32	_SBEikanKimi4[]       = {0x00040404,0x00000000,0x00000000,0x00000000,0x00040404};
static Uint32	_SBEikanKimi5[]       = {0x00030303,0x00000000,0x00000000,0x00000000,0x00030303};
static Uint32	_SBEikanKimi6[]       = {0x00020202,0x00000000,0x00000000,0x00000000,0x00020202};
static Uint32	_SBEikanKimi7[]       = {0x00010101,0x00000000,0x00000000,0x00000000,0x00010101};
static Uint32	_SBEikanKimi8[]       = {0x00000000,0x00010101,0x00010101,0x00000000,0x00000000};
static Uint32	_SBNaxatStadium[]     = {0x00050507,0x00050507,0x00050507,0x00050507,0x00050507,0x00000000};
static Uint32	_SBRomRomStadium[]    = {0x00000000,0x00000000,0x00070707,0x00070707,0x00070707,0x00000000};
static Uint32	_SBBaseballer1[]      = {0x00070707,0x00040507,0x00000202,0x00000202,0x00000202,0x00070707};
static Uint32	_SBBaseballer2[]      = {0x00040404,0x00020304,0x00000101,0x00000101,0x00000101,0x00040404};
static Uint32	_SBBaseballer3[]      = {0x00030303,0x00020303,0x00000101,0x00000101,0x00000101,0x00030303};
static Uint32	_SBKoregaPro1[]       = {0x00010204,0x00000000,0x00000000,0x00000000,0x00000000};
static Uint32	_SBKoregaPro2[]       = {0x00010203,0x00000000,0x00000000,0x00000000,0x00000000};
static Uint32	_SBKoregaPro3[]       = {0x00010202,0x00000000,0x00000000,0x00000000,0x00000000};
static Uint32	_SBKoregaPro4[]       = {0x00010101,0x00000000,0x00000000,0x00000000,0x00000000};
static Uint32	_SBKoregaPro5[]       = {0x00000103,0x00060606,0x00060606,0x00060606,0x00060606};
static Uint32	_SBKoregaPro6[]       = {0x00000002,0x00050505,0x00050505,0x00050505,0x00050505};
static Uint32	_SBKoregaPro7[]       = {0x00000001,0x00040404,0x00040404,0x00040404,0x00040404};
static Uint32	_SBKoregaPro8[]       = {0x00000000,0x00030303,0x00030303,0x00030303,0x00030303};
static Uint32	_SBKoregaPro9[]       = {0x00000000,0x00020202,0x00020202,0x00020202,0x00020202};
static Uint32	_SBKoregaProA[]       = {0x00000000,0x00010101,0x00010101,0x00010101,0x00010101};
static Uint32	_SBTheProYakyuu1[]    = {0x00000000,0x00070707,0x00070707,0x00070707,0x00070707};
static Uint32	_SBTheProYakyuu2[]    = {0x00000000,0x00060606,0x00060606,0x00060606,0x00060606};
static Uint32	_SBTheProYakyuu3[]    = {0x00000000,0x00050505,0x00050505,0x00050505,0x00050505};
static Uint32	_SBTheProYakyuu4[]    = {0x00000000,0x00040404,0x00040404,0x00040404,0x00040404};
static Uint32	_SBTheProYakyuu11[]   = {0x00000000,0x00070707,0x00070707,0x00000000,0x00070707};
static Uint32	_SBTheProYakyuu12[]   = {0x00000000,0x00060606,0x00060606,0x00000000,0x00060606};
static Uint32	_SBTheProYakyuu13[]   = {0x00000000,0x00050505,0x00050505,0x00000000,0x00050505};
static Uint32	_SBTheProYakyuu14[]   = {0x00000000,0x00040404,0x00040404,0x00000000,0x00040404};
static Uint32	_SBTheProYakyuu15[]   = {0x00000000,0x00030303,0x00030303,0x00000000,0x00030303};
static Uint32	_SBTheProYakyuu16[]   = {0x00000000,0x00020202,0x00020202,0x00000000,0x00020202};
static Uint32	_SBTheProYakyuu17[]   = {0x00000000,0x00010101,0x00010101,0x00000000,0x00010101};
static Uint32	_SBTheProYakyuu21[]   = {0x00000000,0x00000000,0x00070707,0x00070707,0x00000000};
static Uint32	_SBTheProYakyuu22[]   = {0x00000000,0x00000000,0x00060606,0x00060606,0x00000000};
static Uint32	_SBTheProYakyuu23[]   = {0x00000000,0x00000000,0x00050505,0x00050505,0x00000000};
static Uint32	_SBTheProYakyuu24[]   = {0x00000000,0x00000000,0x00040404,0x00040404,0x00000000};
static Uint32	_SBTheProYakyuu25[]   = {0x00000000,0x00000000,0x00030303,0x00030303,0x00000000};
static Uint32	_SBTheProYakyuu26[]   = {0x00000000,0x00000000,0x00020202,0x00020202,0x00000000};
static Uint32	_SBTheProYakyuu27[]   = {0x00000000,0x00000000,0x00010101,0x00010101,0x00000000};
static Uint32	_SBTatsujin[]		  = {0x00050506,0x00070707,0x00060607,0x00060607,0x00050506};
static Uint32	_SBNecros[]			  = {0x00000000,0x00070707,0x00000000,0x00000000,0x00070707}; //v3.05

//Kitao追加。他アプリのウィンドウの状態を保存しておくための変数。v2.24
static HWND				_OtherAppWindowHWnd[512];
static WINDOWPLACEMENT	_OtherAppWindowPlacement[512];
static Sint32			_OtherAppWindowN;


//Kitao追加。ウィンドウ位置保存のためのコールバック。v2.24
static BOOL
CALLBACK EnumWindowsSaveProc(HWND hWnd, LPARAM lParam)
{
	if ((IsWindowVisible(hWnd))&&(hWnd != WINMAIN_GetHwnd()))
	{
		if (_OtherAppWindowN < 512)
		{
			_OtherAppWindowHWnd[_OtherAppWindowN] = hWnd;
			_OtherAppWindowPlacement[_OtherAppWindowN].length = sizeof(WINDOWPLACEMENT);
			GetWindowPlacement(hWnd, &_OtherAppWindowPlacement[_OtherAppWindowN]);
			_OtherAppWindowN++;
		}
	}
	return TRUE;
}

//Kitao追加。ウィンドウ位置保存。v2.24
void
SCREEN_SaveWindowPosition()
{
	_OtherAppWindowN = 0;
	EnumWindows(EnumWindowsSaveProc, NULL);
	//PRINTF("WindowN %d",_OtherAppWindowN); //test
}

//Kitao追加。ウィンドウ位置を戻すためのコールバック。v2.24
static BOOL
CALLBACK EnumWindowsLoadProc(HWND hWnd, LPARAM lParam)
{
	int		i;	

	for (i=0; i<_OtherAppWindowN; i++)
		if (_OtherAppWindowHWnd[i] == hWnd)
		{
			SetWindowPlacement(hWnd, &_OtherAppWindowPlacement[i]);
			break;
		}
	return TRUE;
}

//Kitao追加。ウィンドウ位置を戻す。v2.24
void
SCREEN_LoadWindowPosition()
{
	EnumWindows(EnumWindowsLoadProc, NULL);
}


/*-----------------------------------------------------------------------------
	[Init]
		スクリーンモードを初期化(変更)します。 Kitao更新。v2.28
-----------------------------------------------------------------------------*/
BOOL
SCREEN_Init(
	Sint32		width,
	Sint32		height,
	Sint32		magnification, //Kitao追加
	Uint32		flags)
{
	BOOL	ret;

	_Width = width;
	_Height = height;
	_Magnification = magnification; //Kitao追加
	_Flags = flags;

	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_Deinit();
			ret = SCREEND3D_Init(_Width, _Height, _Magnification, _Flags);
			break;
		case 2: //DirectDraw
			SCREENDD_Deinit();
			ret = SCREENDD_Init(_Width, _Height, _Magnification, _Flags);
			break;
		default:
			ret = FALSE;
			break;
	}

	return ret;
}


/*-----------------------------------------------------------------------------
	[Deinit]
		スクリーンの終了処理を行ないます。
-----------------------------------------------------------------------------*/
void
SCREEN_Deinit()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_Deinit();
			break;
		case 2: //DirectDraw
			SCREENDD_Deinit();
			break;
	}
}


//オーディオグラフ表示用のバッファをクリアする。v2.92追加
void
SCREEN_InitAudioGraph()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_InitAudioGraph();
			break;
	}
}


/*-----------------------------------------------------------------------------
	[ToggleFullScreen]
		スクリーンをウインドウ／フルスクリーンに切り替えます．
-----------------------------------------------------------------------------*/
BOOL
SCREEN_ToggleFullScreen()
{
	if (_Flags & SCREEN_FFULLSCREEN)
		_Flags &= ~SCREEN_FFULLSCREEN;
	else
		_Flags |= SCREEN_FFULLSCREEN;
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_Deinit();
			return SCREEND3D_Init(_Width, _Height, _Magnification, _Flags);
		case 2: //DirectDraw
			SCREENDD_Deinit();
			return SCREENDD_Init(_Width, _Height, _Magnification, _Flags);
		default:
			return FALSE;
	}
}


/*-----------------------------------------------------------------------------
	[WaitVBlank]
		垂直帰線期間を待ちます。 
-----------------------------------------------------------------------------*/
//Kitao更新
BOOL
SCREEN_WaitVBlank(
	BOOL	bDraw) //bDrawをTRUEにして呼ぶと描画も行う。FALSEの場合VSync待ちのみ。Direct3D利用時用。Kitao追加。
{
	BOOL	ret;

	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			ret = SCREEND3D_WaitVBlank(bDraw);
			break;
		case 2: //DirectDraw
			ret = SCREENDD_WaitVBlank(FALSE); //DirectDrawのときは常に描画は行われない。
			break;
		default:
			return FALSE;
	}
	
	return ret;
}


void*
SCREEN_GetBuffer()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			return SCREEND3D_GetBuffer();
		case 2: //DirectDraw
			return SCREENDD_GetBuffer();
		default:
			return NULL;
	}
}


const Sint32
SCREEN_GetBufferPitch()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			return SCREEND3D_GetBufferPitch();
		case 2: //DirectDraw
			return SCREENDD_GetBufferPitch();
		default:
			return 0;
	}
}


//Kitao追加。スクリーン全体をクリアする。※ゲーム画面の範囲外の部分も含めて、ディスプレイ全体をクリアします。v1.43
void
SCREEN_Clear(
	byte	r,
	byte	g,
	byte	b) //v2.73更新。任意の色でクリアできるようにした。※16ビットモードのときはモノクロ以外だと色化けするので現在はモノクロ固定でrのデータのみ使用。
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_Clear(r,g,b);
			break;
		case 2: //DirectDraw
			SCREENDD_Clear(r,g,b);
			break;
	}
}


//Kitao追加。ゲーム画面を指定色でクリアする。VSync待ちも行われる。v2.77
void
SCREEN_Fill(
	Uint32	color)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			//120Hz以上の場合必要数ぶんのVBlank待ち処理を行う。v2.98
			if (MAINBOARD_GetVSyncHz() >= 120)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。
			if (MAINBOARD_GetVSyncHz() >= 180)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。180Hzの場合これで計2回待つことになる。
			if (MAINBOARD_GetVSyncHz() >= 240)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。240Hzの場合これで計3回待つことになる。
			SCREEND3D_Fill(color);
			break;
		case 2: //DirectDraw
			//120Hz以上の場合必要数ぶんのVBlank待ち処理を行う。v2.98
			if (MAINBOARD_GetVSyncHz() >= 120)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。
			if (MAINBOARD_GetVSyncHz() >= 180)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。180Hzの場合これで計2回待つことになる。
			if (MAINBOARD_GetVSyncHz() >= 240)
				SCREEN_WaitVBlank(FALSE); //垂直帰線期間を待つ。描画は行わない。240Hzの場合これで計3回待つことになる。
			SCREENDD_Fill(color, TRUE);
			break;
	}
}


/*-----------------------------------------------------------------------------
	[Blt]
		pSrc からバックバッファへ画像を書き込みます。拡大／縮小、
	呼ぶ前に SCREEN_Lock() しましょう。
-----------------------------------------------------------------------------*/
//Kitao更新。ラインごとに解像度を変えているゲーム(龍虎の拳，あすか120%など)に対応。
void
SCREEN_Blt(
	Uint32*		pSrc,
	Sint32		srcX,
	Sint32		srcY,
	Uint16*		pSrcW,	//Kitao更新。転送元の横ピクセル数。※srcHラインの数ぶん
	Sint32		srcH,	//Kitao更新。dstWとdstH はカットした。(ここで固定せず、様々な大きさでのペーストに対応するため)
	Sint32		executeCode)  //Kitao追加。実行コード。0…エンコードだけ行う。1…プライマリ画面へ転送も行う。
							  //					   3…左右に黒帯(オーバースキャン部)を配置しての転送(あとは1と同じ)
							  //					   5…左右のオーバースキャン部をカットしての転送(あとは1と同じ)
							  //					   6…周波数測定時用でfps表示や音グラフ表示は行わない(あとは1と同じ)
{
	Sint32		a,i;
	Sint32		ov;

	//Kitao追加。ネクロスの要塞のフラッシュ演出処理を健康を害さない程度に軽減。v3.05
	if ((MAINBOARD_GetNecros()) && (APP_GetFlashGuardNecros()))//ネクロスの要塞の場合
	{
		//戦闘シーンかどうかを検出する
		ov = 3 + (MAINBOARD_GetShowOverscanLeft() / 4 * 3); //左端のオーバースキャンエリアを表示するために確保してあるドット数。デフォルト(非表示)は0・表示する場合は5に、黒帯3ドットぶん足した数。v2.92更新(黒帯横4ドット)。v2.98から黒帯横3ドットに。
		if ((srcY <= 16+127) && (srcY+srcH >= 16+127+15))
		{
			if (memcmp(_SBNecros, pSrc+(16+127)*512+ov+17, 4*5) == 0) //戦闘シーン画面のメッセージ枠が表示されていたら
				VDC_SetNecrosBattle(TRUE);
			else
				VDC_SetNecrosBattle(FALSE);
		}
		//実際の軽減処理(パレット変更時に明るさを4分の1にする)はVDC.cppで行う

		//	Uint32* pT = pSrc+(16+127)*512+ov+17; PRINTF("Color = %08X %08X %08X %08X %08X %08X", *pT,*(pT+1),*(pT+2),*(pT+3),*(pT+4),*(pT+5)); //Test用
	}

	//Kitao追加。野球ゲームでカウント表示を現代の並び順に表示
	if (APP_GetStrikeBallSwap())
	{
		ov = 3 + (MAINBOARD_GetShowOverscanLeft() / 4 * 3); //左端のオーバースキャンエリアを表示するために確保してあるドット数。デフォルト(非表示)は0・表示する場合は5に、黒帯3ドットぶん足した数。v2.92更新(黒帯横4ドット)。v2.98から黒帯横3ドットに。

		//ワールドスタジアム'91でストライクとボールの表示をスワップする。v2.70。v2.92更新。v2.98から横256モード時には左側に黒帯3ドット入るようにしたのでここの座標も3ドットプラスした。
		if (MAINBOARD_GetWorldStadium91())
			if ((srcY <= 16+136)&&(srcY+srcH >= 16+144+7))
				if (memcmp(_SBWorldStadium91, pSrc+(16+136)*512+ov+14, 4*6) == 0) //ストライクが表示されていたら
				{
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+i*40, pSrc+(16+136+i)*512+ov+16, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+136+i)*512+ov+16, pSrc+(16+144+i)*512+ov+16, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+144+i)*512+ov+16, _SBSwapBuf+i*40, 4*32);
				}
		//ワールドスタジアム(初代)でストライクとボールの表示をスワップする。v2.70。v2.92更新
		if (MAINBOARD_GetWorldStadium())
			if ((srcY <= 16+136)&&(srcY+srcH >= 16+144+7))
				if (memcmp(_SBWorldStadium, pSrc+(16+136)*512+ov+14, 4*6) == 0) //ストライクが表示されていたら
				{
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+i*40, pSrc+(16+136+i)*512+ov+16, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+136+i)*512+ov+16, pSrc+(16+144+i)*512+ov+16, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+144+i)*512+ov+16, _SBSwapBuf+i*40, 4*32);
				}
				//else if (memcmp(_SBWorldStadium2, pSrc+(16+144)*512+ov+14, 4*6) == 0) //ボールカウントだけが表示されていた場合。代打が残り２人のときのタイム時に必要。
				//{
				//	for (i = 0; i < 8; i++)
				//		memcpy(pSrc+(16+144+i)*512+ov+16, pSrc+(16+176+i)*512+ov+16, 4*32); //ボールカウント表示を消す。現状は消さないほうが自然なのでボツ。
				//}
		//パワーリーグ３,４,５,'93でストライクとボールの表示をスワップする。v2.70。v2.92更新
		if (MAINBOARD_GetPowerLeague34593())
			if ((srcY <= 16+136)&&(srcY+srcH >= 16+146+7))
				if (memcmp(_SBPowerLeague34593, pSrc+(16+137)*512+ov+210, 4*6) == 0) //ストライクが表示されていたら
				{
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+i*40, pSrc+(16+136+i)*512+ov+210, 4*8);
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+(8+i)*40, pSrc+(16+146+i)*512+ov+210, 4*8);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+136+i)*512+ov+210, pSrc+(16+136+i)*512+ov+222, 4*8);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+146+i)*512+ov+210, pSrc+(16+146+i)*512+ov+222, 4*8);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+136+i)*512+ov+222, _SBSwapBuf+i*40, 4*8);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+146+i)*512+ov+222, _SBSwapBuf+(8+i)*40, 4*8);
				}
		//パワーリーグ２でストライクとボールの表示をスワップする。v2.70。v2.92更新
		if (MAINBOARD_GetPowerLeague2())
			if ((srcY <= 16+136)&&(srcY+srcH >= 16+144+7))
				if (memcmp(_SBPowerLeague2, pSrc+(16+137)*512+ov+216, 4*6) == 0) //ストライクが表示されていたら
				{
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+i*40, pSrc+(16+136+i)*512+ov+216, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+136+i)*512+ov+216, pSrc+(16+144+i)*512+ov+216, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+144+i)*512+ov+216, _SBSwapBuf+i*40, 4*32);
				}
		//パワーリーグのスコアボード画面で、でストライクとボールの表示をスワップする。試合中のカウントはスプライトなのでVDC.cppにておこなう。v2.70。v2.92更新
		if (VDC_GetPowerLeague())
			if ((srcY <= 16+121)&&(srcY+srcH >= 16+129+7))
				if (memcmp(_SBPowerLeague, pSrc+(16+121)*512+ov+145, 4*6) == 0) //ストライクが表示されていたら
				{
					for (i = 0; i < 8; i++)
						memcpy(_SBSwapBuf+i*40, pSrc+(16+121+i)*512+ov+144, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+121+i)*512+ov+144, pSrc+(16+129+i)*512+ov+144, 4*32);
					for (i = 0; i < 8; i++)
						memcpy(pSrc+(16+129+i)*512+ov+144, _SBSwapBuf+i*40, 4*32);
				}
		//栄冠は君にのスコアボード画面で、ストライクとボールの表示をスワップする。試合中のカウントはスプライトなのでVDP.cppにておこなう。v2.70
		if (VDC_GetEikanKimi())
		{
			if ((srcY <= 16+9)&&(srcY+srcH >= 16+17+8)&&
				(memcmp(_SBEikanKimi1, pSrc+(16+10)*512+287, 4*5) == 0)) //ストライクが表示されていたら。自チーム対戦のスコアボード表示時。横350モードなのでovは0。
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+9+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+9+i)*512+286, pSrc+(16+17+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+17+i)*512+286, _SBSwapBuf+i*40, 4*32);
			}
			else if ((srcY <= 16+9)&&(srcY+srcH >= 16+17+7)&&
					 ((memcmp(_SBEikanKimi8, pSrc+(16+21)*512+313, 4*5) == 0)||
					  (memcmp(_SBEikanKimi2, pSrc+(16+10)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi3, pSrc+(16+10)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi4, pSrc+(16+10)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi5, pSrc+(16+10)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi6, pSrc+(16+10)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi7, pSrc+(16+10)*512+287, 4*5) == 0))) //ストライクが表示されていたら。試合終了後
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+9+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+9+i)*512+286, pSrc+(16+17+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+17+i)*512+286, _SBSwapBuf+i*40, 4*32);
			}
			else if ((srcY <= 16+7)&&(srcY+srcH >= 16+15+7)&&
					 ((memcmp(_SBEikanKimi8, pSrc+(16+19)*512+313, 4*5) == 0)||
					  (memcmp(_SBEikanKimi2, pSrc+(16+ 8)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi3, pSrc+(16+ 8)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi4, pSrc+(16+ 8)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi5, pSrc+(16+ 8)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi6, pSrc+(16+ 8)*512+287, 4*5) == 0)||
					  (memcmp(_SBEikanKimi7, pSrc+(16+ 8)*512+287, 4*5) == 0))) //ストライクが表示されていたら。他チーム同士の対戦時
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+7+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+7+i)*512+286, pSrc+(16+15+i)*512+286, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+15+i)*512+286, _SBSwapBuf+i*40, 4*32);
			}
		}
		//ナグザットスタジアム。v2.92更新
		if (MAINBOARD_GetNaxatStadium())
		{
			if ((srcY <= 16+168)&&(srcY+srcH >= 16+176+7)&&
				(memcmp(_SBNaxatStadium, pSrc+(16+182)*512+ov+17, 4*6) == 0)) //アウトカウントが表示されていたら
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+168+i)*512+ov+16, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+168+i)*512+ov+16, pSrc+(16+176+i)*512+ov+16, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+176+i)*512+ov+16, _SBSwapBuf+i*40, 4*32);
			}
		}
		//ROMROMスタジアム。v2.92更新
		if (CDROM_GetRomRomStadium())
		{
			if ((srcY <= 16+185)&&(srcY+srcH >= 16+193+7)&&
				(memcmp(_SBRomRomStadium, pSrc+(16+185)*512+ov+16, 4*6) == 0)) //ストライクが表示されていたら
			{
				if (*(pSrc+(16+185)*512+ov+25) == 0x00000000) //代打ウィンドウ非表示時
					a = 40;
				else
					a = 8;
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+185+i)*512+ov+16, 4*a);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+185+i)*512+ov+16, pSrc+(16+193+i)*512+ov+16, 4*a);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+193+i)*512+ov+16, _SBSwapBuf+i*40, 4*a);
			}
		}
		//熱血レジェンドベースボーラー。v2.92更新
		if (CDROM_GetBaseballer())
		{
			if ((srcY <= 16+64)&&(srcY+srcH >= 16+72+7)&&
				((memcmp(_SBBaseballer1, pSrc+(16+65)*512+ov+17, 4*6) == 0)||
				 (memcmp(_SBBaseballer2, pSrc+(16+65)*512+ov+17, 4*6) == 0)||
				 (memcmp(_SBBaseballer3, pSrc+(16+65)*512+ov+17, 4*6) == 0))) //ストライクが表示されていた
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+64+i)*512+ov+16, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+64+i)*512+ov+16, pSrc+(16+72+i)*512+ov+16, 4*32);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+72+i)*512+ov+16, _SBSwapBuf+i*40, 4*32);
			}
		}
		//これがプロ野球'89＆これがプロ野球'90。v2.92更新
		if (MAINBOARD_GetKoregaPro())
		{
			if ((srcY <= 16+144)&&(srcY+srcH >= 16+152+7)&&
				((memcmp(_SBKoregaPro1, pSrc+(16+153)*512+ov+224, 4*5) == 0)|| //ボールの３つ目のカウントが表示されていたら
				 (memcmp(_SBKoregaPro2, pSrc+(16+153)*512+ov+224, 4*5) == 0)||
				 (memcmp(_SBKoregaPro3, pSrc+(16+153)*512+ov+224, 4*5) == 0)||
				 (memcmp(_SBKoregaPro4, pSrc+(16+153)*512+ov+224, 4*5) == 0)||
				 (memcmp(_SBKoregaPro5, pSrc+(16+145)*512+ov+192, 4*5) == 0)|| //ストライクが表示されていたら
				 (memcmp(_SBKoregaPro6, pSrc+(16+145)*512+ov+192, 4*5) == 0)||
				 (memcmp(_SBKoregaPro7, pSrc+(16+145)*512+ov+192, 4*5) == 0)||
				 (memcmp(_SBKoregaPro8, pSrc+(16+145)*512+ov+192, 4*5) == 0)||
				 (memcmp(_SBKoregaPro9, pSrc+(16+145)*512+ov+192, 4*5) == 0)||
				 (memcmp(_SBKoregaProA, pSrc+(16+145)*512+ov+192, 4*5) == 0)))
			{
				for (i = 0; i < 8; i++)
					memcpy(_SBSwapBuf+i*40, pSrc+(16+144+i)*512+ov+192, 4*40);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+144+i)*512+ov+192, pSrc+(16+152+i)*512+ov+192, 4*40);
				for (i = 0; i < 8; i++)
					memcpy(pSrc+(16+152+i)*512+ov+192, _SBSwapBuf+i*40, 4*40);
			}
		}
		//インテックCD野球３作（ザ・プロ野球，ザ・プロ野球SUPER，ザ・プロ野球SUPER'94）。v2.92更新
		if (VDC_GetTheProYakyuu())
		{
			if ((srcY <= 16+113)&&(srcY+srcH >= 16+128)&&
				((memcmp(_SBTheProYakyuu1, pSrc+(16+113)*512+ov+212, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu2, pSrc+(16+113)*512+ov+212, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu3, pSrc+(16+113)*512+ov+212, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu4, pSrc+(16+113)*512+ov+212, 4*5) == 0))) //打席画面でボールカウントが表示されていたら
			{
				if (*(pSrc+(16+120)*512+ov+213) != 0x00000000) //まだ未修正の場合
				{
					memcpy(pSrc+(16+128)*512+ov+212, pSrc+(16+120)*512+ov+212, 4*2); //O(アウト)の左上背景を修正
					*(pSrc+(16+120)*512+ov+213) = 0x00000000; //B(ボール)の左下背景を修正
				}
			}
			if ((srcY <= 16+165)&&(srcY+srcH >= 16+169)&&
				((memcmp(_SBTheProYakyuu11, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu12, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu13, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu14, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu15, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu16, pSrc+(16+169)*512+ov+38, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu17, pSrc+(16+169)*512+ov+38, 4*5) == 0))) //フィールド画面でボールカウントが表示されていたら
			{
				if ((*(pSrc+(16+166)*512+ov+62) != 0x00070003)&&
					((*(pSrc+(16+166)*512+ov+62) != 0x00000000)||(*(pSrc+(16+169)*512+ov+39) != 0x00070707))) //ピンク色(＆黒縁)のランナーがディスティネーションでなければorフェードアウト時
						*(pSrc+(16+166)*512+ov+62) = *(pSrc+(16+169)*512+ov+39); //VDC.cppで処理できなかった部分（一二塁間白線）をここで処理
			}
			if ((srcY <= 16+174)&&(srcY+srcH >= 16+175)&&
				((memcmp(_SBTheProYakyuu21, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu22, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu23, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu24, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu25, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu26, pSrc+(16+175)*512+ov+40, 4*5) == 0)||
				 (memcmp(_SBTheProYakyuu27, pSrc+(16+175)*512+ov+40, 4*5) == 0))) //フィールド画面でストライクが表示されていたら
			{
				memcpy(pSrc+(16+175)*512+ov+47, pSrc+(16+174)*512+ov+47, 4*3); //VDC.cppで処理できなかった部分（ラスタ単位でキャラ色が書き換えられている）をここで処理
				memcpy(pSrc+(16+175)*512+ov+53, pSrc+(16+174)*512+ov+53, 4*3); //同上
			}
		}
	}
	//Uint32* pT = pSrc+(16+136)*512+24; PRINTF("Color = %08X %08X %08X %08X %08X %08X", *pT,*(pT+1),*(pT+2),*(pT+3),*(pT+4),*(pT+5)); //Test用

	//TATSUJINで上下のオーバースキャン領域を表示した場合に、スタート時orやられた時に画面上下端に表示されるゴミを非表示にする。v3.04
	if (MAINBOARD_GetTatsujin())
	{
		if (!VDC_GetPerformSpriteLimit()) //スプライト欠けを再現しない時のみ。再現する設定にしてある場合は、実機同様にゴミも表示する。
		{
			if (VDC_GetTvWidth() == 350)
				ov = MAINBOARD_GetShowOverscanLeft()/8 * 9; //左端のオーバースキャンエリアを表示するために確保してあるドット数
			else //(VDC_GetTvWidth() == 260)
				ov = 3 + (MAINBOARD_GetShowOverscanLeft()/8 * 6); //左端のオーバースキャンエリアを表示するために確保してあるドット数
			if ((srcY <= 16 + 3) && (srcY + srcH >= 16 + 3 + 15))
			{
				if (MAINBOARD_GetShowOverscanTop() > 0)
				{
					if (VDC_GetTvWidth() == 350)
					{//横解像度350モードの場合（裏技でSLIMを選択している場合）
						//上から3ライン目、左から271ドット目からの5ドットで、スタート時の幕が表示されているかどうかをチェック
						if (memcmp(_SBTatsujin, pSrc + (16 + 3) * 512 + ov + 271, 4 * 5) == 0) //幕が表示されていたら（スタートor再スタート直後）
						{
							for (i = 0; i < 15; i++) //オーバースキャン領域の上から15ライン分をクリアする
								memset(pSrc + i * 512 + ov, 0, 4 * 350); //横350ドットぶんを0でクリア
						}
					}
					else
					{//通常(VDC_GetTvWidth()==260。横256モード)
						//上から3ライン目、左から224ドット目からの5ドットで、スタート時の幕が表示されているかどうかをチェック
						if (memcmp(_SBTatsujin, pSrc + (16 + 3) * 512 + ov + 224, 4 * 5) == 0) //幕が表示されていたら（スタートor再スタート直後）
						{
							for (i = 0; i < 15; i++) //オーバースキャン領域の上から15ライン分をクリアする
								memset(pSrc + i * 512 + ov, 0, 4 * 256); //横256ドットぶんを0でクリア
						}
					}
				}
			}
			if (srcY + srcH >= 240)
			{
				if ((MAINBOARD_GetShowOverscanBottom() > 0) || (MAINBOARD_GetShowOverscanTop() > 0)) //上側だけオーバースキャン領域表示の設定の場合も、スタート時1ライン乱れのために下側をクリアする。
				{
					if (VDC_GetTvWidth() == 350)
					{//横解像度350モードの場合（裏技でSLIMを選択している場合）
						//上から3ライン目、左から271ドット目からの5ドットで、スタート時の幕が表示されているかどうかをチェック
						if (memcmp(_SBTatsujin, pSrc + (16 + 3) * 512 + ov + 271, 4 * 5) == 0) //幕が表示されていたら（スタートor再スタート直後）
						{
							for (i = 239; i < 248; i++) //オーバースキャン領域の下から9ライン分をクリアする
								memset(pSrc + i * 512 + ov, 0, 4 * 350); //横350ドットぶんを0でクリア
						}
					}
					else
					{//通常(VDC_GetTvWidth()==260。横256モード)
						//上から3ライン目、左から224ドット目からの5ドットで、スタート時の幕が表示されているかどうかをチェック
						if (memcmp(_SBTatsujin, pSrc + (16 + 3) * 512 + ov + 224, 4 * 5) == 0) //幕が表示されていたら（スタートor再スタート直後）
						{
							for (i = 239; i < 248; i++) //オーバースキャン領域の下から9ライン分をクリアする
								memset(pSrc + i * 512 + ov, 0, 4 * 256); //横256ドットぶんを0でクリア
						}
					}
				}
			}
			//Uint32* pT = pSrc + (16 + 3) * 512 + ov + 224; PRINTF("Color = %08X %08X %08X %08X %08X %08X", *pT, *(pT + 1), *(pT + 2), *(pT + 3), *(pT + 4), *(pT + 5)); //Test用
			//Uint32* pT = pSrc + (16 + 3) * 512 + ov + 271; PRINTF("Color = %08X %08X %08X %08X %08X %08X", *pT, *(pT + 1), *(pT + 2), *(pT + 3), *(pT + 4), *(pT + 5)); //Test用
		}
	}

	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_Blt(pSrc, srcX, srcY, pSrcW, srcH, executeCode);
			break;
		case 2: //DirectDraw
			SCREENDD_Blt(pSrc, srcX, srcY, pSrcW, srcH, executeCode);
			break;
	}
}


//Kitao追加。VSync(垂直帰線待ち)を行うかどうかを設定。現在のディスプレイ表示環境でVSyncが行えるかどうかのチェックも行う。
void
SCREEN_SetVSyncScreen(
	BOOL	bVSyncScreen)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_SetVSyncScreen(bVSyncScreen); //ここで_bVSync120Hzも設定される。v2.79追加
			break;
		case 2: //DirectDraw
			SCREENDD_SetVSyncScreen(bVSyncScreen); //ここで_bVSync120Hzも設定される。v2.79追加
			break;
	}
}

//Kitao追加。VSync(垂直帰線待ち)を行っているかどうかを得る（現在のディスプレイ表示環境でVSyncが行えるかどうかのチェックを反映した値）。
BOOL
SCREEN_GetVSyncScreen()
{
	BOOL	bVSyncScreen;

	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			bVSyncScreen = SCREEND3D_GetVSyncScreen();
			break;
		case 2: //DirectDraw
			bVSyncScreen = SCREENDD_GetVSyncScreen();
			break;
	}

	return bVSyncScreen;
}


//Kitao追加。テキストメッセージを設定
void
SCREEN_SetMessageText(
	char*	pText)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_SetMessageText(pText);
			break;
		case 2: //DirectDraw
			SCREENDD_SetMessageText(pText);
			break;
	}
}


//Kitao追加。ガンマ（明るさ調整）を計算済みのテーブルを用意。v2.28更新。Direct3DとDirectDrawで共用にした。
void
SCREEN_SetGamma(
	Sint32	scanlineType,
	Sint32	scanlineDensity) //スキャンラインの濃度(%)
{
	Sint32	magnification;
	int 	a,i;
	double	d = APP_GetGammaValue(); //縦横スキャンライン時の基本ガンマ値
	Sint32	b = APP_GetBrightValue(); //ブライトネス
	Uint32	t;
	Uint32	gamma[8];
	Uint32	gammaS80[8];
	Uint32	gammaS90[8];
	Uint32	m,n;
	Uint32	monoR[256], monoRs80[256], monoRs90[256];
	Uint32	monoG[256], monoGs80[256], monoGs90[256];;
	Uint32	monoB[256], monoBs80[256], monoBs90[256];;

	//基本設定ではなく、現在実際に表示されているMagnificationを取得する。v2.28
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			magnification = SCREEND3D_GetMagnification();
			break;
		case 2: //DirectDraw
			magnification = SCREENDD_GetMagnification();
			break;
		default:
			magnification = 2;
			break;
	}

	//横スキャンラインの場合、スキャンラインで暗くなるぶん、ガンマを明るめにする。ノンスキャンラインの場合、ガンマを暗めにする。
	if (magnification >= 2)
	{
		switch (scanlineType)
		{
			case 1: //縦横スキャンライン（デフォルト）
				break;
			case  2: //横だけスキャンライン
			case  3: //横だけスキャンラインファースト
			case  4: //TVスキャンライン
			case  5: //横だけスキャンラインシャープ
			case  6: //横だけスキャンラインモアシャープ
			case 11: //TVスキャンラインぼやけ
				d = (1-15/800)*d; // (1-15/800)*1.305 縦横時と比べて明るいぶんを引く
				if (APP_GetOptimizeGamma())
					d = d * (1+(80-(double)scanlineDensity)*0.005); // ダウンした明るさ分を上げる。v2.35更新
				break;
			default: //ノンスキャンライン(0,7,8,9,10)
				d = (1-95/800)*d; // (1-95/800)*1.305 縦横時と比べて明るいぶんを引く
				break;
		}
	}
	else //ノンスキャンラインもしくはx1の場合
		d = (1-95/800)*d; // (1-95/800)*1.305 縦横時と比べて明るいぶんを引く

	if (((scanlineType >= 2)&&(scanlineType <= 6))||(scanlineType == 11)) //横スキャンラインの場合。v2.92更新
	{
		for (i=0; i<=7 ; i++)
		{
			if (i == 0)
				a = 0; //黒は真っ黒に
			else
				a = (i << 5) + b; //+1。白が白く見える範囲で小さめにしたほうが目にきつくない。
			gamma[i] = (Uint32)((pow((double)a / 256.0, 1.0/d) * 256.0) +0.5);
			gammaS80[i] = (Uint32)((pow((double)a*((double)scanlineDensity / 100) / 256.0, 1.0/d) * 256.0) +0.5); //スキャンライン本線用
			gammaS90[i] = (Uint32)((pow((double)a*((double)(scanlineDensity+(100-scanlineDensity)/2) / 100) / 256.0, 1.0/d) * 256.0) +0.5); //スキャンラインとドットの境界用
		}
	}
	else //横スキャンライン以外の場合。横スキャンライン80%を基準とする。v2.35更新
	{
		for (i=0; i<=7 ; i++)
		{
			a = i*32 + (Sint32)((double)b * ((double)i/7) + 0.5); //黒は真っ黒に。白は白く見える範囲で小さめにしたほうが目にきつくない。v2.35更新
			gamma[i] = (Uint32)((pow((double)a / 256.0, 1.0/d) * 256.0) +0.5);
			gammaS80[i] = (Uint32)((pow((double)a*0.80 / 256.0, 1.0/d) * 256.0) +0.5); //スキャンライン80%用（スキャンライン本線）
			gammaS90[i] = (Uint32)((pow((double)a*0.90 / 256.0, 1.0/d) * 256.0) +0.5); //スキャンライン90%用（スキャンラインとドットの境界用）
		}
	}

	//モノクロ変換用テーブルを作成。v2.28追加。v2.99更新
	for (i = 0; i<= 0xFF; i++)
	{
		//R,G,Bの輝度を平均化してモノクロ化
		monoR[i] = (Uint32)((pow(((double)i * 0.298912) / 256.0, 1.0 / 1.0752080) * 256.0)); //※画面の暗さを抑えるためガンマも上げる。
		monoG[i] = (Uint32)((pow(((double)i * 0.586611) / 256.0, 1.0 / 1.0752080) * 256.0)); //※RGB足したときに値オーバーしないために小数点以下は切り捨て。
		monoB[i] = (Uint32)((pow(((double)i * 0.114478) / 256.0, 1.0 / 1.0752080) * 256.0)); //
		monoRs80[i] = (Uint32)((pow(((double)i * 0.298912) * 0.80 / 256.0, 1.0 / 1.0752080) * 256.0)); //スキャンライン80%用（スキャンライン本線）
		monoGs80[i] = (Uint32)((pow(((double)i * 0.586611) * 0.80 / 256.0, 1.0 / 1.0752080) * 256.0));
		monoBs80[i] = (Uint32)((pow(((double)i * 0.114478) * 0.80 / 256.0, 1.0 / 1.0752080) * 256.0));
		monoRs90[i] = (Uint32)((pow(((double)i * 0.298912) * 0.90 / 256.0, 1.0 / 1.0752080) * 256.0)); //スキャンライン90%用（スキャンラインとドットの境界用）
		monoGs90[i] = (Uint32)((pow(((double)i * 0.586611) * 0.90 / 256.0, 1.0 / 1.0752080) * 256.0));
		monoBs90[i] = (Uint32)((pow(((double)i * 0.114478) * 0.90 / 256.0, 1.0 / 1.0752080) * 256.0));
	}

	//9bitのRGB値の色情報をテーブル化して高速化。v2.99
	for (t = 0; t <= 0xFFFFFF; t++)
	{
		_GammaTBL[t] = (gamma[(t >> 16) & 7] << 16) | (gamma[(t >> 8) & 7] << 8) | gamma[t & 7]; //RGB->BGR
		_GammaTBLs80[t] = (gammaS80[(t >> 16) & 7] << 16) | (gammaS80[(t >> 8) & 7] << 8) | gammaS80[t & 7];
		_GammaTBLs90[t] = (gammaS90[(t >> 16) & 7] << 16) | (gammaS90[(t >> 8) & 7] << 8) | gammaS90[t & 7];
		m = monoR[gamma[(t >> 16) & 7]] + monoG[gamma[(t >> 8) & 7]] + monoB[gamma[t & 7]]; //R,G,Bの輝度を平均化
		if (MAINBOARD_GetForceMonoColor() == 2) //グリーンディスプレイモードの場合。 ステートロード(orレジューム)で一時的に白黒モードにすることに対応したので、APP_GetMonoColorMode()ではなくMAINBOARD_GetForceMonoColor()を使う。
		{
			n = m + (m >> 3) + (m >> 4);
			if (n > 0xFF) n = 0xFF;
			m = m >> 2;
		}
		else
			n = m;
		_MonoTBL[t] = (m << 16) | (n << 8) | m;
		m = monoRs80[gamma[(t >> 16) & 7]] + monoGs80[gamma[(t >> 8) & 7]] + monoBs80[gamma[t & 7]]; //R,G,Bの輝度を平均化
		if (MAINBOARD_GetForceMonoColor() == 2) //グリーンディスプレイモードの場合。 ステートロード(orレジューム)で一時的に白黒モードにすることに対応したので、APP_GetMonoColorMode()ではなくMAINBOARD_GetForceMonoColor()を使う。
		{
			n = m + (m >> 3) + (m >> 4);
			if (n > 0xFF) n = 0xFF;
			m = m >> 2;
		}
		else
			n = m;
		_MonoTBLs80[t] = (m << 16) | (n << 8) | m;
		m = monoRs90[gamma[(t >> 16) & 7]] + monoGs90[gamma[(t >> 8) & 7]] + monoBs90[gamma[t & 7]]; //R,G,Bの輝度を平均化
		if (MAINBOARD_GetForceMonoColor() == 2) //グリーンディスプレイモードの場合。 ステートロード(orレジューム)で一時的に白黒モードにすることに対応したので、APP_GetMonoColorMode()ではなくMAINBOARD_GetForceMonoColor()を使う。
		{
			n = m + (m >> 3) + (m >> 4);
			if (n > 0xFF) n = 0xFF;
			m = m >> 2;
		}
		else
			n = m;
		_MonoTBLs90[t] = (m << 16) | (n << 8) | m;
	}
}


//Kitao追加。前回のVBlank待ちが終わった時刻を返す。
Sint64
SCREEN_GetLastTimeSyncTime()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			return SCREEND3D_GetLastTimeSyncTime();
		case 2: //DirectDraw
			return SCREENDD_GetLastTimeSyncTime();
		default:
			return 0;
	}
}


//Kitao追加。スクリーンショットのBitmapを書き込む。v2.12
void
SCREEN_WriteScreenshot(
	FILE*	fp)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_WriteScreenshot(fp);
			break;
		case 2: //DirectDraw
			SCREENDD_WriteScreenshot(fp);
			break;
	}
}


//Kitao追加。描画倍率を設定する。v2.36
void
SCREEN_SetMagnification(
	Sint32	magnification)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_SetMagnification(magnification);
			break;
		case 2: //DirectDraw
			SCREENDD_SetMagnification(magnification);
			break;
	}
}


//Kitao追加。実際に「描画処理で使用している」描画倍率を得る。v2.36
Sint32
SCREEN_GetMagnification()
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			return SCREEND3D_GetMagnification();
		case 2: //DirectDraw
			return SCREENDD_GetMagnification();
	}
	return 0;
}


//Kitao追加。描画時のドット拡大率を設定する。
void
SCREEN_SetPixelMagnification(
	Sint32*		wMag,
	Sint32*		hMag)
{
	Sint32		magnification;
	Sint32		scanlineType;

	//基本設定ではなく、現在実際に表示されているMagnificationを取得する。v2.28
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			magnification = SCREEND3D_GetMagnification();
			break;
		case 2: //DirectDraw
			magnification = SCREENDD_GetMagnification();
			break;
		default:
			magnification = 2;
			break;
	}

	if (magnification >= 2) //x2以上の場合
	{
		scanlineType = APP_GetScanlineType();
		if ((scanlineType >= 1)&&(scanlineType <= 6)) //スキャンライン(縦横含む)時
		{
			if ((scanlineType == 3)||(scanlineType == 4))
				*wMag = 1;
			else
				*wMag = 2; //x2以上も横は２倍ドット固定。（速度アップ＋拡大されたときにバイリニアフィルタがいい感じにかかる）
			*hMag = magnification; //縦は倍率ぶんのソースを用意して転送。
		}
		else //ノンスキャンライン時
		{
			*wMag = 2;
			if (magnification == 2)
				*hMag = magnification; //縦は倍率ぶんのソースを用意して転送。
			else
				*hMag = magnification-1; //3x,4xのときは、それぞれ2x,3xに拡大。（ジャギー軽減＆速度アップ）
		}
	}
	else //x1の場合
		*wMag = *hMag = 1;
}


//Kitao追加。ゲーム画面真ん中にメッセージを表示する。v2.77
void
SCREEN_SetCenterText(
	char*	pText)
{
	switch (APP_GetDrawMethod())
	{
		case 1: //Direct3D
			SCREEND3D_SetCenterText(pText);
			break;
		case 2: //DirectDraw
			SCREENDD_SetCenterText(pText);
			break;
	}
}


//Kitao追加。ディスプレイの垂直走査周波数を測定して返す。v2.43
Sint32
SCREEN_GetVerticalScanFrequency()
{
	Sint32	vsf = 0;
	Sint32	a;
	Sint64	t, t2;
	MSG		msg;

	MAINBOARD_ScreenClear();
	SCREEN_SetCenterText(" Checking Now... Please wait for 30 seconds. ");
	MAINBOARD_ScreenUpdateClear(); //ゲーム画面はクリア。メッセージのみ表示する。ゲーム開始前でも使える関数。
	//return 0; //Test用

	Sleep(1000);//初動時の処理落ちを防ぐため、Windowsのウィンドウ周りの更新処理などが終わるまで１秒待つ。v2.94

	SCREEN_WaitVBlank(FALSE); //"前回VBlankが終わった時刻"を更新するために必要。
	t = APP_QPCtimeGetTime();
	t2 = t + 30000;
	while (t2 < t) //終了予定時刻のタイマーカウンタがオーバーフローしていた場合、開始予定時刻のタイマーカウンタが0に戻るまで待つ。
	{
		SCREEN_WaitVBlank(FALSE); //"前回VBlankが終わった時刻"を更新するために必要。
		t = APP_QPCtimeGetTime();
		t2 = t + 30000;
	}
	
	while (APP_QPCtimeGetTime() <= t2)
	{
		vsf++;
		SetCursor(LoadCursor(NULL, IDC_WAIT)); //カーソルを砂時計に。

		//実際の描画処理をシミュレーションする。v2.79
		MAINBOARD_AdvanceFrameSim();

		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	vsf = (vsf * 20) / 6; //v3.05から30秒にしたため20倍(600秒)して計算

	//一の位を0か5に丸める。音ズレの際、音が遅い分には目立ちにくく早いと目立つので、丸める際は遅いほう(周波数が小さい方)に丸める。v2.94
	a = vsf % 10; //一の位を退避
	vsf /= 10; //一の位をカット
	if (a <= 4) //0〜4の場合切り捨て
		vsf *= 10;
	else //5〜9の場合5に丸める
		vsf = vsf * 10 + 5;

	SetCursor(LoadCursor(NULL, IDC_ARROW)); //カーソルを元に戻す
	SCREEN_SetCenterText("");
	MAINBOARD_ScreenUpdateClear();

	return vsf;
}
