/******************************************************************************
Ootake
・VistaのAERO等に対応するため、Direct3Dでの描画を実装。現状、機能的には
  ScreenDD.c(DirectDraw描画)と同等。カラーは32ビットのみ対応とする。

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

******************************************************************************/
#define _CRT_SECURE_NO_DEPRECATE

#define DIRECT3D_VERSION	0x0900

#define SOURCE_PITCH 		512

#include <D3D9.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "ScreenD3D.h"
#include "Screen.h"
#include "WinMain.h"
#include "App.h"
#include "MainBoard.h"
#include "Printf.h"
#include "ADPCM.h"
#include "CDROM.h"

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

//Kitao追加。Direct3D9Ex用定義。v2.21
static HINSTANCE _hD3D9Dll;
typedef HRESULT(WINAPI* FuncDirect3DCreate9Ex) (UINT, IDirect3D9Ex**);
static FuncDirect3DCreate9Ex _FuncDirect3DCreate9Ex;
static UINT		_MaxFrameLatency = 1; //ビデオのコントロールパネルで設定されている「最大レンダリングフレーム数」。XPでは得ることができないので1としておく。

static LPDIRECT3D9				_pD3D = NULL; //Direct3D object
static LPDIRECT3DDEVICE9		_pD3DDev = NULL; //デバイス
static IDirect3D9Ex*			_pD3DEx = NULL; //Direct3DEx object。v2.21追加。Win7/Vistaで描画遅延を少なくするために必要。
static IDirect3DDevice9Ex*		_pD3DDevEx = NULL; //同上
static LPDIRECT3DSURFACE9		_pD3DSBack = NULL; //バックバッファ(ここに転送すると実際に描画される)
static LPDIRECT3DSURFACE9		_pD3DSGame = NULL; //メイン画面用
static LPDIRECT3DSURFACE9		_pD3DSText = NULL; //テキストメッセージ,FPS,オーディオグラフ表示用
static D3DPRESENT_PARAMETERS	_pD3DParam;

static BOOL		_bIntelVideoChip = FALSE; //インテルの内蔵ビデオチップの場合TRUEに。v2.79
static BOOL		_bOldVideoChip = FALSE; //古いビデオチップ(ビデオカード)の場合TRUEに。v2.19

static LONG		_SurfacePitch = 0;

static Sint32	_Width;
static Sint32	_Height;
static Sint32	_Magnification;
static Uint32	_Flags;
static HWND		_hWnd;
static Uint16* _pScreenPixels;
static BOOL		_bChangedFullScreen = FALSE;
static Sint32	_GameLeft;   //print_message()で表示位置決定のために、ゲーム画面転送先の座標を控えておく。
static Sint32	_GameRight;  //
static Sint32	_GameTop;    //
static Sint32	_GameBottom; //

//Win9x用変数
static DWORD	_PrevWidth;
static DWORD	_PrevHeight;
static DWORD	_PrevBitsPerPixel;
static DWORD	_PrevFrequency;

static Sint64	_LastTimeSyncTime; //前回Syncしたときのタイマーカウント。単位は1/1000ms
static Sint64	_LastTimeSyncTime3; //3フレーム単位での、前回Syncしたときのタイマーカウント。v2.43
static BOOL		_bVSyncScreen = TRUE;

static LONG		_Pitch;
static LONG		_Pitch2; //pitchの2倍の値を入れておく。速度アップ用。
static LONG		_Pitch3; //pitchの3倍の値を入れておく。速度アップ用。
static LONG		_Pitch4; //pitchの4倍の値を入れておく。速度アップ用。
static LONG		_Pitch5; //pitchの5倍の値を入れておく。速度アップ用。
static Uint32* _pPixels; //v2.13更新。32bit単位で扱うようにした。(高速化)

static char* _pMessageText = ""; //テキストメッセージ表示用バッファ。ヌル（先頭が0）の場合、非表示。
static char* _pCenterText = ""; //センターテキストメッセージ表示用バッファ。ヌル（先頭が0）の場合、非表示。

//オーディオグラフを表示するときのデータ。表示する１フレーム前にここに保存しておく。１フレーム遅らせる事で画面とのタイミングも近づく。v2.92
static Sint32	_PSGChannelStatus[6];
static Sint32	_PSGSampleL[6];
static Sint32	_PSGSampleR[6];
static Sint32	_ADPCMSample;
static Sint32	_CDDASampleL;
static Sint32	_CDDASampleR;

//D3Dデバイスの開放処理
static void
d3dSurfaceRelease()
{
	if (_pD3DSText != NULL)
	{
		_pD3DSText->Release(); _pD3DSText = NULL;
	}

	if (_pD3DSGame != NULL)
	{
		_pD3DSGame->Release(); _pD3DSGame = NULL;
	}

	if (_pD3DSBack != NULL)
	{
		_pD3DSBack->Release(); _pD3DSBack = NULL;
	}
}

//Direct3D用スクリーンを安全に開放します。
void
SCREEND3D_Deinit()
{
	if (_pD3D != NULL)
	{
		d3dSurfaceRelease();
		if (APP_GetWindowsVista()) //Vista以降でDirect3D9Exを使った場合
		{
			if (_pD3DDevEx != NULL)
			{
				_pD3DDevEx->Release(); //Ex側で開放を行う
				_pD3DDevEx = NULL;
			}
			_pD3DEx->Release(); //Ex側で開放を行う
			_pD3DEx = NULL;
		}
		else //Vistaより前のWindowsの場合、普通に開放。
		{
			if (_pD3DDev != NULL)
				_pD3DDev->Release();
			_pD3D->Release();
		}
		_pD3DDev = NULL;
		_pD3D = NULL;
	}
	if (APP_GetWindowsVista())
		FreeLibrary(_hD3D9Dll); //DLLを開放
}

//v2.70更新
static HRESULT
d3dCreate()
{
	HRESULT		hr;

	if (APP_GetWindowsVista())
	{	//v2.21追加
		hr = _pD3DEx->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING,
			&_pD3DParam, NULL, &_pD3DDevEx); //v2.70更新。HARDWARE処理のほうが何となく遅延少ない気が。
		if (FAILED(hr))
		{
			hr = _pD3DEx->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
				&_pD3DParam, NULL, &_pD3DDevEx); //v2.71更新。インテルの古いチップなどでシェーダー未装備の場合エラーになるのでその場合SOFTWARE処理。
			if (FAILED(hr))
				MessageBox(_hWnd, "ERROR: DIRECT3D::CreateDeviceEx() failed.    ", "Ootake", MB_OK);
		}
		_pD3DDevEx->GetMaximumFrameLatency(&_MaxFrameLatency); //コントロールパネルで設定されているレンダリングフレーム数を得る。コントロールパネルで設定していても3が必ず返ってくるようなので現状使えず。v2.88
		_pD3DDevEx->SetMaximumFrameLatency(1); //遅延を最小限に抑える。Aeroオフ＋これでやっとXP並に。※コントロールパネルの設定によっては効かない（多くの環境ではデフォルト設定で効かない）ので気休め程度。v2.88更新
		_pD3DDev = _pD3DDevEx; //型キャスト。あとは通常のDirect3D9に任せる。
		//{
		//	char s[100];
		//	sprintf(s,"MaxLatency = %d", _MaxFrameLatency);
		//	MessageBox(WINMAIN_GetHwnd(), s, "Test", MB_OK); //Kitao Test
		//}
	}
	else
	{
		hr = _pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING,
			&_pD3DParam, &_pD3DDev); //v2.70更新。HARDWARE処理のほうが何となく遅延少ない気が。
		if (FAILED(hr))
			hr = _pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
				&_pD3DParam, &_pD3DDev); //v2.71更新。インテルの古いチップなどでシェーダー未装備の場合エラーになるのでその場合SOFTWARE処理。
	}
	return hr;
}

static HRESULT
d3dDevReset()
{
	int			a;
	HRESULT		hr;

	if (_pD3DDev == NULL) //未作成の場合
	{
		hr = d3dCreate(); //2.70更新
	}
	else //すでに作成済みの場合
	{
		d3dSurfaceRelease();
		hr = _pD3DDev->Reset(&_pD3DParam);
		if (FAILED(hr)) //リセットに失敗したら作り直す
		{
			_pD3DDev->Release();
			_pD3DDev = NULL;
			hr = d3dCreate(); //2.70更新
		}
	}
	if (FAILED(hr))
		return hr;

	//バックバッファーのアドレスを取得
	_pD3DDev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &_pD3DSBack);

	//メイン(ゲーム)画面表示用オフスクリーンサーフェスを作成する
	if (_Magnification == 1)
		a = 512 + 8; //※+8は右端にクリアマージンを書き込み用。Direct3Dでは右端を黒でクリアしておかないとフィルタがそのバイトまで参照するので右端が乱れることがあるようだ。v2.06更新。+1で大丈夫と思うが念のため8の倍数にした。
	else //2倍以上は全て横2倍。速度アップ＋こうすることで適度にバイリニアフィルタが掛かりいい感じになる
		a = 512 * 2 + 8; //※+8は右端にクリアマージンを書き込み用。Direct3Dでは右端を黒でクリアしておかないとフィルタがそのバイトまで参照するので右端が乱れることがあるようだ。v2.06更新。+1で大丈夫と思うが念のため8の倍数にした。
	_pD3DDev->CreateOffscreenPlainSurface(a, 256 * _Magnification, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &_pD3DSGame, NULL); //D3DPOOL_DEFAULTにしないとStretchRectが使えない。

	//テキスト表示用オフスクリーンサーフェスを作成する
	_pD3DDev->CreateOffscreenPlainSurface(512 * 2, 67, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &_pD3DSText, NULL);//オーディオグラフのために縦67ドット確保。v2.92更新

	return hr;
}

//Direct3D用スクリーンを初期化します。
BOOL
SCREEND3D_Init(
	Sint32		width,
	Sint32		height,
	Sint32		magnification,
	Uint32		flags)
{
	DEVMODE					dm;
	D3DDISPLAYMODE			dmode;
	D3DADAPTER_IDENTIFIER9	di;
	HRESULT					hr;

	//パラメータを保管
	_Width = width;
	_Height = height;
	_Magnification = magnification;
	_Flags = flags;
	_hWnd = WINMAIN_GetHwnd();

	if (APP_GetWindowsVista()) //Vista以降でDirect3D9Exが使える環境ならば
	{	//v2.21追加
		_hD3D9Dll = LoadLibrary("d3d9.dll"); //Vistaより前のWindowsでエラーを起こさないため、動的にDLLをロードして関数を検索し使用する。
		_FuncDirect3DCreate9Ex = (FuncDirect3DCreate9Ex)GetProcAddress(_hD3D9Dll, "Direct3DCreate9Ex");
		_FuncDirect3DCreate9Ex(D3D_SDK_VERSION, &_pD3DEx);
		_pD3D = _pD3DEx;
	}
	else //XP以前
		_pD3D = Direct3DCreate9(D3D_SDK_VERSION);

	if (_pD3D == NULL)
	{
		MessageBox(_hWnd, "ERROR: DIRECT3D::Direct3DCreate9() failed.    ", "Ootake", MB_OK);
		return FALSE;
	}

	//フルスクリーンの場合、 画面の解像度を設定し、ウィンドウを左上に配置＆画面全域の大きさにする。
	//	※Direct3D9のフルスクリーンモードは、メニューや他のダイアログ表示が正常に出来ないので使えない。手動でフルスクリーンに切り替えてからウィンドウモードを使う。
	//										 タイマーの進み方がおかしくなる不具合も、ウィンドウモードなら回避できる。
	if (flags & SCREEN_FFULLSCREEN) //フルスクリーンモードなら
	{
		if (_bChangedFullScreen == FALSE) //ウィンドウ(デスクトップ)→フルスクリーンへの切り替え時
		{
			//他のアプリのウィンドウの状態を保存しておく。v2.24
			SCREEN_SaveWindowPosition();
			//Windows98/Meの場合、CDS_FULLSCREENが効かなく、デスクトップに戻れないので、手動で元に戻すため変更前の設定を退避しておく。
			if (APP_GetWindows9x())
			{
				memset(&dm, 0, sizeof(DEVMODE));
				dm.dmSize = sizeof(DEVMODE);
				EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
				_PrevWidth = dm.dmPelsWidth;
				_PrevHeight = dm.dmPelsHeight;
				_PrevBitsPerPixel = dm.dmBitsPerPel;
				_PrevFrequency = dm.dmDisplayFrequency;
			}
		}
		memset(&dm, 0, sizeof(DEVMODE));
		dm.dmSize = sizeof(DEVMODE);
		dm.dmPelsWidth = width;
		dm.dmPelsHeight = height;
		dm.dmBitsPerPel = 32;
		dm.dmDisplayFrequency = APP_GetFullVSyncHz();
		dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
		if ((APP_GetResolutionAutoChange()) && (_bChangedFullScreen))
			hr = ChangeDisplaySettings(&dm, 0); //設定を控えずに切替。一部の環境で切替時のもたつきを解消。v2.23。古いRADEONで切り替え時に画像の乱れ解消確認済み。v2.65
		else //通常
			ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
		WINMAIN_SetFullScreenWindow(width, height);//v2.23追加。このタイミングで、ウィンドウスタイルの変更を行うようにした。パワーの無いマシンではDirectX初期化に時間が掛かるのでこのタイミングで画面を整えたほうが良さそう。
		_bChangedFullScreen = TRUE;
	}
	else //ウィンドウモードなら
	{
		if (_bChangedFullScreen) //フルスクリーン→ウィンドウへの切り替え時
		{
			if (APP_GetWindows9x()) //Windows98/Meの場合、CDS_FULLSCREENが効かなく、自動でデスクトップを元の状態に戻せないので、手動で元に戻す。
			{
				memset(&dm, 0, sizeof(DEVMODE));
				dm.dmSize = sizeof(DEVMODE);
				dm.dmPelsWidth = _PrevWidth;
				dm.dmPelsHeight = _PrevHeight;
				dm.dmBitsPerPel = _PrevBitsPerPixel;
				dm.dmDisplayFrequency = _PrevFrequency;
				dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
				ChangeDisplaySettings(&dm, 0);
			}
			else //Windows XP/2000/Vista以降の場合	
				ChangeDisplaySettings(NULL, 0); //デスクトップを元に戻す
			SCREEN_LoadWindowPosition(); //他のアプリのウィンドウ状態を元に戻す。v2.24
			Sleep(1000); //デスクトップに戻る処理が完全に終わるまで念のためにウェイトを入れる。早いとDirecDrawのときのようにDirect3Dのデバイスの作成失敗しそうなので。
		}
		WINMAIN_SetNormalWindow(width, height);//v2.23追加。このタイミングで、ウィンドウスタイルの変更を行うようにした。パワーの無いマシンではDirectX初期化に時間が掛かるのでこのタイミングで画面を整えたほうが良さそう。
		APP_WindowWithinScreen(); //ウィンドウが画面からはみ出していた場合、画面内に収まるように移動する。
		_bChangedFullScreen = FALSE;
	}

	//ディスプレイモードを取得。WindowモードにてVSync可能かどうかの判断を行うために利用
	_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dmode);

	//ディスプレイアダプタ(ビデオチップ)の情報を取得。v2.19
	_pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &di);
	APP_StrToLower(di.Description);
	//PRINTF("VideoCard = %s", di.Description); //Test用
	if ((strstr(di.Description, "radeon ve") != NULL) ||
		(strstr(di.Description, "radeon 2")  != NULL) ||
		(strstr(di.Description, "radeon 3")  != NULL) ||
		(strstr(di.Description, "radeon 7")  != NULL) ||
		(strstr(di.Description, "radeon 8")  != NULL) ||
		(strstr(di.Description, "radeon 90") != NULL) ||
		(strstr(di.Description, "radeon 92") != NULL) ||
		(strstr(di.Description, "wonder ve") != NULL) ||
		(strstr(di.Description, "wonder 7")  != NULL) ||
		(strstr(di.Description, "wonder 90") != NULL) ||
		(strstr(di.Description, "wonder 92") != NULL))
	{
		//PRINTF("Old! VideoCard = %s ", di.Description); //Test用
		_bOldVideoChip = TRUE; //古いビデオチップ
	}
	if (strstr(di.Description, "intel(r) hd graphics") != NULL)
	{
		//PRINTF("Intel! VideoChip = %s ", di.Description); //Test用
		_bIntelVideoChip = TRUE; //インテルのIrisより前のローパワー内蔵ビデオチップ。
	}

	//Direct3Dデバイスを作成。メニューやダイアログ表示のため、フルスクリーン時もウィンドウデバイスとして作成する。
	ZeroMemory(&_pD3DParam, sizeof(_pD3DParam));
	_pD3DParam.hDeviceWindow = _hWnd;
	_pD3DParam.BackBufferWidth = width;
	_pD3DParam.BackBufferHeight = height;
	_pD3DParam.BackBufferFormat = D3DFMT_X8R8G8B8; //32ビットカラー専用
	_pD3DParam.BackBufferCount = 1;
	_pD3DParam.Windowed = TRUE; //Windowモード。参考：WinodwedがTRUEのときにリフレッシュレートを設定するとCreateエラー
	if ((APP_GetVSyncScreen()) &&
		((dmode.RefreshRate == 60) || (dmode.RefreshRate == 59) || (dmode.RefreshRate == 61) ||
			(dmode.RefreshRate == 120) || (dmode.RefreshRate == 119) || (dmode.RefreshRate == 121) ||
			(APP_GetWindowsAutoVSyncBool()))) //リフレッシュレートが60Hz(59と61も含む)か120Hz、またはWindows自動V-Sync設定のときだけVSyncをOKに。
	{	//V-Syncする場合
		_pD3DParam.SwapEffect = D3DSWAPEFFECT_DISCARD; //COPYより軽い
		_pD3DParam.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		if (APP_GetWindowsAutoVSyncBool()) //v2.65追加
			_pD3DParam.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; //D3DPRESENT_INTERVAL_DEFAULT。D3DPRESENT_INTERVAL_ONEとほぼ同じ動作（表示遅延あり）だが処理が軽いようで遅延少なめ。だがしかしドライバで垂直同期オフにして手動VSyncをするほうが遅延少ないので現在非使用。v2.94
																		   //古いRADEON機はV-Syncお知らせタイミングがアバウトなようなので640x480フル時にはこれ(Windowsの自動V-Sync)を使うしかなさそう。v2.65
		//D3DSWAPEFFECT_FLIPについて…D3DSWAPEFFECT_DISCARD(COPY)にして手動でVSyncを取るよりも、メモリ転送処理が省かれるためだいぶCPUへの負荷は軽いが
		//							  処理のタイミングが遅いときがあって処理落ち(＆ノイズ載り)がしやすいようなので、現状はDISCARD+IMMEDIATEで行く。
		//_pD3DParam.SwapEffect = D3DSWAPEFFECT_FLIP;
		//_pD3DParam.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
	}
	else //V-Syncしない(orできない)場合
	{
		_pD3DParam.SwapEffect = D3DSWAPEFFECT_DISCARD; //COPYより軽い
		_pD3DParam.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	}
	_pD3DParam.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	hr = d3dDevReset(); //作成
	if (FAILED(hr))
	{
		MessageBox(_hWnd, "ERROR: DIRECT3D::CreateDevice() failed.    ", "Ootake", MB_OK);
		SCREEND3D_Deinit();
		return FALSE;
	}

	//ガンマ（明るさ調整）を計算済みのテーブルを用意。
	SCREEN_SetGamma(APP_GetScanlineType(), APP_GetScanlineDensity());

	_LastTimeSyncTime = APP_QPCtimeGetTime1000(); //前回Syncしたときのタイマーカウント
	_LastTimeSyncTime3 = _LastTimeSyncTime; //v2.43追加
	SCREEND3D_InitAudioGraph(); //オーディオグラフ表示用のバッファをクリア。v2.92

	//画面をクリア
	SCREEND3D_Clear(0, 0, 0);

	return TRUE;
}


//オーディオグラフ表示用のバッファをクリアする。v2.92追加
void
SCREEND3D_InitAudioGraph()
{
	int	i;

	for (i = 0; i < 6; i++)
	{
		_PSGChannelStatus[i] = 0;
		_PSGSampleL[i] = 0;
		_PSGSampleR[i] = 0;
	}
	_ADPCMSample = 0;
	_CDDASampleL = 0;
	_CDDASampleR = 0;
}


//スクリーンモードを変更します。 
BOOL
SCREEND3D_ChangeMode(
	Sint32		width,
	Sint32		height,
	Sint32		magnification,
	Uint32		flags)
{
	SCREEND3D_Deinit();
	return SCREEND3D_Init(width, height, magnification, flags);
}

//垂直帰線期間を待ちます。(VSync処理)
BOOL
SCREEND3D_WaitVBlank(
	BOOL	bDraw) //bDrawをTRUEにして呼ぶと描画も行う。FALSEの場合VSync待ちのみ。
{
	Sint64				t, vt;
	D3DRASTER_STATUS	rs;
	HRESULT				hr;
	RECT				rc;
	UINT				bt, bt2;
	UINT				cy, cy2;
	UINT				gh;
	Sint32				top;
	Sint32				h2;
	Sint32				vSyncHz = MAINBOARD_GetVSyncHz();

	if (APP_GetWindowsAutoVSyncBool()) //v2.65追加。明示的にWindowsの自動V-Syncを使うモードを設置。表示遅延が起こるので非推奨だが、通常のV-Syncがうまく行かないPC環境のために実装。
	{
		//Waitする前に描画処理を済ませる。
		if (bDraw)
		{
			hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Direct3Dは、SwapEffectをVSync待ちするものに設定すると、Present実行時に自動でVSync待ちもしてくれる。※自動VSyncだと処理が重いときがあるので現状は手動で待ち。
			UpdateWindow(_hWnd); //遅延対策。v2.89
		}
		return TRUE;
	}

	//Vista以降で"Aero"か"Vistaスタンダード(Home Basicにのみ存在)"，"Win8/10"のデスクトップ利用時は、Windows自体でV-Syncを自動で行うので、処理がぶつかって遅くならないようにOotake側はV-Syncに構わず、先に描画を済ませる。v2.02
	if (APP_DwmIsCompositionEnabled())
	{
		//Waitする前に描画処理を済ませる。
		if (bDraw)
		{
			//描画処理
			hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Direct3Dは、SwapEffectをVSync待ちするものに設定すると、Present実行時に自動でVSync待ちもしてくれる。※自動VSyncだと処理が重いときがあるので現状は手動で待ち。
			if (hr == D3DERR_DEVICELOST)
			{
				d3dDevReset();
				_pD3DDev->Present(NULL, NULL, NULL, NULL);
			}
			UpdateWindow(_hWnd); //遅延対策。v2.89
			if (APP_GetRemoveDisplayLagFrame() >= 1) //表示ラグ削減モード（デフォルト）の場合。v2.89
			{
				hr = _pD3DDev->Present(NULL, NULL, NULL, NULL);
				UpdateWindow(_hWnd); //遅延対策。v2.89
				if (APP_GetRemoveDisplayLagFrame() >= 3) //表示ラグ削減モード3F（デフォルト）の場合
				{	//Aero(&Win8/10)の場合、レンダリングバッファがもう2フレームぶんあるのでそれも消化する
					hr = _pD3DDev->Present(NULL, NULL, NULL, NULL);
					UpdateWindow(_hWnd); //遅延対策。v2.89
					hr = _pD3DDev->Present(NULL, NULL, NULL, NULL);
					UpdateWindow(_hWnd); //遅延対策。v2.89
				}
			}
			bDraw = FALSE; //描画処理完了
		}
	}

	if (_bVSyncScreen)
	{
		if (vSyncHz == 240) //v2.94追加。v2.98更新
			vt = 4167;
		else if (vSyncHz == 120)
			vt = 8333;
		else
			vt = 16667;
		t = APP_QPCtimeGetTime1000() - _LastTimeSyncTime;
		if (t <= vt) //16.667ms(1/60s)以上経過していた場合は待たない
		{
			//他のアプリのために処理を一休み。※VBlank待ちループ中はCPU占有率が100%になるので、これをやると省電力。＆Sleepを入れない場合、連続してWaitする場合に処理されないことがあるので、これが必要。
			if (vt == 16667)
			{
				t = 16667 - t - 200; //-200は処理落ちしないためのマージン。-2だとGTX1060の省電力動作(デフォルト)時に処理落ちあり。引かな過ぎるとテンポの揺らぎからわずかに音が篭る気がする。16=１コマの時間約16ms。v2.94
								//マイナス値が大きいとCDDAタイミング測定時(IntelAtom640x480fullscreen環境で確認)にSleepが短すぎて連続のVSyncがカットされてしまうことがあった。-12NG
				if ((t >= 1) && (t <= 1667)) //タイマーカウンタのオーバーフローも考えて、この範囲のときだけSleepをおこなう。
					APP_QPCSleep1000(t);
			}
			else if (vt == 8333)
			{
				t = 8333 - t - 400; //-400は処理落ちしないためのマージン。8=１コマの時間約8ms。v2.94更新
				if ((t >= 1) && (t <= 8333))
					APP_QPCSleep1000(t);
			}
			else //(vt == 4167) for 240Hz
			{
				t = 4167 - t - 1600; //-800は処理落ちしないためのマージン。4167=１コマの時間約4.167ms。v3.05
				if ((t >= 1) && (t <= 4167))
					APP_QPCSleep1000(t);
				//APP_QPCSleep1000(957); //956OK。955だとまだ速いかも。941-950-980OK。940NG。240Hzの場合時間の余裕がなく処理落ちの恐れがあるので最低限のウェイトだけ入れる
			}
			//待つ。GetRasterStatusメソッドで垂直同期を取る。
			//		ウィンドウが最小化されている時はなぜかPresentによるVSyncが効かない(環境にもよるかもしれないがDirect3D9の仕様っぽい)ので、手動でVSync処理を行う。
			cy = (UINT)GetSystemMetrics(SM_CYSCREEN);
			cy2 = cy - (cy / 6); //縦1200のディスプレイの場合で-200ライン(1200/6)までチェック。1ラインだけの判定だとSyncを見逃すことがあるので、マージンぶん。-300だとLightPSG時に２回VSyncしてしまう。マイナスが少ないとSync逃すことがありテンポやや乱れ。v2.92更新
			if (_Flags & SCREEN_FFULLSCREEN)
			{	//フルスクリーンの時は0ラインでVSyncさせる。
				//画面上部の隙間(＝下部の隙間)を計算
				if (APP_GetFullStretched(TRUE))
				{
					//アスペクト比を4:3に保つ
					if (_Width / 4 >= _Height / 3)
					{	//縦のほうが短いので縦を基準とする
						if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
							top = (_Height - (Sint32)((double)_Height * 0.96137 + 0.5)) / 2; //0.96137=224/((268-256)/4*3+224)
						else
							top = 0;
					}
					else
					{	//横のほうが短いので横を基準とする
						h2 = (Sint32)((double)(_Width / 4.0 * 3.0 + 0.5));
						if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
							top = (_Height - (Sint32)((double)h2 * 0.96137 + 0.5)) / 2; //0.96137=224/((268-256)/4*3+224)
						else
							top = (_Height - h2) / 2;
					}
				}
				else if (APP_GetStretched())
				{
					//アスペクト比をTV画面と同じ4:3に。縦の長さををPCE標準224の整数倍(２倍)にする
					top = (_Height - 224 * _Magnification) / 2;
					if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
					{
						top -= MAINBOARD_GetShowOverscanTop() * _Magnification;
						if (top < 0) //画面からはみ出してしまう場合。1440x900,1600x900などであり得る。
							top = (_Height - 224 * _Magnification) / 2; //アスペクト比を保つため、オーバースキャン部をカット。
					}
				}
				else //ノンストレッチの場合
				{
					top = (_Height - 224 * _Magnification) / 2;
					if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
					{
						top -= MAINBOARD_GetShowOverscanTop() * _Magnification;
						if (top < 0) top = 0; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。1440x900,1600x900などであり得る。
					}
				}
				bt = cy - top;
			}
			else if (IsIconic(_hWnd))
			{	//ウィンドウが最小化されている時は最下256ラインでVSyncさせる。
				bt = cy2;
			}
			else
			{	//ウィンドウモードの場合、ウィンドウ表示領域最下ラインを越えた所でSyncさせる。こうしないと画面上部でのSyncが間に合わない。v2.54
				GetWindowRect(_hWnd, &rc);
				bt = (UINT)rc.bottom - GetSystemMetrics(SM_CYFRAME);//v2.92更新
			}
			bt2 = bt + (cy - cy2);
			GetClientRect(_hWnd, &rc);
			gh = rc.bottom - rc.top;//ゲーム表示画面の縦幅。v2.92
			//PRINTF("Test bt=%d bt2=%d cy=%d cy2=%d gh=%d", bt, bt2, cy, cy2, gh);//Test用
			while (TRUE)
			{
				_pD3DDev->GetRasterStatus(0, &rs);
				if (bt <= cy2)
				{
					if ((rs.ScanLine >= cy2) || (rs.ScanLine == 0) || (rs.InVBlank)) //スキャンラインの判定もしたほうがSyncを逃さずテンポ安定。v2.54
						break; //VBlank期間に入ったらループを抜け出す。
				}
				else //ウィンドウが下のほうにある場合,もしくはフルスクリーンモード
				{
					if ((APP_GetVSyncAdjust() > 0) && (((cy - bt) + (bt - gh) < cy / 3))) //縦の空いた部分(ウィンドウの無い部分)が画面全体の1/3を下回っていた場合（フルスクリーンまたは、フルHDディスプレイでx5ウィンドウ等）、転送処理がVBlank期間内に終わらない可能性があるので、早めに転送処理を開始することでティアリングを防ぐ。v2.92。v2.94
					{															//										画面全体の1/4だとx4サイズの時にこれが発動せず上辺のティアリングが頻繁に起こった。
						if ((rs.InVBlank) || (rs.ScanLine == 0) || (rs.ScanLine >= bt - (cy / 20)) || (rs.ScanLine <= bt2 - cy)) //20。19だと下側が乱れる。(cy(Windows画面の縦幅)/20)のライン分早めにVBlank期間にやる予定だった処理を開始する。v2.92更新
							break; //VBlank期間に入ったのでループを抜け出す。
					}
					else //通常
					{
						if ((rs.InVBlank) || (rs.ScanLine == 0) || (rs.ScanLine >= bt) || (rs.ScanLine <= bt2 - cy))
							//if ((rs.InVBlank)||(rs.ScanLine == 0)||(rs.ScanLine >= 1148-50))//test。-50OK。-45NG
							//if ((rs.InVBlank)||(rs.ScanLine == 0)||(rs.ScanLine >= 1148-50))//test。-65OK。-70NG
							break; //VBlank期間に入ったのでループを抜け出す。
					}
				}
				t = APP_QPCtimeGetTime1000() - _LastTimeSyncTime;
				if (vSyncHz == 240) //v2.98更新
				{
					if ((t > 4167) || (t < 0)) //4.167msより経過していたら。orカウンタがオーバーフロー
						break;
				}
				else if (vSyncHz == 120)
				{
					if ((t > 8333) || (t < 0)) //8.333msより経過していたら。orカウンタがオーバーフロー
						break;
				}
				else
				{
					if ((t > 16667) || (t < 0)) //16.67msより経過していたら。orカウンタがオーバーフロー
						break;
				}
			}
		}
		_LastTimeSyncTime = APP_QPCtimeGetTime1000(); //前回のSyncが終わったタイムとして記録しておく
		if (bDraw)
		{
			//描画処理
			hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Direct3Dは、SwapEffectをVSync待ちするものに設定すると、Present実行時に自動でVSync待ちもしてくれる。※自動VSyncだと処理が重いときがあるので現状は手動で待ち。
			if (hr == D3DERR_DEVICELOST)
			{
				d3dDevReset();
				_pD3DDev->Present(NULL, NULL, NULL, NULL);
			}
			UpdateWindow(_hWnd); //遅延対策。v2.89
			//即時にレンダリングバッファを消化するために、同じ画面をもう一度描画処理する。遅延解消。v2.88追加
			if (APP_GetRemoveDisplayLagFrame() >= 1) //表示ラグ削減モード（デフォルト）の場合
			{
				hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //今表示したいものを最前面にするため、更に同じ内容でバッファを更新。1フレーム遅延削減効果あり
				UpdateWindow(_hWnd); //遅延対策。v2.89
				//if (APP_GetRemoveDisplayLagFrame() >= 3) //表示ラグ削減モード3F（デフォルト）の場合。※Aero/8/10の場合、先に描画処理が完了しているためここはカット。非Aeroの場合もこの処理は行わないのでカット。
				//{
				//	hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Aero/8/10のみ
				//	UpdateWindow(_hWnd); //遅延対策。v2.89
				//	hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Aero/8/10のみ
				//	UpdateWindow(_hWnd); //遅延対策。v2.89
				//}
			}
		}
		return TRUE;
	}
	else //_bVSyncScreen が FALSEの場合。v3.05からWindows11のデフォルトにした（Win11はテアリングを自動で抑えてくれるので）。
	{
		t = APP_QPCtimeGetTime1000() - _LastTimeSyncTime;
		if (t <= 16723) //16.723ms(16.667x60.0/59.4s)以上経過していた場合は待たない
		{
			//待つ
			t = 16723 - t; //16723=１コマの時間。60.0HzでPCE実機の59.8Hz相当となるようにする。v3.05
			if ((t >= 1) && (t <= 16723)) //タイマーカウンタのオーバーフローも考えて、この範囲のときだけSleepをおこなう。
				APP_QPCSleep1000(t);
		}
		_LastTimeSyncTime = APP_QPCtimeGetTime1000(); //前回のSyncが終わったタイムとして記録しておく

		if (bDraw)
		{
			//描画処理
			hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //PresentしてもVSync待ちせず描画だけする設定になっているので、待たずに描画される。
			if (hr == D3DERR_DEVICELOST)
			{
				d3dDevReset();
				_pD3DDev->Present(NULL, NULL, NULL, NULL);
			}
			UpdateWindow(_hWnd); //遅延対策。v2.89
		}
		return TRUE;
	}
}


void*
SCREEND3D_GetBuffer()
{
	return (void*)_pScreenPixels;
}


const Sint32
SCREEND3D_GetBufferPitch()
{
	return _SurfacePitch / 2;
}


//メイン(ゲーム)画面用オフスクリーンをロックして、書き込み(描画)用アドレスを設定する。v2.13更新。32bit単位で扱うようにした。
static BOOL
lock_offscreen_surface(
	LONG* pPitch,
	Uint32** ppPixels)
{
	D3DLOCKED_RECT	pLockedRect;
	HRESULT			hr;

	hr = _pD3DSGame->LockRect(&pLockedRect, NULL, 0);
	if (hr == D3D_OK)
	{
		*pPitch = pLockedRect.Pitch >> 2; //横１ラインのバイト数。32bit単位で扱うために4で割っておく。v2.13更新
		*ppPixels = (Uint32*)pLockedRect.pBits;
		return TRUE;
	}
	else
		return FALSE;
}


//画面全体をクリアします。
void
SCREEND3D_Clear(
	byte	r,
	byte	g,
	byte	b) //v2.73更新。任意の色でクリアできるようにした。
{
	HRESULT		hr;

	_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(r, g, b), 1.0, 0);
	hr = _pD3DDev->Present(NULL, NULL, NULL, NULL); //Presentを実行しないと反映されない。
	if (hr == D3DERR_DEVICELOST)
	{
		d3dDevReset();
		_pD3DDev->Present(NULL, NULL, NULL, NULL);
	}
}


//テキストメッセージを設定します。
void
SCREEND3D_SetMessageText(
	char* pText)
{
	_pMessageText = pText;
}


//センターテキストメッセージを設定します。
void
SCREEND3D_SetCenterText(
	char* pText)
{
	_pCenterText = pText;
}


//テキストメッセージの表示処理
static void
print_message()
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	int			a, fs;

	if (!APP_GetInit())
		return; //最初の初期化が全て完了していない場合は、表示処理を行わない。CD-ROMゲームをレジュームしたときにメッセージ欄がごちゃつくのを回避。v1.67

	//デバイスコンテキストを取得
	if (_pD3DSText->GetDC(&dc) != D3D_OK)
		return;

	//フォントサイズを決定
	switch (_Magnification)
	{
	case 1: fs = 12; break;
	case 2:	fs = 16; break;
	case 3: fs = 12; break; //v2.44更新
	default: //4,5
		fs = 17; break; //v2.44更新
	}
	hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	GetObject(hFont, sizeof(LOGFONT), &logFont);
	logFont.lfHeight = fs;
	logFont.lfWidth = 0;
	hFont = CreateFontIndirect(&logFont);
	prevFont = (HFONT)SelectObject(dc, hFont);
	//文字列をバッファに描画
	rc.left = 0;
	rc.right = 0;
	rc.top = 0;
	rc.bottom = fs;
	SetBkColor(dc, RGB(48, 96, 48));//濃い緑
	SetTextColor(dc, RGB(255, 255, 255));//白
	//SetTextColor(dc, RGB(216,255,216));//薄い緑
	DrawText(dc, _pMessageText, lstrlen(_pMessageText), &rc,
		DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX); //表示シミュレーション。rcに表示したときのサイズが入る。
	DrawText(dc, _pMessageText, lstrlen(_pMessageText), &rc, DT_SINGLELINE | DT_NOPREFIX); //表示

	SelectObject(dc, prevFont);
	DeleteObject(hFont);
	_pD3DSText->ReleaseDC(dc);

	//転送元位置＆サイズの調整
	rcSrc.left = 0;
	rcSrc.top = 0;
	rcSrc.right = rc.right;
	rcSrc.bottom = fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
		rcDst.left = _GameLeft; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
	else //通常
		rcDst.left = _GameLeft + 3;
	if ((APP_GetOverscanHideBlackTop()) && (MAINBOARD_GetShowOverscanTop() < 8)) //上側のオーバースキャン領域に黒帯を付けるなら
	{
		rcDst.top = _GameTop; //黒帯があるのでギリギリまで上に寄せても見栄えOK。
		a = (8 - MAINBOARD_GetShowOverscanTop()) * _Magnification;
		if (a < 4) //黒帯部分が4ドット未満なら
			rcDst.top += (4 - a);
	}
	else //通常
		rcDst.top = _GameTop + 4;
	if (_Magnification >= 3)
	{
		rcDst.right = rcDst.left + rcSrc.right * 2; //横幅２倍拡大
		rcDst.bottom = rcDst.top + fs * 2; //縦幅２倍拡大
	}
	else
	{
		rcDst.right = rcDst.left + rcSrc.right;
		rcDst.bottom = rcDst.top + fs;
	}
	if (rcDst.right > _GameRight) //メッセージが長すぎてはみ出てしまう場合
	{
		rcSrc.right = (LONG)((double)rcSrc.right / ((double)rcDst.right / (double)_GameRight)); //ソースを小さくする
		rcDst.right = _GameRight;
	}
	//転送
	_pD3DDev->StretchRect(_pD3DSText, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //文字はクッキリさせるためNONEを使う。
}


//FPSの表示処理
static void
print_fps()
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	int			fs;
	char		text[3 + 1]; //一応３桁まで用意
	int			a;

	//デバイスコンテキストを取得
	if (_pD3DSText->GetDC(&dc) != D3D_OK)
		return;

	//フォントサイズを決定
	switch (_Magnification)
	{
	case 1: fs = 12; break;
	case 2:	fs = 16; break;
	case 3: fs = 12; break; //v2.44更新
	default: //4,5
		fs = 17; break; //v2.44更新
	}
	hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	GetObject(hFont, sizeof(LOGFONT), &logFont);
	logFont.lfHeight = fs;
	logFont.lfWidth = 0;
	hFont = CreateFontIndirect(&logFont);
	prevFont = (HFONT)SelectObject(dc, hFont);
	//文字列をバッファに描画
	rc.left = 0;
	rc.right = 0;
	rc.top = 0;
	rc.bottom = fs;
	SetBkColor(dc, RGB(48, 96, 48));
	SetTextColor(dc, RGB(255, 255, 255));
	a = MAINBOARD_GetDisplayedFrames();
	if (a > 999) a = 999;
	sprintf(text, "%d", a);
	DrawText(dc, text, lstrlen(text), &rc,
		DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX); //表示シミュレーション。rcに表示したときのサイズが入る。
	DrawText(dc, text, lstrlen(text), &rc, DT_SINGLELINE | DT_NOPREFIX); //表示

	SelectObject(dc, prevFont);
	DeleteObject(hFont);
	_pD3DSText->ReleaseDC(dc);

	//転送元位置＆サイズの調整
	rcSrc.left = 0;
	rcSrc.top = 0;
	rcSrc.right = rc.right;
	rcSrc.bottom = fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
		rcDst.left = _GameLeft; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
	else //通常
		rcDst.left = _GameLeft + 3;
	if ((APP_GetOverscanHideBlackBottom()) && (MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
	{
		rcDst.bottom = _GameBottom; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
		a = (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification;
		if (a < 3) //黒帯部分が3ドット未満なら
			rcDst.bottom -= (3 - a);
	}
	else //通常
		rcDst.bottom = _GameBottom - 3; //下側に表示
	if (_Magnification >= 3)
	{
		rcDst.right = rcDst.left + rcSrc.right * 2; //横幅２倍拡大
		rcDst.top = rcDst.bottom - fs * 2; //縦幅２倍拡大
	}
	else
	{
		rcDst.right = rcDst.left + rcSrc.right;
		rcDst.top = rcDst.bottom - fs;
	}
	//転送
	_pD3DDev->StretchRect(_pD3DSText, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //文字はクッキリさせるためNONEを使う。
}


//オーディオグラフの表示処理。v2.92
static void
print_audio_graph()
{
	HDC			dc;
	RECT		rc;
	//RECT		rc2;
	RECT		rcSrc;
	RECT		rcDst;
	int			gw, gh, bw, cd;
	int			a, i;
	HBRUSH		hBr0, hBr1, hBr2, hBr3, hBr4, hBr5, hBr6;

	//デバイスコンテキストを取得
	if (_pD3DSText->GetDC(&dc) != D3D_OK)
		return;

	//グラフサイズを決定
	switch (_Magnification)
	{
	case 1: gw = 36; gh = 29; break;
	case 2:	gw = 48; gh = 38; break;
	case 3: gw = 60; gh = 48; break;
	default: //4,5
		gw = 84; gh = 67; break;
	}
	if (APP_GetCDGame())
	{
		gw = gw + (gw / 3); //CDゲームの場合は横２列分（ADPCM＆CD-DA）のグラフを増やす
		cd = 2;
	}
	else
		cd = 0;

	//転送元位置＆サイズの調整
	rcSrc.left = 0;
	rcSrc.top = 0;
	rcSrc.right = gw + (7 + cd);//7=グラフの隙間5+両端2
	rcSrc.bottom = gh;
	hBr0 = CreateSolidBrush(RGB(48, 96, 48));//濃い緑。背景
	hBr1 = CreateSolidBrush(RGB(216, 255, 216));//薄い緑。通常の音
	hBr2 = CreateSolidBrush(RGB(255, 255, 56));//薄い黄色。DDAサンプリングの音
	hBr3 = CreateSolidBrush(RGB(255, 200, 200));//薄いピンク。ノイズの音
	hBr4 = CreateSolidBrush(RGB(255, 148, 0));//オレンジ色。LFO変調機能を利用したチャンネル0の音。はにいいんざすかい、獣王記（Huカード版）とフラッシュハイダースで利用されている。
											  //            LFO。ヴォルフィードでも使われていたのをv2.92のこの機能で発見！
	hBr5 = CreateSolidBrush(RGB(248, 248, 248));//白に近い灰色。CD-ROM2のADPCM音
	hBr6 = CreateSolidBrush(RGB(216, 208, 255));//薄い青紫色。CD-ROM2のCD-DA音
	//グラフをバッファに描画
	FillRect(dc, &rcSrc, hBr0);
	if (APP_GetAudioGraphDelay() == 0) //オーディオグラフの表示を遅らせない設定の場合、PSGの状態を今現在の状態に更新する
	{
		for (i = 0; i < 6; i++)
		{
			_PSGChannelStatus[i] = PSG_GetChannelStatus(i);
			_PSGSampleL[i] = PSG_GetSampleL(i);
			_PSGSampleR[i] = PSG_GetSampleR(i);
		}
		_ADPCMSample = ADPCM_GetSample();
		_CDDASampleL = CDROM_GetCDDASampleL();
		_CDDASampleR = CDROM_GetCDDASampleR();
	}
	bw = gw / (12 + cd * 2);
	rc.left = 1;
	rc.top = 0;
	rc.right = 1 + bw;
	rc.bottom = gh;
	for (i = 0; i < 6; i++)
	{
		rc.top = (LONG)(gh - abs(_PSGSampleL[i]) / (double)(9000.0 * (48.0 / (double)gh)));//ビックリマンワールドのタイトル画面の音楽（音量大きい）で振り切れないように合わせた。
		if (rc.top < 0)	rc.top = 0;//グラフがMAXに振り切れてた場合
		switch (_PSGChannelStatus[i])
		{
		case 1: FillRect(dc, &rc, hBr1); break;
		case 2:
			if ((_PSGSampleL[i] != PSG_GetSampleL(i)) || (_PSGSampleR[i] != PSG_GetSampleR(i))) //DDAサンプリングモードの場合、波形に変化が無い場合は無音扱いとする。ワールドスタジアム'91やスト２で黄色いグラフが残るのを回避。v2.95
				FillRect(dc, &rc, hBr2);
			break;
		case 3: FillRect(dc, &rc, hBr3); break;
		case 4: FillRect(dc, &rc, hBr4); break;
		}
		//rc2.left = rc.left;
		//rc2.top = rc.top;
		//rc2.right = rc2.left + 1;
		//rc2.bottom = rc2.top + 1;
		//FillRect(dc, &rc2, hBr0);//グラフの左角を丸める
		rc.left += bw;
		rc.right += bw;
		rc.top = (LONG)(gh - abs(_PSGSampleR[i]) / (double)(9000.0 * (48.0 / (double)gh)));//ビックリマンワールドのタイトル画面の音楽（音量大きい）で振り切れないように合わせた。
		if (rc.top < 0)	rc.top = 0;
		switch (_PSGChannelStatus[i])
		{
		case 1: FillRect(dc, &rc, hBr1); break;
		case 2:
			if ((_PSGSampleL[i] != PSG_GetSampleL(i)) || (_PSGSampleR[i] != PSG_GetSampleR(i))) //DDAサンプリングモードの場合、波形に変化が無い場合は無音扱いとする。ワールドスタジアム'91やスト２で黄色いグラフが残るのを回避。v2.95
				FillRect(dc, &rc, hBr2);
			break;
		case 3: FillRect(dc, &rc, hBr3); break;
		case 4: FillRect(dc, &rc, hBr4); break;
		}
		//rc2.left = rc.left + 5;
		//rc2.top = rc.top;
		//rc2.right = rc2.left + 1;
		//rc2.bottom = rc2.top + 1;
		//FillRect(dc, &rc2, hBr0);//グラフの右角を丸める
		rc.left += bw + 1;
		rc.right += bw + 1;
	}
	rc.top = (LONG)(gh - abs(_ADPCMSample) / (double)(200.0 * (48.0 / (double)gh)));
	if (rc.top < 0)	rc.top = 0;
	rc.right += bw;
	FillRect(dc, &rc, hBr5);
	rc.left += bw + bw + 1;
	rc.right += bw + 1;
	rc.top = (LONG)(gh - abs(_CDDASampleL) / (double)(200.0 * (48.0 / (double)gh)));
	if (rc.top < 0)	rc.top = 0;
	FillRect(dc, &rc, hBr6);
	rc.left += bw;
	rc.right += bw;
	rc.top = (LONG)(gh - abs(_CDDASampleR) / (double)(200.0 * (48.0 / (double)gh)));
	if (rc.top < 0)	rc.top = 0;
	FillRect(dc, &rc, hBr6);
	DeleteObject(hBr0);
	DeleteObject(hBr1);
	DeleteObject(hBr2);
	DeleteObject(hBr3);
	DeleteObject(hBr4);
	DeleteObject(hBr5);
	DeleteObject(hBr6);
	_pD3DSText->ReleaseDC(dc);

	//転送先位置の調整
	switch (APP_GetAudioGraphPos())
	{
	case 1: //左上
		if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
			rcDst.left = _GameLeft; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
		else //通常
			rcDst.left = _GameLeft + 3;
		if ((APP_GetOverscanHideBlackBottom()) && (MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
		{
			rcDst.bottom = _GameBottom; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
			a = (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification;
			if (a < 3) //黒帯部分が3ドット未満なら
				rcDst.bottom -= (3 - a);
		}
		else //通常
			rcDst.top = _GameTop + 3; //上側に表示
		rcDst.right = rcDst.left + rcSrc.right;
		rcDst.bottom = rcDst.top + rcSrc.bottom;
		break;
	case 2: //右上
		if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
			rcDst.right = _GameRight; //黒帯があるのでギリギリまで右に寄せても見栄えOK。
		else //通常
			rcDst.right = _GameRight - 3;
		if ((APP_GetOverscanHideBlackBottom()) && (MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
		{
			rcDst.bottom = _GameBottom; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
			a = (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification;
			if (a < 3) //黒帯部分が3ドット未満なら
				rcDst.bottom -= (3 - a);
		}
		else //通常
			rcDst.top = _GameTop + 3; //上側に表示
		rcDst.left = rcDst.right - rcSrc.right;
		rcDst.bottom = rcDst.top + rcSrc.bottom;
		break;
	case 3: //左下
		if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
			rcDst.left = _GameLeft; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
		else //通常
			rcDst.left = _GameLeft + 3;
		if ((APP_GetOverscanHideBlackBottom()) && (MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
		{
			rcDst.bottom = _GameBottom; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
			a = (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification;
			if (a < 3) //黒帯部分が3ドット未満なら
				rcDst.bottom -= (3 - a);
		}
		else //通常
			rcDst.bottom = _GameBottom - 3; //下側に表示
		rcDst.right = rcDst.left + rcSrc.right;
		rcDst.top = rcDst.bottom - rcSrc.bottom;
		break;
	case 4: //右下
		if ((APP_GetOverscanHideBlackLR()) && (MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
			rcDst.right = _GameRight; //黒帯があるのでギリギリまで右に寄せても見栄えOK。
		else //通常
			rcDst.right = _GameRight - 3;
		if ((APP_GetOverscanHideBlackBottom()) && (MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
		{
			rcDst.bottom = _GameBottom; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
			a = (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification;
			if (a < 3) //黒帯部分が3ドット未満なら
				rcDst.bottom -= (3 - a);
		}
		else //通常
			rcDst.bottom = _GameBottom - 3; //下側に表示
		rcDst.left = rcDst.right - rcSrc.right;
		rcDst.top = rcDst.bottom - rcSrc.bottom;
		break;
	}

	//転送
	_pD3DDev->StretchRect(_pD3DSText, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //グラフはクッキリさせるためNONEを使う。

	//次回に表示するグラフのデータをこの瞬間に取得して置く。つまりデータを１フレーム遅らせて表示する。実際の発声とグラフ表示のタイミングが近づく。
	for (i = 0; i < 6; i++)
	{
		_PSGChannelStatus[i] = PSG_GetChannelStatus(i);
		_PSGSampleL[i] = PSG_GetSampleL(i);
		_PSGSampleR[i] = PSG_GetSampleR(i);
	}
	_ADPCMSample = ADPCM_GetSample();
	_CDDASampleL = CDROM_GetCDDASampleL();
	_CDDASampleR = CDROM_GetCDDASampleR();
}


//画面の中央にメッセージを表示する。v2.77追加
void
print_center_message()
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	int			fs;
	LONG		adjust;

	//デバイスコンテキストを取得
	if (_pD3DSText->GetDC(&dc) != D3D_OK)
		return;

	//フォントサイズを決定
	switch (_Magnification)
	{
	case 1: fs = 12; break;
	case 2:	fs = 16; break;
	case 3: fs = 12; break; //v2.44更新
	default: //4,5
		fs = 17; break; //v2.44更新
	}
	hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	GetObject(hFont, sizeof(LOGFONT), &logFont);
	logFont.lfHeight = fs;
	logFont.lfWidth = 0;
	hFont = CreateFontIndirect(&logFont);
	prevFont = (HFONT)SelectObject(dc, hFont);
	//文字列をバッファに描画
	rc.left = 0;
	rc.right = 0;
	rc.top = 0;
	rc.bottom = fs;
	SetBkColor(dc, RGB(48, 96, 48));
	SetTextColor(dc, RGB(255, 255, 255));
	DrawText(dc, _pCenterText, lstrlen(_pCenterText), &rc,
		DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX); //表示シミュレーション。rcに表示したときのサイズが入る。
	DrawText(dc, _pCenterText, lstrlen(_pCenterText), &rc, DT_SINGLELINE | DT_NOPREFIX); //表示

	SelectObject(dc, prevFont);
	DeleteObject(hFont);
	_pD3DSText->ReleaseDC(dc);

	//転送元位置＆サイズの調整
	rcSrc.left = 0;
	rcSrc.top = 0;
	rcSrc.right = rc.right;
	rcSrc.bottom = fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	rcDst.left = _GameLeft;
	rcDst.top = 0;
	if (_Magnification >= 3)
	{
		rcDst.right = rcDst.left + rcSrc.right * 2; //横幅２倍拡大
		rcDst.bottom = fs * 2; //縦幅２倍拡大
	}
	else
	{
		rcDst.right = rcDst.left + rcSrc.right;
		rcDst.bottom = fs;
	}
	//センタリング
	adjust = (_GameRight - _GameLeft) / 2 - (rcDst.right - rcDst.left) / 2;
	rcDst.left += adjust;
	rcDst.right += adjust;
	adjust = (_GameBottom - _GameTop) / 2 - (rcDst.bottom - rcDst.top) / 2;
	rcDst.top += adjust;
	rcDst.bottom += adjust;
	//転送
	_pD3DDev->StretchRect(_pD3DSText, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //文字はクッキリさせるためNONEを使う。
}


//描画用バックサーフェスへ転送する処理など。
static void
zoom_d3dTensou(
	Uint16* pTvW,	//転送元の横ピクセル数。※[0]〜[255]の16+224+16(下部のOverscan)ラインぶん。ここには表示開始(例.オーバースキャン表示しない場合は[16]になる)のアドレスを入れる。
	Sint32		h,	//転送元の縦ピクセル数。通常は224。上下のオーバースキャンをする場合そのぶん増やしてここを呼ぶ。
	Sint32		executeCode)
{
	RECT		rcSrc;
	RECT		rcDst;
	Sint32		a, b, c, d, s, i, w2, h2;
	BOOL		bRasterChange = FALSE; //ラスタごとに異なる解像度が設定されていた場合TRUE
	Uint16* pTvW2;
	Sint32		ot = 16 - MAINBOARD_GetShowOverscanTop(); //転送元の開始Y座標
	Sint32		wMag; //横方向のソース倍率
	Sint32		hMag; //縦方向のソース倍率
	Sint32		scanlineType;
	Sint32		tvWidth = VDC_GetTvWidth(); //PCE画面描画完了時点での横解像度モード。v2.98

	if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
	{
		h -= 4; //縦の下側4ドットをカット
		//上側をカットする設定の場合、転送元座標を調整する。
		ot += APP_GetOverscanTvShift();
		pTvW += APP_GetOverscanTvShift();
	}

	//転送元の横幅調整 
	pTvW2 = pTvW;
	s = (*pTvW2);
	//PRINTF("TvW2 = %d",s);//test
	for (i = 0; i < h; i++)
	{
		if ((*pTvW2) != s)
		{
			bRasterChange = TRUE;
			if ((*pTvW) < s)
				s = (*pTvW); //s=一番小さい横幅。フルストレッチ時にごみが出ないように。
		}
		pTvW2++;
	}

	//転送先の位置調整 
	if (_Flags & SCREEN_FFULLSCREEN)
	{
		if (APP_GetFullStretched(TRUE))
		{
			//アスペクト比を4:3に保つ
			if (_Width / 4 >= _Height / 3)
			{	//縦のほうが短いので縦を基準とする
				w2 = (Sint32)((double)(_Height / 3.0 * 4.0 + 0.5));
				a = (_Width - w2) / 2;
				if ((MAINBOARD_GetShowOverscanLeft() > 0) && (tvWidth != 512) && (executeCode != 3)) //左右のオーバースキャン領域を表示するなら
					b = (_Height - (Sint32)((double)_Height * 0.96137 + 0.5)) / 2; //0.96137=224/((274-262)/4*3+224)
				else
					b = 0;
				c = _Width - a;
				d = _Height - b;
			}
			else
			{	//横のほうが短いので横を基準とする
				h2 = (Sint32)((double)(_Width / 4.0 * 3.0 + 0.5));
				a = 0;
				if ((MAINBOARD_GetShowOverscanLeft() > 0) && (tvWidth != 512) && (executeCode != 3)) //左右のオーバースキャン領域を表示するなら
					b = (_Height - (Sint32)((double)h2 * 0.96137 + 0.5)) / 2; //0.96137=224/((274-262)/4*3+224)
				else
					b = (_Height - h2) / 2;
				c = _Width;
				d = _Height - b;
			}
			//フルストレッチ時は上下のオーバースキャン領域を表示できないため、上下のオーバースキャン領域をカット。
			h -= MAINBOARD_GetShowOverscanTop() + MAINBOARD_GetShowOverscanBottom();
			ot = 16;
			bRasterChange = FALSE; //フルストレッチ時はラスタごとの解像度変更ができない。
		}
		else if (APP_GetStretched())
		{
			if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
				w2 = 292 * _Magnification; //v3.01
			else if ((MAINBOARD_GetShowOverscanLeft() > 0) && (executeCode != 3)) //左右のオーバースキャン領域を表示するなら
				w2 = 320 * _Magnification;
			else
				w2 = 298 * _Magnification; //アスペクト比固定のため横は298(2倍時596)の表示範囲。
			if (APP_GetVStretched()) //縦画面モードなら
				w2 = (Sint32)((double)w2 * TATEGAMEN + 0.5);
			//アスペクト比をTV画面と同じ4:3に。縦の長さををPCE標準224の整数倍(２倍)にする
			a = (_Width - w2) / 2;
			b = (_Height - 224 * _Magnification) / 2;
			c = a + w2;
			d = b + 224 * _Magnification;
			if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
			{
				b += 2 * _Magnification;
				d -= 2 * _Magnification;
			}
			else
			{
				if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
				{
					b -= MAINBOARD_GetShowOverscanTop() * _Magnification;
					if (b < 0) //画面からはみ出してしまう場合。1440x900,1600x900などであり得る。
					{	//アスペクト比を保つため、オーバースキャン部をカット。
						b = (_Height - 224 * _Magnification) / 2;
						h -= MAINBOARD_GetShowOverscanTop();
						ot = 16;
					}
				}
				if (MAINBOARD_GetShowOverscanBottom() > 0) //下側のオーバースキャン領域を表示するなら
				{
					d += MAINBOARD_GetShowOverscanBottom() * _Magnification;
					if (d > _Height) //画面からはみ出してしまう場合。1440x900,1600x900などであり得る。
					{	//アスペクト比を保つため、オーバースキャン部をカット。
						d = b + 224 * _Magnification;
						h -= MAINBOARD_GetShowOverscanBottom();
					}
				}
				if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
				{
					//PRINTF("Test = %d", tvWidth);//test
					if (tvWidth == 350) //横350モードの時は左右約3ドットぶん黒帯を付ける。v2.98
					{
						if (APP_GetVStretched()) //縦画面モードなら
						{
							a += (Sint32)(2.55 * (double)_Magnification + 0.5); //2.55=(298/262)*(262/350)*(262/350)*4dot。v3.01更新
							c -= (Sint32)(2.55 * (double)_Magnification + 0.5);
						}
						else //通常
						{
							a += (Sint32)(3.4057 * (double)_Magnification + 0.5); //3.4057=298/350*4dot。v3.01更新
							c -= (Sint32)(3.4057 * (double)_Magnification + 0.5);
						}
					}
					else if (tvWidth == 262) //横262モードの時は左右約5ドットぶん黒帯を付ける。v2.98
					{
						if (APP_GetVStretched()) //縦画面モードなら
						{
							a += (Sint32)(3.4057 * (double)_Magnification + 0.5); //3.4057=(298/262)*(262/350)*4dot。v3.01更新
							c -= (Sint32)(3.4057 * (double)_Magnification + 0.5);
						}
						else //通常
						{
							a += (Sint32)(4.5496 * (double)_Magnification + 0.5); //4.5496=298/262*4dot。v3.01更新
							c -= (Sint32)(4.5496 * (double)_Magnification + 0.5);
						}
					}
				}
			}
		}
		else //ストレッチをしない場合
		{
			if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合、左右の黒帯をカット。v2.95
			{
				switch (tvWidth)
				{
				case 262: w2 = (tvWidth - 3 * 2) * _Magnification; break; //このとき左右黒帯部分のソースは3ドットx倍率。
				case 350: w2 = (tvWidth - 4 * 2) * _Magnification; break; //このとき左右黒帯部分のソースは4.01(3x(350/262))ドットx倍率。
				default: //512
					w2 = tvWidth * _Magnification; break;//このとき左右黒帯部分のソースはなし。
				}
			}
			else
				w2 = tvWidth * _Magnification;
			if (w2 > _Width) w2 = _Width; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。
			a = (_Width - w2) / 2;
			b = (_Height - 224 * _Magnification) / 2;
			c = a + w2;
			d = b + 224 * _Magnification;
			if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合、上下4ドットぶん転送先領域を狭める。v2.95
			{
				b += 2 * _Magnification;
				d -= 2 * _Magnification;
			}
			else
			{
				if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
				{
					b -= MAINBOARD_GetShowOverscanTop() * _Magnification;
					if (b < 0) b = 0; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。1440x900,1600x900などであり得る。
				}
				if (MAINBOARD_GetShowOverscanBottom() > 0) //下側のオーバースキャン領域を表示するなら
				{
					d += MAINBOARD_GetShowOverscanBottom() * _Magnification;
					if (d > _Height) d = _Height; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。1440x900,1600x900などであり得る。
				}
			}
		}
	}
	else //ウィンドウモードのとき
	{
		//Direct3DのStretchRectではWindowの位置や枠の太さぶんなども調整する必要がない。常に(0,0)が描画の左上になる。
		a = 0;
		b = 0;
		if (APP_GetOverscanHideBlackTop()) //上側のオーバースキャン領域に黒帯を入れて隠す設定なら
			b += (8 - MAINBOARD_GetShowOverscanTop()) * _Magnification;
		c = APP_GetGameWidth(_Magnification);
		d = b + APP_GetGameHeight(_Magnification);
		if (APP_GetOverscanTV()) //オーバースキャンTVモード時
		{
			if (APP_GetOverscanHideBlackTop()) //v2.98追加
			{
				b += 2 * _Magnification;
				d += 2 * _Magnification;
			}
			if (APP_GetOverscanHideBlackLR()) //v2.98追加
			{
				if (APP_GetVStretched()) //縦画面モードなら
				{
					a += (Sint32)(7.6629 * (double)_Magnification + 0.5); // 7.6629=(298/262)*(262/350)*9dot
					c += (Sint32)(12.7714 * (double)_Magnification + 0.5); //12.7714=(298/262)*(262/350)*15dot
				}
				else if (APP_GetStretched())
				{
					a += (Sint32)(10.2366 * (double)_Magnification + 0.5); //10.2366=298/262*9dot
					c += (Sint32)(17.0611 * (double)_Magnification + 0.5); //17.0611=298/262*15dot
				}
			}
		}
		else //通常
		{
			if (((MAINBOARD_GetShowOverscanLeft() > 0) && (tvWidth == 512)) || //横解像度512の時は横オーバースキャン表示しない
				((MAINBOARD_GetShowOverscanLeft() == 0) && (APP_GetOverscanHideBlackLR())) ||
				(executeCode == 3))
			{	//左右に黒帯を配置
				if (APP_GetVStretched()) //縦画面モードなら
				{
					a += (Sint32)(5.1086 * (double)_Magnification + 0.5); //5.1086=(298/262)*(262/350)*6dot。v3.01更新
					c -= (Sint32)(5.1086 * (double)_Magnification + 0.5);
				}
				else if (APP_GetStretched())
				{
					a += (Sint32)(6.8244 * (double)_Magnification + 0.5); //6.8244=298/262*6dot
					c -= (Sint32)(6.8244 * (double)_Magnification + 0.5);
				}
			}
			if ((tvWidth == 512) && (APP_GetStretched())) //横解像度512の時は左右6ドットぶん黒帯を付ける。※オーバースキャンTVモード時は黒帯付けない。v2.98
			{
				a += (Sint32)(3.4122 * (double)_Magnification + 0.5); //3.4122=298/524*6dot
				c -= (Sint32)(3.4122 * (double)_Magnification + 0.5);
			}
			if (APP_GetOverscanHideBlackTop()) //上側のオーバースキャン領域に黒帯を付けるなら
				d -= (8 - MAINBOARD_GetShowOverscanTop()) * _Magnification; //転送先の縦範囲を黒帯分カット
			if (APP_GetOverscanHideBlackBottom()) //下側のオーバースキャン領域に黒帯を付けるなら
				d -= (8 - MAINBOARD_GetShowOverscanBottom()) * _Magnification; //転送先の縦範囲を黒帯分カット
		}
	}
	//アーケード基板からの移植ゲームをの実際に店頭に出ていたピクセルアスペクト比に近いキャラ表示で遊ぶ。v3.05
	if (APP_GetArcadeStretched() == 1)
	{
		a += (Sint32)(14.0 * (double)_Magnification); //横解像度を1.03倍相当(横256時)に縮める（1.143倍(横256時)されたものから左右14ドットぶんだけ横圧縮する）
		c -= (Sint32)(14.0 * (double)_Magnification); //
	}
	if (APP_GetArcadeStretched() == 2)
	{
		a += (Sint32)(10.0 * (double)_Magnification); //横解像度を1.06倍相当(横256時)に縮める（1.143倍(横256時)されたものから左右10ドットぶんだけ横圧縮する）
		c -= (Sint32)(10.0 * (double)_Magnification); //
	}
	if (APP_GetArcadeStretched() == 3)
	{
		a += (Sint32)(6.0 * (double)_Magnification); //横解像度を1.09倍相当(横256時)に縮める（1.143倍(横256時)されたものから左右6ドットぶんだけ横圧縮する）
		c -= (Sint32)(6.0 * (double)_Magnification); //
	}

	//PRINTF("%d, %d, %d, %d, w=%d, h=%d", a, b, c, d, APP_GetGameWidth(_Magnification), APP_GetGameHeight(_Magnification));//test
	_GameLeft   = a; //print_message()で表示位置決定のために、ゲーム画面転送先の座標を控えておく。
	_GameRight  = c; //
	_GameTop    = b; //
	_GameBottom = d; //

	if (IsIconic(_hWnd)) //ウィンドウが最小化されている時。hesを最小化して聴くときなどに処理を軽くする。
		if (executeCode != 2) //スクリーンショット用へ転送以外の場合
		{
			//垂直帰線期間を待つ
			SCREEN_WaitVBlank(FALSE); //描画処理は行わない
			return;
		}

	//描画解像度の変更があった場合、ゴミが残らないように画面全体をクリアする。
	if ((MAINBOARD_GetUpdateFullScreen()) && (MAINBOARD_GetResolutionChange()))//フルスクリーン時
		_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
	else if ((APP_GetArcadeStretched() > 0) || (tvWidth == 512)) //v3.05追加。Arcadeストレッチモードか横512時、描画解像度の変更があった場合（スクリーンショット時など）、ゴミが残らないように画面全体をクリアする。
			if ((MAINBOARD_GetResolutionChange()) || (executeCode == 2))
				_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);

	//ソース範囲を決定し、デバイスのバックサーフェス(DirectDrawでのプライマリサーフェスと同様)へ転送する。
	if (_Magnification >= 2) //x2以上の場合
	{
		scanlineType = APP_GetScanlineType();
		if (((scanlineType >= 1) && (scanlineType <= 6)) || (scanlineType == 11)) //スキャンライン(縦横含む)時
		{
			if ((scanlineType == 3) || (scanlineType == 4) || (scanlineType == 11))
				wMag = 1;
			else
				wMag = 2; //x2以上も横は２倍ドット固定。（速度アップ＋拡大されたときにバイリニアフィルタがいい感じにかかる）
			if (scanlineType == 11) //スキャンラインぼやけTVモード
				hMag = 2;
			else
				hMag = _Magnification; //縦は倍率ぶんのソースを用意して転送。
		}
		else //ノンスキャンライン時
		{
			if (scanlineType == 9) //ノンスキャンTVモード
			{
				wMag = 1;
				if (_Magnification <= 3)
					hMag = 1; //ぼやけさせるためソース１倍で。v2.92
				else
					hMag = _Magnification - 2; //4x,5xのときは、それぞれ2x,3xに拡大。適度なぼやけ具合。v2.92
			}
			else if (scanlineType == 10) //ノンスキャンぼやけTVモード
			{
				wMag = 1;
				hMag = 1; //ぼやけさせるためソース１倍で。v2.92
			}
			else //ノンスキャンTVモード以外
			{
				wMag = 2;
				if (_Magnification == 2)
					hMag = _Magnification; //縦は倍率ぶんのソースを用意して転送。
				else
					hMag = _Magnification - 1; //3x,4x,5xのときは、それぞれ2x,3x,4xに拡大。（ジャギー軽減＆速度アップ）
			}
		}
	}
	else //x1の場合
		wMag = hMag = 1;

	if (!bRasterChange)
	{	//通常
		if (MAINBOARD_GetFourSplitScreen()) //妖怪道中記,ワールドコート,はにいいんざすかい,パワードリフト,サイコチェイサーの４分割画面の場合。v2.27更新
		{
			SetRect(&rcSrc, s / 4 * wMag, ot * hMag, (s / 4 + s / 2) * wMag, (ot + h) * hMag); //ソースの左右黒帯部分をカット
			//４分の１に縮小して４回転送
			c = a + (c - a) / 2;
			d = b + (d - b) / 2;
			w2 = c - a;
			h2 = d - b;
			for (i = 1; i <= 4; i++)
			{
				SetRect(&rcDst, a, b, c, d);
				switch (APP_GetD3DEffect())
				{
				case 1:
					if ((APP_GetHalfMode()) && ((_Magnification == 2) || (_Magnification == 4)))
						_pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_LINEAR); //Halfモードでx2,x4のPOINTだと1/4縮小の影響で画面が暗くなるのでLINEARにする。
					else
						_pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_POINT); //だいぶドットがクッキリ
					break;
				case 2: _pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_LINEAR); //ちょうどいい按配
					break;
				default: //0
					_pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //かなりドットがクッキリ
					break;
				}
				switch (i)
				{
					case 1:	a += w2; c += w2; break; //次は右上
					case 2:	b += h2; d += h2; break; //次は右下
					case 3:	a -= w2; c -= w2; break; //次は左下
				}
			}
		}
		else //通常
		{
			SetRect(&rcDst, a, b, c, d);
			if ((!APP_GetStretched()) && ((_Flags & SCREEN_FFULLSCREEN) == 0)) //ウィンドウモードでストレッチをしない場合。v2.14更新
			{
				w2 = c / _Magnification;
				//PRINTF("%d, %d", s,w2);//test
				w2 = (s - w2) / 2;
				SetRect(&rcSrc, w2 * wMag, ot * hMag, (s - w2) * wMag, (ot + h) * hMag); //転送元を転送先と同じドット数に合わせる。
			}
			else if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
			{
				switch (tvWidth)
				{
					case 262: SetRect(&rcSrc, 3 * wMag, ot * hMag, (s - 3) * wMag, (ot + h) * hMag); break;//このとき左右黒帯部分のソースは3ドットx倍率。
					case 350: SetRect(&rcSrc, 4 * wMag, ot * hMag, (s - 4) * wMag, (ot + h) * hMag); break; //このとき左右黒帯部分のソースは4.01ドットx倍率。
					default: //512
						SetRect(&rcSrc, 0, ot * hMag, s * wMag, (ot + h) * hMag); break;//このとき左右黒帯部分のソースはなし。
				}
			}
			else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
				SetRect(&rcSrc, 6 * wMag, ot * hMag, (s - 6) * wMag, (ot + h) * hMag); //このとき左右黒帯部分のソースは6ドットx倍率。
			else //通常
			{
				if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
				{
					if (tvWidth == 350) //横350表示の場合。v2.98更新
						SetRect(&rcSrc, 4, ot * hMag, s * wMag, (ot + h) * hMag); //ソース(横352ぶんある)の参照開始を右に4ドットシフトして全体をセンタリングする。
					else
						SetRect(&rcSrc, 0, ot * hMag, s * wMag, (ot + h) * hMag);
				}
				else
				{
					if (tvWidth == 350) //横350表示の場合。v2.98更新
						SetRect(&rcSrc, 1 * wMag, ot * hMag, s * wMag, (ot + h) * hMag); //ソース(横352ぶんある)の参照開始を右に1ドットシフト
					else //通常
						SetRect(&rcSrc, 0, ot * hMag, s * wMag, (ot + h) * hMag);
				}
			}

			if (APP_GetArcadeStretched() == 4) //v3.05追加
			{
				//ソースの左右端をカットしてそのぶん横解像度を現状から横352ドットモード時1.04倍相当、横256モード時1.06倍相当に拡大する
				SetRect(&rcSrc, rcSrc.left+7*wMag, rcSrc.top, rcSrc.right-7*wMag, rcSrc.bottom); //7。7で横256モード時1.06倍相当。14で横256モード時1.12倍(ドットバイドットからは1.29倍)相当
			}

			switch (APP_GetD3DEffect())
			{
				case 1: _pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_POINT); //だいぶドットがクッキリ
					break;
				case 2: _pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_LINEAR); //ちょうどいい按配
					break;
				default: //0
					_pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //かなりドットがクッキリ
					break;
			}
		}
	}
	else
	{	//ラスタごとに異なる解像度が設定されていたとき。v1.28更新。１ラインずつ転送するようにした。v2.00更新。Direct3DならVistaでも高速。
		for (i = 0; i < h; i++)
		{
			if (((*pTvW) == 512) && (MAINBOARD_GetShowOverscanLeft() > 0)) //512のときはオーバースキャン表示しない
			{	//左右に黒帯を配置
				if (APP_GetVStretched()) //縦画面モードなら
					SetRect(&rcDst, a + (int)(5.126 * (double)_Magnification + 0.5), b + i * _Magnification,
						c - (int)(5.126 * (double)_Magnification + 0.5) - 1, b + i * _Magnification + _Magnification);
				else if (APP_GetStretched())
					SetRect(&rcDst, a + (int)(6.847 * (double)_Magnification + 0.5), b + i * _Magnification,
						c - (int)(6.847 * (double)_Magnification + 0.5) - 1, b + i * _Magnification + _Magnification);
				else
					SetRect(&rcDst, a, b + i * _Magnification, c, b + i * _Magnification + _Magnification);
			}
			else //通常
				SetRect(&rcDst, a, b + i * _Magnification, c, b + i * _Magnification + _Magnification);
			if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
			{
				switch ((*pTvW))
				{
				case 262: SetRect(&rcSrc, 3 * wMag, (ot + i) * hMag, ((*pTvW) - 3) * wMag, (ot + i) * hMag + hMag); break; //このとき左右黒帯部分のソースは3ドットx倍率。
				case 350: SetRect(&rcSrc, 4 * wMag, (ot + i) * hMag, ((*pTvW) - 4) * wMag, (ot + i) * hMag + hMag); break; //このとき左右黒帯部分のソースは4.01ドットx倍率。
				case 512: SetRect(&rcSrc, 0, (ot + i) * hMag, (*pTvW) * wMag, (ot + i) * hMag + hMag); break;//このとき左右黒帯部分のソースはなし。
				}
			}
			else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
				SetRect(&rcSrc, 6 * wMag, (ot + i) * hMag, ((*pTvW) - 6) * wMag, (ot + i) * hMag + hMag);
			else //通常
			{
				if ((*pTvW) == 350) //横350表示の場合。v2.98更新
					SetRect(&rcSrc, 1 * wMag, (ot + i) * hMag, 350 * wMag, (ot + i) * hMag + hMag);
				else
					SetRect(&rcSrc, 0, (ot + i) * hMag, (*pTvW) * wMag, (ot + i) * hMag + hMag);
			}
			if (APP_GetArcadeStretched() == 4) //v3.05追加
			{
				//ソースの左右端をカットしてそのぶん横解像度を現状から横352ドットモード時1.04倍相当、横256モード時1.06倍相当に拡大する
				SetRect(&rcSrc, rcSrc.left+7*wMag, rcSrc.top, rcSrc.right-7*wMag, rcSrc.bottom); //7。7で横256モード時1.06倍相当。14で横256モード時1.12倍(ドットバイドットからは1.29倍)相当
			}
			switch (APP_GetD3DEffect())
			{
			case 1: _pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_POINT); //だいぶドットがクッキリ
				break;
			case 2: _pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_LINEAR); //ちょうどいい按配
				break;
			default: //0
				_pD3DDev->StretchRect(_pD3DSGame, &rcSrc, _pD3DSBack, &rcDst, D3DTEXF_NONE); //かなりドットがクッキリ
				break;
			}
			pTvW++;
		}
	}

	if (executeCode == 2) return;//スクリーンショット用へ転送の場合ここで終了

	if (executeCode != 6) //周波数測定時の場合、オーディオグラフとFPS、左上テキストの表示は行わない。v2.92
	{
		//オーディオグラフの表示。v2.92追加。※FPSやテキストメッセージの表示を優先するため、重ね合わせた時には隠れるようオーディオグラフは一番最初に表示処理を行う。
		if (APP_GetShowAudioGraph())
			print_audio_graph();

		//FPSの表示。v1.50追加
		if (APP_GetShowFPS())
			print_fps();

		//テキストメッセージの表示。Direct3D9のStretchRectはプレーンサーフェス同士の等倍転送が出来ないので、
		//							DirectDrawのときと違い、テキストメッセージもバックバッファーへ直接転送するようにした。
		if (*_pMessageText != 0) //メッセージが設定されていれば
			print_message();
	}

	//センターテキストメッセージの表示。v2.77追加
	if (*_pCenterText != 0)
		print_center_message();

	//垂直帰線期間を待つ＆実際の描画処理
	SCREEN_WaitVBlank(TRUE);
}


//描画。メモリ書き込み処理。
//zoom3x()，zoom2x()，zoom1x()等も用意。速度重視のためそれぞれ別ルーチンに。ラインごとに解像度を変えているゲームに対応。
//32ビットカラーx５倍用
static void
zoom5x_dd32(
	Uint32*	pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16*	pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32*	pSrc0;
	Uint32*	pDst;
	Uint32*	pDst0;
	Uint32	d;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch5;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			d = *pSrc++; //通常は"& 0xFFFFFF"でマスクするべきだが高速化のため省略。dが万が一0xFFFFFFを超えていたとしても、ここでは配列からは読み出しのみなので不具合にはならない。
			*pDst++ = _GammaTBL[d]; //１ドットぶん書き込まれる。
			*pDst++ = _GammaTBLs90[d]; //１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90スキャンラインで。v2.34更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。v2.98更新
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.34更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //v2.98更新
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //４列目も90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //v2.98更新
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch4;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a; //v2.98更新
			*pDst++ = a; //横を２倍拡大(80%縦スキャンラインで)
		}
		*pDst = 0;
		pDst0 += _Pitch5;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//32ビットカラーx４倍用
static void
zoom4x_dd32(
	Uint32*	pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16*	pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32*	pSrc0;
	Uint32*	pDst;
	Uint32*	pDst0;
	Uint32	d;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch4;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			d = *pSrc++;
			*pDst++ = _GammaTBL[d]; //１ドットぶん書き込まれる。
			*pDst++ = _GammaTBLs90[d]; //１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90スキャンラインで。v2.34更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。v2.98更新
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.34更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //v2.98更新
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a; //v2.98更新
			*pDst++ = a; //横を２倍拡大(80%縦スキャンラインで)
		}
		*pDst = 0;
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//32ビットカラーx３倍用
static void
zoom3x_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32*	pSrc0;
	Uint32*	pDst;	//v2.13更新。32bit単位にして高速化。
	Uint32*	pDst0;
	Uint32	d;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch3;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			d = *pSrc++;
			*pDst++ = _GammaTBL[d]; //4byteぶん(１ドット)書き込まれる。
			*pDst++ = _GammaTBLs90[d]; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。v2.98更新
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。v2.98更新
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(80%縦スキャンラインで)
		}
		*pDst = 0;
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//32ビットカラーx２倍用。
static void
zoom2x_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	d;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch2;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			d = *pSrc++;
			*pDst++ = _GammaTBL[d];
			*pDst++ = _GammaTBLs90[d]; //横を２倍拡大(90%縦スキャンラインで)
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。v2.98更新
			*pDst++ = a; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(80%縦スキャンラインで)
		}
		*pDst = 0;
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//横スキャンライン32ビットカラーx５倍用
static void
zoom5xHS_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch5;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBL[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //４列目も90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch4;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst0 += _Pitch5;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//横スキャンライン32ビットカラーx４倍用
static void
zoom4xHS_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch4;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBL[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//横スキャンライン32ビットカラーx３倍用
static void
zoom3xHS_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch3;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBL[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//横スキャンライン32ビットカラーx２倍用。
static void
zoom2xHS_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	a;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch2;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBL[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a;
		}
		*pDst = 0;
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//スキャンラインぼやけTVで２倍３倍４倍５倍用。32ビットカラー。v2.92追加
static void
zoom2x3x4x5xTVB_dd32(
	Sint32	bairitsu,	// 2xか3xか4xか。2xなら2。3xなら3。4xなら4。5xなら4。
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンライン80%。縦横1x2倍のソースを作成。
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch * 2;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		*pDst = 0;
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//ノンスキャンラインで２倍３倍４倍５倍用。32ビットカラー。
static void
zoom2x3x4x5xNS_dd32(
	Sint32	bairitsu,	// 2xか3xか4xか。2xなら2。3xなら3。4xなら4。5xなら5。
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j, k;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Uint32	a;
	Sint32	bairitsu2; //縦のドットを何倍にデジタル拡大するか。x2かx3なら2。x4なら3。5xなら4。

	if (bairitsu == 2)
		bairitsu2 = 2;
	else
		bairitsu2 = bairitsu - 1;

	// スキャンラインなし
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * (_Pitch * bairitsu2);
	for (i = 0; i < srcH; i++)
	{
		for (k = 1; k <= bairitsu2; k++) //縦をbairitsu倍にデジタル拡大する
		{
			pSrc = pSrc0;
			pDst = pDst0;
			for (j = 0; j < *pTvW; j++)
			{
				a = _GammaTBL[*pSrc++];
				*pDst++ = a;
				*pDst++ = a; //横を２倍拡大(スキャンラインなし)
			}
			*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
			pDst0 += _Pitch;
		}
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//ノンスキャンラインTVで２倍３倍４倍５倍用。32ビットカラー。
static void
zoom2x3x4x5xNSTV_dd32(
	Sint32	bairitsu,	// 2xか3xか4xか。2xなら2。3xなら3。4xなら4。5xなら4。
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j, k;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;
	Sint32	bairitsu2; //縦のドットを何倍にデジタル拡大するか。x2かx3なら1。x4なら2。5xなら3。

	if (bairitsu <= 3)
		bairitsu2 = 1;
	else
		bairitsu2 = bairitsu - 2;

	// スキャンラインなし
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * (_Pitch * bairitsu2);
	for (i = 0; i < srcH; i++)
	{
		for (k = 1; k <= bairitsu2; k++) //縦をbairitsu倍にデジタル拡大する
		{
			pSrc = pSrc0;
			pDst = pDst0;
			for (j = 0; j < *pTvW; j++)
				*pDst++ = _GammaTBL[*pSrc++];
			*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
			pDst0 += _Pitch;
		}
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//ノンスキャンラインぼやけTVで２倍３倍４倍５倍用。32ビットカラー。
static void
zoom2x3x4x5xNSTVB_dd32(
	Sint32	bairitsu,	// 2xか3xか4xか。2xなら2。3xなら3。4xなら4。5xなら4。
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンラインなし。縦横1x1倍(等倍)のソースを作成。
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst0 += _Pitch;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//32ビットカラーx１倍用。スキャンラインなし。
static void
zoom1xNS_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW, 		//
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// １倍なのでスキャンラインは無し
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst0 += _Pitch;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//TV Mode の32ビットカラーｘ５倍用。
static void
zoom5xTV_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch5;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //４列目も90%スキャンラインで。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch4;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		*pDst = 0;
		pDst0 += _Pitch5;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//TV Mode の32ビットカラーｘ４倍用。
static void
zoom4xTV_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch4;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新。中央部分はグラデーションを付けずに平坦にしたほうが違和感がない。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		*pDst = 0;
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//TV Mode の32ビットカラーｘ３倍用。
static void
zoom3xTV_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch3;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
			*pDst++ = _GammaTBLs90[*pSrc++];
		*pDst = 0;
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		*pDst = 0;
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//Half Mode の32ビットカラー ｘ２倍用。
static void
zoom2xTV_dd32(
	Uint32* pSrc,		// 転送元のポインタ
	Sint32	srcY,		// 転送元のY座標
	Uint16* pTvW,		// 各ラインの幅
	Sint32	srcH)		// 転送元の高さ
{
	int		i, j;
	Uint32* pSrc0;
	Uint32* pDst;
	Uint32* pDst0;

	// スキャンライン80%
	pSrc0 = pSrc;
	pDst0 = _pPixels + srcY * _Pitch2;
	for (i = 0; i < srcH; i++)
	{
		pSrc = pSrc0;
		pDst = pDst0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBL[*pSrc++];
		*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		*pDst = 0;
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

//白黒モード 32ビットカラー。メモリ(&ソースコード)効率も優先。各画面モードで共通処理は共通して行う。v2.29
static void
monoColor_32(
	Sint32		bairitsu,		// ウィンドウ表示倍率
	Sint32		scanlineType,	// スキャンラインのタイプ
	Uint32*		pSrc,			// 転送元のポインタ
	Sint32		srcY,			// 転送元のY座標
	Uint16*		pTvW,			// 各ラインの幅
	Sint32		srcH,			// 転送元の高さ
	Sint32		bGreen)			// グリーンディスプレイ
{
	int			q, i, j, k;
	Uint32*		pSrc0;
	Uint32*		pDst;
	Uint32*		pDst0;
	Uint32		d;
	Uint32		a;
	int			sl;
	BOOL		bSpecial;

	if (bairitsu == 1)
		scanlineType = 0;
	if (((scanlineType == 2) || (scanlineType == 0) || ((scanlineType >= 5) && (scanlineType <= 8))) && (bairitsu >= 2))
		sl = 2; //横スキャンラインかノンスキャンライン(ぼやけタイプを除く)の場合（つまり、縦横スキャンラインかTVスキャンラインかぼやけタイプではない場合）
	else
		sl = 1;
	if ((scanlineType == 0) || ((scanlineType >= 7) && (scanlineType <= 10))) //ノンスキャンライン時
	{
		if (scanlineType == 10) //ノンスキャンラインぼやけタイプ2の場合
		{
			bairitsu = 1;
		}
		else if (scanlineType == 9) //ノンスキャンラインぼやけタイプの場合
		{
			if (bairitsu >= 4)
				bairitsu -= 2;
			else
				bairitsu = 1;
		}
		else //それ以外
		{
			if (bairitsu >= 3)
				bairitsu--;
		}
		pSrc0 = pSrc;
		pDst0 = _pPixels + srcY * _Pitch * bairitsu;
		for (i = 0; i < srcH; i++)
		{
			for (q = 1; q <= bairitsu; q++)
			{
				pSrc = pSrc0;
				pDst = pDst0;
				for (j = 0; j < *pTvW; j++)
				{
					a = _MonoTBL[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
				}
				*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
				pDst0 += _Pitch;
			}
			pSrc0 += SOURCE_PITCH;
			pTvW++;
		}
	}
	else //スキャンライン時
	{
		// スキャンライン80%
		bSpecial = (scanlineType == 1); //縦横スキャンラインの場合TRUE。v2.78
		if (scanlineType == 11) //TVスキャンライン2ぼやけタイプの場合
			bairitsu = 2;
		pSrc0 = pSrc;
		pDst0 = _pPixels + srcY * _Pitch * bairitsu;
		for (i = 0; i < srcH; i++)
		{
			pSrc = pSrc0;
			pDst = pDst0;
			for (j = 0; j < *pTvW; j++)
			{
				d = *pSrc++;
				for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
					*pDst++ = _MonoTBL[d]; //モノクロ化。１ドットぶん書き込まれる。
				if (bSpecial) //縦横スキャンラインの場合
					*pDst++ = _MonoTBLs90[d]; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
			}
			*pDst = 0; //Direct3Dでは右端に１ドットぶんクリア(黒)マージンを書いておかないと右端が乱れる。
			pDst = pDst0 + _Pitch;
			pSrc = pSrc0;
			switch (bairitsu)
			{
			case 5: //x5倍。v2.92
				for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch2;
				pSrc = pSrc0;
				for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch3;
				pSrc = pSrc0;
				for (j = 0; j < *pTvW; j++) //４列目も90%スキャンラインで。
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch4;
				break;
			case 4: //x4倍
				for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch2;
				pSrc = pSrc0;
				for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch3;
				break;
			case 3: //x3倍
				for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで
				{
					a = _MonoTBLs90[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(90%縦スキャンラインで)
				}
				*pDst = 0;
				pDst = pDst0 + _Pitch2;
				break;
				//case 2: //x2倍
				//	x2倍のときは処理なし。
			}
			if (bairitsu >= 2)
			{
				pSrc = pSrc0;
				for (j = 0; j < *pTvW; j++)
				{
					a = _MonoTBLs80[*pSrc++];
					for (k = 0; k < sl; k++) //横スキャンラインの場合、同じドットで２倍拡大。
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。
					if (bSpecial) //縦横スキャンラインの場合
						*pDst++ = a; //モノクロ化。１ドットぶん書き込まれる。横を２倍拡大(80%縦スキャンラインで)
				}
				*pDst = 0;
			}
			pDst0 += _Pitch * bairitsu;
			pSrc0 += SOURCE_PITCH;
			pTvW++;
		}
	}
}


//pSrcの描画データをパソコン表示向けにエンコードし、バックバッファへ画像を書き込みます。
//		ラインごとに解像度を変えているゲーム（龍虎の拳，あすか120%など）にも対応。
void
SCREEND3D_Blt(
	Uint32* pSrc,
	Sint32		srcX,
	Sint32		srcY,
	Uint16* pTvW,	//転送元の横ピクセル数。※srcHラインの数ぶん
	Sint32		srcH,
	Sint32		executeCode)  //Kitao追加。実行コード。0…エンコードだけ行う。1…プライマリ画面へ転送も行う。2…スクリーンショット用画面へ転送を行う。
							  //					   3…左右に黒帯(オーバースキャン部)を配置しての転送(あとは1と同じ)
							  //					   5…左右のオーバースキャン部をカットしての転送(あとは1と同じ)
							  //					   6…周波数測定時用でfps表示や音グラフ表示は行わない(あとは1と同じ)
{
	Uint32*	pSrc32;
	Uint16*	pTvW2;
	Sint32	scanlineType;

	if (pSrc == NULL) return;

	//v1.28更新。ライン途中からのエンコードに対応。
	pSrc32 = pSrc + srcY * 512 + srcX;
	pTvW2 = pTvW + srcY;

	if (srcH > 0) //v2.43更新
	{
		if (lock_offscreen_surface(&_Pitch, &_pPixels)) //バックサーフェスをロック
		{
			_Pitch2 = _Pitch * 2;
			_Pitch3 = _Pitch * 3;
			_Pitch4 = _Pitch * 4;
			_Pitch5 = _Pitch * 5;

			//速度重視のためそれぞれの画面モードで、別々のルーチンを使う。
			scanlineType = APP_GetScanlineType();
			if (MAINBOARD_GetMonoColor()) //白黒モードなら。はにいいんざすかい,パワードリフト等の裏技
			{	//白黒モードの場合。v2.28,2.29
				monoColor_32(_Magnification, scanlineType, pSrc32, srcY, pTvW2, srcH, (MAINBOARD_GetForceMonoColor() == 2));
			}
			else
			{	//通常
				switch (scanlineType)
				{
				case 0: //ノンスキャンライン
				case 7: //ノンスキャンライン2(Sharp,Fast)
				case 8: //ノンスキャンライン3(Soft)
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 2:
						case 3:
						case 4:
						case 5://v2.92追加
							zoom2x3x4x5xNS_dd32(_Magnification, pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 1: //縦横スキャンライン
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //※ノンスキャンラインと同じ。
							break;
						case 2:	zoom2x_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 3:	zoom3x_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 4:	zoom4x_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 5:	zoom5x_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 2: //横スキャンライン
				case 5: //横スキャンライン2
				case 6: //横スキャンライン3
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //※ノンスキャンラインと同じ。
							break;
						case 2:	zoom2xHS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 3:	zoom3xHS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 4:	zoom4xHS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 5:	zoom5xHS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 3: //横スキャンライン2(Fast)
				case 4: //TV横スキャンライン
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //※ノンスキャンラインと同じ。
							break;
						case 2:	zoom2xTV_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 3:	zoom3xTV_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 4:	zoom4xTV_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 5:	zoom5xTV_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 9: //ノンスキャンラインTV。v2.92更新
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 2:
						case 3:
						case 4:
						case 5:
							zoom2x3x4x5xNSTV_dd32(_Magnification, pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 10: //ノンスキャンラインTVぼやけ。v2.92更新
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 2:
						case 3:
						case 4:
						case 5:
							zoom2x3x4x5xNSTVB_dd32(_Magnification, pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				case 11: //TV横スキャンライン2(ぼやけ)。v2.92更新
					switch (_Magnification)
					{
						case 1:	zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH);
							break;
						case 2:
						case 3:
						case 4:
						case 5:
							zoom2x3x4x5xTVB_dd32(_Magnification, pSrc32, srcY, pTvW2, srcH);
							break;
					}
					break;
				}
			}

			//ロックを解除
			_pD3DSGame->UnlockRect();
		}
	}

	//プライマリ画面への転送
	if (executeCode > 0)
		zoom_d3dTensou(pTvW + (16 - MAINBOARD_GetShowOverscanTop()),
			srcY + srcH - (16 - MAINBOARD_GetShowOverscanTop()),
			executeCode);
}


//VSync(垂直帰線待ち)を行うかどうかを設定。VSyncをする設定でもSyncできない環境の場合は自動で一時的にオフにする。
void
SCREEND3D_SetVSyncScreen(
	BOOL	bVSyncScreen)
{
	BOOL		bPrevVSyncScreen = _bVSyncScreen;
	DEVMODE		dm;

	//ディスプレイの現在の表示周波数を求める。V-Syncを行えるかどうか判断する。
	MAINBOARD_SetVSyncHz(60); //60Hzに初期化
	//MAINBOARD_SetVSyncHz(120);//test用
	memset(&dm, 0, sizeof(DEVMODE));
	dm.dmSize = sizeof(DEVMODE);
	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
	if ((dm.dmDisplayFrequency ==  60) || (dm.dmDisplayFrequency ==  59) || (dm.dmDisplayFrequency ==  61) || //リフレッシュレートが60Hz(59と61も含む)か強制設定のとき。ForceVSyncはv2.65から廃止。
		(dm.dmDisplayFrequency == 120) || (dm.dmDisplayFrequency == 119) || (dm.dmDisplayFrequency == 121) || //リフレッシュレートが120Hz(119と121も含む)のとき
		(dm.dmDisplayFrequency == 180) || (dm.dmDisplayFrequency == 179) || (dm.dmDisplayFrequency == 181) || //リフレッシュレートが180Hz(179と181も含む)のとき
		(dm.dmDisplayFrequency == 240) || (dm.dmDisplayFrequency == 239) || (dm.dmDisplayFrequency == 241) || //リフレッシュレートが240Hz(239と241も含む)のとき
		(dm.dmDisplayFrequency == 300) || (dm.dmDisplayFrequency == 299) || (dm.dmDisplayFrequency == 301) || //リフレッシュレートが300Hz(299と301も含む)のとき
		(dm.dmDisplayFrequency == 360) || (dm.dmDisplayFrequency == 359) || (dm.dmDisplayFrequency == 361))   //リフレッシュレートが360Hz(359と361も含む)のとき
	{
		_bVSyncScreen = APP_GetVSyncScreen();
		APP_EnableVSyncMenu(TRUE); //メニュー更新
		if ((dm.dmDisplayFrequency == 120) || (dm.dmDisplayFrequency == 119) || (dm.dmDisplayFrequency == 121))      //リフレッシュレートが120Hz(119と121も含む)のとき
			MAINBOARD_SetVSyncHz(120);
		else if ((dm.dmDisplayFrequency == 180) || (dm.dmDisplayFrequency == 179) || (dm.dmDisplayFrequency == 181)) //リフレッシュレートが180Hz(179と181も含む)のとき
			MAINBOARD_SetVSyncHz(180);
		else if ((dm.dmDisplayFrequency == 240) || (dm.dmDisplayFrequency == 239) || (dm.dmDisplayFrequency == 241)) //リフレッシュレートが240Hz(239と241も含む)のとき
			MAINBOARD_SetVSyncHz(240);
		else if ((dm.dmDisplayFrequency == 300) || (dm.dmDisplayFrequency == 299) || (dm.dmDisplayFrequency == 301)) //リフレッシュレートが300Hz(299と301も含む)のとき
			MAINBOARD_SetVSyncHz(300);
		else if ((dm.dmDisplayFrequency == 360) || (dm.dmDisplayFrequency == 359) || (dm.dmDisplayFrequency == 361)) //リフレッシュレートが360Hz(359と361も含む)のとき
			MAINBOARD_SetVSyncHz(360);
	}
	else
	{
		_bVSyncScreen = FALSE;
		APP_EnableVSyncMenu(FALSE); //メニュー更新
	}
	//設定が変更された場合、Direct3Dオブジェクトを初期化し直す。
	if (_bVSyncScreen != bPrevVSyncScreen)
		if (_pD3D != NULL) //初期化が終了していれば。初期化前に呼ばれた場合(App.cから呼ばれる)は変数の更新のみ。
			SCREEND3D_ChangeMode(_Width, _Height, _Magnification, _Flags);
}

//VSync(垂直帰線待ち)を行っているかどうかを返す。VSyncをする設定でもSyncできない環境の場合はFALSEが返る。v2.43
BOOL
SCREEND3D_GetVSyncScreen()
{
	return _bVSyncScreen;
}


//Windowsの自動V-Sync機能周りの変数を更新する。v2.65
void
SCREEND3D_UpdateWindowsAutoVSync()
{
	SCREEND3D_ChangeMode(_Width, _Height, _Magnification, _Flags); //Direct3Dオブジェクトを初期化し直す。
}


//前回のVBlank待ちが終わった時刻を返す。v1.28
Sint64
SCREEND3D_GetLastTimeSyncTime()
{
	return _LastTimeSyncTime;
}

//スクリーンショットをファイルに書き込む。v2.12
void
SCREEND3D_WriteScreenshot(
	FILE* fp)
{
	int				i, j;
	D3DLOCKED_RECT	pLockedRect;
	HRESULT			hr;
	LONG			pitch;
	Uint32* pPixels0;
	Uint32* pPixels;
	Uint32			BGRR; //Bule,Green,Red,Reserved
	Sint32			width;
	Sint32			height;
	Sint32			left;
	Sint32			top;
	Sint32			w2, h2;
	Sint32			wp;

	//バッファに再描画する。
	if ((_Flags & SCREEN_FFULLSCREEN)|| //フルスクリーン時。v2.98更新
		((APP_GetOverscanHideBlackLR())&&(MAINBOARD_GetShowOverscanLeft() == 0))|| //左右のオーバースキャン領域に黒帯表示していた場合
		((APP_GetOverscanHideBlackTop())&&(MAINBOARD_GetShowOverscanTop() < 8))) //上側のオーバースキャン領域に黒帯表示していた場合
			MAINBOARD_SetResolutionChange(TRUE); //描画時にゴミが残らないように画面全体をクリアしてから描画する。
	MAINBOARD_DrawScreenshot();
	//※_pD3DDev->Presentでの表示更新は行わない。
	MAINBOARD_SetResolutionChange(FALSE); //元に戻す

	//サイズを計算
	width = APP_GetGameWidth(_Magnification);
	height = APP_GetGameHeight(_Magnification);
	wp = 4 - (width * 3 % 4); //１ラインを4byte単位に整えるための値
	if (wp == 4) wp = 0;

	//左上の位置を計算
	if (_Flags & SCREEN_FFULLSCREEN)
	{
		if (APP_GetFullStretched(TRUE))
		{
			//アスペクト比を4:3に保つ
			if (_Width / 4 >= _Height / 3)
			{	//縦のほうが短いので縦を基準とする
				w2 = (Sint32)((double)(_Height / 3.0 * 4.0 + 0.5));
				left = (_Width - w2) / 2;
				if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
					top = (_Height - (Sint32)((double)_Height * 0.96137 + 0.5)) / 2; //0.96137=224/((268-256)/4*3+224)
				else
					top = 0;
			}
			else
			{	//横のほうが短いので横を基準とする
				h2 = (Sint32)((double)(_Width / 4.0 * 3.0 + 0.5));
				left = 0;
				if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
					top = (_Height - (Sint32)((double)h2 * 0.96137 + 0.5)) / 2; //0.96137=224/((268-256)/4*3+224)
				else
					top = (_Height - h2) / 2;
			}
		}
		else if (APP_GetStretched())
		{
			if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示するなら
				w2 = 320 * _Magnification;
			else
				w2 = 298 * _Magnification;
			if (APP_GetVStretched()) //縦画面モードなら
				w2 = (Sint32)((double)w2 * TATEGAMEN + 0.5);
			//アスペクト比をTV画面と同じ4:3に。縦の長さををPCE標準224の整数倍(２倍)にする
			left = (_Width - w2) / 2;
			top = (_Height - 224 * _Magnification) / 2;
			if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
			{
				top -= MAINBOARD_GetShowOverscanTop() * _Magnification;
				if (top < 0) //画面からはみ出してしまう場合。1440x900,1600x900などであり得る。
					top = (_Height - 224 * _Magnification) / 2; //アスペクト比を保つため、オーバースキャン部をカット。
			}
		}
		else //ノンストレッチの場合
		{
			w2 = APP_GetNonstretchedWidth() * _Magnification;
			if (w2 > _Width) w2 = _Width; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。
			left = (_Width - w2) / 2;
			top = (_Height - 224 * _Magnification) / 2;
			if (MAINBOARD_GetShowOverscanTop() > 0) //上側のオーバースキャン領域を表示するなら
			{
				top -= MAINBOARD_GetShowOverscanTop() * _Magnification;
				if (top < 0) top = 0; //画面からはみ出してしまう場合は、ストレッチ(縮小)して対応。1440x900,1600x900などであり得る。
			}
		}
	}
	else
	{
		left = 0;
		top = 0;
	}

	//キャプチャするメモリをロック
	hr = _pD3DSBack->LockRect(&pLockedRect, NULL, D3DLOCK_READONLY); //READONLYで高速化。v2.28
	if (hr != D3D_OK) return;

	pitch = pLockedRect.Pitch / 4; //32bit単位で処理するためピッチは1/4に。
	pPixels0 = (Uint32*)pLockedRect.pBits + pitch * (top + height - 1) + left; //最下段からファイルに出力して行く(BMPの仕様)。

	for (i = 0; i < height; i++)
	{
		pPixels = pPixels0;
		for (j = 0; j < width; j++)
		{
			BGRR = (*pPixels++);
			fwrite(&BGRR, 3, 1, fp); //BGRだけ書き込む
		}
		if (wp > 0)
		{	//１ラインを4byte単位に整える
			BGRR = 0;
			fwrite(&BGRR, wp, 1, fp);
		}
		pPixels0 -= pitch;
	}

	//ロックを解除
	_pD3DSBack->UnlockRect();
}


//Kitao追加。スクリーンショット時のために一時的に描画倍率を変更する。Screen.cppから使用。v2.13
void
SCREEND3D_SetMagnification(
	Sint32	magnification)
{
	_Magnification = magnification;
}

//Kitao追加。v2.13
Sint32
SCREEND3D_GetMagnification()
{
	return _Magnification;
}

//Kitao追加。v2.79
BOOL
SCREEND3D_GetIntelVideoChip()
{
	return _bIntelVideoChip;
}

//Kitao追加。v2.19
BOOL
SCREEND3D_GetOldVideoChip()
{
	return _bOldVideoChip;
}


//メイン(ゲーム)画面用オフスクリーンを１色で塗りつぶし、プライマリ画面へ転送する。v2.77更新
void
SCREEND3D_Fill(
	Uint32		color)
{
	int			a, i, j;
	LONG		pitch;
	Uint32*		pPixels;
	Uint32*		pPixels0;
	Uint16		tvW[256];

	if (_Magnification == 1)
		a = 512;
	else
		a = 512 * 2;
	if (lock_offscreen_surface(&pitch, &pPixels))
	{
		pPixels0 = pPixels;
		for (i = 0; i < 256*_Magnification; i++)
		{
			pPixels = pPixels0;
			for (j = 0; j < a; j++)
				*pPixels++ = color; //4byteぶん(１ドット)書き込まれる。
			for (j = 0; j < 8; j++)
				*pPixels++ = 0; //右端８ドットは黒で埋める（Direct3Dのフィルター処理時にゴミを出さないため必要）
			pPixels0 += pitch;
		}
		_pD3DSGame->UnlockRect();
	}

	//プライマリ画面への転送
	for (i = 0; i < 256; i++)
		tvW[i] = 256;
	zoom_d3dTensou(tvW, 224, 1);
}
