Rewrite stats loading for FEATURE_STATS.
The same should be doable for all BASE_STATS.master
parent
6f71d8925e
commit
7481694e9f
127
src/feature.cpp
127
src/feature.cpp
|
@ -72,13 +72,7 @@ FEATURE_STATS* oilResFeature = NULL;
|
||||||
//specifies how far round (in tiles) a constructor droid sound look for more wreckage
|
//specifies how far round (in tiles) a constructor droid sound look for more wreckage
|
||||||
#define WRECK_SEARCH 3
|
#define WRECK_SEARCH 3
|
||||||
|
|
||||||
struct featureTypeMap
|
static const StringToEnum<FEATURE_TYPE> mapUnsorted_FEATURE_TYPE[] =
|
||||||
{
|
|
||||||
const char *typeStr;
|
|
||||||
FEATURE_TYPE type;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct featureTypeMap map[] =
|
|
||||||
{
|
{
|
||||||
{ "PROPULSION_TYPE_HOVER WRECK", FEAT_HOVER },
|
{ "PROPULSION_TYPE_HOVER WRECK", FEAT_HOVER },
|
||||||
{ "TANK WRECK", FEAT_TANK },
|
{ "TANK WRECK", FEAT_TANK },
|
||||||
|
@ -93,6 +87,7 @@ static const struct featureTypeMap map[] =
|
||||||
{ "TREE", FEAT_TREE },
|
{ "TREE", FEAT_TREE },
|
||||||
{ "SKYSCRAPER", FEAT_SKYSCRAPER }
|
{ "SKYSCRAPER", FEAT_SKYSCRAPER }
|
||||||
};
|
};
|
||||||
|
static const StringToEnumMap<FEATURE_TYPE> map_FEATURE_TYPE(mapUnsorted_FEATURE_TYPE, ARRAY_SIZE(mapUnsorted_FEATURE_TYPE));
|
||||||
|
|
||||||
|
|
||||||
void featureInitVars(void)
|
void featureInitVars(void)
|
||||||
|
@ -102,112 +97,54 @@ void featureInitVars(void)
|
||||||
oilResFeature = NULL;
|
oilResFeature = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void featureType(FEATURE_STATS* psFeature, const char *pType)
|
FEATURE_STATS::FEATURE_STATS(LineView line)
|
||||||
|
: BASE_STATS(REF_FEATURE_START + line.line(), line.s(0))
|
||||||
|
, subType(line.e(7, map_FEATURE_TYPE))
|
||||||
|
, psImd(line.imdShape(6))
|
||||||
|
, baseWidth(line.u16(1))
|
||||||
|
, baseBreadth(line.u16(2))
|
||||||
|
, tileDraw(line.b(8))
|
||||||
|
, allowLOS(line.b(9))
|
||||||
|
, visibleAtStart(line.b(10))
|
||||||
|
, damageable(line.b(3))
|
||||||
|
, body(line.u32(5))
|
||||||
|
, armourValue(line.u32(4))
|
||||||
{
|
{
|
||||||
unsigned int i;
|
if (damageable && body == 0)
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(map); i++)
|
|
||||||
{
|
{
|
||||||
if (strcmp(pType, map[i].typeStr) == 0)
|
debug(LOG_ERROR, "The feature \"%s\", ref %d is damageable, but has no body points! The files need to be updated / fixed. Assigning 1 body point to feature.", pName, ref);
|
||||||
{
|
body = 1;
|
||||||
psFeature->subType = map[i].type;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(false, "featureType: Unknown feature type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load the feature stats */
|
/* Load the feature stats */
|
||||||
BOOL loadFeatureStats(const char *pFeatureData, UDWORD bufferSize)
|
BOOL loadFeatureStats(const char *pFeatureData, UDWORD bufferSize)
|
||||||
{
|
{
|
||||||
FEATURE_STATS *psFeature;
|
|
||||||
unsigned int i;
|
|
||||||
char featureName[MAX_STR_LENGTH], GfxFile[MAX_STR_LENGTH],
|
|
||||||
type[MAX_STR_LENGTH];
|
|
||||||
|
|
||||||
numFeatureStats = numCR(pFeatureData, bufferSize);
|
|
||||||
|
|
||||||
// Skip descriptive header
|
// Skip descriptive header
|
||||||
if (strncmp(pFeatureData,"Feature ",8)==0)
|
if (strncmp(pFeatureData,"Feature ",8)==0)
|
||||||
{
|
{
|
||||||
pFeatureData = strchr(pFeatureData,'\n') + 1;
|
pFeatureData = strchr(pFeatureData,'\n') + 1;
|
||||||
numFeatureStats--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asFeatureStats = (FEATURE_STATS*)malloc(sizeof(FEATURE_STATS) * numFeatureStats);
|
TableView table(pFeatureData, bufferSize);
|
||||||
|
|
||||||
if (asFeatureStats == NULL)
|
asFeatureStats = new FEATURE_STATS[table.size()];
|
||||||
|
numFeatureStats = table.size();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < table.size(); ++i)
|
||||||
{
|
{
|
||||||
debug( LOG_FATAL, "Feature Stats - Out of memory" );
|
asFeatureStats[i] = FEATURE_STATS(LineView(table, i));
|
||||||
abort();
|
if (table.isError())
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "%s", table.getError().toUtf8().constData());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
psFeature = asFeatureStats;
|
|
||||||
|
|
||||||
for (i = 0; i < numFeatureStats; i++)
|
|
||||||
{
|
|
||||||
UDWORD Width, Breadth;
|
|
||||||
int damageable = 0, tileDraw = 0, allowLOS = 0, visibleAtStart = 0;
|
|
||||||
|
|
||||||
memset(psFeature, 0, sizeof(FEATURE_STATS));
|
|
||||||
|
|
||||||
featureName[0] = '\0';
|
|
||||||
GfxFile[0] = '\0';
|
|
||||||
type[0] = '\0';
|
|
||||||
|
|
||||||
//read the data into the storage - the data is delimeted using comma's
|
|
||||||
sscanf(pFeatureData, "%[^','],%d,%d,%d,%d,%d,%[^','],%[^','],%d,%d,%d",
|
|
||||||
featureName, &Width, &Breadth,
|
|
||||||
&damageable, &psFeature->armourValue, &psFeature->body,
|
|
||||||
GfxFile, type, &tileDraw, &allowLOS,
|
|
||||||
&visibleAtStart);
|
|
||||||
|
|
||||||
psFeature->damageable = damageable;
|
|
||||||
psFeature->tileDraw = tileDraw;
|
|
||||||
psFeature->allowLOS = allowLOS;
|
|
||||||
psFeature->visibleAtStart = visibleAtStart;
|
|
||||||
|
|
||||||
// These are now only 16 bits wide - so we need to copy them
|
|
||||||
psFeature->baseWidth = Width;
|
|
||||||
psFeature->baseBreadth = Breadth;
|
|
||||||
|
|
||||||
psFeature->pName = allocateName(featureName);
|
|
||||||
if (!psFeature->pName)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (psFeature->damageable && psFeature->body == 0)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "The feature %s, ref %d, is damageable, but has no body points! The files need to be updated / fixed. " \
|
|
||||||
"Assigning 1 body point to feature.", psFeature->pName, psFeature->ref);
|
|
||||||
psFeature->body = 1;
|
|
||||||
}
|
|
||||||
//determine the feature type
|
|
||||||
featureType(psFeature, type);
|
|
||||||
|
|
||||||
//and the oil resource - assumes only one!
|
//and the oil resource - assumes only one!
|
||||||
if (psFeature->subType == FEAT_OIL_RESOURCE)
|
if (asFeatureStats[i].subType == FEAT_OIL_RESOURCE)
|
||||||
{
|
{
|
||||||
oilResFeature = psFeature;
|
oilResFeature = &asFeatureStats[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the IMD for the feature
|
|
||||||
psFeature->psImd = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
||||||
if (psFeature->psImd == NULL)
|
|
||||||
{
|
|
||||||
debug( LOG_ERROR, "Cannot find the feature PIE for record %s", getName( psFeature->pName ) );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
psFeature->ref = REF_FEATURE_START + i;
|
|
||||||
|
|
||||||
//increment the pointer to the start of the next record
|
|
||||||
pFeatureData = strchr(pFeatureData,'\n') + 1;
|
|
||||||
//increment the list to the start of the next storage block
|
|
||||||
psFeature++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -216,11 +153,13 @@ BOOL loadFeatureStats(const char *pFeatureData, UDWORD bufferSize)
|
||||||
/* Release the feature stats memory */
|
/* Release the feature stats memory */
|
||||||
void featureStatsShutDown(void)
|
void featureStatsShutDown(void)
|
||||||
{
|
{
|
||||||
if(numFeatureStats)
|
for (unsigned i = 0; i < numFeatureStats; ++i)
|
||||||
{
|
{
|
||||||
free(asFeatureStats);
|
free(asFeatureStats[i].pName);
|
||||||
asFeatureStats = NULL;
|
|
||||||
}
|
}
|
||||||
|
delete[] asFeatureStats;
|
||||||
|
asFeatureStats = NULL;
|
||||||
|
numFeatureStats = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deals with damage to a feature
|
/** Deals with damage to a feature
|
||||||
|
|
|
@ -60,6 +60,9 @@ typedef enum _feature_type
|
||||||
/* Stats for a feature */
|
/* Stats for a feature */
|
||||||
struct FEATURE_STATS : public BASE_STATS
|
struct FEATURE_STATS : public BASE_STATS
|
||||||
{
|
{
|
||||||
|
FEATURE_STATS() {}
|
||||||
|
FEATURE_STATS(LineView line);
|
||||||
|
|
||||||
FEATURE_TYPE subType; ///< type of feature
|
FEATURE_TYPE subType; ///< type of feature
|
||||||
|
|
||||||
iIMDShape* psImd; ///< Graphic for the feature
|
iIMDShape* psImd; ///< Graphic for the feature
|
||||||
|
|
223
src/stats.cpp
223
src/stats.cpp
|
@ -24,6 +24,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "lib/framework/frame.h"
|
#include "lib/framework/frame.h"
|
||||||
#include "lib/framework/strres.h"
|
#include "lib/framework/strres.h"
|
||||||
|
@ -123,6 +124,228 @@ static void updateMaxECMStats(UWORD maxValue);
|
||||||
static void updateMaxBodyStats(UWORD maxBody, UWORD maxPower, UWORD maxArmour);
|
static void updateMaxBodyStats(UWORD maxBody, UWORD maxPower, UWORD maxArmour);
|
||||||
static void updateMaxConstStats(UWORD maxValue);
|
static void updateMaxConstStats(UWORD maxValue);
|
||||||
|
|
||||||
|
|
||||||
|
BASE_STATS::BASE_STATS(unsigned ref, std::string const &str)
|
||||||
|
: ref(ref)
|
||||||
|
, pName(allocateName(str.c_str()))
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
TableView::TableView(char const *buffer, unsigned size)
|
||||||
|
: buffer(buffer)
|
||||||
|
{
|
||||||
|
size = std::min<unsigned>(size, UINT32_MAX - 1); // Shouldn't be a problem...
|
||||||
|
|
||||||
|
char const *bufferEnd = buffer + size;
|
||||||
|
bufferEnd = std::find(buffer, bufferEnd, '\0'); // Truncate buffer at first null character, if any.
|
||||||
|
|
||||||
|
// Split into lines.
|
||||||
|
char const *lineNext = buffer;
|
||||||
|
while (lineNext != bufferEnd)
|
||||||
|
{
|
||||||
|
char const *lineBegin = lineNext;
|
||||||
|
char const *lineEnd = std::find(lineBegin, bufferEnd, '\n');
|
||||||
|
lineNext = lineEnd + (lineEnd != bufferEnd);
|
||||||
|
|
||||||
|
// Remove stuff that isn't data.
|
||||||
|
lineEnd = std::find(lineBegin, lineEnd, '#'); // Remove comments, if present.
|
||||||
|
while (lineBegin != lineEnd && uint8_t(*(lineEnd - 1)) <= ' ')
|
||||||
|
{
|
||||||
|
--lineEnd; // Remove trailing whitespace, if present.
|
||||||
|
}
|
||||||
|
while (lineBegin != lineEnd && uint8_t(*lineBegin) <= ' ')
|
||||||
|
{
|
||||||
|
++lineBegin; // Remove leading whitespace, if present.
|
||||||
|
}
|
||||||
|
if (lineBegin == lineEnd)
|
||||||
|
{
|
||||||
|
continue; // Remove empty lines.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into cells.
|
||||||
|
size_t firstCell = cells.size();
|
||||||
|
char const *cellNext = lineBegin;
|
||||||
|
while (cellNext != lineEnd)
|
||||||
|
{
|
||||||
|
char const *cellBegin = cellNext;
|
||||||
|
char const *cellEnd = std::find(cellBegin, lineEnd, ',');
|
||||||
|
cellNext = cellEnd + (cellEnd != lineEnd);
|
||||||
|
|
||||||
|
cells.push_back(cellBegin - buffer);
|
||||||
|
}
|
||||||
|
cells.push_back(lineEnd - buffer + 1);
|
||||||
|
lines.push_back(std::make_pair(firstCell, cells.size() - firstCell));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineView::setError(unsigned index, char const *error)
|
||||||
|
{
|
||||||
|
if (!table.parseError.isEmpty())
|
||||||
|
{
|
||||||
|
return; // Already have an error.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < numCells)
|
||||||
|
{
|
||||||
|
char const *cellBegin = table.buffer + cells[index];
|
||||||
|
char const *cellEnd = table.buffer + (cells[index + 1] - 1);
|
||||||
|
|
||||||
|
char cellDesc[150];
|
||||||
|
ssprintf(cellDesc, "Line %u, column %u \"%*s\": ", lineNumber, index, std::min<unsigned>(100, cellEnd - cellBegin), cellBegin);
|
||||||
|
table.parseError = QString::fromUtf8((std::string(cellDesc) + error).c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char cellDesc[50];
|
||||||
|
ssprintf(cellDesc, "Line %u, column %u: ", lineNumber, index);
|
||||||
|
table.parseError = QString::fromUtf8((std::string(cellDesc) + error).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LineView::checkRange(unsigned index)
|
||||||
|
{
|
||||||
|
if (index < numCells)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setError(index, "Not enough cells.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t LineView::i(unsigned index, int min, int max)
|
||||||
|
{
|
||||||
|
int errorReturn = std::min(std::max(0, min), max); // On error, return 0 if possible.
|
||||||
|
|
||||||
|
if (!checkRange(index))
|
||||||
|
{
|
||||||
|
return errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *cellBegin = table.buffer + cells[index];
|
||||||
|
char const *cellEnd = table.buffer + (cells[index + 1] - 1);
|
||||||
|
if (cellBegin != cellEnd)
|
||||||
|
{
|
||||||
|
bool negative = false;
|
||||||
|
switch (*cellBegin++)
|
||||||
|
{
|
||||||
|
case '-':
|
||||||
|
negative = true;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--cellBegin;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int64_t absolutePart = 0;
|
||||||
|
while (cellBegin != cellEnd && absolutePart < int64_t(1000000000)*1000000000)
|
||||||
|
{
|
||||||
|
unsigned digit = *cellBegin - '0';
|
||||||
|
if (digit > 9)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
absolutePart = absolutePart*10 + digit;
|
||||||
|
++cellBegin;
|
||||||
|
}
|
||||||
|
if (cellBegin == cellEnd)
|
||||||
|
{
|
||||||
|
int64_t result = negative? -absolutePart : absolutePart;
|
||||||
|
if (result >= min && result <= max)
|
||||||
|
{
|
||||||
|
return result; // Maybe should just have copied the string to null-terminate it, and used scanf...
|
||||||
|
}
|
||||||
|
setError(index, "Integer out of range.");
|
||||||
|
return errorReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setError(index, "Expected integer.");
|
||||||
|
return errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LineView::f(unsigned index, float min, float max)
|
||||||
|
{
|
||||||
|
float errorReturn = std::min(std::max(0.f, min), max); // On error, return 0 if possible.
|
||||||
|
|
||||||
|
std::string const &str = s(index);
|
||||||
|
if (!str.empty())
|
||||||
|
{
|
||||||
|
int parsed = 0;
|
||||||
|
float result;
|
||||||
|
sscanf(str.c_str(), "%f%n", &result, &parsed);
|
||||||
|
if ((unsigned)parsed == str.size())
|
||||||
|
{
|
||||||
|
if (result >= min && result <= max)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
setError(index, "Float out of range.");
|
||||||
|
return errorReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setError(index, "Expected float.");
|
||||||
|
return errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const &LineView::s(unsigned index)
|
||||||
|
{
|
||||||
|
if (!checkRange(index))
|
||||||
|
{
|
||||||
|
table.returnString.clear();
|
||||||
|
return table.returnString;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *cellBegin = table.buffer + cells[index];
|
||||||
|
char const *cellEnd = table.buffer + (cells[index + 1] - 1);
|
||||||
|
table.returnString.assign(cellBegin, cellEnd);
|
||||||
|
return table.returnString;
|
||||||
|
}
|
||||||
|
|
||||||
|
iIMDShape *LineView::imdShape(unsigned int index)
|
||||||
|
{
|
||||||
|
std::string const &str = s(index);
|
||||||
|
iIMDShape *result = (iIMDShape *)resGetData("IMD", str.c_str());
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
setError(index, "Expected PIE shape.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool stringToEnumFindFunction(std::pair<char const *, unsigned> const &a, char const *b)
|
||||||
|
{
|
||||||
|
return strcmp(a.first, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned LineView::eu(unsigned int index, std::vector<std::pair<char const *, unsigned> > const &map)
|
||||||
|
{
|
||||||
|
typedef std::vector<std::pair<char const *, unsigned> > M;
|
||||||
|
|
||||||
|
std::string const &str = s(index);
|
||||||
|
M::const_iterator i = std::lower_bound(map.begin(), map.end(), str.c_str(), stringToEnumFindFunction);
|
||||||
|
if (i != map.end() && strcmp(i->first, str.c_str()) == 0)
|
||||||
|
{
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't find it, give error and return 0.
|
||||||
|
if (table.parseError.isEmpty())
|
||||||
|
{
|
||||||
|
std::string values = "Expected one of";
|
||||||
|
for (i = map.begin(); i != map.end(); ++i)
|
||||||
|
{
|
||||||
|
values += std::string(" \"") + i->first + '"';
|
||||||
|
}
|
||||||
|
values = values + '.';
|
||||||
|
setError(index, "Expected one of");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Generic stats macros/functions
|
* Generic stats macros/functions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#ifndef __INCLUDED_SRC_STATS_H__
|
#ifndef __INCLUDED_SRC_STATS_H__
|
||||||
#define __INCLUDED_SRC_STATS_H__
|
#define __INCLUDED_SRC_STATS_H__
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "objectdef.h"
|
#include "objectdef.h"
|
||||||
|
|
||||||
/**************************************************************************************
|
/**************************************************************************************
|
||||||
|
|
|
@ -26,6 +26,84 @@
|
||||||
|
|
||||||
#include "lib/ivis_opengl/ivisdef.h"
|
#include "lib/ivis_opengl/ivisdef.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "lib/framework/utf.h" // For QString.
|
||||||
|
|
||||||
|
static inline bool stringToEnumSortFunction(std::pair<char const *, unsigned> const &a, std::pair<char const *, unsigned> const &b) { return strcmp(a.first, b.first) < 0; }
|
||||||
|
|
||||||
|
template <typename Enum>
|
||||||
|
struct StringToEnum
|
||||||
|
{
|
||||||
|
operator std::pair<char const *, unsigned>() const { return std::make_pair(string, value); }
|
||||||
|
|
||||||
|
char const * string;
|
||||||
|
Enum value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Enum>
|
||||||
|
struct StringToEnumMap : public std::vector<std::pair<char const *, unsigned> >
|
||||||
|
{
|
||||||
|
typedef std::vector<std::pair<char const *, unsigned> > V;
|
||||||
|
|
||||||
|
StringToEnumMap(StringToEnum<Enum> const entries[], unsigned numEntries) : V(entries, entries + numEntries) { std::sort(V::begin(), V::end(), stringToEnumSortFunction); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Read-only view of file data in "A,1,2\nB,3,4" format as a 2D array-like object. Note — does not copy the file data.
|
||||||
|
class TableView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TableView(char const *buffer, unsigned size);
|
||||||
|
|
||||||
|
bool isError() const { return parseError.isEmpty(); }
|
||||||
|
QString getError() const { return parseError; }
|
||||||
|
|
||||||
|
unsigned size() const { return lines.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class LineView;
|
||||||
|
char const * buffer;
|
||||||
|
std::vector<uint32_t> cells;
|
||||||
|
std::vector<std::pair<uint32_t, uint32_t> > lines;
|
||||||
|
QString parseError;
|
||||||
|
std::string returnString;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Read-only view of a line of file data in "A,1,2" format as an array-like object. Note — does not copy the file data.
|
||||||
|
class LineView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LineView(class TableView &table, unsigned lineNumber) : table(table), cells(&table.cells[table.lines.at(lineNumber).first]), numCells(table.lines.at(lineNumber).second), lineNumber(lineNumber) {} ///< This LineView is only valid for the lifetime of the TableView.
|
||||||
|
|
||||||
|
void setError(unsigned index, char const *error); ///< Only the first error is saved.
|
||||||
|
|
||||||
|
unsigned size() const { return numCells; }
|
||||||
|
unsigned line() const { return lineNumber; }
|
||||||
|
|
||||||
|
bool b(unsigned index) { return i(index, 0, 1); }
|
||||||
|
int64_t i(unsigned index, int min, int max);
|
||||||
|
uint32_t i8(unsigned index) { return i(index, INT8_MIN, INT8_MAX); }
|
||||||
|
uint32_t u8(unsigned index) { return i(index, 0, UINT8_MAX); }
|
||||||
|
uint32_t i16(unsigned index) { return i(index, INT16_MIN, INT16_MAX); }
|
||||||
|
uint32_t u16(unsigned index) { return i(index, 0, UINT16_MAX); }
|
||||||
|
uint32_t i32(unsigned index) { return i(index, INT32_MIN, INT32_MAX); }
|
||||||
|
uint32_t u32(unsigned index) { return i(index, 0, UINT32_MAX); }
|
||||||
|
float f(unsigned index, float min = -1.e30f, float max = 1.e30f);
|
||||||
|
std::string const &s(unsigned index);
|
||||||
|
template <typename Enum>
|
||||||
|
Enum e(unsigned index, StringToEnumMap<Enum> const &map) { return (Enum)eu(index, map); }
|
||||||
|
|
||||||
|
iIMDShape *imdShape(unsigned index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned eu(unsigned index, std::vector<std::pair<char const *, unsigned> > const &map);
|
||||||
|
bool checkRange(unsigned index);
|
||||||
|
class TableView & table;
|
||||||
|
unsigned const * cells;
|
||||||
|
unsigned numCells;
|
||||||
|
unsigned lineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if any types are added BEFORE 'COMP_BODY' - then Save/Load Game will have to be
|
if any types are added BEFORE 'COMP_BODY' - then Save/Load Game will have to be
|
||||||
altered since it loops through the components from 1->MAX_COMP
|
altered since it loops through the components from 1->MAX_COMP
|
||||||
|
@ -206,6 +284,12 @@ typedef enum TRAVEL_MEDIUM
|
||||||
/* Stats common to all stats structs */
|
/* Stats common to all stats structs */
|
||||||
struct BASE_STATS
|
struct BASE_STATS
|
||||||
{
|
{
|
||||||
|
BASE_STATS() : ref(0), pName(NULL) {} ///< Only initialised here when using new/delete! TODO Use new/delete only, not malloc()/free().
|
||||||
|
BASE_STATS(unsigned ref, std::string const &str); ///< Only initialised here when using new/delete! TODO Use new/delete only, not malloc()/free(). TODO Then pName could be a QString...
|
||||||
|
//Gah, too soon to add destructors to BASE_STATS, thanks to local temporaries that are copied with memcpy()... --- virtual ~BASE_STATS() { free(pName); } ///< pName is only freed here when using new/delete! TODO Use new/delete only, not malloc()/free().
|
||||||
|
//So this one isn't needed for now, maybe not ever. --- BASE_STATS(BASE_STATS const &stats) : ref(stats.ref), pName(strdup(stats.pName)) {} // TODO Not needed when pName is a QString...
|
||||||
|
//So this one isn't needed for now, maybe not ever. --- BASE_STATS const &operator =(BASE_STATS const &stats) { ref = stats.ref; free(pName); pName = strdup(stats.pName); return *this; } // TODO Not needed when pName is a QString...
|
||||||
|
|
||||||
UDWORD ref; /**< Unique ID of the item */
|
UDWORD ref; /**< Unique ID of the item */
|
||||||
char *pName; /**< pointer to the text id name (i.e. short language-independant name) */
|
char *pName; /**< pointer to the text id name (i.e. short language-independant name) */
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue