text: /* $Id: VBoxVideoWinDbg.cpp 71179 2018-03-02 15:08:52Z vboxsync $ */
  /** @file
   * ???
   */
  
  /*
   * Copyright (C) 2017 Oracle Corporation
   *
   * This file is part of VirtualBox Open Source Edition (OSE), as
   * available from http://www.virtualbox.org. This file is free software;
   * you can redistribute it and/or modify it under the terms of the GNU
   * General Public License (GPL) as published by the Free Software
   * Foundation, in version 2 as it comes in the "COPYING" file of the
   * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
   * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
   */
  
  #include <iprt/win/windows.h>
  #define KDEXT_64BIT
  #include <wdbgexts.h>
  
  #define VBOXVWD_VERSION_MAJOR 1
  #define VBOXVWD_VERSION_MINOR 1
  
  static EXT_API_VERSION g_VBoxVWDVersion = {
          VBOXVWD_VERSION_MAJOR,
          VBOXVWD_VERSION_MINOR,
          EXT_API_VERSION_NUMBER64,
          0
  };
  
  /**
   * DLL entry point.
   */
  BOOL WINAPI DllMain(HINSTANCE hInstance,
                      DWORD     dwReason,
                      LPVOID    lpReserved)
  {
      BOOL bOk = TRUE;
  
      switch (dwReason)
      {
          case DLL_PROCESS_ATTACH:
          {
              break;
          }
  
          case DLL_PROCESS_DETACH:
          {
              break;
          }
  
          default:
              break;
      }
      return bOk;
  }
  
  /* note: need to name it this way to make dprintf & other macros defined in wdbgexts.h work */
  WINDBG_EXTENSION_APIS64 ExtensionApis = {0};
  USHORT SavedMajorVersion;
  USHORT SavedMinorVersion;
  
  #ifdef __cplusplus
  extern "C"
  {
  #endif
  LPEXT_API_VERSION WDBGAPI ExtensionApiVersion();
  VOID WDBGAPI CheckVersion();
  VOID WDBGAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS64 lpExtensionApis, USHORT MajorVersion, USHORT MinorVersion);
  #ifdef __cplusplus
  }
  #endif
  
  LPEXT_API_VERSION WDBGAPI ExtensionApiVersion()
  {
      return &g_VBoxVWDVersion;
  }
  
  VOID WDBGAPI CheckVersion()
  {
      return;
  }
  
  VOID WDBGAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS64 lpExtensionApis, USHORT MajorVersion, USHORT MinorVersion)
  {
      ExtensionApis = *lpExtensionApis;
      SavedMajorVersion = MajorVersion;
      SavedMinorVersion = MinorVersion;
  }
  
  DECLARE_API(help)
  {
      dprintf("**** VirtualBox Video Driver debugging extension ****\n"
              " The following commands are supported: \n"
              " !ms - save memory (video data) to clipboard \n"
              "  usage: !ms <virtual memory address> <width> <height> [bitsPerPixel (default is 32)] [pitch (default is ((width * bpp + 7) >> 3) + 3) & ~3)]\n");
  }
  
  DECLARE_API(ms)
  {
      ULONG64 u64Mem;
      ULONG64 u64Width;
      ULONG64 u64Height;
      ULONG64 u64Bpp = 32;
      ULONG64 u64NumColors = 3;
      ULONG64 u64Pitch;
      ULONG64 u64DefaultPitch;
      PCSTR pExpr = args;
  
      /* address */
      if (!pExpr) { dprintf("address not specified\n"); return; }
      if (!GetExpressionEx(pExpr, &u64Mem, &pExpr)) { dprintf("error evaluating address\n"); return; }
      if (!u64Mem) { dprintf("address value can not be NULL\n"); return; }
  
      /* width */
      if (!pExpr) { dprintf("width not specified\n"); return; }
      if (!GetExpressionEx(pExpr, &u64Width, &pExpr)) { dprintf("error evaluating width\n"); return; }
      if (!u64Width) { dprintf("width value can not be NULL\n"); return; }
  
      /* height */
      if (!pExpr) { dprintf("height not specified\n"); return; }
      if (!GetExpressionEx(pExpr, &u64Height, &pExpr)) { dprintf("error evaluating height\n"); return; }
      if (!u64Height) { dprintf("height value can not be NULL\n"); return; }
  
  #if 0
      if (pExpr && GetExpressionEx(pExpr, &u64NumColors, &pExpr))
      {
          if (!u64NumColors) { dprintf("Num Colors value can not be NULL\n"); return; }
      }
  #endif
  
      /* bpp */
      if (pExpr && GetExpressionEx(pExpr, &u64Bpp, &pExpr))
      {
          if (!u64Bpp) { dprintf("bpp value can not be NULL\n"); return; }
      }
  
      /* pitch */
      u64DefaultPitch = (((((u64Width * u64Bpp) + 7) >> 3) + 3) & ~3ULL);
      if (pExpr && GetExpressionEx(pExpr, &u64Pitch, &pExpr))
      {
          if (u64Pitch < u64DefaultPitch) { dprintf("pitch value can not be less than (%d)\n", (UINT)u64DefaultPitch); return; }
      }
      else
      {
          u64Pitch = u64DefaultPitch;
      }
  
      dprintf("processing data for address(0x%p), width(%d), height(%d), bpp(%d), pitch(%d)...\n",
                  u64Mem, (UINT)u64Width, (UINT)u64Height, (UINT)u64Bpp, (UINT)u64Pitch);
  
      ULONG64 cbSize = u64DefaultPitch * u64Height;
      PVOID pvBuf = malloc(cbSize);
      if (!pvBuf)
      {
          dprintf("failed to allocate memory buffer of size(%d)\n", (UINT)cbSize);
          return;
      }
      ULONG uRc = 0;
      if(u64DefaultPitch == u64Pitch)
      {
          ULONG cbRead = 0;
          dprintf("reading the entire memory buffer...\n");
          uRc = ReadMemory(u64Mem, pvBuf, cbSize, &cbRead);
          if (!uRc)
          {
              dprintf("Failed to read the memory buffer of size(%d)\n", (UINT)cbSize);
          }
          else if (cbRead != cbSize)
          {
              dprintf("the actual number of bytes read(%d) no equal the requested size(%d)\n", (UINT)cbRead, (UINT)cbSize);
              uRc = 0;
          }
  
      }
      else
      {
          ULONG64 u64Offset = u64Mem;
          char* pvcBuf = (char*)pvBuf;
          ULONG64 i;
          dprintf("reading memory by chunks since custom pitch is specified...\n");
          for (i = 0; i < u64Height; ++i, u64Offset+=u64Pitch, pvcBuf+=u64DefaultPitch)
          {
              ULONG cbRead = 0;
              uRc = ReadMemory(u64Offset, pvcBuf, u64DefaultPitch, &cbRead);
              if (!uRc)
              {
                  dprintf("WARNING!!! Failed to read the memory buffer of size(%d), chunk(%d)\n", (UINT)u64DefaultPitch, (UINT)i);
                  dprintf("ignoring this one and the all the rest, using height(%d)\n", (UINT)i);
                  u64Height = i;
                  uRc = 1;
                  break;
              }
              else if (cbRead != u64DefaultPitch)
              {
                  dprintf("WARNING!!! the actual number of bytes read(%d) not equal the requested size(%d), chunk(%d)\n", (UINT)cbRead, (UINT)u64DefaultPitch, (UINT)i);
                  dprintf("ignoring this one and the all the rest, using height(%d)\n", (UINT)i);
                  u64Height = i;
                  break;
              }
          }
      }
  
      if (!uRc)
      {
          dprintf("read memory failed\n");
          free(pvBuf);
          return;
      }
  
      if (!u64Height)
      {
          dprintf("no data to be processed since height it 0\n");
          free(pvBuf);
          return;
      }
  
      switch (u64Bpp)
      {
          case 32:
          case 24:
          case 16:
  #if 0
              if (u64NumColors != 3)
              {
                  dprintf("WARNING: unsupported number colors: (%d)\n", (UINT)u64NumColors);
              }
  #else
              u64NumColors = 3;
  #endif
              break;
          case 8:
              {
  #if 1
                  u64NumColors = 1;
  #endif
  
                  if (u64NumColors == 1)
                  {
                      ULONG64 cbSize32 = u64DefaultPitch * 4 * u64Height;
                      PVOID pvBuf32 = malloc(cbSize32);
                      if (!pvBuf32)
                      {
                          dprintf("ERROR: failed to allocate memory buffer of size(%d)", cbSize32);
                          free(pvBuf);
                          return;
                      }
                      byte* pByteBuf32 = (byte*)pvBuf32;
                      byte* pByteBuf = (byte*)pvBuf;
                      memset(pvBuf32, 0, cbSize32);
                      for (UINT i = 0; i < u64Height; ++i)
                      {
                          for (UINT j = 0; j < u64Width; ++j)
                          {
                              pByteBuf32[0] = pByteBuf[0];
                              pByteBuf32[1] = pByteBuf[0];
                              pByteBuf32[2] = pByteBuf[0];
                              pByteBuf32 += 4;
                              pByteBuf += 1;
                          }
                      }
                      free(pvBuf);
                      pvBuf = pvBuf32;
                      u64DefaultPitch *= 4;
                      u64Bpp *= 4;
                  }
                  else
                  {
                      dprintf("WARNING: unsupported number colors: (%d)\n", (UINT)u64NumColors);
                  }
              }
              break;
      }
      BITMAP Bmp = {0};
      HBITMAP hBmp;
      dprintf("read memory succeeded..\n");
      Bmp.bmType = 0;
      Bmp.bmWidth = (LONG)u64Width;
      Bmp.bmHeight = (LONG)u64Height;
      Bmp.bmWidthBytes = (LONG)u64DefaultPitch;
      Bmp.bmPlanes = 1;
      Bmp.bmBitsPixel = (WORD)u64Bpp;
      Bmp.bmBits = (LPVOID)pvBuf;
      hBmp = CreateBitmapIndirect(&Bmp);
      if (hBmp)
      {
          if (OpenClipboard(GetDesktopWindow()))
          {
              if (EmptyClipboard())
              {
                  if (SetClipboardData(CF_BITMAP, hBmp))
                  {
                      dprintf("succeeded!! You can now do <ctrl>+v in your favourite image editor\n");
                  }
                  else
                  {
                      DWORD winEr = GetLastError();
                      dprintf("SetClipboardData failed, err(%d)\n", winEr);
                  }
              }
              else
              {
                  DWORD winEr = GetLastError();
                  dprintf("EmptyClipboard failed, err(%d)\n", winEr);
              }
  
              CloseClipboard();
          }
          else
          {
              DWORD winEr = GetLastError();
              dprintf("OpenClipboard failed, err(%d)\n", winEr);
          }
  
          DeleteObject(hBmp);
      }
      else
      {
          DWORD winEr = GetLastError();
          dprintf("CreateBitmapIndirect failed, err(%d)\n", winEr);
      }
  }
  