//////////////////////////////////////////////////////////////////////////////
//
// Asura_TIM2.cpp
//
// By: Richard M Smith - 28.08.2001.
//
// Copyright (c) 2001 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"
#include "Rendering/Asura_TIM2.h"

// Standard independent.

// Standard platform-specific.
#include <eekernel.h>
#include <libgifpk.h>
#include <libgraph.h>
#include <sifdev.h>

// Engine independent.
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_Logfile.h"

// Engine platform-specific.
#include "Core/Asura_PlatformBasics.h"
#include "Core/Asura_PS2_DMA.h"
#include "Rendering/Asura_PS2_GS.h"

// M A C R O S ///////////////////////////////////////////////////////////////

#define	DMA_FROM_MEM	(1<<0)
#define	DMA_CHAIN_MODE	(1<<2)
#define	DMA_ASP_OFF		(0<<4)
#define	DMA_TTE_ON		(1<<6)
#define	DMA_TIE_OFF		(0<<7)
#define	DMA_START		(1<<8)

// P R O T O T Y P E S   A N D   I N L I N E   F U N C T I O N S /////////////

static bool CanGSCalculateMipmapAddrs( u_int uPSM, u_int uW, u_int uH );

int Tim2CalcBufWidth(int psm, int w);

// F U N C T I O N S /////////////////////////////////////////////////////////

bool Tim2CheckFileHeader( const void* pvFileHdr )
{
	const TIM2_FILEHEADER* pxFileHdr = reinterpret_cast<const TIM2_FILEHEADER*>( pvFileHdr );

	if( pxFileHdr->FileId[0] == 'T' && pxFileHdr->FileId[1] == 'I' && pxFileHdr->FileId[2] == 'M' && pxFileHdr->FileId[3] == '2' )
	{
	    if( 3 == pxFileHdr->FormatVersion || (4 == pxFileHdr->FormatVersion && (0 == pxFileHdr->FormatId || 1 == pxFileHdr->FormatId)) )
	    {
            return true;
        }
    }

    Asura_Assert( false, "Bad TIM2 header." );
    return false;
}

/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetPictureHeader
// PARAMETERS	Pointer to the suspected TIM2 data
//				Number of the picture header to reference
// RETURNS		Pointer to picture header
// DESCRIPTION	Get picture header corresponding to specified number
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
TIM2_PICTUREHEADER* Tim2GetPictureHeader( void* pTim2, int imgno )
{
	TIM2_FILEHEADER*	pFileHdr = (TIM2_FILEHEADER*)pTim2;
	TIM2_PICTUREHEADER*	pPictHdr;
	int					i;

// Check picture number
	if( imgno >= pFileHdr->Pictures )
	{
		Asura_OutputToDebugger_Retail(("Tim2GetPictureHeader: Illegal image no.(%d)\n", imgno));
		return( NULL );
	}

	if( pFileHdr->FormatId == 0x00 )
	{
	// When format ID is 0x00, 16-byte alignment
		pPictHdr = (TIM2_PICTUREHEADER*)((char*)pTim2 + sizeof( TIM2_FILEHEADER ) );
	}
	else
	{
	// When format ID is 0x01, 128-byte alignment
		pPictHdr = (TIM2_PICTUREHEADER*)((char*)pTim2 + 0x80 );
	}

// Skip up to specified picture number
	for( i = 0; i < imgno; i++ )
	{
		pPictHdr = (TIM2_PICTUREHEADER*)((char*)pPictHdr + pPictHdr->TotalSize );
	}

	return( pPictHdr );

} // End Function 'Tim2GetPictureHeader'


/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetMipMapHeader
// PARAMETERS	Starting address of TIM2 picture header
//				Pointer to int-type variable for receiving MIPMAP header size (NULL if not needed)
// RETURNS		NULL (if no MIPMAP header) or Starting address of MIPMAP header
// DESCRIPTION	Get MIPMAP header address and size
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
TIM2_MIPMAPHEADER* Tim2GetMipMapHeader( TIM2_PICTUREHEADER* ph, int* pSize )
{
	static const char mmsize[] = {
		0,		// Texture number 0 (for CLUT2 data)
		0,		// LV0 texture only (no MIPMAP header)
		32,		// Up to LV1 MipMap
		32,		// Up to LV2 MipMap
		32,		// Up to LV3 MipMap
		48,		// Up to LV4 MipMap
		48,		// Up to LV5 MipMap
		48		// Up to LV6 MipMap
	};
	TIM2_MIPMAPHEADER*	pMmHdr;

	if( ph->MipMapTextures > 1 )
	{
		pMmHdr = (TIM2_MIPMAPHEADER*)( (char*)ph + sizeof( TIM2_PICTUREHEADER ) );
	}
	else
	{
		pMmHdr = NULL;
	}

	if( pSize )
	{
	// When there was no extended header
		*pSize = mmsize[ ph->MipMapTextures ];
	}

	return( pMmHdr );

} // End Function 'Tim2GetMipMapHeader'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetUserSpace
// PARAMETERS	Starting address of TIM2 picture header
//				Pointer to int-type variable for receiving user space size (NULL if not needed)
// RETURNS		NULL (if no user space) or Starting address of user space
// DESCRIPTION	Get user space address and size
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
const void* Tim2GetUserSpace( const TIM2_PICTUREHEADER* ph, u_int* puSize )
{
	const void* pvUserSpace;
	static const char	mmsize[] = {
		sizeof(TIM2_PICTUREHEADER),			// Texture number 0 (for CLUT2 data)
		sizeof(TIM2_PICTUREHEADER),			// LV0 texture only (no MIPMAP header)
		sizeof(TIM2_PICTUREHEADER) + 32,	// Up to LV1 MipMap
		sizeof(TIM2_PICTUREHEADER) + 32,	// Up to LV2 MipMap
		sizeof(TIM2_PICTUREHEADER) + 32,	// Up to LV3 MipMap
		sizeof(TIM2_PICTUREHEADER) + 48,	// Up to LV4 MipMap
		sizeof(TIM2_PICTUREHEADER) + 48,	// Up to LV5 MipMap
		sizeof(TIM2_PICTUREHEADER) + 48		// Up to LV6 MipMap
	};

// Check header size
	if( ph->HeaderSize == mmsize[ ph->MipMapTextures ] )
	{
	// When no user space exists
		if( puSize )
		{
			*puSize = 0;
		}
		return(NULL);
	}

// When user space exists
	pvUserSpace = reinterpret_cast<const void*>( reinterpret_cast<const char*>(ph) + mmsize[ph->MipMapTextures] );
	
	if( puSize )
	{
	// Calculate user space size
	// When there is an extended header, get title size from it
		const TIM2_EXHEADER* pxExHdr = reinterpret_cast<const TIM2_EXHEADER*>( pvUserSpace );
		
		if( pxExHdr->ExHeaderId[ 0 ] != 'e' ||
			pxExHdr->ExHeaderId[ 1 ] != 'X' ||
			pxExHdr->ExHeaderId[ 2 ] != 't' ||
			pxExHdr->ExHeaderId[ 3 ] != 0x00 )
		{
		// When there was no extended header
			*puSize = ( ph->HeaderSize - mmsize[ ph->MipMapTextures ] );
		}
		else
		{
		// When there was an extended header
			*puSize = pxExHdr->UserSpaceSize;
		}
	}

	return( pvUserSpace );

} // End Function 'Tim2GetUserSpace'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetMipMapHeader
// PARAMETERS	Starting address of TIM2 picture header
//				Pointer to int-type variable for receiving user data size (NULL if not needed)
// RETURNS		NULL (if no user space) or Starting address of user data
// DESCRIPTION	Get user data address and size
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
const void* Tim2GetUserData( const TIM2_PICTUREHEADER* ph, u_int* puSize )
{
	const void* pvUserSpace;
	const TIM2_EXHEADER* pxExHdr;

	pvUserSpace = Tim2GetUserSpace( ph, puSize );
	
	if( pvUserSpace == NULL )
	{
	// When no user space existed
		return( NULL );
	}

// Check whether or not there is an extended header in the user space
	pxExHdr = reinterpret_cast<const TIM2_EXHEADER*>( pvUserSpace );
	
	if( pxExHdr->ExHeaderId[ 0 ] != 'e' ||
		pxExHdr->ExHeaderId[ 1 ] != 'X' ||
		pxExHdr->ExHeaderId[ 2 ] != 't' ||
		pxExHdr->ExHeaderId[ 3 ] != 0x00 )
	{
	// When no extended header was found
		return( pvUserSpace );
	}

// When an extended header was found
	if( puSize )
	{
	// Return size of user data part
		*puSize = pxExHdr->UserDataSize;
	}

	return( reinterpret_cast<const char*>(pvUserSpace) + sizeof(TIM2_EXHEADER) );

} // End Function 'Tim2GetUserData'





///////////////////////////////////////////////////////////////////////////////////////////
//// FUNCTION		Tim2GetMipMapHeader
//// PARAMETERS	Starting address of TIM2 picture header
//// RETURNS		NULL (if no character string) or Starting address of character string (ASCIZ)
//// DESCRIPTION	Get starting address of comment character string that was stored in user space
//// AUTHOR		Unknown
///////////////////////////////////////////////////////////////////////////////////////////
//const char* Tim2GetComment( const TIM2_PICTUREHEADER* ph )
//{
//	const void* pvUserSpace;
//	const TIM2_EXHEADER* pxExHdr;
//
//	pvUserSpace = Tim2GetUserSpace( ph, NULL );
//	
//	if( !pvUserSpace )
//	{
//    	// No user space => no comment.
//		return NULL;
//	}
//
//// Check whether or not there is an extended header in the user space
//	pExHdr = (TIM2_EXHEADER*)pUserSpace;
//
//	if( pExHdr->ExHeaderId[ 0 ] != 'e' ||
//		pExHdr->ExHeaderId[ 1 ] != 'X' ||
//		pExHdr->ExHeaderId[ 2 ] != 't' ||
//		pExHdr->ExHeaderId[ 3 ] != 0x00 )
//	{
//	// When no extended header was found
//		return( NULL );
//	}
//
//// When an extended header had existed
//	if( pExHdr->UserSpaceSize == ( ( sizeof( TIM2_EXHEADER ) + pExHdr->UserDataSize ) ) )
//	{
//	// When intended size of user space was equal to total size of extended header and user data
//	// You can decide that no comment character string exists
//		return( NULL );
//	}
//
//// When no comment was found
//	return( (char*)pUserSpace + sizeof( TIM2_EXHEADER ) + pExHdr->UserDataSize );
//
//} // End Function 'Tim2GetUserData'
//




/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetMipMapHeader
// PARAMETERS	Starting address of TIM2 picture header
//				MIPMAP texture level
// RETURNS		NULL (if no relevant image data) or starting address of image data
// DESCRIPTION	Get starting address of image data for specified MIPMAAP level
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
unsigned char* Tim2GetImage( TIM2_PICTUREHEADER* ph, int mipmap )
{
	unsigned char*		pImage;
	TIM2_MIPMAPHEADER*	pm;
	int					i;

	if( mipmap >= ph->MipMapTextures )
	{
	// No MIPMAP texture corresponding to specified level exists
		return( NULL );
	}

// Calculate starting address of image data
	pImage = ( (unsigned char*)ph + ph->HeaderSize );
	
	if( ph->MipMapTextures == 1 )
	{
	// For only LV0 texture
		return( pImage );
	}

// When MIPMAP texture exists
	pm = (TIM2_MIPMAPHEADER*)( (unsigned char*)ph + sizeof( TIM2_PICTUREHEADER ) );
	
	for( i = 0; i < mipmap; i++ )
	{
		pImage = ( (unsigned char*)pImage + pm->MMImageSize[ i ] );
	}

	return( pImage );

} // End Function 'Tim2GetUserData'





/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2GetMipMapHeader
// PARAMETERS	Starting address of TIM2 picture header
// RETURNS		NULL (if no relevant CLUT data) or starting address of CLUT data
// DESCRIPTION	Get starting address of CLUT data
// AUTHOR		Unknown
/////////////////////////////////////////////////////////////////////////////////////////
unsigned char* Tim2GetClut( TIM2_PICTUREHEADER* ph )
{
	unsigned char*	pClut;

	if( ph->ClutColors == 0 )
	{
	// When number of colors constituting CLUT data part is 0
		pClut = NULL;
	}
	else
	{
	// When CLUT data exists
		pClut = ( (unsigned char*)ph + ph->HeaderSize + ph->ImageSize );
	}

	return(pClut);

} // End Function 'Tim2GetClut'

void Tim2SetupTim2Header( TIM2_PICTUREHEADER* pxPicHdr, bool bIsSwizzled )
{
    const u_int uCLUTType = pxPicHdr->ClutType;
    const bool  bHasCLUT  = ( TIM2_NONE != uCLUTType );

    u_int uWidth  = pxPicHdr->ImageWidth;
    u_int uHeight = pxPicHdr->ImageHeight;

    // Determine real dimensions of texture, accounting for swizzling if
    // necessary.
    u_int uActualWidth  = uWidth;
    u_int uActualHeight = uHeight;
    if( bIsSwizzled )
    {
        // The change in dimensions depends on the original format. See §8.6
        // of the GS User's Manual (v5 or v6.)
        uActualWidth  <<= ( 256 == pxPicHdr->ClutColors ? 1 : 2 );
        uActualHeight <<= 1;
    }

    // Pixel storage format.
    u_int uActualPSM;
    u_int uUploadPSM;
    if( bIsSwizzled )
    {
        // Texture is swizzled. We assume it was originally either in
        // PSMT4 or PSMT8. To determine which, we look at the number of
        // colours in the CLUT; 4-bit palettised textures will have a 
        // 16 colour CLUT while 8-bit textures will have 256.
        Asura_Assert( bHasCLUT, "Swizzled textures must have a CLUT." );
        uActualPSM = ( 256 == pxPicHdr->ClutColors ? SCE_GS_PSMT8 : SCE_GS_PSMT4 );
        uUploadPSM = SCE_GS_PSMCT32;
    }
    else
    {
        static const u_int auImageTypeToPSMTable[] =
        {
            SCE_GS_PSMCT16,
            SCE_GS_PSMCT24,
            SCE_GS_PSMCT32,
            SCE_GS_PSMT4,
            SCE_GS_PSMT8,
        };

        uActualPSM = uUploadPSM = auImageTypeToPSMTable[pxPicHdr->ImageType - 1];
    }

    // Determine CLUT PSM.
    u_int uCLUTPSM = SCE_GS_PSMCT32;
    if( bHasCLUT )
    {
        u_int uFormat = uCLUTType & ASURA_PS2_TIM2_CLUT_FORMAT_MASK;
        if( TIM2_RGB16 == uFormat )
        {
            uCLUTPSM = SCE_GS_PSMCT16;
        }
        else if( TIM2_RGB24 == uFormat )
        {
            uCLUTPSM = SCE_GS_PSMCT24;
        }
    }

    // TCC. Fix at 1 unless we're dealing with a 24-bit image. (For the
    // 16-bit case, we want to refer to the TEXA register so the 1-bit
    // alpha component is correctly interpreted.)
    u_int uTCC;
    if( SCE_GS_PSMCT24 == uActualPSM || (bHasCLUT && SCE_GS_PSMCT24 == uCLUTPSM) )
    {
        uTCC = 0;
    }
    else
    {
        uTCC = 1;
    }

    // Write out TEX0. Note that we force TBP and CBP to 0, so we can go
    // ahead and overwrite them later without bothering to mask first.
    pxPicHdr->uTex0 = SCE_GS_SET_TEX0( 0,                                          // TBP
                                       Tim2CalcBufWidth(uActualPSM, uActualWidth), // TBW
                                       uActualPSM,                                 // PSM
                                       Asura_Maths::GetCeilLog2(uActualWidth),     // TW
                                       Asura_Maths::GetCeilLog2(uActualHeight),    // TH
                                       uTCC,                                       // TCC
                                       SCE_GS_MODULATE,                            // TFX
                                       0,                                          // CBP
                                       uCLUTPSM,                                   // CPSM
                                       0,                                          // CSM
                                       0,                                          // CSA
                                       1 );                                        // CLD

    // And TEX1. Rather simpler case here, mainly because most of it needs to
    // be setup on the fly.
    pxPicHdr->uTex1 = SCE_GS_SET_TEX1( 0,                                                      // LCM
                                       pxPicHdr->MipMapTextures - 1,                           // MXL
                                       0,                                                      // MMAG
                                       0,                                                      // MMIN
                                       CanGSCalculateMipmapAddrs(uUploadPSM, uWidth, uHeight), // MTBA
                                       0,                                                      // L
                                       0 );                                                    // K
}

#ifndef ASURA_PS2_SONY_SUBMISSION
/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Tim2TakeSnapshot
// PARAMETERS	Pointer to the DISP env containing the image
//				Filename of TIM2 to create
// RETURNS		0 on error; Non zero for success
// DESCRIPTION	Write snapshot in TIM2 file
// AUTHOR		Unknown
// MODIFIED		Richard M. Smith (2 October 2001)
//					It was DMA timing out!
/////////////////////////////////////////////////////////////////////////////////////////
int Tim2TakeSnapshot( const char* pszFname, u_int tbp, u_int tbw, u_int psm, short x, short y, short w, short h )
{
	int	i;
	int	fh;				// File handle
	//int	nWidth;
	//int	nHeight;
	int	nImageType;		// Image part type
	//int	psm;			// Pixel format
	int	nBytes;			// Number of bytes constituting one raster line

// Ensure all paths are idle
	sceGsSyncPath( 0, 0 );

// Get image size and pixel format
	//psm		= d0->dispfb.PSM;

// Obtain number of bytes per raster line and TIM2 pixel type from pixel format
	switch( psm )
	{
	case SCE_GS_PSMCT32:
		nBytes = w * 4;
		nImageType = TIM2_RGB32;
		break;
	case SCE_GS_PSMCT24:
		nBytes = w * 3;
		nImageType = TIM2_RGB24;
		break;
	case SCE_GS_PSMCT16:
		nBytes = w * 2;
		nImageType = TIM2_RGB16;
		break;
	case SCE_GS_PSMCT16S:
		nBytes = w * 2;
		nImageType = TIM2_RGB16;
		break;
	default:
	// When pixel format is unknown, terminate with error
	// GS_PSGPU24 format is not supported
		Asura_OutputToDebugger_Retail(("Illegal pixel format.\n"));
		return(0);
	}

// Open TIM2 file
	fh = sceOpen( pszFname, SCE_WRONLY | SCE_CREAT );
	
	if( fh < 0 )
	{
	// Processing for opening file failed
		Asura_OutputToDebugger_Retail(( "Couldn't create output file. Check output dir exists.\n" ));
		return(0);
	}

// Write file header
	TIM2_FILEHEADER fhdr;
	fhdr.FileId[0] = 'T';
	fhdr.FileId[1] = 'I';
	fhdr.FileId[2] = 'M';
	fhdr.FileId[3] = '2';
	fhdr.FormatVersion = 3;
	fhdr.FormatId  = 0;					// 16-byte alignment mode
	fhdr.Pictures  = 1;					// Number of pictures is 1

	for( i = 0; i < 8; i++ )
	{
		fhdr.pad[i] = 0x00;				// Clear padding member with 0x00
	}

	sceWrite( fh, &fhdr, sizeof( TIM2_FILEHEADER ) );	// Write file header

// Write picture header
	TIM2_PICTUREHEADER phdr;
	int nImageSize;

	nImageSize = nBytes * h;
	phdr.TotalSize   = sizeof(TIM2_PICTUREHEADER) + nImageSize;	// Total size
	phdr.ClutSize    = 0;							// No CLUT part
	phdr.ImageSize   = nImageSize;					// Image part size
	phdr.HeaderSize  = sizeof(TIM2_PICTUREHEADER);	// Header part size
	phdr.ClutColors  = 0;							// Number of CLUT colors
	phdr.PictFormat  = 0;							// Picture format
	phdr.MipMapTextures = 1;						// Number of MIPMAP textures
	phdr.ClutType    = TIM2_NONE;					// No CLUT part
	phdr.ImageType   = nImageType;					// Image type
	phdr.ImageWidth  = w;						// Image width
	phdr.ImageHeight = h;						// Image height

// Set all GS register settings to 0
	(*reinterpret_cast<u_long*>(&phdr.xTex0)) = 0;
	phdr.xTex0.TBW = Tim2CalcBufWidth(psm, w);
	phdr.xTex0.PSM = psm;
	phdr.xTex0.TW  = Asura_Maths::GetCeilLog2( w );
	phdr.xTex0.TH  = Asura_Maths::GetCeilLog2( h );
	(*reinterpret_cast<u_long*>(&phdr.xTex1)) = 0;
	phdr.GsTexaFbaPabe = 0;
	phdr.GsTexClut     = 0;
	sceWrite( fh, &phdr, sizeof( TIM2_PICTUREHEADER ) );	// Write picture header

/* Store the DMA channel 1 settings since these are changed by sceGsExecStoreImage(). */
	u_int	store_D1_CHCR = *D1_CHCR;

// Write image data
	for( i = 0; i < h; i++ )
	{
		u_char buf[4096];			// Reserve 4KB raster buffer
		
		sceGsStoreImage si;

//		sceGsSetDefStoreImage( &si, d0->dispfb.FBP * 32, d0->dispfb.FBW, psm,
//									d0->dispfb.DBX, (d0->dispfb.DBY + i),
//									nWidth, 1);
        sceGsSetDefStoreImage( &si, tbp, tbw, psm, x, y + i, w, 1);
		FlushCache( WRITEBACK_DCACHE );				// Flush D cache

		sceGsExecStoreImage( &si, (u_long128*)buf );	// Start transfer to VRAM
	
		FlushCache( WRITEBACK_DCACHE );				// Flush D cache

		sceWrite( fh, buf, nBytes );						// Write data for one raster line
	}

// File writing completed
	sceClose( fh );						// Close file

/* Restore the DMA channel 1 settings. */
	*D1_CHCR = store_D1_CHCR;

	return(1);
}

int Tim2TakeSnapshot( const sceGsDispEnv* d0, const char* pszFname )
{
    u_int tbp = d0->dispfb.FBP * 32;
    u_int tbw = d0->dispfb.FBW;
    u_int psm = d0->dispfb.PSM;
    short x = d0->dispfb.DBX;
    short y = d0->dispfb.DBY;
    short w = (d0->display.DW + 1) / ( d0->display.MAGH + 1 );
    short h = d0->display.DH + 1;
    return Tim2TakeSnapshot( pszFname, tbp, tbw, psm, x, y, w, h);
}
#endif//#ifndef ASURA_PS2_SONY_SUBMISSION

// Computes TBW for TEX0, etc.
int Tim2CalcBufWidth( int iPSM, int iWidth )
{
    Asura_Assert( 0 <= iWidth && iWidth <= 1024, "Invalid texture width." );

    u_int uTBW = (iWidth + 63) >> 6;

    // Palettised textures' TBWs must be a multiple of 2.
    if( (SCE_GS_PSMT8 == iPSM || SCE_GS_PSMT4 == iPSM) && (uTBW & 1) ) { ++uTBW; }

    return uTBW;
}

static bool CanGSCalculateMipmapAddrs( u_int uPSM, u_int uW, u_int uH )
{
    // See §3.4.11 of the GS User's Manual for further details on the
    // requirements below.

    if( uW == uH )
    {
        switch( uW * uH )
        {
            case  32 *  32:
            case  64 *  64:
            case 128 * 128:
            case 256 * 256:
            case 512 * 512:
                return true;

            case 1024 * 1024:
                switch( uPSM )
                {
                    case SCE_GS_PSMCT16:
                    case SCE_GS_PSMT8:
                    case SCE_GS_PSMT4:
                        return true;
                }
                break;
        }
    }

    return false;
}
