clean up map header handling

Older versions of ac did not obey the map header field "headersize".
When a map was written, only the "version" field was updated, even if
the new written header had a different size. Thanks to bigger headers
in map versions 7 & 8 (and now smaller headers again in 9), the headersize
of basically all official maps is bogus. This fix makes sure, only proper
headersizes are written. It also makes the game tolerate different (bigger)
headers - and keeps their content untouched. Future map format extensions
can use this to add header information without breaking existing map parsers.

This patch establishes a new map format 10, which is identical to format 9,
with the exception that the headersize field is guaranteed to be correct.
Format 10 also allows any amount of additional header data.

This patch also cleans up the transformation routines for old map entities.
This commit is contained in:
ac-stef
2014-01-27 23:01:23 +01:00
parent 6e4b23734f
commit c3d4713904
6 changed files with 94 additions and 70 deletions
+44 -5
View File
@@ -24,6 +24,41 @@ const char *numtime()
return numt;
}
static const uchar transformenttab[] = {
/* mapformat 1..5 6..7 */
/* 0 */ /* NOTUSED */ NOTUSED, /* NOTUSED */ NOTUSED,
/* 1 */ /* LIGHT */ LIGHT, /* LIGHT */ LIGHT,
/* 2 */ /* PLAYERSTART */ PLAYERSTART, /* PLAYERSTART */ PLAYERSTART,
/* 3 */ /* I_SHELLS */ I_AMMO, /* I_CLIPS */ I_CLIPS,
/* 4 */ /* I_BULLETS */ I_AMMO, /* I_AMMO */ I_AMMO,
/* 5 */ /* I_ROCKETS */ I_AMMO, /* I_GRENADE */ I_GRENADE,
/* 6 */ /* I_ROUNDS */ I_AMMO, /* I_HEALTH */ I_HEALTH,
/* 7 */ /* I_HEALTH */ I_HEALTH, /* I_ARMOUR */ I_ARMOUR,
/* 8 */ /* I_BOOST */ I_HEALTH, /* I_AKIMBO */ I_AKIMBO,
/* 9 */ /* I_GREENARMOUR */ I_HELMET, /* MAPMODEL */ MAPMODEL,
/* 10 */ /* I_YELLOWARMOUR */ I_ARMOUR, /* CARROT */ CARROT,
/* 11 */ /* I_QUAD */ I_AKIMBO, /* LADDER */ LADDER,
/* 12 */ /* TELEPORT */ NOTUSED, /* CTF_FLAG */ CTF_FLAG,
/* 13 */ /* TELEDEST */ NOTUSED, /* SOUND */ SOUND,
/* 14 */ /* MAPMODEL */ MAPMODEL, /* CLIP */ CLIP,
/* 15 */ /* MONSTER */ NOTUSED, /* PLCLIP */ PLCLIP,
/* 16 */ /* CARROT */ NOTUSED, 16,
/* 17 */ /* JUMPPAD */ NOTUSED, 17 };
void transformoldentities(int mapversion, uchar &enttype)
{
const uchar *usetab = transformenttab + (mapversion > 5 ? 1 : 0);
if(mapversion < 8 && enttype < 18) enttype = usetab[enttype * 2];
}
int fixmapheadersize(int version, int headersize) // we can't trust hdr.headersize for file versions < 10 (thx flow)
{
if(version < 4) return sizeof(header) - sizeof(int) * 16;
else if(version == 7 || version == 8) return sizeof(header) + sizeof(char) * 128; // mediareq
else if(version < 10 || headersize < int(sizeof(header))) return sizeof(header);
return headersize;
}
int mapdims[8]; // min/max X/Y and delta X/Y and min/max Z
extern char *maplayout, *testlayout;
@@ -33,6 +68,7 @@ extern int checkarea(int, char *);
mapstats *loadmapstats(const char *filename, bool getlayout)
{
const int sizeof_header = sizeof(header), sizeof_baseheader = sizeof_header - sizeof(int) * 16;
static mapstats s;
static uchar *enttypes = NULL;
static short *entposs = NULL;
@@ -44,11 +80,14 @@ mapstats *loadmapstats(const char *filename, bool getlayout)
stream *f = opengzfile(filename, "rb");
if(!f) return NULL;
memset(&s.hdr, 0, sizeof(header));
if(f->read(&s.hdr, sizeof(header)-sizeof(int)*16)!=sizeof(header)-sizeof(int)*16 || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { delete f; return NULL; }
memset(&s.hdr, 0, sizeof_header);
if(f->read(&s.hdr, sizeof_baseheader) != sizeof_baseheader || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { delete f; return NULL; }
lilswap(&s.hdr.version, 4);
if(s.hdr.version>MAPVERSION || s.hdr.numents > MAXENTITIES || (s.hdr.version>=4 && f->read(&s.hdr.waterlevel, sizeof(int)*16)!=sizeof(int)*16)) { delete f; return NULL; }
if((s.hdr.version==7 || s.hdr.version==8) && !f->seek(sizeof(char)*128, SEEK_CUR)) { delete f; return NULL; }
s.hdr.headersize = fixmapheadersize(s.hdr.version, s.hdr.headersize);
int restofhead = min(s.hdr.headersize, sizeof_header) - sizeof_baseheader;
if(s.hdr.version > MAXMAPVERSION || s.hdr.numents > MAXENTITIES ||
f->read(&s.hdr.waterlevel, restofhead) != restofhead ||
!f->seek(clamp(s.hdr.headersize - sizeof_header, 0, MAXHEADEREXTRA), SEEK_CUR)) { delete f; return NULL; }
if(s.hdr.version>=4)
{
lilswap(&s.hdr.waterlevel, 1);
@@ -62,7 +101,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout)
{
f->read(&e, sizeof(persistent_entity));
lilswap((short *)&e, 4);
TRANSFORMOLDENTITIES(s.hdr)
transformoldentities(s.hdr.version, e.type);
if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) s.spawns[e.attr2 == 100 ? 2 : e.attr2]++;
if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) { s.flags[e.attr2]++; s.flagents[e.attr2] = i; }
s.entcnt[e.type]++;