// DDSReader.cpp : Defines the entry point for the DLL application. // #include "DDSReader.h" #include "dds.h" #include "ddraw.h" //////////////////////////////////////////////////////////////////// #ifdef USE_DX9 #include "d3dx9.h" #define LPDIRECT3DDEVICE LPDIRECT3DDEVICE9 #define LPDIRECT3DTEXTURE LPDIRECT3DTEXTURE9 #define LPDIRECT3DSURFACE LPDIRECT3DSURFACE9 #define Direct3DCreate Direct3DCreate9 IDirect3D9* g_D3D = NULL; IDirect3DDevice9* g_Device = NULL; //////////////////////////////////////////////////////////////////// #else #include "d3dx8.h" #define LPDIRECT3DDEVICE LPDIRECT3DDEVICE8 #define LPDIRECT3DTEXTURE LPDIRECT3DTEXTURE8 #define LPDIRECT3DSURFACE LPDIRECT3DSURFACE8 #define Direct3DCreate Direct3DCreate8 IDirect3D8* g_D3D = NULL; IDirect3DDevice8* g_Device = NULL; #endif #define READER_COUNT 1 #define DDS_READER_ID 0 /********************************************************* Since we are using D3D8 for file io and format conversion we need a IDirect3D8 interface created and destroyed with this DLL **********************************************************/ #ifndef CK_LIB BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_D3D = Direct3DCreate(D3D_SDK_VERSION); if (!g_D3D) return FALSE; break; case DLL_PROCESS_DETACH: if(g_Device) g_Device->Release(); if (g_D3D) g_D3D->Release(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; } #endif /************************************************************************ PLUGIN DECLARATION **************************************************************************/ CKPluginInfo g_DDSReader_PInfo[READER_COUNT]={ CKPluginInfo( DDS_READER_GUID, // GUID "Dds", // Extension supported "Direct Draw Surface", // Reader Name "Virtools", // Company "Direct Draw Surface", // Summary String DDS_READER_VERSION, // Reader Version NULL, // No Init Instance function needed NULL, // No Exit Instance function needed CKPLUGIN_BITMAP_READER)}; // Plugin Type : Bitmap Reader /********************************************** Called by the engine when a file with the DDS extension is being loaded, a reader has to be created. ***********************************************/ #ifdef CK_LIB CKDataReader *CKGet_DDSReader_Static(int pos) #else CKDataReader *CKGetReader(int pos) #endif { switch(pos) { case DDS_READER_ID: return new DDSReader(); break; } return NULL; } /****************************************** Returns information about the reader which is the same as given to the engine at initialisation. *******************************************/ #ifndef CK_LIB CKPluginInfo* CKGetPluginInfo(int index) #else CKPluginInfo* CKGet_DDSReader_PluginInfo(int index) #endif { return &g_DDSReader_PInfo[index]; } /************************************************************************ PLUGIN IMPLEMENTATION **************************************************************************/ //----------------------------------------- // Construction... DDSReader::DDSReader() { m_Properties.m_Extension = "dds"; m_Properties.m_ReaderGuid = DDS_READER_GUID; m_Properties.m_Data = NULL; VxPixelFormat2ImageDesc(_32_ARGB8888,m_Properties.m_Format); } DDSReader::~DDSReader() { delete[] m_Properties.m_Data; m_Properties.m_Data = NULL; } CKPluginInfo* DDSReader::GetReaderInfo() { return &g_DDSReader_PInfo[DDS_READER_ID]; } //----------------------------------------- // Get Options Count (1 in our case which is the // pixel format when saving..) int DDSReader::GetOptionsCount() { return 1; } /*-------------------------------------------------- Get Options Description: Returns a string describing the options this reader has when writing to file... In this case we want to alias a DirectX enumeration (D3DFormat) typedef enum _D3DFORMAT { D3DFMT_R8G8B8 = 20, D3DFMT_A8R8G8B8 = 21, D3DFMT_R5G6B5 = 23, D3DFMT_A1R5G5B5 = 25, D3DFMT_A4R4G4B4 = 26, D3DFMT_R3G3B2 = 27, D3DFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), D3DFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), D3DFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), D3DFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), D3DFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), } D3DFORMAT; */ CKSTRING DDSReader::GetOptionDescription(int i) { static XString OptionsString; OptionsString.Format("Enum:Pixel Format:" "D3DFMT_R8G8B8=%d," "D3DFMT_A8R8G8B8=%d," "D3DFMT_R5G6B5=%d," "D3DFMT_A1R5G5B5=%d," "D3DFMT_A4R4G4B4=%d," "D3DFMT_R3G3B2=%d," "D3DFMT_DXT1=%d," "D3DFMT_DXT2=%d," "D3DFMT_DXT3=%d," "D3DFMT_DXT4=%d," "D3DFMT_DXT5=%d" ,D3DFMT_R8G8B8 ,D3DFMT_A8R8G8B8 ,D3DFMT_R5G6B5 ,D3DFMT_A1R5G5B5 ,D3DFMT_A4R4G4B4 ,D3DFMT_R3G3B2 ,D3DFMT_DXT1 ,D3DFMT_DXT2 ,D3DFMT_DXT3 ,D3DFMT_DXT4 ,D3DFMT_DXT5); switch(i) { case 0: return OptionsString.Str(); break; } return ""; } //------------------------------------- // Alpha Support : This function must return // whether the format stored in bp will handle // alpha information.. CKBOOL DDSReader::IsAlphaSaved(CKBitmapProperties* bp) { // TODO : optimize... DDSBitmapProperties dds_bp; if(bp->m_Size == dds_bp.m_Size) { // It's a Jpg Properties memcpy(&dds_bp,bp,bp->m_Size); } else { memcpy(&dds_bp,bp,sizeof(CKBitmapProperties)); } switch ( dds_bp.m_PixelFormat) { case D3DFMT_R8G8B8: case D3DFMT_R5G6B5: case D3DFMT_R3G3B2: return FALSE; } return TRUE; } BOOL CreateTemporaryDevice(LPDIRECT3DDEVICE* Device) { D3DDISPLAYMODE dispMode; D3DPRESENT_PARAMETERS presentParams; //----- Create a D3D Device for the time to load the file... g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dispMode); ZeroMemory(&presentParams, sizeof(presentParams)); presentParams.Windowed = TRUE; #ifdef USE_DX9 presentParams.SwapEffect = D3DSWAPEFFECT_DISCARD; #else presentParams.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC; #endif presentParams.BackBufferWidth = 8; presentParams.BackBufferHeight = 8; presentParams.BackBufferFormat = dispMode.Format; HRESULT res = g_D3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,GetDesktopWindow(), D3DCREATE_MULTITHREADED|D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_FPU_PRESERVE,&presentParams,Device); return SUCCEEDED(res); } #define CLEANUPIFFAILED(res,error)\ if (FAILED(res)) {\ if (Surface) Surface->Release();\ if (TextureSurface) TextureSurface->Release();\ if (Texture) Texture->Release();\ return error;\ }\ /************************************************************ Load a .dds file (from memory or from file) *************************************************************/ CKERROR DDS_Read(void* memptr,int size,CKBitmapProperties* bp) { LPDIRECT3DTEXTURE Texture = NULL; LPDIRECT3DSURFACE Surface = NULL; LPDIRECT3DSURFACE TextureSurface = NULL; HRESULT res; #ifdef CK_LIB if (!g_D3D) { g_D3D = Direct3DCreate(D3D_SDK_VERSION); } #endif if(!g_Device){ CreateTemporaryDevice(&g_Device); } //-------- Create a texture from this file if (!size) { // Loads from a file //res = D3DXCreateTextureFromFile(Device,(char *)memptr,&Texture); res = D3DXCreateTextureFromFileEx(g_Device,(char *)memptr,D3DX_DEFAULT,D3DX_DEFAULT,1,0, D3DFMT_UNKNOWN,D3DPOOL_SCRATCH ,D3DX_DEFAULT,D3DX_DEFAULT,0x00000000,NULL,NULL,&Texture); } else { // Loads from a file in a memory buffer //res = D3DXCreateTextureFromFileInMemory(Device,memptr,size,&Texture); res = D3DXCreateTextureFromFileInMemoryEx(g_Device,(char *)memptr,size,D3DX_DEFAULT,D3DX_DEFAULT,1,0, D3DFMT_UNKNOWN,D3DPOOL_SCRATCH ,D3DX_DEFAULT,D3DX_DEFAULT,0x00000000,NULL,NULL,&Texture); } CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); D3DSURFACE_DESC TextureDesc; Texture->GetLevelDesc(0,&TextureDesc); VX_PIXELFORMAT vpf = VxImageDesc2PixelFormat(((CKBitmapProperties*)bp)->m_Format); D3DFORMAT dpf = DDS_VxPixelFormatToD3DFormat(vpf); vpf = DDS_D3DFormatToVxPixelFormat(dpf); if(TextureDesc.Format != dpf){ //--- Create a surface to perform conversion from the loaded image.. VxPixelFormat2ImageDesc(vpf,((CKBitmapProperties*)bp)->m_Format); #ifdef USE_DX9 res = g_Device->CreateOffscreenPlainSurface(TextureDesc.Width, TextureDesc.Height, dpf, D3DPOOL_SCRATCH, &Surface, NULL); #else res = g_Device->CreateImageSurface(TextureDesc.Width,TextureDesc.Height,dpf,&Surface); #endif CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); res= Texture->GetSurfaceLevel(0,&TextureSurface); CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); res= D3DXLoadSurfaceFromSurface(Surface,NULL,NULL,TextureSurface,NULL,NULL,D3DX_FILTER_NONE,0); TextureSurface->Release(); TextureSurface = NULL; CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); }else{ Texture->GetSurfaceLevel(0,&Surface); } //--------- Copy bits from DX surface to our surface... D3DLOCKED_RECT src; res= Surface->LockRect(&src,NULL,D3DLOCK_READONLY); CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); //-------- Create the Image in virtools format VxPixelFormat2ImageDesc(vpf,bp->m_Format); bp->m_Format.Width = TextureDesc.Width; bp->m_Format.Height = TextureDesc.Height; bp->m_Format.BytesPerLine = TextureDesc.Width*(bp->m_Format.BitsPerPixel/8); BYTE* memory = NULL; if(bp->m_Format.BytesPerLine){ memory = new BYTE[ bp->m_Format.BytesPerLine * bp->m_Format.Height ]; }else{ memory = new BYTE[ src.Pitch/4 * bp->m_Format.Height ]; } BYTE* Src = (BYTE *)src.pBits; BYTE* Dst = memory; bp->m_Data = memory; if(bp->m_Format.BytesPerLine){ for (unsigned int i = 0; i< TextureDesc.Height; ++i,Src+=src.Pitch,Dst+=bp->m_Format.BytesPerLine) { memcpy(Dst,Src,bp->m_Format.BytesPerLine); } }else{ memcpy(Dst,Src,src.Pitch/4 * bp->m_Format.Height); } Surface->UnlockRect(); Surface->Release(); Texture->Release(); #ifdef CK_LIB if(g_Device) { g_Device->Release(); g_Device=NULL; } if (g_D3D) { g_D3D->Release(); g_D3D=NULL; } #endif return 0; } ///------------------------------------------------------------ // Saving Functions int DDS_Save(void** memptr,DDSBitmapProperties* bp) { LPDIRECT3DTEXTURE Texture = NULL; LPDIRECT3DSURFACE Surface = NULL; LPDIRECT3DSURFACE TextureSurface = NULL; HRESULT res; if(!g_Device){ CreateTemporaryDevice(&g_Device); } //--- Source format can only be a 32 bits image // if (bp->m_Format.BitsPerPixel != 32) return 0; D3DFORMAT fmt = DDS_VxPixelFormatToD3DFormat(VxImageDesc2PixelFormat(bp->m_Format)); //---- Create Source image surface #ifdef USE_DX9 res = g_Device->CreateOffscreenPlainSurface(bp->m_Format.Width, bp->m_Format.Height, fmt, D3DPOOL_SCRATCH, &Surface, NULL); #else res = g_Device->CreateImageSurface(bp->m_Format.Width,bp->m_Format.Height,fmt,&Surface); #endif CLEANUPIFFAILED(res,0); //--------- Copy bits from our surface to DX 32 bit surface D3DLOCKED_RECT SrcLock; res= Surface->LockRect(&SrcLock,NULL,0 ); CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); BYTE* Src = (BYTE *)bp->m_Data; BYTE* Dst = (BYTE*)SrcLock.pBits; if(bp->m_Format.BytesPerLine){ for (int i = 0; i< bp->m_Format.Height; ++i,Src+=SrcLock.Pitch,Dst+=bp->m_Format.BytesPerLine) { memcpy(Dst,Src,bp->m_Format.BytesPerLine); } }else{ memcpy(Dst,Src,SrcLock.Pitch/4 * bp->m_Format.Height); } Surface->UnlockRect(); if(bp->m_PixelFormat != fmt){ //---- Create destnation image surface #ifdef USE_DX9 res = g_Device->CreateOffscreenPlainSurface(bp->m_Format.Width, bp->m_Format.Height, bp->m_PixelFormat, D3DPOOL_SCRATCH, &TextureSurface, NULL); #else res = g_Device->CreateImageSurface(bp->m_Format.Width,bp->m_Format.Height,bp->m_PixelFormat,&TextureSurface); #endif CLEANUPIFFAILED(res,0); //---- Convert formats res= D3DXLoadSurfaceFromSurface(TextureSurface,NULL,NULL,Surface,NULL,NULL,D3DX_FILTER_NONE,0); CLEANUPIFFAILED(res,CKBITMAPERROR_READERROR); }else{ TextureSurface = Surface; Surface->AddRef(); } //---- And Save D3DSURFACE_DESC Desc; TextureSurface->GetDesc(&Desc); //--------- Copy bits from DX surface to a DDS file buffer... D3DLOCKED_RECT Dest; res = TextureSurface->LockRect(&Dest,NULL,D3DLOCK_READONLY); //-- Prepare a big buffer to store the file #ifdef USE_DX9 int DataSize = Dest.Pitch * Desc.Height; if ( bp->m_PixelFormat == D3DFMT_DXT1 ) DataSize = max(1, Desc.Width/4) * max(1,Desc.Height/4) * 8; else if ( (bp->m_PixelFormat == D3DFMT_DXT2) || (bp->m_PixelFormat == D3DFMT_DXT3) || (bp->m_PixelFormat == D3DFMT_DXT4) || (bp->m_PixelFormat == D3DFMT_DXT5) ) DataSize = max(1, Desc.Width/4) * max(1, Desc.Height/4) * 16; #else int DataSize = Desc.Size; #endif DWORD FileSize = 4 + sizeof(DDS_HEADER) + DataSize; BYTE* FileData = new BYTE[FileSize]; //memset(&FileData[4 + sizeof(DDS_HEADER)],0xFF,Desc.Size); memcpy(&FileData[4 + sizeof(DDS_HEADER)],Dest.pBits,DataSize); TextureSurface->UnlockRect(); //---- Release all Dx Objects... Surface->Release(); TextureSurface->Release(); //------ Now write Header DWORD MagicDword = MAKEFOURCC('D','D','S',' '); DDS_HEADER Header; memset(&Header,0,sizeof(Header)); BOOL Compressed = ((bp->m_PixelFormat == D3DFMT_DXT1) || (bp->m_PixelFormat == D3DFMT_DXT2) || (bp->m_PixelFormat == D3DFMT_DXT3) || (bp->m_PixelFormat == D3DFMT_DXT4) || (bp->m_PixelFormat == D3DFMT_DXT5)); Header.dwSize = 124; Header.dwHeaderFlags = DDSD_CAPS|DDSD_PIXELFORMAT|DDSD_WIDTH|DDSD_HEIGHT; if (Compressed) { Header.dwHeaderFlags |= DDS_HEADER_FLAGS_LINEARSIZE; } else { Header.dwHeaderFlags |= DDS_HEADER_FLAGS_PITCH; } Header.dwHeight = bp->m_Format.Height; Header.dwWidth = bp->m_Format.Width; Header.dwPitchOrLinearSize = Compressed ? DataSize : Dest.Pitch; Header.dwSurfaceFlags = DDSCAPS_TEXTURE; //--- Pixel Format Header.ddspf.dwSize = 32; if (Compressed) { Header.ddspf.dwFlags = DDPF_FOURCC; Header.ddspf.dwFourCC = bp->m_PixelFormat; } else { switch ( bp->m_PixelFormat) { case D3DFMT_R8G8B8: case D3DFMT_R5G6B5: case D3DFMT_R3G3B2: Header.ddspf.dwFlags = DDS_RGB; break; default: Header.ddspf.dwFlags = DDS_RGBA; break; } VxImageDescEx TempFormat; VxPixelFormat2ImageDesc(DDS_D3DFormatToVxPixelFormat(bp->m_PixelFormat),TempFormat); Header.ddspf.dwRGBBitCount = TempFormat.BitsPerPixel; Header.ddspf.dwRBitMask = TempFormat.RedMask; Header.ddspf.dwGBitMask = TempFormat.GreenMask; Header.ddspf.dwBBitMask = TempFormat.BlueMask; Header.ddspf.dwABitMask = TempFormat.AlphaMask; } //----- And Write to buffer memcpy(FileData,&MagicDword,sizeof(DWORD)); memcpy(&FileData[4],&Header,sizeof(Header)); //---- File ? or memory if (*memptr == NULL) { // Memory *memptr = FileData; } else{ // File XString filename = (char*)*memptr; filename.CheckFileNameValidity(); FILE* f = fopen(filename.Str(),"wb"); if (!f) { delete[] FileData; return 0; } fwrite(FileData,FileSize,1,f); fclose(f); } return FileSize; } ///----------------------------------------------- // Loading Functions // Synchronous Reading from file or URL int DDSReader::ReadFile(char* name,CKBitmapProperties** bp) { if (!name || !bp) return CKBITMAPERROR_GENERIC; CKERROR ret=DDS_Read(name,0,&m_Properties); *bp=&m_Properties; return ret; } // Synchronous Reading from memory int DDSReader::ReadMemory(void* memory,int size,CKBitmapProperties** bp) { if (!bp) return CKBITMAPERROR_GENERIC; CKERROR ret=DDS_Read(memory,size,&m_Properties); *bp=&m_Properties; return ret; } ///----------------------------------------------- // Saving Functions // Synchronous Reading from file or URL int DDSReader::SaveFile(char* name,CKBitmapProperties* bp) { if (!name || !bp) return 0; DDSBitmapProperties jbp; if(bp->m_Size == jbp.m_Size) { // It's a Bmp Properties memcpy(&jbp,bp,bp->m_Size); } else { memcpy(&jbp,bp,sizeof(CKBitmapProperties)); } return DDS_Save((void**)&name,&jbp); } // Synchronous Reading from memory, return number of bytes written int DDSReader::SaveMemory(void** memory,CKBitmapProperties* bp) { if (!memory || !bp) return 0; DDSBitmapProperties jbp; if(bp->m_Size == jbp.m_Size) { // It's a DDS Properties memcpy(&jbp,bp,bp->m_Size); } else { memcpy(&jbp,bp,sizeof(CKBitmapProperties)); } *memory=NULL; return DDS_Save(memory,&jbp); } ///----------------------------------------------- // Image Properties void DDSReader::SetBitmapDefaultProperties(CKBitmapProperties* bm) { if(bm->m_Size == m_Properties.m_Size) { // It's a valid DDS Properties we can copy our properties memcpy(&m_Properties,bm,bm->m_Size); } else { memcpy(&m_Properties,bm,sizeof(CKBitmapProperties)); } } void DDSReader::GetBitmapDefaultProperties(CKBitmapProperties** bm) { // We return the properties *bm = &m_Properties; } //-------------------------------------------- // Cleaning void DDSReader::ReleaseMemory(void* memory) { delete [] memory; } void DDSReader::Release() { delete this; }