warzone2100/src/data.cpp

858 lines
19 KiB
C++

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 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
*/
/**
* @file data.c
*
* Data loading functions used by the framework resource module.
*
*/
#include <physfs.h>
#include "lib/framework/frame.h"
#include "lib/framework/frameresource.h"
#include "lib/framework/strres.h"
#include "lib/framework/crc.h"
#include "lib/framework/resly.h"
#include "lib/gamelib/parser.h"
#include "lib/ivis_opengl/bitimage.h"
#include "lib/ivis_opengl/png_util.h"
#include "lib/script/script.h"
#include "lib/sound/audio.h"
#include "qtscript.h"
#include "data.h"
#include "droid.h"
#include "feature.h"
#include "mechanics.h"
#include "message.h"
#include "multiplay.h"
#include "research.h"
#include "scriptvals.h"
#include "stats.h"
#include "template.h"
#include "text.h"
#include "texture.h"
// whether a save game is currently being loaded
static bool saveFlag = false;
uint32_t DataHash[DATA_MAXDATA]= {0};
/**
* hashBuffer()
* \param pData pointer to our buffer (always a text file)
* \param size the size of the buffer
* \return hash calculated from the buffer.
*
* Note, this is obviously not a very complex hash routine. This most likely has many collisions possible.
* This is almost the same routine that Pumpkin had, minus the ugly bug :)
* And minus the old algorithm and debugging trace, replaced with a simple CRC...
*/
static UDWORD hashBuffer(uint8_t *pData, uint32_t size)
{
char nl = '\n';
uint32_t crc = 0;
uint32_t i, j;
uint32_t lines = 0;
uint32_t bytes = 0;
for (i = 0; i < size; i = j + 1)
{
for (j = i; j < size && pData[j] != '\n' && pData[j] != '\r'; ++j)
{}
if (i != j) // CRC non-empty lines only.
{
crc = crcSum(crc, pData + i, j - i); // CRC the line.
crc = crcSum(crc, &nl, 1); // CRC the line ending.
++lines;
bytes += j - i + 1;
}
}
debug(LOG_NET, "The size of the old buffer (%u bytes - %d stripped), New buffer size of %u bytes, %u non-empty lines.", size, size - bytes, bytes, lines);
return ~crc;
}
// create the hash for that data block.
// Data should be converted to Network byte order
static void calcDataHash(uint8_t *pBuffer, uint32_t size, uint32_t index)
{
const uint32_t oldHash = DataHash[index];
if (!bMultiPlayer)
{
return;
}
DataHash[index] += hashBuffer(pBuffer, size);
if (!DataHash[index] && oldHash)
{
debug(LOG_NET, "The new hash is 0, the old hash was %u. We added the negated value!", oldHash);
}
debug(LOG_NET, "DataHash[%2u] = %08x", index, DataHash[index]);
return;
}
void resetDataHash(void)
{
UDWORD i;
for (i = 0; i < DATA_MAXDATA; i++)
{
DataHash[i] = 0;
}
debug(LOG_NET, "== Hash is reset ==");
}
/**********************************************************/
void dataSetSaveFlag(void)
{
saveFlag = true;
}
void dataClearSaveFlag(void)
{
saveFlag = false;
}
/* Load the body stats */
static bool bufferSBODYLoad(const char* fileName, void** ppData)
{
if (!loadBodyStats(fileName) || !allocComponentList(COMP_BODY, numBodyStats))
{
return false;
}
*ppData = (void *)1;
return true;
}
static void dataReleaseStats(WZ_DECL_UNUSED void *pData)
{
// FIXME, huge hack, is called many times!
freeComponentLists();
statsShutDown();
}
/* Load the weapon stats */
static bool bufferSWEAPONLoad(const char *fileName, void **ppData)
{
if (!loadWeaponStats(fileName)
|| !allocComponentList(COMP_WEAPON, numWeaponStats))
{
return false;
}
// not interested in this value
*ppData = NULL;
return true;
}
/* Load the constructor stats */
static bool bufferSCONSTRLoad(const char *fileName, void **ppData)
{
if (!loadConstructStats(fileName)
|| !allocComponentList(COMP_CONSTRUCT, numConstructStats))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the ECM stats */
static bool bufferSECMLoad(const char *fileName, void **ppData)
{
if (!loadECMStats(fileName)
|| !allocComponentList(COMP_ECM, numECMStats))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Propulsion stats */
static bool bufferSPROPLoad(const char* fileName, void** ppData)
{
if (!loadPropulsionStats(fileName) || !allocComponentList(COMP_PROPULSION, numPropulsionStats))
{
return false;
}
//not interested in this value
*ppData = (void *)1;
return true;
}
static bool bufferSSENSORLoad(const char *fileName, void **ppData)
{
if (!loadSensorStats(fileName)
|| !allocComponentList(COMP_SENSOR, numSensorStats))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Repair stats */
static bool bufferSREPAIRLoad(const char *fileName, void **ppData)
{
if (!loadRepairStats(fileName) || !allocComponentList(COMP_REPAIRUNIT, numRepairStats))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Brain stats */
static bool bufferSBRAINLoad(const char *fileName, void **ppData)
{
if (!loadBrainStats(fileName) || !allocComponentList(COMP_BRAIN, numBrainStats))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the PropulsionType stats */
static bool bufferSPROPTYPESLoad(const char *fileName, void **ppData)
{
if (!loadPropulsionTypes(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the propulsion type sound stats */
static bool bufferSPROPSNDLoad(const char *fileName, void **ppData)
{
if (!loadPropulsionSounds(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the STERRTABLE stats */
static bool bufferSTERRTABLELoad(const char *fileName, void **ppData)
{
if (!loadTerrainTable(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the body/propulsion IMDs stats */
static bool bufferSBPIMDLoad(const char *fileName, void **ppData)
{
if (!loadBodyPropulsionIMDs(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Weapon Effect modifier stats */
static bool bufferSWEAPMODLoad(const char *fileName, void **ppData)
{
if (!loadWeaponModifiers(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Template stats */
static bool bufferSTEMPLLoad(const char *fileName, void **ppData)
{
if (!loadDroidTemplates(fileName))
{
return false;
}
// set a dummy value so the release function gets called
*ppData = (void *)1;
return true;
}
// release the templates
static void dataSTEMPLRelease(WZ_DECL_UNUSED void *pData)
{
//free the storage allocated to the droid templates
droidTemplateShutDown();
}
/* Load the Structure stats */
static bool bufferSSTRUCTLoad(const char *fileName, void **ppData)
{
if (!loadStructureStats(QString(fileName)))
{
return false;
}
if (!allocStructLists())
{
return false;
}
// set a dummy value so the release function gets called
*ppData = (void *)1;
return true;
}
// release the structure stats
static void dataSSTRUCTRelease(WZ_DECL_UNUSED void *pData)
{
freeStructureLists();
structureStatsShutDown();
}
/* Load the Structure strength modifier stats */
static bool bufferSSTRMODLoad(const char *fileName, void **ppData)
{
if (!loadStructureStrengthModifiers(fileName))
{
return false;
}
//not interested in this value
*ppData = NULL;
return true;
}
/* Load the Feature stats */
static bool bufferSFEATLoad(const char *fileName, void **ppData)
{
if (!loadFeatureStats(fileName))
{
return false;
}
// set a dummy value so the release function gets called
*ppData = (void *)1;
return true;
}
// free the feature stats
static void dataSFEATRelease(WZ_DECL_UNUSED void *pData)
{
featureStatsShutDown();
}
// release the research stats
static void dataRESCHRelease(WZ_DECL_UNUSED void *pData)
{
//free the storage allocated to the stats
ResearchShutDown();
}
/* Load the Research stats */
static bool bufferRESCHLoad(const char *fileName, void **ppData)
{
//calcDataHash((uint8_t *)pBuffer, size, DATA_RESCH);
//check to see if already loaded
if (!asResearch.empty())
{
//release previous data before loading in the new
dataRESCHRelease(NULL);
}
if(!loadResearch(QString(fileName)))
{
return false;
}
return true;
}
/* Load the message viewdata */
static bool bufferSMSGLoad(const char *pBuffer, UDWORD size, void **ppData)
{
const char *ptr;
ptr = loadViewData(pBuffer, size);
if (!ptr)
{
return false;
}
// set the pointer so the release function gets called with it
*ppData = (void *)ptr;
return true;
}
/* Load research message viewdata */
static bool dataResearchMsgLoad(const char* fileName, void** ppData)
{
const char *ptr = loadResearchViewData(fileName);
if (!ptr)
{
return false;
}
// set the pointer so the release function gets called with it
*ppData = (void *)ptr;
return true;
}
// release the message viewdata
static void dataSMSGRelease(void *pData)
{
viewDataShutDown((const char *)pData);
}
/*!
* Load an image from file
*/
static bool dataImageLoad(const char *fileName, void **ppData)
{
iV_Image *psSprite = (iV_Image *)malloc(sizeof(iV_Image));
if (!psSprite)
{
return false;
}
if (!iV_loadImage_PNG(fileName, psSprite))
{
debug( LOG_ERROR, "IMGPAGE load failed" );
free(psSprite);
return false;
}
*ppData = psSprite;
return true;
}
// Tertiles (terrain tiles) loader.
static bool dataTERTILESLoad(const char *fileName, void **ppData)
{
bool status;
status = texLoad(fileName);
ASSERT_OR_RETURN(false, status, "Error loading tertiles!");
debug(LOG_TEXTURE, "HW Tiles loaded");
*ppData = NULL; // don't bother calling cleanup
return true;
}
static bool dataIMGLoad(const char *fileName, void **ppData)
{
*ppData = iV_LoadImageFile(fileName);
if(*ppData == NULL)
{
return false;
}
return true;
}
static void dataIMGRelease(void *pData)
{
iV_FreeImageFile((IMAGEFILE*)pData);
}
/*!
* Release an Image
*/
static void dataImageRelease(void *pData)
{
iV_Image *psSprite = (iV_Image*) pData;
if( psSprite )
{
free(psSprite);
}
}
/* Load an audio file */
static bool dataAudioLoad(const char* fileName, void **ppData)
{
if ( audio_Disabled() == true )
{
*ppData = NULL;
// No error occurred (sound is just disabled), so we return true
return true;
}
// Load the track from a file
*ppData = sound_LoadTrackFromFile( fileName );
return *ppData != NULL;
}
/* Load an audio file */
static bool dataAudioCfgLoad(const char* fileName, void **ppData)
{
bool success;
PHYSFS_file* fileHandle;
*ppData = NULL;
if (audio_Disabled())
{
return true;
}
debug(LOG_WZ, "Reading...[directory: %s] %s", PHYSFS_getRealDir(fileName), fileName);
fileHandle = PHYSFS_openRead(fileName);
if (fileHandle == NULL)
{
return false;
}
success = ParseResourceFile(fileHandle);
PHYSFS_close(fileHandle);
return success;
}
/* Load an anim file */
static bool dataAnimLoad(const char *fileName, void **ppData)
{
PHYSFS_file* fileHandle = PHYSFS_openRead(fileName);
debug(LOG_WZ, "Reading...[directory: %s] %s", PHYSFS_getRealDir(fileName), fileName);
if (fileHandle == NULL)
{
*ppData = NULL;
return false;
}
*ppData = anim_LoadFromFile(fileHandle);
PHYSFS_close(fileHandle);
return *ppData != NULL;
}
/* Load an audio config file */
static bool dataAnimCfgLoad(const char *fileName, void **ppData)
{
bool success;
PHYSFS_file* fileHandle = PHYSFS_openRead(fileName);
*ppData = NULL;
debug(LOG_WZ, "Reading...[directory: %s] %s", PHYSFS_getRealDir(fileName), fileName);
if (fileHandle == NULL)
{
return false;
}
success = ParseResourceFile(fileHandle);
PHYSFS_close(fileHandle);
return success;
}
static void dataAnimRelease( void *pData )
{
anim_ReleaseAnim((BASEANIM*)pData);
}
/* Load a string resource file */
static bool dataStrResLoad(const char* fileName, void** ppData)
{
// recreate the string resource if it was freed by a WRF release
if (psStringRes == NULL)
{
if (!stringsInitialise())
{
return false;
}
}
if (!strresLoad(psStringRes, fileName))
{
return false;
}
*ppData = psStringRes;
return true;
}
static void dataStrResRelease(WZ_DECL_UNUSED void *pData)
{
if (psStringRes != NULL)
{
strresDestroy(psStringRes);
psStringRes = NULL;
}
}
/* Load a script file */
// All scripts, binary or otherwise are now passed through this routine
static bool dataScriptLoad(const char* fileName, void **ppData)
{
static const bool printHack = false;
SCRIPT_CODE** psProg = (SCRIPT_CODE**)ppData;
PHYSFS_file* fileHandle;
uint8_t *pBuffer;
PHYSFS_sint64 fileSize = 0;
debug(LOG_WZ, "COMPILING SCRIPT ...%s", GetLastResourceFilename());
fileHandle = PHYSFS_openRead(fileName);
debug(LOG_WZ, "Reading...[directory: %s] %s", PHYSFS_getRealDir(fileName), fileName);
if (fileHandle == NULL)
{
return false;
}
// due to the changes in r2531 we must do this routine a bit different.
fileSize = PHYSFS_fileLength(fileHandle);
pBuffer = (uint8_t *)malloc(fileSize * sizeof(uint8_t));
ASSERT_OR_RETURN(false, pBuffer, "Out of memory");
PHYSFS_read(fileHandle, pBuffer, 1, fileSize);
calcDataHash(pBuffer, fileSize, DATA_SCRIPT);
free(pBuffer);
PHYSFS_seek(fileHandle, 0); //reset position
*psProg = scriptCompile(fileHandle, SCRIPTTYPE);
PHYSFS_close(fileHandle);
if (!*psProg) // see script.h
{
debug(LOG_ERROR, "Script %s did not compile", GetLastResourceFilename());
return false;
}
if (printHack)
{
cpPrintProgram(*psProg);
}
return true;
}
static void dataScriptRelease(void *pData)
{
SCRIPT_CODE *psCode = (SCRIPT_CODE *)pData;
scriptFreeCode(psCode);
}
static bool jsLoad(const char *fileName, void **ppData)
{
debug(LOG_WZ, "jsload: %s", fileName);
*ppData = NULL;
return loadGlobalScript(fileName);
}
// Load a script variable values file
static bool dataScriptLoadVals(const char* fileName, void **ppData)
{
bool success;
PHYSFS_file* fileHandle;
uint8_t *pBuffer;
PHYSFS_sint64 fileSize = 0;
*ppData = NULL;
// don't load anything if a saved game is being loaded
if (saveFlag)
{
return true;
}
debug(LOG_WZ, "Loading script data %s", GetLastResourceFilename());
fileHandle = PHYSFS_openRead(fileName);
debug(LOG_WZ, "Reading...[directory: %s] %s", PHYSFS_getRealDir(fileName), fileName);
if (fileHandle == NULL)
{
return false;
}
// due to the changes in r2532 we must do this routine a bit different.
fileSize = PHYSFS_fileLength(fileHandle);
pBuffer = (uint8_t *)malloc(fileSize * sizeof(uint8_t));
ASSERT_OR_RETURN(false, pBuffer, "Out of memory");
PHYSFS_read(fileHandle, pBuffer, 1, fileSize);
calcDataHash(pBuffer, fileSize, DATA_SCRIPTVAL);
free(pBuffer);
PHYSFS_seek(fileHandle, 0); //reset position
success = scrvLoad(fileHandle);
if (!success)
debug(LOG_FATAL, "Script %s did not compile", GetLastResourceFilename());
PHYSFS_close(fileHandle);
return success;
}
// New reduced resource type ... specially for PSX
// These are statically defined in data.c
// this is also defined in frameresource.c - needs moving to a .h file
// This basically matches the argument list of resAddBufferLoad in frameresource.c
struct RES_TYPE_MIN_BUF
{
const char *aType; ///< points to the string defining the type (e.g. SCRIPT) - NULL indicates end of list
RES_BUFFERLOAD buffLoad; ///< routine to process the data for this type
RES_FREE release; ///< routine to release the data (NULL indicates none)
};
static const RES_TYPE_MIN_BUF BufferResourceTypes[] =
{
{"SMSG", bufferSMSGLoad, dataSMSGRelease},
{"IMD", NULL, NULL}, // ignored
};
struct RES_TYPE_MIN_FILE
{
const char *aType; ///< points to the string defining the type (e.g. SCRIPT) - NULL indicates end of list
RES_FILELOAD fileLoad; ///< routine to process the data for this type
RES_FREE release; ///< routine to release the data (NULL indicates none)
};
static const RES_TYPE_MIN_FILE FileResourceTypes[] =
{
{"SFEAT", bufferSFEATLoad, dataSFEATRelease}, //feature stats file
{"STEMPL", bufferSTEMPLLoad, dataSTEMPLRelease}, //template and associated files
{"WAV", dataAudioLoad, (RES_FREE)sound_ReleaseTrack},
{"SWEAPON", bufferSWEAPONLoad, dataReleaseStats},
{"SBPIMD", bufferSBPIMDLoad, dataReleaseStats},
{"SBRAIN", bufferSBRAINLoad, dataReleaseStats},
{"SSENSOR", bufferSSENSORLoad, dataReleaseStats},
{"SECM", bufferSECMLoad, dataReleaseStats},
{"SREPAIR", bufferSREPAIRLoad, dataReleaseStats},
{"SCONSTR", bufferSCONSTRLoad, dataReleaseStats},
{"SPROP", bufferSPROPLoad, dataReleaseStats},
{"SPROPTYPES", bufferSPROPTYPESLoad, dataReleaseStats},
{"STERRTABLE", bufferSTERRTABLELoad, dataReleaseStats},
{"SBODY", bufferSBODYLoad, dataReleaseStats},
{"SWEAPMOD", bufferSWEAPMODLoad, dataReleaseStats},
{"SPROPSND", bufferSPROPSNDLoad, dataReleaseStats},
{"AUDIOCFG", dataAudioCfgLoad, NULL},
{"ANI", dataAnimLoad, dataAnimRelease},
{"ANIMCFG", dataAnimCfgLoad, NULL},
{"IMGPAGE", dataImageLoad, dataImageRelease},
{"TERTILES", dataTERTILESLoad, NULL},
{"IMG", dataIMGLoad, dataIMGRelease},
{"TEXPAGE", NULL, NULL}, // ignored
{"TCMASK", NULL, NULL}, // ignored
{"SCRIPT", dataScriptLoad, dataScriptRelease},
{"SCRIPTVAL", dataScriptLoadVals, NULL},
{"STR_RES", dataStrResLoad, dataStrResRelease},
{"RESEARCHMSG", dataResearchMsgLoad, dataSMSGRelease },
{"SSTRMOD", bufferSSTRMODLoad, NULL},
{"JAVASCRIPT", jsLoad, NULL},
{"SSTRUCT", bufferSSTRUCTLoad, dataSSTRUCTRelease}, //structure stats and associated files
{"RESCH", bufferRESCHLoad, dataRESCHRelease}, //research stats files
};
/* Pass all the data loading functions to the framework library */
bool dataInitLoadFuncs(void)
{
// init the data integrity hash;
resetDataHash();
// Using iterator style: begin iterator (ResourceTypes),
// end iterator (EndType), and current iterator (CurrentType)
// iterate through buffer load functions
{
const RES_TYPE_MIN_BUF *CurrentType;
// Points just past the last item in the list
const RES_TYPE_MIN_BUF * const EndType = &BufferResourceTypes[sizeof(BufferResourceTypes) / sizeof(RES_TYPE_MIN_BUF)];
for (CurrentType = BufferResourceTypes; CurrentType != EndType; ++CurrentType)
{
if(!resAddBufferLoad(CurrentType->aType, CurrentType->buffLoad, CurrentType->release))
{
return false; // error whilst adding a buffer load
}
}
}
// iterate through file load functions
{
const RES_TYPE_MIN_FILE *CurrentType;
// Points just past the last item in the list
const RES_TYPE_MIN_FILE * const EndType = &FileResourceTypes[sizeof(FileResourceTypes) / sizeof(RES_TYPE_MIN_BUF)];
for (CurrentType = FileResourceTypes; CurrentType != EndType; ++CurrentType)
{
if(!resAddFileLoad(CurrentType->aType, CurrentType->fileLoad, CurrentType->release))
{
return false; // error whilst adding a file load
}
}
}
return true;
}