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
  • Jisk/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
117 results
Select Git revision
Show changes
Commits on Source (21)
...@@ -2158,14 +2158,14 @@ void CV_AddValue(consvar_t *var, INT32 increment) ...@@ -2158,14 +2158,14 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(increment > 0) // Going up! if(increment > 0) // Going up!
{ {
newvalue++; newvalue++;
if (newvalue == NUMMAPS) if (newvalue == numgamemaps)
newvalue = 0; newvalue = 0;
} }
else // Going down! else // Going down!
{ {
newvalue--; newvalue--;
if (newvalue == -1) if (newvalue == -1)
newvalue = NUMMAPS-1; newvalue = numgamemaps-1;
} }
if (newvalue == oldvalue) if (newvalue == oldvalue)
...@@ -2173,7 +2173,6 @@ void CV_AddValue(consvar_t *var, INT32 increment) ...@@ -2173,7 +2173,6 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(!mapheaderinfo[newvalue]) if(!mapheaderinfo[newvalue])
continue; // Don't allocate the header. That just makes memory usage skyrocket. continue; // Don't allocate the header. That just makes memory usage skyrocket.
} while (newvalue != oldvalue && !M_CanShowLevelInList(newvalue, gt)); } while (newvalue != oldvalue && !M_CanShowLevelInList(newvalue, gt));
var->value = newvalue + 1; var->value = newvalue + 1;
......
...@@ -952,13 +952,7 @@ void D_StartTitle(void) ...@@ -952,13 +952,7 @@ void D_StartTitle(void)
if (server) if (server)
{ {
char mapname[6]; COM_BufAddText(va("map %s\n", G_BuildMapName(spstage_start)));
strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname));
strlwr(mapname);
mapname[5] = '\0';
COM_BufAddText(va("map %s\n", mapname));
} }
} }
...@@ -1377,6 +1371,8 @@ void D_SRB2Main(void) ...@@ -1377,6 +1371,8 @@ void D_SRB2Main(void)
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n"); CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init(); Z_Init();
G_InitMaps();
clientGamedata = M_NewGameDataStruct(); clientGamedata = M_NewGameDataStruct();
serverGamedata = M_NewGameDataStruct(); serverGamedata = M_NewGameDataStruct();
...@@ -1690,7 +1686,7 @@ void D_SRB2Main(void) ...@@ -1690,7 +1686,7 @@ void D_SRB2Main(void)
{ {
pstartmap = bootmap; pstartmap = bootmap;
if (pstartmap < 1 || pstartmap > NUMMAPS) if (pstartmap < 1 || pstartmap > numgamemaps)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap); I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else else
{ {
...@@ -1737,7 +1733,7 @@ void D_SRB2Main(void) ...@@ -1737,7 +1733,7 @@ void D_SRB2Main(void)
if (server && !M_CheckParm("+map")) if (server && !M_CheckParm("+map"))
{ {
// Prevent warping to nonexistent levels // 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)); I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
else else
{ {
......
...@@ -168,7 +168,7 @@ void clear_levels(void) ...@@ -168,7 +168,7 @@ void clear_levels(void)
// This is potentially dangerous but if we're resetting these headers, // This is potentially dangerous but if we're resetting these headers,
// we may as well try to save some memory, right? // 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)) if (!mapheaderinfo[i] || i == (tutorialmap-1))
continue; continue;
...@@ -1352,6 +1352,39 @@ void readgametype(MYFILE *f, char *gtname) ...@@ -1352,6 +1352,39 @@ void readgametype(MYFILE *f, char *gtname)
CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]); 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) void readlevelheader(MYFILE *f, INT32 num)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
...@@ -1574,33 +1607,17 @@ void readlevelheader(MYFILE *f, INT32 num) ...@@ -1574,33 +1607,17 @@ void readlevelheader(MYFILE *f, INT32 num)
} }
else if (fastcmp(word, "NEXTLEVEL")) else if (fastcmp(word, "NEXTLEVEL"))
{ {
if (fastcmp(word2, "TITLE")) i = 1100; i = ConvertLevelHeaderMapNum(i);
else if (fastcmp(word2, "EVALUATION")) i = 1101; if (i == 0)
else if (fastcmp(word2, "CREDITS")) i = 1102; i = ParseNextLevelName(word2);
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]);
mapheaderinfo[num-1]->nextlevel = (INT16)i; mapheaderinfo[num-1]->nextlevel = (INT16)i;
} }
else if (fastcmp(word, "MARATHONNEXT")) else if (fastcmp(word, "MARATHONNEXT"))
{ {
if (fastcmp(word2, "TITLE")) i = 1100; i = ConvertLevelHeaderMapNum(i);
else if (fastcmp(word2, "EVALUATION")) i = 1101; if (i == 0)
else if (fastcmp(word2, "CREDITS")) i = 1102; i = ParseNextLevelName(word2);
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]);
mapheaderinfo[num-1]->marathonnext = (INT16)i; mapheaderinfo[num-1]->marathonnext = (INT16)i;
} }
...@@ -2015,8 +2032,7 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) ...@@ -2015,8 +2032,7 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
} }
else if (fastcmp(word, "MUSIC")) else if (fastcmp(word, "MUSIC"))
{ {
strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7); strlcpy(cutscenes[num]->scene[scenenum].musswitch, word2, MAX_MUSIC_NAME+1);
cutscenes[num]->scene[scenenum].musswitch[6] = 0;
} }
else if (fastcmp(word, "MUSICTRACK")) else if (fastcmp(word, "MUSICTRACK"))
{ {
...@@ -2277,8 +2293,7 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum) ...@@ -2277,8 +2293,7 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
} }
else if (fastcmp(word, "MUSIC")) else if (fastcmp(word, "MUSIC"))
{ {
strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7); strlcpy(textprompts[num]->page[pagenum].musswitch, word2, MAX_MUSIC_NAME+1);
textprompts[num]->page[pagenum].musswitch[6] = 0;
} }
else if (fastcmp(word, "MUSICTRACK")) else if (fastcmp(word, "MUSICTRACK"))
{ {
...@@ -2601,8 +2616,7 @@ void readmenu(MYFILE *f, INT32 num) ...@@ -2601,8 +2616,7 @@ void readmenu(MYFILE *f, INT32 num)
} }
else if (fastcmp(word, "MUSIC")) else if (fastcmp(word, "MUSIC"))
{ {
strncpy(menupres[num].musname, word2, 7); strlcpy(menupres[num].musname, word2, MAX_MUSIC_NAME+1);
menupres[num].musname[6] = 0;
titlechanged = true; titlechanged = true;
} }
else if (fastcmp(word, "MUSICTRACK")) else if (fastcmp(word, "MUSICTRACK"))
...@@ -3016,12 +3030,10 @@ void reademblemdata(MYFILE *f, INT32 num) ...@@ -3016,12 +3030,10 @@ void reademblemdata(MYFILE *f, INT32 num)
emblemlocations[num-1].tag = (INT16)value; emblemlocations[num-1].tag = (INT16)value;
else if (fastcmp(word, "MAPNUM")) else if (fastcmp(word, "MAPNUM"))
{ {
// Support using the actual map name, if (G_IsValidMapName(word2))
// i.e., Level AB, Level FZ, etc. value = G_GetMapNumber(word2);
if (!value)
// Convert to map number value = get_number(word2);
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
emblemlocations[num-1].level = (INT16)value; emblemlocations[num-1].level = (INT16)value;
} }
...@@ -3263,6 +3275,12 @@ void readunlockable(MYFILE *f, INT32 num) ...@@ -3263,6 +3275,12 @@ void readunlockable(MYFILE *f, INT32 num)
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]); 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; unlockables[num].variable = (INT16)i;
} }
...@@ -3336,15 +3354,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ...@@ -3336,15 +3354,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
PARAMCHECK(1); PARAMCHECK(1);
ty = UC_MAPVISITED + offset; ty = UC_MAPVISITED + offset;
// Convert to map number if it appears to be one // Convert to map number
if (params[1][0] >= 'A' && params[1][0] <= 'Z') re = G_GetMapNumber(params[1]);
re = M_MapNumber(params[1][0], params[1][1]); if (!re)
else
re = atoi(params[1]); 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; return;
} }
} }
...@@ -3356,15 +3373,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ...@@ -3356,15 +3373,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
ty = UC_MAPSCORE + offset; ty = UC_MAPSCORE + offset;
re = atoi(params[2]); re = atoi(params[2]);
// Convert to map number if it appears to be one // Convert to map number
if (params[1][0] >= 'A' && params[1][0] <= 'Z') x1 = G_GetMapNumber(params[1]);
x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); if (!x1)
else
x1 = (INT16)atoi(params[1]); 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; return;
} }
} }
...@@ -3391,15 +3407,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ...@@ -3391,15 +3407,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
else else
re = atoi(params[i]); re = atoi(params[i]);
// Convert to map number if it appears to be one // Convert to map number
if (params[1][0] >= 'A' && params[1][0] <= 'Z') x1 = G_GetMapNumber(params[1]);
x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); if (!x1)
else
x1 = (INT16)atoi(params[1]); 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; return;
} }
...@@ -3609,9 +3624,9 @@ void readmaincfg(MYFILE *f) ...@@ -3609,9 +3624,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
spstage_start = spmarathon_start = (INT16)value; spstage_start = spmarathon_start = (INT16)value;
...@@ -3622,9 +3637,9 @@ void readmaincfg(MYFILE *f) ...@@ -3622,9 +3637,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
spmarathon_start = (INT16)value; spmarathon_start = (INT16)value;
...@@ -3635,9 +3650,9 @@ void readmaincfg(MYFILE *f) ...@@ -3635,9 +3650,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
sstage_start = (INT16)value; sstage_start = (INT16)value;
...@@ -3649,9 +3664,9 @@ void readmaincfg(MYFILE *f) ...@@ -3649,9 +3664,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
smpstage_start = (INT16)value; smpstage_start = (INT16)value;
...@@ -3743,9 +3758,9 @@ void readmaincfg(MYFILE *f) ...@@ -3743,9 +3758,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
titlemap = (INT16)value; titlemap = (INT16)value;
...@@ -3919,13 +3934,12 @@ void readmaincfg(MYFILE *f) ...@@ -3919,13 +3934,12 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
bootmap = (INT16)value; bootmap = (INT16)value;
//titlechanged = true;
} }
else if (fastcmp(word, "STARTCHAR")) else if (fastcmp(word, "STARTCHAR"))
{ {
...@@ -3938,9 +3952,9 @@ void readmaincfg(MYFILE *f) ...@@ -3938,9 +3952,9 @@ void readmaincfg(MYFILE *f)
// i.e., Level AB, Level FZ, etc. // i.e., Level AB, Level FZ, etc.
// Convert to map number // Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z') if (G_IsValidMapName(word2))
value = M_MapNumber(word2[0], word2[1]); value = G_GetMapNumber(word2);
else if (!value)
value = get_number(word2); value = get_number(word2);
tutorialmap = (INT16)value; tutorialmap = (INT16)value;
......
...@@ -372,18 +372,32 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) ...@@ -372,18 +372,32 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
} }
else if (fastcmp(word, "LEVEL")) else if (fastcmp(word, "LEVEL"))
{ {
// Support using the actual map name, boolean is_valid_level = false;
// i.e., Level AB, Level FZ, etc.
// Convert to map number if (G_IsValidMapName(word2))
if (word2[0] >= 'A' && word2[0] <= 'Z') {
i = M_MapNumber(word2[0], word2[1]); 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); readlevelheader(f, i);
else 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); ignorelines(f);
} }
} }
......
...@@ -32,23 +32,6 @@ ...@@ -32,23 +32,6 @@
// used in the lumps of the WAD files. // 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. // Extra flag for objects.
#define MTF_EXTRA 1 #define MTF_EXTRA 1
...@@ -223,6 +206,4 @@ typedef struct ...@@ -223,6 +206,4 @@ typedef struct
#define ZSHIFT 4 #define ZSHIFT 4
#define NUMMAPS 1035
#endif // __DOOMDATA__ #endif // __DOOMDATA__
...@@ -253,6 +253,19 @@ extern char logfilename[1024]; ...@@ -253,6 +253,19 @@ extern char logfilename[1024];
#define MAXSKINS 255 #define MAXSKINS 255
#define MAXCHARACTERSLOTS (MAXSKINS * 3) // Should be higher than MAXSKINS. #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 COLORRAMPSIZE 16
#define MAXCOLORNAME 32 #define MAXCOLORNAME 32
#define NUMCOLORFREESLOTS 1024 #define NUMCOLORFREESLOTS 1024
...@@ -467,6 +480,8 @@ extern skincolor_t skincolors[MAXSKINCOLORS]; ...@@ -467,6 +480,8 @@ extern skincolor_t skincolors[MAXSKINCOLORS];
#define MUSICRATE 1000 // sound timing is calculated by milliseconds #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 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. #define PUSHACCEL (2*FRACUNIT) // Acceleration for MF2_SLIDEPUSH items.
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
// Selected by user. // Selected by user.
extern INT16 gamemap; extern INT16 gamemap;
extern char mapmusname[7]; extern char mapmusname[MAX_MUSIC_NAME+1];
extern UINT16 mapmusflags; extern UINT16 mapmusflags;
extern UINT32 mapmusposition; extern UINT32 mapmusposition;
#define MUSIC_TRACKMASK 0x0FFF // ----************ #define MUSIC_TRACKMASK 0x0FFF // ----************
...@@ -79,6 +79,7 @@ extern boolean usedCheats; ...@@ -79,6 +79,7 @@ extern boolean usedCheats;
extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true
extern boolean imcontinuing; // Temporary flag while continuing extern boolean imcontinuing; // Temporary flag while continuing
extern boolean metalrecording; extern boolean metalrecording;
extern boolean replacedcurrentmap;
#define ATTACKING_NONE 0 #define ATTACKING_NONE 0
#define ATTACKING_RECORD 1 #define ATTACKING_RECORD 1
...@@ -175,7 +176,7 @@ typedef struct ...@@ -175,7 +176,7 @@ typedef struct
UINT16 textxpos; UINT16 textxpos;
UINT16 textypos; UINT16 textypos;
char musswitch[7]; char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags; UINT16 musswitchflags;
UINT32 musswitchposition; UINT32 musswitchposition;
...@@ -215,7 +216,7 @@ typedef struct ...@@ -215,7 +216,7 @@ typedef struct
UINT16 ycoord[MAX_PROMPT_PICS]; // gfx UINT16 ycoord[MAX_PROMPT_PICS]; // gfx
UINT16 picduration[MAX_PROMPT_PICS]; UINT16 picduration[MAX_PROMPT_PICS];
char musswitch[7]; char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags; UINT16 musswitchflags;
UINT8 musicloop; UINT8 musicloop;
...@@ -277,6 +278,23 @@ extern struct quake ...@@ -277,6 +278,23 @@ extern struct quake
fixed_t radius, intensity; fixed_t radius, intensity;
} quake; } 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 // NiGHTS grades
typedef struct typedef struct
{ {
...@@ -296,22 +314,22 @@ typedef struct ...@@ -296,22 +314,22 @@ typedef struct
typedef struct typedef struct
{ {
// The original eight, plus one. // The original eight, plus one.
char lvlttl[21+1]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) char lvlttl[21+1]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway)
char subttl[32+1]; ///< Subtitle for level char subttl[32+1]; ///< Subtitle for level
UINT8 actnum; ///< Act number or 0 for none. UINT8 actnum; ///< Act number or 0 for none.
UINT32 typeoflevel; ///< Combination of typeoflevel flags. 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 1100-1102 to end.
INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI. INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI.
char keywords[32+1]; ///< Keywords separated by space to search for. 32 characters. char keywords[32+1]; ///< 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. 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. UINT32 muspos; ///< Music position to jump to.
char forcecharacter[16+1]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. char forcecharacter[16+1]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable.
UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave. UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave.
INT16 skynum; ///< Sky number to use. INT16 skynum; ///< Sky number to use.
INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.)
INT16 skybox_scaley; ///< Skybox Y axis scale. INT16 skybox_scaley; ///< Skybox Y axis scale.
INT16 skybox_scalez; ///< Skybox Z axis scale. INT16 skybox_scalez; ///< Skybox Z axis scale.
// Extra information. // Extra information.
char interscreen[8+1]; ///< 320x200 patch to display at intermission. char interscreen[8+1]; ///< 320x200 patch to display at intermission.
...@@ -351,10 +369,10 @@ typedef struct ...@@ -351,10 +369,10 @@ typedef struct
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful. nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
// Music stuff. // Music stuff.
UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds 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. UINT16 muspostbosstrack; ///< Post-bossdeath track.
UINT32 muspostbosspos; ///< Post-bossdeath position UINT32 muspostbosspos; ///< Post-bossdeath position
UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds. UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds.
...@@ -389,7 +407,11 @@ typedef struct ...@@ -389,7 +407,11 @@ typedef struct
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level #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 #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 // Gametypes
#define NUMGAMETYPEFREESLOTS 128 #define NUMGAMETYPEFREESLOTS 128
......
...@@ -3972,7 +3972,7 @@ void F_EndCutScene(void) ...@@ -3972,7 +3972,7 @@ void F_EndCutScene(void)
F_StartGameEvaluation(); F_StartGameEvaluation();
else if (cutnum == introtoplay-1) else if (cutnum == introtoplay-1)
D_StartTitle(); D_StartTitle();
else if (nextmap < 1100-1) else if (!G_IsGameEndMap(nextmap+1))
G_NextLevel(); G_NextLevel();
else else
G_EndGame(); G_EndGame();
......
...@@ -858,6 +858,8 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders) ...@@ -858,6 +858,8 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
if (strstr(fullname, path)) if (strstr(fullname, path))
fullname += strlen(path) + 1; fullname += strlen(path) + 1;
size_t fullnamelength = strlen(fullname);
// Get the 8-character long lump name. // Get the 8-character long lump name.
trimname = strrchr(fullname, '/'); trimname = strrchr(fullname, '/');
if (trimname) if (trimname)
...@@ -869,21 +871,30 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders) ...@@ -869,21 +871,30 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
{ {
char *dotpos = strrchr(trimname, '.'); char *dotpos = strrchr(trimname, '.');
if (dotpos == NULL) if (dotpos == NULL)
dotpos = fullname + strlen(fullname); dotpos = fullname + fullnamelength;
strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
// The name of the file, without the extension. // The name of the file, without the extension.
lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL);
strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); 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->longname = Z_Calloc(1, PU_STATIC, NULL);
lump_p->longnamelength = 0;
}
// The complete name of the file, with its extension, // The complete name of the file, with its extension,
// excluding the path of the directory where it resides. // excluding the path of the directory where it resides.
lump_p->fullname = Z_StrDup(fullname); 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++; lump_p++;
i++; i++;
......
...@@ -46,7 +46,7 @@ boolean nodrawers; // for comparative timing purposes ...@@ -46,7 +46,7 @@ boolean nodrawers; // for comparative timing purposes
boolean noblit; // for comparative timing purposes boolean noblit; // for comparative timing purposes
tic_t demostarttime; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes
static char demoname[64]; static char demoname[512];
boolean demorecording; boolean demorecording;
boolean demoplayback; boolean demoplayback;
boolean titledemo; // Title Screen demo can be cancelled by any key boolean titledemo; // Title Screen demo can be cancelled by any key
...@@ -1417,15 +1417,14 @@ void G_WriteMetalTic(mobj_t *metal) ...@@ -1417,15 +1417,14 @@ void G_WriteMetalTic(mobj_t *metal)
// //
void G_RecordDemo(const char *name) void G_RecordDemo(const char *name)
{ {
INT32 maxsize; strlcpy(demoname, name, sizeof(demoname));
strcpy(demoname, name); FIL_ForceExtension(demoname, ".lmp");
strcat(demoname, ".lmp");
maxsize = 1024*1024; INT32 maxsize = 1024*1024;
if (M_CheckParm("-maxdemo") && M_IsNextParm()) if (M_CheckParm("-maxdemo") && M_IsNextParm())
maxsize = atoi(M_GetNextParm()) * 1024; maxsize = atoi(M_GetNextParm()) * 1024;
// if (demobuffer)
// free(demobuffer);
demo_p = NULL; demo_p = NULL;
demobuffer = malloc(maxsize); demobuffer = malloc(maxsize);
demoend = demobuffer + maxsize; demoend = demobuffer + maxsize;
...@@ -2666,7 +2665,7 @@ void G_DoPlayMetal(void) ...@@ -2666,7 +2665,7 @@ void G_DoPlayMetal(void)
thinker_t *th; thinker_t *th;
// it's an internal demo // 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")); CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n"));
return; return;
...@@ -2792,7 +2791,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill) ...@@ -2792,7 +2791,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
{ {
WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker
WriteDemoChecksum(); 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. saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
} }
free(demobuffer); free(demobuffer);
......
...@@ -78,7 +78,7 @@ static void G_DoStartContinue(void); ...@@ -78,7 +78,7 @@ static void G_DoStartContinue(void);
static void G_DoContinued(void); static void G_DoContinued(void);
static void G_DoWorldDone(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 UINT16 mapmusflags; // Track and reset bit
UINT32 mapmusposition; // Position to jump to UINT32 mapmusposition; // Position to jump to
...@@ -105,6 +105,7 @@ UINT8 paused; ...@@ -105,6 +105,7 @@ UINT8 paused;
UINT8 modeattacking = ATTACKING_NONE; UINT8 modeattacking = ATTACKING_NONE;
boolean disableSpeedAdjust = false; boolean disableSpeedAdjust = false;
boolean imcontinuing = false; boolean imcontinuing = false;
boolean replacedcurrentmap = false;
boolean runemeraldmanager = false; boolean runemeraldmanager = false;
UINT16 emeraldspawndelay = 60*TICRATE; UINT16 emeraldspawndelay = 60*TICRATE;
...@@ -172,7 +173,13 @@ mapthing_t *bflagpoint; ...@@ -172,7 +173,13 @@ mapthing_t *bflagpoint;
struct quake quake; struct quake quake;
// Map Header Information // 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 exitgame = false;
static boolean retrying = false; static boolean retrying = false;
...@@ -465,7 +472,7 @@ void G_AllocNightsRecordData(INT16 i, gamedata_t *data) ...@@ -465,7 +472,7 @@ void G_AllocNightsRecordData(INT16 i, gamedata_t *data)
void G_ClearRecords(gamedata_t *data) void G_ClearRecords(gamedata_t *data)
{ {
INT16 i; INT16 i;
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < numgamemaps; ++i)
{ {
if (data->mainrecords[i]) if (data->mainrecords[i])
{ {
...@@ -573,7 +580,8 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player) ...@@ -573,7 +580,8 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player)
if (modeattacking) 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 *gpath;
char lastdemo[256], bestdemo[256]; char lastdemo[256], bestdemo[256];
...@@ -589,7 +597,7 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player) ...@@ -589,7 +597,7 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player)
if ((gpath = malloc(glen)) == NULL) if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n"); 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); snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name);
if (FIL_FileExists(lastdemo)) if (FIL_FileExists(lastdemo))
...@@ -711,7 +719,8 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player) ...@@ -711,7 +719,8 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player)
if (modeattacking) 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 *gpath;
char lastdemo[256], bestdemo[256]; char lastdemo[256], bestdemo[256];
...@@ -727,7 +736,7 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player) ...@@ -727,7 +736,7 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player)
if ((gpath = malloc(glen)) == NULL) if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n"); 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); snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name);
if (FIL_FileExists(lastdemo)) if (FIL_FileExists(lastdemo))
...@@ -805,6 +814,20 @@ void G_SetUsedCheats(boolean silent) ...@@ -805,6 +814,20 @@ void G_SetUsedCheats(boolean silent)
Command_ExitGame_f(); 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. /** Builds an original game map name from a map number.
* The complexity is due to MAPA0-MAPZZ. * The complexity is due to MAPA0-MAPZZ.
* *
...@@ -812,12 +835,12 @@ void G_SetUsedCheats(boolean silent) ...@@ -812,12 +835,12 @@ void G_SetUsedCheats(boolean silent)
* \return Pointer to a static buffer containing the desired map name. * \return Pointer to a static buffer containing the desired map name.
* \sa M_MapNumber * \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) static char mapname[10] = "MAPXX"; // internal map name (wad resource name)
I_Assert(map > 0); I_Assert(map > 0);
I_Assert(map <= NUMMAPS); I_Assert(map <= NUMBASEMAPS);
if (map < 100) if (map < 100)
sprintf(&mapname[3], "%.2d", map); sprintf(&mapname[3], "%.2d", map);
...@@ -834,6 +857,219 @@ const char *G_BuildMapName(INT32 map) ...@@ -834,6 +857,219 @@ const char *G_BuildMapName(INT32 map)
return mapname; 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. /** Clips the console player's mouse aiming to the current view.
* Used whenever the player view is changed manually. * Used whenever the player view is changed manually.
* *
...@@ -1872,7 +2108,7 @@ void G_DoLoadLevel(boolean resetplayer) ...@@ -1872,7 +2108,7 @@ void G_DoLoadLevel(boolean resetplayer)
// cleanup // cleanup
if (titlemapinaction == TITLEMAP_LOADING) 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 titlemap = 0; // let's not infinite recursion ok
Command_ExitGame_f(); Command_ExitGame_f();
...@@ -2789,8 +3025,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) ...@@ -2789,8 +3025,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
{ {
if (mapmusflags & MUSIC_RELOADRESET) if (mapmusflags & MUSIC_RELOADRESET)
{ {
strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7); strlcpy(mapmusname, mapheaderinfo[gamemap-1]->musname, MAX_MUSIC_NAME+1);
mapmusname[6] = 0;
mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK); mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
mapmusposition = mapheaderinfo[gamemap-1]->muspos; mapmusposition = mapheaderinfo[gamemap-1]->muspos;
} }
...@@ -3866,12 +4101,12 @@ UINT32 G_TOLFlag(INT32 pgametype) ...@@ -3866,12 +4101,12 @@ UINT32 G_TOLFlag(INT32 pgametype)
*/ */
static INT16 RandMap(UINT32 tolflags, INT16 pprevmap) 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; INT32 numokmaps = 0;
INT16 ix; INT16 ix;
// Find all the maps that are ok and and put them in an array. // 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 if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
&& ix != pprevmap // Don't pick the same map. && ix != pprevmap // Don't pick the same map.
&& (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps. && (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
...@@ -3993,7 +4228,7 @@ static void G_HandleSaveLevel(void) ...@@ -3993,7 +4228,7 @@ static void G_HandleSaveLevel(void)
G_UpdateAllVisited(); 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 // 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) 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 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
...@@ -4039,7 +4274,7 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent) ...@@ -4039,7 +4274,7 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
{ {
newmapnum = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); newmapnum = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
if (marathonmode && newmapnum == spmarathon_start-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; INT16 gametype_to_use;
...@@ -4055,13 +4290,11 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent) ...@@ -4055,13 +4290,11 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
// for instance). // for instance).
if (!spec || nextmapoverride) if (!spec || nextmapoverride)
{ {
if (newmapnum >= 0 && newmapnum < NUMMAPS) if (newmapnum >= 0 && newmapnum < numgamemaps)
{ {
INT16 cm = newmapnum; INT16 cm = newmapnum;
UINT32 tolflag = G_TOLFlag(gametype_to_use); UINT32 tolflag = G_TOLFlag(gametype_to_use);
UINT8 visitedmap[(NUMMAPS+7)/8]; UINT8 *visitedmap = Z_Calloc(((numgamemaps+7)/8) * sizeof(UINT8), PU_STATIC, NULL);
memset(visitedmap, 0, sizeof (visitedmap));
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{ {
...@@ -4073,12 +4306,12 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent) ...@@ -4073,12 +4306,12 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
else else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); 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. 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! //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) if (!silent)
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1); CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
...@@ -4097,14 +4330,17 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent) ...@@ -4097,14 +4330,17 @@ INT16 G_GetNextMap(boolean ignoretokens, boolean silent)
break; break;
} }
} }
Z_Free(visitedmap);
newmapnum = cm; newmapnum = cm;
} }
// wrap around in race // 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); 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); I_Error("Followed map %d to invalid map %d\n", prevmap + 1, newmapnum + 1);
if (!spec) if (!spec)
...@@ -4177,6 +4413,7 @@ static void G_DoCompleted(void) ...@@ -4177,6 +4413,7 @@ static void G_DoCompleted(void)
//Get and set prevmap/nextmap //Get and set prevmap/nextmap
prevmap = (INT16)(gamemap-1); prevmap = (INT16)(gamemap-1);
nextmap = G_GetNextMap(false, false); nextmap = G_GetNextMap(false, false);
automapactive = false; automapactive = false;
...@@ -4184,7 +4421,7 @@ static void G_DoCompleted(void) ...@@ -4184,7 +4421,7 @@ static void G_DoCompleted(void)
// We are committed to this map now. // We are committed to this map now.
// We may as well allocate its header if it doesn't exist // We may as well allocate its header if it doesn't exist
// (That is, if it's a real map) // (That is, if it's a real map)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) if (nextmap < numgamemaps && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap); P_AllocMapHeader(nextmap);
Y_DetermineIntermissionType(); Y_DetermineIntermissionType();
...@@ -4230,7 +4467,7 @@ void G_AfterIntermission(void) ...@@ -4230,7 +4467,7 @@ void G_AfterIntermission(void)
} }
else else
{ {
if (nextmap < 1100-1) if (!G_IsGameEndMap(nextmap+1))
G_NextLevel(); G_NextLevel();
else else
G_EndGame(); G_EndGame();
...@@ -4350,24 +4587,21 @@ void G_EndGame(void) ...@@ -4350,24 +4587,21 @@ void G_EndGame(void)
// Only do evaluation and credits in coop games. // Only do evaluation and credits in coop games.
if (gametyperules & GTR_CUTSCENES) if (gametyperules & GTR_CUTSCENES)
{ {
if (nextmap == 1103-1) // end game with ending switch (nextmap+1)
{ {
case NEXTMAP_ENDING:
F_StartEnding(); F_StartEnding();
return; return;
} case NEXTMAP_CREDITS:
if (nextmap == 1102-1) // end game with credits
{
F_StartCredits(); F_StartCredits();
return; return;
} case NEXTMAP_EVALUATION:
if (nextmap == 1101-1) // end game with evaluation
{
F_StartGameEvaluation(); F_StartGameEvaluation();
return; return;
} }
} }
// 1100 or competitive multiplayer, so go back to title screen. // NEXTMAP_TITLE or competitive multiplayer, so go back to title screen.
D_StartTitle(); D_StartTitle();
} }
...@@ -4391,6 +4625,62 @@ void G_LoadGameSettings(void) ...@@ -4391,6 +4625,62 @@ void G_LoadGameSettings(void)
#define GAMEDATA_ID 0x86E4A27C // Change every major version, as usual #define GAMEDATA_ID 0x86E4A27C // Change every major version, as usual
#define COMPAT_GAMEDATA_ID 0xFCAFE211 // TODO: 2.3: Delete #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 // G_LoadGameData
// Loads the main data file, which stores information such as emblems found, etc. // Loads the main data file, which stores information such as emblems found, etc.
...@@ -4402,14 +4692,6 @@ void G_LoadGameData(gamedata_t *data) ...@@ -4402,14 +4692,6 @@ void G_LoadGameData(gamedata_t *data)
UINT32 versionID; UINT32 versionID;
UINT8 rtemp; UINT8 rtemp;
//For records
UINT32 recscore;
tic_t rectime;
UINT16 recrings;
UINT8 recmares;
INT32 curmare;
// Stop saving, until we successfully load it again. // Stop saving, until we successfully load it again.
data->loaded = false; data->loaded = false;
...@@ -4517,9 +4799,12 @@ void G_LoadGameData(gamedata_t *data) ...@@ -4517,9 +4799,12 @@ void G_LoadGameData(gamedata_t *data)
} }
// TODO put another cipher on these things? meh, I don't care... // TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++) // Read map visited flags
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX) for (i = 0; i < NUMBASEMAPS; i++)
{
if (!ReadMapVisited(data, i))
goto datacorrupt; goto datacorrupt;
}
// To save space, use one bit per collected/achieved/unlocked flag // To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < max_emblems;) for (i = 0; i < max_emblems;)
...@@ -4556,47 +4841,57 @@ void G_LoadGameData(gamedata_t *data) ...@@ -4556,47 +4841,57 @@ void G_LoadGameData(gamedata_t *data)
data->timesBeatenUltimate = READUINT32(save_p); data->timesBeatenUltimate = READUINT32(save_p);
// Main records // Main records
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < NUMBASEMAPS; ++i)
{ {
recscore = READUINT32(save_p); if (!ReadMainRecords(data, i))
rectime = (tic_t)READUINT32(save_p);
recrings = READUINT16(save_p);
save_p++; // compat
if (recrings > 10000 || recscore > MAXSCORE)
goto datacorrupt; 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 // Nights records
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < NUMBASEMAPS; ++i)
{ {
if ((recmares = READUINT8(save_p)) == 0) if (!ReadNightsRecords(data, i))
continue; goto datacorrupt;
}
G_AllocNightsRecordData((INT16)i, data); #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;
for (curmare = 0; curmare < (recmares+1); ++curmare) UINT8 extraID = READUINT8(save_p);
if (extraID != 0x00)
goto datacorrupt;
UINT16 nummaps = READUINT16(save_p);
if (nummaps >= MAXMAPS)
goto datacorrupt;
// Read map visited flags
for (i = NUMBASEMAPS; i < nummaps; i++)
{ {
data->nightsrecords[i]->score[curmare] = READUINT32(save_p); if (!ReadMapVisited(data, i))
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p); goto datacorrupt;
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p); }
if (data->nightsrecords[i]->grade[curmare] > GRADE_S) // Main records
{ for (i = NUMBASEMAPS; i < nummaps; i++)
{
if (!ReadMainRecords(data, i))
goto datacorrupt; goto datacorrupt;
}
} }
data->nightsrecords[i]->nummares = recmares; // Nights records
for (i = NUMBASEMAPS; i < nummaps; i++)
{
if (!ReadNightsRecords(data, i))
goto datacorrupt;
}
} }
#endif
// done // done
Z_Free(savebuffer); Z_Free(savebuffer);
...@@ -4628,6 +4923,41 @@ void G_LoadGameData(gamedata_t *data) ...@@ -4628,6 +4923,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 // G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc. // Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(gamedata_t *data) void G_SaveGameData(gamedata_t *data)
...@@ -4638,8 +4968,6 @@ void G_SaveGameData(gamedata_t *data) ...@@ -4638,8 +4968,6 @@ void G_SaveGameData(gamedata_t *data)
INT32 i, j; INT32 i, j;
UINT8 btemp; UINT8 btemp;
INT32 curmare;
if (!data) if (!data)
return; // data struct not valid return; // data struct not valid
...@@ -4668,7 +4996,7 @@ void G_SaveGameData(gamedata_t *data) ...@@ -4668,7 +4996,7 @@ void G_SaveGameData(gamedata_t *data)
WRITEUINT32(data_p, quickncasehash(timeattackfolder, sizeof timeattackfolder)); WRITEUINT32(data_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
// TODO put another cipher on these things? meh, I don't care... // 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)); WRITEUINT8(data_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag // To save space, use one bit per collected/achieved/unlocked flag
...@@ -4710,41 +5038,35 @@ void G_SaveGameData(gamedata_t *data) ...@@ -4710,41 +5038,35 @@ void G_SaveGameData(gamedata_t *data)
WRITEUINT32(data_p, data->timesBeatenUltimate); WRITEUINT32(data_p, data->timesBeatenUltimate);
// Main records // Main records
for (i = 0; i < NUMMAPS; i++) for (i = 0; i < NUMBASEMAPS; i++)
{ WriteMainRecords(data, i, &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
}
// NiGHTS records // 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) // Write extra data
{ WRITEUINT32(save_p, EXTRA_DATA_MARKER);
WRITEUINT8(data_p, 0); WRITEUINT8(save_p, 0x00);
continue;
}
WRITEUINT8(data_p, data->nightsrecords[i]->nummares); WRITEUINT16(save_p, numgamemaps);
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare) // Write map visited flags
{ for (i = NUMBASEMAPS; i < numgamemaps; i++)
WRITEUINT32(data_p, data->nightsrecords[i]->score[curmare]); WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
WRITEUINT8(data_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(data_p, data->nightsrecords[i]->time[curmare]); // 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; length = data_p - savebuffer;
...@@ -5059,7 +5381,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 character, bo ...@@ -5059,7 +5381,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 character, bo
SetPlayerSkinByNum(consoleplayer, character); SetPlayerSkinByNum(consoleplayer, character);
if (mapname) 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);
} }
// //
...@@ -5140,18 +5462,18 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean ...@@ -5140,18 +5462,18 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
// (Looks a bit silly, but it works.) // (Looks a bit silly, but it works.)
boolean reset_skin = netgame && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0'; 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 // internal game map
// well this check is useless because it is done before (d_netcmd.c::command_map_f) // well this check is useless because it is done before (d_netcmd.c::command_map_f)
// but in case of for demos.... // but in case of for demos....
if (W_CheckNumForName(mapname) == LUMPERROR) if (gamemap == 0)
{ {
I_Error("Internal game map '%s' not found\n", mapname); I_Error("Internal game map '%s' not found\n", mapname);
Command_ExitGame_f(); Command_ExitGame_f();
return; 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, // gamemap changed; we assume that its map header is always valid,
// so make it so // so make it so
if(!mapheaderinfo[gamemap-1]) if(!mapheaderinfo[gamemap-1])
...@@ -5202,7 +5524,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean ...@@ -5202,7 +5524,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
} }
} }
char *G_BuildMapTitle(INT32 mapnum) char *G_BuildMapTitle(INT32 mapnum)
{ {
char *title = NULL; char *title = NULL;
...@@ -5299,7 +5620,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, ...@@ -5299,7 +5620,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
mapnamelen = strlen(mapname); mapnamelen = strlen(mapname);
/* Count available maps; how ugly. */ /* Count available maps; how ugly. */
for (i = 0, freqc = 0; i < NUMMAPS; ++i) for (i = 0, freqc = 0; i < numgamemaps; ++i)
{ {
if (mapheaderinfo[i]) if (mapheaderinfo[i])
freqc++; freqc++;
...@@ -5310,7 +5631,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, ...@@ -5310,7 +5631,7 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep,
wanttable = !!( freqp ); wanttable = !!( freqp );
freqc = 0; freqc = 0;
for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum) for (i = 0, mapnum = 1; i < numgamemaps; ++i, ++mapnum)
if (mapheaderinfo[i]) if (mapheaderinfo[i])
{ {
if (!( realmapname = G_BuildMapTitle(mapnum) )) if (!( realmapname = G_BuildMapTitle(mapnum) ))
...@@ -5437,7 +5758,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) ...@@ -5437,7 +5758,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
{ {
usemapcode = true; usemapcode = true;
newmapnum = mapheaderinfo[gamemap-1]->nextlevel; 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); CONS_Alert(CONS_ERROR, M_GetText("NextLevel (%d) is not a valid map.\n"), newmapnum);
return 0; return 0;
...@@ -5454,6 +5775,11 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) ...@@ -5454,6 +5775,11 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) )) if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
usemapcode = true; usemapcode = true;
} }
else
{
if (( newmapnum = G_GetMapNumber(mapname) ))
usemapcode = true;
}
if (!usemapcode) if (!usemapcode)
{ {
...@@ -5461,7 +5787,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) ...@@ -5461,7 +5787,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
newmapnum = strtol(mapname, &p, 10); newmapnum = strtol(mapname, &p, 10);
if (*p == '\0')/* we got it */ 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); CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
return 0; return 0;
...@@ -5477,7 +5803,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) ...@@ -5477,7 +5803,7 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
if (usemapcode) if (usemapcode)
{ {
/* we can't check mapheaderinfo for this hahahaha */ /* we can't check mapheaderinfo for this hahahaha */
if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR) if (!G_MapFileExists(G_BuildMapName(newmapnum)))
return 0; return 0;
if (realmapnamep) if (realmapnamep)
......
...@@ -114,8 +114,18 @@ INT32 Joy2Axis(joyaxis_e axissel); ...@@ -114,8 +114,18 @@ INT32 Joy2Axis(joyaxis_e axissel);
#define SLOWTURNTICS (6) #define SLOWTURNTICS (6)
// build an internal map name MAPxx from map number // build an internal map name MAPxx from map number
const char *G_BuildClassicMapName(INT32 map);
const char *G_BuildMapName(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 INT16 ticcmd_oldangleturn[2];
extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object? extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
...@@ -142,6 +152,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps); ...@@ -142,6 +152,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene, boolean FLS); boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum); 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 struct searchdim
{ {
......
...@@ -1396,7 +1396,7 @@ static int lib_pPlayJingleMusic(lua_State *L) ...@@ -1396,7 +1396,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
{ {
player_t *player = NULL; player_t *player = NULL;
const char *musnamearg = luaL_checkstring(L, 2); 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); UINT16 musflags = luaL_optinteger(L, 3, 0);
boolean looping = lua_opttrueboolean(L, 4); boolean looping = lua_opttrueboolean(L, 4);
jingletype_t jingletype = luaL_optinteger(L, 5, JT_OTHER); jingletype_t jingletype = luaL_optinteger(L, 5, JT_OTHER);
...@@ -1411,8 +1411,7 @@ static int lib_pPlayJingleMusic(lua_State *L) ...@@ -1411,8 +1411,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
if (jingletype >= NUMJINGLES) if (jingletype >= NUMJINGLES)
return luaL_error(L, "jingletype %d out of range (0 - %d)", jingletype, NUMJINGLES-1); return luaL_error(L, "jingletype %d out of range (0 - %d)", jingletype, NUMJINGLES-1);
musname[6] = '\0'; strlcpy(musname, musnamearg, MAX_MUSIC_NAME+1);
strncpy(musname, musnamearg, 6);
while (*p) { while (*p) {
*p = tolower(*p); *p = tolower(*p);
...@@ -3988,10 +3987,53 @@ static int lib_gSetUsedCheats(lua_State *L) ...@@ -3988,10 +3987,53 @@ static int lib_gSetUsedCheats(lua_State *L)
return 0; 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) static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
{ {
if (ISINLEVEL) if (ISINLEVEL)
return luaL_optinteger(L, idx, gamemap); {
if (!lua_isnoneornil(L, idx))
return GetMapNameOrNumber(L, idx);
else
return gamemap;
}
else else
{ {
if (lua_isnoneornil(L, idx)) if (lua_isnoneornil(L, idx))
...@@ -4002,14 +4044,21 @@ static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) ...@@ -4002,14 +4044,21 @@ static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
); );
} }
else else
return luaL_checkinteger(L, idx); return GetMapNameOrNumber(L, idx);
} }
} }
static int lib_gBuildMapName(lua_State *L) static int lib_gBuildMapName(lua_State *L)
{ {
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapName"); 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)); lua_pushstring(L, G_BuildMapName(map));
return 1; return 1;
} }
...@@ -4018,12 +4067,12 @@ static int lib_gBuildMapTitle(lua_State *L) ...@@ -4018,12 +4067,12 @@ static int lib_gBuildMapTitle(lua_State *L)
{ {
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle"); INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle");
char *name; char *name;
if (map < 1 || map > NUMMAPS) if (map < 1 || map > numgamemaps)
{ {
return luaL_error(L, return luaL_error(L,
"map number %d out of range (1 - %d)", "map number %d out of range (1 - %d)",
map, map,
NUMMAPS numgamemaps
); );
} }
name = G_BuildMapTitle(map); name = G_BuildMapTitle(map);
...@@ -4143,6 +4192,36 @@ static int lib_gFindMapByNameOrCode(lua_State *L) ...@@ -4143,6 +4192,36 @@ static int lib_gFindMapByNameOrCode(lua_State *L)
return 1; 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) static int lib_gDoReborn(lua_State *L)
{ {
INT32 playernum = luaL_checkinteger(L, 1); INT32 playernum = luaL_checkinteger(L, 1);
...@@ -4176,7 +4255,19 @@ static int lib_gSetCustomExitVars(lua_State *L) ...@@ -4176,7 +4255,19 @@ static int lib_gSetCustomExitVars(lua_State *L)
if (n >= 1) 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); skipstats = (INT16)luaL_optinteger(L, 2, 0);
nextgametype = (INT16)luaL_optinteger(L, 3, -1); nextgametype = (INT16)luaL_optinteger(L, 3, -1);
} }
...@@ -4204,9 +4295,22 @@ static int lib_gExitLevel(lua_State *L) ...@@ -4204,9 +4295,22 @@ static int lib_gExitLevel(lua_State *L)
static int lib_gIsSpecialStage(lua_State *L) static int lib_gIsSpecialStage(lua_State *L)
{ {
INT32 mapnum = luaL_optinteger(L, 1, gamemap); INT32 mapnum;
//HUDSAFE
INLEVEL 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)); lua_pushboolean(L, G_IsSpecialStage(mapnum));
return 1; return 1;
} }
...@@ -4623,6 +4727,8 @@ static luaL_Reg lib[] = { ...@@ -4623,6 +4727,8 @@ static luaL_Reg lib[] = {
{"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_BuildMapTitle",lib_gBuildMapTitle},
{"G_FindMap",lib_gFindMap}, {"G_FindMap",lib_gFindMap},
{"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode}, {"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode},
{"G_GetMapThumbnail",lib_gGetMapThumbnail},
{"G_GetMapThumbnailWide",lib_gGetMapThumbnailWide},
{"G_DoReborn",lib_gDoReborn}, {"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars}, {"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished}, {"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
......
...@@ -1128,16 +1128,16 @@ static void update_music_name(struct MusicChange *musicchange) ...@@ -1128,16 +1128,16 @@ static void update_music_name(struct MusicChange *musicchange)
size_t length; size_t length;
const char * new = lua_tolstring(gL, -6, &length); const char * new = lua_tolstring(gL, -6, &length);
if (length < 7) if (length <= MAX_MUSIC_NAME)
{ {
strcpy(musicchange->newname, new); strcpy(musicchange->newname, new);
lua_pushvalue(gL, -6);/* may as well keep it for next call */ lua_pushvalue(gL, -6);/* may as well keep it for next call */
} }
else else
{ {
memcpy(musicchange->newname, new, 6); memcpy(musicchange->newname, new, MAX_MUSIC_NAME);
musicchange->newname[6] = '\0'; musicchange->newname[MAX_MUSIC_NAME] = '\0';
lua_pushlstring(gL, new, 6); lua_pushlstring(gL, new, MAX_MUSIC_NAME);
} }
lua_replace(gL, -7); lua_replace(gL, -7);
......
...@@ -2696,32 +2696,22 @@ static int vector3_get(lua_State *L) ...@@ -2696,32 +2696,22 @@ static int vector3_get(lua_State *L)
static int lib_getMapheaderinfo(lua_State *L) static int lib_getMapheaderinfo(lua_State *L)
{ {
// i -> mapheaderinfo[i-1] // i -> mapheaderinfo[i-1]
//int field;
lua_settop(L, 2); lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused. lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1)) if (lua_isnumber(L, 1))
{ {
size_t i = lua_tointeger(L, 1)-1; size_t i = lua_tointeger(L, 1)-1;
if (i >= NUMMAPS) if (i >= numgamemaps)
return 0; return 0;
LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER); 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 1;
}*/ }
return 0; return 0;
} }
static int lib_nummapheaders(lua_State *L) static int lib_nummapheaders(lua_State *L)
{ {
lua_pushinteger(L, NUMMAPS); lua_pushinteger(L, numgamemaps);
return 1; return 1;
} }
......
...@@ -78,7 +78,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src) ...@@ -78,7 +78,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited)); memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
// Main records // Main records
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < MAXMAPS; ++i)
{ {
if (!src->mainrecords[i]) if (!src->mainrecords[i])
continue; continue;
...@@ -90,7 +90,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src) ...@@ -90,7 +90,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
} }
// Nights records // Nights records
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < MAXMAPS; ++i)
{ {
if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares) if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
continue; continue;
...@@ -577,7 +577,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data) ...@@ -577,7 +577,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
INT32 mscore = 0; INT32 mscore = 0;
INT32 i; INT32 i;
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < numgamemaps; ++i)
{ {
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK)) if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue; continue;
...@@ -595,7 +595,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data) ...@@ -595,7 +595,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
INT32 curtics = 0; INT32 curtics = 0;
INT32 i; INT32 i;
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < numgamemaps; ++i)
{ {
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK)) if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue; continue;
...@@ -613,7 +613,7 @@ UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data) ...@@ -613,7 +613,7 @@ UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
INT32 mrings = 0; INT32 mrings = 0;
INT32 i; INT32 i;
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < numgamemaps; ++i)
{ {
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK)) if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue; continue;
......
...@@ -203,9 +203,9 @@ typedef struct ...@@ -203,9 +203,9 @@ typedef struct
boolean unlocked[MAXUNLOCKABLES]; boolean unlocked[MAXUNLOCKABLES];
// TIME ATTACK DATA // TIME ATTACK DATA
recorddata_t *mainrecords[NUMMAPS]; recorddata_t *mainrecords[MAXMAPS];
nightsdata_t *nightsrecords[NUMMAPS]; nightsdata_t *nightsrecords[MAXMAPS];
UINT8 mapvisited[NUMMAPS]; UINT8 mapvisited[MAXMAPS];
// # OF TIMES THE GAME HAS BEEN BEATEN // # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten; UINT32 timesBeaten;
......
...@@ -419,7 +419,7 @@ consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesN ...@@ -419,7 +419,7 @@ consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesN
static CV_PossibleValue_t map_cons_t[] = { static CV_PossibleValue_t map_cons_t[] = {
{1,"MIN"}, {1,"MIN"},
{NUMMAPS, "MAX"}, {MAXMAPS, "MAX"},
{0,NULL} {0,NULL}
}; };
consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange); consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange);
...@@ -2246,9 +2246,9 @@ void Nextmap_OnChange(void) ...@@ -2246,9 +2246,9 @@ void Nextmap_OnChange(void)
{ {
gamedata_t *data = clientGamedata; gamedata_t *data = clientGamedata;
char *leveltitle; char *leveltitle;
char tabase[256]; char tabase[4096];
#ifdef OLDNREPLAYNAME #ifdef OLDNREPLAYNAME
char tabaseold[256]; char tabaseold[4096];
#endif #endif
short i; short i;
boolean active; boolean active;
...@@ -2516,13 +2516,13 @@ void M_InitMenuPresTables(void) ...@@ -2516,13 +2516,13 @@ void M_InitMenuPresTables(void)
menupres[i].muslooping = true; menupres[i].muslooping = true;
} }
if (i == MN_SP_TIMEATTACK) 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) 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) 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) 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) else if (i == MN_SR_SOUNDTEST)
{ {
*menupres[i].musname = '\0'; *menupres[i].musname = '\0';
...@@ -2548,7 +2548,7 @@ typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **, boolean fr ...@@ -2548,7 +2548,7 @@ typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **, boolean fr
// a single input. Maybe someday use this struct program-wide. // a single input. Maybe someday use this struct program-wide.
typedef struct typedef struct
{ {
char musname[7]; char musname[MAX_MUSIC_NAME+1];
UINT16 mustrack; UINT16 mustrack;
boolean muslooping; boolean muslooping;
} menupresmusic_t; } menupresmusic_t;
...@@ -2759,8 +2759,7 @@ void M_ChangeMenuMusic(const char *defaultmusname, boolean defaultmuslooping) ...@@ -2759,8 +2759,7 @@ void M_ChangeMenuMusic(const char *defaultmusname, boolean defaultmuslooping)
if (!defaultmusname) if (!defaultmusname)
defaultmusname = ""; defaultmusname = "";
strncpy(defaultmusic.musname, defaultmusname, 7); strlcpy(defaultmusic.musname, defaultmusname, MAX_MUSIC_NAME+1);
defaultmusic.musname[6] = 0;
defaultmusic.mustrack = 0; defaultmusic.mustrack = 0;
defaultmusic.muslooping = defaultmuslooping; defaultmusic.muslooping = defaultmuslooping;
...@@ -5135,7 +5134,7 @@ static INT32 M_CountLevelsToShowOnPlatter(INT32 gt) ...@@ -5135,7 +5134,7 @@ static INT32 M_CountLevelsToShowOnPlatter(INT32 gt)
{ {
INT32 mapnum, count = 0; INT32 mapnum, count = 0;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++) for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt)) if (M_CanShowLevelOnPlatter(mapnum, gt))
count++; count++;
...@@ -5170,7 +5169,7 @@ static boolean M_GametypeHasLevels(INT32 gt) ...@@ -5170,7 +5169,7 @@ static boolean M_GametypeHasLevels(INT32 gt)
{ {
INT32 mapnum; INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++) for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt)) if (M_CanShowLevelOnPlatter(mapnum, gt))
return true; return true;
...@@ -5182,11 +5181,9 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) ...@@ -5182,11 +5181,9 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
INT32 col = 0, rows = 0; INT32 col = 0, rows = 0;
INT32 mapIterate = 0; INT32 mapIterate = 0;
INT32 headingIterate = 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 < numgamemaps; mapIterate++)
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++)
{ {
boolean forceNewRow = true; boolean forceNewRow = true;
...@@ -5202,7 +5199,7 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) ...@@ -5202,7 +5199,7 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
continue; continue;
} }
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++) for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{ {
boolean wide = false; boolean wide = false;
...@@ -5249,6 +5246,8 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) ...@@ -5249,6 +5246,8 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
rows++; rows++;
} }
Z_Free(mapAddedAlready);
return rows; return rows;
} }
...@@ -5280,11 +5279,13 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) ...@@ -5280,11 +5279,13 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
INT32 col = 0, row = 0, startrow = 0; INT32 col = 0, row = 0, startrow = 0;
INT32 mapIterate = 0; // First level of map loop -- find starting points for select headings 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. INT32 headingIterate = 0; // Second level of map loop -- finding maps that match mapIterate's heading.
boolean mapAddedAlready[NUMMAPS]; boolean *mapAddedAlready;
if (!numrows) if (!numrows)
return false; return false;
mapAddedAlready = Z_Calloc(numgamemaps*sizeof(boolean), PU_STATIC, NULL);
if (levelselect.rows) if (levelselect.rows)
Z_Free(levelselect.rows); Z_Free(levelselect.rows);
levelselect.rows = NULL; levelselect.rows = NULL;
...@@ -5297,8 +5298,6 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) ...@@ -5297,8 +5298,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 // done here so lsrow and lscol can be set if cv_nextmap is on the platter
lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0; lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0;
memset(mapAddedAlready, 0, sizeof mapAddedAlready);
if (levellistmode == LLM_CREATESERVER) if (levellistmode == LLM_CREATESERVER)
{ {
sprintf(levelselect.rows[0].header, "Gametype"); sprintf(levelselect.rows[0].header, "Gametype");
...@@ -5310,7 +5309,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) ...@@ -5310,7 +5309,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
char_notes = NULL; char_notes = NULL;
} }
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++) for (mapIterate = 0; mapIterate < numgamemaps; mapIterate++)
{ {
INT32 headerRow = -1; INT32 headerRow = -1;
boolean anyAvailable = false; boolean anyAvailable = false;
...@@ -5328,7 +5327,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) ...@@ -5328,7 +5327,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
continue; continue;
} }
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++) for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{ {
UINT8 actnum = 0; UINT8 actnum = 0;
boolean headingisname = false; boolean headingisname = false;
...@@ -5479,6 +5478,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) ...@@ -5479,6 +5478,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
} }
#endif #endif
Z_Free(mapAddedAlready);
M_CacheLevelPlatter(); M_CacheLevelPlatter();
return true; return true;
...@@ -5714,8 +5715,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo ...@@ -5714,8 +5715,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
} }
else else
{ {
if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR) const char *thumbnail = G_GetMapThumbnailWide(map);
patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_PATCH); if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else else
patch = levselp[1][2]; // don't static to indicate that it's just a normal level patch = levselp[1][2]; // don't static to indicate that it's just a normal level
...@@ -5745,8 +5747,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea ...@@ -5745,8 +5747,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
} }
else else
{ {
if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR) const char *thumbnail = G_GetMapThumbnail(map);
patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_PATCH); if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else else
patch = levselp[0][2]; // don't static to indicate that it's just a normal level patch = levselp[0][2]; // don't static to indicate that it's just a normal level
...@@ -6059,7 +6062,7 @@ static INT32 M_GetFirstLevelInList(INT32 gt) ...@@ -6059,7 +6062,7 @@ static INT32 M_GetFirstLevelInList(INT32 gt)
{ {
INT32 mapnum; INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++) for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelInList(mapnum, gt)) if (M_CanShowLevelInList(mapnum, gt))
return mapnum + 1; return mapnum + 1;
...@@ -6818,12 +6821,9 @@ static boolean M_ExitPandorasBox(void) ...@@ -6818,12 +6821,9 @@ static boolean M_ExitPandorasBox(void)
static void M_ChangeLevel(INT32 choice) static void M_ChangeLevel(INT32 choice)
{ {
char mapname[6]; const char *mapname = G_BuildMapName(cv_nextmap.value);
(void)choice;
strlcpy(mapname, G_BuildMapName(cv_nextmap.value), sizeof (mapname)); (void)choice;
strlwr(mapname);
mapname[5] = '\0';
M_ClearMenus(true); M_ClearMenus(true);
COM_BufAddText(va("map %s -gametype \"%s\"\n", mapname, cv_newgametype.string)); COM_BufAddText(va("map %s -gametype \"%s\"\n", mapname, cv_newgametype.string));
...@@ -6988,7 +6988,7 @@ static void M_LevelSelectWarp(INT32 choice) ...@@ -6988,7 +6988,7 @@ static void M_LevelSelectWarp(INT32 choice)
{ {
(void)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)); CONS_Alert(CONS_WARNING, "Internal game map '%s' not found\n", G_BuildMapName(cv_nextmap.value));
return; return;
...@@ -8155,7 +8155,7 @@ static void M_SinglePlayerMenu(INT32 choice) ...@@ -8155,7 +8155,7 @@ static void M_SinglePlayerMenu(INT32 choice)
if (mapheaderinfo[spmarathon_start-1] if (mapheaderinfo[spmarathon_start-1]
&& !mapheaderinfo[spmarathon_start-1]->marathonnext && !mapheaderinfo[spmarathon_start-1]->marathonnext
&& (mapheaderinfo[spmarathon_start-1]->nextlevel == spmarathon_start && (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... 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 // ...and lower the above options' display positions by 8 pixels to close the gap
...@@ -8358,7 +8358,7 @@ static void M_DrawLoadGameData(void) ...@@ -8358,7 +8358,7 @@ static void M_DrawLoadGameData(void)
#ifdef PERFECTSAVE // disabled on request #ifdef PERFECTSAVE // disabled on request
else if ((savegameinfo[savetodraw].skinnum == 1) else if ((savegameinfo[savetodraw].skinnum == 1)
&& (savegameinfo[savetodraw].lives == 99) && (savegameinfo[savetodraw].lives == 99)
&& (savegameinfo[savetodraw].gamemap & 8192) && (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
&& (savegameinfo[savetodraw].numgameovers == 0) && (savegameinfo[savetodraw].numgameovers == 0)
&& (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save && (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save
{ {
...@@ -8414,11 +8414,11 @@ static void M_DrawLoadGameData(void) ...@@ -8414,11 +8414,11 @@ static void M_DrawLoadGameData(void)
else else
{ {
patch_t *patch; patch_t *patch;
if (savegameinfo[savetodraw].gamemap & 8192) if (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
patch = savselp[3]; patch = savselp[3];
else 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) if (lumpnum != LUMPERROR)
patch = W_CachePatchNum(lumpnum, PU_PATCH); patch = W_CachePatchNum(lumpnum, PU_PATCH);
else else
...@@ -8433,7 +8433,7 @@ static void M_DrawLoadGameData(void) ...@@ -8433,7 +8433,7 @@ static void M_DrawLoadGameData(void)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME"); V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666) else if (savegameinfo[savetodraw].lives == -666)
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!"); 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!"); V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else else
V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname); V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname);
...@@ -8651,7 +8651,7 @@ static void M_LoadSelect(INT32 choice) ...@@ -8651,7 +8651,7 @@ static void M_LoadSelect(INT32 choice)
// This slot is empty, so start a new game here. // This slot is empty, so start a new game here.
M_NewGame(); M_NewGame();
} }
else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed else if (savegameinfo[saveSlotSelected-1].flags & SAVE_GAME_COMPLETE_BIT) // Completed
{ {
M_LoadGameLevelSelect(0); M_LoadGameLevelSelect(0);
} }
...@@ -8677,6 +8677,7 @@ static void M_ReadSavegameInfo(UINT32 slot) ...@@ -8677,6 +8677,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
UINT8 *end_p; // buffer end point, don't read past here UINT8 *end_p; // buffer end point, don't read past here
UINT8 *sav_p; UINT8 *sav_p;
INT32 fake; // Dummy variable INT32 fake; // Dummy variable
INT16 mapnum;
char temp[sizeof(timeattackfolder)]; char temp[sizeof(timeattackfolder)];
char vcheck[VERSIONSIZE]; char vcheck[VERSIONSIZE];
#ifdef NEWSKINSAVES #ifdef NEWSKINSAVES
...@@ -8711,19 +8712,44 @@ static void M_ReadSavegameInfo(UINT32 slot) ...@@ -8711,19 +8712,44 @@ static void M_ReadSavegameInfo(UINT32 slot)
CHECKPOS CHECKPOS
fake = READINT16(sav_p); 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'; savegameinfo[slot].levelname[0] = '\0';
else if (V_ThinStringWidth(mapheaderinfo[(fake-1) & 8191]->lvlttl, 0) <= 78) else if (V_ThinStringWidth(mapheaderinfo[mapnum-1]->lvlttl, 0) <= 78)
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 22); strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 22);
else else
{ {
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 15); strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 15);
strcat(savegameinfo[slot].levelname, "..."); strcat(savegameinfo[slot].levelname, "...");
} }
savegameinfo[slot].gamemap = fake; savegameinfo[slot].gamemap = mapnum;
CHECKPOS CHECKPOS
savegameinfo[slot].numemeralds = READUINT16(sav_p)-357; // emeralds savegameinfo[slot].numemeralds = READUINT16(sav_p)-357; // emeralds
...@@ -9538,7 +9564,8 @@ static void M_ChoosePlayer(INT32 choice) ...@@ -9538,7 +9564,8 @@ static void M_ChoosePlayer(INT32 choice)
static INT32 statsLocation; static INT32 statsLocation;
static INT32 statsMax; static INT32 statsMax;
static INT16 statsMapList[NUMMAPS+1]; static INT16 *statsMapList = NULL;
static size_t statsMapLength = 0;
static void M_Statistics(INT32 choice) static void M_Statistics(INT32 choice)
{ {
...@@ -9546,9 +9573,17 @@ static void M_Statistics(INT32 choice) ...@@ -9546,9 +9573,17 @@ static void M_Statistics(INT32 choice)
(void)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') if (!mapheaderinfo[i] || mapheaderinfo[i]->lvlttl[0] == '\0')
continue; continue;
...@@ -9684,7 +9719,7 @@ static void M_DrawLevelStats(void) ...@@ -9684,7 +9719,7 @@ static void M_DrawLevelStats(void)
G_TicsToMinutes(data->totalplaytime, false), G_TicsToMinutes(data->totalplaytime, false),
G_TicsToSeconds(data->totalplaytime))); G_TicsToSeconds(data->totalplaytime)));
for (i = 0; i < NUMMAPS; i++) for (i = 0; i < numgamemaps; i++)
{ {
boolean mapunfinished = false; boolean mapunfinished = false;
...@@ -9895,10 +9930,10 @@ void M_DrawTimeAttackMenu(void) ...@@ -9895,10 +9930,10 @@ void M_DrawTimeAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false); M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP // 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) 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 else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH); PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
...@@ -10160,10 +10195,10 @@ void M_DrawNightsAttackMenu(void) ...@@ -10160,10 +10195,10 @@ void M_DrawNightsAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false); M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP // 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) 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 else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH); PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
...@@ -10328,7 +10363,8 @@ static void M_NightsAttack(INT32 choice) ...@@ -10328,7 +10363,8 @@ static void M_NightsAttack(INT32 choice)
static void M_ChooseNightsAttack(INT32 choice) static void M_ChooseNightsAttack(INT32 choice)
{ {
char *gpath; 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]; char nameofdemo[256];
(void)choice; (void)choice;
emeralds = 0; emeralds = 0;
...@@ -10342,7 +10378,7 @@ static void M_ChooseNightsAttack(INT32 choice) ...@@ -10342,7 +10378,7 @@ static void M_ChooseNightsAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL) if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n"); 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); snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value) if (!cv_autorecord.value)
...@@ -10357,7 +10393,8 @@ static void M_ChooseNightsAttack(INT32 choice) ...@@ -10357,7 +10393,8 @@ static void M_ChooseNightsAttack(INT32 choice)
static void M_ChooseTimeAttack(INT32 choice) static void M_ChooseTimeAttack(INT32 choice)
{ {
char *gpath; 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]; char nameofdemo[256];
(void)choice; (void)choice;
emeralds = 0; emeralds = 0;
...@@ -10371,7 +10408,7 @@ static void M_ChooseTimeAttack(INT32 choice) ...@@ -10371,7 +10408,7 @@ static void M_ChooseTimeAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL) if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n"); 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); snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value) if (!cv_autorecord.value)
...@@ -10704,7 +10741,7 @@ static void M_Marathon(INT32 choice) ...@@ -10704,7 +10741,7 @@ static void M_Marathon(INT32 choice)
SP_MarathonMenu[marathonplayer].status = (skinset == MAXCHARACTERSLOTS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED; SP_MarathonMenu[marathonplayer].status = (skinset == MAXCHARACTERSLOTS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED;
while (mapnum < NUMMAPS) while (mapnum < numgamemaps)
{ {
if (mapheaderinfo[mapnum]) if (mapheaderinfo[mapnum])
{ {
...@@ -10714,7 +10751,7 @@ static void M_Marathon(INT32 choice) ...@@ -10714,7 +10751,7 @@ static void M_Marathon(INT32 choice)
mapnum++; 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); M_ChangeMenuMusic("spec8", true);
...@@ -11616,10 +11653,10 @@ static void M_DrawServerMenu(void) ...@@ -11616,10 +11653,10 @@ static void M_DrawServerMenu(void)
M_DrawLevelPlatterHeader(currentMenu->y + imgheight - 10 - lsheadingheight/2, (const char *)headerstr, true, false); M_DrawLevelPlatterHeader(currentMenu->y + imgheight - 10 - lsheadingheight/2, (const char *)headerstr, true, false);
// A 160x100 image of the level as entry MAPxxP // 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) 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 else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH); PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......
...@@ -159,7 +159,7 @@ typedef struct ...@@ -159,7 +159,7 @@ typedef struct
INT16 ttloop; // # frame to loop; -1 means dont loop INT16 ttloop; // # frame to loop; -1 means dont loop
UINT16 tttics; // # of tics per frame 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. 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 muslooping; ///< Loop the music
boolean musstop; ///< Don't play any music boolean musstop; ///< Don't play any music
...@@ -427,6 +427,7 @@ typedef struct ...@@ -427,6 +427,7 @@ typedef struct
INT32 lives; INT32 lives;
INT32 continuescore; INT32 continuescore;
INT32 gamemap; INT32 gamemap;
UINT8 flags;
} saveinfo_t; } saveinfo_t;
extern description_t *description; extern description_t *description;
......
...@@ -2261,6 +2261,30 @@ boolean M_IsStringEmpty(const char *s) ...@@ -2261,6 +2261,30 @@ boolean M_IsStringEmpty(const char *s)
return true; 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. // Converts a string containing a whole number into an int. Returns false if the conversion failed.
boolean M_StringToNumber(const char *input, int *out) boolean M_StringToNumber(const char *input, int *out)
{ {
...@@ -2312,3 +2336,48 @@ int M_RoundUp(double number) ...@@ -2312,3 +2336,48 @@ int M_RoundUp(double number)
return (int)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;
}