/**************************************************************************
 * 
 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
 * Copyright (c) 2008 VMware, Inc.
 * All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 **************************************************************************/


#include "pipe/p_config.h" 

#include <stdarg.h>


#ifdef PIPE_SUBSYSTEM_WINDOWS_DISPLAY

#include <windows.h>
#include <winddi.h>

#elif defined(PIPE_SUBSYSTEM_WINDOWS_CE)

#include <stdio.h> 
#include <stdlib.h> 
#include <windows.h> 
#include <types.h> 

#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER)

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN      // Exclude rarely-used stuff from Windows headers
#endif
#include <windows.h>
#include <stdio.h>

#else

#include <stdio.h>
#include <stdlib.h>

#endif

#include "pipe/p_compiler.h" 
#include "util/u_debug.h" 
#include "pipe/p_format.h" 
#include "pipe/p_state.h" 
#include "pipe/p_inlines.h" 
#include "util/u_memory.h" 
#include "util/u_string.h" 
#include "util/u_stream.h" 
#include "util/u_math.h" 
#include "util/u_tile.h" 


#ifdef PIPE_SUBSYSTEM_WINDOWS_DISPLAY
static INLINE void 
_EngDebugPrint(const char *format, ...)
{
   va_list ap;
   va_start(ap, format);
   EngDebugPrint("", (PCHAR)format, ap);
   va_end(ap);
}
#endif


void _debug_vprintf(const char *format, va_list ap)
{
#if defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY)
   /* EngDebugPrint does not handle float point arguments, so we need to use
    * our own vsnprintf implementation. It is also very slow, so buffer until
    * we find a newline. */
   static char buf[512] = {'\0'};
   size_t len = strlen(buf);
   int ret = util_vsnprintf(buf + len, sizeof(buf) - len, format, ap);
   if(ret > (int)(sizeof(buf) - len - 1) || util_strchr(buf + len, '\n')) {
      _EngDebugPrint("%s", buf);
      buf[0] = '\0';
   }
#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER)
   /* OutputDebugStringA can be very slow, so buffer until we find a newline. */
   static char buf[4096] = {'\0'};
   size_t len = strlen(buf);
   int ret = util_vsnprintf(buf + len, sizeof(buf) - len, format, ap);
   if(ret > (int)(sizeof(buf) - len - 1) || util_strchr(buf + len, '\n')) {
      OutputDebugStringA(buf);
      buf[0] = '\0';
   }
   
   if(GetConsoleWindow() && !IsDebuggerPresent()) {
      fflush(stdout);
      vfprintf(stderr, format, ap);
      fflush(stderr);
   }
   
#elif defined(PIPE_SUBSYSTEM_WINDOWS_CE)
   wchar_t *wide_format;
   long wide_str_len;   
   char buf[512];   
   int ret;   
#if (_WIN32_WCE < 600)
   ret = vsprintf(buf, format, ap);   
   if(ret < 0){   
       sprintf(buf, "Cant handle debug print!");   
       ret = 25;
   }
#else
   ret = vsprintf_s(buf, 512, format, ap);   
   if(ret < 0){   
       sprintf_s(buf, 512, "Cant handle debug print!");   
       ret = 25;
   }
#endif
   buf[ret] = '\0';   
   /* Format is ascii - needs to be converted to wchar_t for printing */   
   wide_str_len = MultiByteToWideChar(CP_ACP, 0, (const char *) buf, -1, NULL, 0);   
   wide_format = (wchar_t *) malloc((wide_str_len+1) * sizeof(wchar_t));   
   if (wide_format) {   
      MultiByteToWideChar(CP_ACP, 0, (const char *) buf, -1,   
            wide_format, wide_str_len);   
      NKDbgPrintfW(wide_format, wide_format);   
      free(wide_format);   
   } 
#elif defined(PIPE_SUBSYSTEM_WINDOWS_MINIPORT)
   /* TODO */
#else /* !PIPE_SUBSYSTEM_WINDOWS */
   fflush(stdout);
   vfprintf(stderr, format, ap);
#endif
}


#ifdef DEBUG
void debug_print_blob( const char *name,
                       const void *blob,
                       unsigned size )
{
   const unsigned *ublob = (const unsigned *)blob;
   unsigned i;

   debug_printf("%s (%d dwords%s)\n", name, size/4,
                size%4 ? "... plus a few bytes" : "");

   for (i = 0; i < size/4; i++) {
      debug_printf("%d:\t%08x\n", i, ublob[i]);
   }
}
#endif


#ifndef debug_break
void debug_break(void) 
{
#if defined(PIPE_SUBSYSTEM_WINDOWS_USER)
   DebugBreak();
#elif defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY)
   EngDebugBreak();
#else
   abort();
#endif
}
#endif


#ifdef PIPE_SUBSYSTEM_WINDOWS_DISPLAY
static const char *
find(const char *start, const char *end, char c) 
{
   const char *p;
   for(p = start; !end || p != end; ++p) {
      if(*p == c)
	 return p;
      if(*p < 32)
	 break;
   }
   return NULL;
}

static int 
compare(const char *start, const char *end, const char *s)
{
   const char *p, *q;
   for(p = start, q = s; p != end && *q != '\0'; ++p, ++q) {
      if(*p != *q)
	 return 0;
   }
   return p == end && *q == '\0';
}

static void 
copy(char *dst, const char *start, const char *end, size_t n) 
{
   const char *p;
   char *q;
   for(p = start, q = dst, n = n - 1; p != end && n; ++p, ++q, --n)
      *q = *p;
   *q = '\0';
}
#endif


static INLINE const char *
_debug_get_option(const char *name)
{
#if defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY)
   /* EngMapFile creates the file if it does not exists, so it must either be
    * disabled on release versions (or put in a less conspicuous place). */
#ifdef DEBUG
   const char *result = NULL;
   ULONG_PTR iFile = 0;
   const void *pMap = NULL;
   const char *sol, *eol, *sep;
   static char output[1024];
   
   pMap = EngMapFile(L"\\??\\c:\\gallium.cfg", 0, &iFile);
   if(pMap) {
      sol = (const char *)pMap;
      while(1) {
	 /* TODO: handle LF line endings */
	 eol = find(sol, NULL, '\r');
	 if(!eol || eol == sol)
	    break;
	 sep = find(sol, eol, '=');
	 if(!sep)
	    break;
	 if(compare(sol, sep, name)) {
	    copy(output, sep + 1, eol, sizeof(output));
	    result = output;
	    break;
	 }
	 sol = eol + 2;
      }
      EngUnmapFile(iFile);
   }
   return result;
#else
   return NULL;
#endif
#elif defined(PIPE_SUBSYSTEM_WINDOWS_CE) || defined(PIPE_SUBSYSTEM_WINDOWS_MINIPORT) 
   /* TODO: implement */
   return NULL;
#else
   return getenv(name);
#endif
}

const char *
debug_get_option(const char *name, const char *dfault)
{
   const char *result;

   result = _debug_get_option(name);
   if(!result)
      result = dfault;
      
   debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? result : "(null)");
   
   return result;
}

boolean
debug_get_bool_option(const char *name, boolean dfault)
{
   const char *str = _debug_get_option(name);
   boolean result;
   
   if(str == NULL)
      result = dfault;
   else if(!util_strcmp(str, "n"))
      result = FALSE;
   else if(!util_strcmp(str, "no"))
      result = FALSE;
   else if(!util_strcmp(str, "0"))
      result = FALSE;
   else if(!util_strcmp(str, "f"))
      result = FALSE;
   else if(!util_strcmp(str, "false"))
      result = FALSE;
   else
      result = TRUE;

   debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? "TRUE" : "FALSE");
   
   return result;
}


long
debug_get_num_option(const char *name, long dfault)
{
   long result;
   const char *str;
   
   str = _debug_get_option(name);
   if(!str)
      result = dfault;
   else {
      long sign;
      char c;
      c = *str++;
      if(c == '-') {
	 sign = -1;
	 c = *str++;
      } 
      else {
	 sign = 1;
      }
      result = 0;
      while('0' <= c && c <= '9') {
	 result = result*10 + (c - '0');
	 c = *str++;
      }
      result *= sign;
   }
   
   debug_printf("%s: %s = %li\n", __FUNCTION__, name, result);

   return result;
}


unsigned long
debug_get_flags_option(const char *name, 
                       const struct debug_named_value *flags,
                       unsigned long dfault)
{
   unsigned long result;
   const char *str;
   
   str = _debug_get_option(name);
   if(!str)
      result = dfault;
   else if (!util_strcmp(str, "help")) {
      result = dfault;
      while (flags->name) {
         debug_printf("%s: help for %s: %s [0x%lx]\n", __FUNCTION__, name, flags->name, flags->value);
         flags++;
      }
   }
   else {
      result = 0;
      while( flags->name ) {
	 if (!util_strcmp(str, "all") || util_strstr(str, flags->name ))
	    result |= flags->value;
	 ++flags;
      }
   }

   if (str) {
      debug_printf("%s: %s = 0x%lx (%s)\n", __FUNCTION__, name, result, str);
   }
   else {
      debug_printf("%s: %s = 0x%lx\n", __FUNCTION__, name, result);
   }

   return result;
}


void _debug_assert_fail(const char *expr, 
                        const char *file, 
                        unsigned line, 
                        const char *function) 
{
   _debug_printf("%s:%u:%s: Assertion `%s' failed.\n", file, line, function, expr);
#if defined(PIPE_OS_WINDOWS) && !defined(PIPE_SUBSYSTEM_WINDOWS_USER)
   if (debug_get_bool_option("GALLIUM_ABORT_ON_ASSERT", FALSE))
#else
   if (debug_get_bool_option("GALLIUM_ABORT_ON_ASSERT", TRUE))
#endif
      debug_break();
   else
      _debug_printf("continuing...\n");
}


const char *
debug_dump_enum(const struct debug_named_value *names, 
                unsigned long value)
{
   static char rest[64];
   
   while(names->name) {
      if(names->value == value)
	 return names->name;
      ++names;
   }

   util_snprintf(rest, sizeof(rest), "0x%08lx", value);
   return rest;
}


const char *
debug_dump_enum_noprefix(const struct debug_named_value *names, 
                         const char *prefix,
                         unsigned long value)
{
   static char rest[64];
   
   while(names->name) {
      if(names->value == value) {
         const char *name = names->name;
         while (*name == *prefix) {
            name++;
            prefix++;
         }
         return name;
      }
      ++names;
   }

   

   util_snprintf(rest, sizeof(rest), "0x%08lx", value);
   return rest;
}


const char *
debug_dump_flags(const struct debug_named_value *names, 
                 unsigned long value)
{
   static char output[4096];
   static char rest[256];
   int first = 1;

   output[0] = '\0';

   while(names->name) {
      if((names->value & value) == names->value) {
	 if (!first)
	    util_strncat(output, "|", sizeof(output));
	 else
	    first = 0;
	 util_strncat(output, names->name, sizeof(output));
	 value &= ~names->value;
      }
      ++names;
   }
   
   if (value) {
      if (!first)
	 util_strncat(output, "|", sizeof(output));
      else
	 first = 0;
      
      util_snprintf(rest, sizeof(rest), "0x%08lx", value);
      util_strncat(output, rest, sizeof(output));
   }
   
   if(first)
      return "0";
   
   return output;
}


static const struct debug_named_value pipe_format_names[] = {
#ifdef DEBUG
   DEBUG_NAMED_VALUE(PIPE_FORMAT_NONE),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8R8G8B8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_X8R8G8B8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B8G8R8A8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B8G8R8X8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A1R5G5B5_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A4R4G4B4_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R5G6B5_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A2B10G10R10_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_L8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_I8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8L8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_L16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_YCBCR),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_YCBCR_REV),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_Z16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_Z32_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_Z32_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_S8Z24_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_Z24S8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_X8Z24_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_Z24X8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_S8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R64_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R64G64_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R64G64B64_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R64G64B64A64_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32A32_FLOAT),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32A32_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32A32_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32A32_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R32G32B32A32_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16A16_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16A16_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16A16_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R16G16B16A16_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8A8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8X8_UNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8A8_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8X8_USCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8A8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8X8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B6G5R5_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8B8G8R8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_X8B8G8R8_SNORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8A8_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8X8_SSCALED),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_L8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8L8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8A8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_R8G8B8X8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_A8R8G8B8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_X8R8G8B8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B8G8R8A8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B8G8R8X8_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_X8UB8UG8SR8S_NORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_B6UG5SR5S_NORM),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT1_RGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT1_RGBA),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT3_RGBA),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT5_RGBA),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT1_SRGB),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT1_SRGBA),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT3_SRGBA),
   DEBUG_NAMED_VALUE(PIPE_FORMAT_DXT5_SRGBA),
#endif
   DEBUG_NAMED_VALUE_END
};

#ifdef DEBUG
void debug_print_format(const char *msg, unsigned fmt )
{
   debug_printf("%s: %s\n", msg, debug_dump_enum(pipe_format_names, fmt)); 
}
#endif

const char *pf_name( enum pipe_format format )
{
   return debug_dump_enum(pipe_format_names, format);
}


#ifdef DEBUG
void debug_dump_image(const char *prefix,
                      unsigned format, unsigned cpp,
                      unsigned width, unsigned height,
                      unsigned stride,
                      const void *data)     
{
#ifdef PIPE_SUBSYSTEM_WINDOWS_DISPLAY
   static unsigned no = 0; 
   char filename[256];
   WCHAR wfilename[sizeof(filename)];
   ULONG_PTR iFile = 0;
   struct {
      unsigned format;
      unsigned cpp;
      unsigned width;
      unsigned height;
   } header;
   unsigned char *pMap = NULL;
   unsigned i;

   util_snprintf(filename, sizeof(filename), "\\??\\c:\\%03u%s.raw", ++no, prefix);
   for(i = 0; i < sizeof(filename); ++i)
      wfilename[i] = (WCHAR)filename[i];
   
   pMap = (unsigned char *)EngMapFile(wfilename, sizeof(header) + height*width*cpp, &iFile);
   if(!pMap)
      return;
   
   header.format = format;
   header.cpp = cpp;
   header.width = width;
   header.height = height;
   memcpy(pMap, &header, sizeof(header));
   pMap += sizeof(header);
   
   for(i = 0; i < height; ++i) {
      memcpy(pMap, (unsigned char *)data + stride*i, cpp*width);
      pMap += cpp*width;
   }
      
   EngUnmapFile(iFile);
#endif
}

void debug_dump_surface(const char *prefix,
                        struct pipe_surface *surface)     
{
   struct pipe_texture *texture;
   struct pipe_screen *screen;
   struct pipe_transfer *transfer;
   void *data;

   if (!surface)
      return;

   texture = surface->texture;
   screen = texture->screen;

   transfer = screen->get_tex_transfer(screen, texture, surface->face,
                                       surface->level, surface->zslice,
                                       PIPE_TRANSFER_READ, 0, 0, surface->width,
                                       surface->height);
   
   data = screen->transfer_map(screen, transfer);
   if(!data)
      goto error;
   
   debug_dump_image(prefix, 
                    transfer->format,
                    transfer->block.size, 
                    transfer->nblocksx,
                    transfer->nblocksy,
                    transfer->stride,
                    data);
   
   screen->transfer_unmap(screen, transfer);
error:
   screen->tex_transfer_destroy(transfer);
}


#pragma pack(push,2)
struct bmp_file_header {
   uint16_t bfType;
   uint32_t bfSize;
   uint16_t bfReserved1;
   uint16_t bfReserved2;
   uint32_t bfOffBits;
};
#pragma pack(pop)

struct bmp_info_header {
   uint32_t biSize;
   int32_t biWidth;
   int32_t biHeight;
   uint16_t biPlanes;
   uint16_t biBitCount;
   uint32_t biCompression;
   uint32_t biSizeImage;
   int32_t biXPelsPerMeter;
   int32_t biYPelsPerMeter;
   uint32_t biClrUsed;
   uint32_t biClrImportant;
};

struct bmp_rgb_quad {
   uint8_t rgbBlue;
   uint8_t rgbGreen;
   uint8_t rgbRed;
   uint8_t rgbAlpha;
};

void
debug_dump_surface_bmp(const char *filename,
                       struct pipe_surface *surface)
{
#ifndef PIPE_SUBSYSTEM_WINDOWS_MINIPORT
   struct pipe_transfer *transfer;
   struct pipe_texture *texture = surface->texture;
   struct pipe_screen *screen = texture->screen;

   transfer = screen->get_tex_transfer(screen, texture, surface->face,
                                       surface->level, surface->zslice,
                                       PIPE_TRANSFER_READ, 0, 0, surface->width,
                                       surface->height);

   debug_dump_transfer_bmp(filename, transfer);

   screen->tex_transfer_destroy(transfer);
#endif
}

void
debug_dump_transfer_bmp(const char *filename,
                        struct pipe_transfer *transfer)
{
#ifndef PIPE_SUBSYSTEM_WINDOWS_MINIPORT
   float *rgba;

   if (!transfer)
      goto error1;

   rgba = MALLOC(transfer->width*transfer->height*4*sizeof(float));
   if(!rgba)
      goto error1;

   pipe_get_tile_rgba(transfer, 0, 0,
                      transfer->width, transfer->height,
                      rgba);

   debug_dump_float_rgba_bmp(filename,
                             transfer->width, transfer->height,
                             rgba, transfer->width);

   FREE(rgba);
error1:
   ;
#endif
}

void
debug_dump_float_rgba_bmp(const char *filename,
                          unsigned width, unsigned height,
                          float *rgba, unsigned stride)
{
#ifndef PIPE_SUBSYSTEM_WINDOWS_MINIPORT
   struct util_stream *stream;
   struct bmp_file_header bmfh;
   struct bmp_info_header bmih;
   unsigned x, y;

   if(!rgba)
      goto error1;

   bmfh.bfType = 0x4d42;
   bmfh.bfSize = 14 + 40 + height*width*4;
   bmfh.bfReserved1 = 0;
   bmfh.bfReserved2 = 0;
   bmfh.bfOffBits = 14 + 40;

   bmih.biSize = 40;
   bmih.biWidth = width;
   bmih.biHeight = height;
   bmih.biPlanes = 1;
   bmih.biBitCount = 32;
   bmih.biCompression = 0;
   bmih.biSizeImage = height*width*4;
   bmih.biXPelsPerMeter = 0;
   bmih.biYPelsPerMeter = 0;
   bmih.biClrUsed = 0;
   bmih.biClrImportant = 0;

   stream = util_stream_create(filename, bmfh.bfSize);
   if(!stream)
      goto error1;

   util_stream_write(stream, &bmfh, 14);
   util_stream_write(stream, &bmih, 40);

   y = height;
   while(y--) {
      float *ptr = rgba + (stride * y * 4);
      for(x = 0; x < width; ++x)
      {
         struct bmp_rgb_quad pixel;
         pixel.rgbRed   = float_to_ubyte(ptr[x*4 + 0]);
         pixel.rgbGreen = float_to_ubyte(ptr[x*4 + 1]);
         pixel.rgbBlue  = float_to_ubyte(ptr[x*4 + 2]);
         pixel.rgbAlpha = 255;
         util_stream_write(stream, &pixel, 4);
      }
   }

   util_stream_close(stream);
error1:
   ;
#endif
}

#endif
