/*
###################################
#
# dxruby.c Ver. 1.0.0
#
###################################
*/
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION        (0x0800)            /* DirectInputバージョン定義 */
#define WINVER 0x0500            //    バージョン定義 Windows2000以上
#define _WIN32_WINNT WINVER

#include "ruby.h"
#include <stdlib.h>
#include <mmsystem.h>
#include <tchar.h>
#include <d3dx9.h>
#include <dmusici.h>
#include <dinput.h>
#include <memory.h>
#include <math.h>

#ifndef RSTRING_PTR
#  define RSTRING_PTR(s) (RSTRING(s)->ptr)
#endif
#ifndef RSTRING_LEN
#  define RSTRING_LEN(s) (RSTRING(s)->len)
#endif

#ifndef RARRAY_PTR
#  define RARRAY_PTR(s) (RARRAY(s)->ptr)
#endif
#ifndef RARRAY_LEN
#  define RARRAY_LEN(s) (RARRAY(s)->len)
#endif

#define RELEASE( x )  if( x ) { x->lpVtbl->Release( x ); x = NULL; }

#define PADMAX 2

#define P_LEFT     0
#define P_RIGHT    1
#define P_UP       2
#define P_DOWN     3
#define P_BUTTON0  4
#define P_BUTTON1  5
#define P_BUTTON2  6
#define P_BUTTON3  7
#define P_BUTTON4  8
#define P_BUTTON5  9
#define P_BUTTON6  10
#define P_BUTTON7  11
#define P_BUTTON8  12
#define P_BUTTON9  13
#define P_BUTTON10 14
#define P_BUTTON11 15
#define P_BUTTON12 16
#define P_BUTTON13 17
#define P_BUTTON14 18
#define P_BUTTON15 19
#define M_LBUTTON   0
#define M_RBUTTON   1
#define M_MBUTTON   2
#define FORMAT_JPEG D3DXIFF_JPG
#define FORMAT_JPG  D3DXIFF_JPG
#define FORMAT_PNG  D3DXIFF_PNG
#define FORMAT_BMP  D3DXIFF_BMP
#define WAVE_RECT 0
#define WAVE_SIN 1
#define WAVE_SAW 2
#define WAVE_TRI 3


static VALUE mWindow;       /* ウィンドウモジュール */
static VALUE mInput;        /* インプットモジュール */
static VALUE cImage;        /* イメージクラス       */
static VALUE cSound;        /* サウンドクラス       */
static VALUE cFont;         /* フォントクラス       */
static VALUE cSoundEffect;  /* 生成効果音クラス     */
static VALUE eDXRubyError;  /* 例外                 */


/* グローバル変数たち */
static HINSTANCE             g_hInstance   = NULL; /* アプリケーションインスタンス   */
static HANDLE                g_hWind       = NULL; /* ウィンドウハンドル             */
static LPDIRECT3D9           g_pD3D        = NULL; /* Direct3Dインターフェイス       */
static LPDIRECT3DDEVICE9     g_pD3DDevice  = NULL; /* Direct3DDeviceインターフェイス */
static D3DPRESENT_PARAMETERS g_D3DPP;              /* D3DDeviceの設定                */
static LPD3DXSPRITE          g_pD3DXSprite = NULL; /* スプライト                     */
static int g_bDeviceLost = FALSE;                  /* デバイスロスト時にTRUEになる   */

static LPDIRECTINPUT8        g_pDInput            = NULL; /* DirectInput */
static LPDIRECTINPUTDEVICE8  g_pDIDKeyBoard       = NULL; /* DirectInputのキーボードデバイス */
static LPDIRECTINPUTDEVICE8  g_pDIDJoyPad[PADMAX];        /* DirectInputのパッドデバイス     */
static BYTE g_diKeyState[256];                            /* DirectInputでのキーボード入力用バッファ */
static BYTE g_diKeyStateOld[256];                         /* DirectInputでのキーボード入力用バッファ１フレーム前 */
static BYTE g_diKeyCount[256];                            /* DirectInputでのキーボード入力用カウンタ */
static int g_JoystickCount = 0;
static BYTE g_byMouseState_L, g_byMouseStateOld_L;
static BYTE g_byMouseState_M, g_byMouseStateOld_M;
static BYTE g_byMouseState_R, g_byMouseStateOld_R;


static IDirectMusicPerformance8 *g_pDMPerformance = NULL;       /* DirectMusicPerformance8インターフェイス */
static IDirectMusicLoader8      *g_pDMLoader = NULL;            /* ローダー */
static LPDIRECTSOUND8           g_pDSound = NULL;               /* DirectSoundインターフェイス */
static int g_iRefDM = 0; /* DirectMusicパフォーマンスの参照カウント */
static int g_iRefDS = 0; /* DirectSoundの参照カウント */
static int g_iRefAll = 1; /* インターフェースの参照カウント */


/* ゲーム調整用 */
static int g_bActive = 0;

/* フレーム調整用 */
static __int64 g_OneSecondCount       = 0;         /* 一秒間にカウンタが数える数         */
static int     g_isPerformanceCounter = 0;         /* パフォーマンスカウンタがあったら１ */
static __int64 g_OldTime              = 0;         /* 前回のフレームが終わった時間       */
static __int64 g_OneFrameCount        = 0;         /* １フレームの処理にかかった時間     */

/* ウィンドウ情報構造体 */
static struct DXRubyWindowInfo {
    int x;              /* ｘ座標 */
    int y;              /* ｙ座標 */
    int width;          /* 幅     */
    int height;         /* 高さ   */
    int windowed;       /* ウィンドウモード時にQtrue */
    int created;        /* ウィンドウを作成したらQtrue */
    float scale;        /* ウィンドウのサイズ倍率 */
    int RefreshRate;    /* リフレッシュレート */
    int enablemouse;    /* マウスを表示するかどうか */
    int mousewheelpos;  /* マウスホイールの位置 */
    int fps;            /* fps */
    int fpscheck;       /* 現在のfps */
    int frameskip;      /* コマ落ち制御フラグ */
    int r;              /* 背景クリア色 赤成分 */
    int g;              /* 背景クリア色 緑成分 */
    int b;              /* 背景クリア色 青成分 */
} g_WindowInfo;

/* テクスチャデータ */
struct DXRubyTexture {
    LPDIRECT3DTEXTURE9 pD3DTexture;     /* スプライトに使うテクスチャ   */
	int width;
	int height;
	int refcount;
};

/* フォントデータ */
struct DXRubyFont {
    LPD3DXFONT pD3DXFont;       /* フォントオブジェクト   */
};

/* フォントデータ */
struct DXRubyFontSprite {
    struct DXRubyFont *font;    /* スプライトに使うフォント   */
    VALUE str;                  /* 文字列オブジェクト */
    int color;                  /* フォントの色 */
};

/* スプライトデータ */
struct DXRubySprite {
    void *image;                /* 描画するイメージオブジェクトもしくはフォント */
    int x;                      /* 描画位置 x */
    int y;                      /* 描画位置 y */
    float angle;                /* 回転角度 */
    float scalex;               /* 横拡大率 */
    float scaley;               /* 縦拡大率 */
    float centerx;              /* 回転時の中心 x */
    float centery;              /* 回転時の中心 y */
    unsigned char alpha;        /* アルファ（透明）値 */
    unsigned char addflag :1;   /* 半透明、加算合成のフラグ */
    unsigned char fontflag :1;  /* フォントなら１ */
    unsigned char reserve1 :6;  /* 予約 */
    char reserve2;              /* 予約 */
};

/* Imageオブジェクトの中身 */
struct DXRubyImage {
    struct DXRubyTexture *texture;
    float x;     /* x始点位置      */
    float y;     /* y始点位置      */
    float width; /* イメージの幅   */
    float height;/* イメージの高さ */
};

/* Soundオブジェクト */
struct DXRubySound {
    IDirectMusicAudioPath8   *pDMDefAudioPath; /* デフォルトオーディオパス */
    IDirectMusicSegment8     *pDMSegment;        /* セグメント       */
	int start;
	int loopstart;
	int loopend;
	int loopcount;
};

/* SoundEffectオブジェクト */
struct DXRubySoundEffect {
    LPDIRECTSOUNDBUFFER pDSBuffer;    /* バッファ         */
};

/* Pad情報 */
static struct DXRubyPadInfo {
    int right;
    int left;
    int up;
    int down;
    LPDIRECTINPUTDEVICE8  pDIDJoyPad; /* DirectInputのパッドデバイス */
} g_PadInfo[PADMAX];

/* Pad状態 */
static struct DXRubyPadState {
    char button[20];
    int PadConfig[20];
} g_PadState[PADMAX], g_PadStateOld[PADMAX];

/* スプライト配列 */
static struct DXRubySpriteList {
	float z;                        /* スプライトのZ座標 */
	struct DXRubySprite *sprite;    /* スプライト構造体へのポインタ */
} *g_SpriteList;
static int g_SpriteCount;                  /* スプライトの登録数 */
static int g_SpriteAllocateCount;          /* スプライト配列のメモリ確保数 */
static struct DXRubySprite *g_SpriteStruct;

/* シンボル */
static VALUE symbol_add     = Qundef;
static VALUE symbol_angle   = Qundef;
static VALUE symbol_alpha   = Qundef;
static VALUE symbol_scalex  = Qundef;
static VALUE symbol_scaley  = Qundef;
static VALUE symbol_centerx = Qundef;
static VALUE symbol_centery = Qundef;
static VALUE symbol_z       = Qundef;
static VALUE symbol_color   = Qundef;

/* キーのオートリピート用 */
static int g_iInputWait;
static int g_iInterval;

/* プロトタイプ宣言 */
static void InitWindow( void );
static void InitDXGraphics( void );
static void InitDirectInput( void );
LRESULT CALLBACK MainWndProc( HWND hWnd,UINT msg,UINT wParam,LONG lParam );
static void InitSync( void );
static __int64 GetSystemCounter( void );
static void Window_DirectXRelease( void );
static struct DXRubyTexture *Image_textureload( char *filename, D3DXIMAGE_INFO *psrcinfo );
static void Input_SetConfig( int number, int pad, int key );
static void ChangeSize( void );
static void Input_update( void );
static void Window_update( void );
static void Window_sync( int );
static void Window_create( void );
static void AllocSpriteList( void );
static void SortSpriteList( void );

static void release_Image( struct DXRubyImage* image );
static void release_Font( struct DXRubyFont* font );

/*********************************************************************
 * Windowモジュール
 * ウィンドウの管理・描画を行う。
 *********************************************************************/

/*--------------------------------------------------------------------
  （内部関数）ウィンドウの生成と初期化
 ---------------------------------------------------------------------*/
static void Window_create( void )
{
    HRESULT hr;
    RECT rect;

    /* ウィンドウのサイズ設定 */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width * g_WindowInfo.scale;
    rect.bottom  = g_WindowInfo.height * g_WindowInfo.scale;

    /* ウィンドウのサイズ修正 */
    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {   /* フルスクリーン */
        SetWindowLong( g_hWind, GWL_STYLE, WS_POPUP );
        SetWindowPos( g_hWind, HWND_TOP, 0, 0, g_WindowInfo.width, g_WindowInfo.height, SWP_NOZORDER);
    }
    else
    {   /* ウィンドウモード */
        SetWindowLong( g_hWind, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU );
        AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, FALSE );

        /* 幅と高さ計算 */
        rect.right   = rect.right - rect.left;
        rect.bottom  = rect.bottom - rect.top;

        /* ウィンドウ移動/サイズ設定 */
        if( g_WindowInfo.x == CW_USEDEFAULT )
        {   /* 位置がデフォルトの場合 */
            SetWindowPos( g_hWind, HWND_TOP, 0, 0, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
        }
        else
        {   /* 位置指定の場合 */
            SetWindowPos( g_hWind, HWND_TOP, g_WindowInfo.x, g_WindowInfo.y, rect.right, rect.bottom, SWP_NOZORDER );
        }
    }

    /* DirectXのスクリーンサイズ変更 */
    ChangeSize();

    /* ウィンドウ表示 */
    ShowWindow( g_hWind, SW_SHOWNORMAL );
    UpdateWindow( g_hWind );

    g_WindowInfo.created = Qtrue;

    return;
}


/*--------------------------------------------------------------------
  （内部関数）スクリーンサイズ変更
 ---------------------------------------------------------------------*/
static void ChangeSize( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;


    g_D3DPP.BackBufferWidth    = g_WindowInfo.width;
    g_D3DPP.BackBufferHeight   = g_WindowInfo.height;

    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {
        /* フルスクリーン時（32bitColor/60Hz固定) */
        g_D3DPP.BackBufferFormat           = D3DFMT_X8R8G8B8;
        g_D3DPP.Windowed                   = FALSE;
        g_D3DPP.FullScreen_RefreshRateInHz = 60;
        g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_ONE;
        g_WindowInfo.RefreshRate = 60;
    }
    else
    {
        /* ウィンドウモード時 */
        g_D3DPP.BackBufferFormat           = D3DFMT_UNKNOWN;
        g_D3DPP.Windowed                   = TRUE;
        g_D3DPP.FullScreen_RefreshRateInHz = 0;
        g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
        g_WindowInfo.RefreshRate = 0;
    }

    /* スプライトをロストさせる */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* 設定変更 */
    hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP );

    if( FAILED( hr ) )
    {
        if( hr == D3DERR_DEVICELOST )
        {
            g_bDeviceLost = TRUE;
        }
        else
        {
            rb_raise( eDXRubyError, "DirectX APIの呼び出しに失敗しました - Reset" );
        }
    }

    /* スプライト復帰 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
    }

    /* ビューポートの設定 */
    vp.X       = 0;
    vp.Y       = 0;
    vp.Width   = g_D3DPP.BackBufferWidth;
    vp.Height  = g_D3DPP.BackBufferHeight;
    vp.MinZ    = 0.0f;
    vp.MaxZ    = 1.0f;

    hr = g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "設定に失敗しました - SetViewport" );
    }
}


/*--------------------------------------------------------------------
   Windowsのメッセージループ
 ---------------------------------------------------------------------*/
static VALUE Window_loop( VALUE obj )
{
    MSG msg;
    VALUE ary[2];
    msg.message = WM_NULL;

    Window_create();

    /* シーンのクリア */
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB( g_WindowInfo.r, g_WindowInfo.g, g_WindowInfo.b ), 0, 0 );
    /* とりあえず画面を黒に初期化 */
    g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );

    /* フレーム調整処理初期化 */
    InitSync();

    /* メッセージループ */
    /* WM_QUITが届くまでメッセージ処理をし続ける */
    /* メッセージが無い時はブロックをyieldする */
    while( msg.message != WM_QUIT )
    {
        if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) != 0)
        {
            /* メッセージがある時 */
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            /* メッセージが無い時 */
            /* 入力状態更新 */
            Input_update();
            /* ブロック実行 */
            rb_yield( obj );
            /* fps調整 */
            Window_sync( g_WindowInfo.fps );
            /* 描画 */
            Window_update();
        }
    }

    return Qtrue;
}


/*--------------------------------------------------------------------
   （内部関数）ウィンドウプロシージャ
 ---------------------------------------------------------------------*/
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    RECT rect;
    
    switch( msg )
    {
    /* ウィンドウアクティブ／非アクティブ化 */
    case WM_ACTIVATE:
        g_bActive = (LOWORD(wParam) != 0);
        break;
    /* ウィンドウ破棄 */
    case WM_DESTROY:

        Window_DirectXRelease();

        /* ウインドウを閉じる */
        PostQuitMessage( 0 );
        g_hWind = NULL;
        return 0;

    case WM_MOUSEWHEEL:
        g_WindowInfo.mousewheelpos += (short)HIWORD(wParam);
        break;
/*
    case WM_SETCURSOR:
        if( g_pD3DDevice )
        {
            if( LOWORD( lParam ) == HTCLIENT )  // クライアント領域のみ
            {
                if( g_WindowInfo.enablemouse == Qfalse )
                {
                    SetCursor( NULL );              // ウインドウの標準カーソルを消す
                    return TRUE;                    // 設定を終えたことを通知
                }
            }
        }
        break;
*/
    }

    /* デフォルト処理 */
    return DefWindowProc( hWnd, msg, wParam, lParam );
}


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE Window_setbgcolor( VALUE obj, VALUE array )
{
    Check_Type(array, T_ARRAY);

    g_WindowInfo.r = NUM2INT( rb_ary_entry(array, 0) );
    g_WindowInfo.g = NUM2INT( rb_ary_entry(array, 1) );
    g_WindowInfo.b = NUM2INT( rb_ary_entry(array, 2) );

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（通常描画）
 ---------------------------------------------------------------------*/
static VALUE Window_draw( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].addflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（拡大縮小描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawScale( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 5 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = NUM2DBL( argv[3] );
    g_SpriteStruct[g_SpriteCount].scaley = NUM2DBL( argv[4] );
    g_SpriteStruct[g_SpriteCount].centerx = argc < 6 || argv[5] == Qnil  ? image->width / 2 : NUM2DBL( argv[5] );
    g_SpriteStruct[g_SpriteCount].centery = argc < 7 || argv[6] == Qnil  ? image->height / 2 : NUM2DBL( argv[6] );
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].addflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 8 || argv[7] == Qnil ? 0.0f : NUM2DBL( argv[7] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（回転描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawRot( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = NUM2DBL( argv[3] );
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].addflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = argc < 5 || argv[4] == Qnil  ? image->width / 2 : NUM2DBL( argv[4] );
    g_SpriteStruct[g_SpriteCount].centery = argc < 6 || argv[5] == Qnil  ? image->height / 2 : NUM2DBL( argv[5] );
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 7 || argv[6] == Qnil ? 0.0f : NUM2DBL( argv[6] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（半透明描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAlpha( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = NUM2INT( argv[3] );
    g_SpriteStruct[g_SpriteCount].addflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（加算合成描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAdd( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].addflag = 1;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
static VALUE Window_drawEx( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    VALUE x, y, z, data, angle, scalex, scaley, alpha, centerx, centery, add;
    VALUE option;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    if( argc < 4 || argv[3] == Qnil )
    {
        option = rb_hash_new();
    }
    else
    {
        Check_Type( argv[3], T_HASH );
        option = argv[3];
    }

    AllocSpriteList();

    add = rb_hash_aref( option, symbol_add );
    angle = rb_hash_aref( option, symbol_angle );
    alpha = rb_hash_aref( option, symbol_alpha );
    scalex = rb_hash_aref( option, symbol_scalex );
    scaley = rb_hash_aref( option, symbol_scaley );
    centerx = rb_hash_aref( option, symbol_centerx );
    centery = rb_hash_aref( option, symbol_centery );
    z = rb_hash_aref( option, symbol_z );

     /* 引数のイメージオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[2], struct DXRubyImage, image );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle   = (angle   == Qnil ? 0.0f             : NUM2DBL( angle   ));
    g_SpriteStruct[g_SpriteCount].scalex  = (scalex  == Qnil ? 1.0f             : NUM2DBL( scalex  ));
    g_SpriteStruct[g_SpriteCount].scaley  = (scaley  == Qnil ? 1.0f             : NUM2DBL( scaley  ));
    g_SpriteStruct[g_SpriteCount].centerx = (centerx == Qnil ? image->width / 2 : NUM2DBL( centerx ));
    g_SpriteStruct[g_SpriteCount].centery = (centery == Qnil ? image->height / 2 : NUM2DBL( centery ));
    g_SpriteStruct[g_SpriteCount].alpha   = (alpha   == Qnil ? 0xff             : NUM2INT( alpha   ));
    g_SpriteStruct[g_SpriteCount].addflag = (add == Qnil || add == Qfalse ) ? 0 : 1;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z == Qnil ? 0.0f : NUM2DBL( z );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   フォント描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite *spr;
    struct DXRubyFont *font;
    VALUE color;
    int cr, cg, cb;
    VALUE z, angle, scalex, scaley, alpha, centerx, centery, add;
    VALUE option;
    struct DXRubyFontSprite *fontsprite;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 4);

    Check_Type(argv[2], T_STRING);

    if( argc < 5 || argv[4] == Qnil )
    {
        option = rb_hash_new();
    }
    else
    {
        Check_Type( argv[4], T_HASH );
        option = argv[4];
    }

    add = rb_hash_aref( option, symbol_add );
    angle = rb_hash_aref( option, symbol_angle );
    alpha = rb_hash_aref( option, symbol_alpha );
    scalex = rb_hash_aref( option, symbol_scalex );
    scaley = rb_hash_aref( option, symbol_scaley );
    centerx = rb_hash_aref( option, symbol_centerx );
    centery = rb_hash_aref( option, symbol_centery );
    z = rb_hash_aref( option, symbol_z );
    color = rb_hash_aref( option, symbol_color );

    AllocSpriteList();

    /* 引数のフォントオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[3], struct DXRubyFont, font );
    if (RDATA(argv[3])->dfree != (RUBY_DATA_FUNC)release_Font)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Font)", rb_obj_classname( argv[3] ));
    }

    fontsprite = ALLOC( struct DXRubyFontSprite );
    if( fontsprite == NULL )
    {
        rb_raise( eDXRubyError, "フォント用メモリの確保に失敗しました" );
    }
    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = fontsprite;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle   = (angle   == Qnil ? 0.0f             : NUM2DBL( angle ));
    g_SpriteStruct[g_SpriteCount].scalex  = (scalex  == Qnil ? 1.0f             : NUM2DBL( scalex ));
    g_SpriteStruct[g_SpriteCount].scaley  = (scaley  == Qnil ? 1.0f             : NUM2DBL( scaley ));
    g_SpriteStruct[g_SpriteCount].centerx = (centerx == Qnil ? 0.0f             : NUM2DBL( centerx ));;
    g_SpriteStruct[g_SpriteCount].centery = (centery == Qnil ? 0.0f             : NUM2DBL( centery ));;
    g_SpriteStruct[g_SpriteCount].alpha   = (alpha   == Qnil ? 0xff             : NUM2INT( alpha ));
    g_SpriteStruct[g_SpriteCount].addflag = (add == Qnil || add == Qfalse ) ? 0 : 1;
    g_SpriteStruct[g_SpriteCount].fontflag = 1;
    fontsprite->str = argv[2];
    if( color != Qnil )
    {
        Check_Type(color, T_ARRAY);
        if( RARRAY_LEN(color) == 4 )
        {
            g_SpriteStruct[g_SpriteCount].alpha = NUM2INT(rb_ary_entry(color, 0));
            cr = NUM2INT(rb_ary_entry(color, 1));
            cg = NUM2INT(rb_ary_entry(color, 2));
            cb = NUM2INT(rb_ary_entry(color, 3));
        }
        else
        {
            cr = NUM2INT(rb_ary_entry(color, 0));
            cg = NUM2INT(rb_ary_entry(color, 1));
            cb = NUM2INT(rb_ary_entry(color, 2));
        }
    }
    else
    {
        cr = 255;
        cg = 255;
        cb = 255;
    }
    fontsprite->color = D3DCOLOR_XRGB(cr, cg, cb);
    fontsprite->font = font;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z == Qnil ? 0.0f : NUM2DBL( z );
    g_SpriteCount++;

    return obj;
}


typedef struct tag_dx_TLVERTEX {
    float           x, y, z;
    D3DCOLOR        color;
    float           tu, tv;
}TLVERTX, *LPTLVERTEX;

/*--------------------------------------------------------------------
   （内部関数）画面更新
 ---------------------------------------------------------------------*/
static void Window_update( void )
{
    HRESULT hr;
    static int max;

    /* デバイスロスト時の復元処理 */
    if( g_bDeviceLost )
    {
        Sleep( 100 );    /* 0.1秒待つ */

        /* デバイス状態のチェック */
        hr  = g_pD3DDevice->lpVtbl->TestCooperativeLevel( g_pD3DDevice );
        if( FAILED( hr ) )
        {
            if( hr == D3DERR_DEVICELOST )
                return;  /* デバイスはロスト状態である */

            if( hr != D3DERR_DEVICENOTRESET ) /* よくわからんエラー */
            {
                   rb_raise( eDXRubyError, "DirectX APIの呼び出しに失敗しました - TestCooperativeLevel" );
            }

            /* ここへ来た時はデバイスがリセット可能状態である */
            /* スプライトをロストさせる */
            if( g_pD3DXSprite )
            {
                g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
            }

            hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP ); /* 復元を試みる */

            if( FAILED( hr ) )
            {
                if( hr == D3DERR_DEVICELOST )
                {
                    return; /* またロストした */
                }
                rb_raise( eDXRubyError, "DirectX APIの呼び出しに失敗しました - Reset" );
            }

            if( g_pD3DXSprite ) /* スプライト復帰 */
            {
                g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
            }

        }
        /* デバイスが復元した */
        g_bDeviceLost = FALSE;
    }

    /* 描画対象が1つも無ければ描画処理そのものを行わない */
    if( g_SpriteCount == 0 )
    {
	    /* シーンの表示 */
	    hr = g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );
	    /* デバイスロスト判定 */
	    if( hr == D3DERR_DEVICELOST )
	    {
	        g_bDeviceLost = TRUE;
	    }
        return;
    }

    /* シーンのクリア */
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB( g_WindowInfo.r, g_WindowInfo.g, g_WindowInfo.b ), 0, 0 );

	/* シーンの描画開始 */
    if( SUCCEEDED( g_pD3DDevice->lpVtbl->BeginScene( g_pD3DDevice ) ) )
    {
		int i;
    	int oldflag = 0;

        /* スプライトの描画開始 */
        g_pD3DXSprite->lpVtbl->Begin( g_pD3DXSprite, D3DXSPRITE_ALPHABLEND );

        /* スプライトのz順ソート */
        SortSpriteList();

        /* スプライトリストに登録されたもののうちスプライトを順に描画 */
        for( i = 0; i < g_SpriteCount; i++ )
        {
            float angle;
            struct DXRubySprite *temp;

            temp = g_SpriteList[i].sprite;

            if( temp->addflag != oldflag ) 
            {

                if( temp->addflag == 1) 
                {
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_ONE);	/* 加算合成の設定 */
                }
                else
                {
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);	/* 半透明合成 */
                }
            }

            oldflag = temp->addflag;

            angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * temp->angle;

            if( temp->fontflag == 0)
            {
                TLVERTX VertexDataTbl[4];
                struct DXRubyImage *image = temp->image;
                float sina = sin(angle);
                float cosa = cos(angle);
                float data1x = temp->scalex * cosa;
                float data2x = temp->scalex * sina;
                float data1y = temp->scaley * sina;
                float data2y = temp->scaley * cosa;
                float tu1 = image->x / image->texture->width;
                float tu2 = (image->x + image->width) / image->texture->width;
                float tv1 = image->y / image->texture->height;
                float tv2 = (image->y + image->height) / image->texture->height;
                float centerx = temp->centerx;
                float centery = temp->centery;
                float x = temp->x;
                float y = temp->y;
                float width = image->width;
                float height = image->height;

                /* 頂点１のデータ */
                VertexDataTbl[0].x      =  -centerx * data1x - (-centery) * data1y + x + centerx;
                VertexDataTbl[0].y      =  -centerx * data2x + (-centery) * data2y + y + centery;
                VertexDataTbl[0].z      =  0.0f;                                    /* Ｚ座標 */
                VertexDataTbl[0].tu     =  tu1;                                     /* テクスチャのＸ座標 */
                VertexDataTbl[0].tv     =  tv1;                                     /* テクスチャのＹ座標 */
                VertexDataTbl[0].color  =  D3DCOLOR_ARGB(temp->alpha,255,255,255);  /* 頂点色 */
                /* 頂点２のデータ */
                VertexDataTbl[1].x      =  (-centerx+width) * data1x - (-centery) * data1y + x + centerx;
                VertexDataTbl[1].y      =  (-centerx+width) * data2x + (-centery) * data2y + y + centery;
                VertexDataTbl[1].z      =  0.0f;
                VertexDataTbl[1].tu     =  tu2;
                VertexDataTbl[1].tv     =  tv1;
                VertexDataTbl[1].color  =  D3DCOLOR_ARGB(temp->alpha,255,255,255);
                /* 頂点３のデータ */
                VertexDataTbl[2].x      =  (-centerx+width) * data1x - (-centery+height) * data1y + x + centerx;
                VertexDataTbl[2].y      =  (-centerx+width) * data2x + (-centery+height) * data2y + y + centery;
                VertexDataTbl[2].z      =  0.0f;
                VertexDataTbl[2].tu     =  tu2;
                VertexDataTbl[2].tv     =  tv2;
                VertexDataTbl[2].color  =  D3DCOLOR_ARGB(temp->alpha,255,255,255);
                /* 頂点４のデータ */
                VertexDataTbl[3].x      =  (-centerx) * data1x - (-centery+height) * data1y + x + centerx;
                VertexDataTbl[3].y      =  (-centerx) * data2x + (-centery+height) * data2y + y + centery;
                VertexDataTbl[3].z      =  0.0f;
                VertexDataTbl[3].tu     =  tu1;
                VertexDataTbl[3].tv     =  tv2;
                VertexDataTbl[3].color  =  D3DCOLOR_ARGB(temp->alpha,255,255,255);

                /* テクスチャをセット */
                g_pD3DDevice->lpVtbl->SetTexture(g_pD3DDevice, 0, (IDirect3DBaseTexture9*)image->texture->pD3DTexture);

                /* デバイスに使用する頂点フォーマットをセット */
                g_pD3DDevice->lpVtbl->SetFVF(g_pD3DDevice, D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

                /* 描画 */
                g_pD3DDevice->lpVtbl->DrawPrimitiveUP(g_pD3DDevice, D3DPT_TRIANGLEFAN, 2, VertexDataTbl, sizeof(TLVERTX));
            }
            else
            {
                D3DVECTOR vector;
                D3DXMATRIX matrix;
                D3DXMATRIX matrix_t;
                RECT rect;

                /* 回転及び拡大縮小 */
                D3DXMatrixScaling    ( &matrix_t, temp->scalex, temp->scaley, 1 );
                D3DXMatrixRotationZ  ( &matrix  , angle );
                D3DXMatrixMultiply   ( &matrix  , &matrix_t, &matrix );

                /* 平行移動 */
                D3DXMatrixTranslation( &matrix_t, (float)temp->x + temp->centerx, (float)temp->y + temp->centery, 0 );
                D3DXMatrixMultiply   ( &matrix  , &matrix, &matrix_t );

                g_pD3DXSprite->lpVtbl->SetTransform( g_pD3DXSprite, &matrix );

                rect.left   = -temp->centerx;
                rect.top    = -temp->centery;
                rect.right  = temp->centerx;
                rect.bottom = temp->centery;
                ((struct DXRubyFontSprite *)temp->image)->font->pD3DXFont->lpVtbl->DrawText(((struct DXRubyFontSprite *)temp->image)->font->pD3DXFont, g_pD3DXSprite,
                                                        RSTRING_PTR(((struct DXRubyFontSprite *)temp->image)->str),
                                                        -1,
                                                        &rect,
                                                        DT_LEFT | DT_NOCLIP,
                                                        ((int)temp->alpha << 24) | ((struct DXRubyFontSprite *)temp->image)->color & 0x00ffffff);
                g_pD3DXSprite->lpVtbl->Flush( g_pD3DXSprite );
                xfree( temp->image );
            }
        }

        /* スプライトの描画終了 */
        g_pD3DXSprite->lpVtbl->End( g_pD3DXSprite );

        /* シーンの描画終了 */
        g_pD3DDevice->lpVtbl->EndScene( g_pD3DDevice );
    }

    /* シーンの表示 */
    hr = g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );

    /* デバイスロスト判定 */
    if( hr == D3DERR_DEVICELOST )
    {
        g_bDeviceLost = TRUE;
    }

    g_SpriteCount = 0;
    return;
}


/*--------------------------------------------------------------------
  （内部関数）フレーム調整
 ---------------------------------------------------------------------*/
static void Window_sync( int frame )
{
    __int64 NowTime;
    __int64 WaitTime;
    static __int64 BeforeSecond = 0;
    static int fps = 0;
    static int skip = 0;

    NowTime = GetSystemCounter();
    g_OneFrameCount = NowTime - g_OldTime;

    /* ウィンドウモード時 */
    if( g_WindowInfo.windowed != Qnil && g_WindowInfo.windowed != Qfalse )
    {
        if ( frame > 0 ) /* fps指定がnil or 0の時はWait処理しない */
        {
            __int64 SleepTime;

            WaitTime = g_OneSecondCount / frame;

            /* もう既に前回から１フレーム分の時間が経っていたら */
            if( g_OldTime + WaitTime < NowTime && skip == 0 )
            {
                /* コマ落ち制御しない場合 */
                if( g_WindowInfo.frameskip == Qfalse )
                {
                    fps++;
                    g_OldTime = NowTime;
                }
                else /* する場合 */
                {
                    /* 今回はウェイトも描画もしない */
                    skip++;
                    g_SpriteCount = 0;
                    g_OldTime = g_OldTime + WaitTime;
                }
            }
            else
            {
                /* 前回描画を飛ばしたのに今回も間に合ってない場合 */
                if( g_OldTime + WaitTime < NowTime && skip == 1 )
                {
                    /* 諦めて処理落ち */
                    g_OldTime = NowTime;
                }
                else
                {
                    __int64 TempTime;
                    /* おおまかな時間をSleepで待つ */
                    TempTime = GetSystemCounter();
                    SleepTime = (WaitTime - (TempTime - g_OldTime)) * 1000 / g_OneSecondCount;
                    if( SleepTime > 2 )
                    {
                        Sleep( (unsigned long)(SleepTime - 2) );
                    }

                    TempTime = GetSystemCounter();

                    /* ループで厳密に処理をする */
                    for ( ; ; )
                    {
                        TempTime = GetSystemCounter();
                        if( g_OldTime +  WaitTime < TempTime )
                        {
                            break;
                        }
                    }
                    g_OldTime = g_OldTime +  WaitTime;
                }
                skip = 0;
                fps++;
            }
        }
        else
        {
            g_OldTime = NowTime;
            fps++;
        }

        /* FPS値設定 */
        if( (NowTime - BeforeSecond) >= g_OneSecondCount )
        {
            BeforeSecond = NowTime;
            g_WindowInfo.fpscheck = fps;
            fps = 0;
        }
    }
    else
    {
        WaitTime = g_OneSecondCount / g_WindowInfo.RefreshRate;

        /* もう既に前回から１フレーム分の時間が経っていたら */
        if( g_OldTime +  WaitTime < NowTime && skip == 0 )
        {
            /* 今回はウェイトも描画もしない */
            skip++;
            g_SpriteCount = 0;
            g_OldTime = NowTime;
            return;
        }

        skip = 0;

        g_OldTime = g_OldTime +  WaitTime;
        fps++;

        /* FPS値設定 */
        if( (NowTime - BeforeSecond) >= g_OneSecondCount )
        {
            BeforeSecond = NowTime;
            g_WindowInfo.fpscheck = fps;
            fps = 0;
        }
    }

    return;
}
/*--------------------------------------------------------------------
  （内部関数）フレーム調整用カウンタ値取得
 ---------------------------------------------------------------------*/
static __int64 GetSystemCounter( void )
{
    __int64 time;

    if( g_isPerformanceCounter == 1 )
    {
        QueryPerformanceCounter( (LARGE_INTEGER *)&time );
        return time;
    }
    else
    {
        return timeGetTime();
    }
}


/*--------------------------------------------------------------------
   ウィンドウのモード（ウィンドウ/全画面）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setwindowed( VALUE obj, VALUE windowed )
{
    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setwindowed" );
    }

    g_WindowInfo.windowed = windowed;
    return g_WindowInfo.windowed;
}


/*--------------------------------------------------------------------
   ウィンドウの位置（x座標）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setx( VALUE obj, VALUE x )
{
    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setx" );
    }

    g_WindowInfo.x = NUM2INT( x );
    return x;
}


/*--------------------------------------------------------------------
   ウィンドウの位置（y座標）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_sety( VALUE obj , VALUE y )
{
    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_sety" );
    }

    g_WindowInfo.y = NUM2INT( y );
    return y;
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setwidth( VALUE obj, VALUE vwidth )
{
    int width;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setwidth" );
    }

    width = NUM2INT( vwidth );
    if( width < 0 )
    {
        width = 0;
    }

    g_WindowInfo.width = width;
    return vwidth;
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（高さ）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setheight( VALUE obj , VALUE vheight)
{
    int height;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setheight" );
    }

    height = NUM2INT( vheight );
    if( height < 0 )
    {
        height = 0;
    }

    g_WindowInfo.height = height;
    return vheight;
}


/*--------------------------------------------------------------------
  ウィンドウタイトル設定
 ---------------------------------------------------------------------*/
static VALUE Window_setCaption( VALUE obj, VALUE caption )
{
    Check_Type(caption, T_STRING);

    SetWindowText( g_hWind, RSTRING_PTR( caption ) );

    return caption;
}


/*--------------------------------------------------------------------
  ウィンドウのサイズ倍率を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setScale( VALUE obj, VALUE scale )
{
    g_WindowInfo.scale = NUM2DBL(scale);

    return scale;
}


/*--------------------------------------------------------------------
  fps値を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setfps( VALUE obj, VALUE fps )
{
    g_WindowInfo.fps = fps == Qnil ? 0 : NUM2INT(fps);

    return fps;
}


/*--------------------------------------------------------------------
  fps値を取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getfps( VALUE obj )
{
    return INT2FIX( g_WindowInfo.fpscheck );
}


/*--------------------------------------------------------------------
  1フレームの処理負荷を％で取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getload( VALUE obj )
{
    if( g_WindowInfo.fps == 0 )
    {
        return INT2FIX( 0 );
    }
    return rb_float_new( (double) ( (double)g_OneFrameCount * 100.0 / ((double)g_OneSecondCount / (double)g_WindowInfo.fps )) );
}


/*--------------------------------------------------------------------
  フレームスキップon/offを設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setframeskip( VALUE obj, VALUE skip )
{
    g_WindowInfo.frameskip = (skip == Qnil || skip == Qfalse) ? Qfalse : Qtrue;

    return skip;
}


/*--------------------------------------------------------------------
  ファイルオープンダイアログを表示する
 ---------------------------------------------------------------------*/
static VALUE Window_openDialog(VALUE obj, VALUE vfilter, VALUE vtitle)
{
    OPENFILENAME OFN; 
    char buf[MAX_PATH*2];
	VALUE filter;
	buf[0] = 0;

	Check_Type(vfilter, T_ARRAY);
	Check_Type(vtitle, T_STRING);

	ZeroMemory(&OFN,sizeof(OPENFILENAME));
    OFN.lStructSize = sizeof(OPENFILENAME); 
    OFN.hwndOwner = g_hWind;

	OFN.lpstrFilter = "すべてのファイル (*.*)\0*.*\0\0";
	filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
	filter = rb_str_cat( filter, "\0", 1 );
	OFN.lpstrFilter = RSTRING_PTR(filter);
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_FILEMUSTEXIST;
	OFN.lpstrTitle = RSTRING_PTR(vtitle);
	OFN.lpstrDefExt = 0;
	if( !GetOpenFileName(&OFN) )
	{
        return Qnil;
	}

	return rb_str_new2( buf );
}


/*--------------------------------------------------------------------
  ファイルセーブダイアログを表示する
 ---------------------------------------------------------------------*/
static VALUE Window_saveDialog(VALUE obj, VALUE vfilter, VALUE vtitle)
{
    OPENFILENAME OFN; 
    char buf[MAX_PATH*2];
	VALUE filter;
	buf[0] = 0;

	Check_Type(vfilter, T_ARRAY);
	Check_Type(vtitle, T_STRING);

	ZeroMemory(&OFN,sizeof(OPENFILENAME));
    OFN.lStructSize = sizeof(OPENFILENAME); 
    OFN.hwndOwner = g_hWind;

	OFN.lpstrFilter = "すべてのファイル (*.*)\0*.*\0\0";
	filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
	filter = rb_str_cat( filter, "\0", 1 );
	OFN.lpstrFilter = RSTRING_PTR(filter);
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
	OFN.lpstrTitle = RSTRING_PTR(vtitle);
	OFN.lpstrDefExt = 0;
	if( !GetSaveFileName(&OFN) )
	{
        return Qnil;
	}

	return rb_str_new2( buf );
}


/*--------------------------------------------------------------------
   終了時に実行する
 ---------------------------------------------------------------------*/
static VALUE Window_shutdown( VALUE obj )
{
	/* マウス状態復元 */
    if( g_WindowInfo.enablemouse == Qfalse )
    {
        int c;
        c = ShowCursor( TRUE );
        while( c < 0 ) c = ShowCursor( TRUE );
    }

    /* リスト解放 */
	g_SpriteCount = 0;

    /* DirectX解放 */
    Window_DirectXRelease();

    /* ウインドウ・クラスの登録解除 */
    UnregisterClass( "DXRuby", g_hInstance );

    /* フレーム調整の後始末 */
    timeEndPeriod( 1 );

	g_iRefAll--;
	if( g_iRefAll == 0 )
	{
		CoUninitialize();
	}

    return obj;
}


/*--------------------------------------------------------------------
   DirectXオブジェクトを解放する
 ---------------------------------------------------------------------*/
void Window_DirectXRelease()
{
    int i;
    HRESULT hr;

    /* スプライトオブジェクトの使用終了 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* スプライトオブジェクト破棄 */
    RELEASE( g_pD3DXSprite );

    for( i = 0 ; i < g_JoystickCount; i++ )
    {
        /* DirectInputDevice(JoyPad)オブジェクトの使用終了 */
        if( g_pDIDJoyPad[i] )
        {
            g_pDIDJoyPad[i]->lpVtbl->Unacquire( g_pDIDJoyPad[i] ); 
        }

        /* DirectInputDevide(JoyPad)オブジェクトの破棄 */
        RELEASE( g_pDIDJoyPad[i] );
    }

    /* DirectInputDevice(Keyboard)オブジェクトの使用終了 */
    if( g_pDIDKeyBoard )
    {
           g_pDIDKeyBoard->lpVtbl->Unacquire( g_pDIDKeyBoard ); 
    }

    /* DirectInputDevide(Keyboard)オブジェクトの破棄 */
    RELEASE( g_pDIDKeyBoard );

    /* DirectInputオブジェクトの破棄 */
    RELEASE( g_pDInput );

    /* Direct3D Deviceオブジェクトの破棄 */
    RELEASE( g_pD3DDevice );

	/* Direct3Dオブジェクトの破棄 */
    RELEASE( g_pD3D );
}


/*--------------------------------------------------------------------
   スクリーンショットを撮る
 ---------------------------------------------------------------------*/
static VALUE Window_getScreenShot( int argc, VALUE *argv, VALUE obj )
{
	HRESULT hr;
	D3DDISPLAYMODE dmode;
	LPDIRECT3DSURFACE9 pSurface;
	RECT rect;
    VALUE filename, format;

    rb_scan_args( argc, argv, "11", &filename, &format );

    Check_Type( filename, T_STRING );

    /* 現在のディスプレイのフォーマットなどを取得 */
	hr = g_pD3D->lpVtbl->GetAdapterDisplayMode(g_pD3D, D3DADAPTER_DEFAULT, &dmode);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - GetAdapterDisplayMode" );
    }

	/* キャプチャ用サーフェス作成 */
	hr = g_pD3DDevice->lpVtbl->CreateOffscreenPlainSurface(g_pD3DDevice,
			dmode.Width,
			dmode.Height,
			D3DFMT_A8R8G8B8,
			D3DPOOL_SCRATCH, &pSurface, NULL);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - CreateOffscreenPlainSurface" );
    }
	/* キャプチャ */
	hr = g_pD3DDevice->lpVtbl->GetFrontBufferData(g_pD3DDevice, 0, pSurface);
	if( FAILED( hr ) )
	{
		RELEASE(pSurface);
        rb_raise( eDXRubyError, "キャプチャに失敗しました - GetFrontBufferData" );
	}

	/* サーフェスの保存 */
	if( g_D3DPP.Windowed )
	{
		POINT p = { 0, 0 };
		ClientToScreen(g_hWind  , &p);
		rect.left = p.x; rect.top = p.y;
		p.x = g_D3DPP.BackBufferWidth * g_WindowInfo.scale;
		p.y = g_D3DPP.BackBufferHeight * g_WindowInfo.scale;
		ClientToScreen(g_hWind, &p);
		rect.right = p.x; rect.bottom = p.y;
	}
	hr = D3DXSaveSurfaceToFile(
			RSTRING_PTR( filename ),							/* 保存ファイル名 */
			format == Qnil ? D3DXIFF_JPG : FIX2INT( format ),	/* ファイルフォーマット */
			pSurface,											/* 保存するサーフェス */
			NULL,												/* パレット */
			g_D3DPP.Windowed ? &rect : NULL);					/* 保存領域 */
	RELEASE(pSurface);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - D3DXSaveSurfaceToFile" );
    }

	return obj;
}



/*********************************************************************
 * Imageクラス
 *
 * 描画用の画像を保持するクラス。
 * ファイル名を渡してloadすると読み込まれ、Window::drawに渡して描画する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Image( struct DXRubyImage* image )
{
    /* テクスチャオブジェクトの開放 */
    if( image )
    {
    	if( image->texture )
    	{
	    	image->texture->refcount--;
	    	if( image->texture->refcount == 0 )
	    	{
				RELEASE( image->texture->pD3DTexture );
				free( image->texture );
	    	}
    	}
        free( image );
        image = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Imgaeクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Image_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyImage *image;

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_Image, image);

    /* とりあえずテクスチャオブジェクトはNULLにしておく */
    image->texture = NULL;

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(内部処理用box描画)
 ---------------------------------------------------------------------*/
static void fill( int x1, int y1, int x2, int y2, int col, struct DXRubyImage *image )
{
    D3DLOCKED_RECT texrect;
    int x, y;

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );
    for( y = y1; y <= y2; y++ )
    {
        for( x = x1; x <= x2; x++ )
        {
            *((int*)((char *)texrect.pBits + 
                ( x + (int)image->x ) * 4 +
                ( y + (int)image->y ) * texrect.Pitch)) = col;
        }
    }
    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );
    return;
}
/*--------------------------------------------------------------------
   ImgaeクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Image_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    HRESULT hr;
    D3DSURFACE_DESC desc;
    VALUE vwidth, vheight, vary;
    int col = 0, width, height;

    rb_scan_args( argc, argv, "21", &vwidth, &vheight, &vary );

    width = NUM2INT( vwidth );
    height = NUM2INT( vheight );

    if( width <= 0 || height <= 0 )
    {
        rb_raise( eDXRubyError, "Imageオブジェクトの作成に失敗しました - Image_initialize" );
    }

    if( vary != Qnil )
    {
        Check_Type( vary, T_ARRAY );
        col   = NUM2INT( rb_ary_entry( vary, 0 ) ) << 24 | NUM2INT( rb_ary_entry( vary, 1 ) ) << 16 |
                NUM2INT( rb_ary_entry( vary, 2 ) ) << 8  | NUM2INT( rb_ary_entry( vary, 3 ) );
    }

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_initialize" );
    }

    /* メモリファイルを読み込んでテクスチャオブジェクトを作成する */
    hr = D3DXCreateTexture( g_pD3DDevice, width, height,
                                      1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "テクスチャの作成に失敗しました - Image_initialize" );
    }

    texture->refcount = 1;

    texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    /* Imageオブジェクト設定 */
    Data_Get_Struct( obj, struct DXRubyImage, image );

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = width;
    image->height = height;

    g_iRefAll++;

    fill( 0, 0, width - 1, height - 1, col, image );

    return obj;
}


/*--------------------------------------------------------------------
   Imgaeクラスのload
 ---------------------------------------------------------------------*/
static VALUE Image_load( int argc, VALUE *argv, VALUE klass )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    D3DXIMAGE_INFO srcinfo;
	D3DSURFACE_DESC desc;
    HRESULT hr;
    VALUE filename, vwidth, vheight, obj;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    rb_scan_args( argc, argv, "12", &filename, &vwidth, &vheight );

    Check_Type(filename, T_STRING);

    if( vwidth != Qnil )
    {
        if( NUM2INT( vwidth ) <= 0 )
        {
            rb_raise( eDXRubyError, "画像のサイズ指定が異常値です(width=%d) - Image_load", NUM2INT( vwidth ) );
        }
    }

    if( vheight != Qnil )
    {
        if( NUM2INT( vheight ) <= 0 )
        {
            rb_raise( eDXRubyError, "画像のサイズ指定が異常値です(height=%d) - Image_load", NUM2INT( vheight ) );
        }
    }

    /* ファイル情報取得 */
    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( filename ), &srcinfo );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", RSTRING_PTR( filename ) );
    }

    /* テクスチャロード */
    texture = Image_textureload( RSTRING_PTR( filename ), &srcinfo );
	texture->refcount = 1;

	texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
	texture->width = desc.Width;
    texture->height = desc.Height;

    /* Imageオブジェクト設定 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_load" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = (vwidth  == Qnil ? srcinfo.Width  : NUM2INT( vwidth ));
    image->height = (vheight == Qnil ? srcinfo.Height : NUM2INT( vheight ));

	g_iRefAll++;

	return obj;
}


/*--------------------------------------------------------------------
   Imgaeオブジェクトの分割作成
 ---------------------------------------------------------------------*/
static VALUE Image_loadToArray( VALUE klass, VALUE filename, VALUE vx, VALUE vy )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    int i, j, x, y;
    VALUE obj;
    VALUE array;
    D3DXIMAGE_INFO srcinfo;
	D3DSURFACE_DESC desc;
    HRESULT hr;

	Check_Type(filename, T_STRING);
	x = NUM2INT( vx );
	y = NUM2INT( vy );

    if( x <= 0 || y <= 0 ) rb_raise( eDXRubyError, "画像の数指定が異常値です(x=%d,y=%d) - Image_loadToArray", x, y );

    /* ファイル情報取得 */
    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( filename ), &srcinfo );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", RSTRING_PTR( filename ) );
    }

    /* テクスチャロード */
    texture = Image_textureload( RSTRING_PTR( filename ), &srcinfo );
	texture->refcount = x * y;

	texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
	texture->width = desc.Width;
    texture->height = desc.Height;

    /* Ruby配列作成 */
    array = rb_ary_new();

    for( i = 0; i < y; i++ )
    {
        for( j = 0; j < x; j++ )
        {
            /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
            image = malloc(sizeof(struct DXRubyImage));
            if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_loadToArray" );
            obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

            image->texture = texture;
            image->x = j * srcinfo.Width / x;
            image->y = i * srcinfo.Height / y;
            image->width = srcinfo.Width / x;
            image->height = srcinfo.Height / y;

            rb_ary_push(array, obj);
			g_iRefAll++;
        }
    }

    return array;
}


/*--------------------------------------------------------------------
   Textureロード
 ---------------------------------------------------------------------*/
static struct DXRubyTexture *Image_textureload( char *filename, D3DXIMAGE_INFO *psrcinfo)
{
    HRESULT hr;
    struct DXRubyTexture *texture;
    int i, j;
    int y, x;
    LPDIRECT3DSURFACE9 pSurface;
    LPD3DXBUFFER pBuffer;
    RECT rect;
    D3DLOCKED_RECT LockedRect;

    /* テクスチャサイズ割り出し */
    for( x = 1; x < psrcinfo->Width; x = x * 2 );
    for( y = 1; y < psrcinfo->Height; y = y * 2 );

    /* サーフェイス作成 */
    g_pD3DDevice->lpVtbl->CreateOffscreenPlainSurface( g_pD3DDevice, x, y, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL );

    /* サーフェイスロック */
    hr = pSurface->lpVtbl->LockRect( pSurface, &LockedRect, NULL, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サーフェイスのロックに失敗しました - LockRect" );
    }

    /* サーフェイス初期化 */
    /* 絵のサイズより大きいサイズを引数に指定された時の空白を初期化する */
    for( i = 0; i < y; i++ )
    {
        for( j = 0; j < x*4; j++ )
        {
            *((char *)LockedRect.pBits + j + i * LockedRect.Pitch) = 0;
        }
    }

    /* サーフェイスアンロック */
    pSurface->lpVtbl->UnlockRect(pSurface);


    /* ファイルからサーフェイスにロード */
    rect.top = 0;
    rect.left = 0;
    rect.bottom = psrcinfo->Height;
    rect.right = psrcinfo->Width;

    hr = D3DXLoadSurfaceFromFile( pSurface, NULL, &rect, filename, 
                                  &rect, D3DX_DEFAULT, 0, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", filename );
    }

    /* バッファ確保 */
    hr = D3DXCreateBuffer( 0, &pBuffer);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "バッファの確保に失敗しました - D3DXCreateBuffer" );
    }

    /* メモリファイルに吐き出し */
    hr = D3DXSaveSurfaceToFileInMemory( &pBuffer, D3DXIFF_DDS, pSurface, NULL, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "メモリファイルの作成に失敗しました - D3DXSaveSurfaceToFileInMemory" );
    }

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_textureload" );
    }

    /* メモリファイルを読み込んでテクスチャオブジェクトを作成する */
    hr = D3DXCreateTextureFromFileInMemoryEx( g_pD3DDevice, pBuffer->lpVtbl->GetBufferPointer( pBuffer ), pBuffer->lpVtbl->GetBufferSize( pBuffer ), 
                                      D3DX_DEFAULT, D3DX_DEFAULT, 1,
                                      0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      D3DX_DEFAULT,  D3DX_DEFAULT, 0,
                                      0, 0, &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", filename );
    }

    RELEASE( pBuffer );
    RELEASE( pSurface );

    return texture;
}


/*--------------------------------------------------------------------
   配列からイメージを作る
 ---------------------------------------------------------------------*/
static VALUE Image_createFromArray( VALUE klass, VALUE vwidth, VALUE vheight, VALUE array )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    D3DXIMAGE_INFO srcinfo;
    HRESULT hr;
    int i, j;
    int y, x;
    LPDIRECT3DSURFACE9 pSurface;
    LPD3DXBUFFER pBuffer;
    RECT rect;
    D3DLOCKED_RECT LockedRect;
    VALUE obj;
    int a1, a2, a3, a4, width, height;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    width = NUM2INT( vwidth );
    height = NUM2INT( vheight );
    Check_Type(array, T_ARRAY);

    if( width <= 0 || height <= 0 ) rb_raise( eDXRubyError, "画像のサイズ指定が異常値です(width=%d,height=%d) - Image_loadToArray", width, height );

    /* テクスチャサイズ割り出し */
    for( x = 1; x < width;  x = x * 2 );
    for( y = 1; y < height; y = y * 2 );

    /* サーフェイス作成 */
    g_pD3DDevice->lpVtbl->CreateOffscreenPlainSurface( g_pD3DDevice, x, y, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL );

    /* サーフェイスロック */
    hr = pSurface->lpVtbl->LockRect( pSurface, &LockedRect, NULL, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サーフェイスのロックに失敗しました - LockRect" );
    }

    /* サーフェイス初期化 */
    /* 絵のサイズより大きいサイズを引数に指定された時の空白を初期化する */
    for( i = 0; i < y; i++ )
    {
        for( j = 0; j < x * 4; j += 4 )
        {
            *((int*)((char *)LockedRect.pBits + j + i * LockedRect.Pitch)) = 0;
        }
    }

    for( i = 0; i < height; i++ )
    {
        for( j = 0; j < width * 4; j += 4 )
        {
            a1 = NUM2INT(rb_ary_entry(array, j + i * width * 4));
            a2 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 1));
            a3 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 2));
            a4 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 3));
            *((int*)((char *)LockedRect.pBits + j + i * LockedRect.Pitch)) = a1 << 24 | a2 << 16 | a3 << 8 | a4;
        }
    }

    /* サーフェイスアンロック */
    pSurface->lpVtbl->UnlockRect(pSurface);

    /* バッファ確保 */
    hr = D3DXCreateBuffer( 0, &pBuffer);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "バッファの確保に失敗しました - D3DXCreateBuffer" );
    }

    /* メモリファイルに吐き出し */
    hr = D3DXSaveSurfaceToFileInMemory( &pBuffer, D3DXIFF_DDS, pSurface, NULL, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "メモリファイルの作成に失敗しました - D3DXSaveSurfaceToFileInMemory" );
    }

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );
	texture->refcount = 1;

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_textureload" );
    }

    /* メモリファイルを読み込んでテクスチャオブジェクトを作成する */
    hr = D3DXCreateTextureFromFileInMemoryEx( g_pD3DDevice, pBuffer->lpVtbl->GetBufferPointer( pBuffer ), pBuffer->lpVtbl->GetBufferSize( pBuffer ), 
                                      D3DX_DEFAULT, D3DX_DEFAULT, 1,
                                      0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      D3DX_DEFAULT,  D3DX_DEFAULT, 0,
                                      0, 0, &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "メモリファイルの読み込みに失敗しました" );
    }

	texture->width = x;
    texture->height = y;

    RELEASE( pBuffer );
    RELEASE( pSurface );

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_createFromArray" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = width;
    image->height = height;

	g_iRefAll++;

	return obj;
}


/*--------------------------------------------------------------------
   イメージのデータ取得
 ---------------------------------------------------------------------*/
static VALUE Image_getPixel( VALUE obj, VALUE vx, VALUE vy )
{
    struct DXRubyImage *image;
	LPDWORD p1;
	VALUE ary[4];
	DWORD a;
	D3DLOCKED_RECT texrect;
	int x, y;

	x = NUM2INT( vx );
	y = NUM2INT( vy );
	
    Data_Get_Struct( obj, struct DXRubyImage, image );

	if( x < 0 || x >= image->width || y < 0 || y >= image->height )
	{
		ary[0] = ary[1] = ary[2] = ary[3] = INT2FIX( 0 );
		return rb_ary_new4( 4, ary );
	}

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    p1 = (LPDWORD)texrect.pBits;
	a = p1[x + (int)image->x + ( y + (int)image->y ) * texrect.Pitch / sizeof(DWORD)];

	image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

	ary[0] = INT2FIX(  a >> 24 );
	ary[1] = INT2FIX( (a >> 16) & 0xff );
	ary[2] = INT2FIX( (a >> 8) & 0xff );
	ary[3] = INT2FIX(  a & 0xff );

	return rb_ary_new4( 4, ary );
}


/*--------------------------------------------------------------------
   イメージのデータ設定
 ---------------------------------------------------------------------*/
static VALUE Image_setPixel( VALUE obj, VALUE vx, VALUE vy, VALUE color )
{
    struct DXRubyImage *image;
	int a1, a2, a3, a4;
	D3DLOCKED_RECT texrect;
	int x, y;

	x = NUM2INT( vx );
	y = NUM2INT( vy );
    Check_Type(color, T_ARRAY);

	Data_Get_Struct( obj, struct DXRubyImage, image );

	if( x < 0 || x >= image->width || y < 0 || y >= image->height )
	{
		return obj;
	}

	a1 = NUM2INT(rb_ary_entry(color, 0));
    a2 = NUM2INT(rb_ary_entry(color, 1));
    a3 = NUM2INT(rb_ary_entry(color, 2));
    a4 = NUM2INT(rb_ary_entry(color, 3));

	image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    *((int*)((char *)texrect.pBits + 
    	( x + (int)image->x ) * 4 +
    	( y + (int)image->y ) * texrect.Pitch))
			= a1 << 24 | a2 << 16 | a3 << 8 | a4;

	image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

	return obj;
}


/*--------------------------------------------------------------------
   イメージの色比較
 ---------------------------------------------------------------------*/
static VALUE Image_compare( VALUE obj, VALUE vx, VALUE vy, VALUE color )
{
    struct DXRubyImage *image;
	LPDWORD p1;
	DWORD a;
	D3DLOCKED_RECT texrect;
	int x, y;
	int a1, a2, a3, a4;

	x = NUM2INT( vx );
	y = NUM2INT( vy );
    Check_Type(color, T_ARRAY);

    Data_Get_Struct( obj, struct DXRubyImage, image );

	if( x < 0 || x >= image->width || y < 0 || y >= image->height )
	{
		return INT2FIX( 0 );
	}

	a1 = NUM2INT(rb_ary_entry(color, 0));
    a2 = NUM2INT(rb_ary_entry(color, 1));
    a3 = NUM2INT(rb_ary_entry(color, 2));
    a4 = NUM2INT(rb_ary_entry(color, 3));

	image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    p1 = (LPDWORD)texrect.pBits;
	a = p1[x + (int)image->x + ( y + (int)image->y ) * texrect.Pitch / sizeof(DWORD)];

	image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

	if( a1 == (a >> 24) && a2 == ((a >> 16) & 0xff) && a3 == ((a >> 8) & 0xff) && a4 == (a & 0xff) )
	{
		return Qtrue;
	}
	else
	{
		return Qfalse;
	}
}


/*--------------------------------------------------------------------
    イメージのデータ設定(box描画)
 ---------------------------------------------------------------------*/
static VALUE Image_box( VALUE obj, VALUE vx1, VALUE vy1, VALUE vx2, VALUE vy2, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x, y, x1, y1, x2, y2;
    int col;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x1 = NUM2INT( vx1 );
    y1 = NUM2INT( vy1 );
    x2 = NUM2INT( vx2 );
    y2 = NUM2INT( vy2 );

    /* 左上から右下の指定に修正 */
    if( x1 > x2 )
    {
        x = x2;
        x2 = x1;
        x1 = x;
    }
    if( y1 > y2 )
    {
        y = y2;
        y2 = y1;
        y1 = y;
    }

    /* 範囲外の指定は無視 */
    if( x1 > image->width - 1 || x2 < 0 || y1 > image->height - 1 || y2 < 0)
    {
        return obj;
    }

    /* クリップ */
    if( x1 < 0 )
    {
        x1 = 0;
    }
    if( x2 > image->width - 1 )
    {
        x2 = image->width - 1;
    }
    if( y1 < 0 )
    {
        y1 = 0;
    }
    if( y2 > image->height - 1 )
    {
        y2 = image->height - 1;
    }

    Check_Type(color, T_ARRAY);
    col   = NUM2INT(rb_ary_entry(color, 0))<<24 | NUM2INT(rb_ary_entry(color, 1))<<16 |
            NUM2INT(rb_ary_entry(color, 2))<<8  | NUM2INT(rb_ary_entry(color, 3));

    fill( x1, y1, x2, y2, col, image );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(circle用のline描画)
 ---------------------------------------------------------------------*/
static void Image_circle_line( int x1, int x2, int y, int col, D3DLOCKED_RECT *texrect, struct DXRubyImage *image )
{
	int x;

	if( y < 0 || y > image->height - 1 )
	{
		return;
	}

	/* クリップ */
	if( x1 < 0 )
	{
		x1 = 0;
	}
	if( x2 > image->width - 1 )
	{
		x2 = image->width - 1;
	}

	for( x = x1; x <= x2; x++ )
	{
		*((int*)((char *)texrect->pBits + 
	    	( x + (int)image->x ) * 4 +
	    	( y + (int)image->y ) * texrect->Pitch)) = col;
	}
}


/*--------------------------------------------------------------------
    イメージのデータ設定(circle描画塗りつぶす)
 ---------------------------------------------------------------------*/
static VALUE Image_circleFill( VALUE obj, VALUE vx0, VALUE vy0, VALUE vr, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x0, y0, r, F, x, y;
    int col;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x0 = NUM2INT( vx0 );
    y0 = NUM2INT( vy0 );
    r  = NUM2INT( vr );

    Check_Type(color, T_ARRAY);
    col   = NUM2INT(rb_ary_entry(color, 0))<<24 | NUM2INT(rb_ary_entry(color, 1))<<16 |
            NUM2INT(rb_ary_entry(color, 2))<<8  | NUM2INT(rb_ary_entry(color, 3));

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    x = r;
    y = 0;
    F = -2 * r + 3;

    while ( x >= y )
    {
        Image_circle_line( x0 - x, x0 + x, y0 + y, col, &texrect, image );
        Image_circle_line( x0 - x, x0 + x, y0 - y, col, &texrect, image );
        Image_circle_line( x0 - y, x0 + y, y0 + x, col, &texrect, image );
        Image_circle_line( x0 - y, x0 + y, y0 - x, col, &texrect, image );
        if ( F >= 0 )
        {
          x--;
          F -= 4 * x;
        }
        y++;
        F += 4 * y + 2;
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(内部処理用pixel描画)
 ---------------------------------------------------------------------*/
static void Image_circle_pixel( int x, int y, int col, D3DLOCKED_RECT *texrect, struct DXRubyImage *image )
{
    if( x < 0 || x >= image->width || y < 0 || y >= image->height )
    {
        return;
    }

    *((int*)((char *)texrect->pBits + 
        ( x + (int)image->x ) * 4 +
        ( y + (int)image->y ) * texrect->Pitch)) = col;

    return;
}
/*--------------------------------------------------------------------
    イメージのデータ設定(circle描画塗りつぶさない)
 ---------------------------------------------------------------------*/
static VALUE Image_circle( VALUE obj, VALUE vx0, VALUE vy0, VALUE vr, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x0, y0, r, F, x, y;
    int col;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x0 = NUM2INT( vx0 );
    y0 = NUM2INT( vy0 );
    r  = NUM2INT( vr );

    Check_Type(color, T_ARRAY);
    col   = NUM2INT(rb_ary_entry(color, 0))<<24 | NUM2INT(rb_ary_entry(color, 1))<<16 |
            NUM2INT(rb_ary_entry(color, 2))<<8  | NUM2INT(rb_ary_entry(color, 3));

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    x = r;
    y = 0;
    F = -2 * r + 3;

    while ( x >= y )
    {
        Image_circle_pixel( x0 + x, y0 + y, col, &texrect, image );
        Image_circle_pixel( x0 - x, y0 + y, col, &texrect, image );
        Image_circle_pixel( x0 + x, y0 - y, col, &texrect, image );
        Image_circle_pixel( x0 - x, y0 - y, col, &texrect, image );
        Image_circle_pixel( x0 + y, y0 + x, col, &texrect, image );
        Image_circle_pixel( x0 - y, y0 + x, col, &texrect, image );
        Image_circle_pixel( x0 + y, y0 - x, col, &texrect, image );
        Image_circle_pixel( x0 - y, y0 - x, col, &texrect, image );
        if ( F >= 0 )
        {
          x--;
          F -= 4 * x;
        }
        y++;
        F += 4 * y + 2;
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(line描画)
 ---------------------------------------------------------------------*/
static VALUE Image_line( VALUE obj, VALUE vx1, VALUE vy1, VALUE vx2, VALUE vy2, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x, y, x1, y1, x2, y2, xp, yp;
    int col;
    int c, d, dx, dy, i;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x1 = NUM2INT( vx1 );
    y1 = NUM2INT( vy1 );
    x2 = NUM2INT( vx2 );
    y2 = NUM2INT( vy2 );

    Check_Type(color, T_ARRAY);
    col   = NUM2INT(rb_ary_entry(color, 0))<<24 | NUM2INT(rb_ary_entry(color, 1))<<16 |
            NUM2INT(rb_ary_entry(color, 2))<<8  | NUM2INT(rb_ary_entry(color, 3));

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, NULL, 0 );

    dx = x2 > x1 ? x2 - x1 : x1 - x2;
    dy = y2 > y1 ? y2 - y1 : y1 - y2;

    /* ブレゼンハムアルゴリズムによる線分描画 */
    if( dx < dy )
    {
        xp = x1 < x2 ? 1 : -1;
        d = y1 < y2 ? 1 : -1;
        c = dy / 2;
        for( i = 0; i <= dy; i++ )
        {
            if( x1 >= 0 && x1 < (int)image->width && y1 >= 0 && y1 < (int)image->height )
            {
                *((int*)((char *)texrect.pBits + 
                            ( x1 + (int)image->x ) * 4 +
                            ( y1 + (int)image->y ) * texrect.Pitch)) = col;
            }
            y1 = y1 + d;
            c = c + dx;
            if( c > dy )
            {
                c = c - dy;
                x1 = x1 + xp;
            }
        }
    }
    else
    {
        yp = y1 < y2 ? 1 : -1;
        d = x1 < x2 ? 1 : -1;
        c = dx / 2;
        for( i = 0; i <= dx; i++ )
        {
            if( x1 >= 0 && x1 < (int)image->width && y1 >= 0 && y1 < (int)image->height )
            {
                *((int*)((char *)texrect.pBits + 
                            ( x1 + (int)image->x ) * 4 +
                            ( y1 + (int)image->y ) * texrect.Pitch)) = col;
            }
            x1 = x1 + d;
            c = c + dy;
            if( c > dx )
            {
                c = c - dx;
                y1 = y1 + yp;
            }
        }
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
   イメージの開始位置xを返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getX( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->x );
}


/*--------------------------------------------------------------------
   イメージの開始位置yを返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getY( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->y );
}


/*--------------------------------------------------------------------
   イメージのサイズ（幅）を返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getWidth( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->width );
}


/*--------------------------------------------------------------------
   イメージのサイズ（高さ）を返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getHeight( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->height );
}


/*--------------------------------------------------------------------
   イメージの開始位置xを設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setX( VALUE obj, VALUE x )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    image->x = NUM2INT( x );

    return x;
}


/*--------------------------------------------------------------------
   イメージの開始位置yを設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setY( VALUE obj, VALUE y )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    image->y = NUM2INT( y );

    return y;
}


/*--------------------------------------------------------------------
   イメージのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setWidth( VALUE obj, VALUE width )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    image->width = NUM2INT( width );

    return width;
}


/*--------------------------------------------------------------------
   イメージのサイズ（高さ）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setHeight( VALUE obj, VALUE height )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    image->height = NUM2INT( height );

    return height;
}



/*********************************************************************
 * Soundクラス
 *
 * DirectMusicを使用して音を鳴らす。
 * とりあえず音を出そうと頑張っている。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Sound( struct DXRubySound* sound )
{
    HRESULT hr;

	/* サウンドオブジェクトの開放 */
    if( sound )
    {
	    /* バンド解放 */
    	if ( sound->pDMSegment != NULL )
    	{
	    	hr = sound->pDMSegment->lpVtbl->Unload( sound->pDMSegment, (IUnknown* )sound->pDMDefAudioPath );
		    if( FAILED( hr ) )
		    {
		        rb_raise( eDXRubyError, "バンドの解放に失敗しました - Unload" );
		    }
			/* セグメントを開放 */
	    	RELEASE( sound->pDMSegment );
    	}

    	/* デフォルトオーディオパスを開放 */
	    RELEASE( sound->pDMDefAudioPath );

    	g_iRefDM--;

    	if( g_iRefDM <= 0 )
    	{
	    	/* 演奏停止 */
		    if ( g_pDMPerformance )
		    {
		        hr = g_pDMPerformance->lpVtbl->Stop( g_pDMPerformance, NULL, NULL, 0, 0 );
		        if ( FAILED( hr ) )
		    	{
		            rb_raise( eDXRubyError, "パフォーマンスの終了に失敗しました - Stop" );
		    	}
		    }
	        g_pDMPerformance->lpVtbl->CloseDown( g_pDMPerformance );
    		RELEASE(g_pDMPerformance);

		    /* ローダを開放 */
		    RELEASE(g_pDMLoader);
    	}

    	free( sound );
        sound = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Soundクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Sound_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubySound *sound;

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    sound = malloc(sizeof(struct DXRubySound));
    if( sound == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Sound_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_Sound, sound);

    /* とりあえずサウンドオブジェクトはNULLにしておく */
    sound->pDMSegment = NULL;

    return obj;
}


/*--------------------------------------------------------------------
   Soundクラスのinitialize。ファイルをロードする。
 ---------------------------------------------------------------------*/
static VALUE Sound_initialize( VALUE obj, VALUE filename )
{
    HRESULT hr;
    WCHAR wstrFileName[MAX_PATH];
    struct DXRubySound *sound;
    CHAR strPath[MAX_PATH];
    DWORD i;
    WCHAR wstrSearchPath[MAX_PATH];

	Check_Type(filename, T_STRING);

	if( g_iRefDM == 0 )
	{
	    /* パフォーマンスの作成 */
	    hr = CoCreateInstance( &CLSID_DirectMusicPerformance, NULL,
	                           CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance8,
	                           (void**)&g_pDMPerformance );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - CoCreateInstance" );
	    }

	    /* パフォーマンスの初期化 */
	    hr = g_pDMPerformance->lpVtbl->InitAudio( g_pDMPerformance,
	                                              NULL,                  /* IDirectMusicインターフェイスは不要 */
	                                              NULL,                  /* IDirectSoundインターフェイスは不要 */
	                                              g_hWind,               /* ウィンドウのハンドル */
	                                              DMUS_APATH_SHARED_STEREOPLUSREVERB,  /* デフォルトのオーディオパス・タイプ */
	                                              64,                    /* パフォーマンス・チャンネルの数 */
	                                              DMUS_AUDIOF_ALL,       /* シンセサイザの機能 */
	                                              NULL );                /* オーディオ・パラメータにはデフォルトを使用 */
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - InitAudio" );
	    }

	    /* ローダーの作成 */
	    hr = CoCreateInstance( &CLSID_DirectMusicLoader, NULL, 
	                           CLSCTX_INPROC_SERVER, &IID_IDirectMusicLoader8,
	                           (void**)&g_pDMLoader );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - CoCreateInstance" );
	    }

	    /* ローダーの初期化（検索パスをカレント・ディレクトリに設定） */
	    i = GetCurrentDirectory( MAX_PATH, strPath );
	    if ( i == 0 || MAX_PATH < i )
	    {
	        rb_raise( eDXRubyError, "カレント・ディレクトリの取得に失敗 - GetCurrentDirectory" );
	    }

	    /* マルチ・バイト文字をUNICODEに変換 */
	    MultiByteToWideChar( CP_ACP, 0, strPath, -1, wstrSearchPath, MAX_PATH );

	    /* ローダーに検索パスを設定 */
	    hr = g_pDMLoader->lpVtbl->SetSearchDirectory( g_pDMLoader, &GUID_DirectMusicAllTypes,
	                                                  wstrSearchPath, FALSE );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "検索パスの設定に失敗 - SetSearchDirectory" );
	    }
	}
    g_iRefDM++;

	/* サウンドオブジェクト取得 */
    Data_Get_Struct( obj, struct DXRubySound, sound );

	/* オーディオ・パス作成 */
	hr = g_pDMPerformance->lpVtbl->CreateStandardAudioPath( g_pDMPerformance,
		DMUS_APATH_DYNAMIC_STEREO,      /* パスの種類。 */
		64,                             /* パフォーマンス チャンネルの数。 */
		TRUE,                           /* ここでアクティブになる。 */
		&sound->pDMDefAudioPath );      /* オーディオパスを受け取るポインタ。 */

	if ( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "オーディオパスの作成に失敗しました - CreateStandardAudioPath" );
	}

    /* ファイルロード */
    MultiByteToWideChar( CP_ACP, 0, RSTRING_PTR( filename ), -1, wstrFileName, MAX_PATH );
	hr = g_pDMLoader->lpVtbl->LoadObjectFromFile( g_pDMLoader, &CLSID_DirectMusicSegment,
                                                  &IID_IDirectMusicSegment8,
                                                  wstrFileName,
                                                  (LPVOID*)&sound->pDMSegment );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルのロードに失敗しました - LoadObjectFromFile" );
    }

	sound->start = 0;
	sound->loopstart = 0;
	sound->loopend = 0;

	/* MIDIの場合 */
    if( strstr( RSTRING_PTR( filename ), ".mid" ) != NULL )
    {
        hr = sound->pDMSegment->lpVtbl->SetParam( sound->pDMSegment, &GUID_StandardMIDIFile,
                                                  0xFFFFFFFF, 0, 0, NULL);
        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "ファイルのロードに失敗しました - SetParam" );
        }
		sound->loopcount = DMUS_SEG_REPEAT_INFINITE;
    }
	else
	{
		sound->loopcount = 1;
	}

    /* バンドダウンロード */
    hr = sound->pDMSegment->lpVtbl->Download( sound->pDMSegment, (IUnknown* )sound->pDMDefAudioPath );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "バンドのロードに失敗しました - Download" );
    }

	g_iRefAll++;

    /* 音量設定 */
    hr = sound->pDMDefAudioPath->lpVtbl->SetVolume( sound->pDMDefAudioPath, 230 * 9600 / 255 - 9600 , 0 );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音量の設定に失敗しました - SetVolume" );
    }

	return obj;
}


/*--------------------------------------------------------------------
   開始位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setStart( VALUE obj, VALUE vstart )
{
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
	sound->start = NUM2INT( vstart );

	return obj;
}


/*--------------------------------------------------------------------
   ループ開始位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopStart( VALUE obj, VALUE vloopstart )
{
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
	sound->loopstart = NUM2INT( vloopstart );

	return obj;
}


/*--------------------------------------------------------------------
   ループ終了位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopEnd( VALUE obj, VALUE vloopend )
{
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
	sound->loopend = NUM2INT( vloopend );

	return obj;
}


/*--------------------------------------------------------------------
   ループ回数を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopCount( VALUE obj, VALUE vloopcount )
{
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
	sound->loopcount = NUM2INT( vloopcount );

	return obj;
}


/*--------------------------------------------------------------------
   音量を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setVolume( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;
    VALUE vvolume, vtime;
    int volume, time;

    rb_scan_args( argc, argv, "11", &vvolume, &vtime );

    time = vtime == Qnil ? 0 : NUM2INT( vtime );
    volume = NUM2INT( vvolume ) > 255 ? 255 : NUM2INT( vvolume );
    Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 音量設定 */
    hr = sound->pDMDefAudioPath->lpVtbl->SetVolume( sound->pDMDefAudioPath, volume * 9600 / 255 - 9600, time );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音量の設定に失敗しました - SetVolume" );
    }

	return obj;
}


/*--------------------------------------------------------------------
   音を鳴らす（効果音）
 ---------------------------------------------------------------------*/
static VALUE Sound_play( VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;

	Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 再生 */
    hr = g_pDMPerformance->lpVtbl->PlaySegmentEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, NULL, NULL,
                                                  DMUS_SEGF_SECONDARY, 0, NULL, NULL, (IUnknown* )sound->pDMDefAudioPath );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - PlaySegmentEx" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   音を鳴らす（BGM）
 ---------------------------------------------------------------------*/
static VALUE Sound_bgmplay( VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;
    MUSIC_TIME temp;
	VALUE vstart, vloopstart, vloopend, vloopcount;
	int start, loopstart, loopend, loopcount;

	Data_Get_Struct( obj, struct DXRubySound, sound );

	/* セグメント長取得 */
    hr = sound->pDMSegment->lpVtbl->GetLength( sound->pDMSegment, &temp );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "セグメント長の取得に失敗しました - GetLength" );
    }

    /* セグメント再生スタート位置設定 */
    hr = sound->pDMSegment->lpVtbl->SetStartPoint( sound->pDMSegment, sound->start / 768 * DMUS_PPQ  );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "セグメント開始位置の設定に失敗しました - SetStartPoint" );
    }

    /* ループ範囲設定 */
	hr = sound->pDMSegment->lpVtbl->SetLoopPoints( sound->pDMSegment, sound->loopstart / 768 * DMUS_PPQ
                                                                    , sound->loopend / 768 * DMUS_PPQ );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ループ範囲の設定に失敗しました - SetLoopPoints" );
    }

    /* ループ回数設定 */
    hr = sound->pDMSegment->lpVtbl->SetRepeats( sound->pDMSegment, sound->loopcount );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ループ回数の設定に失敗しました - SetRepeats" );
    }

    /* 再生 */
    hr = g_pDMPerformance->lpVtbl->PlaySegmentEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, NULL, NULL,
                                                  DMUS_SEGF_CONTROL, 0, NULL, NULL, (IUnknown* )sound->pDMDefAudioPath );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - PlaySegmentEx" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   音を止める
 ---------------------------------------------------------------------*/
static VALUE Sound_stop( VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 再生 */
    hr = g_pDMPerformance->lpVtbl->StopEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, 0, 0 );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の停止に失敗しました - StopEx" );
    }

    return obj;

}


/*********************************************************************
 * SoundEffectクラス
 *
 * DirectSoundを使用して音を鳴らす。
 * とりあえず音を出そうと頑張っている。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_SoundEffect( struct DXRubySoundEffect* soundeffect )
{
    /* サウンドオブジェクトの開放 */
    if( soundeffect )
    {
    	/* サウンドバッファを開放 */
	    RELEASE( soundeffect->pDSBuffer );

    	g_iRefDS--;

    	if( g_iRefDS == 0 )
    	{
    		RELEASE( g_pDSound );
    	}

    	free( soundeffect );
        soundeffect = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   SoundEffectクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubySoundEffect *soundeffect;

    /* DXRubySoundEffectのメモリ取得＆SoundEffectオブジェクト生成 */
    soundeffect = malloc(sizeof(struct DXRubySoundEffect));
    if( soundeffect == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - SoundEffect_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_SoundEffect, soundeffect);

    /* とりあえずサウンドオブジェクトはNULLにしておく */
    soundeffect->pDSBuffer = NULL;

    return obj;
}


static short calcwave(int type, int f, int vol, int count, int p)
{
	switch( type )
	{
	case 1: /* サイン波 */
		return (sin( (3.141592653589793115997963468544185161590576171875f * 2) * (double)count / (double)p )) * (double)vol * 128;
		break;
	case 2: /* ノコギリ波 */
		return ((double)count / (double)p - 0.5) * (double)vol * 256;
		break;
	case 3: /* 三角波 */
		if( count < p / 4 )			/* 1/4 */
		{
			return (double)count / ((double)p / 4) * (double)vol * 128;
		}
		else if( count < p / 2 )		/* 2/4 */
		{
			return ((double)p / 2 - (double)count) / ((double)p / 4) * (double)vol * 128;
		}
		else if( count < p * 3 / 4 )	/* 3/4 */
		{
			return -((double)count - (double)p / 2)/ ((double)p / 4) * (double)vol * 128;
		}
		else											/* 最後 */
		{
			return -((double)p - (double)count) / ((double)p / 4) * (double)vol * 128;
		}
		break;
	case 0: /* 矩形波 */
	default: /* デフォルト */
		if( count < p / 2 )	/* 前半 */
		{
			return vol * 128;
		}
		else									/* 後半 */
		{
			return -vol * 128;
		}
		break;
	}
}
/*--------------------------------------------------------------------
   SoundEffectクラスのinitialize。波形を生成する。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_initialize( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    WCHAR wstrFileName[MAX_PATH];
    struct DXRubySoundEffect *soundeffect;
	DSBUFFERDESC desc;
	WAVEFORMATEX pcmwf;
	int i;
	short *pointer, *pointer2;
	DWORD size, size2;
	VALUE vf;
	int count;
	int vol, f;
	VALUE vsize, vtype, vresolution;
	int type, resolution;

    rb_scan_args( argc, argv, "12", &vsize, &vtype, &vresolution );

	type       = vtype       == Qnil ? 0    : NUM2INT( vtype );
	resolution = vresolution == Qnil ? 1000 : (NUM2INT( vresolution ) > 44100 ? 44100 : NUM2INT( vresolution ));

    /* DirectSoundオブジェクトの作成 */
	if( g_iRefDS == 0 )
	{
		hr = DirectSoundCreate8( &DSDEVID_DefaultPlayback, &g_pDSound, NULL );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectSoundの初期化に失敗しました - DirectSoundCreate8" );
	    }

		hr = g_pDSound->lpVtbl->SetCooperativeLevel( g_pDSound, g_hWind, DSSCL_PRIORITY );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectSoundの初期化に失敗しました - SetCooperativeLevel" );
	    }
	}
	g_iRefDS++;

	/* サウンドオブジェクト作成 */
    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* サウンドバッファ作成 */
	pcmwf.wFormatTag = WAVE_FORMAT_PCM;
	pcmwf.nChannels = 1;
	pcmwf.nSamplesPerSec = 44100 * 2;
	pcmwf.wBitsPerSample = 16;
	pcmwf.nBlockAlign = pcmwf.nChannels * pcmwf.wBitsPerSample / 8;
	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	pcmwf.cbSize = 0;

	desc.dwSize = sizeof(desc);
	desc.dwFlags = 0;
	desc.dwBufferBytes = pcmwf.nAvgBytesPerSec / 100 * NUM2INT(vsize) / 10;
	desc.dwReserved = 0;
	desc.lpwfxFormat = &pcmwf;
	desc.guid3DAlgorithm = DS3DALG_DEFAULT;

	hr = g_pDSound->lpVtbl->CreateSoundBuffer( g_pDSound, &desc, &soundeffect->pDSBuffer, NULL );

	if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファの作成に失敗しました - CreateSoundBuffer" );
    }

	/* ロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Lock( soundeffect->pDSBuffer, 0, 0, &pointer, &size, &pointer2, &size2, DSBLOCK_ENTIREBUFFER );
    if( FAILED( hr ) || size2 != 0 )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	/* 音バッファ初期化 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		pointer[i] = 0;
	}

	count = 0;

	/* 波形生成 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		/* 指定時間単位でブロックを呼び出す */
		if ( i % (pcmwf.nSamplesPerSec / resolution) == 0 )
		{
			vf = rb_yield( obj );
			if( TYPE( vf ) != T_ARRAY )
			{
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
				break;
			}
			switch (TYPE( rb_ary_entry(vf, 0) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				f = NUM2INT( rb_ary_entry(vf, 0) );
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			switch (TYPE( rb_ary_entry(vf, 1) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				vol = NUM2INT(rb_ary_entry(vf, 1));
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			/* 最大/最低周波数と最大ボリュームの制限 */
			f = f > pcmwf.nSamplesPerSec / 2 ? pcmwf.nSamplesPerSec / 2 : f;
			f = f < 20 ? 20 : f;
			vol = vol > 255 ? 255 : vol;
			vol = vol < 0 ? 0 : vol;
		}
		count = count + f;
		if( count >= pcmwf.nSamplesPerSec )
		{
			count = count - pcmwf.nSamplesPerSec;
		}
		pointer[i] = calcwave(type, f, vol, count, pcmwf.nSamplesPerSec);
	}

	/* アンロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	g_iRefAll++;

	return obj;

}


/*--------------------------------------------------------------------
   波形を合成する。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_add( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;
	DSBUFFERDESC desc;
	WAVEFORMATEX pcmwf;
	int i;
	short *pointer, *pointer2;
	DWORD size, size2;
	VALUE vf, vtype, vresolution;
	int count;
	int vol, f;
	int type, resolution;
	int data;

	rb_scan_args( argc, argv, "11", &vtype, &vresolution );

	type       = vtype       == Qnil ? 0    : NUM2INT( vtype );
	resolution = vresolution == Qnil ? 1000 : (NUM2INT( vresolution ) > 44100 ? 44100 : NUM2INT( vresolution ));

    /* サウンドオブジェクト取得 */
    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* ロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Lock( soundeffect->pDSBuffer, 0, 0, &pointer, &size, &pointer2, &size2, DSBLOCK_ENTIREBUFFER );
    if( FAILED( hr ) || size2 != 0 )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	pcmwf.nChannels = 1;
	pcmwf.nSamplesPerSec = 44100 * 2;
	pcmwf.wBitsPerSample = 16;
	pcmwf.nBlockAlign = pcmwf.nChannels * pcmwf.wBitsPerSample / 8;
	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	desc.dwBufferBytes = size;

	count = 0;

	/* 波形生成 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		/* 指定時間単位でブロックを呼び出す */
		if ( i % (pcmwf.nSamplesPerSec / resolution) == 0 )
		{
			vf = rb_yield( obj );
			if( TYPE( vf ) != T_ARRAY )
			{
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
				break;
			}
			switch (TYPE( rb_ary_entry(vf, 0) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				f = NUM2INT( rb_ary_entry(vf, 0) );
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			switch (TYPE( rb_ary_entry(vf, 1) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				vol = NUM2INT(rb_ary_entry(vf, 1));
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			/* 最大/最低周波数と最大ボリュームの制限 */
			f = f > pcmwf.nSamplesPerSec / 2 ? pcmwf.nSamplesPerSec / 2 : f;
			f = f < 20 ? 20 : f;
			vol = vol > 255 ? 255 : vol;
			vol = vol < 0 ? 0 : vol;
		}
		count = count + f;
		if( count >= pcmwf.nSamplesPerSec )
		{
			count = count - pcmwf.nSamplesPerSec;
		}

		data = calcwave(type, f, vol, count, pcmwf.nSamplesPerSec);

		if( data + (int)pointer[i] > 32767 )
		{
			pointer[i] = 32767;
		}
		else if( data + (int)pointer[i] < -32768 )
		{
			pointer[i] = -32768;
		}
		else
		{
			pointer[i] += data;
		}
	}

	/* アンロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

    return obj;

}


/*--------------------------------------------------------------------
   音を鳴らす
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_play( VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;
	DWORD flag;
	
	Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* とめる */
    hr = soundeffect->pDSBuffer->lpVtbl->Stop( soundeffect->pDSBuffer );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
    }

	hr = soundeffect->pDSBuffer->lpVtbl->SetCurrentPosition( soundeffect->pDSBuffer, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
    }

	/* 再生 */
    hr = soundeffect->pDSBuffer->lpVtbl->Play( soundeffect->pDSBuffer, 0, 0, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
	}
    return obj;
}


/*--------------------------------------------------------------------
   音を止める
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_stop( VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;

    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

    /* とめる */
    hr = soundeffect->pDSBuffer->lpVtbl->Stop( soundeffect->pDSBuffer );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の停止に失敗しました - Stop" );
    }

    return obj;
}



/*********************************************************************
 * Inputモジュール
 *
 * DirectInputを使用してキーボード・パッドの入力を行う。
 *********************************************************************/

/*--------------------------------------------------------------------
   Inputモジュールの入力処理
 ---------------------------------------------------------------------*/
static void Input_update( void )
{
    HRESULT hr;
    int i, j;
    DIJOYSTATE paddata;

    /* フォーカスがはずれた場合、キーとパッド、マウスボタンの入力を受け付けなくする */
    if( g_bActive == 0 )
    {
        /* キーボードのデータをクリアする */
        memcpy( g_diKeyStateOld, g_diKeyState, sizeof(g_diKeyState) );

        /* パッドデータをクリアする */
        for( i = 0; i < g_JoystickCount; i++ )
        {
            for( j = 0; j < 20; j++ )
            {
                g_PadStateOld[i].button[j] = g_PadState[i].button[j];
                g_PadState[i].button[j] = 0;
            }
        }

        /* マウスボタンのデータをクリアする */
        g_byMouseStateOld_L = g_byMouseState_L;
        g_byMouseStateOld_M = g_byMouseState_M;
        g_byMouseStateOld_R = g_byMouseState_R;
        g_byMouseState_L = 0;
        g_byMouseState_M = 0;
        g_byMouseState_R = 0;

        return;
    }

    /* デバイスアクセス権を再取得する */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );

    for( i = 0; i < g_JoystickCount; i++ )
    {
        g_pDIDJoyPad[i]->lpVtbl->Poll( g_pDIDJoyPad[i] );
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

    /* キーボードの直接データを取得する */
    memcpy( g_diKeyStateOld, g_diKeyState, sizeof(g_diKeyState) );
    g_pDIDKeyBoard->lpVtbl->GetDeviceState( g_pDIDKeyBoard, 256, g_diKeyState );

    /* ゲームパッドのデータを取得する */
    for( i = 0; i < g_JoystickCount; i++ )
    {
        int temp;
        int j;

        g_pDIDJoyPad[i]->lpVtbl->GetDeviceState( g_pDIDJoyPad[i], sizeof(DIJOYSTATE), &paddata );

        for( j = 0; j < 20; j++ )
        {
            g_PadStateOld[i].button[j] = g_PadState[i].button[j];
            g_PadState[i].button[j] = 0;
        }

        /* 左 */
        if( paddata.lX < g_PadInfo[i].left )
        {
            g_PadState[i].button[P_LEFT] = 1;
        }
        /* 右 */
        else if( paddata.lX > g_PadInfo[i].right )
        {
            g_PadState[i].button[P_RIGHT] = 1;
        }

        /* 上 */
        if( paddata.lY < g_PadInfo[i].up )
        {
            g_PadState[i].button[P_UP] = 1;
        }
        /* 下 */
        else if( paddata.lY > g_PadInfo[i].down )
        {
            g_PadState[i].button[P_DOWN] = 1;
        }

        /* ボタン状態 */
        for( j = 0; j < 16; j++)
        {
            g_PadState[i].button[j + 4] = paddata.rgbButtons[j] >> 7;
        }
    }

    /* マウスボタンの状態 */
    g_byMouseStateOld_L = g_byMouseState_L;
    g_byMouseStateOld_M = g_byMouseState_M;
    g_byMouseStateOld_R = g_byMouseState_R;
    g_byMouseState_L = GetKeyState( VK_LBUTTON );
    g_byMouseState_M = GetKeyState( VK_MBUTTON );
    g_byMouseState_R = GetKeyState( VK_RBUTTON );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   横方向の入力をxの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_x( int argc, VALUE *argv, VALUE obj )
{
    int x = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_x" );
    }

	/* ひだり */
    if( g_diKeyState[g_PadState[number].PadConfig[P_LEFT]] & 0x80  || g_PadState[number].button[P_LEFT] == 1 )
    {
        x = x - 1;
    }

    /* みぎ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_RIGHT]] & 0x80  || g_PadState[number].button[P_RIGHT] == 1 )
    {
        x = x + 1;
    }

    return INT2FIX( x );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   縦方向の入力をyの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_y( int argc, VALUE *argv, VALUE obj )
{
    int y = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_y" );
    }


    /* うえ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_UP]] & 0x80  || g_PadState[number].button[P_UP] == 1 )
    {
        y = y - 1;
    }

    /* した */
    if( g_diKeyState[g_PadState[number].PadConfig[P_DOWN]] & 0x80  || g_PadState[number].button[P_DOWN] == 1 )
    {
        y = y + 1;
    }

    return INT2FIX( y );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_key( VALUE obj , VALUE vkey )
{
	int key;

	key = NUM2INT( vkey );
	if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_key" );
    }

    if( g_diKeyState[key] & 0x80 )
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_buttonPush( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_button" );
    }

	if( g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 ||	/* 入力されている */
         g_PadState[number].button[button] == 1 )
    {
    	if( !(g_diKeyStateOld[g_PadState[number].PadConfig[button]] & 0x80) &&	/* 前回OFFである */
         g_PadStateOld[number].button[button] != 1 )
	    {
	    	g_diKeyCount[g_PadState[number].PadConfig[button]] = 0;				/* カウント初期化 */
	    	return Qtrue;														/* なにはともあれTrue */
	    }
		else																	/* 前回ONだった */
		{
			g_diKeyCount[g_PadState[number].PadConfig[button]]++;				/* カウント */
			if( g_iInputWait <= g_diKeyCount[g_PadState[number].PadConfig[button]] )	/* ウェイトタイムを超えたか */
			{
				if( g_iInterval != 0 &&
				   (g_diKeyCount[g_PadState[number].PadConfig[button]] - g_iInputWait) % g_iInterval == 0 ) /* インターバルタイムごとに */
				{
			    	return Qtrue;														/* True */
				}
			}
			
		}
	}
    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_keyPush( VALUE obj , VALUE vkey )
{
	int key;

	key = NUM2INT( vkey );
    if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_key" );
    }

    if( (g_diKeyState[key] & 0x80) )	/* 入力されている */
	{
		if( !(g_diKeyStateOld[key] & 0x80) )	/* 前回OFFである */
	    {
	    	g_diKeyCount[key] = 0;				/* カウント初期化 */
    	    return Qtrue;						/* なにはともあれTrue */
	    }
		else																	/* 前回ONだった */
		{
			g_diKeyCount[key]++;				/* カウント */
			if( g_iInputWait <= g_diKeyCount[key] )	/* ウェイトタイムを超えたか */
			{
				if( g_iInterval != 0 &&
				   (g_diKeyCount[key] - g_iInputWait) % g_iInterval == 0 ) /* インターバルタイムごとに */
				{
			    	return Qtrue;														/* True */
				}
			}
			
		}
	}
    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_button( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_button" );
    }

    if( g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 ||
        g_PadState[number].button[button] == 1 )
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   パッドとキーの対応を設定する
 ---------------------------------------------------------------------*/
static void Input_SetConfig( int number, int pad, int key )
{
    g_PadState[number].PadConfig[pad] = key;
}

static VALUE Input_setconfig( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vpad, vkey;
	int number, pad, key;

	rb_scan_args( argc, argv, "21", &vpad, &vkey, &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));
	pad = NUM2INT( vpad );
	key = NUM2INT( vkey );

	if( number < 0 || pad < 0 || key < 0 || number >= PADMAX || pad >= 20 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setconfig" );
    }

    Input_SetConfig( number, pad, key );

    return obj;
}


/*--------------------------------------------------------------------
   マウスホイールの状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmousewheelpos( VALUE obj )
{
    return INT2FIX(g_WindowInfo.mousewheelpos);
}


/*--------------------------------------------------------------------
   マウスホイールの状態を設定する
 ---------------------------------------------------------------------*/
static VALUE Input_setmousewheelpos( VALUE obj, VALUE wheelpos )
{
    g_WindowInfo.mousewheelpos = NUM2INT( wheelpos );

    return obj;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmousebutton( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( g_byMouseState_L & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_mousePush( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( (g_byMouseState_L & 0x80) && !(g_byMouseStateOld_L & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 && !(g_byMouseStateOld_R & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 && !(g_byMouseStateOld_M & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposx( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWind, &cursor );
    return INT2FIX( cursor.x );
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposy( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWind, &cursor );

    return INT2FIX( cursor.y );
}


/*--------------------------------------------------------------------
   マウス描画するかどうかの設定
 ---------------------------------------------------------------------*/
static VALUE Input_enablemouse( VALUE obj, VALUE draw )
{
    VALUE flag = Qfalse;
    int c;

    if( draw != Qnil && draw != Qfalse )
    {
        flag = Qtrue;
    }

    if( flag == Qtrue )
    {
        c = ShowCursor( TRUE );
        while( c < 0 ) c = ShowCursor( TRUE );
    }
    else
    {
        c = ShowCursor( FALSE );
        while( c >= 0 ) c = ShowCursor( FALSE );
    }

    g_WindowInfo.enablemouse = flag;

    return draw;
}


/*--------------------------------------------------------------------
   Inputモジュールのオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setrepeat( VALUE obj , VALUE vwait, VALUE vinterval )
{
	int wait, interval;

	g_iInputWait = NUM2INT( vwait );
	g_iInterval = NUM2INT( vinterval );

	return obj;
}



/*********************************************************************
 * Fontモジュール
 *
 * D3DXFontインターフェースを使用してフォントを描画する。
 * スプライト描画に混ぜ込むことでとりあえず実装。
 *********************************************************************/
/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Font( struct DXRubyFont* font )
{
    /* フォントオブジェクトの開放 */
    if( font )
    {
		RELEASE( font->pD3DXFont );
        free( font );
        font = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Fontクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Font_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFont *font;

    /* DXRubyFontのメモリ取得＆Fontオブジェクト生成 */
    font = malloc( sizeof(struct DXRubyFont) );
    if( font == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Font_allocate" );
    return Data_Wrap_Struct(klass, 0, release_Font, font);
}


/*--------------------------------------------------------------------
   FontクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Font_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    HRESULT hr;
	D3DXFONT_DESC desc;
	VALUE size, fontname;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

	rb_scan_args( argc, argv, "11", &size, &fontname );

	desc.Height          = NUM2INT( size );
	desc.Width           = 0;
	desc.Weight          = FW_DONTCARE;
	desc.MipLevels       = 0;
	desc.Italic          = FALSE;
	desc.CharSet         = SHIFTJIS_CHARSET;
	desc.OutputPrecision = 0;
	desc.Quality         = DEFAULT_QUALITY;
	desc.PitchAndFamily  = DEFAULT_PITCH || FF_DONTCARE;
	if( fontname == Qnil )
	{
		lstrcpy(desc.FaceName, "ＭＳ ゴシック");
	}
	else
	{
		Check_Type(fontname, T_STRING);
		lstrcpy(desc.FaceName, RSTRING_PTR( fontname ));
	}

	/* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );

	hr = D3DXCreateFontIndirect( g_pD3DDevice, &desc, &font->pD3DXFont );

	if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "フォントの作成に失敗しました - D3DXCreateFontIndirect" );
    }

	g_iRefAll++;

	return obj;
}


/*
***************************************************************
*
*         Global functions
*
***************************************************************/

void Init_dxruby()
{
    HRESULT hr;

    hr = CoInitialize(NULL);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "COMの初期化に失敗しました - CoInitialize" );
    }

    /* インスタンスハンドル取得 */
    g_hInstance = (HINSTANCE)GetModuleHandle( NULL );

    /* Windowモジュール登録 */
    mWindow = rb_define_module( "Window" );

    /* Windowモジュールにメソッド登録 */
    rb_define_singleton_method( mWindow, "loop"     , Window_loop       , 0  );
    rb_define_singleton_method( mWindow, "draw"     , Window_draw       , -1 );
    rb_define_singleton_method( mWindow, "drawScale", Window_drawScale  , -1 );
    rb_define_singleton_method( mWindow, "drawRot"  , Window_drawRot    , -1 );
    rb_define_singleton_method( mWindow, "drawAlpha", Window_drawAlpha  , -1 );
    rb_define_singleton_method( mWindow, "drawAdd"  , Window_drawAdd    , -1 );
    rb_define_singleton_method( mWindow, "drawEx"   , Window_drawEx     , -1 );
    rb_define_singleton_method( mWindow, "drawFont" , Window_drawFont   , -1 );
    rb_define_singleton_method( mWindow, "x="       , Window_setx       , 1  );
    rb_define_singleton_method( mWindow, "y="       , Window_sety       , 1  );
    rb_define_singleton_method( mWindow, "width="   , Window_setwidth   , 1  );
    rb_define_singleton_method( mWindow, "height="  , Window_setheight  , 1  );
    rb_define_singleton_method( mWindow, "caption=" , Window_setCaption , 1  );
    rb_define_singleton_method( mWindow, "scale="   , Window_setScale   , 1  );
    rb_define_singleton_method( mWindow, "windowed=", Window_setwindowed, 1  );
    rb_define_singleton_method( mWindow, "getScreenShot", Window_getScreenShot, -1 );
    rb_define_singleton_method( mWindow, "fps="     , Window_setfps     , 1  );
    rb_define_singleton_method( mWindow, "fps"      , Window_getfps     , 0  );
    rb_define_singleton_method( mWindow, "openFilename" , Window_openDialog, 2 );
    rb_define_singleton_method( mWindow, "saveFilename" , Window_saveDialog, 2 );
    rb_define_singleton_method( mWindow, "getLoad"      , Window_getload, 0  );
    rb_define_singleton_method( mWindow, "frameskip="   , Window_setframeskip, 1  );
    rb_define_singleton_method( mWindow, "bgcolor=" , Window_setbgcolor , 1  );



	/* Imageクラス定義 */
    cImage = rb_define_class( "Image", rb_cObject );

    /* Imageクラスにクラスメソッド登録*/
    rb_define_singleton_method(cImage, "load", Image_load, -1);
    rb_define_singleton_method(cImage, "loadToArray", Image_loadToArray, 3);
    rb_define_singleton_method(cImage, "createFromArray", Image_createFromArray, 3);

    /* Imageクラスにメソッド登録*/
    rb_define_method( cImage, "initialize", Image_initialize, -1 );
    rb_define_method( cImage, "width"     , Image_getWidth  , 0 );
    rb_define_method( cImage, "height"    , Image_getHeight , 0 );
    rb_define_method( cImage, "[]="       , Image_setPixel  , 3 );
    rb_define_method( cImage, "[]"        , Image_getPixel  , 2 );
    rb_define_method( cImage, "box"       , Image_box       , 5 );
    rb_define_method( cImage, "line"      , Image_line      , 5 );
    rb_define_method( cImage, "circle"    , Image_circle    , 4 );
    rb_define_method( cImage, "circleFill", Image_circleFill, 4 );
    rb_define_method( cImage, "x"         , Image_getX      , 0 );
    rb_define_method( cImage, "y"         , Image_getY      , 0 );
    rb_define_method( cImage, "width="    , Image_setWidth  , 1 );
    rb_define_method( cImage, "height="   , Image_setHeight , 1 );
    rb_define_method( cImage, "x="        , Image_setX      , 1 );
    rb_define_method( cImage, "y="        , Image_setY      , 1 );
    rb_define_method( cImage, "compare"   , Image_compare   , 3 );

    /* Imageオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cImage, Image_allocate );


	/* Inputモジュール登録 */
    mInput = rb_define_module( "Input" );

    /* Inputモジュールにメソッド登録 */
    rb_define_singleton_method( mInput, "x"              , Input_x               , -1 );
    rb_define_singleton_method( mInput, "y"              , Input_y               , -1 );
    rb_define_singleton_method( mInput, "mousePosX"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mousePosY"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "keyDown?"       , Input_key             , 1  );
    rb_define_singleton_method( mInput, "keyPush?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "padDown?"       , Input_button          , -1 );
    rb_define_singleton_method( mInput, "padPush?"       , Input_buttonPush      , -1 );
    rb_define_singleton_method( mInput, "mouseDown?"     , Input_getmousebutton  , 1  );
    rb_define_singleton_method( mInput, "mousePush?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mouseEnable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouseWheelPos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouseWheelPos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "setConfig"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "setRepeat"      , Input_setrepeat       , 2  );


    /* Fontクラス登録 */
    cFont = rb_define_class( "Font", rb_cObject );

	/* Imageクラスにメソッド登録*/
    rb_define_method( cFont, "initialize", Font_initialize, -1 );

    /* Fontオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cFont, Font_allocate );


	/* Soundクラス定義 */
    cSound = rb_define_class( "Sound", rb_cObject );

    /* Soundクラスにメソッド登録*/
    rb_define_method( cSound, "initialize"   , Sound_initialize   , 1 );
    rb_define_method( cSound, "play"         , Sound_play         , 0 );
    rb_define_method( cSound, "bgmplay"      , Sound_bgmplay      , 0 );
    rb_define_method( cSound, "stop"         , Sound_stop         , 0 );
    rb_define_method( cSound, "setVolume"    , Sound_setVolume    , -1 );
    rb_define_method( cSound, "start="       , Sound_setStart     , 1 );
    rb_define_method( cSound, "loopStart="   , Sound_setLoopStart , 1 );
    rb_define_method( cSound, "loopEnd="     , Sound_setLoopEnd   , 1 );
    rb_define_method( cSound, "loopCount="   , Sound_setLoopCount , 1 );

    /* Soundオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cSound, Sound_allocate );


	/* SoundEffectクラス定義 */
    cSoundEffect = rb_define_class( "SoundEffect", rb_cObject );

    /* SoundEffectクラスにメソッド登録*/
    rb_define_method( cSoundEffect, "initialize", SoundEffect_initialize, -1 );
    rb_define_method( cSoundEffect, "play"      , SoundEffect_play      , 0 );
    rb_define_method( cSoundEffect, "stop"      , SoundEffect_stop      , 0 );
    rb_define_method( cSoundEffect, "add"       , SoundEffect_add       , -1 );

    /* Soundオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cSoundEffect, SoundEffect_allocate );


	/* キーボードのスキャンコード定数設定 */
    rb_define_const( rb_cObject, "K_ESCAPE"     , INT2FIX(DIK_ESCAPE) );
    rb_define_const( rb_cObject, "K_TAB"        , INT2FIX(DIK_TAB) );
    rb_define_const( rb_cObject, "K_RETURN"     , INT2FIX(DIK_RETURN) );
    rb_define_const( rb_cObject, "K_LCONTROL"   , INT2FIX(DIK_LCONTROL) );
    rb_define_const( rb_cObject, "K_LSHIFT"     , INT2FIX(DIK_LSHIFT) );
    rb_define_const( rb_cObject, "K_RCONTROL"   , INT2FIX(DIK_RCONTROL) );
    rb_define_const( rb_cObject, "K_RSHIFT"     , INT2FIX(DIK_RSHIFT) );
    rb_define_const( rb_cObject, "K_LMENU"      , INT2FIX(DIK_LMENU) );
    rb_define_const( rb_cObject, "K_RMENU"      , INT2FIX(DIK_RMENU) );
    rb_define_const( rb_cObject, "K_SPACE"      , INT2FIX(DIK_SPACE) );
    rb_define_const( rb_cObject, "K_1"          , INT2FIX(DIK_1) );
    rb_define_const( rb_cObject, "K_2"          , INT2FIX(DIK_2) );
    rb_define_const( rb_cObject, "K_3"          , INT2FIX(DIK_3) );
    rb_define_const( rb_cObject, "K_4"          , INT2FIX(DIK_4) );
    rb_define_const( rb_cObject, "K_5"          , INT2FIX(DIK_5) );
    rb_define_const( rb_cObject, "K_6"          , INT2FIX(DIK_6) );
    rb_define_const( rb_cObject, "K_7"          , INT2FIX(DIK_7) );
    rb_define_const( rb_cObject, "K_8"          , INT2FIX(DIK_8) );
    rb_define_const( rb_cObject, "K_9"          , INT2FIX(DIK_9) );
    rb_define_const( rb_cObject, "K_0"          , INT2FIX(DIK_0) );
    rb_define_const( rb_cObject, "K_Q"          , INT2FIX(DIK_Q) );
    rb_define_const( rb_cObject, "K_W"          , INT2FIX(DIK_W) );
    rb_define_const( rb_cObject, "K_E"          , INT2FIX(DIK_E) );
    rb_define_const( rb_cObject, "K_R"          , INT2FIX(DIK_R) );
    rb_define_const( rb_cObject, "K_T"          , INT2FIX(DIK_T) );
    rb_define_const( rb_cObject, "K_Y"          , INT2FIX(DIK_Y) );
    rb_define_const( rb_cObject, "K_U"          , INT2FIX(DIK_U) );
    rb_define_const( rb_cObject, "K_I"          , INT2FIX(DIK_I) );
    rb_define_const( rb_cObject, "K_O"          , INT2FIX(DIK_O) );
    rb_define_const( rb_cObject, "K_P"          , INT2FIX(DIK_P) );
    rb_define_const( rb_cObject, "K_A"          , INT2FIX(DIK_A) );
    rb_define_const( rb_cObject, "K_S"          , INT2FIX(DIK_S) );
    rb_define_const( rb_cObject, "K_D"          , INT2FIX(DIK_D) );
    rb_define_const( rb_cObject, "K_F"          , INT2FIX(DIK_F) );
    rb_define_const( rb_cObject, "K_G"          , INT2FIX(DIK_G) );
    rb_define_const( rb_cObject, "K_H"          , INT2FIX(DIK_H) );
    rb_define_const( rb_cObject, "K_J"          , INT2FIX(DIK_J) );
    rb_define_const( rb_cObject, "K_K"          , INT2FIX(DIK_K) );
    rb_define_const( rb_cObject, "K_L"          , INT2FIX(DIK_L) );
    rb_define_const( rb_cObject, "K_Z"          , INT2FIX(DIK_Z) );
    rb_define_const( rb_cObject, "K_X"          , INT2FIX(DIK_X) );
    rb_define_const( rb_cObject, "K_C"          , INT2FIX(DIK_C) );
    rb_define_const( rb_cObject, "K_V"          , INT2FIX(DIK_V) );
    rb_define_const( rb_cObject, "K_B"          , INT2FIX(DIK_B) );
    rb_define_const( rb_cObject, "K_N"          , INT2FIX(DIK_N) );
    rb_define_const( rb_cObject, "K_M"          , INT2FIX(DIK_M) );
    rb_define_const( rb_cObject, "K_F1"         , INT2FIX(DIK_F1) );
    rb_define_const( rb_cObject, "K_F2"         , INT2FIX(DIK_F2) );
    rb_define_const( rb_cObject, "K_F3"         , INT2FIX(DIK_F3) );
    rb_define_const( rb_cObject, "K_F4"         , INT2FIX(DIK_F4) );
    rb_define_const( rb_cObject, "K_F5"         , INT2FIX(DIK_F5) );
    rb_define_const( rb_cObject, "K_F6"         , INT2FIX(DIK_F6) );
    rb_define_const( rb_cObject, "K_F7"         , INT2FIX(DIK_F7) );
    rb_define_const( rb_cObject, "K_F8"         , INT2FIX(DIK_F8) );
    rb_define_const( rb_cObject, "K_F9"         , INT2FIX(DIK_F9) );
    rb_define_const( rb_cObject, "K_F10"        , INT2FIX(DIK_F10) );
    rb_define_const( rb_cObject, "K_F11"        , INT2FIX(DIK_F11) );
    rb_define_const( rb_cObject, "K_F12"        , INT2FIX(DIK_F12) );
    rb_define_const( rb_cObject, "K_NUMPAD7"    , INT2FIX(DIK_NUMPAD7) );
    rb_define_const( rb_cObject, "K_NUMPAD8"    , INT2FIX(DIK_NUMPAD8) );
    rb_define_const( rb_cObject, "K_NUMPAD9"    , INT2FIX(DIK_NUMPAD9) );
    rb_define_const( rb_cObject, "K_NUMPAD4"    , INT2FIX(DIK_NUMPAD4) );
    rb_define_const( rb_cObject, "K_NUMPAD5"    , INT2FIX(DIK_NUMPAD5) );
    rb_define_const( rb_cObject, "K_NUMPAD6"    , INT2FIX(DIK_NUMPAD6) );
    rb_define_const( rb_cObject, "K_NUMPAD1"    , INT2FIX(DIK_NUMPAD1) );
    rb_define_const( rb_cObject, "K_NUMPAD2"    , INT2FIX(DIK_NUMPAD2) );
    rb_define_const( rb_cObject, "K_NUMPAD3"    , INT2FIX(DIK_NUMPAD3) );
    rb_define_const( rb_cObject, "K_NUMPAD0"    , INT2FIX(DIK_NUMPAD0) );
    rb_define_const( rb_cObject, "K_NUMPADENTER", INT2FIX(DIK_NUMPADENTER) );
    rb_define_const( rb_cObject, "K_UP"         , INT2FIX(DIK_UP) );
    rb_define_const( rb_cObject, "K_LEFT"       , INT2FIX(DIK_LEFT) );
    rb_define_const( rb_cObject, "K_RIGHT"      , INT2FIX(DIK_RIGHT) );
    rb_define_const( rb_cObject, "K_DOWN"       , INT2FIX(DIK_DOWN) );

    rb_define_const( rb_cObject, "P_UP"         , INT2FIX(P_UP)       );
    rb_define_const( rb_cObject, "P_LEFT"       , INT2FIX(P_LEFT)     );
    rb_define_const( rb_cObject, "P_RIGHT"      , INT2FIX(P_RIGHT)    );
    rb_define_const( rb_cObject, "P_DOWN"       , INT2FIX(P_DOWN)     );
    rb_define_const( rb_cObject, "P_BUTTON0"    , INT2FIX(P_BUTTON0)  );
    rb_define_const( rb_cObject, "P_BUTTON1"    , INT2FIX(P_BUTTON1)  );
    rb_define_const( rb_cObject, "P_BUTTON2"    , INT2FIX(P_BUTTON2)  );
    rb_define_const( rb_cObject, "P_BUTTON3"    , INT2FIX(P_BUTTON3)  );
    rb_define_const( rb_cObject, "P_BUTTON4"    , INT2FIX(P_BUTTON4)  );
    rb_define_const( rb_cObject, "P_BUTTON5"    , INT2FIX(P_BUTTON5)  );
    rb_define_const( rb_cObject, "P_BUTTON6"    , INT2FIX(P_BUTTON6)  );
    rb_define_const( rb_cObject, "P_BUTTON7"    , INT2FIX(P_BUTTON7)  );
    rb_define_const( rb_cObject, "P_BUTTON8"    , INT2FIX(P_BUTTON8)  );
    rb_define_const( rb_cObject, "P_BUTTON9"    , INT2FIX(P_BUTTON9)  );
    rb_define_const( rb_cObject, "P_BUTTON10"   , INT2FIX(P_BUTTON10) );
    rb_define_const( rb_cObject, "P_BUTTON11"   , INT2FIX(P_BUTTON11) );
    rb_define_const( rb_cObject, "P_BUTTON12"   , INT2FIX(P_BUTTON12) );
    rb_define_const( rb_cObject, "P_BUTTON13"   , INT2FIX(P_BUTTON13) );
    rb_define_const( rb_cObject, "P_BUTTON14"   , INT2FIX(P_BUTTON14) );
    rb_define_const( rb_cObject, "P_BUTTON15"   , INT2FIX(P_BUTTON15) );

    rb_define_const( rb_cObject, "M_LBUTTON"    , INT2FIX(M_LBUTTON)  );
    rb_define_const( rb_cObject, "M_RBUTTON"    , INT2FIX(M_RBUTTON)  );
    rb_define_const( rb_cObject, "M_MBUTTON"    , INT2FIX(M_MBUTTON)  );

    rb_define_const( rb_cObject, "FORMAT_JPEG"  , INT2FIX(FORMAT_JPEG));
    rb_define_const( rb_cObject, "FORMAT_JPG"   , INT2FIX(FORMAT_JPG) );
 //   rb_define_const( rb_cObject, "FORMAT_PNG"   , INT2FIX(FORMAT_PNG) );
    rb_define_const( rb_cObject, "FORMAT_BMP"   , INT2FIX(FORMAT_BMP) );

    rb_define_const( rb_cObject, "WAVE_SIN"     , INT2FIX(WAVE_SIN) );
    rb_define_const( rb_cObject, "WAVE_SAW"     , INT2FIX(WAVE_SAW) );
    rb_define_const( rb_cObject, "WAVE_TRI"     , INT2FIX(WAVE_TRI) );
    rb_define_const( rb_cObject, "WAVE_RECT"    , INT2FIX(WAVE_RECT) );

    /* 例外定義 */
    eDXRubyError = rb_define_class_under( mWindow, "DXRubyError", rb_eRuntimeError );

    /* 定数登録 */
/*    rb_define_const( cDXRuby, "VERSION", rb_str_new2( DXRUBY_VERSION ) );*/

    /* シンボル定義 */
    symbol_add            = ID2SYM(rb_intern("add"));
    symbol_angle          = ID2SYM(rb_intern("angle"));
    symbol_alpha          = ID2SYM(rb_intern("alpha"));
    symbol_scalex         = ID2SYM(rb_intern("scalex"));
    symbol_scaley         = ID2SYM(rb_intern("scaley"));
    symbol_centerx        = ID2SYM(rb_intern("centerx"));
    symbol_centery        = ID2SYM(rb_intern("centery"));
    symbol_z              = ID2SYM(rb_intern("z"));
    symbol_color          = ID2SYM(rb_intern("color"));

    /* 終了時に実行する関数 */
    rb_set_end_proc( Window_shutdown, Qnil );

    /* スプライト構造体の初期値設定 */
	g_SpriteCount = 0;
	g_SpriteAllocateCount = 128;
	g_SpriteList = malloc( g_SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
	g_SpriteStruct = malloc( g_SpriteAllocateCount * sizeof(struct DXRubySprite) );

    g_WindowInfo.width    = 640;
    g_WindowInfo.height   = 480;
    g_WindowInfo.windowed = Qtrue;
    g_WindowInfo.created  = Qfalse;
    g_WindowInfo.scale    = 1;
    g_WindowInfo.enablemouse = Qtrue;
    g_WindowInfo.mousewheelpos = 0;
    g_WindowInfo.fps = 60;
    g_WindowInfo.frameskip = Qfalse;
    g_WindowInfo.r = 0;
    g_WindowInfo.g = 0;
    g_WindowInfo.b = 0;

    Input_SetConfig( 0, P_LEFT   , DIK_LEFT  );
    Input_SetConfig( 0, P_RIGHT  , DIK_RIGHT );
    Input_SetConfig( 0, P_UP     , DIK_UP    );
    Input_SetConfig( 0, P_DOWN   , DIK_DOWN  );
    Input_SetConfig( 0, P_BUTTON0, DIK_Z     );
    Input_SetConfig( 0, P_BUTTON1, DIK_X     );
    Input_SetConfig( 0, P_BUTTON2, DIK_C     );

	g_iInputWait = 0;
	g_iInterval = 0;

	/* ウィンドウ作成(この時点では非表示) */
    InitWindow();

    /* DirectX Graphicsの初期化 */
    InitDXGraphics();

    /* DirectInput初期化 */
    InitDirectInput();
}


/*--------------------------------------------------------------------
  （内部関数）ウィンドウの生成
 ---------------------------------------------------------------------*/
static void InitWindow( void )
{
    WNDCLASSEX wcex;
    RECT rect;

    /* ウインドウ・クラスの登録 */
    wcex.cbSize        = sizeof( WNDCLASSEX );
    wcex.style         = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = (WNDPROC)MainWndProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = g_hInstance;
    wcex.hIcon         = NULL;
    wcex.hIconSm       = NULL;
    wcex.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = "DXRuby";

    if( !RegisterClassEx( &wcex ) )
    {
        rb_raise( eDXRubyError, "ウィンドウの初期化に失敗しました - RegusterClassEx" );
    }

    /* メイン・ウインドウ作成(ウインドウ・モード用) */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width;
    rect.bottom  = g_WindowInfo.height;

    AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, FALSE );

    rect.right   = rect.right - rect.left;
    rect.bottom  = rect.bottom - rect.top;
    rect.left    = 0;
    rect.top     = 0;

    g_hWind = CreateWindow( "DXRuby", "DXRuby Application",
                           WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
                           CW_USEDEFAULT, CW_USEDEFAULT,
                           rect.right - rect.left, rect.bottom - rect.top,
                           NULL, NULL, g_hInstance, NULL );
    if( g_hWind == NULL )
    {
        rb_raise( eDXRubyError, "ウィンドウの初期化に失敗しました - CreateWindow" );
    }

    GetWindowRect( g_hWind, &rect );
    g_WindowInfo.x        = rect.left;
    g_WindowInfo.y        = rect.top;
}


/*--------------------------------------------------------------------
  （内部関数）DirectX Graphics初期化
 ---------------------------------------------------------------------*/
static void InitDXGraphics( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;

    /* Direct3Dオブジェクトの作成 */
    g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

    if( g_pD3D == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - Direct3DCreate9" );
    }

    /* D3DDeviceオブジェクトの作成(ウインドウ・モード) */
    ZeroMemory( &g_D3DPP, sizeof( g_D3DPP ) );

    g_D3DPP.BackBufferWidth            = 0;
    g_D3DPP.BackBufferHeight           = 0;
    g_D3DPP.BackBufferFormat           = D3DFMT_A8R8G8B8;
    g_D3DPP.BackBufferCount            = 1;
    g_D3DPP.MultiSampleType            = D3DMULTISAMPLE_NONE;
    g_D3DPP.MultiSampleQuality         = 0;
    g_D3DPP.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
    g_D3DPP.hDeviceWindow              = g_hWind;
    g_D3DPP.Windowed                   = TRUE;
    g_D3DPP.EnableAutoDepthStencil     = FALSE;
    g_D3DPP.AutoDepthStencilFormat     = D3DFMT_UNKNOWN;
    g_D3DPP.Flags                      = 0;
    g_D3DPP.FullScreen_RefreshRateInHz = 0;
    g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;

    hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWind,
                                       D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

    if( FAILED( hr ) )
    {
        /* デバイスの生成に失敗したらリファレンスラスタライザを使うようにしてみる */
        /* これは動くけど遅くて話にならないモード（このコード消すかも） */
        hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, g_hWind,
                                           D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - CreateDevice" );
        }
    }

    /* ビューポートの設定 */
    vp.X       = 0;
    vp.Y       = 0;
    vp.Width   = g_D3DPP.BackBufferWidth;
    vp.Height  = g_D3DPP.BackBufferHeight;
    vp.MinZ    = 0.0f;
    vp.MaxZ    = 1.0f;

    hr = g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - SetViewport" );
    }

    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

    /* スプライトオブジェクト作成 */
    hr = D3DXCreateSprite( g_pD3DDevice, &g_pD3DXSprite );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - CreateSprite" );
    }
}


/*--------------------------------------------------------------------
  （内部関数）フレーム調整初期化
 ---------------------------------------------------------------------*/
static void InitSync( void )
{
    timeBeginPeriod( 1 );

    /* パフォーマンスカウンタの秒間カウント値取得 */
    if( QueryPerformanceFrequency( (LARGE_INTEGER *)&g_OneSecondCount ) )
    {
        /* パフォーマンスカウンタがある場合 */
        g_isPerformanceCounter = 1;
        QueryPerformanceCounter( (LARGE_INTEGER *)&g_OldTime );
    }
    else
    {
        /* パフォーマンスカウンタが無い場合 */
        g_isPerformanceCounter = 0;
        g_OneSecondCount = 1000;
        g_OldTime = timeGetTime();
    }
}


/* デバイス列挙関数 */
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, void* pContext )
{
    HRESULT hr;

    /* 列挙されたジョイスティックへのインターフェイスを取得する */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput,
                            &pdidInstance->guidInstance,
                            &g_pDIDJoyPad[g_JoystickCount],
                            NULL );
    if( SUCCEEDED( hr ) )
    {
        return (++g_JoystickCount == PADMAX)?(DIENUM_STOP):(DIENUM_CONTINUE);
    }

    return DIENUM_STOP;
}

/* オブジェクト（軸）列挙関数 */
BOOL CALLBACK EnumAxisCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, void *pContext )
{
    HRESULT hr;
    DIPROPRANGE diprg = { sizeof(DIPROPRANGE), sizeof(DIPROPHEADER) };
    struct DXRubyPadInfo *pad;

    pad = (struct DXRubyPadInfo *)pContext;

    diprg.diph.dwHow = DIPH_BYID; 
    diprg.diph.dwObj = pdidoi->dwType;

    /*========================================================*/
    /* 取得 */
    hr =  pad->pDIDJoyPad->lpVtbl->GetProperty( pad->pDIDJoyPad, DIPROP_RANGE, &diprg.diph );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - GetProperty" );
    }

    /* x軸 */
    if( memcmp( &pdidoi->guidType, &GUID_XAxis ,sizeof(GUID)) == 0 )
    {
        int xcenter;
        xcenter = (diprg.lMin + diprg.lMax) / 2;
        pad->left  = (xcenter - diprg.lMin) / 2;
        pad->right = (diprg.lMax - xcenter) / 2 + xcenter;
    }

    /* y軸 */
    if( memcmp( &pdidoi->guidType, &GUID_YAxis ,sizeof(GUID)) == 0 )
    {
        int ycenter;
        ycenter = (diprg.lMin + diprg.lMax) / 2;
        pad->up   = (ycenter - diprg.lMin) / 2;
        pad->down = (diprg.lMax - ycenter) / 2 + ycenter;
    }

    return DIENUM_CONTINUE;
}

/*--------------------------------------------------------------------
  （内部関数）DirectInput初期化
 ---------------------------------------------------------------------*/
static void InitDirectInput( void )
{
    HRESULT hr;
    int i;

/* DirectInput初期化 */

    /* DirectInputオブジェクトの作成 */
    hr = DirectInput8Create( g_hInstance, DIRECTINPUT_VERSION,
                             &IID_IDirectInput8, (void **)&g_pDInput, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - DirectInput8Create" );
    }

/* キーボード */

    /* デバイス・オブジェクトを作成（キーボード） */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput, &GUID_SysKeyboard, &g_pDIDKeyBoard, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - CreateDevice" );
    }

    /* データ形式を設定（キーボードですよ） */
    hr = g_pDIDKeyBoard->lpVtbl->SetDataFormat( g_pDIDKeyBoard, &c_dfDIKeyboard );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
    }

    /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
    hr = g_pDIDKeyBoard->lpVtbl->SetCooperativeLevel( g_pDIDKeyBoard, g_hWind, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
    }

    /* 入力制御開始 */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );


/* ゲームパッド */

    for( i = 0; i < PADMAX; i++)
    {
        g_pDIDJoyPad[i] = NULL;
    }
    g_JoystickCount = 0;

    /* ゲームデバイス列挙 */
    hr = g_pDInput->lpVtbl->EnumDevices( g_pDInput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumDevices" );
    }

    /* ゲームデバイス初期化 */
    for ( i = 0; i < g_JoystickCount; i++ )
    {
        /* データ形式を設定 */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetDataFormat( g_pDIDJoyPad[i], &c_dfDIJoystick );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
        }

        /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetCooperativeLevel( g_pDIDJoyPad[i], g_hWind, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
        }

        /* オブジェクト列挙 */
        g_PadInfo[i].pDIDJoyPad = g_pDIDJoyPad[i];
        hr = g_pDIDJoyPad[i]->lpVtbl->EnumObjects( g_pDIDJoyPad[i], EnumAxisCallback, &g_PadInfo[i], DIDFT_AXIS );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumObjects" );
        }

        /* 入力制御開始 */
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

}




static void AllocSpriteList( void )
{
	int i;

	if( g_SpriteCount >= g_SpriteAllocateCount )
	{
		g_SpriteAllocateCount = g_SpriteAllocateCount * 3 / 2; /* 1.5倍にする */
		g_SpriteList = realloc(g_SpriteList, g_SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
		g_SpriteStruct = realloc(g_SpriteStruct, g_SpriteAllocateCount * sizeof(struct DXRubySprite) );
		for( i = 0; i < g_SpriteCount; i++)
		{
			g_SpriteList[i].sprite = &g_SpriteStruct[i];
		}
	}
}


/* マージソート */
void merge( struct DXRubySpriteList *list, struct DXRubySpriteList *temp, int left, int mid, int right )
{
    int i, left_end, num_elements, tmp_pos;

    left_end = mid - 1;
    tmp_pos = left;
    num_elements = right - left + 1;

    while ((left <= left_end) && (mid <= right))
    {
        if (list[left].z <= list[mid].z)
            temp[tmp_pos++] = list[left++];
        else
            temp[tmp_pos++] = list[mid++];
    }

    while (left <= left_end)
    {
        temp[tmp_pos++] = list[left++];
    }
    while (mid <= right)
    {
        temp[tmp_pos++] = list[mid++];
    }

    while (num_elements--)
    {
        list[right] = temp[right--];
    }
}
void m_sort( struct DXRubySpriteList *list, struct DXRubySpriteList *temp, int left, int right )
{
    int mid;

    if( right > left )
    {
        mid = (right + left) / 2;
        m_sort( list, temp, left, mid );
        m_sort( list, temp, mid+1, right );

        merge( list, temp, left, mid+1, right );
    }
}
void SortSpriteList( void )
{
    struct DXRubySpriteList *temp;
    int i;

    for( i = 0; i < g_SpriteCount; i++ )
    {
        if( g_SpriteList[i].z != 0 )
        {
            temp = malloc( sizeof(struct DXRubySpriteList)* g_SpriteCount );
            m_sort( g_SpriteList, temp, 0, g_SpriteCount - 1 );
            free(temp);
            break;
        }
    }
}