/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * FrameResource.c * * Framework Resource file processing functions * */ #include #include #include #include #include "frame.h" #include "frameresource.h" #include "resly.h" // Local prototypes static RES_TYPE *psResTypes=NULL; // check to see if RES_TYPE entry is valid // this is a NULL check (ie linked list) #define resValidType(Type) (Type) // linked list macros #define resNextType(Type) (Type->psNext) #define resGetResDataPointer(psRes) (psRes->pData) #define resGetResBlockID(psRes) (psRes->blockID) /* The initial resource directory and the current resource directory */ char aResDir[FILE_MAXCHAR]; char aCurrResDir[FILE_MAXCHAR]; // the current resource block ID static SDWORD resBlockID; // buffer to load file data into static char *pFileBuffer = NULL; static SDWORD fileBufferSize = 0; // prototypes static void ResetResourceFile(void); // callback to resload screen. static RESLOAD_CALLBACK resLoadCallback=NULL; static RESPRELOAD_CALLBACK resPreLoadCallback=NULL; void resSetPreLoadCallback(RESPRELOAD_CALLBACK funcToCall) { resPreLoadCallback=funcToCall; } /* set the callback function for the res loader*/ void resSetLoadCallback(RESLOAD_CALLBACK funcToCall) { resLoadCallback = funcToCall; } /* do the callback for the resload display function */ void resDoResLoadCallback(void) { if(resLoadCallback) { resLoadCallback(); } } /* Initialise the resource module */ BOOL resInitialise(void) { ASSERT( psResTypes == NULL, "resInitialise: resource module hasn't been shut down??" ); psResTypes = NULL; resBlockID = 0; resLoadCallback = NULL; resPreLoadCallback = NULL; ResetResourceFile(); return TRUE; } /* Shutdown the resource module */ void resShutDown(void) { if (psResTypes != NULL) { debug(LOG_WZ, "resShutDown: warning resources still allocated"); resReleaseAll(); } } // set the base resource directory void resSetBaseDir(char *pResDir) { strncpy(aResDir, pResDir, FILE_MAXCHAR - 1); } /* Parse the res file */ BOOL resLoad(const char *pResFile, SDWORD blockID, char *pLoadBuffer, SDWORD bufferSize) { char *pBuffer; UDWORD size; strcpy(aCurrResDir, aResDir); // Note the buffer for file data pFileBuffer = pLoadBuffer; fileBufferSize = bufferSize; // Note the block id number resBlockID = blockID; debug(LOG_WZ, "resLoad: loading %s", pResFile); // Load the RES file; allocate memory for a wrf, and load it if (!loadFile(pResFile, &pBuffer, &size)) { debug(LOG_ERROR, "resLoad: failed to load %s", pResFile); return FALSE; } // and parse it resSetInputBuffer(pBuffer, size); if (res_parse() != 0) { debug(LOG_ERROR, "resLoad: failed to parse %s", pResFile); return FALSE; } FREE(pBuffer); return TRUE; } /* Allocate a RES_TYPE structure */ static BOOL resAlloc(const char *pType, RES_TYPE **ppsFunc) { RES_TYPE *psT; #ifdef DEBUG // Check for a duplicate type for(psT = psResTypes; psT; psT = psT->psNext) { ASSERT( strcmp(psT->aType, pType) != 0, "resAlloc: Duplicate function for type: %s", pType ); } #endif // Allocate the memory psT = (RES_TYPE *)MALLOC(sizeof(RES_TYPE)); if (!psT) { debug( LOG_ERROR, "resAlloc: Out of memory" ); abort(); return FALSE; } // setup the structure strncpy(psT->aType, pType, RESTYPE_MAXCHAR - 1); psT->aType[RESTYPE_MAXCHAR - 1] = 0; psT->HashedType=HashString(psT->aType); // store a hased version for super speed ! psT->psRes = NULL; *ppsFunc = psT; return TRUE; } /* Add a buffer load function for a file type */ BOOL resAddBufferLoad(const char *pType, RES_BUFFERLOAD buffLoad, RES_FREE release) { RES_TYPE *psT; if (!resAlloc(pType, &psT)) { return FALSE; } psT->buffLoad = buffLoad; psT->fileLoad = NULL; psT->release = release; psT->psNext = psResTypes; psResTypes = psT; return TRUE; } /* Add a file name load function for a file type */ BOOL resAddFileLoad(const char *pType, RES_FILELOAD fileLoad, RES_FREE release) { RES_TYPE *psT; if (!resAlloc(pType, &psT)) { return FALSE; } psT->buffLoad = NULL; psT->fileLoad = fileLoad; psT->release = release; psT->psNext = psResTypes; psResTypes = psT; return TRUE; } // Make a string lower case void resToLower(char *pStr) { while (*pStr != 0) { if (isupper(*pStr)) { *pStr = (char)(*pStr - (char)('A' - 'a')); } pStr += 1; } } static char LastResourceFilename[FILE_MAXCHAR]; // Returns the filename of the last resource file loaded char *GetLastResourceFilename(void) { return(LastResourceFilename); } // Set the resource name of the last resource file loaded void SetLastResourceFilename(char *pName) { strncpy(LastResourceFilename, pName, FILE_MAXCHAR-1); LastResourceFilename[FILE_MAXCHAR-1] = 0; } static UDWORD LastHashName; // Returns the filename of the last resource file loaded UDWORD GetLastHashName(void) { return (LastHashName); } // Set the resource name of the last resource file loaded void SetLastHashName(UDWORD HashName) { LastHashName = HashName; } // Structure for each file currently in use in the resource ... probably only going to be one ... but we will handle upto MAXLOADEDRESOURCE typedef struct { char *pBuffer; // a pointer to the data UDWORD size; // number of bytes UBYTE type; // what type of resource is it } RESOURCEFILE; #define RESFILETYPE_EMPTY (0) // empty entry #define RESFILETYPE_PC_SBL (1) // Johns SBL stuff #define RESFILETYPE_LOADED (2) // Loaded from a file (!) #define RESFILETYPE_WDGPTR (3) // A pointer from the WDG cache #define MAXLOADEDRESOURCES (6) static RESOURCEFILE LoadedResourceFiles[MAXLOADEDRESOURCES]; // Clear out the resource list ... needs to be called during init. static void ResetResourceFile(void) { UWORD i; for (i=0;itype=RESFILETYPE_PC_SBL; ResData->size=size; ResData->pBuffer=pBuffer; return(TRUE); } #endif // This is needed for files that do not fit in the WDG cache ... (VAB file for example) if (!loadFile(ResourceName, &pBuffer, &size)) { return FALSE; } ResData->type=RESFILETYPE_LOADED; ResData->size=size; ResData->pBuffer=pBuffer; return(TRUE); } // Free up the file depending on what type it is static void FreeResourceFile(RESOURCEFILE *OldResource) { switch (OldResource->type) { case RESFILETYPE_LOADED: FREE(OldResource->pBuffer); break; default: debug(LOG_WARNING, "resource not freed"); } // Remove from the list OldResource->type=RESFILETYPE_EMPTY; } static void resDataInit(RES_DATA* psRes, char *DebugName, UDWORD DataIDHash, void *pData, UDWORD BlockID) { psRes->pData = pData; psRes->blockID = BlockID; psRes->HashedID=DataIDHash; strcpy(psRes->aID, DebugName); psRes->usage = 0; } /* Call the load function for a file */ BOOL resLoadFile(char *pType, char *pFile) { RES_TYPE *psT; void *pData; RES_DATA *psRes; char aFileName[FILE_MAXCHAR]; BOOL loadresource; UDWORD HashedName; loadresource=TRUE; if(resPreLoadCallback) { loadresource=resPreLoadCallback(pType,pFile,aCurrResDir); } if (loadresource==TRUE) { UDWORD HashedType=HashString(pType); for(psT = psResTypes; resValidType(psT); psT = resNextType(psT) ) { if (psT->HashedType==HashedType) { break; } } if (psT == NULL) { debug(LOG_WZ, "resLoadFile: Unknown type: %s", pType); return FALSE; } HashedName = HashStringIgnoreCase(pFile); for (psRes = psT->psRes; psRes; psRes = psRes->psNext) { if(psRes->HashedID == HashedName) { debug(LOG_WZ, "resLoadFile: Duplicate file name: %s (hash %x) for type %s", pFile, HashedName, psT->aType); // assume that they are actually both the same and silently fail // lovely little hack to allow some files to be loaded from disk (believe it or not!). return TRUE; } } // Create the file name if (strlen(aCurrResDir) + strlen(pFile) + 1 >= FILE_MAXCHAR) { debug(LOG_ERROR, "resLoadFile: Filename too long!! %s%s", aCurrResDir, pFile); return FALSE; } strcpy(aFileName, aCurrResDir); strcat(aFileName, pFile); strcpy(LastResourceFilename,pFile); // Save the filename in case any routines need it resToLower(LastResourceFilename); SetLastHashName(HashStringIgnoreCase(LastResourceFilename)); // load the resource if (psT->buffLoad) { RESOURCEFILE *Resource; BOOL Result; Result=RetreiveResourceFile(aFileName,&Resource); if (Result == FALSE) { debug(LOG_ERROR, "resLoadFile: Unable to retreive resource - %s", aFileName); return(FALSE); } // Now process the buffer data if (!psT->buffLoad(Resource->pBuffer, Resource->size, &pData)) { FreeResourceFile(Resource); psT->release( pData ); return FALSE; } FreeResourceFile(Resource); resDoResLoadCallback(); // do callback. } else { debug(LOG_ERROR, "resLoadFile: No load functions for this type (%s)", pType); return FALSE; } // Set up the resource structure if there is something to store if (pData != NULL) { psRes = (RES_DATA*)MALLOC(sizeof(RES_DATA)); if (!psRes) { debug(LOG_ERROR, "resLoadFile: Out of memory"); psT->release(pData); return FALSE; } // LastResourceFilename may have been changed (e.g. by TEXPAGE loading) resDataInit( psRes, LastResourceFilename, HashStringIgnoreCase(LastResourceFilename), pData, resBlockID ); // Add the resource to the list psRes->psNext = psT->psRes; psT->psRes = psRes; } } return TRUE; } /* Return the resource for a type and hashedname */ void *resGetDataFromHash(const char *pType, UDWORD HashedID) { RES_TYPE *psT; RES_DATA *psRes; UDWORD HashedType; // Find the correct type HashedType=HashString(pType); // la da la for(psT = psResTypes; resValidType(psT); psT = resNextType(psT) ) { if (psT->HashedType==HashedType) { break; } } if (psT == NULL) { ASSERT( FALSE, "resGetData: Unknown type: %s", pType ); return NULL; } { // UDWORD HashedID=HashStringIgnoreCase(pID); for(psRes = psT->psRes; psRes; psRes = psRes->psNext) { if (psRes->HashedID==HashedID) { /* We found it */ break; } } } if (psRes == NULL) { ASSERT( FALSE, "resGetDataFromHash: Unknown ID:" ); return NULL; } psRes->usage += 1; return resGetResDataPointer(psRes); } /* Return the resource for a type and ID */ void *resGetData(const char *pType, const char *pID) { RES_TYPE *psT; RES_DATA *psRes; UDWORD HashedType; // Find the correct type HashedType=HashString(pType); // la da la //printf("[resGetData] entering with %s / %s = %0x\n",pID,pType,HashedType); for(psT = psResTypes; resValidType(psT); psT = resNextType(psT) ) { if (psT->HashedType==HashedType) { break; } } if (psT == NULL) { ASSERT( FALSE, "resGetData: Unknown type: %s", pType ); return NULL; } { UDWORD HashedID=HashStringIgnoreCase(pID); for(psRes = psT->psRes; psRes; psRes = psRes->psNext) { if (psRes->HashedID==HashedID) { /* We found it */ // printf("[resGetData] looking for %s = %0x ******found!\n",pID,HashedID); break; } } } if (psRes == NULL) { ASSERT( FALSE, "resGetData: Unknown ID: %s", pID ); // resLoadFile(pType,pID); // resGetData(pType,pID); // return NULL; } psRes->usage += 1; return resGetResDataPointer(psRes); } BOOL resGetHashfromData(const char *pType, const void *pData, UDWORD *pHash) { RES_TYPE *psT; RES_DATA *psRes; // Find the correct type UDWORD HashedType=HashString(pType); for(psT = psResTypes; resValidType(psT); psT = resNextType(psT) ) { if (psT->HashedType==HashedType) { break; } } if (psT == NULL) { ASSERT( FALSE, "resGetHashfromData: Unknown type: %x", HashedType ); return FALSE; } // Find the resource for(psRes = psT->psRes; psRes; psRes = psRes->psNext) { if (resGetResDataPointer(psRes) == pData) { break; } } if (psRes == NULL) { ASSERT( FALSE, "resGetHashfromData:: couldn't find data for type %x\n", HashedType ); return FALSE; } *pHash = psRes->HashedID; return TRUE; } /* Simply returns true if a resource is present */ BOOL resPresent(const char *pType, const char *pID) { RES_TYPE *psT; RES_DATA *psRes; // Find the correct type UDWORD HashedType=HashString(pType); for(psT = psResTypes; resValidType(psT); psT = resNextType(psT) ) { if (psT->HashedType==HashedType) { break; } } /* Bow out if unrecognised type */ if (psT == NULL) { // ASSERT( FALSE, "resPresent: Unknown type" ); return FALSE; } { UDWORD HashedID=HashStringIgnoreCase(pID); // DBPRINTF(("%x - %d\n",HashedID,pID)); for(psRes = psT->psRes; psRes; psRes = psRes->psNext) { // DBPRINTF(("!= %x\n",psRes->HashedID)); if (psRes->HashedID==HashedID) { /* We found it */ break; } } } /* Did we find it? */ if (psRes != NULL) { return (TRUE); } return (FALSE); } /* Release all the resources currently loaded and the resource load functions */ void resReleaseAll(void) { RES_TYPE *psT, *psNT; RES_DATA *psRes, *psNRes; for(psT = psResTypes; resValidType(psT); psT = psNT) { for(psRes = psT->psRes; psRes; psRes = psNRes) { if (psRes->usage == 0) { debug(LOG_WZ, "resReleaseAll: %s resource: %s(%04x) not used", psT->aType, psRes->aID, psRes->HashedID); } if(psT->release != NULL) { psT->release( resGetResDataPointer(psRes) ); } else { ASSERT( FALSE,"resReleaseAll: NULL release function" ); } psNRes = psRes->psNext; FREE(psRes); } psNT = resNextType(psT); FREE(psT); } psResTypes = NULL; } // release the data for a particular block ID void resReleaseBlockData(SDWORD blockID) { RES_TYPE *psT, *psNT; RES_DATA *psPRes, *psRes, *psNRes; for(psT = psResTypes; resValidType(psT); psT = psNT) { psPRes = NULL; for(psRes = psT->psRes; psRes; psRes = psNRes) { ASSERT( psRes != NULL,"resReleaseBlockData: null pointer passed into loop" ); if (resGetResBlockID(psRes) == blockID) { if (psRes->usage == 0) { debug(LOG_WZ, "resReleaseBlockData: %s resource: %s(%04x) not used", psT->aType, psRes->aID, psRes->HashedID); } if(psT->release != NULL) { psT->release( resGetResDataPointer(psRes) ); } else { ASSERT( FALSE,"resReleaseAllData: NULL release function" ); } psNRes = psRes->psNext; FREE(psRes); if (psPRes == NULL) { psT->psRes = psNRes; } else { psPRes->psNext = psNRes; } } else { psPRes = psRes; psNRes = psRes->psNext; } ASSERT( psNRes != (RES_DATA *)0xdddddddd,"resReleaseBlockData: next data (next pointer) already freed" ); } psNT = resNextType(psT); ASSERT( psNT != (RES_TYPE *)0xdddddddd,"resReleaseBlockData: next data (next pointer) already freed" ); } } /* Release all the resources currently loaded but keep the resource load functions */ void resReleaseAllData(void) { RES_TYPE *psT, *psNT; RES_DATA *psRes, *psNRes; for (psT = psResTypes; resValidType(psT); psT = psNT) { for (psRes = psT->psRes; psRes; psRes = psNRes) { if (psRes->usage == 0) { debug(LOG_WZ, "resReleaseAllData: %s resource: %s(%04x) not used", psT->aType, psRes->aID, psRes->HashedID); } if(psT->release != NULL) { psT->release( resGetResDataPointer(psRes) ); } else { ASSERT( FALSE,"resReleaseAllData: NULL release function" ); } psNRes = psRes->psNext; FREE(psRes); } psT->psRes = NULL; psNT = resNextType(psT); } }