/*=========================================================================== * * File: Dftextimg.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Thursday, June 28, 2001 * * Implements the CDFTextureImage class for handling one image from a * Daggerfall texture file. * *=========================================================================*/ /* Include Files */ #include "dftextimg.H" /*=========================================================================== * * Begin Local Variable Definitions * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variable Definitions *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Constructor * *=========================================================================*/ CDFTextureImage::CDFTextureImage() { m_HeaderOffset = 0; m_ImageSize = 0; m_NumSubImages = 0; m_Tag = 0; /* Initialize the image header */ memset(&m_Header, 0, sizeof(dftextimgheader_t)); /* Initialize the sub-image data array */ m_pSubImages = NULL; } /*=========================================================================== * End of Class CTextureImage Constructor *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Destructor * *=========================================================================*/ void CDFTextureImage::Destroy (void) { DEFINE_FUNCTION("CDFTextureImage::Destroy"); int Index; /* Delete the sub-image data arrays */ for (Index = 0; Index < m_NumSubImages; Index++) { DestroyArrayPointer(m_pSubImages[Index].pData); } /* Delete the subimage array */ DestroyArrayPointer(m_pSubImages); memset(&m_Header, 0, sizeof(dftextimgheader_t)); m_NumSubImages = 0; m_HeaderOffset = 0; m_ImageSize = 0; m_Tag = 0; /* DO NOT call the base class Destroy method! */ } /*=========================================================================== * End of Class CDFTextureImage Destructor *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - void AllocateSubImages (void); * * Allocates the array of sub-image data pointers. * *=========================================================================*/ void CDFTextureImage::AllocateSubImages (void) { DEFINE_FUNCTION("CDFTextureImage::AllocateSubImages()"); /* Ensure valid object state */ ASSERT(m_pSubImages == NULL && GetNumSubImages() >= 0); /* Check for no sub-images */ if (GetNumSubImages() == 0) return; /* Allocate the array */ CreateArrayPointer(m_pSubImages, dftextsubimg_t, (size_t)GetNumSubImages()); memset(m_pSubImages, 0, sizeof(dftextsubimg_t) * GetNumSubImages()); m_NumSubImages = GetNumSubImages(); } /*=========================================================================== * End of Class Method CDFTextureImage::AllocateSubImages() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - byte* AllocateSubImageData (Index, Size); * * Protected class method to allocate the data for the given sub-image. * Returns a pointer to the newly allocated data. Returns NULL only if * the image size is zero. * *=========================================================================*/ byte* CDFTextureImage::AllocateSubImageData (const int Index, const size_t Size) { DEFINE_FUNCTION("CDFTextureImage::AllocateSubImageData()"); /* Ensure valid input and object state */ ASSERT(IsValidImageIndex(Index)); ASSERT(m_pSubImages != NULL); ASSERT(m_pSubImages[Index].pData == NULL); /* Check for a 0-sized image */ if (Size == 0) return (NULL); /* Allocate and return the image data */ CreateArrayPointer(m_pSubImages[Index].pData, byte, Size); return (m_pSubImages[Index].pData); } /*=========================================================================== * End of Class Method CDFTextureImage::AllocateSubImageData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean Read (void); * * Attempt to read in the image header and data starting from the current * position in the file stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDFTextureImage::Read (void) { DEFINE_FUNCTION("CDFTextureImage::Read()"); boolean Result; /* Delete any current contents */ Destroy(); /* Set the offset of the image header */ m_HeaderOffset = Tell(); /* Attempt to input header information */ Result = ReadHeader(); if (!Result) return (FALSE); /* Check for no images to read */ if (GetNumSubImages() == 0) { Result = TRUE; } /* Read multiple, compressed, sub-images */ else if (GetNumSubImages() > 1 && IsCompressed()) { Result = ReadRLESubImageData(); } /* Read in uncompressed multiple sub-images */ else if (GetNumSubImages() > 1) { Result = ReadSubImageInfo(); if (Result) Result = ReadSubImageData(); } /* Read in single subimage data, RLE encoded */ else if (IsCompressed()) Result = ReadRLEData(); /* Read in single, uncompressed, image */ else Result = ReadData(); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::Read() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadHeader (void); * * Attempt to read in the image header from the current position in * the file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadHeader (void) { DEFINE_FUNCTION("CDFTextureImage::ReadHeader()"); boolean Result; /* Assume we are at the header position currently and read in the * header data all at once.*/ Result = CGenFile::Read ((char*)&m_Header, DFTEXTURE_IMAGEHEADER_SIZE); if (!Result) return (FALSE); /* Ensure a valid number of sub-images */ if (GetNumSubImages() >= DFTEXTURE_MAX_SUBIMAGES) { ErrorHandler.AddError(DFERR_TEXTURE_SUBIMAGES, "SubImages=%d", (int)GetNumSubImages()); return (FALSE); } /* Ensure a valid image height */ if (GetHeight() >= DFTEXTURE_MAXIMAGE_HEIGHT) { ErrorHandler.AddError(DFERR_TEXTURE_BADHEIGHT, "Height=%d", (int)GetHeight()); return (FALSE); } /* Ensure a valid true, image size */ m_ImageSize = (long)GetWidth() * (long)GetHeight(); if (m_ImageSize < 0 || (ulong)m_ImageSize >= (ulong) UINT_MAX) { ErrorHandler.AddError(DFERR_TEXTURE_IMAGESIZE, "ImageSize=%ld", m_ImageSize); return (FALSE); } /* Allocate the sub-image array */ AllocateSubImages(); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::ReadHeader() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadData (void); * * Attempt to read in the image data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadData (void) { DEFINE_FUNCTION("CDFTextureImage::ReadData()"); boolean Result; int RowIndex; byte* pDataPtr; /* Move to the start of the image data */ Result = Seek(GetAbsDataOffset(), SEEK_SET); if (!Result) return (FALSE); /* Allocate the image data */ pDataPtr = AllocateSubImageData(0, (size_t)GetTrueImageSize()); /* Must read data row by row */ for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) { /* Read in one row of image data */ Result = CGenFile::Read((char*)pDataPtr, (size_t)GetWidth()); if (!Result) return (FALSE); /* Move to the start of the next row as all texture images * are stored in 256 byte wide rows */ Result = Seek(DFTEXTURE_IMAGEROW_WIDTH - GetWidth(), SEEK_CUR); pDataPtr += GetWidth(); } return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::ReadData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadRLEData (void); * * Attempt to read in the RLE image data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadRLEData (void) { DEFINE_FUNCTION("CDFTextureImage::ReadRLEData()"); boolean Result; int RowIndex; byte* pDataPtr; dftextrleinfo_t RowOffsets[DFTEXTURE_MAXIMAGE_HEIGHT+1]; /* Move to the start of the image data */ Result = Seek(GetAbsDataOffset(), SEEK_SET); if (!Result) return (Result); /* Allocate the image data */ pDataPtr = AllocateSubImageData(0, (size_t)GetTrueImageSize()); /* Read in the row offset data all at once */ Result = CGenFile::ReadEx((char*)&RowOffsets[0], sizeof(dftextrleinfo_t), GetHeight()); RowOffsets[GetHeight()].Offset = (short) GetImageSize(); if (!Result) return (FALSE); /* Must read the data row by row */ for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) { Result = UncompressRLERow(RowOffsets[RowIndex], &pDataPtr); if (!Result) return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::ReadRLEData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadRLESubImageData (void); * * Attempt to read in the RLE sub-image data from the current position in * the file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadRLESubImageData (void) { DEFINE_FUNCTION("CDFTextureImage::ReadRLESubImageData()"); boolean Result; int SubImageIndex; int RowIndex; size_t NumRowOffsets; byte* pDataPtr; dftextrleinfo_t* pRowOffsets; /* Ensure valid object state */ ASSERT(m_pSubImages != NULL && GetNumSubImages() > 1); /* Allocate the temporary offset structure */ NumRowOffsets = (size_t) GetHeight() * GetNumSubImages(); CreateArrayPointer(pRowOffsets, dftextrleinfo_t, NumRowOffsets+1); /* Read in the row offset data all at once */ Result = ReadEx((char *)pRowOffsets, sizeof(dftextrleinfo_t), NumRowOffsets); if (!Result) return (FALSE); /* Read in each subimage */ for (SubImageIndex = 0; SubImageIndex < GetNumSubImages() && Result; SubImageIndex++) { /* Set the subimage info and allocate data */ m_pSubImages[SubImageIndex].Width = GetWidth(); m_pSubImages[SubImageIndex].Height = GetHeight(); pDataPtr = AllocateSubImageData(SubImageIndex, (size_t)GetTrueImageSize()); /* Must read data row by row */ for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) { Result = UncompressRLERow(pRowOffsets[RowIndex], &pDataPtr); if (!Result) break; } } /* Unallocate the temporary offset array */ DestroyArrayPointer(pRowOffsets); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::ReadRLESubImageData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadSubImageData (void); * * Attempt to read in the subimage data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadSubImageData (void) { DEFINE_FUNCTION("CDFTextureImage::ReadSubImageData()"); boolean Result; int SubImageIndex; int RowIndex; long ImageSize; byte* pDataPtr; /* Read in each sub-image */ for (SubImageIndex = 0; SubImageIndex < GetNumSubImages(); SubImageIndex++) { /* Move to the start of the sub-image data and * read the subimage width and height */ Result = Seek(m_pSubImages[SubImageIndex].Offset + GetAbsDataOffset(), SEEK_SET); if (Result) Result = ReadShort(m_pSubImages[SubImageIndex].Width); if (Result) Result = ReadShort(m_pSubImages[SubImageIndex].Height); /* Compute the image size and allocate image data */ ImageSize = (long)m_pSubImages[SubImageIndex].Width * (long)m_pSubImages[SubImageIndex].Height; m_pSubImages[SubImageIndex].ImageSize = ImageSize; if (ImageSize < 0 || (ulong)ImageSize >= (ulong) UINT_MAX) { ErrorHandler.AddError(DFERR_TEXTURE_IMAGESIZE, "ImageSize=%lu", ImageSize); return (FALSE); } pDataPtr = AllocateSubImageData(SubImageIndex, (size_t)ImageSize); /* Must read sub-image data row by row */ for (RowIndex = 0; RowIndex < m_pSubImages[SubImageIndex].Height; RowIndex++) { Result = UncompressSubImageRow(SubImageIndex, &pDataPtr); if (!Result) return (FALSE); } } return (TRUE); } /*========================================================================== * End of Class Method CDFTextureImage::ReadSubImageData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean ReadSubImageInfo (void); * * Attempt to read in the sub-image offsets from the current position * in the file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::ReadSubImageInfo (void) { DEFINE_FUNCTION("CDFTextureImage::ReadSubImageInfo()"); boolean Result; long Offsets[DFTEXTURE_MAX_SUBIMAGES]; int SubImageIndex; /* Ensure valid object state */ ASSERT(GetNumSubImages() > 1 && m_pSubImages != NULL && GetNumSubImages() < DFTEXTURE_MAX_SUBIMAGES); /* Jump to the position of the sub-image offsets and read offsets */ Result = Seek(GetAbsDataOffset(), SEEK_SET); if (Result) Result = ReadEx((char *) &Offsets[0], sizeof(long), (size_t)GetNumSubImages()); if (!Result) return (FALSE); /* Save offsets to sub-image data */ for (SubImageIndex = 0; SubImageIndex < GetNumSubImages(); SubImageIndex++) { m_pSubImages[SubImageIndex].Offset = Offsets[SubImageIndex]; } return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::ReadSubImageInfo() *=========================================================================*/ #if defined(WRITE) /*=========================================================================== * * Class CDFTextureImage Method - boolean Write (void); * * Attempt to write the image header and data starting from the current * position in the file stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDFTextureImage::Write (void) { DEFINE_FUNCTION("CDFTextureImage::Write()"); boolean Result; /* Set the offset of the image header */ m_HeaderOffset = Tell(); /* Attempt to output header information */ Result = WriteHeader(); if (!Result) return (FALSE); /* Check for no images to write */ if (GetNumSubImages() == 0) { Result = TRUE; } /* Write multiple, compressed, sub-images */ else if (GetNumSubImages() > 1 && IsCompressed()) { Result = WriteRLESubImageData(); } /* Write uncompressed multiple sub-images */ else if (GetNumSubImages() > 1) { Result = WriteSubImageInfo(); if (Result) Result = WriteSubImageData(); } /* Write single subimage data, RLE encoded */ else if (IsCompressed()) Result = WriteRLEData(); /* Write single, uncompressed, image */ else Result = WriteData(); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::Write() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean WriteHeader (void); * * Attempt to write the image header to the current position in * the file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::WriteHeader (void) { DEFINE_FUNCTION("CDFTextureImage::WriteHeader()"); boolean Result; /* Assume we are at the header position currently and read in the * header data all at once.*/ Result = CGenFile::Write ((char*)&m_Header, DFTEXTURE_IMAGEHEADER_SIZE); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::WriteHeader() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean WriteData (void); * * Attempt to write a single image data to the file stream. Returns * FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::WriteData (void) { DEFINE_FUNCTION("CDFTextureImage::WriteData()"); boolean Result; int RowIndex; byte* pDataPtr; /* Move to the start of the image data */ m_Header.DataOffset = sizeof(dftextimgheader_t); Result = Seek(GetAbsDataOffset(), SEEK_SET); if (!Result) return (FALSE); pDataPtr = GetImageData(0); ASSERT(pDataPtr != NULL); /* Must write data row by row */ for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) { /* WRite one row of image data */ Result = CGenFile::Write((char*)pDataPtr, (size_t)GetWidth()); if (!Result) return (FALSE); /* Move to the start of the next row as all texture images * are stored in 256 byte wide rows */ Result = Seek(DFTEXTURE_IMAGEROW_WIDTH - GetWidth(), SEEK_CUR); pDataPtr += GetWidth(); } return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::WriteData() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - boolean WriteRLEData (void); * * Attempt to write a single RLE image data to the file stream. Returns * FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFTextureImage::WriteRLEData (void) { DEFINE_FUNCTION("CDFTextureImage::WriteRLEData()"); boolean Result; long RowOffsetPos; int RowIndex; byte* pDataPtr; dftextrleinfo_t RowOffsets[DFTEXTURE_MAXIMAGE_HEIGHT+1]; /* Move to the start of the image data */ m_Header.DataOffset = sizeof(dftextimgheader_t); Result = Seek(GetAbsDataOffset(), SEEK_SET); if (!Result) return (Result); RowOffsetPos = Tell(); pDataPtr = GetImageData(0); RowOffsets[0] = sizeof(dftextrleinfo_t) * GetHeight() + sizeof(dftextimgheader_t); /* Skip the row offset data for now */ Result = Seek(sizeof(dftextrleinfo_t) * GetHeight(), SEEK_CUR); if (!Result) return (FALSE); /* Must write the data row by row */ for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) { Result = CompressRLERow(RowOffsets[RowIndex+1], &pDataPtr); if (!Result) return (FALSE); } /* Write out the row offset data all at once */ Result = Seek(RowOffsetPos, SEEK_SET); if (Result) Result = CGenFile::WriteEx((char*)&RowOffsets[0], sizeof(dftextrleinfo_t), GetHeight()); return (Result); } /*=========================================================================== * End of Class Method CDFTextureImage::WriteRLEData() *=========================================================================*/ #endif /*=========================================================================== * * Class CDFTextureImage Method - * boolean UnCompressRLERow (RowInfo, ppDataPtr); * * Protected class method to uncompress one row of RLE image data from the * current position in the file stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDFTextureImage::UncompressRLERow (const dftextrleinfo_t& RowInfo, byte** ppDataPtr) { DEFINE_FUNCTION("CDFTextureImage::UncompressRLERow()"); boolean Result; int BytesWritten; int BytesDecoded; short RLECounter; short ByteCount; short RowWidth; byte RLEData; /* Ensure valid input */ ASSERT(ppDataPtr != NULL && *ppDataPtr != NULL); /* Jump to the start of the row data */ Result = Seek(RowInfo.Offset + m_HeaderOffset, SEEK_SET); if (!Result) return (Result); /* Check if we can input uncompressed data */ if (RowInfo.Compressed == 0x0000) { /* Read in the row data all at once and ensure success */ Result = CGenFile::Read((char*)*ppDataPtr, GetWidth()); if (!Result) return (FALSE); (*ppDataPtr) += GetWidth(); return (TRUE); } /* Input the row width value */ Result = ReadShort(RowWidth); if (!Result) return (FALSE); /* Initialize the RLE variables */ BytesWritten = 0; BytesDecoded = 0; ClearError(); /* Uncompress the row data */ while (BytesWritten < RowWidth) { fread(&ByteCount, 1, sizeof(short), GetHandle()); /* Write out pixel values explicitly */ if (ByteCount > 0) { for (RLECounter = 0; RLECounter < ByteCount; RLECounter++) { **ppDataPtr = fgetc(GetHandle()); (*ppDataPtr)++; } BytesDecoded += ByteCount + 2; BytesWritten += ByteCount; } else { /* Decode RLE bytes */ RLEData = fgetc(GetHandle()); ByteCount = -ByteCount; for (RLECounter = 0; RLECounter < ByteCount; RLECounter++) { **ppDataPtr = (byte)RLEData; (*ppDataPtr)++; } BytesDecoded += 3; BytesWritten += ByteCount; } /* Check for error conditions */ if (IsError() || IsEOF()) { ErrorHandler.AddError(ERR_SYSTEM, errno, "Failed to read compressed image row from file!"); return (FALSE); } } /* End of while decoding */ return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::UnCompressRLERow() *=========================================================================*/ /*=========================================================================== * * Class CDFTextureImage Method - * boolean UnCompressSubImageRow (SubImageIndex, ppDataPtr); * * Protected class method to uncompress one row of regular subimage data * from the current position in the file stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDFTextureImage::UncompressSubImageRow (const int SubImageIndex, byte** ppDataPtr) { DEFINE_FUNCTION("CDFTextureImage::UncompressSubImageRow()"); int RowSkipBytes; int RowBytes; int RowWidth; int BytesWritten = 0; /* Ensure valid input */ ASSERT(ppDataPtr != NULL && *ppDataPtr != NULL); RowWidth = m_pSubImages[SubImageIndex].Width; ClearError(); /* Input all bytes from row */ while (BytesWritten < RowWidth) { /* Read the two compressed rowinfo bytes */ RowSkipBytes = fgetc(GetHandle()); RowBytes = fgetc(GetHandle()); /* Fill the first section of the row data */ memset(*ppDataPtr, 0x00, RowSkipBytes); (*ppDataPtr) += RowSkipBytes; BytesWritten += RowSkipBytes; /* Move to next byte sequence if no bytes to read */ if (!RowBytes) continue; /* Read in the row data */ fread (*ppDataPtr, 1, RowBytes, GetHandle()); (*ppDataPtr) += RowBytes; BytesWritten += RowBytes; /* Check for error conditions */ if (IsError() || IsEOF()) { ErrorHandler.AddError(ERR_SYSTEM, errno, "Failed to read sub-image row from file!"); return (FALSE); } } /* End of while loop */ return (TRUE); } /*=========================================================================== * End of Class Method CDFTextureImage::UnCompressSubImageRow() *=========================================================================*/