/***********************************************************************************
           Copyright 1998, NEC Electronics Inc. All rights reserved.

Information and source code samples contained herein are provided "as-is",
without representations or warranties, and are subject to change without notice.
NEC Electronics Inc. cannot support modifications or derivative works created from
the sample source code provided. Developers may use, reproduce, and modify portions
or entire sections of the sample source code for the purposes of creating Dreamcast
applications.  Developers may only commercially distribute the executable or binary
portions of the sample source code.  Sample source code contained herein may not be
appropriate for use under all circumstances.  Please consult Developer documentation
provided along with your Dreamcast unit for additional information and suggestions.
************************************************************************************/


/***********************************************************************************
** File:     ScrnGrab.c
** Purpose:  Dump a framebuffer to a .BMP file on disk
** Author:   Kevin Kralian
** Created:  December 14, 1998
**
** Version:  1.0
**
** Description:
**    This module will copy the current front display buffer to a .BMP format file.
**    It uses the module "ScrnInfo.c" to access the framebuffer, and uses the LIBC
**    library to write to the disk. All dumps are 24 bit RGB. This function works
**    with all screen formats and resolutions.
**
**    The Libc library requires Codescape to be running in order to facilitate the
**    Set5 -> PC file I/O. Also, file writing can be quite slow- a 640x480 screen
**    dump can take around 30 seconds. Writes are currently queued up to write one
**    entire scan line at a time; you may wish to buffer things up further (at the
**    cost of stack-ram usage) to increase writing speed.
**
**    Files are written out to the "katana\utl\dev\codescape\" directory by default
**
***********************************************************************************/

#include <kamui.h>
#include <sn_fcntl.h>   /* LibCross file types. */
#include <usrsnasm.h>   /* LibCross I/O routines. */
#include "ScrnInfo.h"   /* for framebuffer access */


/*=========================================================================*/
/* OPTIONS */

/* minimum buffer to dump 1 row of pixels- max 640 across X 24 bits (always) */
#define MAX_BYTES_PER_LINE		640*3

/* To speed up slow file I/O, we can queue up several lines per write */
/* This # needs to be a perfect divisor of the screen heights (240 & 480) */
/* The minimum is 1: a 640x480 screen grab takes ~ 1.5 minutes */
/* Suggested is 10: same screen grab takes less than 10 seconds */
/* Warning- Don't make it too big- the memory is allocated on the stack! */
#define NUM_LINES_TO_BUFFER 	10

/*=========================================================================*/
/* .BMP FILE HEADERS */

#define BI_RGB		0L		/* compression type */
char	bfType[2];

typedef struct tagBITMAPFILEHEADER
{
	int		bfSize;
	short	bfReserved1;
	short	bfReserved2;
	int		bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{
   int		biSize;
   int		biWidth;
   int		biHeight;
   short	biPlanes;
   short	biBitCount;
   int		biCompression;
   int		biSizeImage;
   int 		biXPelsPerMeter;
   int		biYPelsPerMeter;
   int		biClrUsed;
   int		biClrImportant;
} BITMAPINFOHEADER;

/*=========================================================================*/

void SaveFrontBufferAsBMP( char *filename )
{
	BITMAPFILEHEADER bmFileHeader;
	BITMAPINFOHEADER bmInfoHeader;
	int nFileID;

	int width, height, x,y, nScreenMode, nScreenStride, nLineCount=0;
	unsigned short *pShort;
	unsigned long *pLong;

	/* output buffer & ptr */
	unsigned char byte_buffer[MAX_BYTES_PER_LINE*NUM_LINES_TO_BUFFER];
	unsigned char *pDstBytes;


	waitForEndOfRender();

	pLong = getFrontBufferAddr();
	pShort = getFrontBufferAddr();
	width = getScreenWidth();
	height = getScreenHeight();
	nScreenMode = getScreenColorMode();
	nScreenStride = getScreenStride();

    nFileID = debug_open ( filename, SNASM_O_WRONLY | SNASM_O_CREAT | SNASM_O_BINARY);
	if (nFileID != -1)
	{
		/**** CREATE AND WRITE .BMP HEADER (24 BIT RGB) ****/
		bfType[0] = 'B';
		bfType[1] = 'M';

		bmFileHeader.bfSize = sizeof(bfType) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + width*height*3; /* total file size */
		bmFileHeader.bfReserved1 = 0;
		bmFileHeader.bfReserved2 = 0;
		bmFileHeader.bfOffBits = sizeof(bfType) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

		bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmInfoHeader.biWidth = width;
		bmInfoHeader.biHeight = height;
		bmInfoHeader.biPlanes = 1;
		bmInfoHeader.biBitCount = 24;
		bmInfoHeader.biCompression = BI_RGB;
		bmInfoHeader.biSizeImage = 0;
		bmInfoHeader.biXPelsPerMeter = 0;
		bmInfoHeader.biYPelsPerMeter = 0;
		bmInfoHeader.biClrUsed = 0;
		bmInfoHeader.biClrImportant = 0;

        debug_write( nFileID, bfType, sizeof(bfType) );
        debug_write( nFileID, (char *) &bmFileHeader, sizeof(bmFileHeader) );
        debug_write( nFileID, (char *) &bmInfoHeader, sizeof(bmInfoHeader) );

		/**** WRITE OUT BITMAP DATA ****/
		pDstBytes = byte_buffer;

		for (y=height-1; y >= 0; y--)
		{
			for (x=0; x < width; x++)
			{
				switch (nScreenMode)
				{
				case KM_DSPBPP_ARGB8888:
					*pDstBytes++ = pLong[x + nScreenStride*y/4];		/* blue */
					*pDstBytes++ = pLong[x + nScreenStride*y/4] >> 8;	/* green */
					*pDstBytes++ = pLong[x + nScreenStride*y/4] >> 16;	/* red */
					break;

				case KM_DSPBPP_RGB888:
					if ( x & 0x01 )
					{
						/* odd pixels */
						*pDstBytes++ = pShort[(x-1)*3/2+1 + nScreenStride*y/2] >> 8;	/* blue */
						*pDstBytes++ = pShort[(x-1)*3/2+2 + nScreenStride*y/2];			/* green */
						*pDstBytes++ = pShort[(x-1)*3/2+2 + nScreenStride*y/2] >> 8;	/* red */
					}
					else
					{
						/* even pixels */
						*pDstBytes++ = pShort[x*3/2+0 + nScreenStride*y/2];			/* blue */
						*pDstBytes++ = pShort[x*3/2+0 + nScreenStride*y/2] >> 8;	/* green */
						*pDstBytes++ = pShort[x*3/2+1 + nScreenStride*y/2];			/* red */
					}
					break;

				case KM_DSPBPP_RGB565:
					*pDstBytes++ = pShort[x + nScreenStride*y/2] << 3;				/* blue */
					*pDstBytes++ = pShort[x + nScreenStride*y/2] >> 3 & 0xfc;		/* green */
					*pDstBytes++ = pShort[x + nScreenStride*y/2] >> 8 & 0xf8;		/* red */
					break;

				case KM_DSPBPP_RGB555:
				case KM_DSPBPP_ARGB1555:
					*pDstBytes++ = pShort[x + nScreenStride*y/2] << 3;				/* blue */
					*pDstBytes++ = pShort[x + nScreenStride*y/2] >> 2 & 0xf8;		/* green */
					*pDstBytes++ = pShort[x + nScreenStride*y/2] >> 7 & 0xf8;		/* red */
					break;
				}
			}

			/* write fewer, larger blocks to speed up file I/O */
			if ( ++nLineCount == NUM_LINES_TO_BUFFER )
			{
                debug_write( nFileID, (char *) byte_buffer, width * 3 * NUM_LINES_TO_BUFFER );
                nLineCount = 0;
				pDstBytes = byte_buffer;
			}
		}
        debug_close( nFileID );
	}
}

/*=========================================================================*/