/******************************************************************************
Ootake
・Direct3D用にScreenD3D.cを作ったため、DirectDraw用はScreenDD.cに名前を変更し
  た。(名前順にファイルを並べたとき、修正などがしやすいように)
・Window表示にもDirectDrawを使うようにした。
・実機をTVにつなげたときの画面の明るさに近づけるため、ガンマを上げて画面を明る
  くし見やすくした。
・画面を拡大するときに、ドットのジャギーが目立たないよう、かつ画面がぼやけてし
  まわないように、独自の拡大処理(明るめのスキャンラインを入れる)で拡大するよう
  にした。
・TVモードを追加した。v0.91
・ディスプレイのリフレッシュレートが60Hzではなかった場合、自動的にVSyncをオフに
  するようにした。
・DirectDrawでVSync待ちを行う際にCPUの利用率が100%状態になるので、CPUパワーを抑
  える省電力モード(音質がやや落ちる)を付けた。
・ラインごとに解像度を変えるゲーム(龍虎の拳，あすか120%等)に対応した。v0.60
・上記の[ラインごとに解像度を変える処理]で重くなっていたので、ラインごとの解像
  度変更を行っていない通常のゲームでは、その処理を省くことで高速化した。v1.06
・スクリーン上にテキストメッセージの表示ができるようにした。v0.64
・DirectDrawのV-Syncで、GetScanline()を使うようにした。環境によってサウンドにノ
  イズが入ることへの対策。v0.95。←オーバーレイを使った他のソフトを同時に起動し
  たときに逆に大きなノイズが定期的に載ってしまったので元に戻した。v0.97
・オフスクリーン描画用のメモリをシステムメモリに取ることも可能にした。ジャギー
  は目立つがクッキリした画質が好みの場合に利用。v1.31
・ｖ2.94から16bitカラーモードは廃止した。

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

*******************************************************************************
	[SCREENDD.c]

	Implement ScreenInterface using Direct Draw.

	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.
******************************************************************************/
#define _CRT_SECURE_NO_DEPRECATE

#define DIRECTDRAW_VERSION	0x0900	//Kitao更新。ドライバ環境にもよるかもしれないが、DirectDraw8以降専用にしたほうが、割り込み処理にきちんと時間を割いてくれるのか音質が大きく上がった。
									//			 8より9のほうが全体は軽めだが8が最高音質だった。DirectX8のプログラマさん(もしくはnVidiaの旧世代ドライバ)は描画以外の部分もより安定して動作するように作ってくれていたのかも。
#define SOURCE_PITCH 		512		//Kitao追加

#include <ddraw.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "ScreenDD.h"
#include "Screen.h"
#include "WinMain.h"
#include "App.h"
#include "MainBoard.h"
#include "Printf.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%用

/*--------------------------------------------------------------------------*
 *                           Static variables                               *
 *--------------------------------------------------------------------------*/
static LPDIRECTDRAW				_pDD         = NULL;  // DirectDraw object
static LPDIRECTDRAWCLIPPER		_pDC         = NULL;  // Kitao追加。DirectDraw clipper object
static LPDIRECTDRAWSURFACE		_pDDSPrimary = NULL;  // DirectDraw primary surface
static LPDIRECTDRAWSURFACE		_pDDSBack    = NULL;  // DirectDraw offscreen surface for BackBuffer
static LPDIRECTDRAWSURFACE		_pDDSText    = NULL;  // Kitao追加。DirectDraw offscreen surface for Text
static LPDIRECTDRAWSURFACE		_pDDS256     = NULL;  // Kitao追加。DirectDraw offscreen surface for 256WidthScreen

//Kitao追加。最初に設定した値を残しておいて高速化。
static	DDSURFACEDESC	_ddsdPrimary;
static	DDSURFACEDESC	_ddsdBack;
static	DDSURFACEDESC	_ddsd256;

static LONG		_SurfacePitch = 0;

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

//Win9x用変数。Kitao追加
static DWORD	_PrevWidth;
static DWORD	_PrevHeight;
static DWORD	_PrevFrequency;

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

static Uint32	_Rmask;
static Uint32	_Gmask;
static Uint32	_Bmask;

static Uint32	_Rshift;
static Uint32	_Gshift;
//static Uint32	_Bshift;

//static BOOL	_bLocked = FALSE; //Kitao更新。プライマリをロックする必要がなくなったのでカット。v1.39

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

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


/*-----------------------------------------------------------------------------
	[Deinit]
		Finished with all objects we use; release them
-----------------------------------------------------------------------------*/
void
SCREENDD_Deinit()
{
	if (_pDD != NULL)
	{
		if (_pDC != NULL) //Kitao追加
		{
			_pDDSPrimary->SetClipper(NULL);
			_pDC->Release();
			_pDC = NULL;
		}

		if (_pDDS256 != NULL) //Kitao追加
		{
			_pDDS256->Release();
			_pDDS256 = NULL;
		}

		if (_pDDSText != NULL) //Kitao追加
		{
			_pDDSText->Release();
			_pDDSText = NULL;
		}

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

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

		_pDD->Release();
		_pDD = NULL;
	}
}


static Uint32
get_shift(
	Uint32		mask)
{
	Uint32		i;

	for (i = 0; i < 32; i++)
		if (mask & (1 << i))
			return i;

	return 0;
}


//Kitao追加
static BOOL
lock_screen256_surface(
	LONG*		pPitch,
	Uint32**	ppPixels)
{
	HRESULT		hRet;
	int			trial = 10;

	while (trial--)
	{
		hRet = _pDDS256->Lock(NULL, &_ddsd256, DDLOCK_WAIT, NULL);
		if (hRet == DD_OK)
		{
			*pPitch = _ddsd256.lPitch >> 2; //横１ラインのバイト数。32bit単位で扱うために4で割っておく。v2.77更新;
			*ppPixels = (Uint32*)_ddsd256.lpSurface;
			return TRUE;
		}
		else if (hRet == DDERR_SURFACELOST)
		{
			_pDDS256->Restore();
		}
    }

	return FALSE;
}


/*-----------------------------------------------------------------------------
	[Init]
		スクリーンを初期化します。 
-----------------------------------------------------------------------------*/
BOOL
SCREENDD_Init(
	Sint32		width,
	Sint32		height,
	Sint32		magnification, //Kitao追加
	Uint32		flags) //Kitao追加
{
	int				i,j;
	HRESULT			hRet;
	DDSURFACEDESC	ddsd;
	HWND			hWnd = WINMAIN_GetHwnd();
	int 			a; //Kitao追加
	DEVMODE			dm; //Kitao追加
	LONG			pitch;
	Uint32*			pPixels;
	Uint32*			pPixels0;

	hRet = DirectDrawCreate(NULL, &_pDD, NULL);
	if (hRet != DD_OK)
	{
		MessageBox(hWnd, "ERROR: DIRECTDRAW::DirectDrawCreate() failed.    ", "Ootake", MB_OK); //Kitao追加
		return FALSE;
	}

	// 協調レベルを設定する。 
	if (flags & SCREEN_FFULLSCREEN) //Kitao追加。フルスクリーンモードなら
		hRet = _pDD->SetCooperativeLevel(hWnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
	else//Windowモードなら
		hRet = _pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
	if (hRet != DD_OK)
	{
		MessageBox(hWnd, "ERROR: DIRECTDRAW::SetCooperativeLevel() failed.    ", "Ootake", MB_OK); //Kitao更新
		return FALSE;
	}

	// 画面の解像度を設定する。
	//  Kitao更新。ウィンドウスタイルの変更は画面の解像度を変えてからにした。
	//			   低解像度から高解像度に切り替えた場合に、他のアプリのウィンドウが縮んでしまうことがあった問題を解消。v0.95
	if (flags & SCREEN_FFULLSCREEN) //Kitao追加。フルスクリーンモードなら
	{
		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;
				_PrevFrequency = dm.dmDisplayFrequency;
			}
		}
		memset(&dm, 0, sizeof(DEVMODE));
		dm.dmSize = sizeof(DEVMODE);
		dm.dmPelsWidth = width;
		dm.dmPelsHeight = height;
		dm.dmBitsPerPel = 32; //v2.94から32bitカラー固定
		dm.dmDisplayFrequency = APP_GetFullVSyncHz();
		dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
		if ((APP_GetResolutionAutoChange())&&(_bChangedFullScreen))
			ChangeDisplaySettings(&dm, 0); //設定を控えずに切替。一部の環境で切替時のもたつきを解消。v2.23。古いRADEONで切り替え時に画像の乱れ解消確認済み。v2.65
		else //通常
			ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
		WINMAIN_SetFullScreenWindow(width, height);//v2.23追加。このタイミングで、ウィンドウスタイルの変更を行うようにした。パワーの無いマシンではDirectX初期化に時間が掛かるのでこのタイミングで画面を整えたほうが良さそう。
		_bChangedFullScreen = TRUE;
	}
	else //Kitao追加。ウィンドウモードなら
	{
		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 = 32; //v2.94から32bitカラー固定
				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); //環境によっては、デスクトップに戻るまでウェイトを入れないとDIRECTDRAWをCreate出来ないことがある。v1.61
		}
		WINMAIN_SetNormalWindow(width, height);//v2.23追加。このタイミングで、ウィンドウスタイルの変更を行うようにした。パワーの無いマシンではDirectX初期化に時間が掛かるのでこのタイミングで画面を整えたほうが良さそう。
		APP_WindowWithinScreen(); //ウィンドウが画面からはみ出していた場合、画面内に収まるように移動する。
		_bChangedFullScreen = FALSE;
	}

	// プライマリサーフェスを作成する
	ZeroMemory(&_ddsdPrimary, sizeof(_ddsdPrimary));
	_ddsdPrimary.dwSize = sizeof(_ddsdPrimary);
	_ddsdPrimary.dwFlags = DDSD_CAPS;
	_ddsdPrimary.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
	hRet = _pDD->CreateSurface(&_ddsdPrimary, &_pDDSPrimary, NULL);
	if (hRet != DD_OK)
	{
		MessageBox(hWnd, "ERROR: DIRECTDRAW::CreateSurface() failed.    ", "Ootake", MB_OK); //Kitao更新
		SCREENDD_Deinit();
		return FALSE;
	}

    // オフスクリーンサーフェスを作成する
	ZeroMemory(&_ddsdBack, sizeof(_ddsdBack));
	_ddsdBack.dwSize = sizeof(_ddsdBack);
	_ddsdBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	if (APP_GetUseVideoCardMemory())
		_ddsdBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
	else
		_ddsdBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; //システムメモリを利用(シャープ＆重い)。v1.31追加
	if (magnification == 1)
	 a = 512;
	else //2倍以上は全て横2倍。速度アップ＋こうすることで適度にバイリニアフィルタが掛かりいい感じになる
	 a = 512*2;
	_ddsdBack.dwWidth = a;
	_ddsdBack.dwHeight = 256*magnification; //オーバースキャン用に256まで用意。上側のオーバースキャン領域は241〜256ライン目の領域を使用する。v1.43
	hRet = _pDD->CreateSurface(&_ddsdBack, &_pDDSBack, NULL);
	if (hRet != DD_OK)
	{
		MessageBox(hWnd, "ERROR: DIRECTDRAW::CreateSurface(Back) failed.    ", "Ootake", MB_OK); //Kitao更新
		SCREENDD_Deinit();
		return FALSE;
	}

    // Kitao追加。テキスト表示用オフスクリーンサーフェスを作成する
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; //テキストはボヤけて読みにくくならないようにシステムメモリ上に作成
	ddsd.dwWidth = 512*2;
	ddsd.dwHeight = 32;
	_pDD->CreateSurface(&ddsd, &_pDDSText, NULL);

    // Kitao追加。横256ドット汎用表示用オフスクリーンサーフェスを作成する
	ZeroMemory(&_ddsd256, sizeof(_ddsd256));
	_ddsd256.dwSize = sizeof(_ddsd256);
	_ddsd256.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	_ddsd256.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; //文字がボヤけて読みにくくならないようにシステムメモリ上に作成
	_ddsd256.dwWidth = 256;
	_ddsd256.dwHeight = 224;
	_pDD->CreateSurface(&_ddsd256, &_pDDS256, NULL);

	if (!flags & SCREEN_FFULLSCREEN) //Kitao追加。ウィンドウモードなら
	{
		//クリッパーを用意 
		hRet = _pDD->CreateClipper(0, &_pDC, NULL);
		if (hRet != DD_OK)
		{
			MessageBox(hWnd, "ERROR: DIRECTDRAW::CreateClipper failed.    ", "Ootake", MB_OK); //Kitao更新
			SCREENDD_Deinit();
			return FALSE;
		}
		//クリッパーにウィンドウを関連付け
		hRet = _pDC->SetHWnd(0, hWnd);
		if (hRet != DD_OK)
		{
			MessageBox(hWnd, "ERROR: DIRECTDRAW::SetHWnd failed.    ", "Ootake", MB_OK); //Kitao更新
			SCREENDD_Deinit();
			return FALSE;
		}
		//サーフェスにクリッパーを関連付け
		hRet = _pDDSPrimary->SetClipper(_pDC);
		if (hRet != DD_OK)
		{
			MessageBox(hWnd, "ERROR: DIRECTDRAW::SetClipper failed.    ", "Ootake", MB_OK); //Kitao更新
			SCREENDD_Deinit();
			return FALSE;
		}
     }

	// Get pixel format 
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_PIXELFORMAT | DDSD_CAPS;
	_pDDSPrimary->GetSurfaceDesc(&ddsd);

	_Rmask = ddsd.ddpfPixelFormat.dwRBitMask;
	_Gmask = ddsd.ddpfPixelFormat.dwGBitMask;
	_Bmask = ddsd.ddpfPixelFormat.dwBBitMask;

	// Rshift, Gshift, Bshift を求める。Kitao更新
	_Rshift = get_shift(_Rmask);
	if (_Rshift == 10) //RGB555のとき
		_Gshift = 5;
	else //RGB565のとき
		_Gshift = 6;

	// Save our screen resolution
	_Width = width;
	_Height = height;
	_Magnification = magnification;//Kitao追加
	_Flags = flags;
	_hWnd =	hWnd;

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

	// 画面クリア用に使うため、オフスクリーン256をクリアしておく。
	if (lock_screen256_surface(&pitch, &pPixels))
	{
		pPixels0 = pPixels;
		for (i = 0; i < 224; i++)
		{
			pPixels = pPixels0;
			for (j = 0; j < 256; j++)
				*pPixels++ = 0; //4byteぶん(黒１ドット)書き込まれる。
			pPixels0 += pitch;
		}
		_pDDS256->Unlock(NULL);
	}

	_LastTimeSyncTime = APP_QPCtimeGetTime1000(); //Kitao追加。前回Syncしたときのタイマーカウント
	_LastTimeSyncTime3 = _LastTimeSyncTime; //v2.43追加

	return TRUE;
}


/*-----------------------------------------------------------------------------
	[ChangeMode]
		スクリーンモードを変更します。 
-----------------------------------------------------------------------------*/
BOOL
SCREENDD_ChangeMode(
	Sint32		width,
	Sint32		height,
	Sint32		magnification, //Kitao追加
	Uint32		flags)
{
	SCREENDD_Deinit();
	return SCREENDD_Init(width, height, magnification, flags);
}


/*-----------------------------------------------------------------------------
	[WaitVBlank]
		垂直帰線期間を待ちます。 
-----------------------------------------------------------------------------*/
//垂直帰線期間を待ちます。(VSync処理)
BOOL
SCREENDD_WaitVBlank(
	BOOL	bDraw) //bDraw…Direct3D用に追加したパラーメータ。TRUEに設定してもここでは常に描画は行われない。Kitao追加
{
	Sint64		t, vt;
	Sint32		vSyncHz = MAINBOARD_GetVSyncHz();

	//Kitao更新
	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の場合時間の余裕がなく処理落ちの恐れがあるので最低限のウェイトだけ入れる
			}
			//待つ
			_pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
		}
		_LastTimeSyncTime = APP_QPCtimeGetTime1000(); //前回のSyncが終わったタイムとして記録しておく
		return TRUE;
	}
	else //_bVSyncScreen が FALSEの場合。Kitao更新。現状60fps専用としてシンプル化した。v1.67
	{
		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が終わったタイムとして記録しておく
		return TRUE;
	}
}


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


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


//Kitao追加。文字表示、塗りつぶし等用のプライマリサーフェスへ転送する処理。現在、ソース解像度256x224固定。
static void
zoom_ddTensou256()
{
	Sint32		w = 256;
	Sint32		h = 224;
	Sint32		a,b;
	RECT		rc;
	RECT		rcDst;
	RECT		rcSrc;

	//転送先の位置調整
	if (_Flags & SCREEN_FFULLSCREEN)
		SetRect(&rcDst, 0, 0, _Width, _Height);
	else //ウィンドウモードのとき
	{
		GetWindowRect(_hWnd, &rc);
		a = rc.left + (rc.right - rc.left - _Width)/2;
		b = rc.bottom - (rc.right - rc.left - _Width)/2 - _Height; //枠の太さと表示領域の高さぶんを引く
		SetRect(&rcDst, a, b, a+_Width, b+_Height);
	}

	//転送元の位置調整
	SetRect(&rcSrc, 0, 0, w, h);

	//プライマリサーフェスへ転送する
	if (_pDDSPrimary->Blt(&rcDst, _pDDS256, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
		_pDDSPrimary->Restore();
}


//Kitao更新。ddsdの初期化(100バイト強のメモリアクセス)を省略して高速化した(v1.02更新)。v2.13更新。32bit単位で扱うようにした。
static BOOL
lock_offscreen_surface(
	LONG*		pPitch,
	Uint32**	ppPixels)
{
	HRESULT			hRet;
	int				trial = 10;

	while (trial--)
	{
		hRet = _pDDSBack->Lock(NULL, &_ddsdBack, DDLOCK_WAIT, NULL);
		if (hRet == DD_OK)
		{
			*pPitch = _ddsdBack.lPitch >> 2; //横１ラインのバイト数。32bit単位で扱うために4で割っておく。v2.13更新
			*ppPixels = (Uint32*)_ddsdBack.lpSurface;
			return TRUE;
		}
		else if (hRet == DDERR_SURFACELOST)
		{
			_pDDSBack->Restore();
		}
    }

	return FALSE;
}


//Kitao追加。画面全体をクリアする。v1.43。v2.77更新
void
SCREENDD_Clear(
	byte	r,
	byte	g,
	byte	b) //v2.73更新。任意の色でクリアできるようにした。※16ビットモードのときはモノクロ以外だと色化けするので現在はモノクロ固定でrのデータのみ使用。
{
    DDBLTFX		ddbltfx;

    ZeroMemory(&ddbltfx, sizeof(DDBLTFX));
	ddbltfx.dwFillColor = r*256 + r;
    ddbltfx.dwSize = sizeof(DDBLTFX);
    _pDDSPrimary->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
}


//Kitao追加。テキストメッセージを設定
void
SCREENDD_SetMessageText(
	char*	pText)
{
	_pMessageText = pText;
}

//Kitao追加。センターテキストメッセージを設定
void
SCREENDD_SetCenterText(
	char*	pText)
{
	_pCenterText = pText;
}


//Kitao追加。テキストメッセージの表示
static void
print_message(
	Sint32	executeCode) //v2.00更新
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	BOOL		bHalfMode;
	int			a,fs;
	Sint32		hMagnification; //縦の表示倍率
	Sint32		scanlineType = APP_GetScanlineType();

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

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

	//フォントサイズを決定
	if (_Magnification == 1)
		bHalfMode = FALSE;
	else
		bHalfMode = APP_GetHalfMode();
	if ((bHalfMode)||(_Magnification == 1))
		fs = 12; //フォントサイズ。v1.09更新
	else
		fs = 16;
	if (_Magnification <= 2)
	{
		if ((!bHalfMode)&&((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))) //※x2のHalfモード時はフォントが小さくてもOK。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
		{	//システムメモリを使用している場合、256より大きな解像度で文字が崩れるのでフォントを大きくする必要がある。v1.53
			if (VDC_GetTvWidth() >= 512)
				fs = 32;
			else if (VDC_GetTvWidth() > 256)
				fs = 28;
		}
		else
		{	//x2以下の場合、横512時に文字が読みづらいので大きくする
			if ((!bHalfMode)&&(VDC_GetTvWidth() >= 512))
				fs = 28;
		}
	}
	if ((_Magnification == 3)&&(!bHalfMode)&&
		((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))&&(VDC_GetTvWidth() >= 512)) //x3(非Halfモード)でシステムメモリ＆横512以上の場合。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
			fs = 28;
	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, _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);
	_pDDSText->ReleaseDC(dc);
	
	//転送元位置＆サイズの調整
	rcSrc.left	= 0;
	rcSrc.top	= 0;
	rcSrc.right	= rc.right;
	a = VDC_GetTvWidth();
	if (APP_GetOverscanHideBlackLR())
		a += 16;
	else
		a += MAINBOARD_GetShowOverscanLeft()+MAINBOARD_GetShowOverscanRight();
	if (_Magnification > 1)
		a *= 2;
	if (rcSrc.right > a)
		rcSrc.right = a;
	rcSrc.bottom= fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	if (fs == 12)
	{
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if ((bHalfMode)&&(_Magnification >= 2))
			{
				if (_Magnification == 4)
					hMagnification = 3;
				else
					hMagnification = 2;
			}
			else
				hMagnification = 1;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
		rcDst.top = 4;
		rcDst.bottom = 4 + fs*hMagnification;
		if (_Magnification == 1)
			rcDst.left = 3;
		else
			rcDst.left = 2;
		if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
		{
			if (_Magnification >= 2)
				rcDst.left += 5;
			rcDst.top    += APP_GetOverscanTvShift() * _Magnification;
			rcDst.bottom += APP_GetOverscanTvShift() * _Magnification;
		}
	}
	else //(fs==16)
	{
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if (_Magnification == 4)
				hMagnification = 3;
			else
				hMagnification = 2;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
		rcDst.top = 4;
		if ((_Magnification == 4)||
			((_Magnification == 3)&&(scanlineType != 0)&&(scanlineType != 7)&&(scanlineType != 8)))
				rcDst.bottom = rcDst.top + fs*2;
		else
			rcDst.bottom = rcDst.top + fs;
		rcDst.left = 3 -_Magnification/4;
		if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
		{
			if (_Magnification == 1)
				rcDst.left += 5;
			else
				rcDst.left += 10;
			rcDst.top    += APP_GetOverscanTvShift() * _Magnification;
			rcDst.bottom += APP_GetOverscanTvShift() * _Magnification;
		}
	}
	if ((APP_GetFullStretched(TRUE))&&(_Flags & SCREEN_FFULLSCREEN)) //フルストレッチモードなら。v2.00追加
	{
		rcDst.top += 16*hMagnification;	   //上側オーバースキャン領域部分を足す
		rcDst.bottom += 16*hMagnification; //
	}
	else
	{
		rcDst.top += (16-MAINBOARD_GetShowOverscanTop())*hMagnification;	//上側オーバースキャン領域部分を足す
		rcDst.bottom += (16-MAINBOARD_GetShowOverscanTop())*hMagnification; //
	}
	if ((!APP_GetStretched())&&((_Flags & SCREEN_FFULLSCREEN) == 0)) //ウィンドウモードでストレッチをしない場合。v2.14更新
	{
		a = APP_GetNonstretchedWidth();
		//PRINTF("Test = %d",a);
		if ((a < 262)||
			((a > 262)&&(a < 350)))
		{
			if ((_Magnification >= 2)&&(fs==16))
				a = (a*2 / 2) - (rcSrc.right / 2);
			else
				a = (a / 2) - (rcSrc.right / 2);
			if (a > rcDst.left)
				rcDst.left = a; //左端を切らさないために中央に表示
		}
		else if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示しているなら。v2.15追加
		{
			if (_Magnification == 1)
				rcDst.left += 8;
			else
				rcDst.left += 8*2;
		}
	}
	else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
	{
		if (_Magnification == 1)
			rcDst.left += 6;
		else
			rcDst.left += 6*2;
	}
	rcDst.right	= rcDst.left + rcSrc.right;
	if ((APP_GetOverscanHideBlackLR())&&(MAINBOARD_GetShowOverscanLeft() == 0) && //左右のオーバースキャン領域に黒帯を付けるなら
		(!APP_GetOverscanTV())) //オーバースキャンTVモード時を除く
	{
		rcDst.right -= rcDst.left;
		rcDst.left = 0; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
	}
	if ((APP_GetOverscanHideBlackTop())&&(MAINBOARD_GetShowOverscanTop() < 8) && //上側のオーバースキャン領域に黒帯を付けるなら
		(!APP_GetOverscanTV())) //オーバースキャンTVモード時を除く
	{
		if ((8-MAINBOARD_GetShowOverscanTop())*hMagnification >= 4) //黒帯部分が4ドット以上あった場合
		{
			a = (16-MAINBOARD_GetShowOverscanTop())*hMagnification;
			rcDst.bottom -= (rcDst.top - a);
			rcDst.top =	a; //黒帯があるのでギリギリまで上に寄せても見栄えOK。
		}
	}
	if (MAINBOARD_GetFourSplitScreen()) //妖怪道中記,ワールドコート,はにいいんざすかい,パワードリフト,サイコチェイサーの４分割画面の場合。v2.27更新
	{	//左側の黒帯部分を考慮
		if ((_Magnification == 1)||(bHalfMode))
		{
			rcDst.left  += 256/2;
			rcDst.right += 256/2;
		}
		else
		{
			rcDst.left  += 512/2;
			rcDst.right += 512/2;
		}
	}
	//転送
	if (_pDDSBack->Blt(&rcDst, _pDDSText, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
		_pDDSBack->Restore();
}


//Kitao追加。FPSの表示
static void
print_fps(
	Sint32	executeCode) //v2.00更新
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	BOOL		bHalfMode;
	int			fs;
	char		text[3+1]; //一応３桁まで用意
	int			a;
	Sint32		hMagnification; //縦の表示倍率
	Sint32		scanlineType = APP_GetScanlineType();

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

	//フォントサイズを決定
	if (_Magnification == 1)
		bHalfMode = FALSE;
	else
		bHalfMode = APP_GetHalfMode();
	if ((bHalfMode)||(_Magnification == 1))
		fs = 12; //フォントサイズ。v1.09更新
	else
		fs = 16;
	if (_Magnification <= 2)
	{
		if ((!bHalfMode)&&((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))) //※x2のHalfモード時はフォントが小さくてもOK。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
		{	//システムメモリを使用している場合、256より大きな解像度で文字が崩れるのでフォントを大きくする必要がある。v1.53
			if (VDC_GetTvWidth() >= 512)
				fs = 32;
			else if (VDC_GetTvWidth() > 256)
				fs = 28;
		}
		else
		{	//x2以下の場合、横512時に文字が読みづらいので大きくする
			if ((!bHalfMode)&&(VDC_GetTvWidth() >= 512))
				fs = 28;
		}
	}
	if ((_Magnification == 3)&&(!bHalfMode)&&
		((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))&&(VDC_GetTvWidth() >= 512)) //x3(非Halfモード)でシステムメモリ＆横512以上の場合。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
			fs = 28;
	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);
	_pDDSText->ReleaseDC(dc);
	
	//転送元位置＆サイズの調整
	rcSrc.left	= 0;
	rcSrc.top	= 0;
	rcSrc.right	= rc.right;
	rcSrc.bottom= fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	if (fs == 12)
	{	//"x1" or "TV mode"
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if ((bHalfMode)&&(_Magnification >= 2))
			{
				if (_Magnification == 4)
					hMagnification = 3;
				else
					hMagnification = 2;
			}
			else
				hMagnification = 1;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
		rcDst.bottom = 224*hMagnification - 3; //下側に表示
	    rcDst.top = rcDst.bottom - fs*hMagnification;
		if (_Magnification == 1)
			rcDst.left = 3;
		else
			rcDst.left = 2;
		if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
		{
			if (_Magnification >= 2)
				rcDst.left += 5;
		}
	}
	else //(fs == 16)
	{
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if (_Magnification == 4)
				hMagnification = 3;
			else
				hMagnification = 2;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
		rcDst.bottom = 224*hMagnification - 3; //下側に表示
		if ((_Magnification == 4)||
			((_Magnification == 3)&&(scanlineType != 0)&&(scanlineType != 7)&&(scanlineType != 8)))
			    rcDst.top = rcDst.bottom - fs*2;
		else
		    rcDst.top = rcDst.bottom - fs;
		rcDst.left = 3 -_Magnification/4;
		if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
		{
			if (_Magnification == 1)
				rcDst.left += 5;
			else
				rcDst.left += 10;
		}
	}
	rcDst.top += (16+MAINBOARD_GetShowOverscanBottom())*hMagnification;	   //上下のオーバースキャン領域部分を足す。上は16ドットぶん必ず足す。
	rcDst.bottom += (16+MAINBOARD_GetShowOverscanBottom())*hMagnification; //
	if ((!APP_GetStretched())&&((_Flags & SCREEN_FFULLSCREEN) == 0)) //ウィンドウモードでストレッチをしない場合。v2.14更新
	{
		a = APP_GetNonstretchedWidth();
		//PRINTF("Test = %d",a);
		if ((a < 256)||
			((a > 256)&&(a < 336)))
		{
			if (_Magnification > 1)
				a = (a*2 / 2) - (rcSrc.right / 2);
			else
				a = (a / 2) - (rcSrc.right / 2);
			if (a > rcDst.left)
				rcDst.left = a; //左端を切らさないために中央に表示
		}
		else if (MAINBOARD_GetShowOverscanLeft() > 0) //左右のオーバースキャン領域を表示しているなら。v2.15追加
		{
			if (_Magnification == 1)
				rcDst.left += 8;
			else
				rcDst.left += 8*2;
		}
	}
	else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
	{
		if (_Magnification == 1)
			rcDst.left += 6;
		else
			rcDst.left += 6*2;
	}
	rcDst.right	= rcDst.left + rcSrc.right;
	if ((APP_GetOverscanHideBlackLR())&&(MAINBOARD_GetShowOverscanLeft() == 0)) //左右のオーバースキャン領域に黒帯を付けるなら
	{
		rcDst.right -= rcDst.left;
		rcDst.left = 0; //黒帯があるのでギリギリまで左に寄せても見栄えOK。
	}
	if ((APP_GetOverscanHideBlackBottom())&&(MAINBOARD_GetShowOverscanBottom() < 8)) //下側のオーバースキャン領域に黒帯を付けるなら
	{
		if ((8-MAINBOARD_GetShowOverscanBottom())*hMagnification >= 3) //黒帯部分が3ドット以上あった場合
		{
			a = rcDst.bottom - (16+224+MAINBOARD_GetShowOverscanBottom())*hMagnification;
			rcDst.bottom -= a;
			rcDst.top -= a; //黒帯があるのでギリギリまで下に寄せても見栄えOK。
		}
	}
	if (MAINBOARD_GetFourSplitScreen()) //妖怪道中記,ワールドコート,はにいいんざすかい,パワードリフト,サイコチェイサーの４分割画面の場合。v2.27更新
	{	//左側の黒帯部分を考慮
		if ((_Magnification == 1)||(bHalfMode))
		{
			rcDst.left  += 256/2;
			rcDst.right += 256/2;
		}
		else
		{
			rcDst.left  += 512/2;
			rcDst.right += 512/2;
		}
	}
	if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
	{
		rcDst.top -= 12*_Magnification;
		rcDst.bottom -= 12*_Magnification;
	}
	//転送
	if (_pDDSBack->Blt(&rcDst, _pDDSText, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
		_pDDSBack->Restore();
}


//画面の中央にメッセージを表示する。v2.77追加
static void
print_center_message(
	Sint32	executeCode)
{
	HDC			dc;
	HFONT		prevFont;
	HFONT		hFont;
	LOGFONT		logFont;
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	BOOL		bHalfMode;
	int			fs;
	Sint32		hMagnification; //縦の表示倍率
	LONG		adjust;
	Sint32		scanlineType = APP_GetScanlineType();

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

	//フォントサイズを決定
	if (_Magnification == 1)
		bHalfMode = FALSE;
	else
		bHalfMode = APP_GetHalfMode();
	if ((bHalfMode)||(_Magnification == 1))
		fs = 12; //フォントサイズ。v1.09更新
	else
		fs = 16;
	if (_Magnification <= 2)
	{
		if ((!bHalfMode)&&((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))) //※x2のHalfモード時はフォントが小さくてもOK。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
		{	//システムメモリを使用している場合、256より大きな解像度で文字が崩れるのでフォントを大きくする必要がある。v1.53
			if (VDC_GetTvWidth() >= 512)
				fs = 32;
			else if (VDC_GetTvWidth() > 256)
				fs = 28;
		}
		else
		{	//x2以下の場合、横512時に文字が読みづらいので大きくする
			if ((!bHalfMode)&&(VDC_GetTvWidth() >= 512))
				fs = 28;
		}
	}
	if ((_Magnification == 3)&&(!bHalfMode)&&
		((!APP_GetUseVideoCardMemory())||(APP_GetWindowsVista()))&&(VDC_GetTvWidth() >= 512)) //x3(非Halfモード)でシステムメモリ＆横512以上の場合。Vistaの場合ビデオメモリでもスムージングが掛からないので同様にフォントを大きくする。
			fs = 28;
	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);
	_pDDSText->ReleaseDC(dc);
	
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	if (fs == 12)
	{	//"x1" or "TV mode"
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if ((bHalfMode)&&(_Magnification >= 2))
			{
				if (_Magnification == 4)
					hMagnification = 3;
				else
					hMagnification = 2;
			}
			else
				hMagnification = 1;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
	}
	else
	{
		if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン
		{
			if (_Magnification == 4)
				hMagnification = 3;
			else
				hMagnification = 2;
		}
		else //ノンスキャンライン以外
			hMagnification = _Magnification;
	}

	//転送元位置＆サイズの調整
	rcSrc.left	= 0;
	rcSrc.top	= 0;
	rcSrc.right	= rc.right;
	rcSrc.bottom= fs;
	//転送先位置の調整＆表示した文字の範囲を見やすくするために拡大する。
	rcDst.left = 0;
	rcDst.top  = 0;
	if (_Magnification >= 3)
	{
		rcDst.right	= rcDst.left + rcSrc.right;
	    rcDst.bottom = fs * 2; //縦幅２倍拡大
	}
	else
	{
		rcDst.right	= rcDst.left + rcSrc.right;
	    rcDst.bottom = fs;
	}
	//センタリング
	GetWindowRect(_hWnd, &rc);
	adjust = 256 - (rcDst.right - rcDst.left) / 2;
	rcDst.left   += adjust;
	rcDst.right  += adjust;
	adjust = (rc.bottom - rc.top) / 2 - (rcDst.bottom - rcDst.top) / 2;
	rcDst.top    += adjust;
	rcDst.bottom += adjust;
	//転送
	if (_pDDSBack->Blt(&rcDst, _pDDSText, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
		_pDDSBack->Restore();
}


//Kitao追加。プライマリサーフェスへ転送する処理など。v2.00更新。上側のオーバースキャンライン領域もここで転送するようにした。
static void
zoom_ddTensou(
	Uint16*		pTvW,	//転送元の横ピクセル数。※[0]〜[239]の224+16(下部のOverscan)ラインぶん。ここには[0](先頭)のアドレスを入れる。
	Sint32		h,		//転送元の縦ピクセル数。通常は224。下部のオーバースキャンをする場合そのぶん増やしてここを呼ぶ。
	Sint32		executeCode)  //実行コード。0…エンコードだけ行う。1…プライマリ画面へ転送も行う。2…スクリーンショット用画面へ転送を行う。
							  //		    3…左右に黒帯(オーバースキャン部)を配置しての転送(あとは1と同じ)。4…スクリーンショット後の画面復元転送を行う。
							  //			5…左右のオーバースキャン部をカットしての転送(あとは1と同じ)。v2.12
							  //			6…周波数測定時用でfps表示や音グラフ表示は行わない(あとは1と同じ)。v2.94
							  //※DirectDrawはブラウン管CRTの低解像度利用時向け。音グラフ表示や今後の機能拡張へは対応しない予定です。v2.94記
{
	RECT		rcSrc;
	RECT		rcDst;
	RECT		rc;
	Sint32		a,b,c,d,s,s2,t,i,w2,h2;
	BOOL		bRasterChange = FALSE; //ラスタごとに異なる解像度が設定されていた場合TRUE
	Uint16*		pTvW2 = pTvW;
	Sint32		ot = 16-MAINBOARD_GetShowOverscanTop(); //転送元の開始Y座標。v2.00追加
	Sint32		scanlineType = APP_GetScanlineType();
	Sint32		tvWidth = VDC_GetTvWidth(); //PCE画面描画完了時点での横解像度モード。v2.98

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

	//転送元の横幅調整 
	s = (*pTvW2);
	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表示モードの場合、上下3ドットぶん転送先領域を狭める。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 //ウィンドウモードのとき
	{
		GetWindowRect(_hWnd, &rc);
		a = rc.left + (rc.right - rc.left - _Width)/2;
		b = rc.bottom - (rc.right - rc.left - _Width)/2 - _Height; //枠の太さと表示領域の高さぶんを引く
		if (APP_GetOverscanHideBlackTop()) //上側のオーバースキャン領域に黒帯を入れて隠す設定なら
			b += (8 - MAINBOARD_GetShowOverscanTop()) * _Magnification;
		c = a + 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); //v2.00記。gccだと-1してちょうどの値だったがVisualC++では-1しないのがピッタリくる。
				}
				else if (APP_GetStretched())
				{
					a += (Sint32)(6.8244 * (double)_Magnification + 0.5); //6.8244=298/262*6dot。v3.01更新
					c -= (Sint32)(6.8244 * (double)_Magnification + 0.5); //v2.00記。gccだと-1してちょうどの値だったがVisualC++では-1しないのがピッタリくる。
				}
			}
			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倍相当に縮める（1.143倍されたものから左右14ドットぶんだけ横圧縮する）
		c -= (Sint32)(14.0 * (double)_Magnification); //
	}
	if (APP_GetArcadeStretched() == 2)
	{
		a += (Sint32)(10.0 * (double)_Magnification); //横解像度を1.06倍相当に縮める（1.143倍されたものから左右10ドットぶんだけ横圧縮する）
		c -= (Sint32)(10.0 * (double)_Magnification); //
	}
	if (APP_GetArcadeStretched() == 3)
	{
		a += (Sint32)(6.0 * (double)_Magnification); //横解像度を1.09倍相当に縮める（1.143倍されたものから左右6ドットぶんだけ横圧縮する）
		c -= (Sint32)(6.0 * (double)_Magnification); //
	}
	_GameLeft   = a; //print_center_message()で表示位置決定のために、ゲーム画面転送先の座標を控えておく。
	_GameRight  = c; //
	_GameTop    = b; //
	_GameBottom = d; //

	if (executeCode != 2) //スクリーンショット用へ転送以外のとき
	{
		if (executeCode != 6) //周波数測定時の場合、オーディオグラフとFPS、左上テキストの表示は行わない。v2.94
		{
			//テキストメッセージの表示
			if (*_pMessageText != 0) //メッセージが設定されていれば
				print_message(executeCode);

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

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

	//垂直帰線期間を待つ
	if (executeCode != 4) //スクリーンショット後の復元時以外の場合
		SCREEN_WaitVBlank(FALSE);

	if (IsIconic(_hWnd)) //ウィンドウが最小化されているとき。hesを最小化して聴くときなどに処理を軽くする。
		if (executeCode != 2) //スクリーンショット用へ転送以外の場合
			return;

	//描画解像度の変更があった場合、ゴミが残らないように画面全体をクリアする。
	if ((MAINBOARD_GetResolutionChange()) ||
		((APP_GetArcadeStretched() > 0) && (executeCode == 2)))//v3.05追加。Arcadeストレッチモード時、描画解像度の変更があった場合（スクリーンショット時など）、ゴミが残らないように画面黒帯部分をクリアする。
			zoom_ddTensou256(); //プライマリサーフェスへ転送する

	//プライマリサーフェスへ転送する
	if (!bRasterChange)
	{	//通常
		s2 = 1;
		if (_Magnification >= 2) //ｘ２以上の場合
		{
			if ((scanlineType >= 1)&&(scanlineType <= 6)) //スキャンライン(縦横含む)時
			{
				if ((scanlineType != 3)&&(scanlineType != 4))
					s2 = 2; //横は２倍ドット固定。（速度アップ＋拡大されたときにバイリニアフィルタがいい感じにかかる）
				h  *= _Magnification; //縦は倍率ぶんのソースを用意して転送。
				ot *= _Magnification; //
			}
			else //ノンスキャンライン時
			{
				s2 = 2;
				if (_Magnification == 2)
				{
					h  *= _Magnification; //縦は倍率ぶんのソースを用意して転送。
					ot *= _Magnification; //
				}
				else
				{
					h  *= _Magnification-1; //3x,4xのときは、それぞれ2x,3xに拡大。（ジャギー軽減＆速度アップ）
					ot *= _Magnification-1; //
				}
			}
		}
		if (MAINBOARD_GetFourSplitScreen()) //妖怪道中記,ワールドコート,はにいいんざすかい,パワードリフト,サイコチェイサーの４分割画面の場合。v2.27更新
		{		
			SetRect(&rcSrc, s/4*s2, ot, (s/4+s/2)*s2, ot+h); //ソースの左右黒帯部分をカット
			//４分の１に縮小して４回転送
			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);
				if (_pDDSPrimary->Blt(&rcDst, _pDDSBack, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
					_pDDSPrimary->Restore();
				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-a) / _Magnification;
				//PRINTF("%d, %d", s,w2);//test
				w2 = (s-w2)/2;
				SetRect(&rcSrc, w2*s2, ot, (s-w2)*s2, ot+h); //転送元を転送先と同じドット数に合わせる。
			}
			else if (APP_GetOverscanTV()) //オーバースキャンTV表示モードの場合。v2.95
			{
				switch (tvWidth)
				{
					case 262: SetRect(&rcSrc, 3*s2, ot, (s-3)*s2, ot+h); break;//このとき左右黒帯部分のソースは3ドットx倍率。
					case 350: SetRect(&rcSrc, 4*s2, ot, (s-5)*s2, ot+h); break;//このとき左右黒帯部分のソースは4.01ドットx倍率。
					default: //512
						SetRect(&rcSrc, 0, ot, s*s2, ot+h); break;//このとき左右黒帯部分のソースはなし。
				}
			}
			else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
				SetRect(&rcSrc, 6*s2, ot, (s-6)*s2, ot+h);
			else //通常
			{
				if (tvWidth == 350) //横350表示の場合。v2.98更新
					SetRect(&rcSrc, 1 * s2, ot, s * s2, ot + h);
				else //通常
					SetRect(&rcSrc, 0, ot, s * s2, ot + h);
			}
			if (APP_GetArcadeStretched() == 4) //v3.05追加
			{
				//ソースの左右端をカットしてそのぶん横解像度を現状から横352ドットモード時1.04倍相当、横256モード時1.06倍相当に拡大する
				SetRect(&rcSrc, rcSrc.left+7*s2, rcSrc.top, rcSrc.right-7*s2, rcSrc.bottom); //7。7で横256モード時1.06倍相当。14で横256モード時1.12倍(ドットバイドットからは1.29倍)相当
			}
			if (_pDDSPrimary->Blt(&rcDst, _pDDSBack, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
				_pDDSPrimary->Restore();
		}
	}
	else
	{	//ラスタごとに異なる解像度が設定されていたとき。※Vistaだと超スロー(nVidia環境で確認)で使い物にならない。Direct3DならOK。
		//v1.28更新。１ラインずつ転送するようにした。
		s2 = 1;
		if (_Magnification >= 2) //ｘ２以上の場合
		{
			if ((scanlineType >= 1)&&(scanlineType <= 6)) //スキャンライン(縦横含む)時
			{
				if ((scanlineType != 3)&&(scanlineType != 4))
					s2 = 2; //ｘ３以上も横は２倍ドット固定。（速度アップ＋拡大されたときにバイリニアフィルタがいい感じにかかる）
				t = _Magnification; //縦は倍率ぶんのソースを用意して転送。
			}
			else
			{	//ノンスキャンライン時
				s2 = 2;
				if (_Magnification == 2)
					t = _Magnification; //縦は倍率ぶんのソースを用意して転送。
				else
					t = _Magnification-1; //3x,4xのときは、それぞれ2x,3xに拡大。（ジャギー軽減＆速度アップ）
			}
		}
		else
			t = 1;
		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*s2, (ot+i)*t, ((*pTvW)-3)*s2, (ot+i)*t+t); break; //このとき左右黒帯部分のソースは3ドットx倍率。
					case 350: SetRect(&rcSrc, 4*s2, (ot+i)*t, ((*pTvW)-4)*s2, (ot+i)*t+t); break; //このとき左右黒帯部分のソースは4.01ドットx倍率。
					case 512: SetRect(&rcSrc, 0, (ot+i)*t, (*pTvW)*s2, (ot+i)*t+t); break;//このとき左右黒帯部分のソースはなし。
				}
			}
			else if (executeCode == 5) //オーバースキャン領域の左右をカットする場合
				SetRect(&rcSrc, 6*s2, (ot+i)*t, ((*pTvW)-6)*s2, (ot+i)*t+t);
			else //通常
			{
				if ((*pTvW) == 350) //横350表示の場合。v2.98更新
					SetRect(&rcSrc, 1 * s2, (ot + i) * t, 350 * s2, (ot + i) * t + t);
				else
					SetRect(&rcSrc, 0, (ot + i) * t, (*pTvW) * s2, (ot + i) * t + t);
			}
			if (APP_GetArcadeStretched() == 4) //v3.05追加
			{
				//ソースの左右端をカットしてそのぶん横解像度を現状から横352ドットモード時1.04倍相当、横256モード時1.06倍相当に拡大する
				SetRect(&rcSrc, rcSrc.left+7*s2, rcSrc.top, rcSrc.right-7*s2, rcSrc.bottom); //7。7で横256モード時1.06倍相当。14で横256モード時1.12倍(ドットバイドットからは1.29倍)相当
			}
			if (_pDDSPrimary->Blt(&rcDst, _pDDSBack, &rcSrc, DDBLT_ASYNC, NULL) == DDERR_SURFACELOST)
				_pDDSPrimary->Restore();
			pTvW++;
		}
	}
}


/*
	ハードウェアに頼るストレッチ描画モード
	VRAM → VRAM 転送
	(ストレッチされた場合バイリニア補完がかかる)
*/
//Kitao更新。zoom3x()，zoom2x()，zoom1x()等も用意。速度重視のためそれぞれ別ルーチンに。ラインごとに解像度を変えているゲームに対応。

//Kitao更新。32ビットカラーにも対応。
static void
zoom4x_dd32(
	Uint32*		pSrc,			// 転送元のポインタ
	Sint32		srcY,			// 転送元のY座標
	Uint16*		pTvW,			// 各ラインの幅。Kitao追加
	Sint32		srcH)			// 転送元の高さ
{
	int			i,j; //Kitao追加
	Uint32*		pSrc0; //Kitao追加
	Uint32*		pDst; //Kitao追加
	Uint32*		pDst0; //Kitao追加
	Uint32		d; //Kitao追加
	Uint32		a; //Kitao追加

	// スキャンライン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++; //通常は"& 0xFFFFFF"でマスクするべきだが高速化のため省略。dが万が一0xFFFFFFを超えていたとしても、ここでは配列からは読み出しのみなので不具合にはならない。
			*pDst++ = _GammaTBL[d]; //4byteぶん(１ドット)書き込まれる。
			*pDst++ = _GammaTBLs90[d]; //4byteぶん(１ドット)書き込まれる。横を２倍拡大(90%縦スキャンラインで)
		}
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.34更新
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)。v2.34更新
		}
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.34更新
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)。v2.34更新
		}
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大(80%縦スキャンラインで)
		}
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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];
			*pDst++ = _GammaTBLs90[d]; //横を２倍拡大(90%縦スキャンラインで)
		}
		pDst = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大(90%縦スキャンラインで)
		}
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大(80%縦スキャンラインで)
		}
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
		{
			a = _GammaTBLs90[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
		{
			a = _GammaTBLs80[*pSrc++];
			*pDst++ = a;
			*pDst++ = a; //横を２倍拡大
		}
		pDst0 += _Pitch2;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	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; //横を２倍拡大(スキャンラインなし)
			}
			pDst0 += _Pitch;
		}
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// １倍なのでスキャンラインは無し
	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++];
		pDst0 += _Pitch;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインで。v2.35更新
			*pDst++ = _GammaTBLs90[*pSrc++];
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //３列目も90%スキャンラインで。v2.35更新
			*pDst++ = _GammaTBLs90[*pSrc++];
		pDst = pDst0 + _Pitch3;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		pDst0 += _Pitch4;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++) //２列目は90%スキャンラインにする。
			*pDst++ = _GammaTBLs90[*pSrc++];
		pDst = pDst0 + _Pitch2;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		pDst0 += _Pitch3;
		pSrc0 += SOURCE_PITCH;
		pTvW++;
	}
}

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

	// スキャンライン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 = pDst0 + _Pitch;
		pSrc = pSrc0;
		for (j = 0; j < *pTvW; j++)
			*pDst++ = _GammaTBLs80[*pSrc++];
		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))&&(bairitsu >= 2))
		sl = 2; //横スキャンラインかノンスキャンラインの場合
	else
		sl = 1;
	if ((scanlineType == 0)||(scanlineType == 7)||(scanlineType == 8)) //ノンスキャンライン時
	{
		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
		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 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++;
		}
	}
}


/*-----------------------------------------------------------------------------
	[Blt]
		pSrc からバックバッファへ画像を書き込みます。拡大／縮小、
-----------------------------------------------------------------------------*/
//Kitao更新。必ずハードウェアアクセラレーションを使用する。
//			 ラインごとに解像度を変えているゲーム（龍虎の拳，あすか120%など）にも対応。
void
SCREENDD_Blt(
	Uint32*		pSrc,
	Sint32		srcX,
	Sint32		srcY,
	Uint16*		pTvW,	//Kitao更新。転送元の横ピクセル数。※srcHラインの数ぶん
	Sint32		srcH,
	Sint32		executeCode)  //Kitao追加。実行コード。0…エンコードだけ行う。1…プライマリ画面へ転送も行う。2…スクリーンショット用画面へ転送を行う。
							  //					   3…左右に黒帯(オーバースキャン部)を配置しての転送(あとは1と同じ)。4…スクリーンショット後の画面復元転送を行う。
							  //					   5…左右のオーバースキャン部をカットしての転送(あとは1と同じ)。v2.12
							  //					   6…周波数測定時用でfps表示や音グラフ表示は行わない(あとは1と同じ)。v2.94
							  //※DirectDrawはブラウン管CRTの低解像度利用時向け。音グラフ表示や今後の機能拡張へは対応しない予定です。v2.94記
{
	Uint32*		pSrc32;
	Uint16*		pTvW2;
	Sint32		scanlineType = APP_GetScanlineType();

	if (pSrc==NULL) return;

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

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

			//Kitao更新。速度重視のためそれぞれの画面モードで、別々のルーチンを使う。
			if (MAINBOARD_GetMonoColor()) //白黒モードなら。はにいいんざすかい,パワードリフト等の裏技
			{	//白黒モードの場合。v2.28,2.29
				monoColor_32(_Magnification, scanlineType, pSrc32, srcY, pTvW2, srcH, (MAINBOARD_GetForceMonoColor() == 2)); //32bitカラー
			}
			else
			{	//通常
				switch (scanlineType)
				{
					case 0: //ノンスキャンライン
					case 7: //ノンスキャンライン2
					case 8: //ノンスキャンラインTV
					case 9: //ノンスキャンラインTV。v2.94追加。DirectDrawでは非対応のためうまく表示できない。
					case 10: //ノンスキャンラインTVぼやけ。v2.94追加。DirectDrawでは非対応のためうまく表示できない。
					case 11: //TV横スキャンライン2(ぼやけ)。v2.94追加。DirectDrawでは非対応のためうまく表示できない。
						switch (_Magnification)
						{
							case 1:
								zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 2:
							case 3:
							case 4:
								zoom2x3x4xNS_dd32(_Magnification, pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
						}
						break;
					case 1: //縦横スキャンライン
						switch (_Magnification)
						{
							case 1:
								zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 2:
								zoom2x_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 3:
								zoom3x_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 4:
								zoom4x_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
						}
						break;
					case 2: //横スキャンライン
					case 5: //横スキャンライン2
					case 6: //横スキャンライン3
						switch (_Magnification)
						{
							case 1:
								zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 2:
								zoom2xHS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 3:
								zoom3xHS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 4:
								zoom4xHS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
						}
						break;
					case 3: //横スキャンライン２(Fast)
					case 4: //TV横スキャンライン
						switch (_Magnification)
						{
							case 1: //ノンスキャンラインと同じ
								zoom1xNS_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 2:
								zoom2xTV_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 3:
								zoom3xTV_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
							case 4:
								zoom4xTV_dd32(pSrc32, srcY, pTvW2, srcH); //32bitカラー
								break;
						}
						break;
				}
			}
	
			//ロックを解除
			_pDDSBack->Unlock(NULL);
		}
	}		

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


//Kitao追加。VSync(垂直帰線待ち)を行うかどうかを設定
void
SCREENDD_SetVSyncScreen(
	BOOL	bVSyncScreen)
{
	DEVMODE		dm; //Kitao追加

	//ディスプレイの現在の表示周波数を求める。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); //メニュー更新
	}
}

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


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


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

	//バッファに再描画する。
	if ((_Flags & SCREEN_FFULLSCREEN)|| //フルスクリーン時。v2.98更新
		((APP_GetOverscanHideBlackLR())&&(MAINBOARD_GetShowOverscanLeft() == 0))|| //左右のオーバースキャン領域に黒帯表示していた場合
		((APP_GetOverscanHideBlackTop())&&(MAINBOARD_GetShowOverscanTop() < 8))) //上側のオーバースキャン領域に黒帯表示していた場合
			MAINBOARD_SetResolutionChange(TRUE); //描画時にゴミが残らないように画面全体をクリアしてから描画する。
	MAINBOARD_DrawScreenshot();
	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
	{
		GetWindowRect(_hWnd, &rc);
		left = rc.left + (rc.right - rc.left - _Width)/2;
		top = rc.bottom - (rc.right - rc.left - _Width)/2 - _Height; //枠の太さと表示領域の高さぶんを引く
	}

	//キャプチャするメモリをロック
	while (trial--)
	{
		hr = _pDDSPrimary->Lock(NULL, &_ddsdPrimary, DDLOCK_WAIT | DDLOCK_READONLY, NULL); //READONLYで高速化。v2.28
		if (hr == DD_OK)
			break;
    }
	if (hr != DD_OK) return;

	pitch = _ddsdPrimary.lPitch / 4; //32bit単位で処理するためピッチは1/4に。
	pPixels0 = (Uint32*)_ddsdPrimary.lpSurface + 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;
	}
	
	//ロックを解除
	_pDDSPrimary->Unlock(NULL);
}


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

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


//メイン(ゲーム)画面用オフスクリーンを１色で塗りつぶし、プライマリ画面へ転送する。v2.77更新
//※プライマリ画面へ転送したくない場合は、bDrawをFALSEにして呼ぶ。
void
SCREENDD_Fill(
	Uint32		color,
	BOOL		bDraw)
{
	int			i,j;
	LONG		pitch;
	Uint32*		pPixels;
	Uint32*		pPixels0;
	Uint16		tvW[256];

	if (lock_offscreen_surface(&pitch, &pPixels))
	{
		pPixels0 = pPixels;
		for (i = 0; i < (int)_ddsdBack.dwHeight; i++)
		{
			pPixels = pPixels0;
			for (j = 0; j < (int)_ddsdBack.dwWidth; j++)
				*pPixels++ = color; //4byteぶん(１ドット)書き込まれる。
			pPixels0 += pitch;
		}
		_pDDSBack->Unlock(NULL);
	}

	if (bDraw)
	{
		for (i = 0; i < 256; i++)
			tvW[i] = 256;
		zoom_ddTensou(tvW, 224, 1); //プライマリサーフェスへ転送する。
	}
}
