Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
117 results
Select Git revision
Show changes
Commits on Source (19)
......@@ -2159,14 +2159,14 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(increment > 0) // Going up!
{
newvalue++;
if (newvalue == NUMMAPS)
if (newvalue == numgamemaps)
newvalue = 0;
}
else // Going down!
{
newvalue--;
if (newvalue == -1)
newvalue = NUMMAPS-1;
newvalue = numgamemaps-1;
}
if (newvalue == oldvalue)
......@@ -2174,7 +2174,6 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(!mapheaderinfo[newvalue])
continue; // Don't allocate the header. That just makes memory usage skyrocket.
} while (newvalue != oldvalue && !M_CanShowLevelInList(newvalue, gt));
var->value = newvalue + 1;
......
......@@ -940,13 +940,7 @@ void D_StartTitle(void)
if (server)
{
char mapname[6];
strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname));
strlwr(mapname);
mapname[5] = '\0';
COM_BufAddText(va("map %s\n", mapname));
COM_BufAddText(va("map %s\n", G_BuildMapName(spstage_start)));
}
}
......@@ -1365,6 +1359,8 @@ void D_SRB2Main(void)
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();
G_InitMaps();
clientGamedata = M_NewGameDataStruct();
serverGamedata = M_NewGameDataStruct();
......@@ -1678,7 +1674,7 @@ void D_SRB2Main(void)
{
pstartmap = bootmap;
if (pstartmap < 1 || pstartmap > NUMMAPS)
if (pstartmap < 1 || pstartmap > numgamemaps)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
......@@ -1725,7 +1721,7 @@ void D_SRB2Main(void)
if (server && !M_CheckParm("+map"))
{
// Prevent warping to nonexistent levels
if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(pstartmap)))
I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
else
{
......
......@@ -168,7 +168,7 @@ void clear_levels(void)
// This is potentially dangerous but if we're resetting these headers,
// we may as well try to save some memory, right?
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || i == (tutorialmap-1))
continue;
......@@ -1352,6 +1352,39 @@ void readgametype(MYFILE *f, char *gtname)
CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
}
static INT32 ParseNextLevelName(const char *name)
{
if (fastcmp(name, "TITLE")) return NEXTMAP_TITLE;
else if (fastcmp(name, "EVALUATION")) return NEXTMAP_EVALUATION;
else if (fastcmp(name, "CREDITS")) return NEXTMAP_CREDITS;
else if (fastcmp(name, "ENDING")) return NEXTMAP_ENDING;
else
{
// Support using the actual map name,
// i.e., Nextlevel = AB, Nextlevel = FZ, etc.
// Convert to map number
return G_GetMapNumber(name);
}
}
static INT32 ConvertLevelHeaderMapNum(INT32 mapnum)
{
switch (mapnum)
{
case 1100:
return NEXTMAP_TITLE;
case 1101:
return NEXTMAP_EVALUATION;
case 1102:
return NEXTMAP_CREDITS;
case 1103:
return NEXTMAP_ENDING;
default:
return mapnum;
}
}
void readlevelheader(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
......@@ -1568,33 +1601,17 @@ void readlevelheader(MYFILE *f, INT32 num)
}
else if (fastcmp(word, "NEXTLEVEL"))
{
if (fastcmp(word2, "TITLE")) i = 1100;
else if (fastcmp(word2, "EVALUATION")) i = 1101;
else if (fastcmp(word2, "CREDITS")) i = 1102;
else if (fastcmp(word2, "ENDING")) i = 1103;
else
// Support using the actual map name,
// i.e., Nextlevel = AB, Nextlevel = FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
i = M_MapNumber(word2[0], word2[1]);
i = ConvertLevelHeaderMapNum(i);
if (i == 0)
i = ParseNextLevelName(word2);
mapheaderinfo[num-1]->nextlevel = (INT16)i;
}
else if (fastcmp(word, "MARATHONNEXT"))
{
if (fastcmp(word2, "TITLE")) i = 1100;
else if (fastcmp(word2, "EVALUATION")) i = 1101;
else if (fastcmp(word2, "CREDITS")) i = 1102;
else if (fastcmp(word2, "ENDING")) i = 1103;
else
// Support using the actual map name,
// i.e., MarathonNext = AB, MarathonNext = FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
i = M_MapNumber(word2[0], word2[1]);
i = ConvertLevelHeaderMapNum(i);
if (i == 0)
i = ParseNextLevelName(word2);
mapheaderinfo[num-1]->marathonnext = (INT16)i;
}
......@@ -2009,8 +2026,7 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7);
cutscenes[num]->scene[scenenum].musswitch[6] = 0;
strlcpy(cutscenes[num]->scene[scenenum].musswitch, word2, MAX_MUSIC_NAME+1);
}
else if (fastcmp(word, "MUSICTRACK"))
{
......@@ -2271,8 +2287,7 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7);
textprompts[num]->page[pagenum].musswitch[6] = 0;
strlcpy(textprompts[num]->page[pagenum].musswitch, word2, MAX_MUSIC_NAME+1);
}
else if (fastcmp(word, "MUSICTRACK"))
{
......@@ -2595,8 +2610,7 @@ void readmenu(MYFILE *f, INT32 num)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(menupres[num].musname, word2, 7);
menupres[num].musname[6] = 0;
strlcpy(menupres[num].musname, word2, MAX_MUSIC_NAME+1);
titlechanged = true;
}
else if (fastcmp(word, "MUSICTRACK"))
......@@ -3010,12 +3024,10 @@ void reademblemdata(MYFILE *f, INT32 num)
emblemlocations[num-1].tag = (INT16)value;
else if (fastcmp(word, "MAPNUM"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
emblemlocations[num-1].level = (INT16)value;
}
......@@ -3257,6 +3269,12 @@ void readunlockable(MYFILE *f, INT32 num)
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
else if (unlockables[num].type == SECRET_WARP)
{
INT16 mapnum = G_GetMapNumber(word2);
if (mapnum)
i = mapnum;
}
unlockables[num].variable = (INT16)i;
}
......@@ -3330,15 +3348,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
PARAMCHECK(1);
ty = UC_MAPVISITED + offset;
// Convert to map number if it appears to be one
if (params[1][0] >= 'A' && params[1][0] <= 'Z')
re = M_MapNumber(params[1][0], params[1][1]);
else
// Convert to map number
re = G_GetMapNumber(params[1]);
if (!re)
re = atoi(params[1]);
if (re <= 0 || re > NUMMAPS)
if (re <= 0 || re > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
}
......@@ -3350,15 +3367,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
ty = UC_MAPSCORE + offset;
re = atoi(params[2]);
// Convert to map number if it appears to be one
if (params[1][0] >= 'A' && params[1][0] <= 'Z')
x1 = (INT16)M_MapNumber(params[1][0], params[1][1]);
else
// Convert to map number
x1 = G_GetMapNumber(params[1]);
if (!x1)
x1 = (INT16)atoi(params[1]);
if (x1 <= 0 || x1 > NUMMAPS)
if (x1 <= 0 || x1 > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
}
......@@ -3385,15 +3401,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
else
re = atoi(params[i]);
// Convert to map number if it appears to be one
if (params[1][0] >= 'A' && params[1][0] <= 'Z')
x1 = (INT16)M_MapNumber(params[1][0], params[1][1]);
else
// Convert to map number
x1 = G_GetMapNumber(params[1]);
if (!x1)
x1 = (INT16)atoi(params[1]);
if (x1 <= 0 || x1 > NUMMAPS)
if (x1 <= 0 || x1 > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
......@@ -3603,9 +3618,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
spstage_start = spmarathon_start = (INT16)value;
......@@ -3616,9 +3631,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
spmarathon_start = (INT16)value;
......@@ -3629,9 +3644,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
sstage_start = (INT16)value;
......@@ -3643,9 +3658,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
smpstage_start = (INT16)value;
......@@ -3737,9 +3752,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
titlemap = (INT16)value;
......@@ -3909,13 +3924,12 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
bootmap = (INT16)value;
//titlechanged = true;
}
else if (fastcmp(word, "STARTCHAR"))
{
......@@ -3928,9 +3942,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
tutorialmap = (INT16)value;
......
......@@ -372,18 +372,32 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
}
else if (fastcmp(word, "LEVEL"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
boolean is_valid_level = false;
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
if (G_IsValidMapName(word2))
{
INT16 mapnum = G_GetMapNumber(word2);
if (mapnum != 0)
{
i = mapnum;
is_valid_level = true;
}
}
else
{
is_valid_level = true;
}
if (i > 0 && i <= NUMMAPS)
if (!is_valid_level)
{
deh_warning("Unknown level %s", word2);
ignorelines(f);
}
else if (i > 0 && i <= numgamemaps)
readlevelheader(f, i);
else
{
deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", i, numgamemaps);
ignorelines(f);
}
}
......
......@@ -32,23 +32,6 @@
// used in the lumps of the WAD files.
//
// Lump order in a map WAD: each map needs a couple of lumps
// to provide a complete scene geometry description.
enum
{
ML_LABEL, // A separator, name, MAPxx
ML_THINGS, // Enemies, rings, monitors, scenery, etc.
ML_LINEDEFS, // Linedefs, from editing
ML_SIDEDEFS, // Sidedefs, from editing
ML_VERTEXES, // Vertices, edited and BSP splits generated
ML_SEGS, // Linesegs, from linedefs split by BSP
ML_SSECTORS, // Subsectors, list of linesegs
ML_NODES, // BSP nodes
ML_SECTORS, // Sectors, from editing
ML_REJECT, // LUT, sector-sector visibility
ML_BLOCKMAP, // LUT, motion clipping, walls/grid element
};
// Extra flag for objects.
#define MTF_EXTRA 1
......@@ -223,6 +206,4 @@ typedef struct
#define ZSHIFT 4
#define NUMMAPS 1035
#endif // __DOOMDATA__
......@@ -252,6 +252,19 @@ extern char logfilename[1024];
#define MAXSKINS 255
#define MAXCHARACTERSLOTS (MAXSKINS * 3) // Should be higher than MAXSKINS.
#define MAXMAPS 16386
#define MAX_MAP_NAME_SIZE 256 // This is an arbitrary limit to prevent exceedingly long map names.
#define NEXTMAP_TITLE (MAXMAPS)
#define NEXTMAP_EVALUATION (MAXMAPS+2)
#define NEXTMAP_CREDITS (MAXMAPS+3)
#define NEXTMAP_ENDING (MAXMAPS+4)
#define NUM_NEXTMAPS 4
#define NUMBASEMAPS 1035 // MAP01 to MAPZZ
#define COLORRAMPSIZE 16
#define MAXCOLORNAME 32
#define NUMCOLORFREESLOTS 1024
......@@ -466,6 +479,8 @@ extern skincolor_t skincolors[MAXSKINCOLORS];
#define MUSICRATE 1000 // sound timing is calculated by milliseconds
#define MAX_MUSIC_NAME 64
#define RING_DIST 512*FRACUNIT // how close you need to be to a ring to attract it
#define PUSHACCEL (2*FRACUNIT) // Acceleration for MF2_SLIDEPUSH items.
......
......@@ -31,7 +31,7 @@
// Selected by user.
extern INT16 gamemap;
extern char mapmusname[7];
extern char mapmusname[MAX_MUSIC_NAME+1];
extern UINT16 mapmusflags;
extern UINT32 mapmusposition;
#define MUSIC_TRACKMASK 0x0FFF // ----************
......@@ -79,6 +79,7 @@ extern boolean usedCheats;
extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true
extern boolean imcontinuing; // Temporary flag while continuing
extern boolean metalrecording;
extern boolean replacedcurrentmap;
#define ATTACKING_NONE 0
#define ATTACKING_RECORD 1
......@@ -175,7 +176,7 @@ typedef struct
UINT16 textxpos;
UINT16 textypos;
char musswitch[7];
char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags;
UINT32 musswitchposition;
......@@ -215,7 +216,7 @@ typedef struct
UINT16 ycoord[MAX_PROMPT_PICS]; // gfx
UINT16 picduration[MAX_PROMPT_PICS];
char musswitch[7];
char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags;
UINT8 musicloop;
......@@ -277,6 +278,23 @@ extern struct quake
fixed_t radius, intensity;
} quake;
typedef struct
{
UINT32 hash;
size_t length;
char *chars;
} mapname_t;
typedef struct
{
mapname_t name;
UINT32 lumpnum;
char *thumbnail;
char *thumbnail_wide;
char *music;
char *metal_replay;
} gamemap_t;
// NiGHTS grades
typedef struct
{
......@@ -300,10 +318,10 @@ typedef struct
char subttl[33]; ///< Subtitle for level
UINT8 actnum; ///< Act number or 0 for none.
UINT32 typeoflevel; ///< Combination of typeoflevel flags.
INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end.
INT16 nextlevel; ///< Map number of next level, or NEXTMAP_* to end.
INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI.
char keywords[33]; ///< Keywords separated by space to search for. 32 characters.
char musname[7]; ///< Music track to play. "" for no music.
char musname[MAX_MUSIC_NAME+1]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
UINT32 muspos; ///< Music position to jump to.
char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable.
......@@ -351,9 +369,9 @@ typedef struct
// Music stuff.
UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds
char musintername[7]; ///< Intermission screen music.
char musintername[MAX_MUSIC_NAME+1]; ///< Intermission screen music.
char muspostbossname[7]; ///< Post-bossdeath music.
char muspostbossname[MAX_MUSIC_NAME+1]; ///< Post-bossdeath music.
UINT16 muspostbosstrack; ///< Post-bossdeath track.
UINT32 muspostbosspos; ///< Post-bossdeath position
UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds.
......@@ -388,7 +406,11 @@ typedef struct
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
#define LF2_WIDEICON 32 ///< If you're in a circumstance where it fits, use a wide map icon
extern mapheader_t* mapheaderinfo[NUMMAPS];
extern mapheader_t* mapheaderinfo[MAXMAPS];
extern gamemap_t gamemaps[MAXMAPS];
extern UINT16 numgamemaps;
// Gametypes
#define NUMGAMETYPEFREESLOTS 128
......
......@@ -3917,7 +3917,7 @@ void F_EndCutScene(void)
F_StartGameEvaluation();
else if (cutnum == introtoplay-1)
D_StartTitle();
else if (nextmap < 1100-1)
else if (!G_IsGameEndMap(nextmap+1))
G_NextLevel();
else
G_EndGame();
......
......@@ -849,6 +849,8 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
if (strstr(fullname, path))
fullname += strlen(path) + 1;
size_t fullnamelength = strlen(fullname);
// Get the 8-character long lump name.
trimname = strrchr(fullname, '/');
if (trimname)
......@@ -860,21 +862,30 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
{
char *dotpos = strrchr(trimname, '.');
if (dotpos == NULL)
dotpos = fullname + strlen(fullname);
dotpos = fullname + fullnamelength;
strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
// The name of the file, without the extension.
lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL);
strlcpy(lump_p->longname, trimname, dotpos - trimname + 1);
lump_p->longnamelength = strlen(lump_p->longname);
}
else
else {
lump_p->longname = Z_Calloc(1, PU_STATIC, NULL);
lump_p->longnamelength = 0;
}
// The complete name of the file, with its extension,
// excluding the path of the directory where it resides.
lump_p->fullname = Z_StrDup(fullname);
lump_p->hash = quickncasehash(lump_p->name, 8);
lump_p->hash.name = W_HashLumpName(lump_p->name);
lump_p->hash.fullname = W_HashLumpName(lump_p->fullname);
lump_p->hash.longname = W_HashLumpName(lump_p->longname);
lump_p->namelength = strlen(lump_p->name);
lump_p->fullnamelength = fullnamelength;
lump_p++;
i++;
......
......@@ -46,7 +46,7 @@ boolean nodrawers; // for comparative timing purposes
boolean noblit; // for comparative timing purposes
tic_t demostarttime; // for comparative timing purposes
static char demoname[64];
static char demoname[512];
boolean demorecording;
boolean demoplayback;
boolean titledemo; // Title Screen demo can be cancelled by any key
......@@ -1405,15 +1405,14 @@ void G_WriteMetalTic(mobj_t *metal)
//
void G_RecordDemo(const char *name)
{
INT32 maxsize;
strlcpy(demoname, name, sizeof(demoname));
strcpy(demoname, name);
strcat(demoname, ".lmp");
maxsize = 1024*1024;
FIL_ForceExtension(demoname, ".lmp");
INT32 maxsize = 1024*1024;
if (M_CheckParm("-maxdemo") && M_IsNextParm())
maxsize = atoi(M_GetNextParm()) * 1024;
// if (demobuffer)
// free(demobuffer);
demo_p = NULL;
demobuffer = malloc(maxsize);
demoend = demobuffer + maxsize;
......@@ -2654,7 +2653,7 @@ void G_DoPlayMetal(void)
thinker_t *th;
// it's an internal demo
if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR)
if ((l = W_CheckNumForName(G_GetMapMetalSonicReplay(gamemap))) == LUMPERROR)
{
CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n"));
return;
......@@ -2780,7 +2779,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
{
WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker
WriteDemoChecksum();
sprintf(demoname, "%sMS.LMP", G_BuildMapName(gamemap));
snprintf(demoname, sizeof(demoname), "%s.lmp", G_GetMapMetalSonicReplay(gamemap));
saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
}
free(demobuffer);
......
......@@ -78,7 +78,7 @@ static void G_DoStartContinue(void);
static void G_DoContinued(void);
static void G_DoWorldDone(void);
char mapmusname[7]; // Music name
char mapmusname[MAX_MUSIC_NAME+1]; // Music name
UINT16 mapmusflags; // Track and reset bit
UINT32 mapmusposition; // Position to jump to
......@@ -105,6 +105,7 @@ UINT8 paused;
UINT8 modeattacking = ATTACKING_NONE;
boolean disableSpeedAdjust = false;
boolean imcontinuing = false;
boolean replacedcurrentmap = false;
boolean runemeraldmanager = false;
UINT16 emeraldspawndelay = 60*TICRATE;
......@@ -172,7 +173,13 @@ mapthing_t *bflagpoint;
struct quake quake;
// Map Header Information
mapheader_t* mapheaderinfo[NUMMAPS] = {NULL};
mapheader_t* mapheaderinfo[MAXMAPS] = {NULL};
gamemap_t gamemaps[MAXMAPS];
UINT16 numgamemaps = 0;
static mapname_t nextmapnames[NUM_NEXTMAPS];
static INT16 nextmapids[NUM_NEXTMAPS] = { NEXTMAP_TITLE, NEXTMAP_EVALUATION, NEXTMAP_CREDITS, NEXTMAP_ENDING };
static boolean exitgame = false;
static boolean retrying = false;
......@@ -463,7 +470,7 @@ void G_AllocNightsRecordData(INT16 i, gamedata_t *data)
void G_ClearRecords(gamedata_t *data)
{
INT16 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (data->mainrecords[i])
{
......@@ -571,7 +578,8 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player)
if (modeattacking)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(gamemap);
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char *gpath;
char lastdemo[256], bestdemo[256];
......@@ -587,7 +595,7 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, mapname);
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name);
if (FIL_FileExists(lastdemo))
......@@ -709,7 +717,8 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player)
if (modeattacking)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(gamemap);
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char *gpath;
char lastdemo[256], bestdemo[256];
......@@ -725,7 +734,7 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, mapname);
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name);
if (FIL_FileExists(lastdemo))
......@@ -803,6 +812,20 @@ void G_SetUsedCheats(boolean silent)
Command_ExitGame_f();
}
/** Gets a game map name from a map number.
*
* \param map Map number.
* \return The desired map name.
* \sa M_MapNumber
*/
const char *G_BuildMapName(INT32 map)
{
if (map > 0 && map <= numgamemaps)
return gamemaps[map - 1].name.chars;
return "UNKNOWN";
}
/** Builds an original game map name from a map number.
* The complexity is due to MAPA0-MAPZZ.
*
......@@ -810,12 +833,12 @@ void G_SetUsedCheats(boolean silent)
* \return Pointer to a static buffer containing the desired map name.
* \sa M_MapNumber
*/
const char *G_BuildMapName(INT32 map)
const char *G_BuildClassicMapName(INT32 map)
{
static char mapname[10] = "MAPXX"; // internal map name (wad resource name)
I_Assert(map > 0);
I_Assert(map <= NUMMAPS);
I_Assert(map <= NUMBASEMAPS);
if (map < 100)
sprintf(&mapname[3], "%.2d", map);
......@@ -832,6 +855,219 @@ const char *G_BuildMapName(INT32 map)
return mapname;
}
static UINT32 G_HashMapNameString(const char *name)
{
return FNV1a_HashLowercaseString(name);
}
static void G_MakeMapName(mapname_t *name, const char *string)
{
name->length = strlen(string);
name->hash = G_HashMapNameString(string);
name->chars = Z_StrDup(string);
strupr(name->chars);
}
void G_InitMaps(void)
{
for (UINT16 i = 0; i < NUMBASEMAPS; i++)
{
const char *name = G_BuildClassicMapName(i + 1);
G_AddMap(name, LUMPERROR);
}
G_MakeMapName(&nextmapnames[0], "SCENE_TITLE");
G_MakeMapName(&nextmapnames[1], "SCENE_EVALUATION");
G_MakeMapName(&nextmapnames[2], "SCENE_CREDITS");
G_MakeMapName(&nextmapnames[3], "SCENE_ENDING");
}
static UINT16 MapIDForHashedString(const char *name, size_t name_length, UINT32 name_hash)
{
// Special case
if (name_length == 2 && name[0] >= 'A' && name[0] <= 'Z')
return M_MapNumber(name[0], name[1]);
for (UINT16 i = 0; i < numgamemaps; i++)
{
if (gamemaps[i].name.length == name_length
&& gamemaps[i].name.hash == name_hash
&& stricmp(name, gamemaps[i].name.chars) == 0)
return i + 1;
}
return 0;
}
UINT16 G_GetMapNumber(const char *name)
{
size_t name_length = strlen(name);
return MapIDForHashedString(name, name_length, G_HashMapNameString(name));
}
UINT16 G_GetMapNumberForNextMap(const char *name)
{
size_t name_length = strlen(name);
UINT32 name_hash = G_HashMapNameString(name);
for (UINT16 i = 0; i < NUM_NEXTMAPS; i++)
{
if (nextmapnames[i].length == name_length
&& nextmapnames[i].hash == name_hash
&& stricmp(name, nextmapnames[i].chars) == 0)
return nextmapids[i];
}
return MapIDForHashedString(name, name_length, name_hash);
}
UINT16 G_AddMap(const char *name, UINT32 lumpnum)
{
// Too many maps loaded
if (numgamemaps == MAXMAPS)
return 0;
UINT16 mapnum = G_GetMapNumber(name);
if (mapnum != 0)
{
// Update that map's lumpnum
gamemaps[mapnum - 1].lumpnum = lumpnum;
return mapnum;
}
G_MakeMapName(&gamemaps[numgamemaps].name, name);
gamemaps[numgamemaps].lumpnum = lumpnum;
numgamemaps++;
CONS_Debug(DBG_SETUP, "Added map %d (%s)\n", numgamemaps, name);
return numgamemaps;
}
lumpnum_t G_GetMapLumpnum(const char *name)
{
UINT16 mapnum = G_GetMapNumber(name);
if (mapnum == 0)
return LUMPERROR;
return gamemaps[mapnum - 1].lumpnum;
}
boolean G_MapFileExists(const char *name)
{
UINT16 mapnum = G_GetMapNumber(name);
if (mapnum == 0)
return false;
return gamemaps[mapnum - 1].lumpnum != LUMPERROR;
}
static boolean IsValidMapNameStartChar(const char chr)
{
return isalpha(chr) || chr == '_' || chr == '$';
}
boolean G_IsValidMapName(const char *name)
{
// Can't be empty, and must begin with a letter, an underscore, or a dollar sign
if (name[0] == '\0' || !IsValidMapNameStartChar(name[0]))
return false;
size_t length = strlen(name);
// Middle and end of name must be a letter, a digit, an underscore, or a dollar sign
for (size_t i = 1; i < length; i++)
{
if (!(IsValidMapNameStartChar(name[i]) || isdigit(name[i])))
return false;
}
return true;
}
static char *BuildCombinedMapString(INT16 map, const char *newfmt, const char *oldfmt)
{
const char *mapname = gamemaps[map].name.chars;
const char *fmt = (map < NUMBASEMAPS) ? oldfmt : newfmt;
size_t len = strlen(mapname) + strlen(fmt) + 1;
char *text = Z_Malloc(len, PU_STATIC, NULL);
snprintf(text, len, fmt, mapname);
return text;
}
const char *G_GetMapThumbnail(INT16 map)
{
map--;
if (map < 0 || map >= numgamemaps)
return "";
// This is done lazily -- it's not created until it's needed.
if (gamemaps[map].thumbnail == NULL)
gamemaps[map].thumbnail = BuildCombinedMapString(map, "%s_PIC", "%sP");
return gamemaps[map].thumbnail;
}
const char *G_GetMapThumbnailWide(INT16 map)
{
map--;
if (map < 0 || map >= numgamemaps)
return "";
if (gamemaps[map].thumbnail_wide == NULL)
gamemaps[map].thumbnail_wide = BuildCombinedMapString(map, "%s_WIDEPIC", "%sW");
return gamemaps[map].thumbnail_wide;
}
const char *G_GetDefaultMapMusic(INT16 map)
{
map--;
if (map < 0 || map >= numgamemaps)
return "";
if (gamemaps[map].music == NULL)
gamemaps[map].music = BuildCombinedMapString(map, "%s", "%sM");
return gamemaps[map].music;
}
const char *G_GetMapMetalSonicReplay(INT16 map)
{
map--;
if (map < 0 || map >= numgamemaps)
return "";
if (gamemaps[map].metal_replay == NULL)
gamemaps[map].metal_replay = BuildCombinedMapString(map, "%s_METALREPLAY", "%sMS");
return gamemaps[map].metal_replay;
}
boolean G_IsGameEndMap(INT16 mapnum)
{
switch (mapnum)
{
case NEXTMAP_TITLE:
case NEXTMAP_EVALUATION:
case NEXTMAP_CREDITS:
case NEXTMAP_ENDING:
return true;
default:
return false;
}
}
/** Clips the console player's mouse aiming to the current view.
* Used whenever the player view is changed manually.
*
......@@ -1853,7 +2089,7 @@ void G_DoLoadLevel(boolean resetplayer)
// cleanup
if (titlemapinaction == TITLEMAP_LOADING)
{
if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(gamemap)))
{
titlemap = 0; // let's not infinite recursion ok
Command_ExitGame_f();
......@@ -2770,8 +3006,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
{
if (mapmusflags & MUSIC_RELOADRESET)
{
strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
mapmusname[6] = 0;
strlcpy(mapmusname, mapheaderinfo[gamemap-1]->musname, MAX_MUSIC_NAME+1);
mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
mapmusposition = mapheaderinfo[gamemap-1]->muspos;
}
......@@ -3847,12 +4082,12 @@ UINT32 G_TOLFlag(INT32 pgametype)
*/
static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
{
INT16 *okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL);
INT16 *okmaps = Z_Malloc(numgamemaps * sizeof(INT16), PU_STATIC, NULL);
INT32 numokmaps = 0;
INT16 ix;
// Find all the maps that are ok and and put them in an array.
for (ix = 0; ix < NUMMAPS; ix++)
for (ix = 0; ix < numgamemaps; ix++)
if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
&& ix != pprevmap // Don't pick the same map.
&& (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
......@@ -3974,7 +4209,7 @@ static void G_HandleSaveLevel(void)
G_UpdateAllVisited();
// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
if (nextmap >= 1100-1)
if (nextmap >= NEXTMAP_TITLE-1)
{
if (!gamecomplete)
gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
......@@ -4020,7 +4255,7 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
{
newmapnum = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
if (marathonmode && newmapnum == spmarathon_start-1)
newmapnum = 1100-1; // No infinite loop for you
newmapnum = NEXTMAP_TITLE-1; // No infinite loop for you
}
INT16 gametype_to_use;
......@@ -4036,13 +4271,11 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
// for instance).
if (!spec || nextmapoverride)
{
if (newmapnum >= 0 && newmapnum < NUMMAPS)
if (newmapnum >= 0 && newmapnum < numgamemaps)
{
INT16 cm = newmapnum;
UINT32 tolflag = G_TOLFlag(gametype_to_use);
UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap));
UINT8 *visitedmap = Z_Calloc(((numgamemaps+7)/8) * sizeof(UINT8), PU_STATIC, NULL);
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{
......@@ -4054,12 +4287,12 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
if (cm >= numgamemaps || cm < 0) // out of range (either NEXTMAP_* or error)
{
cm = newmapnum; //Start the loop again so that the error checking below is executed.
//Make sure the map actually exists before you try to go to it!
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
if (!G_MapFileExists(G_BuildMapName(cm + 1)))
{
if (!silent)
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
......@@ -4078,14 +4311,17 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
break;
}
}
Z_Free(visitedmap);
newmapnum = cm;
}
// wrap around in race
if (newmapnum >= 1100-1 && newmapnum <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
if (G_IsGameEndMap(newmapnum+1) && !(gametyperules & GTR_CAMPAIGN))
newmapnum = (INT16)(spstage_start-1);
if (newmapnum < 0 || (newmapnum >= NUMMAPS && newmapnum < 1100-1) || newmapnum > 1103-1)
if (newmapnum < 0 || (newmapnum >= numgamemaps && !G_IsGameEndMap(newmapnum+1)))
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, newmapnum + 1);
if (!spec)
......@@ -4158,14 +4394,15 @@ static void G_DoCompleted(void)
//Get and set prevmap/nextmap
prevmap = (INT16)(gamemap-1);
nextmap = G_GetNextMap(false, false);
automapactive = false;
// We are committed to this map now.
// We may as well allocate its header if it doesn't exist
// (That is, if it's a real map)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
if (nextmap < numgamemaps && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap);
Y_DetermineIntermissionType();
......@@ -4211,7 +4448,7 @@ void G_AfterIntermission(void)
}
else
{
if (nextmap < 1100-1)
if (!G_IsGameEndMap(nextmap+1))
G_NextLevel();
else
G_EndGame();
......@@ -4331,24 +4568,21 @@ void G_EndGame(void)
// Only do evaluation and credits in coop games.
if (gametyperules & GTR_CUTSCENES)
{
if (nextmap == 1103-1) // end game with ending
switch (nextmap+1)
{
case NEXTMAP_ENDING:
F_StartEnding();
return;
}
if (nextmap == 1102-1) // end game with credits
{
case NEXTMAP_CREDITS:
F_StartCredits();
return;
}
if (nextmap == 1101-1) // end game with evaluation
{
case NEXTMAP_EVALUATION:
F_StartGameEvaluation();
return;
}
}
// 1100 or competitive multiplayer, so go back to title screen.
// NEXTMAP_TITLE or competitive multiplayer, so go back to title screen.
D_StartTitle();
}
......@@ -4372,6 +4606,62 @@ void G_LoadGameSettings(void)
#define GAMEDATA_ID 0x86E4A27C // Change every major version, as usual
#define COMPAT_GAMEDATA_ID 0xFCAFE211 // TODO: 2.3: Delete
#define EXTRA_DATA_MARKER 0x71B9F853 // TODO: 2.3: Refactor all related code, then delete this
static boolean ReadMapVisited(gamedata_t *data, UINT16 i)
{
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
return false;
return true;
}
static boolean ReadMainRecords(gamedata_t *data, UINT16 i)
{
UINT32 recscore = READUINT32(save_p);
tic_t rectime = (tic_t)READUINT32(save_p);
UINT16 recrings = READUINT16(save_p);
save_p++; // compat
if (recrings > 10000 || recscore > MAXSCORE)
return false;
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
return true;
}
static boolean ReadNightsRecords(gamedata_t *data, UINT16 i)
{
UINT8 recmares;
if ((recmares = READUINT8(save_p)) == 0)
return true;
G_AllocNightsRecordData((INT16)i, data);
for (INT32 curmare = 0; curmare < (recmares+1); ++curmare)
{
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
return false;
}
}
data->nightsrecords[i]->nummares = recmares;
return true;
}
// G_LoadGameData
// Loads the main data file, which stores information such as emblems found, etc.
......@@ -4383,14 +4673,6 @@ void G_LoadGameData(gamedata_t *data)
UINT32 versionID;
UINT8 rtemp;
//For records
UINT32 recscore;
tic_t rectime;
UINT16 recrings;
UINT8 recmares;
INT32 curmare;
// Stop saving, until we successfully load it again.
data->loaded = false;
......@@ -4498,9 +4780,12 @@ void G_LoadGameData(gamedata_t *data)
}
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
// Read map visited flags
for (i = 0; i < NUMBASEMAPS; i++)
{
if (!ReadMapVisited(data, i))
goto datacorrupt;
}
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < max_emblems;)
......@@ -4537,47 +4822,57 @@ void G_LoadGameData(gamedata_t *data)
data->timesBeatenUltimate = READUINT32(save_p);
// Main records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < NUMBASEMAPS; ++i)
{
recscore = READUINT32(save_p);
rectime = (tic_t)READUINT32(save_p);
recrings = READUINT16(save_p);
save_p++; // compat
if (recrings > 10000 || recscore > MAXSCORE)
if (!ReadMainRecords(data, i))
goto datacorrupt;
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < NUMBASEMAPS; ++i)
{
if ((recmares = READUINT8(save_p)) == 0)
continue;
if (!ReadNightsRecords(data, i))
goto datacorrupt;
}
#ifdef EXTRA_DATA_MARKER
// Read extra data
if (save_p < savebuffer+length && (length - (save_p-savebuffer)) >= 4)
{
UINT32 marker = READUINT32(save_p);
if (marker != EXTRA_DATA_MARKER)
goto datacorrupt;
UINT8 extraID = READUINT8(save_p);
if (extraID != 0x00)
goto datacorrupt;
G_AllocNightsRecordData((INT16)i, data);
UINT16 nummaps = READUINT16(save_p);
if (nummaps >= MAXMAPS)
goto datacorrupt;
for (curmare = 0; curmare < (recmares+1); ++curmare)
// Read map visited flags
for (i = NUMBASEMAPS; i < nummaps; i++)
{
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (!ReadMapVisited(data, i))
goto datacorrupt;
}
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
// Main records
for (i = NUMBASEMAPS; i < nummaps; i++)
{
if (!ReadMainRecords(data, i))
goto datacorrupt;
}
}
data->nightsrecords[i]->nummares = recmares;
// Nights records
for (i = NUMBASEMAPS; i < nummaps; i++)
{
if (!ReadNightsRecords(data, i))
goto datacorrupt;
}
}
#endif
// done
Z_Free(savebuffer);
......@@ -4609,6 +4904,41 @@ void G_LoadGameData(gamedata_t *data)
}
}
static void WriteMainRecords(gamedata_t *data, UINT16 i, UINT8 **data_p)
{
if (data->mainrecords[i])
{
WRITEUINT32(*data_p, data->mainrecords[i]->score);
WRITEUINT32(*data_p, data->mainrecords[i]->time);
WRITEUINT16(*data_p, data->mainrecords[i]->rings);
}
else
{
WRITEUINT32(*data_p, 0);
WRITEUINT32(*data_p, 0);
WRITEUINT16(*data_p, 0);
}
WRITEUINT8(*data_p, 0); // compat
}
static void WriteNightsRecords(gamedata_t *data, UINT16 i, UINT8 **data_p)
{
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(*data_p, 0);
return;
}
WRITEUINT8(*data_p, data->nightsrecords[i]->nummares);
for (INT32 curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(*data_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(*data_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(*data_p, data->nightsrecords[i]->time[curmare]);
}
}
// G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(gamedata_t *data)
......@@ -4619,8 +4949,6 @@ void G_SaveGameData(gamedata_t *data)
INT32 i, j;
UINT8 btemp;
INT32 curmare;
if (!data)
return; // data struct not valid
......@@ -4649,7 +4977,7 @@ void G_SaveGameData(gamedata_t *data)
WRITEUINT32(data_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < NUMBASEMAPS; i++)
WRITEUINT8(data_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag
......@@ -4691,41 +5019,35 @@ void G_SaveGameData(gamedata_t *data)
WRITEUINT32(data_p, data->timesBeatenUltimate);
// Main records
for (i = 0; i < NUMMAPS; i++)
{
if (data->mainrecords[i])
{
WRITEUINT32(data_p, data->mainrecords[i]->score);
WRITEUINT32(data_p, data->mainrecords[i]->time);
WRITEUINT16(data_p, data->mainrecords[i]->rings);
}
else
{
WRITEUINT32(data_p, 0);
WRITEUINT32(data_p, 0);
WRITEUINT16(data_p, 0);
}
WRITEUINT8(data_p, 0); // compat
}
for (i = 0; i < NUMBASEMAPS; i++)
WriteMainRecords(data, i, &data_p);
// NiGHTS records
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < NUMBASEMAPS; i++)
WriteNightsRecords(data, i, &data_p);
#ifdef EXTRA_DATA_MARKER
if (numgamemaps > NUMBASEMAPS)
{
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(data_p, 0);
continue;
}
// Write extra data
WRITEUINT32(save_p, EXTRA_DATA_MARKER);
WRITEUINT8(save_p, 0x00);
WRITEUINT8(data_p, data->nightsrecords[i]->nummares);
WRITEUINT16(save_p, numgamemaps);
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(data_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(data_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(data_p, data->nightsrecords[i]->time[curmare]);
}
// Write map visited flags
for (i = NUMBASEMAPS; i < numgamemaps; i++)
WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
// Main records
for (i = NUMBASEMAPS; i < numgamemaps; i++)
WriteMainRecords(data, i, &data_p);
// NiGHTS records
for (i = NUMBASEMAPS; i < numgamemaps; i++)
WriteNightsRecords(data, i, &data_p);
}
#endif
length = data_p - savebuffer;
......@@ -5040,7 +5362,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 character, bo
SetPlayerSkinByNum(consoleplayer, character);
if (mapname)
D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pultmode, true, 1, false, FLS);
D_MapChange(G_GetMapNumber(mapname), gametype, pultmode, true, 1, false, FLS);
}
//
......@@ -5121,18 +5443,18 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
// (Looks a bit silly, but it works.)
boolean reset_skin = netgame && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0';
gamemap = (INT16)G_GetMapNumber(mapname); // get xx out of MAPxx
// internal game map
// well this check is useless because it is done before (d_netcmd.c::command_map_f)
// but in case of for demos....
if (W_CheckNumForName(mapname) == LUMPERROR)
if (gamemap == 0)
{
I_Error("Internal game map '%s' not found\n", mapname);
Command_ExitGame_f();
return;
}
gamemap = (INT16)M_MapNumber(mapname[3], mapname[4]); // get xx out of MAPxx
// gamemap changed; we assume that its map header is always valid,
// so make it so
if(!mapheaderinfo[gamemap-1])
......@@ -5183,7 +5505,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
}
}
char *G_BuildMapTitle(INT32 mapnum)
{
char *title = NULL;
......@@ -5280,7 +5601,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
mapnamelen = strlen(mapname);
/* Count available maps; how ugly. */
for (i = 0, freqc = 0; i < NUMMAPS; ++i)
for (i = 0, freqc = 0; i < numgamemaps; ++i)
{
if (mapheaderinfo[i])
freqc++;
......@@ -5291,7 +5612,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
wanttable = !!( freqp );
freqc = 0;
for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum)
for (i = 0, mapnum = 1; i < numgamemaps; ++i, ++mapnum)
if (mapheaderinfo[i])
{
if (!( realmapname = G_BuildMapTitle(mapnum) ))
......@@ -5418,7 +5739,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
{
usemapcode = true;
newmapnum = mapheaderinfo[gamemap-1]->nextlevel;
if (newmapnum < 1 || newmapnum > NUMMAPS)
if (newmapnum < 1 || newmapnum > numgamemaps)
{
CONS_Alert(CONS_ERROR, M_GetText("NextLevel (%d) is not a valid map.\n"), newmapnum);
return 0;
......@@ -5435,6 +5756,11 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
usemapcode = true;
}
else
{
if (( newmapnum = G_GetMapNumber(mapname) ))
usemapcode = true;
}
if (!usemapcode)
{
......@@ -5442,7 +5768,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
newmapnum = strtol(mapname, &p, 10);
if (*p == '\0')/* we got it */
{
if (newmapnum < 1 || newmapnum > NUMMAPS)
if (newmapnum < 1 || newmapnum > numgamemaps)
{
CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
return 0;
......@@ -5458,7 +5784,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
if (usemapcode)
{
/* we can't check mapheaderinfo for this hahahaha */
if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(newmapnum)))
return 0;
if (realmapnamep)
......
......@@ -113,8 +113,18 @@ INT32 Joy2Axis(joyaxis_e axissel);
#define SLOWTURNTICS (6)
// build an internal map name MAPxx from map number
const char *G_BuildClassicMapName(INT32 map);
const char *G_BuildMapName(INT32 map);
void G_InitMaps(void);
UINT16 G_GetMapNumber(const char *name);
UINT16 G_GetMapNumberForNextMap(const char *name);
UINT16 G_AddMap(const char *name, UINT32 lumpnum);
lumpnum_t G_GetMapLumpnum(const char *name);
boolean G_MapFileExists(const char *name);
boolean G_IsValidMapName(const char *name);
extern INT16 ticcmd_oldangleturn[2];
extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
......@@ -141,6 +151,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum);
const char *G_GetMapThumbnail(INT16 map);
const char *G_GetMapThumbnailWide(INT16 map);
const char *G_GetDefaultMapMusic(INT16 map);
const char *G_GetMapMetalSonicReplay(INT16 map);
boolean G_IsGameEndMap(INT16 mapnum);
struct searchdim
{
......
......@@ -1378,7 +1378,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
{
player_t *player = NULL;
const char *musnamearg = luaL_checkstring(L, 2);
char musname[7], *p = musname;
char musname[MAX_MUSIC_NAME+1], *p = musname;
UINT16 musflags = luaL_optinteger(L, 3, 0);
boolean looping = lua_opttrueboolean(L, 4);
jingletype_t jingletype = luaL_optinteger(L, 5, JT_OTHER);
......@@ -1393,8 +1393,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
if (jingletype >= NUMJINGLES)
return luaL_error(L, "jingletype %d out of range (0 - %d)", jingletype, NUMJINGLES-1);
musname[6] = '\0';
strncpy(musname, musnamearg, 6);
strlcpy(musname, musnamearg, MAX_MUSIC_NAME+1);
while (*p) {
*p = tolower(*p);
......@@ -3914,10 +3913,53 @@ static int lib_gSetUsedCheats(lua_State *L)
return 0;
}
static int GetMapNameOrNumber(lua_State *L, int idx)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
const char *mapname = luaL_checkstring(L, idx);
INT16 mapnum = G_GetMapNumber(mapname);
if (mapnum == 0)
{
return luaL_error(L,
"%s is not a valid game map.",
mapname
);
}
return mapnum;
}
else
return luaL_checkinteger(L, idx);
}
static int GetNextMapNameOrNumber(lua_State *L, int idx)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
const char *mapname = luaL_checkstring(L, idx);
INT16 mapnum = G_GetMapNumberForNextMap(mapname);
if (mapnum == 0)
{
return luaL_error(L,
"%s is not a valid game map.",
mapname
);
}
return mapnum;
}
else
return luaL_checkinteger(L, idx);
}
static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
{
if (ISINLEVEL)
return luaL_optinteger(L, idx, gamemap);
{
if (!lua_isnoneornil(L, idx))
return GetMapNameOrNumber(L, idx);
else
return gamemap;
}
else
{
if (lua_isnoneornil(L, idx))
......@@ -3928,14 +3970,21 @@ static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
);
}
else
return luaL_checkinteger(L, idx);
return GetMapNameOrNumber(L, idx);
}
}
static int lib_gBuildMapName(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapName");
//HUDSAFE
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_BuildMapName(map));
return 1;
}
......@@ -3944,12 +3993,12 @@ static int lib_gBuildMapTitle(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle");
char *name;
if (map < 1 || map > NUMMAPS)
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
NUMMAPS
numgamemaps
);
}
name = G_BuildMapTitle(map);
......@@ -4069,6 +4118,36 @@ static int lib_gFindMapByNameOrCode(lua_State *L)
return 1;
}
static int lib_gGetMapThumbnail(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_GetMapThumbnail");
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_GetMapThumbnail(map));
return 1;
}
static int lib_gGetMapThumbnailWide(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_GetMapThumbnailWide");
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_GetMapThumbnailWide(map));
return 1;
}
static int lib_gDoReborn(lua_State *L)
{
INT32 playernum = luaL_checkinteger(L, 1);
......@@ -4102,7 +4181,19 @@ static int lib_gSetCustomExitVars(lua_State *L)
if (n >= 1)
{
nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
if (!lua_isnoneornil(L, 1))
{
INT16 mapnum = GetNextMapNameOrNumber(L, 1);
if (mapnum < 1 || (mapnum > numgamemaps && !G_IsGameEndMap(mapnum)))
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
mapnum,
numgamemaps
);
}
nextmapoverride = mapnum;
}
skipstats = (INT16)luaL_optinteger(L, 2, 0);
nextgametype = (INT16)luaL_optinteger(L, 3, -1);
}
......@@ -4130,9 +4221,22 @@ static int lib_gExitLevel(lua_State *L)
static int lib_gIsSpecialStage(lua_State *L)
{
INT32 mapnum = luaL_optinteger(L, 1, gamemap);
//HUDSAFE
INT32 mapnum;
INLEVEL
if (!lua_isnoneornil(L, 1))
{
mapnum = GetMapNameOrNumber(L, 1);
if (mapnum < 1 || mapnum > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
mapnum,
numgamemaps
);
}
}
else
mapnum = gamemap;
lua_pushboolean(L, G_IsSpecialStage(mapnum));
return 1;
}
......@@ -4541,6 +4645,8 @@ static luaL_Reg lib[] = {
{"G_BuildMapTitle",lib_gBuildMapTitle},
{"G_FindMap",lib_gFindMap},
{"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode},
{"G_GetMapThumbnail",lib_gGetMapThumbnail},
{"G_GetMapThumbnailWide",lib_gGetMapThumbnailWide},
{"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
......
......@@ -1086,16 +1086,16 @@ static void update_music_name(struct MusicChange *musicchange)
size_t length;
const char * new = lua_tolstring(gL, -6, &length);
if (length < 7)
if (length <= MAX_MUSIC_NAME)
{
strcpy(musicchange->newname, new);
lua_pushvalue(gL, -6);/* may as well keep it for next call */
}
else
{
memcpy(musicchange->newname, new, 6);
musicchange->newname[6] = '\0';
lua_pushlstring(gL, new, 6);
memcpy(musicchange->newname, new, MAX_MUSIC_NAME);
musicchange->newname[MAX_MUSIC_NAME] = '\0';
lua_pushlstring(gL, new, MAX_MUSIC_NAME);
}
lua_replace(gL, -7);
......
......@@ -2696,32 +2696,22 @@ static int vector3_get(lua_State *L)
static int lib_getMapheaderinfo(lua_State *L)
{
// i -> mapheaderinfo[i-1]
//int field;
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1)-1;
if (i >= NUMMAPS)
if (i >= numgamemaps)
return 0;
LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER);
//CONS_Printf(mapheaderinfo[i]->lvlttl);
return 1;
}/*
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateSubsectors);
return 1;
}*/
}
return 0;
}
static int lib_nummapheaders(lua_State *L)
{
lua_pushinteger(L, NUMMAPS);
lua_pushinteger(L, numgamemaps);
return 1;
}
......
......@@ -78,7 +78,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
// Main records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < MAXMAPS; ++i)
{
if (!src->mainrecords[i])
continue;
......@@ -90,7 +90,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < MAXMAPS; ++i)
{
if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
continue;
......@@ -577,7 +577,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
INT32 mscore = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......@@ -595,7 +595,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
INT32 curtics = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......@@ -613,7 +613,7 @@ UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
INT32 mrings = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......
......@@ -203,9 +203,9 @@ typedef struct
boolean unlocked[MAXUNLOCKABLES];
// TIME ATTACK DATA
recorddata_t *mainrecords[NUMMAPS];
nightsdata_t *nightsrecords[NUMMAPS];
UINT8 mapvisited[NUMMAPS];
recorddata_t *mainrecords[MAXMAPS];
nightsdata_t *nightsrecords[MAXMAPS];
UINT8 mapvisited[MAXMAPS];
// # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten;
......
......@@ -416,7 +416,7 @@ consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesN
static CV_PossibleValue_t map_cons_t[] = {
{1,"MIN"},
{NUMMAPS, "MAX"},
{MAXMAPS, "MAX"},
{0,NULL}
};
consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange);
......@@ -2233,9 +2233,9 @@ void Nextmap_OnChange(void)
{
gamedata_t *data = clientGamedata;
char *leveltitle;
char tabase[256];
char tabase[4096];
#ifdef OLDNREPLAYNAME
char tabaseold[256];
char tabaseold[4096];
#endif
short i;
boolean active;
......@@ -2501,13 +2501,13 @@ void M_InitMenuPresTables(void)
menupres[i].muslooping = true;
}
if (i == MN_SP_TIMEATTACK)
strncpy(menupres[i].musname, "_recat", 7);
strlcpy(menupres[i].musname, "_recat", MAX_MUSIC_NAME+1);
else if (i == MN_SP_NIGHTSATTACK)
strncpy(menupres[i].musname, "_nitat", 7);
strlcpy(menupres[i].musname, "_nitat", MAX_MUSIC_NAME+1);
else if (i == MN_SP_MARATHON)
strncpy(menupres[i].musname, "spec8", 6);
strlcpy(menupres[i].musname, "spec8", MAX_MUSIC_NAME+1);
else if (i == MN_SP_PLAYER || i == MN_SR_PLAYER)
strncpy(menupres[i].musname, "_chsel", 7);
strlcpy(menupres[i].musname, "_chsel", MAX_MUSIC_NAME+1);
else if (i == MN_SR_SOUNDTEST)
{
*menupres[i].musname = '\0';
......@@ -2533,7 +2533,7 @@ typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **, boolean fr
// a single input. Maybe someday use this struct program-wide.
typedef struct
{
char musname[7];
char musname[MAX_MUSIC_NAME+1];
UINT16 mustrack;
boolean muslooping;
} menupresmusic_t;
......@@ -2744,8 +2744,7 @@ void M_ChangeMenuMusic(const char *defaultmusname, boolean defaultmuslooping)
if (!defaultmusname)
defaultmusname = "";
strncpy(defaultmusic.musname, defaultmusname, 7);
defaultmusic.musname[6] = 0;
strlcpy(defaultmusic.musname, defaultmusname, MAX_MUSIC_NAME+1);
defaultmusic.mustrack = 0;
defaultmusic.muslooping = defaultmuslooping;
......@@ -5129,7 +5128,7 @@ static INT32 M_CountLevelsToShowOnPlatter(INT32 gt)
{
INT32 mapnum, count = 0;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt))
count++;
......@@ -5164,7 +5163,7 @@ static boolean M_GametypeHasLevels(INT32 gt)
{
INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt))
return true;
......@@ -5176,11 +5175,9 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
INT32 col = 0, rows = 0;
INT32 mapIterate = 0;
INT32 headingIterate = 0;
boolean mapAddedAlready[NUMMAPS];
boolean *mapAddedAlready = Z_Calloc(numgamemaps*sizeof(boolean), PU_STATIC, NULL);
memset(mapAddedAlready, 0, sizeof mapAddedAlready);
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++)
for (mapIterate = 0; mapIterate < numgamemaps; mapIterate++)
{
boolean forceNewRow = true;
......@@ -5196,7 +5193,7 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
continue;
}
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++)
for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{
boolean wide = false;
......@@ -5243,6 +5240,8 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
rows++;
}
Z_Free(mapAddedAlready);
return rows;
}
......@@ -5274,11 +5273,13 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
INT32 col = 0, row = 0, startrow = 0;
INT32 mapIterate = 0; // First level of map loop -- find starting points for select headings
INT32 headingIterate = 0; // Second level of map loop -- finding maps that match mapIterate's heading.
boolean mapAddedAlready[NUMMAPS];
boolean *mapAddedAlready;
if (!numrows)
return false;
mapAddedAlready = Z_Calloc(numgamemaps*sizeof(boolean), PU_STATIC, NULL);
if (levelselect.rows)
Z_Free(levelselect.rows);
levelselect.rows = NULL;
......@@ -5291,8 +5292,6 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
// done here so lsrow and lscol can be set if cv_nextmap is on the platter
lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0;
memset(mapAddedAlready, 0, sizeof mapAddedAlready);
if (levellistmode == LLM_CREATESERVER)
{
sprintf(levelselect.rows[0].header, "Gametype");
......@@ -5304,7 +5303,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
char_notes = NULL;
}
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++)
for (mapIterate = 0; mapIterate < numgamemaps; mapIterate++)
{
INT32 headerRow = -1;
boolean anyAvailable = false;
......@@ -5322,7 +5321,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
continue;
}
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++)
for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{
UINT8 actnum = 0;
boolean headingisname = false;
......@@ -5473,6 +5472,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
}
#endif
Z_Free(mapAddedAlready);
M_CacheLevelPlatter();
return true;
......@@ -5708,8 +5709,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
}
else
{
if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR)
patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_PATCH);
const char *thumbnail = G_GetMapThumbnailWide(map);
if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else
patch = levselp[1][2]; // don't static to indicate that it's just a normal level
......@@ -5739,8 +5741,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
}
else
{
if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR)
patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_PATCH);
const char *thumbnail = G_GetMapThumbnail(map);
if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else
patch = levselp[0][2]; // don't static to indicate that it's just a normal level
......@@ -6053,7 +6056,7 @@ static INT32 M_GetFirstLevelInList(INT32 gt)
{
INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelInList(mapnum, gt))
return mapnum + 1;
......@@ -6926,12 +6929,9 @@ static boolean M_ExitPandorasBox(void)
static void M_ChangeLevel(INT32 choice)
{
char mapname[6];
(void)choice;
const char *mapname = G_BuildMapName(cv_nextmap.value);
strlcpy(mapname, G_BuildMapName(cv_nextmap.value), sizeof (mapname));
strlwr(mapname);
mapname[5] = '\0';
(void)choice;
M_ClearMenus(true);
COM_BufAddText(va("map %s -gametype \"%s\"\n", mapname, cv_newgametype.string));
......@@ -7096,7 +7096,7 @@ static void M_LevelSelectWarp(INT32 choice)
{
(void)choice;
if (W_CheckNumForName(G_BuildMapName(cv_nextmap.value)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(cv_nextmap.value)))
{
CONS_Alert(CONS_WARNING, "Internal game map '%s' not found\n", G_BuildMapName(cv_nextmap.value));
return;
......@@ -8259,7 +8259,7 @@ static void M_SinglePlayerMenu(INT32 choice)
if (mapheaderinfo[spmarathon_start-1]
&& !mapheaderinfo[spmarathon_start-1]->marathonnext
&& (mapheaderinfo[spmarathon_start-1]->nextlevel == spmarathon_start
|| mapheaderinfo[spmarathon_start-1]->nextlevel >= 1100))
|| G_IsGameEndMap(mapheaderinfo[spmarathon_start-1]->nextlevel)))
{
SP_MainMenu[spmarathon].status = IT_NOTHING|IT_DISABLED; // Hide and disable the Marathon Run option...
// ...and lower the above options' display positions by 8 pixels to close the gap
......@@ -8462,7 +8462,7 @@ static void M_DrawLoadGameData(void)
#ifdef PERFECTSAVE // disabled on request
else if ((savegameinfo[savetodraw].skinnum == 1)
&& (savegameinfo[savetodraw].lives == 99)
&& (savegameinfo[savetodraw].gamemap & 8192)
&& (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
&& (savegameinfo[savetodraw].numgameovers == 0)
&& (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save
{
......@@ -8518,11 +8518,11 @@ static void M_DrawLoadGameData(void)
else
{
patch_t *patch;
if (savegameinfo[savetodraw].gamemap & 8192)
if (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
patch = savselp[3];
else
{
lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191)));
lumpnum_t lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(savegameinfo[savetodraw].gamemap));
if (lumpnum != LUMPERROR)
patch = W_CachePatchNum(lumpnum, PU_PATCH);
else
......@@ -8537,7 +8537,7 @@ static void M_DrawLoadGameData(void)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666)
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!");
else if (savegameinfo[savetodraw].gamemap & 8192)
else if (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else
V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname);
......@@ -8755,7 +8755,7 @@ static void M_LoadSelect(INT32 choice)
// This slot is empty, so start a new game here.
M_NewGame();
}
else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed
else if (savegameinfo[saveSlotSelected-1].flags & SAVE_GAME_COMPLETE_BIT) // Completed
M_LoadGameLevelSelect(0);
else
G_LoadGame((UINT32)saveSlotSelected, 0);
......@@ -8776,6 +8776,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
UINT8 *end_p; // buffer end point, don't read past here
UINT8 *sav_p;
INT32 fake; // Dummy variable
INT16 mapnum;
char temp[sizeof(timeattackfolder)];
char vcheck[VERSIONSIZE];
#ifdef NEWSKINSAVES
......@@ -8810,19 +8811,44 @@ static void M_ReadSavegameInfo(UINT32 slot)
CHECKPOS
fake = READINT16(sav_p);
if (((fake-1) & 8191) >= NUMMAPS) BADSAVE
#ifdef NEWMAPSAVES
if (fake == NEWMAPSAVES)
{
char mapname[MAX_MAP_NAME_SIZE+1];
READSTRINGN(sav_p, mapname, MAX_MAP_NAME_SIZE);
savegameinfo[slot].flags = READUINT8(sav_p);
if(!mapheaderinfo[(fake-1) & 8191])
mapnum = G_GetMapNumber(mapname);
if (mapnum == 0)
BADSAVE
}
else
#endif
{
if (((fake-1) & 8191) >= NUMBASEMAPS) BADSAVE
mapnum = (fake-1) & 8191;
mapnum++;
if (fake & 8192)
savegameinfo[slot].flags = SAVE_GAME_COMPLETE_BIT;
else
savegameinfo[slot].flags = 0;
}
if(!mapheaderinfo[mapnum-1])
savegameinfo[slot].levelname[0] = '\0';
else if (V_ThinStringWidth(mapheaderinfo[(fake-1) & 8191]->lvlttl, 0) <= 78)
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 22);
else if (V_ThinStringWidth(mapheaderinfo[mapnum-1]->lvlttl, 0) <= 78)
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 22);
else
{
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 15);
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 15);
strcat(savegameinfo[slot].levelname, "...");
}
savegameinfo[slot].gamemap = fake;
savegameinfo[slot].gamemap = mapnum;
CHECKPOS
savegameinfo[slot].numemeralds = READUINT16(sav_p)-357; // emeralds
......@@ -9635,7 +9661,8 @@ static void M_ChoosePlayer(INT32 choice)
static INT32 statsLocation;
static INT32 statsMax;
static INT16 statsMapList[NUMMAPS+1];
static INT16 *statsMapList = NULL;
static size_t statsMapLength = 0;
static void M_Statistics(INT32 choice)
{
......@@ -9643,9 +9670,17 @@ static void M_Statistics(INT32 choice)
(void)choice;
memset(statsMapList, 0, sizeof(statsMapList));
size_t num_maps = (size_t)(numgamemaps+1);
if (statsMapLength != num_maps)
{
statsMapLength = num_maps;
statsMapList = Z_Realloc(statsMapList, statsMapLength*sizeof(INT16), PU_STATIC, NULL);
}
memset(statsMapList, 0, statsMapLength*sizeof(INT16));
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < numgamemaps; i++)
{
if (!mapheaderinfo[i] || mapheaderinfo[i]->lvlttl[0] == '\0')
continue;
......@@ -9781,7 +9816,7 @@ static void M_DrawLevelStats(void)
G_TicsToMinutes(data->totalplaytime, false),
G_TicsToSeconds(data->totalplaytime)));
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < numgamemaps; i++)
{
boolean mapunfinished = false;
......@@ -9992,10 +10027,10 @@ void M_DrawTimeAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......@@ -10256,10 +10291,10 @@ void M_DrawNightsAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......@@ -10420,7 +10455,8 @@ static void M_NightsAttack(INT32 choice)
static void M_ChooseNightsAttack(INT32 choice)
{
char *gpath;
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(cv_nextmap.value);
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char nameofdemo[256];
(void)choice;
emeralds = 0;
......@@ -10434,7 +10470,7 @@ static void M_ChooseNightsAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value));
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, mapname);
snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value)
......@@ -10449,7 +10485,8 @@ static void M_ChooseNightsAttack(INT32 choice)
static void M_ChooseTimeAttack(INT32 choice)
{
char *gpath;
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(cv_nextmap.value);
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char nameofdemo[256];
(void)choice;
emeralds = 0;
......@@ -10463,7 +10500,7 @@ static void M_ChooseTimeAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value));
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, mapname);
snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value)
......@@ -10795,7 +10832,7 @@ static void M_Marathon(INT32 choice)
SP_MarathonMenu[marathonplayer].status = (skinset == MAXCHARACTERSLOTS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED;
while (mapnum < NUMMAPS)
while (mapnum < numgamemaps)
{
if (mapheaderinfo[mapnum])
{
......@@ -10805,7 +10842,7 @@ static void M_Marathon(INT32 choice)
mapnum++;
}
SP_MarathonMenu[marathoncutscenes].status = (mapnum < NUMMAPS) ? IT_CVAR|IT_STRING : IT_NOTHING|IT_DISABLED;
SP_MarathonMenu[marathoncutscenes].status = (mapnum < numgamemaps) ? IT_CVAR|IT_STRING : IT_NOTHING|IT_DISABLED;
M_ChangeMenuMusic("spec8", true);
......@@ -11704,10 +11741,10 @@ static void M_DrawServerMenu(void)
M_DrawLevelPlatterHeader(currentMenu->y + imgheight - 10 - lsheadingheight/2, (const char *)headerstr, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......
......@@ -159,7 +159,7 @@ typedef struct
INT16 ttloop; // # frame to loop; -1 means dont loop
UINT16 tttics; // # of tics per frame
char musname[7]; ///< Music track to play. "" for no music.
char musname[MAX_MUSIC_NAME+1]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
boolean muslooping; ///< Loop the music
boolean musstop; ///< Don't play any music
......@@ -427,6 +427,7 @@ typedef struct
INT32 lives;
INT32 continuescore;
INT32 gamemap;
UINT8 flags;
} saveinfo_t;
extern description_t *description;
......
......@@ -2259,6 +2259,30 @@ boolean M_IsStringEmpty(const char *s)
return true;
}
const char *M_GetFilenameFromPath(const char *path)
{
const char *slash = strrchr(path, PATHSEP[0]);
if (slash)
return slash + 1;
return path;
}
const char *M_GetExtensionFromFilename(const char *filename)
{
const char *dot = strrchr(filename, '.');
if (dot)
return dot + 1;
return NULL;
}
const char *M_CheckFilenameExtension(const char *filename, const char *ext)
{
const char *dot = strrchr(filename, '.');
if (dot && (strstr(dot, ext) || strstr(dot + 1, ext)))
return dot + 1;
return NULL;
}
// Converts a string containing a whole number into an int. Returns false if the conversion failed.
boolean M_StringToNumber(const char *input, int *out)
{
......@@ -2310,3 +2334,48 @@ int M_RoundUp(double number)
return (int)number;
}
// Hashes some message using FNV-1a
#define FNV1A_OFFSET_BASIS 0x811C9DC5
#define FNV1A_PRIME 0x01000193
UINT32 FNV1a_Hash(const char *message, size_t size)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
for (size_t i = 0; i < size; i++)
{
hash ^= message[i];
hash *= FNV1A_PRIME;
}
return hash;
}
UINT32 FNV1a_HashString(const char *message)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
while (*message)
{
hash ^= *message;
hash *= FNV1A_PRIME;
message++;
}
return hash;
}
UINT32 FNV1a_HashLowercaseString(const char *message)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
while (*message)
{
hash ^= tolower(*message);
hash *= FNV1A_PRIME;
message++;
}
return hash;
}