/*=========================================================================== * * File: Dfrmb.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Friday, June 29, 2001 * * Implements the CDFRmbRecord and CDFRmbFile classes for handling * Daggerfall RMB files from the BLOCKS.BSA file. * *=========================================================================*/ /* Include Files */ #include "uesp/dagger/bsa/dfrmb.h" #include "uesp/dagger/bsa/dfarchdt.h" /*=========================================================================== * * Begin Local Variable Definitions * *=========================================================================*/ DEFINE_FILE(); /* Local array of RMB filenames */ char l_DFRMBFilenames[45][5] = { "TVRN", "GENR", "RESI", "WEAP", "ARMR", "ALCH", "BANK", "BOOK", "CLOT", "FURN", "GEMS", "LIBR", "PAWN", "TEMP", "TEMP", "PALA", "FARM", "DUNG", "CAST", "MANR", "SHRI", "RUIN", "SHCK", "GRVE", "FILL", "KRAV", "KDRA", "KOWL", "KMOO", "KCAN", "KFLA", "KHOR", "KROS", "KWHE", "KSCA", "KHAW", "MAGE", "THIE", "DARK", "FIGH", "CUST", "WALL", "MARK", "SHIP", "WITC" }; /*=========================================================================== * End of Local Variable Definitions *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Constructor * *=========================================================================*/ CDFRmbRecord::CDFRmbRecord() { /* Initialize array pointers */ m_p3DObjectRecords = NULL; m_pFlatObjectRecords = NULL; m_pSection3Records = NULL; m_pPeopleRecords = NULL; m_pDoorRecords = NULL; m_ExtraByte = 0; m_HasExtraByte = FALSE; /* Initialize header data */ memset(&m_Header, 0, sizeof(dfrmb_header_t)); } /*=========================================================================== * End of Class CDFRmbRecord Constructor *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Destructor * *=========================================================================*/ void CDFRmbRecord::Destroy (void) { DEFINE_FUNCTION("CDFRmbRecord::Destroy()"); /* Unallocate memory from object arrays */ DestroyArrayPointer(m_p3DObjectRecords); DestroyArrayPointer(m_pFlatObjectRecords); DestroyArrayPointer(m_pSection3Records); DestroyArrayPointer(m_pPeopleRecords); DestroyArrayPointer(m_pDoorRecords); /* Clear header data */ memset(&m_Header, 0, sizeof(dfrmb_header_t)); m_HasExtraByte = FALSE; } /*=========================================================================== * End of Class CDFRmbRecord Destructor *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean Export3DS (File3DS, Flags, pTransPoint); * * Export the RMB file to the given 3DS file. The optional flags indicate * which objects to include in the export. Assumes that the 3DS file is * already open for output. The point object, if not NULL, gives the * optional position translation for the RMB record. * *=========================================================================*/ boolean CDFRmbRecord::Export3DS (C3dsFile& File3DS, const int Flags, const df3dpoint_t* pTransPoint) { DEFINE_FUNCTION("CDFRmbRecord::Export3DS()"); boolean Result = TRUE; /* Export the relevant sections */ if (Flags & DFRMB_FLAG_3DOBJECTS) { if (Flags & DFRMB_FLAG_OUTSIDE) Result &= Export3DS_3DObjects(File3DS, "RmbO3d", pTransPoint); else Result &= Export3DS_3DObjects(File3DS, "RmbI3d", pTransPoint); } return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::Export3DS() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - * boolean Export3DS_3DObjects (File3DS, pNameHeader, pTransPoint); * * Protected class method to export the 3D objects of the RMB to the given * 3DS file. Returns FALSE on any error. The optional point object gives * the position translation to apply to all objects in the record. The name * header string is used to name the 3DS objects. * *=========================================================================*/ boolean CDFRmbRecord::Export3DS_3DObjects (C3dsFile& File3DS, const char* pNameHeader, const df3dpoint_t* pTransPoint) { DEFINE_FUNCTION("CDFRmbRecord::Export3DS_3DObjects()"); boolean Result; float MeshMatrix[12]; CDF3dObject* pObject; long ObjectValue; float ObjectAngle; float XPos; float YPos; float ZPos; int Index; /* Ensure valid input */ ASSERT(pNameHeader != NULL); /* Ignore if no 3D objects to export */ if (m_p3DObjectRecords == NULL || m_Header.Num3DObjectRecords == 0) return (TRUE); /* Export each 3D object in array */ for (Index = 0; Index < m_Header.Num3DObjectRecords; Index++) { ObjectValue = m_p3DObjectRecords[Index].Get3DObjectValue(); /* Get the specified 3D object */ Result = GetDF3dObject(&pObject, ObjectValue); if (!Result) return (FALSE); /* Get object rotation/position translation */ ObjectAngle = m_p3DObjectRecords[Index].Get3DObjectAngle(); XPos = (float) m_p3DObjectRecords[Index].Get3DObjectX(); ZPos = (float) m_p3DObjectRecords[Index].Get3DObjectZ(); /* Compute the proper Y-Position for the object */ switch (m_p3DObjectRecords[Index].Unknown1) { case 3: YPos = -m_p3DObjectRecords[Index].Get3DObjectY(); YPos -= (float) (pObject->GetMaxY() - pObject->GetMinY())/DF_COOR_BLOCKTO3D/2; break; case 4: YPos = m_p3DObjectRecords[Index].Get3DObjectY(); break; case 13: YPos = m_p3DObjectRecords[Index].Get3DObjectY(); break; default: YPos = (float) m_p3DObjectRecords[Index].Get3DObjectY(); SystemLog.Printf ("\t\t%3d) Unknown1 = %d (%ld, %ld, %6.0f)", Index+1, m_p3DObjectRecords[Index].Unknown1, m_p3DObjectRecords[Index].YPos2, m_p3DObjectRecords[Index].YPos1, YPos); break; } //SystemLog.Printf ("\t\t%3d) Unknown1 = %d (%ld, %ld, %6.0f)", Index+1, m_p3DObjectRecords[Index].Unknown1, m_p3DObjectRecords[Index].YPos2, m_p3DObjectRecords[Index].YPos1, YPos); /* Apply the optional group translation */ if (pTransPoint != NULL) { XPos += (float) pTransPoint->X; YPos += (float) pTransPoint->Y; ZPos += (float) pTransPoint->Z; } /* Apply the 3DS coordinate scaling factor */ XPos *= (float)DF_COOR_BLOCKTO3D/(float)DF3D_3DS_FACTOR; YPos *= (float)DF_COOR_BLOCKTO3D/(float)DF3D_3DS_FACTOR; ZPos *= (float)DF_COOR_BLOCKTO3D/(float)DF3D_3DS_FACTOR; C3dsFile::CreateMeshMatrix(&MeshMatrix[0], XPos, ZPos, -YPos, /* Position */ 1.0, 1.0, 1.0, /* Scale */ 0.0, 0.0, ObjectAngle); /* Rotation, radians */ /* Export object */ //Result = pObject->Export3DSObject(File3DS, &MeshMatrix[0]); Result = ExportDF3dObjectTo3DS(File3DS, *pObject, &MeshMatrix[0], NULL); if (!Result) return (FALSE); LOOPEND:; } return (TRUE); } /*=========================================================================== * End of Class Method CDFRmbRecord::Export3DS_3DObjects() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadRecord (void); * * Attempts to read the record from the current position in the file stream. * Returns FALSE on any error. * *=========================================================================*/ boolean CDFRmbRecord::ReadRecord (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadRecord()"); boolean Result; /* Delete the current contents, if any */ Destroy(); /* Attempt to read in all the record data sections */ Result = ReadHeader(); if (Result) Result = Read3DObjects(); if (Result) Result = ReadFlatObjects(); if (Result) Result = ReadSection3(); if (Result) Result = ReadPeople(); if (Result) Result = ReadDoors(); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadRecord() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadHeader (void); * * Attempts to read the record header from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::ReadHeader (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadHeader()"); boolean Result; /* Attempt to read the header all at once */ Result = Read((char *)&m_Header, sizeof(dfrmb_header_t)); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadHeader() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean Read3DObjects (void); * * Attempts to read the record 3D Object data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::Read3DObjects (void) { DEFINE_FUNCTION("CDFRmbRecord::Read3DObjects()"); boolean Result; /* Ensure valid object state */ ASSERT(m_p3DObjectRecords == NULL); if (m_Header.Num3DObjectRecords == 0) return (TRUE); /* Allocate pointer array and read data */ CreateArrayPointer(m_p3DObjectRecords, dfrmb_3dobject_t, m_Header.Num3DObjectRecords); Result = ReadEx((char *)m_p3DObjectRecords, sizeof(dfrmb_3dobject_t), m_Header.Num3DObjectRecords); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::Read3DObjects() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadFlatObjects (void); * * Attempts to read the record section2 data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::ReadFlatObjects (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadFlatObjects()") boolean Result; /* Ensure valid object state */ ASSERT(m_pFlatObjectRecords == NULL); if (m_Header.NumFlatObjectRecords == 0) return (TRUE); /* Allocate pointer array and read data */ CreateArrayPointer(m_pFlatObjectRecords, dfrmb_flatobject_t, m_Header.NumFlatObjectRecords); Result = ReadEx((char *)m_pFlatObjectRecords, sizeof(dfrmb_flatobject_t), m_Header.NumFlatObjectRecords); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadFlatObjects() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadSection3 (void); * * Attempts to read the record section3 data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::ReadSection3 (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadSection3()"); boolean Result; /* Ensure valid object state */ ASSERT(m_pSection3Records == NULL); if (m_Header.NumSection3Records == 0) return (TRUE); /* Allocate pointer array and read data */ CreateArrayPointer(m_pSection3Records, dfrmb_section3_t, m_Header.NumSection3Records); Result = ReadEx ((char *)m_pSection3Records, sizeof(dfrmb_section3_t), m_Header.NumSection3Records); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadSection3() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadPeople (void); * * Attempts to read the record section4 data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::ReadPeople (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadPeople()"); boolean Result; /* Ensure valid object state */ ASSERT(m_pPeopleRecords == NULL); if (m_Header.NumPeopleRecords == 0) return (TRUE); /* Allocate pointer array and read data */ CreateArrayPointer(m_pPeopleRecords, dfrmb_people_t, m_Header.NumPeopleRecords); Result = ReadEx ((char *)m_pPeopleRecords, sizeof(dfrmb_people_t), m_Header.NumPeopleRecords); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadPeople() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbRecord Method - boolean ReadDoors (void); * * Attempts to read the record Door data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbRecord::ReadDoors (void) { DEFINE_FUNCTION("CDFRmbRecord::ReadDoors()"); boolean Result; /* Ensure valid object state */ ASSERT(m_pDoorRecords == NULL); if (m_Header.NumDoorRecords == 0) return (TRUE); /* Allocate pointer array and read data */ CreateArrayPointer(m_pDoorRecords, dfrmb_door_t, m_Header.NumDoorRecords); Result = ReadEx ((char *)m_pDoorRecords, sizeof(dfrmb_door_t), m_Header.NumDoorRecords); return (Result); } /*=========================================================================== * End of Class Method CDFRmbRecord::ReadDoors() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Constructor * *=========================================================================*/ CDFRmbFile::CDFRmbFile() { /* Initialize array pointers */ m_pInsideRecords = NULL; m_pOutsideRecords = NULL; m_p3DObjects = NULL; m_pFlatObjects = NULL; m_RecordOffset = 0; m_RecordSize = 0; /* Initialize fixed length data */ memset(&m_FLDData, 0, sizeof(dfrmbfld_t)); } /*=========================================================================== * End of Class CDFRmbFile Constructor *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Destructor * *=========================================================================*/ void CDFRmbFile::Destroy (void) { DEFINE_FUNCTION("CDFRmbFile::Destroy()"); /* Unallocate memory from arrays */ DestroyArrayPointer(m_pOutsideRecords); DestroyArrayPointer(m_pInsideRecords); DestroyArrayPointer(m_p3DObjects); DestroyArrayPointer(m_pFlatObjects); m_RecordOffset = 0; m_RecordSize = 0; /* Initialize fixed length data */ memset(&m_FLDData, 0, sizeof(dfrmbfld_t)); } /*=========================================================================== * End of Class CDFRmbFile Destructor *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean Export3DS (File3DS, Flags); * * Attempts to export the RMB file to the given 3DS file. Returns FALSE * on any error. * *=========================================================================*/ boolean CDFRmbFile::Export3DS (C3dsFile& File3DS, const int Flags) { //DEFINE_FUNCTION("CDFRmbFile::Export3DS()"); boolean Result; int Index; df3dpoint_t TransPoint; /* Attempt to output 3DS chunk headers */ Result = File3DS.StartMainChunk(); Result &= File3DS.StartEditChunk(); /* Output each sub-block to the 3DS file */ for (Index = 0; Index < m_FLDData.NumBlocks; Index++) { /* Get the position translation for the sub-block */ TransPoint.X = m_FLDData.PositionRecords[Index].XPos; TransPoint.Y = 0; TransPoint.Z = m_FLDData.PositionRecords[Index].ZPos; /* Export the outside objects */ if ((Flags & DFRMB_FLAG_OUTSIDE)) { Result &= m_pOutsideRecords[Index].Export3DS(File3DS, Flags, &TransPoint); } /* Export the inside objects */ if ((Flags & DFRMB_FLAG_INSIDE)) { Result &= m_pInsideRecords[Index].Export3DS(File3DS, Flags, &TransPoint); } } /* End the main chunk sections */ Result &= File3DS.EndEditChunk(); Result &= File3DS.EndMainChunk(); return (Result); } /*=========================================================================== * End of Class Method CDFRmbFile::Export3DS() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean ReadRMB (RecordSize); * * Attempts to read the RMB file from the current position in the file stream. * Returns FALSE on any error. * *=========================================================================*/ boolean CDFRmbFile::ReadRMB (const long RecordSize) { DEFINE_FUNCTION("CDFRmbFile::ReadRMB()"); boolean Result; /* Delete any current information */ Destroy(); /* Save record information */ m_RecordOffset = Tell(); m_RecordSize = RecordSize; /* Attempt to read in all the record data */ Result = ReadFLD(); if (Result) Result = ReadBlocks(); if (Result) Result = Read3DObjects(); if (Result) Result = ReadFlatObjects(); return (Result); } /*=========================================================================== * End of Class Method CDFRmbFile::ReadRMB() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean ReadFLD (void); * * Attempts to read the RMB file FLD data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbFile::ReadFLD (void) { DEFINE_FUNCTION("CDFRmbFile::ReadFLD()"); boolean Result; /* Read in FLD all at once */ Result = Read((char *)&m_FLDData, sizeof(dfrmbfld_t)); return (Result); } /*=========================================================================== * End of Class Method CDFRmbFile::ReadFLD() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean ReadBlocks (void); * * Attempts to read the RMB blocks data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbFile::ReadBlocks (void) { DEFINE_FUNCTION("CDFRmbFile::ReadBlocks()"); boolean Result; int Index; char InputByte; long RecOffset; long NumExtraBytes; /* Ensure valid object state */ ASSERT(m_pOutsideRecords == NULL && m_pInsideRecords == NULL); if (m_FLDData.NumBlocks == 0) return (TRUE); /* Allocate the block arrays */ CreateArrayPointer(m_pOutsideRecords, CDFRmbRecord, m_FLDData.NumBlocks); CreateArrayPointer(m_pInsideRecords, CDFRmbRecord, m_FLDData.NumBlocks); /* Read all block sub-records */ for (Index = 0; Index < m_FLDData.NumBlocks; Index++) { RecOffset = Tell(); /* Attempt to read in the first record set */ m_pOutsideRecords[Index].Attach(GetHandle()); Result = m_pOutsideRecords[Index].ReadRecord(); m_pOutsideRecords[Index].Detach(); if (!Result) return (FALSE); /* Attempt to read in the second record set */ m_pInsideRecords[Index].Attach(GetHandle()); Result = m_pInsideRecords[Index].ReadRecord(); m_pInsideRecords[Index].Detach(); if (!Result) return (FALSE); /* Compute amount of extra bytes remaining */ NumExtraBytes = Tell() - RecOffset - m_FLDData.BlockSizes[Index].RecordSize; /* Special case of 1 extra byte at end of record */ if (NumExtraBytes == -1) { Result = ReadChar(InputByte); if (!Result) return (FALSE); m_pInsideRecords[Index].SetExtraByte(InputByte); } /* Ensure there are no extra bytes left over */ else if (NumExtraBytes != 0) { ErrorHandler.AddError(DFERR_RMB_RECORDSIZE, "ExtraBytes=%ld", NumExtraBytes); return (FALSE); } /* Jump to the proper record offset for the next data */ Result = Seek(RecOffset + m_FLDData.BlockSizes[Index].RecordSize, SEEK_SET); if (!Result) return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDFRmbFile::ReadBlocks() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean Read3DObjects (void); * * Attempts to read the RMB 3D Object data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbFile::Read3DObjects (void) { DEFINE_FUNCTION("CDFRmbFile::Read3DObjects()"); boolean Result; /* Ensure valid object state */ ASSERT(m_p3DObjects == NULL); if (m_FLDData.Num3DObjects == 0) return (TRUE); /* Allocate and read data */ CreateArrayPointer(m_p3DObjects, dfrmb_3dobject_t, m_FLDData.Num3DObjects); Result = ReadEx ((char *)m_p3DObjects, sizeof(dfrmb_3dobject_t), m_FLDData.Num3DObjects); return (Result); } /*=========================================================================== * End of Class Method CDFRmbFile::Read3DObjects() *=========================================================================*/ /*=========================================================================== * * Class CDFRmbFile Method - boolean ReadFlatObjects (void); * * Attempts to read the RMB Flat Object data from the current position in the * file stream. Returns FALSE on any error. Protected class method. * *=========================================================================*/ boolean CDFRmbFile::ReadFlatObjects (void) { DEFINE_FUNCTION("CDFRmbFile::ReadFlatObjects()"); boolean Result; /* Ensure valid object state */ ASSERT(m_pFlatObjects == NULL); if (m_FLDData.NumFlatObjects == 0) return (TRUE); /* Allocate object array and read data */ CreateArrayPointer(m_pFlatObjects, dfrmb_flatobject_t, m_FLDData.NumFlatObjects); Result = ReadEx ((char *)m_pFlatObjects, sizeof(dfrmb_flatobject_t), m_FLDData.NumFlatObjects); return (Result); } /*=========================================================================== * End of Class Method CDFRmbFile::ReadRecords3() *=========================================================================*/ /*=========================================================================== * * Function - boolean GetDFRmbFileChar (pString, CharIndex); * * Converts an RMB character index to a 2 byte string. The input string * pointer must be valid and be at least 3 bytes in size. Returns FALSE * if the given index is not valid. * *=========================================================================*/ boolean GetDFRmbFileChar (char* pString, const int CharIndex) { DEFINE_FUNCTION("GetDFRmbFileChar()"); /* Ensure valid input */ ASSERT(pString != NULL && CharIndex >= 0); /* Fill in the file character string */ if (CharIndex < 0x0F) strcpy(pString, "AA"); else if (CharIndex <= 0x1F) strcpy(pString, "DA"); else if (CharIndex <= 0x2F) strcpy(pString, "AL"); else if (CharIndex <= 0x3F) strcpy(pString, "DL"); else if (CharIndex <= 0x4F) strcpy(pString, "AM"); else if (CharIndex <= 0x5F) strcpy(pString, "DM"); else if (CharIndex <= 0x6F) strcpy(pString, "AS"); else if (CharIndex <= 0x7F) strcpy(pString, "DS"); else if (CharIndex <= 0x8F) strcpy(pString, "AA"); else if (CharIndex <= 0x9F) strcpy(pString, "DA"); else { /* Invalid character index */ *pString = NULL_CHAR; ErrorHandler.AddError(DFERR_RMB_CHARINDEX, "CharIndex=%d", CharIndex); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Function GetDFRmbFileChar() *=========================================================================*/ /*=========================================================================== * * Function - boolean GetDFRmbFilename (pString, Index); * * Copies the specified RMB filename indicated by the index value into the * given string. The index should range from 0 to 44 or NULL will be returned. * The input string must be at least 5 bytes in size. * *=========================================================================*/ boolean GetDFRmbFilename (char* pString, const int Index) { DEFINE_FUNCTION("GetDFRmbFilename()"); /* Ensure a valid input */ ASSERT(pString != NULL); /* Ensure a valid index */ if (Index < 0 || Index >= DFRMB_MAX_FILEINDEX) { ASSERT(FALSE); return (FALSE); } /* Copy the string */ strnncpy(pString, l_DFRMBFilenames[Index], 4); return (TRUE); } /*=========================================================================== * End of Function GetDFRmbFilename() *=========================================================================*/