CDirectDrawGame.cpp
上传用户:sycq158
上传日期:2008-10-22
资源大小:15361k
文件大小:21k
源码类别:

游戏

开发平台:

Visual C++

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /// File:    CDirectDrawGame.cpp
  3. /// Purpose: Implementation of CDirectDrawGame Class
  4. ///////////////////////////////////////////////////////////////////////////////
  5. /// Configuation Management
  6. /// 
  7. /// Who         When          Description
  8. /// ===========================================================================
  9. /// R. Walter   28-Dec-2003   Initial Version/Release
  10. ///
  11. ///////////////////////////////////////////////////////////////////////////////
  12. /// Copyright 2003: Robert Walter   All rights reserved
  13. ///////////////////////////////////////////////////////////////////////////////
  14. /// HEADER FILE INCLUDES //////////////////////////////////////////////////////
  15. #include "CDirectDrawGame.h"
  16. /// CLASS CONSTRUCTORS / DESTRUCTORS //////////////////////////////////////////
  17. ///////////////////////////////////////////////////////////////////////////////
  18. /// Method:  CDirectDrawGame --> default constructor
  19. /// Purpose: Initialize member variables
  20. ///////////////////////////////////////////////////////////////////////////////
  21. /// Receives: nothing
  22. /// Returns:  nothing
  23. ///////////////////////////////////////////////////////////////////////////////
  24. CDirectDrawGame::CDirectDrawGame()
  25. {
  26. m_lpdd7 = NULL;
  27.     m_lpdds_primary = NULL;
  28.     m_lpdds_back = NULL;
  29.     m_lpdi8 = NULL;
  30.     m_di_keyboard = NULL;
  31. m_screen_width = 640;
  32.     m_screen_height = 480;
  33.     m_screen_bpp = 16;
  34.     m_frame_fps = 10;
  35.     m_wait_ms = 0;
  36.     m_tick_cnt = 0;
  37.     // 16-bit color management variables
  38.     m_num_red_bits = 0;
  39.     m_num_green_bits = 0;
  40.     m_num_blue_bits = 0;
  41.     m_low_red_bit = 0;
  42.     m_low_green_bit = 0;
  43.     m_low_blue_bit = 0;
  44.     
  45. return;
  46. }
  47. ///////////////////////////////////////////////////////////////////////////////
  48. /// Method:  ~CDirectDrawGame --> class destructor
  49. /// Purpose: Release member variables
  50. ///////////////////////////////////////////////////////////////////////////////
  51. /// Receives: nothing
  52. /// Returns:  nothing
  53. ///////////////////////////////////////////////////////////////////////////////
  54. CDirectDrawGame::~CDirectDrawGame()
  55. {
  56. // release the DirectX objects just incase
  57. ShutdownDirectInput();
  58. ShutdownDirectDraw();
  59.     return;
  60. }
  61. /// CLASS MEMBER FUNCTIONS ////////////////////////////////////////////////////
  62. ///////////////////////////////////////////////////////////////////////////////
  63. /// Method:  CalculateRBGBitShift
  64. /// Purpose: Calculate the bit shift required to convert an RGB color to a
  65. ///          16-bit (DWORD) number DirectDraw understands.  This function needs
  66. ///          to be called for each color component
  67. /// Scope:   private
  68. ///////////////////////////////////////////////////////////////////////////////
  69. /// Receives: Bitmask of color component
  70. ///           Pointer to 8-bit (WORD) number to hold low order bit
  71. ///           Pointer to 8-bit (WORD) number to hold number of bits to shift
  72. /// Returns:  none
  73. ///////////////////////////////////////////////////////////////////////////////
  74. void CDirectDrawGame::CalculateRGBBitShift(DWORD p_bit_mask, WORD* p_low_bit, WORD* p_num_bits)
  75. {
  76. *p_low_bit = 0;
  77. *p_num_bits = 0;
  78. // count number of zeroes on right side of mask
  79. while ( !(p_bit_mask & 1) )
  80. {
  81. (*p_low_bit)++;
  82. p_bit_mask >>= 1;
  83. }
  84. while (p_bit_mask & 1)
  85. {
  86. (*p_num_bits)++;
  87. p_bit_mask >>= 1;
  88. }
  89. }
  90. ///////////////////////////////////////////////////////////////////////////////
  91. /// Method:  DrawRectangle
  92. /// Purpose: Draw the passed rectangle to the back buffer
  93. /// Scope:   public
  94. ///////////////////////////////////////////////////////////////////////////////
  95. /// Receives: RECT structure of destination rectangle to BLT to the back buffer
  96. ///           DWORD color of the rectangle to BLT
  97. /// Returns:  true if BLT successful, otherwise false
  98. ///////////////////////////////////////////////////////////////////////////////
  99. bool CDirectDrawGame::DrawRectangle(RECT* p_rectangle, const DWORD p_color)
  100. {
  101. DWORD dxr = 0;
  102. // initialize BLTFX structure
  103. DDBLTFX ddbfx;
  104.     memset(&ddbfx, 0, sizeof(ddbfx));
  105. ddbfx.dwSize = sizeof(ddbfx);
  106. ddbfx.dwFillColor = p_color;
  107. // BLT specified color to entire back buffer
  108. dxr = m_lpdds_back->Blt
  109.   (
  110.       /* lpDestRect     */ p_rectangle, // pointer to rectangle on back buffer to fill with color
  111.               /* lpDDSrcSurface */ NULL, // source surface, NULL for a color fill
  112.   /* lpSrcRect      */ NULL, // source RECT, NULL for a color fill
  113.   /* dwFlags        */ DDBLT_COLORFILL | DDBLT_WAIT, // tell BLT to fill and wait till not busy
  114.   /* lpDDBltFx      */ &ddbfx // pointer to BLTFX struct
  115.   );
  116. if (FAILED(dxr))
  117. {
  118. return(false);
  119. }
  120.     return true;
  121. }
  122. ///////////////////////////////////////////////////////////////////////////////
  123. /// Method:  DrawGDIText
  124. /// Purpose: Draw the passed text in the specified font and rectangle
  125. /// Scope:   public
  126. ///////////////////////////////////////////////////////////////////////////////
  127. /// Receives: - text to draw on the screen
  128. ///           - red component of text color
  129. ///           - green component of text color
  130. ///           - blue component of text color
  131. ///           - HFONT of font to draw
  132. ///           - x coordinate of top left corner of text rectangle
  133. ///           - y coordinate of top left corner of text rectangle
  134. /// Returns:  true if DrawText successful, otherwise false
  135. ///////////////////////////////////////////////////////////////////////////////
  136. bool CDirectDrawGame::DrawGDIText(char* p_text, DWORD p_r, DWORD p_g, DWORD p_b, HFONT p_font, int p_x, int p_y)
  137. {
  138. HFONT l_old_font = NULL;
  139. l_old_font = (HFONT)SelectObject(m_hdc, p_font);
  140. if (!l_old_font)
  141. {
  142. return(false);
  143. }
  144. SetTextColor(m_hdc, RGB(p_r,p_g,p_b));
  145.          
  146.     // set the background color to black
  147.     SetBkColor(m_hdc, RGB(0,0,0));
  148.     
  149.     // set the transparency mode to OPAQUE
  150.     SetBkMode(m_hdc, TRANSPARENT);
  151. TextOut(m_hdc, p_x, p_y, p_text, strlen(p_text));
  152.     SelectObject(m_hdc, l_old_font);
  153. return(true);
  154. }
  155. ///////////////////////////////////////////////////////////////////////////////
  156. /// Method:  EndGDIDrawing
  157. /// Purpose: Release the GDI variables
  158. /// Scope:   public
  159. ///////////////////////////////////////////////////////////////////////////////
  160. /// Receives: None
  161. /// Returns:  Nothing
  162. ///////////////////////////////////////////////////////////////////////////////
  163. void CDirectDrawGame::EndGDIDrawing()
  164. {
  165. m_lpdds_back->ReleaseDC(m_hdc);
  166. return;
  167. }
  168. ///////////////////////////////////////////////////////////////////////////////
  169. /// Method:  EndScene
  170. /// Purpose: Complete the scene and flip the primary surface
  171. /// Scope:   public
  172. ///////////////////////////////////////////////////////////////////////////////
  173. /// Receives: None
  174. /// Returns:  true if flip successful, otherwise false
  175. ///////////////////////////////////////////////////////////////////////////////
  176. bool CDirectDrawGame::EndScene()
  177. {
  178. DWORD dxr = 0;
  179. dxr = m_lpdds_primary->Flip(NULL, DDFLIP_WAIT);
  180. if (FAILED(dxr))
  181. {
  182. return(false);
  183. }
  184. return(true);
  185. }
  186. ///////////////////////////////////////////////////////////////////////////////
  187. /// Method:  FrameRateStart
  188. /// Purpose: Method used to start the timing to sync the frame
  189. /// Scope:   public
  190. ///////////////////////////////////////////////////////////////////////////////
  191. /// Receives: None
  192. /// Returns:  None
  193. ///////////////////////////////////////////////////////////////////////////////
  194. void CDirectDrawGame::FrameRateStart()
  195. {
  196.     m_tick_cnt = GetTickCount();
  197. }
  198. ///////////////////////////////////////////////////////////////////////////////
  199. /// Method:  FrameRateEnd
  200. /// Purpose: Method used to end the timing to sync the frame
  201. /// Scope:   public
  202. ///////////////////////////////////////////////////////////////////////////////
  203. /// Receives: None
  204. /// Returns:  None
  205. ///////////////////////////////////////////////////////////////////////////////
  206. void CDirectDrawGame::FrameRateEnd()
  207. {
  208.     DWORD l_wait = 0;
  209. l_wait = (m_wait_ms - (GetTickCount() - m_tick_cnt));
  210. if (l_wait > 0)
  211. {
  212. Sleep(l_wait);
  213. }
  214. //Sleep(100);
  215. }
  216. ///////////////////////////////////////////////////////////////////////////////
  217. /// Method:  GetKeyboardState
  218. /// Purpose: Method used to retrieve the keys pressed from DirectInput
  219. /// Scope:   public
  220. ///////////////////////////////////////////////////////////////////////////////
  221. /// Receives: KEYSTATE (UCHAR[256]) Array
  222. /// Returns:  True is successfully retrieve keyboard state, otherwise false
  223. ///////////////////////////////////////////////////////////////////////////////
  224. bool CDirectDrawGame::GetKeyboardState(UCHAR* p_keyboard)
  225. {
  226.     DWORD dxr = 0;
  227. dxr = m_di_keyboard->GetDeviceState(256, (void*)p_keyboard);
  228. if (FAILED(dxr))
  229. {
  230. return(false);
  231. }
  232. return(true);
  233. }
  234. ///////////////////////////////////////////////////////////////////////////////
  235. /// Method:  InitializeDirectDraw
  236. /// Purpose: Initialize all the required DirectDraw objects and interfaces
  237. /// Scope:   private
  238. ///////////////////////////////////////////////////////////////////////////////
  239. /// Receives: None
  240. /// Returns:  true if initialization successful, otherwise false
  241. ///////////////////////////////////////////////////////////////////////////////
  242. bool CDirectDrawGame::InitializeDirectDraw()
  243. {
  244.     DWORD dxr = 0; /** DirectX function return value **/
  245.     ////////////////////////////////////////////////////
  246.     /// create main DirectDraw interface object
  247.     ////////////////////////////////////////////////////
  248.     dxr = DirectDrawCreateEx(NULL, (void**)&m_lpdd7, IID_IDirectDraw7, NULL);
  249.     if (FAILED(dxr))
  250.     {
  251.         return false;
  252.     }
  253.     ////////////////////////////////////////////////////
  254.     /// set DirectDraw cooperation level with the
  255.     /// main window
  256.     ////////////////////////////////////////////////////
  257. dxr = m_lpdd7->SetCooperativeLevel(m_main_hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN |DDSCL_ALLOWREBOOT);
  258. if (FAILED(dxr))
  259.     {
  260.         return false;
  261.     }
  262.     ////////////////////////////////////////////////////
  263.     /// set the display mode for DirectDraw
  264.     ////////////////////////////////////////////////////
  265. dxr = m_lpdd7->SetDisplayMode(m_screen_width, m_screen_height, m_screen_bpp, 0, 0);
  266. if (FAILED(dxr))
  267.     {
  268.         return false;
  269.     }
  270.     ////////////////////////////////////////////////////
  271.     /// set description of the primary surface
  272.     ////////////////////////////////////////////////////
  273. DDSURFACEDESC2 ddsd;
  274. memset(&ddsd, 0, sizeof(ddsd));
  275. ddsd.dwSize = sizeof(ddsd);
  276. ddsd.dwFlags = (DDSD_CAPS | DDSD_BACKBUFFERCOUNT);
  277. ddsd.ddsCaps.dwCaps = (DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP);
  278. ddsd.dwBackBufferCount = 1;
  279.     ////////////////////////////////////////////////////
  280.     /// create the primary surface
  281.     ////////////////////////////////////////////////////
  282.     dxr = m_lpdd7->CreateSurface(&ddsd, &m_lpdds_primary, NULL);
  283.     if (FAILED(dxr))
  284.     {
  285.         return false;
  286.     }
  287.     ////////////////////////////////////////////////////
  288.     /// retrieve pointer to the back buffer
  289.     ////////////////////////////////////////////////////
  290. memset(&ddsd, 0, sizeof(ddsd));
  291. ddsd.dwSize = sizeof(ddsd);
  292. ddsd.dwFlags = DDSD_CAPS;
  293. ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
  294. dxr = m_lpdds_primary->GetAttachedSurface(&ddsd.ddsCaps, &m_lpdds_back);
  295. if (FAILED(dxr))
  296.     {
  297.         return false;
  298.     }
  299.     ////////////////////////////////////////////////////
  300.     /// determine pixel format for 16-bit color
  301.     ////////////////////////////////////////////////////
  302. if (m_screen_bpp == 16)
  303. {
  304. DDPIXELFORMAT ddpf;
  305. memset(&ddpf, 0, sizeof(ddpf));
  306. ddpf.dwSize = sizeof(ddpf);
  307. dxr = m_lpdds_primary->GetPixelFormat(&ddpf);
  308. if (FAILED(dxr))
  309. {
  310. return(false);
  311. }
  312. // determine bit count and set function pointer for later transfer
  313. // from the bitmap to the surface
  314. if (ddpf.dwFlags & DDPF_RGB)
  315. {
  316. // calculate bit shift for each RGB component
  317. CalculateRGBBitShift(ddpf.dwRBitMask, &m_low_red_bit, &m_num_red_bits);
  318. CalculateRGBBitShift(ddpf.dwGBitMask, &m_low_green_bit, &m_num_green_bits);
  319. CalculateRGBBitShift(ddpf.dwBBitMask, &m_low_blue_bit, &m_num_blue_bits);
  320. }
  321. else
  322. {
  323. return(false);
  324. }
  325. }
  326.     return true;
  327. }
  328. ///////////////////////////////////////////////////////////////////////////////
  329. /// Method:  InitializeDirectInput
  330. /// Purpose: Initialize all the required DirectInput objects and interfaces
  331. /// Scope:   private
  332. ///////////////////////////////////////////////////////////////////////////////
  333. /// Receives: None
  334. /// Returns:  true if initialization successful, otherwise false
  335. ///////////////////////////////////////////////////////////////////////////////
  336. bool CDirectDrawGame::InitializeDirectInput()
  337. {
  338. DWORD dxr = 0; /** DirectX function return value **/
  339.     ////////////////////////////////////////////////////
  340.     /// create main DirectInput interface object
  341.     ////////////////////////////////////////////////////
  342. dxr = DirectInput8Create(m_instance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_lpdi8, NULL);
  343. if (FAILED(dxr))
  344. {
  345. return(false);
  346. }
  347.     ////////////////////////////////////////////////////
  348.     /// create keyboard device
  349.     ////////////////////////////////////////////////////
  350. dxr = m_lpdi8->CreateDevice(GUID_SysKeyboard, &m_di_keyboard, NULL);
  351. if (FAILED(dxr))
  352. {
  353. return(false);
  354. }
  355. ////////////////////////////////////////////////////
  356. /// set cooperation level of the keyboard device
  357. ////////////////////////////////////////////////////
  358. dxr = m_di_keyboard->SetCooperativeLevel(m_main_hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
  359. if (FAILED(dxr))
  360. {
  361. return(false);
  362. }
  363.     ////////////////////////////////////////////////////
  364. /// set data format of the keyboard device
  365. ////////////////////////////////////////////////////
  366. dxr = m_di_keyboard->SetDataFormat(&c_dfDIKeyboard);
  367. if (FAILED(dxr))
  368. {
  369. return(false);
  370. }
  371. ////////////////////////////////////////////////////
  372. /// last, but not least, acquire the keyboard
  373. ////////////////////////////////////////////////////
  374. dxr = m_di_keyboard->Acquire();
  375. if (FAILED(dxr))
  376. {
  377. return(false);
  378. }
  379.     return true;
  380. }
  381. ///////////////////////////////////////////////////////////////////////////////
  382. /// Method:  RGBto16BitColor
  383. /// Purpose: Convert a RGB Triad to a 16-bit (DWORD) color
  384. /// Scope:   public
  385. ///////////////////////////////////////////////////////////////////////////////
  386. /// Receives: DWORD for the red color
  387. ///           DWORD for the green color
  388. ///           DWORD for the blue color
  389. /// Returns:  16-bit (DWORD) 
  390. ///////////////////////////////////////////////////////////////////////////////
  391. DWORD CDirectDrawGame::RGBto16BitColor(DWORD p_red, DWORD p_green, DWORD p_blue)
  392. {
  393. DWORD l_red = (p_red >> (8 - m_num_red_bits));
  394. DWORD l_green = (p_green >> (8 - m_num_green_bits));
  395. DWORD l_blue = (p_blue >> (8 - m_num_blue_bits));
  396. l_red <<= m_low_red_bit;
  397. l_green <<= m_low_green_bit;
  398. l_blue <<= m_low_blue_bit;
  399. return(l_red | l_green | l_blue);
  400. };
  401. ///////////////////////////////////////////////////////////////////////////////
  402. /// Method:  SetApplicationInstance
  403. /// Purpose: Initialize application instance handle to be used by DirectInput
  404. /// Scope:   public
  405. ///////////////////////////////////////////////////////////////////////////////
  406. /// Receives: application instance handle
  407. /// Returns:  None
  408. ///////////////////////////////////////////////////////////////////////////////
  409. void CDirectDrawGame::SetApplicationInstance(const HINSTANCE p_instance)
  410. {
  411. m_instance = p_instance;
  412. };
  413. ///////////////////////////////////////////////////////////////////////////////
  414. /// Method:  SetFrameRate
  415. /// Purpose: Initialize frame rate (ie. frames per second) of the game
  416. /// Scope:   public
  417. ///////////////////////////////////////////////////////////////////////////////
  418. /// Receives: frame rate
  419. /// Returns:  None
  420. ///////////////////////////////////////////////////////////////////////////////
  421. void CDirectDrawGame::SetFrameRate(const DWORD p_frame_fps)
  422. {
  423. m_frame_fps = p_frame_fps;
  424. m_wait_ms = 1000 / m_frame_fps;
  425. }
  426. ///////////////////////////////////////////////////////////////////////////////
  427. /// Method:  SetScreenProperties
  428. /// Purpose: Initialize all screen related properties for DirectDraw
  429. /// Scope:   public
  430. ///////////////////////////////////////////////////////////////////////////////
  431. /// Receives: screen width in pixels
  432. ///           screen height in pixels
  433. ///           screen bpp (bits per pixel)
  434. /// Returns:  None
  435. ///////////////////////////////////////////////////////////////////////////////
  436. void CDirectDrawGame::SetScreenProperties(const DWORD p_screen_width, const DWORD p_screen_height, const DWORD p_screen_bpp)
  437. {
  438. m_screen_width = p_screen_width;
  439. m_screen_height = p_screen_height;
  440. m_screen_bpp = p_screen_bpp;
  441. }
  442. ///////////////////////////////////////////////////////////////////////////////
  443. /// Method:  SetWindowHandle
  444. /// Purpose: Initialize main window handle to be used by DirectDraw
  445. /// Scope:   public
  446. ///////////////////////////////////////////////////////////////////////////////
  447. /// Receives: window handle
  448. /// Returns:  None
  449. ///////////////////////////////////////////////////////////////////////////////
  450. void CDirectDrawGame::SetWindowHandle(const HWND p_hwnd)
  451. {
  452. m_main_hwnd = p_hwnd;
  453. };
  454. ///////////////////////////////////////////////////////////////////////////////
  455. /// Method:  ShutdownDirectDraw
  456. /// Purpose: Uninitialize the allocated DirectDraw objects
  457. /// Scope:   public
  458. ///////////////////////////////////////////////////////////////////////////////
  459. /// Receives: None
  460. /// Returns:  None
  461. ///////////////////////////////////////////////////////////////////////////////
  462. void CDirectDrawGame::ShutdownDirectDraw()
  463. {
  464. if (m_lpdds_back)
  465. {
  466. m_lpdds_back->Release();
  467. m_lpdds_back = NULL;
  468. }
  469. if (m_lpdds_primary)
  470. {
  471. m_lpdds_primary->Release();
  472. m_lpdds_primary = NULL;
  473. }
  474. if (m_lpdd7)
  475. {
  476. m_lpdd7->Release();
  477. m_lpdd7 = NULL;
  478. }
  479. return;
  480. };
  481. ///////////////////////////////////////////////////////////////////////////////
  482. /// Method:  ShutdownDirectInput
  483. /// Purpose: Uninitialize the allocated DirectInput objects
  484. /// Scope:   public
  485. ///////////////////////////////////////////////////////////////////////////////
  486. /// Receives: None
  487. /// Returns:  None
  488. ///////////////////////////////////////////////////////////////////////////////
  489. void CDirectDrawGame::ShutdownDirectInput()
  490. {
  491. if (m_di_keyboard)
  492. {
  493. m_di_keyboard->Unacquire();
  494. m_di_keyboard->Release();
  495. m_di_keyboard = NULL;
  496. }
  497. if (m_lpdi8)
  498. {
  499. m_lpdi8->Release();
  500. m_lpdi8 = NULL;
  501. }
  502. return;
  503. };
  504. ///////////////////////////////////////////////////////////////////////////////
  505. /// Method:  StartGDIDrawing
  506. /// Purpose: Capture the GDI variables
  507. /// Scope:   public
  508. ///////////////////////////////////////////////////////////////////////////////
  509. /// Receives: None
  510. /// Returns:  None
  511. ///////////////////////////////////////////////////////////////////////////////
  512. bool CDirectDrawGame::StartGDIDrawing()
  513. {
  514. DWORD dxr = 0;
  515. dxr = m_lpdds_back->GetDC(&m_hdc);
  516. if (FAILED(dxr))
  517. {
  518. return(false);
  519. }
  520. return(true);
  521. };
  522. ///////////////////////////////////////////////////////////////////////////////
  523. /// Method:  StartScene
  524. /// Purpose: Erases the back buffer filling it with the passed color
  525. /// Scope:   public
  526. ///////////////////////////////////////////////////////////////////////////////
  527. /// Receives: USHORT color to fill background
  528. /// Returns:  true if clear successful, otherwise false
  529. ///////////////////////////////////////////////////////////////////////////////
  530. bool CDirectDrawGame::StartScene(const DWORD p_color)
  531. {
  532. DWORD dxr = 0;
  533. // initialize BLTFX structure
  534. DDBLTFX ddbfx;
  535. memset(&ddbfx, 0, sizeof(ddbfx));
  536. ddbfx.dwSize = sizeof(ddbfx);
  537. ddbfx.dwFillColor = p_color;
  538. // BLT specified color to entire back buffer
  539. dxr = m_lpdds_back->Blt
  540.   (
  541.       /* lpDestRect     */ NULL, // destination RECT, NULL for whole surface
  542.               /* lpDDSrcSurface */ NULL, // source surface, NULL for a color fill
  543.   /* lpSrcRect      */ NULL, // source RECT, NULL for whole surface
  544.   /* dwFlags        */ DDBLT_COLORFILL | DDBLT_WAIT, // tell BLT to fill and wait till not busy
  545.   /* lpDDBltFx      */ &ddbfx // pointer to BLTFX struct
  546.   );
  547. if (FAILED(dxr))
  548. {
  549. return(false);
  550. }
  551.     return true;
  552. }
  553. /// END OF FILE ///////////////////////////////////////////////////////////////